From 32e0edff78765eb9433b53946fc0d76bee8e08b3 Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 27 Nov 2023 11:12:25 -0500 Subject: [PATCH 01/66] Add docs to `optimism` `InvalidTransaction` errors. (#884) --- crates/primitives/src/result.rs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/crates/primitives/src/result.rs b/crates/primitives/src/result.rs index b5dd59df8c..015046397d 100644 --- a/crates/primitives/src/result.rs +++ b/crates/primitives/src/result.rs @@ -216,12 +216,36 @@ pub enum InvalidTransaction { TooManyBlobs, /// Blob transaction contains a versioned hash with an incorrect version BlobVersionNotSupported, - /// System transactions are not supported - /// post-regolith hardfork. + /// System transactions are not supported post-regolith hardfork. + /// + /// Before the Regolith hardfork, there was a special field in the `Deposit` transaction + /// type that differentiated between `system` and `user` deposit transactions. This field + /// was deprecated in the Regolith hardfork, and this error is thrown if a `Deposit` transaction + /// is found with this field set to `true` after the hardfork activation. + /// + /// In addition, this error is internal, and bubbles up into a [Halt::FailedDeposit] error + /// in the `revm` handler for the consumer to easily handle. This is due to a state transition + /// rule on OP Stack chains where, if for any reason a deposit transaction fails, the transaction + /// must still be included in the block, the sender nonce is bumped, the `mint` value persists, and + /// special gas accounting rules are applied. Normally on L1, [EVMError::Transaction] errors + /// are cause for non-inclusion, so a special [Halt] variant was introduced to handle this + /// case for failed deposit transactions. #[cfg(feature = "optimism")] DepositSystemTxPostRegolith, - /// Deposit transaction haults bubble up to the global main return handler, - /// wiping state and only increasing the nonce + persisting the mint value. + /// Deposit transaction haults bubble up to the global main return handler, wiping state and + /// only increasing the nonce + persisting the mint value. + /// + /// This is a catch-all error for any deposit transaction that is results in a [Halt] error + /// post-regolith hardfork. This allows for a consumer to easily handle special cases where + /// a deposit transaction fails during validation, but must still be included in the block. + /// + /// In addition, this error is internal, and bubbles up into a [Halt::FailedDeposit] error + /// in the `revm` handler for the consumer to easily handle. This is due to a state transition + /// rule on OP Stack chains where, if for any reason a deposit transaction fails, the transaction + /// must still be included in the block, the sender nonce is bumped, the `mint` value persists, and + /// special gas accounting rules are applied. Normally on L1, [EVMError::Transaction] errors + /// are cause for non-inclusion, so a special [Halt] variant was introduced to handle this + /// case for failed deposit transactions. #[cfg(feature = "optimism")] HaltedDepositPostRegolith, } From a9ba160e07f83b6352011127f875d5ce8e03c469 Mon Sep 17 00:00:00 2001 From: Iaroslav Mazur Date: Sun, 3 Dec 2023 15:57:00 +0200 Subject: [PATCH 02/66] refactor: fix case for CreateInitCodeSizeLimit error (#896) --- crates/interpreter/src/instruction_result.rs | 6 +++--- crates/interpreter/src/instructions/host.rs | 2 +- crates/primitives/src/env.rs | 2 +- crates/primitives/src/result.rs | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index 97a2403745..0202d380de 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -43,7 +43,7 @@ pub enum InstructionResult { /// Error on created contract that begins with EF CreateContractStartingWithEF, /// EIP-3860: Limit and meter initcode. Initcode size limit exceeded. - CreateInitcodeSizeLimit, + CreateInitCodeSizeLimit, /// Fatal external error. Returned by database. FatalExternalError, @@ -87,7 +87,7 @@ impl InstructionResult { | Self::NonceOverflow | Self::CreateContractSizeLimit | Self::CreateContractStartingWithEF - | Self::CreateInitcodeSizeLimit + | Self::CreateInitCodeSizeLimit | Self::FatalExternalError ) } @@ -190,7 +190,7 @@ impl From for SuccessOrHalt { InstructionResult::CreateContractStartingWithEF => { Self::Halt(Halt::CreateContractSizeLimit) } - InstructionResult::CreateInitcodeSizeLimit => Self::Halt(Halt::CreateInitcodeSizeLimit), + InstructionResult::CreateInitCodeSizeLimit => Self::Halt(Halt::CreateInitCodeSizeLimit), InstructionResult::FatalExternalError => Self::FatalExternalError, } } diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 12ca33ee49..9cdfbccb8e 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -258,7 +258,7 @@ pub fn create( .map(|limit| limit.saturating_mul(2)) .unwrap_or(MAX_INITCODE_SIZE); if len > max_initcode_size { - interpreter.instruction_result = InstructionResult::CreateInitcodeSizeLimit; + interpreter.instruction_result = InstructionResult::CreateInitCodeSizeLimit; return; } gas!(interpreter, gas::initcode_cost(len as u64)); diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index 4bcb535065..aa9fe202f3 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -108,7 +108,7 @@ impl Env { .map(|limit| limit.saturating_mul(2)) .unwrap_or(MAX_INITCODE_SIZE); if self.tx.data.len() > max_initcode_size { - return Err(InvalidTransaction::CreateInitcodeSizeLimit); + return Err(InvalidTransaction::CreateInitCodeSizeLimit); } } diff --git a/crates/primitives/src/result.rs b/crates/primitives/src/result.rs index 015046397d..649a9ce95c 100644 --- a/crates/primitives/src/result.rs +++ b/crates/primitives/src/result.rs @@ -196,7 +196,7 @@ pub enum InvalidTransaction { state: u64, }, /// EIP-3860: Limit and meter initcode - CreateInitcodeSizeLimit, + CreateInitCodeSizeLimit, /// Transaction chain id does not match the config chain id. InvalidChainId, /// Access list is not supported for blocks before the Berlin hardfork. @@ -286,7 +286,7 @@ impl fmt::Display for InvalidTransaction { InvalidTransaction::NonceTooLow { tx, state } => { write!(f, "Nonce {} too low, expected {}", tx, state) } - InvalidTransaction::CreateInitcodeSizeLimit => { + InvalidTransaction::CreateInitCodeSizeLimit => { write!(f, "Create initcode size limit") } InvalidTransaction::InvalidChainId => write!(f, "Invalid chain id"), @@ -382,7 +382,7 @@ pub enum Halt { /// Error on created contract that begins with EF CreateContractStartingWithEF, /// EIP-3860: Limit and meter initcode. Initcode size limit exceeded. - CreateInitcodeSizeLimit, + CreateInitCodeSizeLimit, /* Internal Halts that can be only found inside Inspector */ OverflowPayment, From d2ede3573d471cfd8acf67e9cc7a10b6ac4928b1 Mon Sep 17 00:00:00 2001 From: "refcell.eth" Date: Sun, 3 Dec 2023 06:24:41 -0800 Subject: [PATCH 03/66] chore(docs): Update top-level benchmark docs (#894) --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 292ba3e958..8a96a0558a 100644 --- a/README.md +++ b/README.md @@ -72,10 +72,17 @@ TODO needs to be updated. Benches can now be found inside `crates/revm/benches` cargo run --package revm-test --release --bin snailtracer ``` +The following flamegraph will require installing [flamegraph] by running `cargo install flamegraph`. + +[flamegraph]: https://docs.rs/crate/flamegraph/0.1.6 + ```shell cargo flamegraph --root --freq 4000 --min-width 0.001 --package revm-test --bin snailtracer ``` +This command will produce a flamegraph image output to `flamegraph.svg`. +Flamegraph also requires sudo mode to run (hence the `--root` cli arg) and will prompt you for your password if not in sudo mode already. + ## Running example ```shell From e802930cf3be9e773d008203665af9005f14dc2d Mon Sep 17 00:00:00 2001 From: "refcell.eth" Date: Mon, 4 Dec 2023 10:23:54 -0800 Subject: [PATCH 04/66] fix(ci): Workflow Touchups (#901) * fix(ci): github workflow touchups with rustdocs * fix(ci): remove unknown msrv --- .github/workflows/ci.yml | 122 +++++++++++------- Cargo.toml | 4 + crates/interpreter/Cargo.toml | 4 + .../src/interpreter/shared_memory.rs | 2 +- crates/interpreter/src/lib.rs | 1 + crates/precompile/Cargo.toml | 4 + crates/precompile/src/blake2.rs | 8 +- crates/precompile/src/hash.rs | 12 +- crates/precompile/src/identity.rs | 4 +- crates/precompile/src/lib.rs | 1 + crates/precompile/src/modexp.rs | 4 +- crates/primitives/Cargo.toml | 4 + .../src/db/components/block_hash.rs | 2 +- crates/primitives/src/db/components/state.rs | 2 +- crates/primitives/src/lib.rs | 1 + crates/revm/Cargo.toml | 4 + crates/revm/src/evm_context.rs | 2 +- crates/revm/src/journaled_state.rs | 2 +- crates/revm/src/lib.rs | 4 +- 19 files changed, 123 insertions(+), 64 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be5e3a5d98..af2b3a4127 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,65 +10,99 @@ on: pull_request: branches: [main, "release/**"] +env: + CARGO_TERM_COLOR: always + jobs: - tests-stable: - name: Tests (Stable) + test: + name: test ${{ matrix.rust }} ${{ matrix.flags }} runs-on: ubuntu-latest timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + rust: ["stable", "beta", "nightly"] + flags: ["--no-default-features", "", "--all-features"] steps: - - name: Checkout sources - uses: actions/checkout@v3 - - - name: Install toolchain - uses: dtolnay/rust-toolchain@stable + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master with: - targets: riscv32imac-unknown-none-elf - + toolchain: ${{ matrix.rust }} - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true + - run: cargo test --workspace ${{ matrix.flags }} - - name: cargo test - run: cargo test --workspace - - - name: cargo test all features - run: cargo test --workspace --all-features - - - name: cargo check no_std - run: cargo check --target riscv32imac-unknown-none-elf --no-default-features - - lint: - name: Lint + test-no-std: + name: test no_std runs-on: ubuntu-latest timeout-minutes: 30 steps: - - name: Checkout sources - uses: actions/checkout@v3 - - - name: Install toolchain - uses: dtolnay/rust-toolchain@nightly + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable with: - components: rustfmt, clippy - - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - - name: cargo fmt - run: cargo +nightly fmt --all -- --check - - - name: cargo clippy - run: cargo +nightly clippy --workspace --all-features -- -D warnings + targets: riscv32imac-unknown-none-elf + - run: cargo check --target riscv32imac-unknown-none-elf --no-default-features - - name: cargo check no-default-features - run: | + check-no-default-features: + name: check no-default-features + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + - run: | cd crates/revm cargo check --no-default-features - - name: cargo check serde - run: | + + check-serde: + name: check serde + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + - run: | cd crates/revm cargo check --no-default-features --features serde - - name: cargo check std - run: | + + check-std: + name: check std + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + - run: | cd crates/revm cargo check --no-default-features --features std + + clippy: + name: clippy + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@clippy + - run: cargo +nightly clippy --workspace --all-targets --all-features + env: + RUSTFLAGS: -Dwarnings + + docs: + name: docs + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly + with: + components: rust-docs + - run: cargo doc --workspace --all-features --no-deps --document-private-items + env: + RUSTDOCFLAGS: "--cfg docsrs -D warnings" + + fmt: + name: fmt + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt + - run: cargo +nightly fmt --all --check diff --git a/Cargo.toml b/Cargo.toml index fcb04eb722..4f7b81ca89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,10 @@ resolver = "2" members = ["bins/*", "crates/*"] default-members = ["crates/revm"] +[workspace.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + [profile.release] lto = true codegen-units = 1 diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index e0dc8e4b28..23266fbbde 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -9,6 +9,10 @@ repository = "https://github.com/bluealloy/revm" version = "1.3.0" readme = "../../README.md" +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + [dependencies] revm-primitives = { path = "../primitives", version = "1.3.0", default-features = false } diff --git a/crates/interpreter/src/interpreter/shared_memory.rs b/crates/interpreter/src/interpreter/shared_memory.rs index fc4f302ccc..77abbf2867 100644 --- a/crates/interpreter/src/interpreter/shared_memory.rs +++ b/crates/interpreter/src/interpreter/shared_memory.rs @@ -21,7 +21,7 @@ pub struct SharedMemory { checkpoints: Vec, /// Invariant: equals `self.checkpoints.last()` last_checkpoint: usize, - /// Memory limit. See [`crate::CfgEnv`]. + /// Memory limit. See [`CfgEnv`](revm_primitives::CfgEnv). #[cfg(feature = "memory_limit")] memory_limit: u64, } diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index 4637b402c9..eac96926bc 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -1,6 +1,7 @@ //! # revm-interpreter //! //! REVM Interpreter. +#![warn(rustdoc::all)] #![warn(unreachable_pub, unused_crate_dependencies)] #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(not(feature = "std"), no_std)] diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index ac5d16fc02..88a2aad9f0 100644 --- a/crates/precompile/Cargo.toml +++ b/crates/precompile/Cargo.toml @@ -8,6 +8,10 @@ name = "revm-precompile" repository = "https://github.com/bluealloy/revm" version = "2.2.0" +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + [dependencies] revm-primitives = { path = "../primitives", version = "1.3.0", default-features = false } bn = { package = "substrate-bn", version = "0.6", default-features = false } diff --git a/crates/precompile/src/blake2.rs b/crates/precompile/src/blake2.rs index b92a586672..e0cd3bc72d 100644 --- a/crates/precompile/src/blake2.rs +++ b/crates/precompile/src/blake2.rs @@ -9,7 +9,7 @@ pub const FUN: PrecompileWithAddress = PrecompileWithAddress( Precompile::Standard(run as StandardPrecompileFn), ); -/// reference: https://eips.ethereum.org/EIPS/eip-152 +/// reference: /// input format: /// [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 byte for f] fn run(input: &[u8], gas_limit: u64) -> PrecompileResult { @@ -55,7 +55,7 @@ fn run(input: &[u8], gas_limit: u64) -> PrecompileResult { } mod algo { - /// SIGMA from spec: https://datatracker.ietf.org/doc/html/rfc7693#section-2.7 + /// SIGMA from spec: const SIGMA: [[usize; 16]; 10] = [ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], @@ -69,7 +69,7 @@ mod algo { [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], ]; - /// got IV from: https://en.wikipedia.org/wiki/BLAKE_(hash_function) + /// got IV from: const IV: [u64; 8] = [ 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, @@ -83,7 +83,7 @@ mod algo { #[inline(always)] #[allow(clippy::many_single_char_names)] - /// G function: https://tools.ietf.org/html/rfc7693#section-3.1 + /// G function: fn g(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) { v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); v[d] = (v[d] ^ v[a]).rotate_right(32); diff --git a/crates/precompile/src/hash.rs b/crates/precompile/src/hash.rs index 2e50821fa2..4afe59c4f3 100644 --- a/crates/precompile/src/hash.rs +++ b/crates/precompile/src/hash.rs @@ -11,9 +11,9 @@ pub const RIPEMD160: PrecompileWithAddress = PrecompileWithAddress( Precompile::Standard(ripemd160_run as StandardPrecompileFn), ); -/// See: https://ethereum.github.io/yellowpaper/paper.pdf -/// See: https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions -/// See: https://etherscan.io/address/0000000000000000000000000000000000000002 +/// See: +/// See: +/// See: fn sha256_run(input: &[u8], gas_limit: u64) -> PrecompileResult { let cost = calc_linear_cost_u32(input.len(), 60, 12); if cost > gas_limit { @@ -24,9 +24,9 @@ fn sha256_run(input: &[u8], gas_limit: u64) -> PrecompileResult { } } -/// See: https://ethereum.github.io/yellowpaper/paper.pdf -/// See: https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions -/// See: https://etherscan.io/address/0000000000000000000000000000000000000003 +/// See: +/// See: +/// See: fn ripemd160_run(input: &[u8], gas_limit: u64) -> PrecompileResult { let gas_used = calc_linear_cost_u32(input.len(), 600, 120); if gas_used > gas_limit { diff --git a/crates/precompile/src/identity.rs b/crates/precompile/src/identity.rs index 5cb28e23a5..d3e9bfbd56 100644 --- a/crates/precompile/src/identity.rs +++ b/crates/precompile/src/identity.rs @@ -13,8 +13,8 @@ const IDENTITY_PER_WORD: u64 = 3; /// Takes the input bytes, copies them, and returns it as the output. /// -/// See: https://ethereum.github.io/yellowpaper/paper.pdf -/// See: https://etherscan.io/address/0000000000000000000000000000000000000004 +/// See: +/// See: fn identity_run(input: &[u8], gas_limit: u64) -> PrecompileResult { let gas_used = calc_linear_cost_u32(input.len(), IDENTITY_BASE, IDENTITY_PER_WORD); if gas_used > gas_limit { diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index b24f23a9b5..ea20a8185b 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -1,6 +1,7 @@ //! # revm-precompile //! //! Implementations of EVM precompiled contracts. +#![warn(rustdoc::all)] #![warn(unused_crate_dependencies)] #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(not(feature = "std"), no_std)] diff --git a/crates/precompile/src/modexp.rs b/crates/precompile/src/modexp.rs index 015d33d561..344bf1dd6c 100644 --- a/crates/precompile/src/modexp.rs +++ b/crates/precompile/src/modexp.rs @@ -17,8 +17,8 @@ pub const BERLIN: PrecompileWithAddress = PrecompileWithAddress( Precompile::Standard(berlin_run as StandardPrecompileFn), ); -/// See: https://eips.ethereum.org/EIPS/eip-198 -/// See: https://etherscan.io/address/0000000000000000000000000000000000000005 +/// See: +/// See: fn byzantium_run(input: &[u8], gas_limit: u64) -> PrecompileResult { run_inner(input, gas_limit, 0, |a, b, c, d| { byzantium_gas_calc(a, b, c, d) diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 17ddf04982..02fd54929c 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -12,6 +12,10 @@ readme = "../../README.md" # Don't need to run build script outside of this repo exclude = ["build.rs", "src/kzg/*.txt"] +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + [dependencies] alloy-primitives = { version = "0.5", default-features = false, features = [ "rlp", diff --git a/crates/primitives/src/db/components/block_hash.rs b/crates/primitives/src/db/components/block_hash.rs index 300f4d6dc8..be250ac0eb 100644 --- a/crates/primitives/src/db/components/block_hash.rs +++ b/crates/primitives/src/db/components/block_hash.rs @@ -1,5 +1,5 @@ //! BlockHash database component from [`crate::db::Database`] -//! it is used inside [crate::db::DatabaseComponents`] +//! it is used inside [`crate::db::DatabaseComponents`] use crate::{B256, U256}; use alloc::sync::Arc; diff --git a/crates/primitives/src/db/components/state.rs b/crates/primitives/src/db/components/state.rs index 63b0fd5c26..9121f0e28e 100644 --- a/crates/primitives/src/db/components/state.rs +++ b/crates/primitives/src/db/components/state.rs @@ -1,5 +1,5 @@ //! State database component from [`crate::db::Database`] -//! it is used inside [crate::db::DatabaseComponents`] +//! it is used inside [`crate::db::DatabaseComponents`] use crate::{AccountInfo, Address, Bytecode, B256, U256}; use alloc::sync::Arc; diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 8c35edc8fa..d721b4bef8 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -1,6 +1,7 @@ //! # revm-primitives //! //! EVM primitive types. +#![warn(rustdoc::all)] #![warn(unreachable_pub, unused_crate_dependencies)] #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(not(feature = "std"), no_std)] diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 1d21d33694..7ddba7cf6f 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -9,6 +9,10 @@ repository = "https://github.com/bluealloy/revm" version = "3.5.0" readme = "../../README.md" +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + [dependencies] revm-interpreter = { path = "../interpreter", version = "1.3.0", default-features = false } revm-precompile = { path = "../precompile", version = "2.2.0", default-features = false } diff --git a/crates/revm/src/evm_context.rs b/crates/revm/src/evm_context.rs index d0b47be655..b0bfa6c8cc 100644 --- a/crates/revm/src/evm_context.rs +++ b/crates/revm/src/evm_context.rs @@ -111,7 +111,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { .ok() } - /// Storage change of storage slot, before storing `sload`` will be called for that slot. + /// Storage change of storage slot, before storing `sload` will be called for that slot. pub fn sstore( &mut self, address: Address, diff --git a/crates/revm/src/journaled_state.rs b/crates/revm/src/journaled_state.rs index 50356f0faa..845ff985cb 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/revm/src/journaled_state.rs @@ -548,7 +548,7 @@ impl JournaledState { /// Load account from database to JournaledState. /// - /// Return boolean pair where first is `is_cold`` second bool `is_exists`. + /// Return boolean pair where first is `is_cold` second bool `is_exists`. #[inline] pub fn load_account_exist( &mut self, diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 71cac5cf6b..555a496aa4 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -1,4 +1,6 @@ -#![warn(unreachable_pub)] +#![doc = include_str!("../../../README.md")] +#![warn(rustdoc::all, unreachable_pub)] +#![allow(rustdoc::bare_urls)] #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![deny(unused_must_use, rust_2018_idioms)] From 8c8e0a429adb7afa7dba4b9ef3809069a38c7059 Mon Sep 17 00:00:00 2001 From: "refcell.eth" Date: Mon, 4 Dec 2023 10:46:29 -0800 Subject: [PATCH 05/66] chore(docs): revme readme update (#898) --- .gitignore | 2 +- bins/revme/README.md | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 2c4cd741e9..e23ee9b8cd 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ target .idea pkg/ - +tests bins/revme/temp_folder bins/revme/tests ethereumjs-util.js diff --git a/bins/revme/README.md b/bins/revme/README.md index a8805fe1a2..61390f233e 100644 --- a/bins/revme/README.md +++ b/bins/revme/README.md @@ -1,4 +1,26 @@ # Rust EVM executor or short REVME -This is binary crate that executed evm multiple ways. Currently it is used to run ethereum tests: -* statetest: takes path to folder where ethereum statetest json can be found. It recursively searches for all json files and execute them. This is how i run all https://github.com/ethereum/tests to check if revm is compliant. Example `revme statests test/GenericEvmTest/` +`revme` is a binary crate to execute the evm in multiple ways. + +Currently it is mainly used to run ethereum tests with the `statetest` subcommand. + +## State Tests + +`statetest` takes a path to the directory where ethereum statetest json can be found. +It recursively parses all json files in the specified directory and executes them. + +Running all [ethereum tests][et] checks that revm is compliant to the ethereum specs. + +To run [ethereum tests][et] locally, clone the [tests][et] repository and provide the +test directory. Below, we clone the repo and execute the `GeneralStateTests` suite of +tests. + +```shell +git clone https://github.com/ethereum/tests +cargo run -p revme statetest tests/GeneralStateTests +``` + +*Notice, in the [`.gitignore`](../../.gitignore), the `bins/revme/tests` directory +is ignored so it won't be checked into git.* + +[et]: https://github.com/ethereum/tests From 9464ae67d5b21be21cf1bdf4790e961853acb8b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 19:56:54 +0100 Subject: [PATCH 06/66] chore(deps): bump alloy-primitives from 0.5.0 to 0.5.2 (#900) Bumps [alloy-primitives](https://github.com/alloy-rs/core) from 0.5.0 to 0.5.2. - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.5.0...v0.5.2) --- updated-dependencies: - dependency-name: alloy-primitives dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf68662c8e..c8547e45ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,9 +46,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-primitives" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5def4b5e1bb8fe7ea37eeac1063246d4ef26f56cbdccf864a5a6bdcb80e91f4" +checksum = "08ca2c09d5911548a5cb620382ea0e1af99d3c898ce0efecbbd274a4676cf53e" dependencies = [ "alloy-rlp", "arbitrary", From d99f020f8ac87ced0450feec54e2880c19c2de5e Mon Sep 17 00:00:00 2001 From: "refcell.eth" Date: Tue, 5 Dec 2023 03:24:41 -0800 Subject: [PATCH 07/66] feat(revm): Evm Context Tests and test-utils Feature (#903) * feat(revm): evm context tests and test-utils * feat(revm): more evm context tests * fix(revm): clippy nits --- crates/revm/Cargo.toml | 2 + crates/revm/src/evm_context.rs | 169 ++++++++++++++++++++++++++++++++- crates/revm/src/lib.rs | 3 + crates/revm/src/test_utils.rs | 2 + 4 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 crates/revm/src/test_utils.rs diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 7ddba7cf6f..a0a4f157e9 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -44,6 +44,8 @@ std = ["revm-interpreter/std", "revm-precompile/std"] serde = ["dep:serde", "dep:serde_json", "revm-interpreter/serde"] arbitrary = ["revm-interpreter/arbitrary"] +test-utils = [] + optimism = ["revm-interpreter/optimism", "revm-precompile/optimism"] ethersdb = ["std", "tokio", "futures", "ethers-providers", "ethers-core"] diff --git a/crates/revm/src/evm_context.rs b/crates/revm/src/evm_context.rs index b0bfa6c8cc..0a42885ac6 100644 --- a/crates/revm/src/evm_context.rs +++ b/crates/revm/src/evm_context.rs @@ -266,13 +266,11 @@ impl<'a, DB: Database> EvmContext<'a, DB> { inputs.transfer.value, self.db, ) { - //println!("transfer error"); self.journaled_state.checkpoint_revert(checkpoint); return return_result(e); } if let Some(precompile) = self.precompiles.get(&inputs.contract) { - //println!("Call precompile"); let result = self.call_precompile(precompile, inputs, gas); if matches!(result.result, return_ok!()) { self.journaled_state.checkpoint_commit(); @@ -432,3 +430,170 @@ impl<'a, DB: Database> EvmContext<'a, DB> { (interpreter_result, address) } } + +/// Test utilities for the [`EvmContext`]. +#[cfg(any(test, feature = "test-utils"))] +pub(crate) mod test_utils { + use super::*; + use crate::db::CacheDB; + use crate::db::EmptyDB; + use crate::primitives::address; + use crate::primitives::SpecId; + + /// 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 { + contract: to, + transfer: revm_interpreter::Transfer { + source: MOCK_CALLER, + target: to, + value: U256::ZERO, + }, + input: Bytes::new(), + gas_limit: 0, + context: revm_interpreter::CallContext { + address: MOCK_CALLER, + caller: MOCK_CALLER, + code_address: MOCK_CALLER, + apparent_value: U256::ZERO, + scheme: revm_interpreter::CallScheme::Call, + }, + is_static: false, + } + } + + /// 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<'a>( + env: &'a mut Env, + db: &'a mut CacheDB, + balance: U256, + ) -> EvmContext<'a, CacheDB> { + db.insert_account_info( + test_utils::MOCK_CALLER, + crate::primitives::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<'a>( + env: &'a mut Env, + db: &'a mut CacheDB, + ) -> EvmContext<'a, CacheDB> { + EvmContext { + env, + journaled_state: JournaledState::new(SpecId::CANCUN, vec![]), + db, + error: None, + precompiles: Precompiles::default(), + #[cfg(feature = "optimism")] + l1_block_info: None, + } + } + + /// Returns a new `EvmContext` with an empty journaled state. + pub fn create_empty_evm_context<'a>( + env: &'a mut Env, + db: &'a mut EmptyDB, + ) -> EvmContext<'a, EmptyDB> { + EvmContext { + env, + journaled_state: JournaledState::new(SpecId::CANCUN, vec![]), + db, + error: None, + precompiles: Precompiles::default(), + #[cfg(feature = "optimism")] + l1_block_info: None, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::db::{CacheDB, EmptyDB}; + use crate::primitives::address; + use crate::JournalEntry; + use test_utils::*; + + // 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 mut env = Env::default(); + let mut db = EmptyDB::default(); + let mut evm_context = test_utils::create_empty_evm_context(&mut env, &mut db); + evm_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 = evm_context.make_call_frame(&call_inputs, 0..0); + let err = res.unwrap_err(); + assert_eq!(err.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 mut env = Env::default(); + let mut db = EmptyDB::default(); + let mut evm_context = test_utils::create_empty_evm_context(&mut env, &mut db); + let contract = address!("dead10000000000000000000000000000001dead"); + let mut call_inputs = test_utils::create_mock_call_inputs(contract); + call_inputs.transfer.value = U256::from(1); + let res = evm_context.make_call_frame(&call_inputs, 0..0); + let err = res.unwrap_err(); + assert_eq!(err.result, InstructionResult::OutOfFund); + let checkpointed = vec![vec![JournalEntry::AccountLoaded { 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() { + let mut env = Env::default(); + let mut cdb = CacheDB::new(EmptyDB::default()); + let bal = U256::from(3_000_000_000_u128); + let mut evm_context = create_cache_db_evm_context_with_balance(&mut env, &mut cdb, bal); + let contract = address!("dead10000000000000000000000000000001dead"); + let call_inputs = test_utils::create_mock_call_inputs(contract); + let res = evm_context.make_call_frame(&call_inputs, 0..0); + assert_eq!(res.unwrap_err().result, InstructionResult::Stop); + } + + #[test] + fn test_make_call_frame_succeeds() { + let mut env = Env::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, + crate::primitives::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(&mut env, &mut cdb, bal); + let call_inputs = test_utils::create_mock_call_inputs(contract); + let res = evm_context.make_call_frame(&call_inputs, 0..0); + let frame = res.unwrap(); + assert!(!frame.is_create); + assert_eq!(frame.created_address, None); + assert_eq!(frame.subcall_return_memory_range, 0..0); + } +} diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 555a496aa4..3ffd746be6 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -12,6 +12,9 @@ compile_error!("`with-serde` feature has been renamed to `serde`."); #[macro_use] extern crate alloc; +#[cfg(any(test, feature = "test-utils"))] +pub mod test_utils; + pub mod db; mod evm; mod evm_context; diff --git a/crates/revm/src/test_utils.rs b/crates/revm/src/test_utils.rs new file mode 100644 index 0000000000..46801c3dd6 --- /dev/null +++ b/crates/revm/src/test_utils.rs @@ -0,0 +1,2 @@ +#[doc(hidden)] +pub use crate::evm_context::test_utils::*; From 80dccc20f7906398c59f53b8274ee104c56f883d Mon Sep 17 00:00:00 2001 From: Iaroslav Mazur Date: Tue, 5 Dec 2023 13:28:32 +0200 Subject: [PATCH 08/66] refactor(revm): leverage StorageSlot methods, where appropriate (#899) --- crates/revm/src/db/states/bundle_account.rs | 6 +- crates/revm/src/db/states/state.rs | 106 +++++------------- .../revm/src/db/states/transition_account.rs | 2 +- 3 files changed, 32 insertions(+), 82 deletions(-) diff --git a/crates/revm/src/db/states/bundle_account.rs b/crates/revm/src/db/states/bundle_account.rs index 1cc873e384..5955cba706 100644 --- a/crates/revm/src/db/states/bundle_account.rs +++ b/crates/revm/src/db/states/bundle_account.rs @@ -102,10 +102,10 @@ impl BundleAccount { match slot { RevertToSlot::Some(value) => { // Don't overwrite original values if present - // if storage is not present set original values as current value. + // if storage is not present set original value as current value. self.storage .entry(key) - .or_insert(StorageSlot::new_changed(value, U256::ZERO)) + .or_insert(StorageSlot::new(value)) .present_value = value; } RevertToSlot::Destroyed => { @@ -140,7 +140,7 @@ impl BundleAccount { |updated_storage: &StorageWithOriginalValues| -> HashMap { updated_storage .iter() - .filter(|s| s.1.previous_or_original_value != s.1.present_value) + .filter(|s| s.1.is_changed()) .map(|(key, value)| { (*key, RevertToSlot::Some(value.previous_or_original_value)) }) diff --git a/crates/revm/src/db/states/state.rs b/crates/revm/src/db/states/state.rs index 8270509c00..95b802ee46 100644 --- a/crates/revm/src/db/states/state.rs +++ b/crates/revm/src/db/states/state.rs @@ -396,12 +396,10 @@ mod tests { previous_info: Some(existing_account_initial_info.clone()), storage: HashMap::from([( slot1, - StorageSlot { - previous_or_original_value: *existing_account_initial_storage - .get(&slot1) - .unwrap(), - present_value: U256::from(1000), - }, + StorageSlot::new_changed( + *existing_account_initial_storage.get(&slot1).unwrap(), + U256::from(1000), + ), )]), storage_was_destroyed: false, }, @@ -431,10 +429,7 @@ mod tests { previous_info: Some(new_account_changed_info), storage: HashMap::from([( slot1, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(1), - }, + StorageSlot::new_changed(U256::ZERO, U256::from(1)), )]), storage_was_destroyed: false, }, @@ -449,27 +444,19 @@ mod tests { storage: HashMap::from([ ( slot1, - StorageSlot { - previous_or_original_value: U256::from(100), - present_value: U256::from(1_000), - }, + StorageSlot::new_changed(U256::from(100), U256::from(1_000)), ), ( slot2, - StorageSlot { - previous_or_original_value: *existing_account_initial_storage - .get(&slot2) - .unwrap(), - present_value: U256::from(2_000), - }, + StorageSlot::new_changed( + *existing_account_initial_storage.get(&slot2).unwrap(), + U256::from(2_000), + ), ), // Create new slot ( slot3, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(3_000), - }, + StorageSlot::new_changed(U256::ZERO, U256::from(3_000)), ), ]), storage_was_destroyed: false, @@ -531,10 +518,7 @@ mod tests { status: AccountStatus::InMemoryChange, storage: HashMap::from([( slot1, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(1), - } + StorageSlot::new_changed(U256::ZERO, U256::from(1)) )]), }), "The latest state of the new account is incorrect" @@ -551,29 +535,22 @@ mod tests { storage: HashMap::from([ ( slot1, - StorageSlot { - previous_or_original_value: *existing_account_initial_storage - .get(&slot1) - .unwrap(), - present_value: U256::from(1_000), - }, + StorageSlot::new_changed( + *existing_account_initial_storage.get(&slot1).unwrap(), + U256::from(1_000) + ) ), ( slot2, - StorageSlot { - previous_or_original_value: *existing_account_initial_storage - .get(&slot2) - .unwrap(), - present_value: U256::from(2_000), - }, + StorageSlot::new_changed( + *existing_account_initial_storage.get(&slot2).unwrap(), + U256::from(2_000) + ) ), // Create new slot ( slot3, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(3_000), - }, + StorageSlot::new_changed(U256::ZERO, U256::from(3_000)) ), ]), }), @@ -646,18 +623,9 @@ mod tests { storage: HashMap::from([ ( slot1, - StorageSlot { - previous_or_original_value: U256::from(1), - present_value: U256::from(10), - }, - ), - ( - slot2, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(20), - }, + StorageSlot::new_changed(U256::from(1), U256::from(10)), ), + (slot2, StorageSlot::new_changed(U256::ZERO, U256::from(20))), ]), storage_was_destroyed: false, }, @@ -696,18 +664,9 @@ mod tests { storage: HashMap::from([ ( slot1, - StorageSlot { - previous_or_original_value: U256::from(10), - present_value: U256::from(1), - }, - ), - ( - slot2, - StorageSlot { - previous_or_original_value: U256::from(20), - present_value: U256::ZERO, - }, + StorageSlot::new_changed(U256::from(10), U256::from(1)), ), + (slot2, StorageSlot::new_changed(U256::from(20), U256::ZERO)), ]), storage_was_destroyed: false, }, @@ -761,10 +720,7 @@ mod tests { previous_info: None, storage: HashMap::from([( slot1, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(1), - }, + StorageSlot::new_changed(U256::ZERO, U256::from(1)), )]), storage_was_destroyed: false, }, @@ -794,10 +750,7 @@ mod tests { previous_info: None, storage: HashMap::from([( slot2, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(2), - }, + StorageSlot::new_changed(U256::ZERO, U256::from(2)), )]), storage_was_destroyed: false, }, @@ -816,10 +769,7 @@ mod tests { original_info: Some(existing_account_info.clone()), storage: HashMap::from([( slot2, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(2), - }, + StorageSlot::new_changed(U256::ZERO, U256::from(2)) )]), status: AccountStatus::DestroyedChanged, } diff --git a/crates/revm/src/db/states/transition_account.rs b/crates/revm/src/db/states/transition_account.rs index 804cb80cfa..1dc6bc33dd 100644 --- a/crates/revm/src/db/states/transition_account.rs +++ b/crates/revm/src/db/states/transition_account.rs @@ -109,7 +109,7 @@ impl TransitionAccount { if value.original_value() == slot.present_value() { entry.remove(); } else { - // is value is different, update transition present value; + // if value is different, update transition present value; value.present_value = slot.present_value; } } From 75a3cc4e1d4ecb42fdf5682318431c7c45cf80e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 16:58:18 +0100 Subject: [PATCH 09/66] chore(deps): bump hashbrown from 0.14.2 to 0.14.3 (#885) Bumps [hashbrown](https://github.com/rust-lang/hashbrown) from 0.14.2 to 0.14.3. - [Changelog](https://github.com/rust-lang/hashbrown/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/hashbrown/compare/v0.14.2...v0.14.3) --- updated-dependencies: - dependency-name: hashbrown dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c8547e45ec..9049c0ebf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1340,9 +1340,9 @@ checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", From 5a47ae0d2bb0909cc70d1b8ae2b6fc721ab1ca7d Mon Sep 17 00:00:00 2001 From: "refcell.eth" Date: Tue, 5 Dec 2023 08:03:42 -0800 Subject: [PATCH 10/66] feat(examples): generate block traces (#895) --- .gitignore | 4 +- Cargo.lock | 1 + README.md | 9 +- crates/revm/Cargo.toml | 6 + examples/generate_block_traces.rs | 175 ++++++++++++++++++++++++++++++ 5 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 examples/generate_block_traces.rs diff --git a/.gitignore b/.gitignore index e23ee9b8cd..5675048759 100644 --- a/.gitignore +++ b/.gitignore @@ -6,8 +6,10 @@ target .idea pkg/ -tests bins/revme/temp_folder bins/revme/tests ethereumjs-util.js book + +# Generated by the block traces example +traces diff --git a/Cargo.lock b/Cargo.lock index 9049c0ebf8..2c2fb89b11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2370,6 +2370,7 @@ dependencies = [ "ethers-core", "ethers-providers", "futures", + "indicatif", "revm-interpreter", "revm-precompile", "serde", diff --git a/README.md b/README.md index 8a96a0558a..d1671d5372 100644 --- a/README.md +++ b/README.md @@ -83,12 +83,19 @@ cargo flamegraph --root --freq 4000 --min-width 0.001 --package revm-test --bin This command will produce a flamegraph image output to `flamegraph.svg`. Flamegraph also requires sudo mode to run (hence the `--root` cli arg) and will prompt you for your password if not in sudo mode already. -## Running example +## Running examples ```shell cargo run -p revm --features ethersdb --example fork_ref_transact ``` +Generate block traces and write them to json files in a new `traces/` directory. +Each file corresponds to a transaction in the block and is named as such: `.json`. + +```shell +cargo run -p revm --features std,serde,ethersdb --example generate_block_traces +``` + # Used by: * [Foundry](https://github.com/foundry-rs/foundry) is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust. diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index a0a4f157e9..85c1d6c7b1 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -37,6 +37,7 @@ futures = { version = "0.3.29", optional = true } ethers-contract = { version = "2.0.11", default-features = false } anyhow = "1.0.75" criterion = "0.5" +indicatif = "0.17" [features] default = ["std", "c-kzg", "secp256k1"] @@ -80,6 +81,11 @@ name = "fork_ref_transact" path = "../../examples/fork_ref_transact.rs" required-features = ["ethersdb"] +[[example]] +name = "generate_block_traces" +path = "../../examples/generate_block_traces.rs" +required-features = ["std", "serde", "ethersdb"] + [[bench]] name = "bench" path = "benches/bench.rs" diff --git a/examples/generate_block_traces.rs b/examples/generate_block_traces.rs new file mode 100644 index 0000000000..37579b7f2c --- /dev/null +++ b/examples/generate_block_traces.rs @@ -0,0 +1,175 @@ +// Example Adapted From: https://github.com/bluealloy/revm/issues/672 + +use ethers_core::types::BlockId; +use ethers_providers::Middleware; +use ethers_providers::{Http, Provider}; +use indicatif::ProgressBar; +use revm::db::{CacheDB, EthersDB, StateBuilder}; +use revm::inspectors::TracerEip3155; +use revm::primitives::{Address, Env, TransactTo, U256}; +use revm::EVM; +use std::fs::OpenOptions; +use std::io::BufWriter; +use std::io::Write; +use std::sync::Arc; +use std::sync::Mutex; + +macro_rules! local_fill { + ($left:expr, $right:expr, $fun:expr) => { + if let Some(right) = $right { + $left = $fun(right.0) + } + }; + ($left:expr, $right:expr) => { + if let Some(right) = $right { + $left = Address::from(right.as_fixed_bytes()) + } + }; +} + +struct FlushWriter { + writer: Arc>>, +} + +impl FlushWriter { + fn new(writer: Arc>>) -> Self { + Self { writer } + } +} + +impl Write for FlushWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.writer.lock().unwrap().write(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.writer.lock().unwrap().flush() + } +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // Create ethers client and wrap it in Arc + let client = Provider::::try_from( + "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27", + )?; + let client = Arc::new(client); + + // Params + let chain_id: u64 = 1; + let block_number = 10889447; + + // Fetch the transaction-rich block + let block = match client.get_block_with_txs(block_number).await { + Ok(Some(block)) => block, + Ok(None) => anyhow::bail!("Block not found"), + Err(error) => anyhow::bail!("Error: {:?}", error), + }; + println!("Fetched block number: {}", block.number.unwrap().0[0]); + let previous_block_number = block_number - 1; + + // Use the previous block state as the db with caching + let prev_id: BlockId = previous_block_number.into(); + // SAFETY: This cannot fail since this is in the top-level tokio runtime + let state_db = EthersDB::new(Arc::clone(&client), Some(prev_id)).expect("panic"); + let cache_db: CacheDB>> = CacheDB::new(state_db); + let mut state = StateBuilder::new_with_database(cache_db).build(); + let mut evm = EVM::new(); + evm.database(&mut state); + + let mut env = Env::default(); + if let Some(number) = block.number { + let nn = number.0[0]; + env.block.number = U256::from(nn); + } + local_fill!(env.block.coinbase, block.author); + local_fill!(env.block.timestamp, Some(block.timestamp), U256::from_limbs); + local_fill!( + env.block.difficulty, + Some(block.difficulty), + U256::from_limbs + ); + local_fill!(env.block.gas_limit, Some(block.gas_limit), U256::from_limbs); + if let Some(base_fee) = block.base_fee_per_gas { + local_fill!(env.block.basefee, Some(base_fee), U256::from_limbs); + } + + let txs = block.transactions.len(); + println!("Found {txs} transactions."); + + let console_bar = Arc::new(ProgressBar::new(txs as u64)); + let elapsed = std::time::Duration::ZERO; + + // Create the traces directory if it doesn't exist + std::fs::create_dir_all("traces").expect("Failed to create traces directory"); + + // Fill in CfgEnv + env.cfg.chain_id = chain_id; + for tx in block.transactions { + env.tx.caller = Address::from(tx.from.as_fixed_bytes()); + env.tx.gas_limit = tx.gas.as_u64(); + local_fill!(env.tx.gas_price, tx.gas_price, U256::from_limbs); + local_fill!(env.tx.value, Some(tx.value), U256::from_limbs); + env.tx.data = tx.input.0.into(); + let mut gas_priority_fee = U256::ZERO; + local_fill!( + gas_priority_fee, + tx.max_priority_fee_per_gas, + U256::from_limbs + ); + env.tx.gas_priority_fee = Some(gas_priority_fee); + env.tx.chain_id = Some(chain_id); + env.tx.nonce = Some(tx.nonce.as_u64()); + if let Some(access_list) = tx.access_list { + env.tx.access_list = access_list + .0 + .into_iter() + .map(|item| { + let new_keys: Vec = item + .storage_keys + .into_iter() + .map(|h256| U256::from_le_bytes(h256.0)) + .collect(); + (Address::from(item.address.as_fixed_bytes()), new_keys) + }) + .collect(); + } else { + env.tx.access_list = Default::default(); + } + + env.tx.transact_to = match tx.to { + Some(to_address) => TransactTo::Call(Address::from(to_address.as_fixed_bytes())), + None => TransactTo::create(), + }; + + evm.env = env.clone(); + + // Construct the file writer to write the trace to + let tx_number = tx.transaction_index.unwrap().0[0]; + let file_name = format!("traces/{}.json", tx_number); + let write = OpenOptions::new().write(true).create(true).open(file_name); + let inner = Arc::new(Mutex::new(BufWriter::new( + write.expect("Failed to open file"), + ))); + let writer = FlushWriter::new(Arc::clone(&inner)); + + // Inspect and commit the transaction to the EVM + let inspector = TracerEip3155::new(Box::new(writer), true, true); + if let Err(error) = evm.inspect_commit(inspector) { + println!("Got error: {:?}", error); + } + + // Flush the file writer + inner.lock().unwrap().flush().expect("Failed to flush file"); + + console_bar.inc(1); + } + + console_bar.finish_with_message("Finished all transactions."); + println!( + "Finished execution. Total CPU time: {:.6}s", + elapsed.as_secs_f64() + ); + + Ok(()) +} From 7c2938f03fd78a1ecbb9880c3578ae1f773df9d0 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 14 Dec 2023 05:45:34 -0700 Subject: [PATCH 11/66] feat: add serde derives for `CacheDB` under "serde" flag (#911) * add: serde derives under flag * add: serde traits to `EmptyDBTyped` * test: `CacheDB` serialization + deserialization --- crates/revm/src/db/emptydb.rs | 1 + crates/revm/src/db/in_memory_db.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/crates/revm/src/db/emptydb.rs b/crates/revm/src/db/emptydb.rs index dbdb630614..2a8b482623 100644 --- a/crates/revm/src/db/emptydb.rs +++ b/crates/revm/src/db/emptydb.rs @@ -10,6 +10,7 @@ pub type EmptyDB = EmptyDBTyped; /// An empty database that always returns default values when queried. /// /// This is generic over a type which is used as the database error type. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct EmptyDBTyped { _phantom: PhantomData, } diff --git a/crates/revm/src/db/in_memory_db.rs b/crates/revm/src/db/in_memory_db.rs index 196f6d5fbf..d93270b43b 100644 --- a/crates/revm/src/db/in_memory_db.rs +++ b/crates/revm/src/db/in_memory_db.rs @@ -18,6 +18,7 @@ pub type InMemoryDB = CacheDB; /// whereas contracts are identified by their code hash, and are stored in the `contracts` map. /// The [DbAccount] holds the code hash of the contract, which is used to look up the contract in the `contracts` map. #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CacheDB { /// Account info where None means it is not existing. Not existing state is needed for Pre TANGERINE forks. /// `code` is always `None`, and bytecode can be found in `contracts`. @@ -288,6 +289,7 @@ impl DatabaseRef for CacheDB { } #[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DbAccount { pub info: AccountInfo, /// If account is selfdestructed or newly created, storage will be cleared. @@ -330,6 +332,7 @@ impl From for DbAccount { } #[derive(Debug, Clone, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum AccountState { /// Before Spurious Dragon hardfork there was a difference between empty and not existing. /// And we are flagging it here. @@ -453,4 +456,28 @@ mod tests { assert_eq!(new_state.storage(account, key0), Ok(U256::ZERO)); assert_eq!(new_state.storage(account, key1), Ok(value1)); } + + #[cfg(feature = "serde")] + #[test] + fn test_serialize_deserialize_cachedb() { + let account = Address::with_last_byte(69); + let nonce = 420; + let mut init_state = CacheDB::new(EmptyDB::default()); + init_state.insert_account_info( + account, + AccountInfo { + nonce, + ..Default::default() + }, + ); + + let serialized = serde_json::to_string(&init_state).unwrap(); + let deserialized: CacheDB = serde_json::from_str(&serialized).unwrap(); + + assert!(deserialized.accounts.get(&account).is_some()); + assert_eq!( + deserialized.accounts.get(&account).unwrap().info.nonce, + nonce + ); + } } From 4acb57f61d44c4ea5a8158dd5612a41240401659 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:46:17 +0200 Subject: [PATCH 12/66] feat: add some conversions to InstructionResult (#910) --- crates/interpreter/src/instruction_result.rs | 44 +++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index 0202d380de..9ddf33db39 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -1,4 +1,4 @@ -use crate::primitives::{Eval, Halt}; +use crate::primitives::{Eval, Halt, OutOfGasError}; #[repr(u8)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] @@ -49,6 +49,48 @@ pub enum InstructionResult { FatalExternalError, } +impl From for InstructionResult { + fn from(value: Eval) -> Self { + match value { + Eval::Return => InstructionResult::Return, + Eval::Stop => InstructionResult::Stop, + Eval::SelfDestruct => InstructionResult::SelfDestruct, + } + } +} + +impl From for InstructionResult { + fn from(value: Halt) -> Self { + match value { + Halt::OutOfGas(OutOfGasError::BasicOutOfGas) => Self::OutOfGas, + Halt::OutOfGas(OutOfGasError::InvalidOperand) => Self::InvalidOperandOOG, + Halt::OutOfGas(OutOfGasError::Memory) => Self::MemoryOOG, + Halt::OutOfGas(OutOfGasError::MemoryLimit) => Self::MemoryLimitOOG, + Halt::OutOfGas(OutOfGasError::Precompile) => Self::PrecompileOOG, + Halt::OpcodeNotFound => Self::OpcodeNotFound, + Halt::InvalidFEOpcode => Self::InvalidFEOpcode, + Halt::InvalidJump => Self::InvalidJump, + Halt::NotActivated => Self::NotActivated, + Halt::StackOverflow => Self::StackOverflow, + Halt::StackUnderflow => Self::StackUnderflow, + Halt::OutOfOffset => Self::OutOfOffset, + Halt::CreateCollision => Self::CreateCollision, + Halt::PrecompileError => Self::PrecompileError, + Halt::NonceOverflow => Self::NonceOverflow, + Halt::CreateContractSizeLimit => Self::CreateContractSizeLimit, + Halt::CreateContractStartingWithEF => Self::CreateContractStartingWithEF, + Halt::CreateInitCodeSizeLimit => Self::CreateInitCodeSizeLimit, + Halt::OverflowPayment => Self::OverflowPayment, + Halt::StateChangeDuringStaticCall => Self::StateChangeDuringStaticCall, + Halt::CallNotAllowedInsideStatic => Self::CallNotAllowedInsideStatic, + Halt::OutOfFund => Self::OutOfFund, + Halt::CallTooDeep => Self::CallTooDeep, + #[cfg(feature = "optimism")] + Halt::FailedDeposit => Self::FatalExternalError, + } + } +} + impl InstructionResult { /// Returns whether the result is a success. #[inline] From 83718d9e75ab06d9c0df2b23b6004553051106cd Mon Sep 17 00:00:00 2001 From: "refcell.eth" Date: Thu, 14 Dec 2023 05:52:46 -0700 Subject: [PATCH 13/66] chore(docs): Update the benchmark docs to point to revm package (#906) benchmarks. --- README.md | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d1671d5372..857c9161f6 100644 --- a/README.md +++ b/README.md @@ -66,23 +66,32 @@ run tests with command: `cargo run --release -- statetest tests/GeneralStateTest ## Running benchmarks -TODO needs to be updated. Benches can now be found inside `crates/revm/benches` +Benches can be found in [`crates/revm/benches`](./crates/revm/benches). + +Currently, available benches include the following. +- *analysis* +- *snailtracer* +- *transfer* + +To run the `snailtracer` bench, execute the `cargo bench` subcommand below. ```shell -cargo run --package revm-test --release --bin snailtracer +cargo bench --package revm --profile release -- snailtracer ``` -The following flamegraph will require installing [flamegraph] by running `cargo install flamegraph`. - -[flamegraph]: https://docs.rs/crate/flamegraph/0.1.6 +Using [flamegraph][flamegraph], you can create a visualization breaking down the runtime of various +sections of the bench execution - a flame graph. Executing the `cargo flamegraph` subcommand requires +installing [flamegraph][flamegraph] by running `cargo install flamegraph`. ```shell -cargo flamegraph --root --freq 4000 --min-width 0.001 --package revm-test --bin snailtracer +cargo flamegraph --root --freq 4000 --min-width 0.001 --package revm --bench bench -- snailtracer ``` This command will produce a flamegraph image output to `flamegraph.svg`. Flamegraph also requires sudo mode to run (hence the `--root` cli arg) and will prompt you for your password if not in sudo mode already. +[flamegraph]: https://docs.rs/crate/flamegraph/0.1.6 + ## Running examples ```shell From be133e635b1a4e1cd2916e502c5317963d4630cc Mon Sep 17 00:00:00 2001 From: PA <50184410+peyha@users.noreply.github.com> Date: Sat, 16 Dec 2023 20:30:54 +0100 Subject: [PATCH 14/66] feat(revm): implement prepend_state for BundleState (#907) * feat(revm): implement prepend_state for BundleState * refactor(revm): implement extend_state for BundleState * fix(revm): format for ci * refactor(revm): ci --- crates/revm/src/db/states/bundle_state.rs | 137 ++++++++++++++++------ 1 file changed, 100 insertions(+), 37 deletions(-) diff --git a/crates/revm/src/db/states/bundle_state.rs b/crates/revm/src/db/states/bundle_state.rs index 1cab208038..fe1e2daf2c 100644 --- a/crates/revm/src/db/states/bundle_state.rs +++ b/crates/revm/src/db/states/bundle_state.rs @@ -7,7 +7,7 @@ use alloc::{ collections::{BTreeMap, BTreeSet}, vec::Vec, }; -use core::ops::RangeInclusive; +use core::{mem, ops::RangeInclusive}; use revm_interpreter::primitives::{ hash_map::{self, Entry}, AccountInfo, Address, Bytecode, HashMap, HashSet, StorageSlot, B256, KECCAK_EMPTY, U256, @@ -515,6 +515,44 @@ impl BundleState { (plain_state, reverts.into_plain_state_reverts()) } + /// Extend the bundle with other state + /// + /// Update the `other` state only if `other` is not flagged as destroyed. + pub fn extend_state(&mut self, other_state: HashMap) { + for (address, other_account) in other_state { + match self.state.entry(address) { + hash_map::Entry::Occupied(mut entry) => { + let this = entry.get_mut(); + self.state_size -= this.size_hint(); + + // if other was destroyed. replace `this` storage with + // the `other one. + if other_account.was_destroyed() { + this.storage = other_account.storage; + } else { + // otherwise extend this storage with other + for (key, storage_slot) in other_account.storage { + // update present value or insert storage slot. + this.storage + .entry(key) + .or_insert(storage_slot) + .present_value = storage_slot.present_value; + } + } + this.info = other_account.info; + this.status.transition(other_account.status); + + // Update the state size + self.state_size += this.size_hint(); + } + hash_map::Entry::Vacant(entry) => { + // just insert if empty + self.state_size += other_account.size_hint(); + entry.insert(other_account); + } + } + } + } /// Extend the state with state that is build on top of it. /// /// If storage was wiped in `other` state, copy `this` plain state @@ -523,8 +561,6 @@ impl BundleState { /// If `this` and `other` accounts were both destroyed invalidate second /// wipe flag (from `other`). As wiping from database should be done only once /// and we already transferred all potentially missing storages to the `other` revert. - /// - /// Additionally update the `other` state only if `other` is not flagged as destroyed. pub fn extend(&mut self, mut other: Self) { // iterate over reverts and if its storage is wiped try to add previous bundle // state as there is potential missing slots. @@ -552,40 +588,8 @@ impl BundleState { // Increment reverts size for each of the updated reverts. self.reverts_size += revert.size_hint(); } - - for (address, other_account) in other.state { - match self.state.entry(address) { - hash_map::Entry::Occupied(mut entry) => { - let this = entry.get_mut(); - self.state_size -= this.size_hint(); - - // if other was destroyed. replace `this` storage with - // the `other one. - if other_account.was_destroyed() { - this.storage = other_account.storage; - } else { - // otherwise extend this storage with other - for (key, storage_slot) in other_account.storage { - // update present value or insert storage slot. - this.storage - .entry(key) - .or_insert(storage_slot) - .present_value = storage_slot.present_value; - } - } - this.info = other_account.info; - this.status.transition(other_account.status); - - // Update the state size - self.state_size += this.size_hint(); - } - hash_map::Entry::Vacant(entry) => { - // just insert if empty - self.state_size += other_account.size_hint(); - entry.insert(other_account); - } - } - } + // Extension of state + self.extend_state(other.state); // Contract can be just extended, when counter is introduced we will take into account that. self.contracts.extend(other.contracts); // Reverts can be just extended @@ -658,6 +662,21 @@ impl BundleState { } } } + + /// Prepends present the state with the given BundleState. + /// It adds changes from the given state but does not override any existing changes. + /// + /// Reverts are not updated. + pub fn prepend_state(&mut self, mut other: BundleState) { + // take this bundle + let this_bundle = mem::take(self); + // extend other bundle state with this + other.extend_state(this_bundle.state); + // extend other contracts + other.contracts.extend(this_bundle.contracts); + // swap bundles + mem::swap(self, &mut other) + } } #[cfg(test)] @@ -1000,4 +1019,48 @@ mod tests { let taken_reverts = extended.take_n_reverts(1); assert_eq!(taken_reverts, bundle2.reverts); } + + #[test] + fn prepend_state() { + let address1 = account1(); + let address2 = account2(); + + let account1 = AccountInfo { + nonce: 1, + ..Default::default() + }; + let account1_changed = AccountInfo { + nonce: 1, + ..Default::default() + }; + let account2 = AccountInfo { + nonce: 1, + ..Default::default() + }; + + let present_state = BundleState::builder(2..=2) + .state_present_account_info(address1, account1_changed.clone()) + .build(); + assert_eq!(present_state.reverts.len(), 1); + let previous_state = BundleState::builder(1..=1) + .state_present_account_info(address1, account1) + .state_present_account_info(address2, account2.clone()) + .build(); + assert_eq!(previous_state.reverts.len(), 1); + + let mut test = present_state; + + test.prepend_state(previous_state); + + assert_eq!(test.state.len(), 2); + // reverts num should stay the same. + assert_eq!(test.reverts.len(), 1); + // account1 is not overwritten. + assert_eq!( + test.state.get(&address1).unwrap().info, + Some(account1_changed) + ); + // account2 got inserted + assert_eq!(test.state.get(&address2).unwrap().info, Some(account2)); + } } From 40b6876ba2e162b5edbd65ebf17d87b72df86d0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 09:34:10 +0100 Subject: [PATCH 15/66] chore(deps): bump thiserror from 1.0.50 to 1.0.51 (#915) Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.50 to 1.0.51. - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.50...1.0.51) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c2fb89b11..6c82af5e09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3089,18 +3089,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" dependencies = [ "proc-macro2", "quote", From e858c57ecf29f5d4b24f469ed2a76ed5b102ca33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 11:03:38 +0100 Subject: [PATCH 16/66] chore(deps): bump tokio from 1.34.0 to 1.35.0 (#909) Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.34.0 to 1.35.0. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.34.0...tokio-1.35.0) --- updated-dependencies: - dependency-name: tokio dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- crates/revm/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c82af5e09..c90f592d6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3181,9 +3181,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" dependencies = [ "backtrace", "bytes", diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 85c1d6c7b1..fe8afc92d2 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -25,7 +25,7 @@ serde = { version = "1.0", features = ["derive", "rc"], optional = true } serde_json = { version = "1.0", features = ["preserve_order"], optional = true } # ethersdb -tokio = { version = "1.34", features = [ +tokio = { version = "1.35", features = [ "rt-multi-thread", "macros", ], optional = true } From d4213159e14fb2105fb2fb7a7b6a8000518485fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 12:27:41 +0100 Subject: [PATCH 17/66] chore(deps): bump once_cell from 1.18.0 to 1.19.0 (#908) Bumps [once_cell](https://github.com/matklad/once_cell) from 1.18.0 to 1.19.0. - [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md) - [Commits](https://github.com/matklad/once_cell/compare/v1.18.0...v1.19.0) --- updated-dependencies: - dependency-name: once_cell dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- crates/precompile/Cargo.toml | 2 +- crates/primitives/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c90f592d6a..b525af3b7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1888,9 +1888,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index 88a2aad9f0..fef719e402 100644 --- a/crates/precompile/Cargo.toml +++ b/crates/precompile/Cargo.toml @@ -15,7 +15,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] revm-primitives = { path = "../primitives", version = "1.3.0", default-features = false } bn = { package = "substrate-bn", version = "0.6", default-features = false } -once_cell = { version = "1.17", default-features = false, features = ["alloc"] } +once_cell = { version = "1.19", default-features = false, features = ["alloc"] } ripemd = { version = "0.1", default-features = false } sha2 = { version = "0.10", default-features = false } # modexp precompile diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 02fd54929c..29c089db18 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -28,7 +28,7 @@ bitflags = { version = "2.4.1", default-features = false } # For setting the CfgEnv KZGSettings. Enabled by c-kzg flag. c-kzg = { version = "0.4.0", default-features = false, optional = true } -once_cell = { version = "1.18", default-features = false, optional = true } +once_cell = { version = "1.19", default-features = false, optional = true } # utility enumn = "0.1" From 67331de7aae47499a35e1907a67692c9d923aa15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 12:27:51 +0100 Subject: [PATCH 18/66] chore(deps): bump alloy-primitives from 0.5.2 to 0.5.3 (#914) Bumps [alloy-primitives](https://github.com/alloy-rs/core) from 0.5.2 to 0.5.3. - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.5.2...v0.5.3) --- updated-dependencies: - dependency-name: alloy-primitives dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b525af3b7c..12892d46bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,9 +46,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-primitives" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ca2c09d5911548a5cb620382ea0e1af99d3c898ce0efecbbd274a4676cf53e" +checksum = "6c0e5e60ff0e0c34c553822dabcfe0f5fece5a8c52f08a915be8c737de4b03fa" dependencies = [ "alloy-rlp", "arbitrary", From 42caf5537f155bddb18dc4b4abfccc478519881c Mon Sep 17 00:00:00 2001 From: Iaroslav Mazur Date: Mon, 25 Dec 2023 11:20:47 +0100 Subject: [PATCH 19/66] perf(primitives): optimize AccountInfo.is_empty() (#922) * perf(primitives): optimize AccountInfo.is_empty() This is a small optimization for AccountInfo.is_empty() with regards to the short-circuiting of bool operators in Rust. * fix(primitives): incorrect use of the cfg attribute --- crates/primitives/src/env.rs | 4 ++-- crates/primitives/src/state.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index aa9fe202f3..3f1b4e44eb 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -352,12 +352,12 @@ impl CfgEnv { false } - #[cfg(feaure = "optional_beneficiary_reward")] + #[cfg(feature = "optional_beneficiary_reward")] pub fn is_beneficiary_reward_disabled(&self) -> bool { self.disable_beneficiary_reward } - #[cfg(not(feaure = "optional_beneficiary_reward"))] + #[cfg(not(feature = "optional_beneficiary_reward"))] pub fn is_beneficiary_reward_disabled(&self) -> bool { false } diff --git a/crates/primitives/src/state.rs b/crates/primitives/src/state.rs index cf80c56bff..401dbd6cc3 100644 --- a/crates/primitives/src/state.rs +++ b/crates/primitives/src/state.rs @@ -239,7 +239,7 @@ impl AccountInfo { /// - nonce is zero pub fn is_empty(&self) -> bool { let code_empty = self.is_empty_code_hash() || self.code_hash == B256::ZERO; - self.balance == U256::ZERO && self.nonce == 0 && code_empty + code_empty && self.balance == U256::ZERO && self.nonce == 0 } /// Returns `true` if the account is not empty. From ea24fc8c0c123bb52b509dddf0bddc9c31fe3f46 Mon Sep 17 00:00:00 2001 From: Iaroslav Mazur Date: Mon, 25 Dec 2023 11:21:11 +0100 Subject: [PATCH 20/66] fix(primitives): incorrect use of the cfg attribute (#923) From 82c0a891dab7393e41de954d9744f96024d0f29c Mon Sep 17 00:00:00 2001 From: Iaroslav Mazur Date: Mon, 25 Dec 2023 11:24:48 +0100 Subject: [PATCH 21/66] chore(interpreter): conditionally enable `optional_beneficiary_reward` (#925) - enable the `optional_beneficiary_reward` feature automatically when the `dev` feature is enabled. --- crates/interpreter/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index 23266fbbde..6c8e58f72c 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -34,6 +34,7 @@ dev = [ "optional_eip3607", "optional_gas_refund", "optional_no_base_fee", + "optional_beneficiary_reward", ] memory_limit = ["revm-primitives/memory_limit"] optional_balance_check = ["revm-primitives/optional_balance_check"] From 0b3cafc05ebf75276bea4148ec6c61803d6d2e6c Mon Sep 17 00:00:00 2001 From: Iaroslav Mazur Date: Mon, 25 Dec 2023 11:25:03 +0100 Subject: [PATCH 22/66] docs(interpreter): fix the name of the macro referenced by record_memory() (#926) --- crates/interpreter/src/gas.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/interpreter/src/gas.rs b/crates/interpreter/src/gas.rs index 2d9ebd6f05..1042262a97 100644 --- a/crates/interpreter/src/gas.rs +++ b/crates/interpreter/src/gas.rs @@ -100,7 +100,7 @@ impl Gas { true } - /// used in memory_resize! macro to record gas used for memory expansion. + /// used in shared_memory_resize! macro to record gas used for memory expansion. #[inline] pub fn record_memory(&mut self, gas_memory: u64) -> bool { if gas_memory > self.memory { From 96813c53332059e5cb25d5b180a7d13269b54002 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Dec 2023 18:31:20 +0100 Subject: [PATCH 23/66] chore(deps): bump tokio from 1.35.0 to 1.35.1 (#920) Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.35.0 to 1.35.1. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.35.0...tokio-1.35.1) --- updated-dependencies: - dependency-name: tokio dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 12892d46bb..a9c1fdd0f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3181,9 +3181,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", From 8cb1905ef6e14299080f112fec14144fd40b812f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Dec 2023 18:31:25 +0100 Subject: [PATCH 24/66] chore(deps): bump anyhow from 1.0.75 to 1.0.76 (#921) Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.75 to 1.0.76. - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](https://github.com/dtolnay/anyhow/compare/1.0.75...1.0.76) --- updated-dependencies: - dependency-name: anyhow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- crates/revm/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9c1fdd0f6..b5aef164f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,9 +114,9 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" [[package]] name = "arbitrary" diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index fe8afc92d2..10e8e09a27 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -35,7 +35,7 @@ futures = { version = "0.3.29", optional = true } [dev-dependencies] ethers-contract = { version = "2.0.11", default-features = false } -anyhow = "1.0.75" +anyhow = "1.0.76" criterion = "0.5" indicatif = "0.17" From 5ad9d3f52a6c7ce0dd9af7717a54521f40889c75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Dec 2023 18:31:32 +0100 Subject: [PATCH 25/66] chore(deps): bump alloy-rlp from 0.3.3 to 0.3.4 (#928) Bumps [alloy-rlp](https://github.com/alloy-rs/rlp) from 0.3.3 to 0.3.4. - [Changelog](https://github.com/alloy-rs/rlp/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/rlp/compare/v0.3.3...v0.3.4) --- updated-dependencies: - dependency-name: alloy-rlp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b5aef164f5..748e4a884a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,21 +70,20 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc0fac0fc16baf1f63f78b47c3d24718f3619b0714076f6a02957d808d52cbef" +checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" dependencies = [ "alloy-rlp-derive", "arrayvec", "bytes", - "smol_str", ] [[package]] name = "alloy-rlp-derive" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0391754c09fab4eae3404d19d0d297aa1c670c1775ab51d8a5312afeca23157" +checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", @@ -2888,15 +2887,6 @@ version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" -[[package]] -name = "smol_str" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" -dependencies = [ - "serde", -] - [[package]] name = "socket2" version = "0.4.10" From 55c67064757d61dc9e7512af6e50baa77ba33189 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Dec 2023 18:31:44 +0100 Subject: [PATCH 26/66] chore(deps): bump futures from 0.3.29 to 0.3.30 (#927) Bumps [futures](https://github.com/rust-lang/futures-rs) from 0.3.29 to 0.3.30. - [Release notes](https://github.com/rust-lang/futures-rs/releases) - [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.29...0.3.30) --- updated-dependencies: - dependency-name: futures dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 36 ++++++++++++++++++------------------ crates/revm/Cargo.toml | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 748e4a884a..a173109f3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1143,9 +1143,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -1158,9 +1158,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -1168,15 +1168,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -1185,15 +1185,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", @@ -1202,15 +1202,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" @@ -1224,9 +1224,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 10e8e09a27..54f0b89b61 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -31,7 +31,7 @@ tokio = { version = "1.35", features = [ ], optional = true } ethers-providers = { version = "2.0", optional = true } ethers-core = { version = "2.0", optional = true } -futures = { version = "0.3.29", optional = true } +futures = { version = "0.3.30", optional = true } [dev-dependencies] ethers-contract = { version = "2.0.11", default-features = false } From 3a829d341888f337130cea513de7281b09c73f62 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Dec 2023 23:37:54 +0100 Subject: [PATCH 27/66] chore(deps): bump thiserror from 1.0.51 to 1.0.52 (#929) Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.51 to 1.0.52. - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.51...1.0.52) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a173109f3f..4933e33d08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3079,18 +3079,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" dependencies = [ "proc-macro2", "quote", From 8a6fab90c3a2462927889fba085caa8d6db632e6 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 29 Dec 2023 12:21:35 +0100 Subject: [PATCH 28/66] chore: relax Bytes requirement and use slice instead (#937) --- crates/revm/src/optimism.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index e6d1f093c8..a2d511b3b3 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -1,6 +1,6 @@ //! Optimism-specific constants, types, and helpers. -use crate::primitives::{address, db::Database, Address, Bytes, Spec, SpecId, U256}; +use crate::primitives::{address, db::Database, Address, Spec, SpecId, U256}; use core::ops::Mul; const ZERO_BYTE_COST: u64 = 4; @@ -59,7 +59,7 @@ 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: &Bytes) -> U256 { + pub fn data_gas(&self, input: &[u8]) -> U256 { let mut rollup_data_gas_cost = U256::from(input.iter().fold(0, |acc, byte| { acc + if *byte == 0x00 { ZERO_BYTE_COST @@ -77,7 +77,7 @@ impl L1BlockInfo { } /// Calculate the gas cost of a transaction based on L1 block data posted on L2 - pub fn calculate_tx_l1_cost(&self, input: &Bytes) -> U256 { + pub fn calculate_tx_l1_cost(&self, input: &[u8]) -> U256 { // If the input is not a deposit transaction, the default value is zero. if input.is_empty() || input.first() == Some(&0x7F) { return U256::ZERO; From b97bbf7d19ac7f9c34a3671638e1a6dea38afe10 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Fri, 29 Dec 2023 12:26:05 +0100 Subject: [PATCH 29/66] fix: make revm-primitives no-std with c-kzg feature (#933) --- Cargo.lock | 34 ++++++++++++------- .../src/kzg/trusted_setup_points.rs | 5 +-- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4933e33d08..fd8e91d9af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,9 +46,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-primitives" -version = "0.5.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c0e5e60ff0e0c34c553822dabcfe0f5fece5a8c52f08a915be8c737de4b03fa" +checksum = "08ca2c09d5911548a5cb620382ea0e1af99d3c898ce0efecbbd274a4676cf53e" dependencies = [ "alloy-rlp", "arbitrary", @@ -70,20 +70,21 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" +checksum = "cc0fac0fc16baf1f63f78b47c3d24718f3619b0714076f6a02957d808d52cbef" dependencies = [ "alloy-rlp-derive", "arrayvec", "bytes", + "smol_str", ] [[package]] name = "alloy-rlp-derive" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" +checksum = "c0391754c09fab4eae3404d19d0d297aa1c670c1775ab51d8a5312afeca23157" dependencies = [ "proc-macro2", "quote", @@ -113,9 +114,9 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anyhow" -version = "1.0.76" +version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" +checksum = "c9d19de80eff169429ac1e9f48fffb163916b448a44e8e046186232046d9e1f9" [[package]] name = "arbitrary" @@ -2887,6 +2888,15 @@ version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", +] + [[package]] name = "socket2" version = "0.4.10" @@ -3079,18 +3089,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.52" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.52" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", diff --git a/crates/primitives/src/kzg/trusted_setup_points.rs b/crates/primitives/src/kzg/trusted_setup_points.rs index 949d2bb660..70b422c81e 100644 --- a/crates/primitives/src/kzg/trusted_setup_points.rs +++ b/crates/primitives/src/kzg/trusted_setup_points.rs @@ -1,7 +1,7 @@ +use alloc::boxed::Box; pub use c_kzg::{BYTES_PER_G1_POINT, BYTES_PER_G2_POINT}; use core::fmt::Display; use derive_more::{AsMut, AsRef, Deref, DerefMut}; -use std::error::Error; /// Number of G1 Points. pub const NUM_G1_POINTS: usize = 4096; @@ -129,4 +129,5 @@ impl Display for KzgErrors { } } -impl Error for KzgErrors {} +#[cfg(feature = "std")] +impl std::error::Error for KzgErrors {} From eff379d817753bf0e5025df6e79be558e3cefce9 Mon Sep 17 00:00:00 2001 From: 0xkratos <132500484+0xkr8os@users.noreply.github.com> Date: Fri, 29 Dec 2023 10:05:41 -0600 Subject: [PATCH 30/66] feat(ethersdb): propagate errors instead of panicking in basic_ref (#935) --- crates/revm/src/db/ethersdb.rs | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/crates/revm/src/db/ethersdb.rs b/crates/revm/src/db/ethersdb.rs index a82107fac2..0c24416429 100644 --- a/crates/revm/src/db/ethersdb.rs +++ b/crates/revm/src/db/ethersdb.rs @@ -48,7 +48,7 @@ impl EthersDB { } impl DatabaseRef for EthersDB { - type Error = (); + type Error = M::Error; fn basic_ref(&self, address: Address) -> Result, Self::Error> { let add = eH160::from(address.0 .0); @@ -60,22 +60,12 @@ impl DatabaseRef for EthersDB { tokio::join!(nonce, balance, code) }; let (nonce, balance, code) = self.block_on(f); - // panic on not getting data? - let bytecode = code.unwrap_or_else(|e| panic!("ethers get code error: {e:?}")); - let bytecode = Bytecode::new_raw(bytecode.0.into()); + + let balance = U256::from_limbs(balance?.0); + let nonce = nonce?.as_u64(); + let bytecode = Bytecode::new_raw(code?.0.into()); let code_hash = bytecode.hash_slow(); - Ok(Some(AccountInfo::new( - U256::from_limbs( - balance - .unwrap_or_else(|e| panic!("ethers get balance error: {e:?}")) - .0, - ), - nonce - .unwrap_or_else(|e| panic!("ethers get nonce error: {e:?}")) - .as_u64(), - code_hash, - bytecode, - ))) + Ok(Some(AccountInfo::new(balance, nonce, code_hash, bytecode))) } fn code_by_hash_ref(&self, _code_hash: B256) -> Result { @@ -115,7 +105,7 @@ impl DatabaseRef for EthersDB { } impl Database for EthersDB { - type Error = (); + type Error = M::Error; #[inline] fn basic(&mut self, address: Address) -> Result, Self::Error> { From 5a8aa99525a53b3c132d18341c0d94ecdcca577e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 Dec 2023 17:05:47 +0100 Subject: [PATCH 31/66] chore(deps): bump alloy-primitives from 0.5.3 to 0.5.4 (#934) Bumps [alloy-primitives](https://github.com/alloy-rs/core) from 0.5.3 to 0.5.4. - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/core/compare/v0.5.3...v0.5.4) --- updated-dependencies: - dependency-name: alloy-primitives dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd8e91d9af..20c545d119 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,9 +46,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-primitives" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ca2c09d5911548a5cb620382ea0e1af99d3c898ce0efecbbd274a4676cf53e" +checksum = "9c234f92024707f224510ff82419b2be0e1d8e1fd911defcac5a085cd7f83898" dependencies = [ "alloy-rlp", "arbitrary", @@ -60,6 +60,7 @@ dependencies = [ "ethereum_ssz", "hex-literal", "itoa", + "keccak-asm", "proptest", "proptest-derive", "rand", @@ -1651,6 +1652,16 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keccak-asm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb8515fff80ed850aea4a1595f2e519c003e2a00a82fe168ebf5269196caf444" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -2845,6 +2856,16 @@ dependencies = [ "keccak", ] +[[package]] +name = "sha3-asm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac61da6b35ad76b195eb4771210f947734321a8d81d7738e1580d953bc7a15e" +dependencies = [ + "cc", + "cfg-if", +] + [[package]] name = "shlex" version = "1.2.0" From e31753247f5f2159b203e9af0bc623bdc35f7ce1 Mon Sep 17 00:00:00 2001 From: Aditya <60684641+0x0elliot@users.noreply.github.com> Date: Sat, 30 Dec 2023 20:35:13 +0530 Subject: [PATCH 32/66] feat(ci) #797: Comment output of cachegrind in PR (#932) * ci: adding cachegrind to PRs! * ci: making it trigger everytime a PR opens and a comment is made * ci: simplifying run rules * ci: making it work * ci: triggering again to see * ci: fixing arguments * ci: installing valgrind * ci: installing cg_annotate * ci: playing around a bit more * ci: this should get the first working comment * ci: this should get the first working comment (1) * ci: this should get the first working comment (2 :P) * ci: this should get the first working comment (3 :P) * ci: trying again lol * ci: trying again lol * ci: trying again lol * ci: trying again lol * ci: trying again lol * ci: trying again lol * ci: debugging * ci: debugging * ci: debugging * ci: debugging * ci: debugging * ci: debugging * ci: debugging * ci: debugging * ci: debugging * ci: debugging * ci: debugging * ci: debugging * ci: debugging * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: sigh * ci: removing garbage * ci: adding edit last flag * ci: testing around with no previous comment edge case * ci: testing with permissions * ci: giving workflow further more permissions * ci: testing workflow --- .github/workflows/cachegrind.yml | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/cachegrind.yml diff --git a/.github/workflows/cachegrind.yml b/.github/workflows/cachegrind.yml new file mode 100644 index 0000000000..e64e0e922a --- /dev/null +++ b/.github/workflows/cachegrind.yml @@ -0,0 +1,49 @@ +name: Valgrind Cachegrind + +on: + pull_request: + branches: [main] + +jobs: + valgrind: + runs-on: ubuntu-latest + permissions: write-all + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup | Rust + uses: ATiltedTree/setup-rust@v1 + with: + rust-version: stable + + - name: Install Valgrind + run: | + sudo apt-get update + sudo apt-get install -y valgrind + + - name: Run Valgrind + run: | + cargo b -r -p revm-test --bin snailtracer + valgrind --tool=cachegrind target/release/snailtracer 2>&1 | tee cachegrind_results.txt + + - name: Valgrind results + id: valgrind_results + run: | + contents=$(printf "%s" "$(head -c 64000 cachegrind_results.txt)") + # dump line by line into a file + printf "Valgrind Results:\n\n\`\`\`\n%s\n\`\`\`" "$contents" > results.md + + - name: Comment on PR + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Try to edit the last comment + if gh pr comment ${{ github.event.pull_request.number }} --edit-last --body-file results.md; then + echo "Successfully edited last comment." + else + echo "Failed to edit last comment. Trying to add a new comment instead!" + # If editing last comment fails, try to add a new comment + gh pr comment ${{ github.event.pull_request.number }} --body-file results.md + fi \ No newline at end of file From d33cb21d036882fa9c5d3d832a054f52360759ea Mon Sep 17 00:00:00 2001 From: Iaroslav Mazur Date: Sat, 30 Dec 2023 16:51:05 +0100 Subject: [PATCH 33/66] refactor(interpreter): simplify the logic of calc.new_cost() (#939) --- crates/interpreter/src/gas/calc.rs | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index 98d26948ab..e5b627b7e7 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -311,22 +311,16 @@ fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 { #[inline] fn new_cost(is_call_or_staticcall: bool, is_new: bool, transfers_value: bool) -> u64 { - if is_call_or_staticcall { - // EIP-161: State trie clearing (invariant-preserving alternative) - if SPEC::enabled(SPURIOUS_DRAGON) { - if transfers_value && is_new { - NEWACCOUNT - } else { - 0 - } - } else if is_new { - NEWACCOUNT - } else { - 0 - } - } else { - 0 + if !is_call_or_staticcall || !is_new { + return 0; } + + // EIP-161: State trie clearing (invariant-preserving alternative) + if SPEC::enabled(SPURIOUS_DRAGON) && !transfers_value { + return 0; + } + + NEWACCOUNT } #[inline] From 75e95675e32e1f16923502829720a174448d5569 Mon Sep 17 00:00:00 2001 From: Iaroslav Mazur Date: Sat, 30 Dec 2023 17:41:04 +0100 Subject: [PATCH 34/66] refactor(primitives): improve readability of Env.validate_tx() (#924) --- crates/primitives/src/env.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index 3f1b4e44eb..de816f2cea 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -75,10 +75,6 @@ impl Env { } } - let gas_limit = self.tx.gas_limit; - let effective_gas_price = self.effective_gas_price(); - let is_create = self.tx.transact_to.is_create(); - // BASEFEE tx check if SPEC::enabled(SpecId::LONDON) { if let Some(priority_fee) = self.tx.gas_priority_fee { @@ -87,21 +83,24 @@ impl Env { return Err(InvalidTransaction::PriorityFeeGreaterThanMaxFee); } } - let basefee = self.block.basefee; // check minimal cost against basefee - if !self.cfg.is_base_fee_check_disabled() && effective_gas_price < basefee { + if !self.cfg.is_base_fee_check_disabled() + && self.effective_gas_price() < self.block.basefee + { return Err(InvalidTransaction::GasPriceLessThanBasefee); } } // Check if gas_limit is more than block_gas_limit - if !self.cfg.is_block_gas_limit_disabled() && U256::from(gas_limit) > self.block.gas_limit { + if !self.cfg.is_block_gas_limit_disabled() + && U256::from(self.tx.gas_limit) > self.block.gas_limit + { return Err(InvalidTransaction::CallerGasLimitMoreThanBlock); } // EIP-3860: Limit and meter initcode - if SPEC::enabled(SpecId::SHANGHAI) && is_create { + if SPEC::enabled(SpecId::SHANGHAI) && self.tx.transact_to.is_create() { let max_initcode_size = self .cfg .limit_contract_code_size @@ -136,7 +135,6 @@ impl Env { } // there must be at least one blob - // assert len(tx.blob_versioned_hashes) > 0 if self.tx.blob_hashes.is_empty() { return Err(InvalidTransaction::EmptyBlobs); } From 9c85dbe42c255d1d9620725cd1e8de6355d47846 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 01:01:37 +0100 Subject: [PATCH 35/66] chore(deps): bump alloy-rlp from 0.3.3 to 0.3.4 (#944) Bumps [alloy-rlp](https://github.com/alloy-rs/rlp) from 0.3.3 to 0.3.4. - [Changelog](https://github.com/alloy-rs/rlp/blob/main/CHANGELOG.md) - [Commits](https://github.com/alloy-rs/rlp/compare/v0.3.3...v0.3.4) --- updated-dependencies: - dependency-name: alloy-rlp dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20c545d119..a93955f4a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,21 +71,20 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc0fac0fc16baf1f63f78b47c3d24718f3619b0714076f6a02957d808d52cbef" +checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" dependencies = [ "alloy-rlp-derive", "arrayvec", "bytes", - "smol_str", ] [[package]] name = "alloy-rlp-derive" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0391754c09fab4eae3404d19d0d297aa1c670c1775ab51d8a5312afeca23157" +checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", @@ -2909,15 +2908,6 @@ version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" -[[package]] -name = "smol_str" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" -dependencies = [ - "serde", -] - [[package]] name = "socket2" version = "0.4.10" From f0c98bfb37d7ff11b0860e6ec2cfdbcc2040ca96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 01:01:43 +0100 Subject: [PATCH 36/66] chore(deps): bump enumn from 0.1.12 to 0.1.13 (#946) Bumps [enumn](https://github.com/dtolnay/enumn) from 0.1.12 to 0.1.13. - [Release notes](https://github.com/dtolnay/enumn/releases) - [Commits](https://github.com/dtolnay/enumn/compare/0.1.12...0.1.13) --- updated-dependencies: - dependency-name: enumn dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a93955f4a6..2777a5a6a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,7 +88,7 @@ checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", ] [[package]] @@ -262,7 +262,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", ] [[package]] @@ -373,7 +373,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.39", + "syn 2.0.46", "which", ] @@ -805,7 +805,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", ] [[package]] @@ -916,13 +916,13 @@ dependencies = [ [[package]] name = "enumn" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" +checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", ] [[package]] @@ -1198,7 +1198,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", ] [[package]] @@ -1878,7 +1878,7 @@ dependencies = [ "proc-macro-crate 2.0.0", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", ] [[package]] @@ -2024,7 +2024,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", ] [[package]] @@ -2111,7 +2111,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 2.0.39", + "syn 2.0.46", ] [[package]] @@ -2173,9 +2173,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" dependencies = [ "unicode-ident", ] @@ -2219,9 +2219,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -2796,7 +2796,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", ] [[package]] @@ -3005,7 +3005,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.39", + "syn 2.0.46", ] [[package]] @@ -3040,9 +3040,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e" dependencies = [ "proc-macro2", "quote", @@ -3115,7 +3115,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", ] [[package]] @@ -3215,7 +3215,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", ] [[package]] @@ -3310,7 +3310,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", ] [[package]] @@ -3540,7 +3540,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", "wasm-bindgen-shared", ] @@ -3574,7 +3574,7 @@ checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3840,7 +3840,7 @@ checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", ] [[package]] @@ -3860,5 +3860,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.46", ] From 6a2941344c64a053305985bf71919b4055848d2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 01:01:49 +0100 Subject: [PATCH 37/66] chore(deps): bump thiserror from 1.0.50 to 1.0.55 (#947) Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.50 to 1.0.55. - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.50...1.0.55) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2777a5a6a3..54aea51494 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3100,18 +3100,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" dependencies = [ "proc-macro2", "quote", From b9c9e9d5013eaf50108440c1da5641485bd4e17f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 01:02:00 +0100 Subject: [PATCH 38/66] chore(deps): bump serde_json from 1.0.108 to 1.0.110 (#949) Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.108 to 1.0.110. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.108...v1.0.110) --- updated-dependencies: - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54aea51494..7d086af6cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2781,18 +2781,18 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" dependencies = [ "proc-macro2", "quote", @@ -2801,9 +2801,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257" dependencies = [ "indexmap", "itoa", From 669d82f3a37f7128d60259ca08865f17fad0ed5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 01:02:10 +0100 Subject: [PATCH 39/66] chore(deps): bump anyhow from 1.0.77 to 1.0.79 (#950) Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.77 to 1.0.79. - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](https://github.com/dtolnay/anyhow/compare/1.0.77...1.0.79) --- updated-dependencies: - dependency-name: anyhow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- crates/revm/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d086af6cd..9b5765bfe0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,9 +114,9 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anyhow" -version = "1.0.77" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9d19de80eff169429ac1e9f48fffb163916b448a44e8e046186232046d9e1f9" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "arbitrary" diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 54f0b89b61..53edafc000 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -35,7 +35,7 @@ futures = { version = "0.3.30", optional = true } [dev-dependencies] ethers-contract = { version = "2.0.11", default-features = false } -anyhow = "1.0.76" +anyhow = "1.0.79" criterion = "0.5" indicatif = "0.17" From 3144caf7c97f3ea95d709edd51f37ed2183fb436 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:13:34 +0100 Subject: [PATCH 40/66] chore(deps): bump secp256k1 from 0.28.0 to 0.28.1 (#954) Bumps [secp256k1](https://github.com/rust-bitcoin/rust-secp256k1) from 0.28.0 to 0.28.1. - [Changelog](https://github.com/rust-bitcoin/rust-secp256k1/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-bitcoin/rust-secp256k1/compare/secp256k1-0.28.0...secp256k1-0.28.1) --- updated-dependencies: - dependency-name: secp256k1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- crates/precompile/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b5765bfe0..deffef831b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2727,18 +2727,18 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" +checksum = "3f622567e3b4b38154fb8190bcf6b160d7a4301d70595a49195b48c116007a27" dependencies = [ "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e67c467c38fd24bd5499dc9a18183b31575c12ee549197e3e20d57aa4fe3b7" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" dependencies = [ "cc", ] diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index fef719e402..b567fde249 100644 --- a/crates/precompile/Cargo.toml +++ b/crates/precompile/Cargo.toml @@ -26,7 +26,7 @@ c-kzg = { version = "0.4.0", default-features = false, optional = true } # ecRecover precompile k256 = { version = "0.13.2", default-features = false, features = ["ecdsa"] } -secp256k1 = { version = "0.28.0", default-features = false, features = [ +secp256k1 = { version = "0.28.1", default-features = false, features = [ "alloc", "recovery", ], optional = true } From aff028e037ff3a5baee99d4c152fc06618f4c7be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:13:42 +0100 Subject: [PATCH 41/66] chore(deps): bump thiserror from 1.0.55 to 1.0.56 (#953) Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.55 to 1.0.56. - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.55...1.0.56) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index deffef831b..30bf930116 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3100,18 +3100,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.55" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.55" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", From b31b48ba8e1056637425a98f74f72992efee2ac4 Mon Sep 17 00:00:00 2001 From: rakita Date: Mon, 8 Jan 2024 04:06:09 +0100 Subject: [PATCH 42/66] ci(cachegrind): add PAT (#958) --- .github/workflows/cachegrind.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cachegrind.yml b/.github/workflows/cachegrind.yml index e64e0e922a..860e580068 100644 --- a/.github/workflows/cachegrind.yml +++ b/.github/workflows/cachegrind.yml @@ -37,7 +37,7 @@ jobs: - name: Comment on PR env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.PAT_COMMENT }} run: | # Try to edit the last comment if gh pr comment ${{ github.event.pull_request.number }} --edit-last --body-file results.md; then From d6f97b9d1e8cee71be51756667ac56232a82ee09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 04:10:00 +0100 Subject: [PATCH 43/66] chore(deps): bump serde from 1.0.194 to 1.0.195 (#957) Bumps [serde](https://github.com/serde-rs/serde) from 1.0.194 to 1.0.195. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.194...v1.0.195) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30bf930116..6f360a58e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2781,18 +2781,18 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.194" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.194" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", From 30bbcdfe81446c9d1e9b37acc95f208943ddf858 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 04:10:07 +0100 Subject: [PATCH 44/66] chore(deps): bump serde_json from 1.0.110 to 1.0.111 (#955) Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.110 to 1.0.111. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.110...v1.0.111) --- updated-dependencies: - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f360a58e8..7c539f0d22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2801,9 +2801,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.110" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "indexmap", "itoa", From 8424ba337ea46ba106e25ac9066ba960627f972b Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 9 Jan 2024 03:38:09 +0100 Subject: [PATCH 45/66] Update cachegrind.yml add env (#960) --- .github/workflows/cachegrind.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cachegrind.yml b/.github/workflows/cachegrind.yml index 860e580068..8c442486ee 100644 --- a/.github/workflows/cachegrind.yml +++ b/.github/workflows/cachegrind.yml @@ -7,6 +7,7 @@ on: jobs: valgrind: runs-on: ubuntu-latest + environment: ci-cachegrind permissions: write-all steps: @@ -46,4 +47,4 @@ jobs: echo "Failed to edit last comment. Trying to add a new comment instead!" # If editing last comment fails, try to add a new comment gh pr comment ${{ github.event.pull_request.number }} --body-file results.md - fi \ No newline at end of file + fi From ce5a4f1a4284ac5854daf412600ca94b6bdf01a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 03:38:24 +0100 Subject: [PATCH 46/66] chore(deps): bump k256 from 0.13.2 to 0.13.3 (#959) Bumps [k256](https://github.com/RustCrypto/elliptic-curves) from 0.13.2 to 0.13.3. - [Commits](https://github.com/RustCrypto/elliptic-curves/compare/k256/v0.13.2...k256/v0.13.3) --- updated-dependencies: - dependency-name: k256 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- crates/precompile/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c539f0d22..082d05d1bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1630,9 +1630,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if", "ecdsa", diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index b567fde249..bd4ffe34f0 100644 --- a/crates/precompile/Cargo.toml +++ b/crates/precompile/Cargo.toml @@ -25,7 +25,7 @@ aurora-engine-modexp = { version = "1.0", default-features = false } c-kzg = { version = "0.4.0", default-features = false, optional = true } # ecRecover precompile -k256 = { version = "0.13.2", default-features = false, features = ["ecdsa"] } +k256 = { version = "0.13.3", default-features = false, features = ["ecdsa"] } secp256k1 = { version = "0.28.1", default-features = false, features = [ "alloc", "recovery", From 6fda02a447c07f5008cb0f38ec1dbd38afd8dc6b Mon Sep 17 00:00:00 2001 From: Iaroslav Mazur Date: Tue, 9 Jan 2024 04:53:09 +0200 Subject: [PATCH 47/66] refactor(interpreter): local return_error! macro (#956) * refactor(interpreter): local return_error! macro * add exhaustive check for return_ macros --- crates/interpreter/src/instruction_result.rs | 156 ++++++++++++++----- 1 file changed, 117 insertions(+), 39 deletions(-) diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index 9ddf33db39..27e84d5f39 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -91,6 +91,50 @@ impl From for InstructionResult { } } +#[macro_export] +macro_rules! return_ok { + () => { + InstructionResult::Continue + | InstructionResult::Stop + | InstructionResult::Return + | InstructionResult::SelfDestruct + }; +} + +#[macro_export] +macro_rules! return_revert { + () => { + InstructionResult::Revert | InstructionResult::CallTooDeep | InstructionResult::OutOfFund + }; +} + +macro_rules! return_error { + () => { + InstructionResult::OutOfGas + | InstructionResult::MemoryOOG + | InstructionResult::MemoryLimitOOG + | InstructionResult::PrecompileOOG + | InstructionResult::InvalidOperandOOG + | InstructionResult::OpcodeNotFound + | InstructionResult::CallNotAllowedInsideStatic + | InstructionResult::StateChangeDuringStaticCall + | InstructionResult::InvalidFEOpcode + | InstructionResult::InvalidJump + | InstructionResult::NotActivated + | InstructionResult::StackUnderflow + | InstructionResult::StackOverflow + | InstructionResult::OutOfOffset + | InstructionResult::CreateCollision + | InstructionResult::OverflowPayment + | InstructionResult::PrecompileError + | InstructionResult::NonceOverflow + | InstructionResult::CreateContractSizeLimit + | InstructionResult::CreateContractStartingWithEF + | InstructionResult::CreateInitCodeSizeLimit + | InstructionResult::FatalExternalError + }; +} + impl InstructionResult { /// Returns whether the result is a success. #[inline] @@ -107,31 +151,7 @@ impl InstructionResult { /// Returns whether the result is an error. #[inline] pub fn is_error(self) -> bool { - matches!( - self, - Self::OutOfGas - | Self::MemoryOOG - | Self::MemoryLimitOOG - | Self::PrecompileOOG - | Self::InvalidOperandOOG - | Self::OpcodeNotFound - | Self::CallNotAllowedInsideStatic - | Self::StateChangeDuringStaticCall - | Self::InvalidFEOpcode - | Self::InvalidJump - | Self::NotActivated - | Self::StackUnderflow - | Self::StackOverflow - | Self::OutOfOffset - | Self::CreateCollision - | Self::OverflowPayment - | Self::PrecompileError - | Self::NonceOverflow - | Self::CreateContractSizeLimit - | Self::CreateContractStartingWithEF - | Self::CreateInitCodeSizeLimit - | Self::FatalExternalError - ) + matches!(self, return_error!()) } } @@ -238,19 +258,77 @@ impl From for SuccessOrHalt { } } -#[macro_export] -macro_rules! return_ok { - () => { - InstructionResult::Continue - | InstructionResult::Stop - | InstructionResult::Return - | InstructionResult::SelfDestruct - }; -} +#[cfg(test)] +mod tests { + use crate::InstructionResult; -#[macro_export] -macro_rules! return_revert { - () => { - InstructionResult::Revert | InstructionResult::CallTooDeep | InstructionResult::OutOfFund - }; + #[test] + fn all_results_are_covered() { + let result = InstructionResult::Continue; + match result { + return_error!() => {} + return_revert!() => (), + return_ok!() => {} + InstructionResult::CallOrCreate => (), + } + } + + #[test] + fn test_results() { + let ok_results = vec![ + InstructionResult::Continue, + InstructionResult::Stop, + InstructionResult::Return, + InstructionResult::SelfDestruct, + ]; + + for result in ok_results { + assert!(result.is_ok()); + assert!(!result.is_revert()); + assert!(!result.is_error()); + } + + let revert_results = vec![ + InstructionResult::Revert, + InstructionResult::CallTooDeep, + InstructionResult::OutOfFund, + ]; + + for result in revert_results { + assert!(!result.is_ok()); + assert!(result.is_revert()); + assert!(!result.is_error()); + } + + let error_results = vec![ + InstructionResult::OutOfGas, + InstructionResult::MemoryOOG, + InstructionResult::MemoryLimitOOG, + InstructionResult::PrecompileOOG, + InstructionResult::InvalidOperandOOG, + InstructionResult::OpcodeNotFound, + InstructionResult::CallNotAllowedInsideStatic, + InstructionResult::StateChangeDuringStaticCall, + InstructionResult::InvalidFEOpcode, + InstructionResult::InvalidJump, + InstructionResult::NotActivated, + InstructionResult::StackUnderflow, + InstructionResult::StackOverflow, + InstructionResult::OutOfOffset, + InstructionResult::CreateCollision, + InstructionResult::OverflowPayment, + InstructionResult::PrecompileError, + InstructionResult::NonceOverflow, + InstructionResult::CreateContractSizeLimit, + InstructionResult::CreateContractStartingWithEF, + InstructionResult::CreateInitCodeSizeLimit, + InstructionResult::FatalExternalError, + ]; + + for result in error_results { + assert!(!result.is_ok()); + assert!(!result.is_revert()); + assert!(result.is_error()); + } + } } From b0fb4b201eff6c3170d2bae87af4c475dc6b5c58 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 10 Jan 2024 13:09:59 +0100 Subject: [PATCH 48/66] chore(deps): alloy 0.6 (#963) --- Cargo.lock | 5 +++-- crates/primitives/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 082d05d1bc..95a6f56122 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,9 +46,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-primitives" -version = "0.5.4" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c234f92024707f224510ff82419b2be0e1d8e1fd911defcac5a085cd7f83898" +checksum = "3729132072f369bc4e8e6e070f9cf4deb3490fc9b9eea6f71f75ec19406df811" dependencies = [ "alloy-rlp", "arbitrary", @@ -60,6 +60,7 @@ dependencies = [ "ethereum_ssz", "hex-literal", "itoa", + "k256", "keccak-asm", "proptest", "proptest-derive", diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 29c089db18..4370580294 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -17,7 +17,7 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] -alloy-primitives = { version = "0.5", default-features = false, features = [ +alloy-primitives = { version = "0.6", default-features = false, features = [ "rlp", ] } alloy-rlp = { version = "0.3", default-features = false, features = ["derive"] } From 0b4c07463ed3a0a0efecfcc8b24d1aa7bfac6871 Mon Sep 17 00:00:00 2001 From: Iaroslav Mazur Date: Wed, 10 Jan 2024 14:31:15 +0200 Subject: [PATCH 49/66] refactor(interpreter): consistency in all_results_are_covered() (#961) - export the return_error! macro to the crate level; - unite the 2 redundant match arms when initializing SuccessOrHalt from from InstructionResult; --- crates/interpreter/src/instruction_result.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index 27e84d5f39..04cd468394 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -108,6 +108,7 @@ macro_rules! return_revert { }; } +#[macro_export] macro_rules! return_error { () => { InstructionResult::OutOfGas @@ -248,8 +249,8 @@ impl From for SuccessOrHalt { InstructionResult::OverflowPayment => Self::Halt(Halt::OverflowPayment), // Check for first call is done separately. InstructionResult::PrecompileError => Self::Halt(Halt::PrecompileError), InstructionResult::NonceOverflow => Self::Halt(Halt::NonceOverflow), - InstructionResult::CreateContractSizeLimit => Self::Halt(Halt::CreateContractSizeLimit), - InstructionResult::CreateContractStartingWithEF => { + InstructionResult::CreateContractSizeLimit + | InstructionResult::CreateContractStartingWithEF => { Self::Halt(Halt::CreateContractSizeLimit) } InstructionResult::CreateInitCodeSizeLimit => Self::Halt(Halt::CreateInitCodeSizeLimit), @@ -264,12 +265,11 @@ mod tests { #[test] fn all_results_are_covered() { - let result = InstructionResult::Continue; - match result { + match InstructionResult::Continue { return_error!() => {} - return_revert!() => (), + return_revert!() => {} return_ok!() => {} - InstructionResult::CallOrCreate => (), + InstructionResult::CallOrCreate => {} } } From 2f1117397532ed70baec87529b2b7e262fe64442 Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 10 Jan 2024 14:51:20 +0100 Subject: [PATCH 50/66] ci(cachegrind): pass action always (#965) --- .github/workflows/cachegrind.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cachegrind.yml b/.github/workflows/cachegrind.yml index 8c442486ee..ed1d42fa7c 100644 --- a/.github/workflows/cachegrind.yml +++ b/.github/workflows/cachegrind.yml @@ -7,7 +7,6 @@ on: jobs: valgrind: runs-on: ubuntu-latest - environment: ci-cachegrind permissions: write-all steps: @@ -38,7 +37,7 @@ jobs: - name: Comment on PR env: - GH_TOKEN: ${{ secrets.PAT_COMMENT }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | # Try to edit the last comment if gh pr comment ${{ github.event.pull_request.number }} --edit-last --body-file results.md; then @@ -46,5 +45,8 @@ jobs: else echo "Failed to edit last comment. Trying to add a new comment instead!" # If editing last comment fails, try to add a new comment - gh pr comment ${{ github.event.pull_request.number }} --body-file results.md + if ! gh pr comment ${{ github.event.pull_request.number }} --body-file results.md; then + echo "Comment failed to be made, printing out results here:" + cat results.md + fi fi From b9ecc0034ff73f97c7cb25f4b3afb609b0fc0ea5 Mon Sep 17 00:00:00 2001 From: Iaroslav Mazur Date: Wed, 10 Jan 2024 16:56:58 +0200 Subject: [PATCH 51/66] refactor(interpreter): improve enum naming (#962) --- crates/interpreter/src/instruction_result.rs | 140 ++++++++++--------- crates/primitives/src/result.rs | 18 +-- crates/revm/src/handler/optimism.rs | 6 +- 3 files changed, 83 insertions(+), 81 deletions(-) diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index 04cd468394..4833bd6f4f 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -1,4 +1,4 @@ -use crate::primitives::{Eval, Halt, OutOfGasError}; +use crate::primitives::{HaltReason, OutOfGasError, SuccessReason}; #[repr(u8)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] @@ -49,44 +49,44 @@ pub enum InstructionResult { FatalExternalError, } -impl From for InstructionResult { - fn from(value: Eval) -> Self { +impl From for InstructionResult { + fn from(value: SuccessReason) -> Self { match value { - Eval::Return => InstructionResult::Return, - Eval::Stop => InstructionResult::Stop, - Eval::SelfDestruct => InstructionResult::SelfDestruct, + SuccessReason::Return => InstructionResult::Return, + SuccessReason::Stop => InstructionResult::Stop, + SuccessReason::SelfDestruct => InstructionResult::SelfDestruct, } } } -impl From for InstructionResult { - fn from(value: Halt) -> Self { +impl From for InstructionResult { + fn from(value: HaltReason) -> Self { match value { - Halt::OutOfGas(OutOfGasError::BasicOutOfGas) => Self::OutOfGas, - Halt::OutOfGas(OutOfGasError::InvalidOperand) => Self::InvalidOperandOOG, - Halt::OutOfGas(OutOfGasError::Memory) => Self::MemoryOOG, - Halt::OutOfGas(OutOfGasError::MemoryLimit) => Self::MemoryLimitOOG, - Halt::OutOfGas(OutOfGasError::Precompile) => Self::PrecompileOOG, - Halt::OpcodeNotFound => Self::OpcodeNotFound, - Halt::InvalidFEOpcode => Self::InvalidFEOpcode, - Halt::InvalidJump => Self::InvalidJump, - Halt::NotActivated => Self::NotActivated, - Halt::StackOverflow => Self::StackOverflow, - Halt::StackUnderflow => Self::StackUnderflow, - Halt::OutOfOffset => Self::OutOfOffset, - Halt::CreateCollision => Self::CreateCollision, - Halt::PrecompileError => Self::PrecompileError, - Halt::NonceOverflow => Self::NonceOverflow, - Halt::CreateContractSizeLimit => Self::CreateContractSizeLimit, - Halt::CreateContractStartingWithEF => Self::CreateContractStartingWithEF, - Halt::CreateInitCodeSizeLimit => Self::CreateInitCodeSizeLimit, - Halt::OverflowPayment => Self::OverflowPayment, - Halt::StateChangeDuringStaticCall => Self::StateChangeDuringStaticCall, - Halt::CallNotAllowedInsideStatic => Self::CallNotAllowedInsideStatic, - Halt::OutOfFund => Self::OutOfFund, - Halt::CallTooDeep => Self::CallTooDeep, + HaltReason::OutOfGas(OutOfGasError::BasicOutOfGas) => Self::OutOfGas, + HaltReason::OutOfGas(OutOfGasError::InvalidOperand) => Self::InvalidOperandOOG, + HaltReason::OutOfGas(OutOfGasError::Memory) => Self::MemoryOOG, + HaltReason::OutOfGas(OutOfGasError::MemoryLimit) => Self::MemoryLimitOOG, + HaltReason::OutOfGas(OutOfGasError::Precompile) => Self::PrecompileOOG, + HaltReason::OpcodeNotFound => Self::OpcodeNotFound, + HaltReason::InvalidFEOpcode => Self::InvalidFEOpcode, + HaltReason::InvalidJump => Self::InvalidJump, + HaltReason::NotActivated => Self::NotActivated, + HaltReason::StackOverflow => Self::StackOverflow, + HaltReason::StackUnderflow => Self::StackUnderflow, + HaltReason::OutOfOffset => Self::OutOfOffset, + HaltReason::CreateCollision => Self::CreateCollision, + HaltReason::PrecompileError => Self::PrecompileError, + HaltReason::NonceOverflow => Self::NonceOverflow, + HaltReason::CreateContractSizeLimit => Self::CreateContractSizeLimit, + HaltReason::CreateContractStartingWithEF => Self::CreateContractStartingWithEF, + HaltReason::CreateInitCodeSizeLimit => Self::CreateInitCodeSizeLimit, + HaltReason::OverflowPayment => Self::OverflowPayment, + HaltReason::StateChangeDuringStaticCall => Self::StateChangeDuringStaticCall, + HaltReason::CallNotAllowedInsideStatic => Self::CallNotAllowedInsideStatic, + HaltReason::OutOfFund => Self::OutOfFund, + HaltReason::CallTooDeep => Self::CallTooDeep, #[cfg(feature = "optimism")] - Halt::FailedDeposit => Self::FatalExternalError, + HaltReason::FailedDeposit => Self::FatalExternalError, } } } @@ -158,9 +158,9 @@ impl InstructionResult { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum SuccessOrHalt { - Success(Eval), + Success(SuccessReason), Revert, - Halt(Halt), + Halt(HaltReason), FatalExternalError, /// Internal instruction that signals Interpreter should continue running. InternalContinue, @@ -175,11 +175,11 @@ impl SuccessOrHalt { matches!(self, SuccessOrHalt::Success(_)) } - /// Returns the [Eval] value if this a successful result + /// Returns the [SuccessReason] value if this a successful result #[inline] - pub fn to_success(self) -> Option { + pub fn to_success(self) -> Option { match self { - SuccessOrHalt::Success(eval) => Some(eval), + SuccessOrHalt::Success(reason) => Some(reason), _ => None, } } @@ -196,11 +196,11 @@ impl SuccessOrHalt { matches!(self, SuccessOrHalt::Halt(_)) } - /// Returns the [Halt] value the EVM has experienced an exceptional halt + /// Returns the [HaltReason] value the EVM has experienced an exceptional halt #[inline] - pub fn to_halt(self) -> Option { + pub fn to_halt(self) -> Option { match self { - SuccessOrHalt::Halt(halt) => Some(halt), + SuccessOrHalt::Halt(reason) => Some(reason), _ => None, } } @@ -210,50 +210,52 @@ impl From for SuccessOrHalt { fn from(result: InstructionResult) -> Self { match result { InstructionResult::Continue => Self::InternalContinue, // used only in interpreter loop - InstructionResult::Stop => Self::Success(Eval::Stop), - InstructionResult::Return => Self::Success(Eval::Return), - InstructionResult::SelfDestruct => Self::Success(Eval::SelfDestruct), + InstructionResult::Stop => Self::Success(SuccessReason::Stop), + InstructionResult::Return => Self::Success(SuccessReason::Return), + InstructionResult::SelfDestruct => Self::Success(SuccessReason::SelfDestruct), InstructionResult::Revert => Self::Revert, InstructionResult::CallOrCreate => Self::InternalCallOrCreate, // used only in interpreter loop - InstructionResult::CallTooDeep => Self::Halt(Halt::CallTooDeep), // not gonna happen for first call - InstructionResult::OutOfFund => Self::Halt(Halt::OutOfFund), // Check for first call is done separately. - InstructionResult::OutOfGas => Self::Halt(Halt::OutOfGas( + InstructionResult::CallTooDeep => Self::Halt(HaltReason::CallTooDeep), // not gonna happen for first call + InstructionResult::OutOfFund => Self::Halt(HaltReason::OutOfFund), // Check for first call is done separately. + InstructionResult::OutOfGas => Self::Halt(HaltReason::OutOfGas( revm_primitives::OutOfGasError::BasicOutOfGas, )), - InstructionResult::MemoryLimitOOG => { - Self::Halt(Halt::OutOfGas(revm_primitives::OutOfGasError::MemoryLimit)) - } + InstructionResult::MemoryLimitOOG => Self::Halt(HaltReason::OutOfGas( + revm_primitives::OutOfGasError::MemoryLimit, + )), InstructionResult::MemoryOOG => { - Self::Halt(Halt::OutOfGas(revm_primitives::OutOfGasError::Memory)) - } - InstructionResult::PrecompileOOG => { - Self::Halt(Halt::OutOfGas(revm_primitives::OutOfGasError::Precompile)) + Self::Halt(HaltReason::OutOfGas(revm_primitives::OutOfGasError::Memory)) } - InstructionResult::InvalidOperandOOG => Self::Halt(Halt::OutOfGas( + InstructionResult::PrecompileOOG => Self::Halt(HaltReason::OutOfGas( + revm_primitives::OutOfGasError::Precompile, + )), + InstructionResult::InvalidOperandOOG => Self::Halt(HaltReason::OutOfGas( revm_primitives::OutOfGasError::InvalidOperand, )), - InstructionResult::OpcodeNotFound => Self::Halt(Halt::OpcodeNotFound), + InstructionResult::OpcodeNotFound => Self::Halt(HaltReason::OpcodeNotFound), InstructionResult::CallNotAllowedInsideStatic => { - Self::Halt(Halt::CallNotAllowedInsideStatic) + Self::Halt(HaltReason::CallNotAllowedInsideStatic) } // first call is not static call InstructionResult::StateChangeDuringStaticCall => { - Self::Halt(Halt::StateChangeDuringStaticCall) + Self::Halt(HaltReason::StateChangeDuringStaticCall) } - InstructionResult::InvalidFEOpcode => Self::Halt(Halt::InvalidFEOpcode), - InstructionResult::InvalidJump => Self::Halt(Halt::InvalidJump), - InstructionResult::NotActivated => Self::Halt(Halt::NotActivated), - InstructionResult::StackUnderflow => Self::Halt(Halt::StackUnderflow), - InstructionResult::StackOverflow => Self::Halt(Halt::StackOverflow), - InstructionResult::OutOfOffset => Self::Halt(Halt::OutOfOffset), - InstructionResult::CreateCollision => Self::Halt(Halt::CreateCollision), - InstructionResult::OverflowPayment => Self::Halt(Halt::OverflowPayment), // Check for first call is done separately. - InstructionResult::PrecompileError => Self::Halt(Halt::PrecompileError), - InstructionResult::NonceOverflow => Self::Halt(Halt::NonceOverflow), + InstructionResult::InvalidFEOpcode => Self::Halt(HaltReason::InvalidFEOpcode), + InstructionResult::InvalidJump => Self::Halt(HaltReason::InvalidJump), + InstructionResult::NotActivated => Self::Halt(HaltReason::NotActivated), + InstructionResult::StackUnderflow => Self::Halt(HaltReason::StackUnderflow), + InstructionResult::StackOverflow => Self::Halt(HaltReason::StackOverflow), + InstructionResult::OutOfOffset => Self::Halt(HaltReason::OutOfOffset), + InstructionResult::CreateCollision => Self::Halt(HaltReason::CreateCollision), + InstructionResult::OverflowPayment => Self::Halt(HaltReason::OverflowPayment), // Check for first call is done separately. + InstructionResult::PrecompileError => Self::Halt(HaltReason::PrecompileError), + InstructionResult::NonceOverflow => Self::Halt(HaltReason::NonceOverflow), InstructionResult::CreateContractSizeLimit | InstructionResult::CreateContractStartingWithEF => { - Self::Halt(Halt::CreateContractSizeLimit) + Self::Halt(HaltReason::CreateContractSizeLimit) + } + InstructionResult::CreateInitCodeSizeLimit => { + Self::Halt(HaltReason::CreateInitCodeSizeLimit) } - InstructionResult::CreateInitCodeSizeLimit => Self::Halt(Halt::CreateInitCodeSizeLimit), InstructionResult::FatalExternalError => Self::FatalExternalError, } } diff --git a/crates/primitives/src/result.rs b/crates/primitives/src/result.rs index 649a9ce95c..666cfca11a 100644 --- a/crates/primitives/src/result.rs +++ b/crates/primitives/src/result.rs @@ -23,7 +23,7 @@ pub struct ResultAndState { pub enum ExecutionResult { /// Returned successfully Success { - reason: Eval, + reason: SuccessReason, gas_used: u64, gas_refunded: u64, logs: Vec, @@ -33,7 +33,7 @@ pub enum ExecutionResult { Revert { gas_used: u64, output: Bytes }, /// Reverted for various reasons and spend all gas. Halt { - reason: Halt, + reason: HaltReason, /// Halting will spend all the gas, and will be equal to gas_limit. gas_used: u64, }, @@ -223,28 +223,28 @@ pub enum InvalidTransaction { /// was deprecated in the Regolith hardfork, and this error is thrown if a `Deposit` transaction /// is found with this field set to `true` after the hardfork activation. /// - /// In addition, this error is internal, and bubbles up into a [Halt::FailedDeposit] error + /// In addition, this error is internal, and bubbles up into a [HaltReason::FailedDeposit] error /// in the `revm` handler for the consumer to easily handle. This is due to a state transition /// rule on OP Stack chains where, if for any reason a deposit transaction fails, the transaction /// must still be included in the block, the sender nonce is bumped, the `mint` value persists, and /// special gas accounting rules are applied. Normally on L1, [EVMError::Transaction] errors - /// are cause for non-inclusion, so a special [Halt] variant was introduced to handle this + /// are cause for non-inclusion, so a special [HaltReason] variant was introduced to handle this /// case for failed deposit transactions. #[cfg(feature = "optimism")] DepositSystemTxPostRegolith, /// Deposit transaction haults bubble up to the global main return handler, wiping state and /// only increasing the nonce + persisting the mint value. /// - /// This is a catch-all error for any deposit transaction that is results in a [Halt] error + /// This is a catch-all error for any deposit transaction that is results in a [HaltReason] error /// post-regolith hardfork. This allows for a consumer to easily handle special cases where /// a deposit transaction fails during validation, but must still be included in the block. /// - /// In addition, this error is internal, and bubbles up into a [Halt::FailedDeposit] error + /// In addition, this error is internal, and bubbles up into a [HaltReason::FailedDeposit] error /// in the `revm` handler for the consumer to easily handle. This is due to a state transition /// rule on OP Stack chains where, if for any reason a deposit transaction fails, the transaction /// must still be included in the block, the sender nonce is bumped, the `mint` value persists, and /// special gas accounting rules are applied. Normally on L1, [EVMError::Transaction] errors - /// are cause for non-inclusion, so a special [Halt] variant was introduced to handle this + /// are cause for non-inclusion, so a special [HaltReason] variant was introduced to handle this /// case for failed deposit transactions. #[cfg(feature = "optimism")] HaltedDepositPostRegolith, @@ -355,7 +355,7 @@ impl fmt::Display for InvalidHeader { /// Reason a transaction successfully completed. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Eval { +pub enum SuccessReason { Stop, Return, SelfDestruct, @@ -365,7 +365,7 @@ pub enum Eval { /// immediately end with all gas being consumed. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Halt { +pub enum HaltReason { OutOfGas(OutOfGasError), OpcodeNotFound, InvalidFEOpcode, diff --git a/crates/revm/src/handler/optimism.rs b/crates/revm/src/handler/optimism.rs index 49e7c69676..cbb248b3f7 100644 --- a/crates/revm/src/handler/optimism.rs +++ b/crates/revm/src/handler/optimism.rs @@ -5,8 +5,8 @@ use crate::{ interpreter::{return_ok, return_revert, Gas, InstructionResult}, optimism, primitives::{ - db::Database, Account, EVMError, Env, ExecutionResult, Halt, HashMap, InvalidTransaction, - Output, ResultAndState, Spec, SpecId::REGOLITH, U256, + db::Database, Account, EVMError, Env, ExecutionResult, HaltReason, HashMap, + InvalidTransaction, Output, ResultAndState, Spec, SpecId::REGOLITH, U256, }, EvmContext, }; @@ -225,7 +225,7 @@ pub fn end_handle( Ok(ResultAndState { result: ExecutionResult::Halt { - reason: Halt::FailedDeposit, + reason: HaltReason::FailedDeposit, gas_used, }, state, From fd3d27983b391ca0762e6d84a8baeaf68fe06f11 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Thu, 11 Jan 2024 09:23:51 -0500 Subject: [PATCH 52/66] feat: add asm-keccak feature (#972) --- crates/interpreter/Cargo.toml | 1 + crates/precompile/Cargo.toml | 1 + crates/primitives/Cargo.toml | 1 + crates/revm/Cargo.toml | 1 + 4 files changed, 4 insertions(+) diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index 6c8e58f72c..11bae30a8a 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -24,6 +24,7 @@ default = ["std"] std = ["revm-primitives/std"] serde = ["dep:serde", "revm-primitives/serde"] arbitrary = ["std", "revm-primitives/arbitrary"] +asm-keccak = ["revm-primitives/asm-keccak"] optimism = ["revm-primitives/optimism"] diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index bd4ffe34f0..f8d6dd62a0 100644 --- a/crates/precompile/Cargo.toml +++ b/crates/precompile/Cargo.toml @@ -43,6 +43,7 @@ std = [ "c-kzg?/std", "secp256k1?/std", ] +asm-keccak = ["revm-primitives/asm-keccak"] optimism = ["revm-primitives/optimism"] diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 4370580294..f57f7001f9 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -59,6 +59,7 @@ serde = [ "c-kzg?/serde", ] arbitrary = ["std", "alloy-primitives/arbitrary", "bitflags/arbitrary"] +asm-keccak = ["alloy-primitives/asm-keccak"] optimism = [] diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 53edafc000..4eaed1bc28 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -44,6 +44,7 @@ default = ["std", "c-kzg", "secp256k1"] std = ["revm-interpreter/std", "revm-precompile/std"] serde = ["dep:serde", "dep:serde_json", "revm-interpreter/serde"] arbitrary = ["revm-interpreter/arbitrary"] +asm-keccak = ["revm-interpreter/asm-keccak", "revm-precompile/asm-keccak"] test-utils = [] From 8136ebd9401feea63e20cfcd58a17d1542efd472 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 15:38:23 +0100 Subject: [PATCH 53/66] chore(deps): bump ethers-core from 2.0.11 to 2.0.12 (#970) Bumps [ethers-core](https://github.com/gakonst/ethers-rs) from 2.0.11 to 2.0.12. - [Release notes](https://github.com/gakonst/ethers-rs/releases) - [Changelog](https://github.com/gakonst/ethers-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/gakonst/ethers-rs/commits) --- updated-dependencies: - dependency-name: ethers-core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 95a6f56122..5db527ba2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1019,9 +1019,9 @@ dependencies = [ [[package]] name = "ethers-core" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f03e0bdc216eeb9e355b90cf610ef6c5bb8aca631f97b5ae9980ce34ea7878d" +checksum = "918b1a9ba585ea61022647def2f27c29ba19f6d2a4a4c8f68a9ae97fd5769737" dependencies = [ "arrayvec", "bytes", From b28c154759934e3577f959f1c237e5210c201777 Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 11 Jan 2024 16:20:32 +0100 Subject: [PATCH 54/66] fix: dont calculate initcode keccak on CREATE (#969) --- crates/interpreter/src/inner_models.rs | 12 +----------- crates/revm/src/evm_context.rs | 16 +++++++++++----- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/crates/interpreter/src/inner_models.rs b/crates/interpreter/src/inner_models.rs index a5d3bcd187..29795e3189 100644 --- a/crates/interpreter/src/inner_models.rs +++ b/crates/interpreter/src/inner_models.rs @@ -1,5 +1,5 @@ pub use crate::primitives::CreateScheme; -use crate::primitives::{Address, Bytes, B256, U256}; +use crate::primitives::{Address, Bytes, U256}; /// Inputs for a call. #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -45,16 +45,6 @@ impl CreateInputs { .create2_from_code(salt.to_be_bytes(), &self.init_code), } } - - /// Returns the address that this create call will create, without calculating the init code hash. - /// - /// Note: `hash` must be `keccak256(&self.init_code)`. - pub fn created_address_with_hash(&self, nonce: u64, hash: &B256) -> Address { - match self.scheme { - CreateScheme::Create => self.caller.create(nonce), - CreateScheme::Create2 { salt } => self.caller.create2(salt.to_be_bytes(), hash), - } - } } /// Call schemes. diff --git a/crates/revm/src/evm_context.rs b/crates/revm/src/evm_context.rs index 0a42885ac6..a643adfda8 100644 --- a/crates/revm/src/evm_context.rs +++ b/crates/revm/src/evm_context.rs @@ -7,8 +7,8 @@ use crate::{ journaled_state::JournaledState, precompile::{Precompile, Precompiles}, primitives::{ - keccak256, Address, AnalysisKind, Bytecode, Bytes, EVMError, Env, Spec, SpecId::*, B256, - U256, + keccak256, Address, AnalysisKind, Bytecode, Bytes, CreateScheme, EVMError, Env, Spec, + SpecId::*, B256, U256, }, CallStackFrame, CALL_STACK_LIMIT, }; @@ -174,8 +174,14 @@ impl<'a, DB: Database> EvmContext<'a, DB> { } // Create address - let code_hash = keccak256(&inputs.init_code); - let created_address = inputs.created_address_with_hash(old_nonce, &code_hash); + 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) + } + }; // Load account so it needs to be marked as warm for access list. if self @@ -204,7 +210,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { let contract = Box::new(Contract::new( Bytes::new(), bytecode, - code_hash, + init_code_hash, created_address, inputs.caller, inputs.value, From aaa42065f18d2a917db9d5408772391bda78382d Mon Sep 17 00:00:00 2001 From: rakita Date: Fri, 12 Jan 2024 01:58:54 +0100 Subject: [PATCH 55/66] feat: EvmBuilder and External Contexts (#888) * rename main Evm structs, introduce wip external type * tests * Split evm and external context * continue previous commit * wip inspector handle register * add few more handlers for frame and host * Add instruction handle * add instruction handler registration and Inspector wrap * Rm Spec generic, more handlers, start factory * move towards the builder, allow EVM modify * wip on EvmBuilder and modify functionality * EvmBuilder with stages wip * Add wip stages for builer * make handle register simple function, add raw instruction table, split external data from registers * wip on simple builder functions and handler registry * Examples and cleanup * fix lifetime and fmt * Add more handlers, deduct caller, validate tx agains state * All handlers counted, started on docs, some cleanup * renaming and docs * Support all Inspector functionality with Handler * Handler restructured. Documentation added * more docs on registers * integrate builder, fmt, move optimism l1block * add utility builder stage functions * add precompiles, fix bugs with journal spec * spec to generic, optimism build * fix optimism test * fuck macros * clippy and fmt * fix trace block example * ci fixes * Flatten builder stages to generic and handler stage * EvmBuilder doc and refactor fn access * ignore rust code in book * make revme compile, will refactor this in future * Rename handles to Pre/Post Execution and ExecutionLoop * fix optimism clippy * small rename * FrameData and docs * check links mdbook * comments and cleanup * comment * Add initialize interepreter to first frame * clippy * clippy2 --- Cargo.lock | 4 +- Cargo.toml | 9 +- bins/revm-test/src/bin/analysis.rs | 40 +- bins/revm-test/src/bin/snailtracer.rs | 65 +- bins/revm-test/src/bin/transfer.rs | 42 +- bins/revme/src/cmd/statetest/runner.rs | 186 ++-- book.toml | 2 +- crates/interpreter/src/gas.rs | 11 + crates/interpreter/src/gas/calc.rs | 2 +- crates/interpreter/src/inner_models.rs | 42 + crates/interpreter/src/instructions/opcode.rs | 24 +- crates/interpreter/src/interpreter.rs | 40 +- crates/precompile/src/lib.rs | 59 +- crates/primitives/src/env.rs | 30 +- crates/primitives/src/specification.rs | 120 ++- crates/revm/CHANGELOG.md | 7 + crates/revm/Cargo.toml | 7 +- crates/revm/benches/bench.rs | 72 +- crates/revm/src/builder.rs | 362 ++++++++ .../revm/src/{evm_context.rs => context.rs} | 250 ++++-- crates/revm/src/db.rs | 5 - crates/revm/src/evm.rs | 445 +++++++--- crates/revm/src/evm_impl.rs | 832 ------------------ crates/revm/src/frame.rs | 60 +- crates/revm/src/handler.rs | 207 +++-- crates/revm/src/handler/handle_types.rs | 25 + .../handler/handle_types/execution_loop.rs | 154 ++++ .../handler/handle_types/post_execution.rs | 101 +++ .../src/handler/handle_types/pre_execution.rs | 59 ++ .../src/handler/handle_types/validation.rs | 60 ++ crates/revm/src/handler/mainnet.rs | 218 +---- .../src/handler/mainnet/execution_loop.rs | 210 +++++ .../src/handler/mainnet/post_execution.rs | 117 +++ .../revm/src/handler/mainnet/pre_execution.rs | 96 ++ crates/revm/src/handler/mainnet/validation.rs | 52 ++ crates/revm/src/handler/register.rs | 31 + crates/revm/src/inspector.rs | 44 +- crates/revm/src/inspector/customprinter.rs | 73 +- crates/revm/src/inspector/eip3155.rs | 29 +- crates/revm/src/inspector/gas.rs | 77 +- crates/revm/src/inspector/handler_register.rs | 436 +++++++++ crates/revm/src/inspector/instruction.rs | 55 -- crates/revm/src/inspector/noop.rs | 8 +- crates/revm/src/journaled_state.rs | 37 +- crates/revm/src/lib.rs | 47 +- crates/revm/src/optimism.rs | 173 +--- .../handler_register.rs} | 273 +++++- crates/revm/src/optimism/l1block.rs | 168 ++++ crates/revm/src/test_utils.rs | 2 +- documentation/src/SUMMARY.md | 5 +- documentation/src/crates/interpreter/host.md | 5 +- documentation/src/crates/revm.md | 17 +- documentation/src/crates/revm/builder.md | 103 +++ documentation/src/crates/revm/evm.md | 39 +- documentation/src/crates/revm/evm_impl.md | 35 - documentation/src/crates/revm/handler.md | 100 +++ documentation/src/crates/revm/host_trait.md | 57 -- documentation/src/crates/revm/state.md | 11 + examples/fork_ref_transact.rs | 32 +- examples/generate_block_traces.rs | 124 +-- 60 files changed, 3742 insertions(+), 2254 deletions(-) create mode 100644 crates/revm/src/builder.rs rename crates/revm/src/{evm_context.rs => context.rs} (75%) delete mode 100644 crates/revm/src/evm_impl.rs create mode 100644 crates/revm/src/handler/handle_types.rs create mode 100644 crates/revm/src/handler/handle_types/execution_loop.rs create mode 100644 crates/revm/src/handler/handle_types/post_execution.rs create mode 100644 crates/revm/src/handler/handle_types/pre_execution.rs create mode 100644 crates/revm/src/handler/handle_types/validation.rs create mode 100644 crates/revm/src/handler/mainnet/execution_loop.rs create mode 100644 crates/revm/src/handler/mainnet/post_execution.rs create mode 100644 crates/revm/src/handler/mainnet/pre_execution.rs create mode 100644 crates/revm/src/handler/mainnet/validation.rs create mode 100644 crates/revm/src/handler/register.rs create mode 100644 crates/revm/src/inspector/handler_register.rs delete mode 100644 crates/revm/src/inspector/instruction.rs rename crates/revm/src/{handler/optimism.rs => optimism/handler_register.rs} (51%) create mode 100644 crates/revm/src/optimism/l1block.rs create mode 100644 documentation/src/crates/revm/builder.md delete mode 100644 documentation/src/crates/revm/evm_impl.md create mode 100644 documentation/src/crates/revm/handler.md delete mode 100644 documentation/src/crates/revm/host_trait.md create mode 100644 documentation/src/crates/revm/state.md diff --git a/Cargo.lock b/Cargo.lock index 5db527ba2f..32520521e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2089,9 +2089,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" [[package]] name = "powerfmt" diff --git a/Cargo.toml b/Cargo.toml index 4f7b81ca89..426d6e403f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,13 @@ [workspace] +members = [ + "bins/revme", + "bins/revm-test", + "crates/revm", + "crates/primitives", + "crates/interpreter", + "crates/precompile", +] resolver = "2" -members = ["bins/*", "crates/*"] default-members = ["crates/revm"] [workspace.metadata.docs.rs] diff --git a/bins/revm-test/src/bin/analysis.rs b/bins/revm-test/src/bin/analysis.rs index a7a81af9ce..b0f2b6404e 100644 --- a/bins/revm-test/src/bin/analysis.rs +++ b/bins/revm-test/src/bin/analysis.rs @@ -3,33 +3,29 @@ use std::time::Instant; use revm::{ db::BenchmarkDB, interpreter::analysis::to_analysed, - primitives::{Bytecode, Bytes, TransactTo}, + primitives::{address, bytes, Bytecode, Bytes, TransactTo}, + Evm, }; extern crate alloc; fn main() { let contract_data : Bytes = hex::decode( "6060604052341561000f57600080fd5b604051610dd1380380610dd18339810160405280805190602001909190805182019190602001805190602001909190805182019190505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100a79291906100e3565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100d99291906100e3565b5050505050610188565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012457805160ff1916838001178555610152565b82800160010185558215610152579182015b82811115610151578251825591602001919060010190610136565b5b50905061015f9190610163565b5090565b61018591905b80821115610181576000816000905550600101610169565b5090565b90565b610c3a806101976000396000f3006060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014257806318160ddd1461019c57806323b872dd146101c557806327e235e31461023e578063313ce5671461028b5780635c658165146102ba57806370a082311461032657806395d89b4114610373578063a9059cbb14610401578063dd62ed3e1461045b575b600080fd5b34156100bf57600080fd5b6100c76104c7565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101075780820151818401526020810190506100ec565b50505050905090810190601f1680156101345780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014d57600080fd5b610182600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610565565b604051808215151515815260200191505060405180910390f35b34156101a757600080fd5b6101af610657565b6040518082815260200191505060405180910390f35b34156101d057600080fd5b610224600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061065d565b604051808215151515815260200191505060405180910390f35b341561024957600080fd5b610275600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108f7565b6040518082815260200191505060405180910390f35b341561029657600080fd5b61029e61090f565b604051808260ff1660ff16815260200191505060405180910390f35b34156102c557600080fd5b610310600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610922565b6040518082815260200191505060405180910390f35b341561033157600080fd5b61035d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610947565b6040518082815260200191505060405180910390f35b341561037e57600080fd5b610386610990565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103c65780820151818401526020810190506103ab565b50505050905090810190601f1680156103f35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561040c57600080fd5b610441600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610a2e565b604051808215151515815260200191505060405180910390f35b341561046657600080fd5b6104b1600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b87565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561055d5780601f106105325761010080835404028352916020019161055d565b820191906000526020600020905b81548152906001019060200180831161054057829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015801561072e5750828110155b151561073957600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108865782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a265780601f106109fb57610100808354040283529160200191610a26565b820191906000526020600020905b815481529060010190602001808311610a0957829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a7e57600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820df254047bc8f2904ad3e966b6db116d703bebd40efadadb5e738c836ffc8f58a0029" ).unwrap().into(); - // BenchmarkDB is dummy state that implements Database trait. - let mut evm = revm::new(); - - // execution globals block hash/gas_limit/coinbase/timestamp.. - evm.env.tx.caller = "0x1000000000000000000000000000000000000000" - .parse() - .unwrap(); - evm.env.tx.transact_to = TransactTo::Call( - "0x0000000000000000000000000000000000000000" - .parse() - .unwrap(), - ); - //evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); - evm.env.tx.data = Bytes::from(hex::decode("8035F0CE").unwrap()); - let bytecode_raw = Bytecode::new_raw(contract_data.clone()); let bytecode_checked = Bytecode::new_raw(contract_data.clone()).to_checked(); let bytecode_analysed = to_analysed(Bytecode::new_raw(contract_data)); - evm.database(BenchmarkDB::new_bytecode(bytecode_raw)); + // BenchmarkDB is dummy state that implements Database trait. + let mut evm = Evm::builder() + .modify_tx_env(|tx| { + // execution globals block hash/gas_limit/coinbase/timestamp.. + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); + //evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); + tx.data = bytes!("8035F0CE"); + }) + .with_db(BenchmarkDB::new_bytecode(bytecode_raw)) + .build(); // just to spead up processor. for _ in 0..10000 { @@ -42,7 +38,10 @@ fn main() { } println!("Raw elapsed time: {:?}", timer.elapsed()); - evm.database(BenchmarkDB::new_bytecode(bytecode_checked)); + let mut evm = evm + .modify() + .reset_handler_with_db(BenchmarkDB::new_bytecode(bytecode_checked)) + .build(); let timer = Instant::now(); for _ in 0..30000 { @@ -50,7 +49,10 @@ fn main() { } println!("Checked elapsed time: {:?}", timer.elapsed()); - evm.database(BenchmarkDB::new_bytecode(bytecode_analysed)); + let mut evm = evm + .modify() + .reset_handler_with_db(BenchmarkDB::new_bytecode(bytecode_analysed)) + .build(); let timer = Instant::now(); for _ in 0..30000 { diff --git a/bins/revm-test/src/bin/snailtracer.rs b/bins/revm-test/src/bin/snailtracer.rs index 19ed7df595..94f3a6c9db 100644 --- a/bins/revm-test/src/bin/snailtracer.rs +++ b/bins/revm-test/src/bin/snailtracer.rs @@ -1,55 +1,36 @@ +use std::time::Duration; + use revm::{ db::BenchmarkDB, interpreter::analysis::to_analysed, - primitives::{Bytecode, Bytes, TransactTo}, + primitives::{address, bytes, Bytecode, Bytes, TransactTo}, + Evm, }; -extern crate alloc; pub fn simple_example() { - let contract_data : Bytes = hex::decode("608060405234801561001057600080fd5b506004361061004c5760003560e01c806330627b7c1461005157806375ac892a14610085578063784f13661461011d578063c294360114610146575b600080fd5b610059610163565b604080516001600160f81b03199485168152928416602084015292168183015290519081900360600190f35b6100a86004803603604081101561009b57600080fd5b50803590602001356102d1565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100e25781810151838201526020016100ca565b50505050905090810190601f16801561010f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100596004803603606081101561013357600080fd5b508035906020810135906040013561055b565b6100a86004803603602081101561015c57600080fd5b5035610590565b6000806000610176610400610300610834565b60405180606001604052806001546000546207d5dc028161019357fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526102259161021c916102139161020e91612ef7565b612f64565b6207d5dc612feb565b620f424061301e565b8051600e556020810151600f55604001516010556102416142dd565b61025a816102556102006101806008613064565b613212565b90506102708161025561014561021c6008613064565b905061028481610255610258806008613064565b905061029a8161025561020a61020c6008613064565b90506102a781600461301e565b90506102b1613250565b8051602082015160409092015160f891821b9692821b9550901b92509050565b606060005b6000548112156104c95760006102ed828686613064565b90506002816000015160f81b90808054603f811680603e811461032a576002830184556001831661031c578192505b600160028404019350610342565b600084815260209081902060ff198516905560419094555b505050600190038154600116156103685790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146103c557600283018455600183166103b7578192505b6001600284040193506103dd565b600084815260209081902060ff198516905560419094555b505050600190038154600116156104035790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146104605760028301845560018316610452578192505b600160028404019350610478565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561049e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016102d6565b506002805460408051602060018416156101000260001901909316849004601f8101849004840282018401909252818152929183018282801561054d5780601f106105225761010080835404028352916020019161054d565b820191906000526020600020905b81548152906001019060200180831161053057829003601f168201915b505050505090505b92915050565b60008060008061056c878787613064565b8051602082015160409092015160f891821b9a92821b9950901b9650945050505050565b600154606090600019015b600081126107a35760005b6000548112156107995760006105bd828487613064565b90506002816000015160f81b90808054603f811680603e81146105fa57600283018455600183166105ec578192505b600160028404019350610612565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106385790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146106955760028301845560018316610687578192505b6001600284040193506106ad565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106d35790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146107305760028301845560018316610722578192505b600160028404019350610748565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561076e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016105a6565b506000190161059b565b506002805460408051602060018416156101000260001901909316849004601f810184900484028201840190925281815292918301828280156108275780601f106107fc57610100808354040283529160200191610827565b820191906000526020600020905b81548152906001019060200180831161080a57829003601f168201915b505050505090505b919050565b8160008190555080600181905550604051806080016040528060405180606001604052806302faf08081526020016303197500815260200163119e7f8081525081526020016108a460405180606001604052806000815260200161a673198152602001620f423f19815250612f64565b815260006020808301829052604092830182905283518051600355808201516004558301516005558381015180516006559081015160075582015160085582820151600955606092830151600a805460ff1916911515919091179055815192830190915260015490548291906207d5dc028161091c57fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526109979161021c916102139161020e91612ef7565b8051600e55602080820151600f55604091820151601055815160a08101835264174876e8008152825160608082018552641748862a40825263026e8f00828501526304dd1e008286015282840191825284518082018652600080825281860181905281870181905284870191825286518084018852620b71b081526203d09081880181905281890152928501928352608085018181526011805460018082018355919093528651600b9093027f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c688101938455955180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c69880155808901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6a8801558901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6b870155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6c870155808801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6d8701558801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6e860155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6f860155958601517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c7085015594909501517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c71830155517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c72909101805492949192909160ff1990911690836002811115610c1057fe5b0217905550505060116040518060a0016040528064174876e8008152602001604051806060016040528064174290493f19815260200163026e8f0081526020016304dd1e008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806203d09081526020016203d0908152602001620b71b0815250815260200160006002811115610cb657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610d5857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164174876e800815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b0815250815260200160006002811115610dfd57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610e9f57fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164173e54e97f1981525081526020016040518060600160405280600081526020016000815260200160008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160006002811115610f3f57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610fe157fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174876e80081526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b081525081526020016000600281111561108657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561112857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174399c9ff1981526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b08152508152602001600060028111156111ce57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561127057fe5b0217905550505060116040518060a0016040528062fbc5208152602001604051806060016040528063019bfcc0815260200162fbc52081526020016302cd29c0815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561131157fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156113b357fe5b0217905550505060116040518060a001604052806323c34600815260200160405180606001604052806302faf080815260200163289c455081526020016304dd1e008152508152602001604051806060016040528062b71b00815260200162b71b00815260200162b71b00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016000600281111561145657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156114f857fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561160c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156116fd57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561180e57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156118ff57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611a1357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611b0457fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611c1557fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611d0657fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611e1a57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611f0b57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a6081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561201c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561210d57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561222157fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561231257fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561242357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561251457fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561262857fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561271957fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561282d57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561291e57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612a3257fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612b2357fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612c3757fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612d2857fe5b0217905550505060005b601254811015612ef257600060128281548110612d4b57fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115612e6c57fe5b6002811115612e7757fe5b815250509050612eac61020e612e95836020015184600001516132cd565b612ea7846040015185600001516132cd565b612ef7565b60128381548110612eb957fe5b60009182526020918290208351600960139093029091019182015590820151600a820155604090910151600b9091015550600101612d32565b505050565b612eff6142dd565b604051806060016040528083602001518560400151028460400151866020015102038152602001836040015185600001510284600001518660400151020381526020018360000151856020015102846020015186600001510203815250905092915050565b612f6c6142dd565b604082015160208301518351600092612f9292918002918002919091019080020161330c565b90506040518060600160405280828560000151620f42400281612fb157fe5b058152602001828560200151620f42400281612fc957fe5b058152602001828560400151620f42400281612fe157fe5b0590529392505050565b612ff36142dd565b5060408051606081018252835183028152602080850151840290820152928101519091029082015290565b6130266142dd565b60405180606001604052808385600001518161303e57fe5b0581526020018385602001518161305157fe5b05815260200183856040015181612fe157fe5b61306c6142dd565b6000546013805463ffffffff1916918502860163ffffffff169190911790556130936142dd565b905060005b828112156131f157600061317261314c61021c613115600b60405180606001604052908160008201548152602001600182015481526020016002820154815250506207a1206000546207a1206130ec613343565b63ffffffff16816130f957fe5b0663ffffffff168d620f424002018161310e57fe5b0503612feb565b60408051606081018252600e548152600f5460208201526010549181019190915260015461025591906207a12090816130ec613343565b604080516060810182526006548152600754602082015260085491810191909152613212565b6040805160e081019091526003546080820190815260045460a083015260055460c083015291925060009181906131ae9061025586608c612feb565b81526020016131bc84612f64565b815260006020820181905260409091015290506131e5846102556131df8461336c565b8861301e565b93505050600101613098565b5061320861021c61320183613753565b60ff612feb565b90505b9392505050565b61321a6142dd565b50604080516060810182528251845101815260208084015181860151019082015291810151928101519092019181019190915290565b60008080556001819055613266906002906142fe565b60006003819055600481905560058190556006819055600781905560088190556009819055600a805460ff19169055600b819055600c819055600d819055600e819055600f81905560108190556132bf90601190614345565b6132cb60126000614366565b565b6132d56142dd565b5060408051606081018252825184510381526020808401518186015103908201528282015184830151039181019190915292915050565b80600260018201055b8181121561333d5780915060028182858161332c57fe5b05018161333557fe5b059050613315565b50919050565b6013805463ffffffff19811663ffffffff9182166341c64e6d0261303901821617918290551690565b6133746142dd565b600a826040015113156133a657604051806060016040528060008152602001600081526020016000815250905061082f565b60008060006133b48561379f565b91945092509050826133e857604051806060016040528060008152602001600081526020016000815250935050505061082f565b6133f0614387565b6133f86143c7565b6134006142dd565b6134086142dd565b600086600181111561341657fe5b1415613505576011858154811061342957fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff909116908111156134e157fe5b60028111156134ec57fe5b8152505093508360600151915083604001519050613653565b6012858154811061351257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff9091169081111561363357fe5b600281111561363e57fe5b8152505092508260a001519150826080015190505b6040820151600190811215613669575060408201515b808360200151131561367c575060208201515b808360400151131561368f575060408201515b60408a01805160010190819052600512156136f75780620f42406136b1613343565b63ffffffff16816136be57fe5b0663ffffffff1612156136e8576136e16136db84620f4240612feb565b8261301e565b92506136f7565b50965061082f95505050505050565b6136ff6142dd565b600088600181111561370d57fe5b14156137255761371e8b878b613a57565b9050613733565b6137308b868b613aec565b90505b6137448361025561021c8785613baa565b9b9a5050505050505050505050565b61375b6142dd565b60405180606001604052806137738460000151613be8565b81526020016137858460200151613be8565b81526020016137978460400151613be8565b905292915050565b60008080808080805b6011548110156138c2576000613890601183815481106137c457fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff9091169081111561387c57fe5b600281111561388757fe5b9052508a613c13565b90506000811380156138a957508415806138a957508481125b156138b957809450600093508192505b506001016137a8565b5060005b601254811015613a49576000613a17601283815481106138e257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115613a0357fe5b6002811115613a0e57fe5b9052508a613cbb565b9050600081138015613a305750841580613a3057508481125b15613a4057809450600193508192505b506001016138c6565b509196909550909350915050565b613a5f6142dd565b6000613a7a856000015161025561021c886020015187612feb565b90506000613a8f61020e8387602001516132cd565b9050600085608001516002811115613aa357fe5b1415613ae1576000613ab9828860200151613e0c565b12613acd57613aca81600019612feb565b90505b613ad8868383613e31565b9250505061320b565b613ad8868383613fc1565b613af46142dd565b6000613b0f856000015161025561021c886020015187612feb565b6060860151909150620a2c2a9015613b2757506216e3605b6000620f4240613b3f87606001518960200151613e0c565b81613b4657fe5b05905060008112613b55576000035b64e8d4a5100081800281038380020281900590036000811215613b8c57613b8188858960600151613fc1565b94505050505061320b565b613b9e88858960600151868686614039565b98975050505050505050565b613bb26142dd565b50604080516060810182528251845102815260208084015181860151029082015291810151928101519092029181019190915290565b600080821215613bfa5750600061082f565b620f4240821315613c0f5750620f424061082f565b5090565b600080613c28846020015184600001516132cd565b90506000620f4240613c3e838660200151613e0c565b81613c4557fe5b865191900591506000908002613c5b8480613e0c565b838402030190506000811215613c775760009350505050610555565b613c808161330c565b90506103e88183031315613c9957900391506105559050565b6103e88183011315613caf570191506105559050565b50600095945050505050565b600080613cd0846020015185600001516132cd565b90506000613ce6856040015186600001516132cd565b90506000613cf8856020015183612ef7565b90506000620f4240613d0a8584613e0c565b81613d1157fe5b0590506103e71981138015613d2757506103e881125b15613d39576000945050505050610555565b85518751600091613d49916132cd565b9050600082613d588386613e0c565b81613d5f57fe5b0590506000811280613d735750620f424081135b15613d875760009650505050505050610555565b6000613d938388612ef7565b9050600084613da68b6020015184613e0c565b81613dad57fe5b0590506000811280613dc35750620f4240818401135b15613dd957600098505050505050505050610555565b600085613de68985613e0c565b81613ded57fe5b0590506103e88112156137445760009950505050505050505050610555565b6040808201519083015160208084015190850151845186510291020191020192915050565b613e396142dd565b6000620f424080613e48613343565b63ffffffff1681613e5557fe5b0663ffffffff16625fdfb00281613e6857fe5b0590506000620f4240613e79613343565b63ffffffff1681613e8657fe5b0663ffffffff1690506000613e9a8261330c565b6103e8029050613ea86142dd565b620186a0613eb98760000151614216565b1315613ee657604051806060016040528060008152602001620f4240815260200160008152509050613f09565b6040518060600160405280620f4240815260200160008152602001600081525090505b613f1661020e8288612ef7565b90506000613f2761020e8884612ef7565b9050613f7f61020e613f64613f5285620f424088613f448c61422e565b0281613f4c57fe5b05612feb565b61025585620f424089613f448d61424e565b6102558a613f7689620f42400361330c565b6103e802612feb565b9150613fb460405180608001604052808a81526020018481526020018b6040015181526020018b60600151151581525061336c565b9998505050505050505050565b613fc96142dd565b6000613ffb61020e8660200151613ff686620f4240613fec898c60200151613e0c565b60020281613f4c57fe5b6132cd565b90506140306040518060800160405280868152602001838152602001876040015181526020018760600151151581525061336c565b95945050505050565b6140416142dd565b60608701516000199015614053575060015b600061408961020e61021c61406c8c602001518a612feb565b613ff68b6140798a61330c565b620f42408c8e0205018802612feb565b60608a0151909150620f42408601906140ba57620f42406140aa838a613e0c565b816140b157fe5b05620f42400390505b60408a0151619c406c0c9f2c9cd04674edea40000000620ea6008480028502850285020205019060021261415e5761412a61411f60405180608001604052808d81526020018681526020018e6040015181526020018e6060015115151581525061336c565b82620f424003612feb565b92506141448361025561413e8e8e8e613fc1565b84612feb565b925061415383620f424061301e565b94505050505061420c565b600281056203d09001620f4240614173613343565b63ffffffff168161418057fe5b0663ffffffff1612156141b2576141536141a461419e8d8d8d613fc1565b83612feb565b600283056203d0900161301e565b6142056141f76141ec60405180608001604052808e81526020018781526020018f6040015181526020018f6060015115151581525061336c565b83620f424003612feb565b60028305620b71b00361301e565b9450505050505b9695505050505050565b60008082131561422757508061082f565b5060000390565b60008061423a8361424e565b905061320b81820264e8d4a510000361330c565b60005b600082121561426757625fdfb082019150614251565b5b625fdfb0821261427f57625fdfb082039150614268565b6001828160025b818313156142d457818385028161429957fe5b0585019450620f4240808788860202816142af57fe5b05816142b757fe5b600095909503940592506001810181029190910290600201614286565b50505050919050565b60405180606001604052806000815260200160008152602001600081525090565b50805460018160011615610100020316600290046000825580601f106143245750614342565b601f0160209004906000526020600020908101906143429190614401565b50565b50805460008255600b02906000526020600020908101906143429190614416565b50805460008255601302906000526020600020908101906143429190614475565b6040518060a00160405280600081526020016143a16142dd565b81526020016143ae6142dd565b81526020016143bb6142dd565b81526020016000905290565b6040518060e001604052806143da6142dd565b81526020016143e76142dd565b81526020016143f46142dd565b81526020016143a16142dd565b5b80821115613c0f5760008155600101614402565b5b80821115613c0f57600080825560018201819055600282018190556003820181905560048201819055600582018190556006820181905560078201819055600882018190556009820155600a8101805460ff19169055600b01614417565b5b80821115613c0f576000808255600182018190556002820181905560038201819055600482018190556005820181905560068201819055600782018190556008820181905560098201819055600a8201819055600b8201819055600c8201819055600d8201819055600e8201819055600f820181905560108201819055601182015560128101805460ff1916905560130161447656fea2646970667358221220037024f5647853879c58fbcc61ac3616455f6f731cc6e84f91eb5a3b4e06c00464736f6c63430007060033").unwrap().into(); + let bytecode = to_analysed(Bytecode::new_raw(CONTRACT_DATA.clone())); // BenchmarkDB is dummy state that implements Database trait. - let mut evm = revm::new(); - let bytecode = to_analysed(Bytecode::new_raw(contract_data)); - evm.database(BenchmarkDB::new_bytecode(bytecode.clone())); - - // execution globals block hash/gas_limit/coinbase/timestamp.. - evm.env.tx.caller = "0x1000000000000000000000000000000000000000" - .parse() - .unwrap(); - evm.env.tx.transact_to = TransactTo::Call( - "0x0000000000000000000000000000000000000000" - .parse() - .unwrap(), - ); - evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); + let mut evm = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) + .modify_tx_env(|tx| { + // execution globals block hash/gas_limit/coinbase/timestamp.. + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); + tx.data = bytes!("30627b7c"); + }) + .build(); // Microbenchmark - //let bench_options = microbench::Options::default().time(Duration::from_secs(2)); + let bench_options = microbench::Options::default().time(Duration::from_secs(2)); - //let env = evm.env.clone(); - //microbench::bench( - // &bench_options, - // "Snailtracer Host+Interpreter benchmark", - // || { - let _ = evm.transact().unwrap(); - // }, - //); - /* - // revm interpreter - let contract = Contract { - input: evm.env.tx.data, - bytecode: BytecodeLocked::try_from(bytecode).unwrap(), - ..Default::default() - }; - - let mut host = DummyHost::new(env); - microbench::bench(&bench_options, "Snailtracer Interpreter benchmark", || { - let mut interpreter = Interpreter::new(Box::new(contract.clone()), u64::MAX, false); - interpreter.run::<_, BerlinSpec>(&mut host); - host.clear() - }); - */ + microbench::bench( + &bench_options, + "Snailtracer Host+Interpreter benchmark", + || { + let _ = evm.transact(); + }, + ); } fn main() { @@ -57,3 +38,5 @@ fn main() { simple_example(); //println!("end!"); } + +static CONTRACT_DATA : Bytes = bytes!("608060405234801561001057600080fd5b506004361061004c5760003560e01c806330627b7c1461005157806375ac892a14610085578063784f13661461011d578063c294360114610146575b600080fd5b610059610163565b604080516001600160f81b03199485168152928416602084015292168183015290519081900360600190f35b6100a86004803603604081101561009b57600080fd5b50803590602001356102d1565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100e25781810151838201526020016100ca565b50505050905090810190601f16801561010f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100596004803603606081101561013357600080fd5b508035906020810135906040013561055b565b6100a86004803603602081101561015c57600080fd5b5035610590565b6000806000610176610400610300610834565b60405180606001604052806001546000546207d5dc028161019357fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526102259161021c916102139161020e91612ef7565b612f64565b6207d5dc612feb565b620f424061301e565b8051600e556020810151600f55604001516010556102416142dd565b61025a816102556102006101806008613064565b613212565b90506102708161025561014561021c6008613064565b905061028481610255610258806008613064565b905061029a8161025561020a61020c6008613064565b90506102a781600461301e565b90506102b1613250565b8051602082015160409092015160f891821b9692821b9550901b92509050565b606060005b6000548112156104c95760006102ed828686613064565b90506002816000015160f81b90808054603f811680603e811461032a576002830184556001831661031c578192505b600160028404019350610342565b600084815260209081902060ff198516905560419094555b505050600190038154600116156103685790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146103c557600283018455600183166103b7578192505b6001600284040193506103dd565b600084815260209081902060ff198516905560419094555b505050600190038154600116156104035790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146104605760028301845560018316610452578192505b600160028404019350610478565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561049e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016102d6565b506002805460408051602060018416156101000260001901909316849004601f8101849004840282018401909252818152929183018282801561054d5780601f106105225761010080835404028352916020019161054d565b820191906000526020600020905b81548152906001019060200180831161053057829003601f168201915b505050505090505b92915050565b60008060008061056c878787613064565b8051602082015160409092015160f891821b9a92821b9950901b9650945050505050565b600154606090600019015b600081126107a35760005b6000548112156107995760006105bd828487613064565b90506002816000015160f81b90808054603f811680603e81146105fa57600283018455600183166105ec578192505b600160028404019350610612565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106385790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146106955760028301845560018316610687578192505b6001600284040193506106ad565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106d35790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146107305760028301845560018316610722578192505b600160028404019350610748565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561076e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016105a6565b506000190161059b565b506002805460408051602060018416156101000260001901909316849004601f810184900484028201840190925281815292918301828280156108275780601f106107fc57610100808354040283529160200191610827565b820191906000526020600020905b81548152906001019060200180831161080a57829003601f168201915b505050505090505b919050565b8160008190555080600181905550604051806080016040528060405180606001604052806302faf08081526020016303197500815260200163119e7f8081525081526020016108a460405180606001604052806000815260200161a673198152602001620f423f19815250612f64565b815260006020808301829052604092830182905283518051600355808201516004558301516005558381015180516006559081015160075582015160085582820151600955606092830151600a805460ff1916911515919091179055815192830190915260015490548291906207d5dc028161091c57fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526109979161021c916102139161020e91612ef7565b8051600e55602080820151600f55604091820151601055815160a08101835264174876e8008152825160608082018552641748862a40825263026e8f00828501526304dd1e008286015282840191825284518082018652600080825281860181905281870181905284870191825286518084018852620b71b081526203d09081880181905281890152928501928352608085018181526011805460018082018355919093528651600b9093027f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c688101938455955180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c69880155808901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6a8801558901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6b870155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6c870155808801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6d8701558801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6e860155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6f860155958601517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c7085015594909501517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c71830155517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c72909101805492949192909160ff1990911690836002811115610c1057fe5b0217905550505060116040518060a0016040528064174876e8008152602001604051806060016040528064174290493f19815260200163026e8f0081526020016304dd1e008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806203d09081526020016203d0908152602001620b71b0815250815260200160006002811115610cb657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610d5857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164174876e800815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b0815250815260200160006002811115610dfd57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610e9f57fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164173e54e97f1981525081526020016040518060600160405280600081526020016000815260200160008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160006002811115610f3f57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610fe157fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174876e80081526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b081525081526020016000600281111561108657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561112857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174399c9ff1981526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b08152508152602001600060028111156111ce57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561127057fe5b0217905550505060116040518060a0016040528062fbc5208152602001604051806060016040528063019bfcc0815260200162fbc52081526020016302cd29c0815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561131157fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156113b357fe5b0217905550505060116040518060a001604052806323c34600815260200160405180606001604052806302faf080815260200163289c455081526020016304dd1e008152508152602001604051806060016040528062b71b00815260200162b71b00815260200162b71b00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016000600281111561145657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156114f857fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561160c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156116fd57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561180e57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156118ff57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611a1357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611b0457fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611c1557fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611d0657fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611e1a57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611f0b57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a6081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561201c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561210d57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561222157fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561231257fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561242357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561251457fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561262857fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561271957fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561282d57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561291e57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612a3257fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612b2357fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612c3757fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612d2857fe5b0217905550505060005b601254811015612ef257600060128281548110612d4b57fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115612e6c57fe5b6002811115612e7757fe5b815250509050612eac61020e612e95836020015184600001516132cd565b612ea7846040015185600001516132cd565b612ef7565b60128381548110612eb957fe5b60009182526020918290208351600960139093029091019182015590820151600a820155604090910151600b9091015550600101612d32565b505050565b612eff6142dd565b604051806060016040528083602001518560400151028460400151866020015102038152602001836040015185600001510284600001518660400151020381526020018360000151856020015102846020015186600001510203815250905092915050565b612f6c6142dd565b604082015160208301518351600092612f9292918002918002919091019080020161330c565b90506040518060600160405280828560000151620f42400281612fb157fe5b058152602001828560200151620f42400281612fc957fe5b058152602001828560400151620f42400281612fe157fe5b0590529392505050565b612ff36142dd565b5060408051606081018252835183028152602080850151840290820152928101519091029082015290565b6130266142dd565b60405180606001604052808385600001518161303e57fe5b0581526020018385602001518161305157fe5b05815260200183856040015181612fe157fe5b61306c6142dd565b6000546013805463ffffffff1916918502860163ffffffff169190911790556130936142dd565b905060005b828112156131f157600061317261314c61021c613115600b60405180606001604052908160008201548152602001600182015481526020016002820154815250506207a1206000546207a1206130ec613343565b63ffffffff16816130f957fe5b0663ffffffff168d620f424002018161310e57fe5b0503612feb565b60408051606081018252600e548152600f5460208201526010549181019190915260015461025591906207a12090816130ec613343565b604080516060810182526006548152600754602082015260085491810191909152613212565b6040805160e081019091526003546080820190815260045460a083015260055460c083015291925060009181906131ae9061025586608c612feb565b81526020016131bc84612f64565b815260006020820181905260409091015290506131e5846102556131df8461336c565b8861301e565b93505050600101613098565b5061320861021c61320183613753565b60ff612feb565b90505b9392505050565b61321a6142dd565b50604080516060810182528251845101815260208084015181860151019082015291810151928101519092019181019190915290565b60008080556001819055613266906002906142fe565b60006003819055600481905560058190556006819055600781905560088190556009819055600a805460ff19169055600b819055600c819055600d819055600e819055600f81905560108190556132bf90601190614345565b6132cb60126000614366565b565b6132d56142dd565b5060408051606081018252825184510381526020808401518186015103908201528282015184830151039181019190915292915050565b80600260018201055b8181121561333d5780915060028182858161332c57fe5b05018161333557fe5b059050613315565b50919050565b6013805463ffffffff19811663ffffffff9182166341c64e6d0261303901821617918290551690565b6133746142dd565b600a826040015113156133a657604051806060016040528060008152602001600081526020016000815250905061082f565b60008060006133b48561379f565b91945092509050826133e857604051806060016040528060008152602001600081526020016000815250935050505061082f565b6133f0614387565b6133f86143c7565b6134006142dd565b6134086142dd565b600086600181111561341657fe5b1415613505576011858154811061342957fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff909116908111156134e157fe5b60028111156134ec57fe5b8152505093508360600151915083604001519050613653565b6012858154811061351257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff9091169081111561363357fe5b600281111561363e57fe5b8152505092508260a001519150826080015190505b6040820151600190811215613669575060408201515b808360200151131561367c575060208201515b808360400151131561368f575060408201515b60408a01805160010190819052600512156136f75780620f42406136b1613343565b63ffffffff16816136be57fe5b0663ffffffff1612156136e8576136e16136db84620f4240612feb565b8261301e565b92506136f7565b50965061082f95505050505050565b6136ff6142dd565b600088600181111561370d57fe5b14156137255761371e8b878b613a57565b9050613733565b6137308b868b613aec565b90505b6137448361025561021c8785613baa565b9b9a5050505050505050505050565b61375b6142dd565b60405180606001604052806137738460000151613be8565b81526020016137858460200151613be8565b81526020016137978460400151613be8565b905292915050565b60008080808080805b6011548110156138c2576000613890601183815481106137c457fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff9091169081111561387c57fe5b600281111561388757fe5b9052508a613c13565b90506000811380156138a957508415806138a957508481125b156138b957809450600093508192505b506001016137a8565b5060005b601254811015613a49576000613a17601283815481106138e257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115613a0357fe5b6002811115613a0e57fe5b9052508a613cbb565b9050600081138015613a305750841580613a3057508481125b15613a4057809450600193508192505b506001016138c6565b509196909550909350915050565b613a5f6142dd565b6000613a7a856000015161025561021c886020015187612feb565b90506000613a8f61020e8387602001516132cd565b9050600085608001516002811115613aa357fe5b1415613ae1576000613ab9828860200151613e0c565b12613acd57613aca81600019612feb565b90505b613ad8868383613e31565b9250505061320b565b613ad8868383613fc1565b613af46142dd565b6000613b0f856000015161025561021c886020015187612feb565b6060860151909150620a2c2a9015613b2757506216e3605b6000620f4240613b3f87606001518960200151613e0c565b81613b4657fe5b05905060008112613b55576000035b64e8d4a5100081800281038380020281900590036000811215613b8c57613b8188858960600151613fc1565b94505050505061320b565b613b9e88858960600151868686614039565b98975050505050505050565b613bb26142dd565b50604080516060810182528251845102815260208084015181860151029082015291810151928101519092029181019190915290565b600080821215613bfa5750600061082f565b620f4240821315613c0f5750620f424061082f565b5090565b600080613c28846020015184600001516132cd565b90506000620f4240613c3e838660200151613e0c565b81613c4557fe5b865191900591506000908002613c5b8480613e0c565b838402030190506000811215613c775760009350505050610555565b613c808161330c565b90506103e88183031315613c9957900391506105559050565b6103e88183011315613caf570191506105559050565b50600095945050505050565b600080613cd0846020015185600001516132cd565b90506000613ce6856040015186600001516132cd565b90506000613cf8856020015183612ef7565b90506000620f4240613d0a8584613e0c565b81613d1157fe5b0590506103e71981138015613d2757506103e881125b15613d39576000945050505050610555565b85518751600091613d49916132cd565b9050600082613d588386613e0c565b81613d5f57fe5b0590506000811280613d735750620f424081135b15613d875760009650505050505050610555565b6000613d938388612ef7565b9050600084613da68b6020015184613e0c565b81613dad57fe5b0590506000811280613dc35750620f4240818401135b15613dd957600098505050505050505050610555565b600085613de68985613e0c565b81613ded57fe5b0590506103e88112156137445760009950505050505050505050610555565b6040808201519083015160208084015190850151845186510291020191020192915050565b613e396142dd565b6000620f424080613e48613343565b63ffffffff1681613e5557fe5b0663ffffffff16625fdfb00281613e6857fe5b0590506000620f4240613e79613343565b63ffffffff1681613e8657fe5b0663ffffffff1690506000613e9a8261330c565b6103e8029050613ea86142dd565b620186a0613eb98760000151614216565b1315613ee657604051806060016040528060008152602001620f4240815260200160008152509050613f09565b6040518060600160405280620f4240815260200160008152602001600081525090505b613f1661020e8288612ef7565b90506000613f2761020e8884612ef7565b9050613f7f61020e613f64613f5285620f424088613f448c61422e565b0281613f4c57fe5b05612feb565b61025585620f424089613f448d61424e565b6102558a613f7689620f42400361330c565b6103e802612feb565b9150613fb460405180608001604052808a81526020018481526020018b6040015181526020018b60600151151581525061336c565b9998505050505050505050565b613fc96142dd565b6000613ffb61020e8660200151613ff686620f4240613fec898c60200151613e0c565b60020281613f4c57fe5b6132cd565b90506140306040518060800160405280868152602001838152602001876040015181526020018760600151151581525061336c565b95945050505050565b6140416142dd565b60608701516000199015614053575060015b600061408961020e61021c61406c8c602001518a612feb565b613ff68b6140798a61330c565b620f42408c8e0205018802612feb565b60608a0151909150620f42408601906140ba57620f42406140aa838a613e0c565b816140b157fe5b05620f42400390505b60408a0151619c406c0c9f2c9cd04674edea40000000620ea6008480028502850285020205019060021261415e5761412a61411f60405180608001604052808d81526020018681526020018e6040015181526020018e6060015115151581525061336c565b82620f424003612feb565b92506141448361025561413e8e8e8e613fc1565b84612feb565b925061415383620f424061301e565b94505050505061420c565b600281056203d09001620f4240614173613343565b63ffffffff168161418057fe5b0663ffffffff1612156141b2576141536141a461419e8d8d8d613fc1565b83612feb565b600283056203d0900161301e565b6142056141f76141ec60405180608001604052808e81526020018781526020018f6040015181526020018f6060015115151581525061336c565b83620f424003612feb565b60028305620b71b00361301e565b9450505050505b9695505050505050565b60008082131561422757508061082f565b5060000390565b60008061423a8361424e565b905061320b81820264e8d4a510000361330c565b60005b600082121561426757625fdfb082019150614251565b5b625fdfb0821261427f57625fdfb082039150614268565b6001828160025b818313156142d457818385028161429957fe5b0585019450620f4240808788860202816142af57fe5b05816142b757fe5b600095909503940592506001810181029190910290600201614286565b50505050919050565b60405180606001604052806000815260200160008152602001600081525090565b50805460018160011615610100020316600290046000825580601f106143245750614342565b601f0160209004906000526020600020908101906143429190614401565b50565b50805460008255600b02906000526020600020908101906143429190614416565b50805460008255601302906000526020600020908101906143429190614475565b6040518060a00160405280600081526020016143a16142dd565b81526020016143ae6142dd565b81526020016143bb6142dd565b81526020016000905290565b6040518060e001604052806143da6142dd565b81526020016143e76142dd565b81526020016143f46142dd565b81526020016143a16142dd565b5b80821115613c0f5760008155600101614402565b5b80821115613c0f57600080825560018201819055600282018190556003820181905560048201819055600582018190556006820181905560078201819055600882018190556009820155600a8101805460ff19169055600b01614417565b5b80821115613c0f576000808255600182018190556002820181905560038201819055600482018190556005820181905560068201819055600782018190556008820181905560098201819055600a8201819055600b8201819055600c8201819055600d8201819055600e8201819055600f820181905560108201819055601182015560128101805460ff1916905560130161447656fea2646970667358221220037024f5647853879c58fbcc61ac3616455f6f731cc6e84f91eb5a3b4e06c00464736f6c63430007060033"); diff --git a/bins/revm-test/src/bin/transfer.rs b/bins/revm-test/src/bin/transfer.rs index ff74d442c4..2af94b040d 100644 --- a/bins/revm-test/src/bin/transfer.rs +++ b/bins/revm-test/src/bin/transfer.rs @@ -1,39 +1,33 @@ use revm::{ db::BenchmarkDB, primitives::{Bytecode, TransactTo, U256}, + Evm, }; -use std::time::{Duration, Instant}; +use std::time::Duration; extern crate alloc; fn main() { // BenchmarkDB is dummy state that implements Database trait. - let mut evm = revm::new(); - - // execution globals block hash/gas_limit/coinbase/timestamp.. - evm.env.tx.caller = "0x0000000000000000000000000000000000000001" - .parse() - .unwrap(); - evm.env.tx.value = U256::from(10); - evm.env.tx.transact_to = TransactTo::Call( - "0x0000000000000000000000000000000000000000" - .parse() - .unwrap(), - ); - //evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); - - evm.database(BenchmarkDB::new_bytecode(Bytecode::new())); + let mut evm = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(Bytecode::new())) + .modify_tx_env(|tx| { + // execution globals block hash/gas_limit/coinbase/timestamp.. + tx.caller = "0x0000000000000000000000000000000000000001" + .parse() + .unwrap(); + tx.value = U256::from(10); + tx.transact_to = TransactTo::Call( + "0x0000000000000000000000000000000000000000" + .parse() + .unwrap(), + ); + }) + .build(); // Microbenchmark - let bench_options = microbench::Options::default().time(Duration::from_secs(1)); + let bench_options = microbench::Options::default().time(Duration::from_secs(3)); microbench::bench(&bench_options, "Simple value transfer", || { let _ = evm.transact().unwrap(); }); - - let time = Instant::now(); - for _ in 0..10000 { - let _ = evm.transact().unwrap(); - } - let elapsed = time.elapsed(); - println!("10k runs in {:?}", elapsed.as_nanos() / 10_000); } diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index 3a490174c4..97c424dfb8 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -1,17 +1,21 @@ use super::{ merkle_trie::{log_rlp_hash, state_merkle_trie_root}, - models::{SpecName, TestSuite}, + models::{SpecName, Test, TestSuite}, }; use indicatif::ProgressBar; use revm::{ + db::EmptyDB, + inspector_handle_register, inspectors::TracerEip3155, interpreter::CreateScheme, primitives::{ - address, b256, calc_excess_blob_gas, keccak256, Bytecode, Env, HashMap, SpecId, TransactTo, - B256, U256, + address, b256, calc_excess_blob_gas, keccak256, Bytecode, EVMResultGeneric, Env, + ExecutionResult, HashMap, SpecId, TransactTo, B256, U256, }, + Evm, State, }; use std::{ + convert::Infallible, io::stdout, path::{Path, PathBuf}, sync::atomic::Ordering, @@ -96,6 +100,65 @@ fn skip_test(path: &Path) -> bool { ) || path_str.contains("stEOF") } +fn check_evm_execution( + test: &Test, + test_name: &str, + exec_result: &EVMResultGeneric, + evm: &Evm<'_, EXT, &mut State>, +) -> Result<(), TestError> { + // if we expect exception revm should return error from execution. + // So we do not check logs and state root. + // + // Note that some tests that have exception and run tests from before state clear + // would touch the caller account and make it appear in state root calculation. + // This is not something that we would expect as invalid tx should not touch state. + // but as this is a cleanup of invalid tx it is not properly defined and in the end + // it does not matter. + // Test where this happens: `tests/GeneralStateTests/stTransactionTest/NoSrcAccountCreate.json` + // and you can check that we have only two "hash" values for before and after state clear. + match (&test.expect_exception, exec_result) { + // do nothing + (None, Ok(_)) => (), + // return okay, exception is expected. + (Some(_), Err(_)) => return Ok(()), + _ => { + return Err(TestError { + name: test_name.to_string(), + kind: TestErrorKind::UnexpectedException { + expected_exception: test.expect_exception.clone(), + got_exception: exec_result.clone().err().map(|e| e.to_string()), + }, + }); + } + } + + let logs_root = log_rlp_hash(&exec_result.as_ref().map(|r| r.logs()).unwrap_or_default()); + + if logs_root != test.logs { + return Err(TestError { + name: test_name.to_string(), + kind: TestErrorKind::LogsRootMismatch { + got: logs_root, + expected: test.logs, + }, + }); + } + + let state_root = state_merkle_trie_root(evm.context.evm.db.cache.trie_account()); + + if state_root != test.hash { + return Err(TestError { + name: test_name.to_string(), + kind: TestErrorKind::StateRootMismatch { + got: state_root, + expected: test.hash, + }, + }); + } + + Ok(()) +} + pub fn execute_test_suite( path: &Path, elapsed: &Arc>, @@ -205,7 +268,7 @@ pub fn execute_test_suite( continue; } - env.cfg.spec_id = spec_name.to_spec_id(); + let spec_id = spec_name.to_spec_id(); for (index, test) in tests.into_iter().enumerate() { env.tx.gas_limit = unit.transaction.gas_limit[test.indexes.gas].saturating_to(); @@ -244,86 +307,48 @@ pub fn execute_test_suite( let mut cache = cache_state.clone(); cache.set_state_clear_flag(SpecId::enabled( - env.cfg.spec_id, + spec_id, revm::primitives::SpecId::SPURIOUS_DRAGON, )); let mut state = revm::db::State::builder() .with_cached_prestate(cache) .with_bundle_update() .build(); - let mut evm = revm::new(); - evm.database(&mut state); - evm.env = env.clone(); + let mut evm = Evm::builder() + .with_db(&mut state) + .modify_env(|e| *e = env.clone()) + .spec_id(spec_id) + .build(); // do the deed let timer = Instant::now(); - let exec_result = if trace { - evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)) + let (e, exec_result) = if trace { + let mut evm = evm + .modify() + .reset_handler_with_external_context(TracerEip3155::new( + Box::new(stdout()), + false, + true, + )) + .append_handler_register(inspector_handle_register) + .build(); + let res = evm.transact_commit(); + + let Err(e) = check_evm_execution(&test, &name, &res, &evm) else { + continue; + }; + // reset external context + (e, res) } else { - evm.transact_commit() - }; - *elapsed.lock().unwrap() += timer.elapsed(); + let res = evm.transact_commit(); - // validate results - // this is in a closure so we can have a common printing routine for errors - let check = || { - // if we expect exception revm should return error from execution. - // So we do not check logs and state root. - // - // Note that some tests that have exception and run tests from before state clear - // would touch the caller account and make it appear in state root calculation. - // This is not something that we would expect as invalid tx should not touch state. - // but as this is a cleanup of invalid tx it is not properly defined and in the end - // it does not matter. - // Test where this happens: `tests/GeneralStateTests/stTransactionTest/NoSrcAccountCreate.json` - // and you can check that we have only two "hash" values for before and after state clear. - match (&test.expect_exception, &exec_result) { - // do nothing - (None, Ok(_)) => (), - // return okay, exception is expected. - (Some(_), Err(_)) => return Ok(()), - _ => { - return Err(TestError { - name: name.clone(), - kind: TestErrorKind::UnexpectedException { - expected_exception: test.expect_exception.clone(), - got_exception: exec_result.clone().err().map(|e| e.to_string()), - }, - }); - } - } - - let logs_root = - log_rlp_hash(&exec_result.as_ref().map(|r| r.logs()).unwrap_or_default()); - - if logs_root != test.logs { - return Err(TestError { - name: name.clone(), - kind: TestErrorKind::LogsRootMismatch { - got: logs_root, - expected: test.logs, - }, - }); - } - - let db = evm.db.as_ref().unwrap(); - let state_root = state_merkle_trie_root(db.cache.trie_account()); - - if state_root != test.hash { - return Err(TestError { - name: name.clone(), - kind: TestErrorKind::StateRootMismatch { - got: state_root, - expected: test.hash, - }, - }); - } - - Ok(()) + // dump state and traces if test failed + let Err(e) = check_evm_execution(&test, &name, &res, &evm) else { + continue; + }; + (e, res) }; - - // dump state and traces if test failed - let Err(e) = check() else { continue }; + *elapsed.lock().unwrap() += timer.elapsed(); // print only once static FAILED: AtomicBool = AtomicBool::new(false); @@ -334,22 +359,29 @@ pub fn execute_test_suite( // re build to run with tracing let mut cache = cache_state.clone(); cache.set_state_clear_flag(SpecId::enabled( - env.cfg.spec_id, + spec_id, revm::primitives::SpecId::SPURIOUS_DRAGON, )); - let mut state = revm::db::StateBuilder::default() + let state = revm::db::State::builder() .with_cached_prestate(cache) + .with_bundle_update() .build(); - evm.database(&mut state); let path = path.display(); println!("\nTraces:"); - let _ = evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)); + let mut evm = Evm::builder() + .spec_id(spec_id) + .with_db(state) + .with_external_context(TracerEip3155::new(Box::new(stdout()), false, false)) + .append_handler_register(inspector_handle_register) + .build(); + let _ = evm.transact_commit(); println!("\nExecution result: {exec_result:#?}"); println!("\nExpected exception: {:?}", test.expect_exception); println!("\nState before: {cache_state:#?}"); - println!("\nState after: {:#?}", evm.db().unwrap().cache); + 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}"); diff --git a/book.toml b/book.toml index ffe9273021..8395e94b05 100644 --- a/book.toml +++ b/book.toml @@ -1,5 +1,5 @@ [book] -authors = ["Colin Roberts, Waylon Jepsen"] +authors = ["Colin Roberts, Waylon Jepsen", "Dragan Rakita"] language = "en" multilingual = false src = "documentation/src" diff --git a/crates/interpreter/src/gas.rs b/crates/interpreter/src/gas.rs index 1042262a97..e700c5501b 100644 --- a/crates/interpreter/src/gas.rs +++ b/crates/interpreter/src/gas.rs @@ -5,6 +5,7 @@ mod constants; pub use calc::*; pub use constants::*; +use revm_primitives::{Spec, SpecId::LONDON}; /// Represents the state of gas during execution. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] @@ -80,6 +81,16 @@ impl Gas { self.refunded += refund; } + /// Set a refund value for final refund. + /// + /// Max refund value is limited to Nth part (depending of fork) of gas spend. + /// + /// Related to EIP-3529: Reduction in refunds + pub fn set_final_refund(&mut self) { + let max_refund_quotient = if SPEC::enabled(LONDON) { 5 } else { 2 }; + self.refunded = (self.refunded() as u64).min(self.spend() / max_refund_quotient) as i64; + } + /// Set a refund value pub fn set_refund(&mut self, refund: i64) { self.refunded = refund; diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index e5b627b7e7..1200bf9709 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -333,7 +333,7 @@ pub fn memory_gas(a: usize) -> u64 { /// Initial gas that is deducted for transaction to be included. /// Initial gas contains initial stipend gas, gas for access list and input data. -pub fn initial_tx_gas( +pub fn validate_initial_tx_gas( input: &[u8], is_create: bool, access_list: &[(Address, Vec)], diff --git a/crates/interpreter/src/inner_models.rs b/crates/interpreter/src/inner_models.rs index 29795e3189..1368634bf2 100644 --- a/crates/interpreter/src/inner_models.rs +++ b/crates/interpreter/src/inner_models.rs @@ -1,3 +1,5 @@ +use revm_primitives::{TransactTo, TxEnv}; + pub use crate::primitives::CreateScheme; use crate::primitives::{Address, Bytes, U256}; @@ -35,7 +37,47 @@ pub struct CreateInputs { pub gas_limit: u64, } +impl CallInputs { + pub fn new(tx_env: &TxEnv, gas_limit: u64) -> Option { + let TransactTo::Call(address) = tx_env.transact_to else { + return None; + }; + + Some(CallInputs { + contract: address, + transfer: Transfer { + source: tx_env.caller, + target: address, + value: tx_env.value, + }, + input: tx_env.data.clone(), + gas_limit, + context: CallContext { + caller: tx_env.caller, + address, + code_address: address, + apparent_value: tx_env.value, + scheme: CallScheme::Call, + }, + is_static: false, + }) + } +} + impl CreateInputs { + pub fn new(tx_env: &TxEnv, gas_limit: u64) -> Option { + let TransactTo::Create(scheme) = tx_env.transact_to else { + return None; + }; + + Some(CreateInputs { + caller: tx_env.caller, + scheme, + value: tx_env.value, + init_code: tx_env.data.clone(), + gas_limit, + }) + } /// Returns the address that this create call will create. pub fn created_address(&self, nonce: u64) -> Address { match self.scheme { diff --git a/crates/interpreter/src/instructions/opcode.rs b/crates/interpreter/src/instructions/opcode.rs index cc129bfcb0..f6265d122b 100644 --- a/crates/interpreter/src/instructions/opcode.rs +++ b/crates/interpreter/src/instructions/opcode.rs @@ -7,7 +7,6 @@ use crate::{ Host, Interpreter, }; use alloc::boxed::Box; -use alloc::sync::Arc; use core::fmt; /// EVM opcode function signature. @@ -17,36 +16,21 @@ pub type Instruction = fn(&mut Interpreter, &mut H); /// 256 EVM opcodes. pub type InstructionTable = [Instruction; 256]; -/// Arc over plain instruction table -pub type InstructionTableArc = Arc>; - /// EVM opcode function signature. pub type BoxedInstruction<'a, H> = Box; /// A table of instructions. pub type BoxedInstructionTable<'a, H> = [BoxedInstruction<'a, H>; 256]; -/// Arc over instruction table -pub type BoxedInstructionTableArc<'a, H> = Arc>; - /// Instruction set that contains plain instruction table that contains simple `fn` function pointer. /// and Boxed `Fn` variant that contains `Box` function pointer that can be used with closured. /// /// Note that `Plain` variant gives us 10-20% faster Interpreter execution. /// /// Boxed variant can be used to wrap plain function pointer with closure. -pub enum InstructionTables<'a, H> { - Plain(InstructionTableArc), - Boxed(BoxedInstructionTableArc<'a, H>), -} - -impl<'a, H> Clone for InstructionTables<'a, H> { - fn clone(&self) -> Self { - match self { - Self::Plain(table) => Self::Plain(table.clone()), - Self::Boxed(table) => Self::Boxed(table.clone()), - } - } +pub enum InstructionTables<'a, H: Host> { + Plain(InstructionTable), + Boxed(BoxedInstructionTable<'a, H>), } macro_rules! opcodes { @@ -95,7 +79,7 @@ pub fn make_boxed_instruction_table<'a, H, SPEC, FN>( outer: FN, ) -> BoxedInstructionTable<'a, H> where - H: Host + 'a, + H: Host, SPEC: Spec + 'static, FN: Fn(Instruction) -> BoxedInstruction<'a, H>, { diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 743e161e3b..1caed8a239 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -95,8 +95,7 @@ impl Interpreter { /// When sub create call returns we can insert output of that call into this interpreter. pub fn insert_create_output(&mut self, result: InterpreterResult, address: Option
) { - let interpreter = self; - interpreter.return_data_buffer = match result.result { + self.return_data_buffer = match result.result { // Save data to return data buffer if the create reverted return_revert!() => result.output, // Otherwise clear it @@ -105,19 +104,19 @@ impl Interpreter { match result.result { return_ok!() => { - push_b256!(interpreter, address.unwrap_or_default().into_word()); - interpreter.gas.erase_cost(result.gas.remaining()); - interpreter.gas.record_refund(result.gas.refunded()); + push_b256!(self, address.unwrap_or_default().into_word()); + self.gas.erase_cost(result.gas.remaining()); + self.gas.record_refund(result.gas.refunded()); } return_revert!() => { - push!(interpreter, U256::ZERO); - interpreter.gas.erase_cost(result.gas.remaining()); + push!(self, U256::ZERO); + self.gas.erase_cost(result.gas.remaining()); } InstructionResult::FatalExternalError => { - interpreter.instruction_result = InstructionResult::FatalExternalError; + self.instruction_result = InstructionResult::FatalExternalError; } _ => { - push!(interpreter, U256::ZERO); + push!(self, U256::ZERO); } } } @@ -135,28 +134,27 @@ impl Interpreter { let out_offset = memory_return_offset.start; let out_len = memory_return_offset.len(); - let interpreter = self; - interpreter.return_data_buffer = result.output; - let target_len = min(out_len, interpreter.return_data_buffer.len()); + self.return_data_buffer = result.output; + let target_len = min(out_len, self.return_data_buffer.len()); match result.result { return_ok!() => { // return unspend gas. - interpreter.gas.erase_cost(result.gas.remaining()); - interpreter.gas.record_refund(result.gas.refunded()); - shared_memory.set(out_offset, &interpreter.return_data_buffer[..target_len]); - push!(interpreter, U256::from(1)); + self.gas.erase_cost(result.gas.remaining()); + self.gas.record_refund(result.gas.refunded()); + shared_memory.set(out_offset, &self.return_data_buffer[..target_len]); + push!(self, U256::from(1)); } return_revert!() => { - interpreter.gas.erase_cost(result.gas.remaining()); - shared_memory.set(out_offset, &interpreter.return_data_buffer[..target_len]); - push!(interpreter, U256::ZERO); + self.gas.erase_cost(result.gas.remaining()); + shared_memory.set(out_offset, &self.return_data_buffer[..target_len]); + push!(self, U256::ZERO); } InstructionResult::FatalExternalError => { - interpreter.instruction_result = InstructionResult::FatalExternalError; + self.instruction_result = InstructionResult::FatalExternalError; } _ => { - push!(interpreter, U256::ZERO); + push!(self, U256::ZERO); } } } diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index ea20a8185b..aa99a81776 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -20,7 +20,7 @@ mod modexp; mod secp256k1; pub mod utilities; -use alloc::{boxed::Box, collections::BTreeMap, vec::Vec}; +use alloc::{boxed::Box, vec::Vec}; use core::{fmt, hash::Hash}; use once_cell::race::OnceBox; #[doc(hidden)] @@ -57,21 +57,22 @@ impl PrecompileOutput { } } } -#[derive(Clone, Debug)] +#[derive(Clone, Default, Debug)] pub struct Precompiles { - pub inner: Vec, + /// Precompiles. + pub inner: HashMap, } impl Precompiles { /// Returns the precompiles for the given spec. - pub fn new(spec: SpecId) -> &'static Self { + pub fn new(spec: PrecompileSpecId) -> &'static Self { match spec { - SpecId::HOMESTEAD => Self::homestead(), - SpecId::BYZANTIUM => Self::byzantium(), - SpecId::ISTANBUL => Self::istanbul(), - SpecId::BERLIN => Self::berlin(), - SpecId::CANCUN => Self::cancun(), - SpecId::LATEST => Self::latest(), + PrecompileSpecId::HOMESTEAD => Self::homestead(), + PrecompileSpecId::BYZANTIUM => Self::byzantium(), + PrecompileSpecId::ISTANBUL => Self::istanbul(), + PrecompileSpecId::BERLIN => Self::berlin(), + PrecompileSpecId::CANCUN => Self::cancun(), + PrecompileSpecId::LATEST => Self::latest(), } } @@ -79,14 +80,14 @@ impl Precompiles { pub fn homestead() -> &'static Self { static INSTANCE: OnceBox = OnceBox::new(); INSTANCE.get_or_init(|| { - let mut inner = vec![ + let mut precompiles = Precompiles::default(); + precompiles.extend([ secp256k1::ECRECOVER, hash::SHA256, hash::RIPEMD160, identity::FUN, - ]; - inner.sort_unstable_by_key(|i| i.0); - Box::new(Self { inner }) + ]); + Box::new(precompiles) }) } @@ -169,13 +170,13 @@ impl Precompiles { /// Returns an iterator over the precompiles addresses. #[inline] pub fn addresses(&self) -> impl Iterator + '_ { - self.inner.iter().map(|i| &i.0) + self.inner.keys() } /// Consumes the type and returns all precompile addresses. #[inline] pub fn into_addresses(self) -> impl Iterator { - self.inner.into_iter().map(|precompile| precompile.0) + self.inner.into_keys() } /// Is the given address a precompile. @@ -188,10 +189,7 @@ impl Precompiles { #[inline] pub fn get(&self, address: &Address) -> Option { //return None; - self.inner - .binary_search_by_key(address, |i| i.0) - .ok() - .map(|i| self.inner[i].1.clone()) + self.inner.get(address).cloned() } /// Is the precompiles list empty. @@ -208,22 +206,7 @@ impl Precompiles { /// /// Other precompiles with overwrite existing precompiles. pub fn extend(&mut self, other: impl IntoIterator) { - self.inner = self - .inner - .iter() - .cloned() - .chain(other) - .map(|i| (i.0, i.1.clone())) - .collect::>() - .into_iter() - .map(|(k, v)| PrecompileWithAddress(k, v)) - .collect::>(); - } -} - -impl Default for Precompiles { - fn default() -> Self { - Self::new(SpecId::LATEST).clone() //berlin + self.inner.extend(other.into_iter().map(Into::into)); } } @@ -258,7 +241,7 @@ impl From for (Address, Precompile) { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub enum SpecId { +pub enum PrecompileSpecId { HOMESTEAD, BYZANTIUM, ISTANBUL, @@ -267,7 +250,7 @@ pub enum SpecId { LATEST, } -impl SpecId { +impl PrecompileSpecId { /// Returns the appropriate precompile Spec for the primitive [SpecId](revm_primitives::SpecId) pub const fn from_spec_id(spec_id: revm_primitives::SpecId) -> Self { use revm_primitives::SpecId::*; diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index de816f2cea..054175cc97 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -19,6 +19,12 @@ pub struct Env { } impl Env { + /// Resets environment to default values. + #[inline] + pub fn clear(&mut self) { + *self = Self::default(); + } + /// Calculates the effective gas price of the transaction. #[inline] pub fn effective_gas_price(&self) -> U256 { @@ -174,7 +180,7 @@ impl Env { /// Validate transaction against state. #[inline] - pub fn validate_tx_against_state( + pub fn validate_tx_against_state( &self, account: &mut Account, ) -> Result<(), InvalidTransaction> { @@ -211,7 +217,7 @@ impl Env { .and_then(|gas_cost| gas_cost.checked_add(self.tx.value)) .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; - if SpecId::enabled(self.cfg.spec_id, SpecId::CANCUN) { + if SPEC::enabled(SpecId::CANCUN) { let data_fee = self.calc_data_fee().expect("already checked"); balance_check = balance_check .checked_add(U256::from(data_fee)) @@ -241,8 +247,9 @@ impl Env { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[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, - pub spec_id: SpecId, /// KZG Settings for point evaluation precompile. By default, this is loaded from the ethereum mainnet trusted setup. #[cfg(feature = "c-kzg")] #[cfg_attr(feature = "serde", serde(skip))] @@ -375,7 +382,6 @@ impl Default for CfgEnv { fn default() -> Self { Self { chain_id: 1, - spec_id: SpecId::LATEST, perf_analyse_created_bytecodes: AnalysisKind::default(), limit_contract_code_size: None, #[cfg(feature = "c-kzg")] @@ -470,6 +476,12 @@ impl BlockEnv { .as_ref() .map(|a| a.excess_blob_gas) } + + /// Clears environment and resets fields to default values. + #[inline] + pub fn clear(&mut self) { + *self = Self::default(); + } } impl Default for BlockEnv { @@ -555,6 +567,12 @@ impl TxEnv { pub fn get_total_blob_gas(&self) -> u64 { GAS_PER_BLOB * self.blob_hashes.len() as u64 } + + /// Clears environment and resets fields to default values. + #[inline] + pub fn clear(&mut self) { + *self = Self::default(); + } } impl Default for TxEnv { @@ -740,13 +758,15 @@ mod tests { #[test] fn test_validate_tx_against_state_deposit_tx() { // Set the optimism flag and source hash. + + use crate::LatestSpec; let mut env = Env::default(); env.cfg.optimism = true; env.tx.optimism.source_hash = Some(B256::ZERO); // Nonce and balance checks should be skipped for deposit transactions. assert!(env - .validate_tx_against_state(&mut Account::default()) + .validate_tx_against_state::(&mut Account::default()) .is_ok()); } diff --git a/crates/primitives/src/specification.rs b/crates/primitives/src/specification.rs index 97f2b5f94e..e7434be7d7 100644 --- a/crates/primitives/src/specification.rs +++ b/crates/primitives/src/specification.rs @@ -69,6 +69,10 @@ impl SpecId { Self::n(spec_id) } + pub fn is_enabled_in(&self, other: Self) -> bool { + Self::enabled(*self, other) + } + #[inline] pub const fn enabled(our: SpecId, other: SpecId) -> bool { our as u8 >= other as u8 @@ -154,11 +158,125 @@ spec!(REGOLITH, RegolithSpec); #[cfg(feature = "optimism")] spec!(CANYON, CanyonSpec); -#[cfg(feature = "optimism")] +#[macro_export] +macro_rules! spec_to_generic { + ($spec_id:expr, $e:expr) => {{ + // We are transitioning from var to generic spec. + match $spec_id { + $crate::SpecId::FRONTIER | SpecId::FRONTIER_THAWING => { + use $crate::FrontierSpec as SPEC; + $e + } + $crate::SpecId::HOMESTEAD | SpecId::DAO_FORK => { + use $crate::HomesteadSpec as SPEC; + $e + } + $crate::SpecId::TANGERINE => { + use $crate::TangerineSpec as SPEC; + $e + } + $crate::SpecId::SPURIOUS_DRAGON => { + use $crate::SpuriousDragonSpec as SPEC; + $e + } + $crate::SpecId::BYZANTIUM => { + use $crate::ByzantiumSpec as SPEC; + $e + } + $crate::SpecId::PETERSBURG | $crate::SpecId::CONSTANTINOPLE => { + use $crate::PetersburgSpec as SPEC; + $e + } + $crate::SpecId::ISTANBUL | $crate::SpecId::MUIR_GLACIER => { + use $crate::IstanbulSpec as SPEC; + $e + } + $crate::SpecId::BERLIN => { + use $crate::BerlinSpec as SPEC; + $e + } + $crate::SpecId::LONDON + | $crate::SpecId::ARROW_GLACIER + | $crate::SpecId::GRAY_GLACIER => { + use $crate::LondonSpec as SPEC; + $e + } + $crate::SpecId::MERGE => { + use $crate::MergeSpec as SPEC; + $e + } + $crate::SpecId::SHANGHAI => { + use $crate::ShanghaiSpec as SPEC; + $e + } + $crate::SpecId::CANCUN => { + use $crate::CancunSpec as SPEC; + $e + } + $crate::SpecId::LATEST => { + use $crate::LatestSpec as SPEC; + $e + } + #[cfg(feature = "optimism")] + $crate::SpecId::BEDROCK => { + use $crate::BedrockSpec as SPEC; + $e + } + #[cfg(feature = "optimism")] + $crate::SpecId::REGOLITH => { + use $crate::RegolithSpec as SPEC; + $e + } + #[cfg(feature = "optimism")] + $crate::SpecId::CANYON => { + use $crate::CanyonSpec 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)); + #[cfg(feature = "optimism")] + spec_to_generic!(BEDROCK, assert_eq!(SPEC::SPEC_ID, BEDROCK)); + #[cfg(feature = "optimism")] + spec_to_generic!(REGOLITH, assert_eq!(SPEC::SPEC_ID, REGOLITH)); + spec_to_generic!(SHANGHAI, assert_eq!(SPEC::SPEC_ID, SHANGHAI)); + #[cfg(feature = "optimism")] + spec_to_generic!(CANYON, assert_eq!(SPEC::SPEC_ID, CANYON)); + spec_to_generic!(CANCUN, assert_eq!(SPEC::SPEC_ID, CANCUN)); + spec_to_generic!(LATEST, assert_eq!(SPEC::SPEC_ID, LATEST)); + } +} + +#[cfg(feature = "optimism")] +#[cfg(test)] +mod optimism_tests { + use super::*; + #[test] fn test_bedrock_post_merge_hardforks() { assert!(BedrockSpec::enabled(SpecId::MERGE)); diff --git a/crates/revm/CHANGELOG.md b/crates/revm/CHANGELOG.md index 60208fea7c..92c8d59e2f 100644 --- a/crates/revm/CHANGELOG.md +++ b/crates/revm/CHANGELOG.md @@ -1,3 +1,10 @@ +# v3.6.0 + +Big renaming long overdue: +* EVMImpl to Evm, +* EVM to EvmFactory +* EVMData to EvmContext + # v3.5.0 date 02.10.2023 diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 4eaed1bc28..8fd90f60d2 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -14,10 +14,11 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] +# revm revm-interpreter = { path = "../interpreter", version = "1.3.0", default-features = false } revm-precompile = { path = "../precompile", version = "2.2.0", default-features = false } -#misc +# misc auto_impl = { version = "1.1", default-features = false } # Optional @@ -73,10 +74,6 @@ optional_beneficiary_reward = ["revm-interpreter/optional_beneficiary_reward"] secp256k1 = ["revm-precompile/secp256k1"] c-kzg = ["revm-precompile/c-kzg"] -# deprecated features -web3db = [] -with-serde = [] - [[example]] name = "fork_ref_transact" path = "../../examples/fork_ref_transact.rs" diff --git a/crates/revm/benches/bench.rs b/crates/revm/benches/bench.rs index 988c2e9b4f..73253cbae9 100644 --- a/crates/revm/benches/bench.rs +++ b/crates/revm/benches/bench.rs @@ -7,19 +7,20 @@ use revm::{ primitives::{ address, bytes, hex, BerlinSpec, Bytecode, BytecodeState, Bytes, TransactTo, U256, }, + Evm, }; use revm_interpreter::{opcode::make_instruction_table, SharedMemory, EMPTY_SHARED_MEMORY}; use std::time::Duration; -type Evm = revm::EVM; - fn analysis(c: &mut Criterion) { - let mut evm = revm::new(); - - evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); - evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); - // evm.env.tx.data = bytes!("30627b7c"); - evm.env.tx.data = bytes!("8035F0CE"); + let evm = Evm::builder() + .modify_tx_env(|tx| { + tx.caller = address!("0000000000000000000000000000000000000002"); + tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); + // evm.env.tx.data = bytes!("30627b7c"); + tx.data = bytes!("8035F0CE"); + }) + .build(); let contract_data: Bytes = hex::decode(ANALYSIS).unwrap().into(); @@ -30,27 +31,38 @@ fn analysis(c: &mut Criterion) { .sample_size(10); let raw = Bytecode::new_raw(contract_data.clone()); - evm.database(BenchmarkDB::new_bytecode(raw)); + let mut evm = evm + .modify() + .reset_handler_with_db(BenchmarkDB::new_bytecode(raw)) + .build(); bench_transact(&mut g, &mut evm); let checked = Bytecode::new_raw(contract_data.clone()).to_checked(); - evm.database(BenchmarkDB::new_bytecode(checked)); + let mut evm = evm + .modify() + .reset_handler_with_db(BenchmarkDB::new_bytecode(checked)) + .build(); bench_transact(&mut g, &mut evm); let analysed = to_analysed(Bytecode::new_raw(contract_data)); - evm.database(BenchmarkDB::new_bytecode(analysed)); + let mut evm = evm + .modify() + .reset_handler_with_db(BenchmarkDB::new_bytecode(analysed)) + .build(); bench_transact(&mut g, &mut evm); g.finish(); } fn snailtracer(c: &mut Criterion) { - let mut evm = revm::new(); - evm.database(BenchmarkDB::new_bytecode(bytecode(SNAILTRACER))); - - evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); - evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); - evm.env.tx.data = bytes!("30627b7c"); + let mut evm = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(bytecode(SNAILTRACER))) + .modify_tx_env(|tx| { + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); + tx.data = bytes!("30627b7c"); + }) + .build(); let mut g = c.benchmark_group("snailtracer"); g.noise_threshold(0.03) @@ -63,12 +75,14 @@ fn snailtracer(c: &mut Criterion) { } fn transfer(c: &mut Criterion) { - let mut evm = revm::new(); - evm.database(BenchmarkDB::new_bytecode(Bytecode::new())); - - evm.env.tx.caller = address!("0000000000000000000000000000000000000001"); - evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); - evm.env.tx.value = U256::from(10); + let mut evm = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(Bytecode::new())) + .modify_tx_env(|tx| { + tx.caller = address!("0000000000000000000000000000000000000001"); + tx.transact_to = TransactTo::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)); @@ -76,8 +90,8 @@ fn transfer(c: &mut Criterion) { g.finish(); } -fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { - let state = match evm.db.as_mut().unwrap().0.state { +fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm<'_, EXT, BenchmarkDB>) { + let state = match evm.context.evm.db.0.state { BytecodeState::Raw => "raw", BytecodeState::Checked { .. } => "checked", BytecodeState::Analysed { .. } => "analysed", @@ -86,15 +100,15 @@ fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { g.bench_function(id, |b| b.iter(|| evm.transact().unwrap())); } -fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { +fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm<'static, (), BenchmarkDB>) { g.bench_function("eval", |b| { let contract = Contract { - input: evm.env.tx.data.clone(), - bytecode: BytecodeLocked::try_from(evm.db.as_ref().unwrap().0.clone()).unwrap(), + input: evm.context.evm.env.tx.data.clone(), + bytecode: BytecodeLocked::try_from(evm.context.evm.db.0.clone()).unwrap(), ..Default::default() }; let mut shared_memory = SharedMemory::new(); - let mut host = DummyHost::new(evm.env.clone()); + let mut host = DummyHost::new(*evm.context.evm.env.clone()); let instruction_table = make_instruction_table::(); b.iter(move || { // replace memory with empty memory to use it inside interpreter. diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs new file mode 100644 index 0000000000..64bfcbffd5 --- /dev/null +++ b/crates/revm/src/builder.rs @@ -0,0 +1,362 @@ +use crate::{ + db::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}, + handler::register, + primitives::{BlockEnv, CfgEnv, Env, LatestSpec, SpecId, TxEnv}, + Context, Evm, EvmContext, Handler, +}; +use core::marker::PhantomData; + +/// 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, Stage: BuilderStage, EXT, DB: Database> { + evm: EvmContext, + external: EXT, + handler: Handler<'a, Evm<'a, EXT, DB>, EXT, DB>, + phantom: PhantomData, +} + +/// Trait that unlocks builder stages. +pub trait BuilderStage {} + +/// First stage of the builder allows setting generic variables. +/// Generic variables are database and external context. +pub struct SetGenericStage; +impl BuilderStage for SetGenericStage {} + +/// Second stage of the builder allows appending handler registers. +/// Requires the database and external context to be set. +pub struct HandlerStage; +impl BuilderStage for HandlerStage {} + +impl<'a> Default for EvmBuilder<'a, SetGenericStage, (), EmptyDB> { + fn default() -> Self { + Self { + evm: EvmContext::new(EmptyDB::default()), + external: (), + handler: Handler::mainnet::(), + phantom: PhantomData, + } + } +} + +impl<'a, EXT, DB: Database> EvmBuilder<'a, SetGenericStage, EXT, DB> { + /// Sets the [`EmptyDB`] as the [`Database`] that will be used by [`Evm`]. + pub fn with_empty_db(self) -> EvmBuilder<'a, SetGenericStage, EXT, EmptyDB> { + EvmBuilder { + evm: self.evm.with_db(EmptyDB::default()), + external: self.external, + handler: Handler::mainnet_with_spec(self.handler.spec_id), + phantom: PhantomData, + } + } + /// Sets the [`Database`] that will be used by [`Evm`]. + pub fn with_db(self, db: ODB) -> EvmBuilder<'a, SetGenericStage, EXT, ODB> { + EvmBuilder { + evm: self.evm.with_db(db), + external: self.external, + handler: Handler::mainnet_with_spec(self.handler.spec_id), + phantom: PhantomData, + } + } + /// Sets the [`DatabaseRef`] that will be used by [`Evm`]. + pub fn with_ref_db( + self, + db: ODB, + ) -> EvmBuilder<'a, SetGenericStage, EXT, WrapDatabaseRef> { + EvmBuilder { + evm: self.evm.with_db(WrapDatabaseRef(db)), + external: self.external, + handler: Handler::mainnet_with_spec(self.handler.spec_id), + phantom: PhantomData, + } + } + + /// Sets the external context that will be used by [`Evm`]. + pub fn with_external_context( + self, + external: OEXT, + ) -> EvmBuilder<'a, SetGenericStage, OEXT, DB> { + EvmBuilder { + evm: self.evm, + external, + handler: Handler::mainnet_with_spec(self.handler.spec_id), + phantom: PhantomData, + } + } +} + +impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { + /// 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, EXT, DB>) -> Self { + Self { + evm: evm.context.evm, + external: evm.context.external, + handler: evm.handler, + phantom: PhantomData, + } + } + + /// Sets the [`EmptyDB`] and resets the [`Handler`] to default mainnet. + pub fn reset_handler_with_empty_db(self) -> EvmBuilder<'a, HandlerStage, EXT, EmptyDB> { + EvmBuilder { + evm: self.evm.with_db(EmptyDB::default()), + external: self.external, + handler: Handler::mainnet_with_spec(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, EXT, ODB> { + EvmBuilder { + evm: self.evm.with_db(db), + external: self.external, + handler: Handler::mainnet_with_spec(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, EXT, WrapDatabaseRef> { + EvmBuilder { + evm: self.evm.with_db(WrapDatabaseRef(db)), + external: self.external, + handler: Handler::mainnet_with_spec(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, OEXT, DB> { + EvmBuilder { + evm: self.evm, + external, + handler: Handler::mainnet_with_spec(self.handler.spec_id), + phantom: PhantomData, + } + } +} + +impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> { + /// Builds the [`Evm`]. + pub fn build(self) -> Evm<'a, EXT, DB> { + Evm::new( + Context { + evm: self.evm, + external: self.external, + }, + 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<'a, EXT, DB>, + ) -> EvmBuilder<'_, HandlerStage, EXT, DB> { + self.handler + .append_handle_register(register::HandleRegisters::Plain(handle_register)); + EvmBuilder { + evm: self.evm, + external: self.external, + handler: self.handler, + + phantom: PhantomData, + } + } + + /// 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, EXT, DB>, + ) -> EvmBuilder<'_, HandlerStage, EXT, DB> { + self.handler + .append_handle_register(register::HandleRegisters::Box(handle_register)); + EvmBuilder { + evm: self.evm, + external: self.external, + handler: self.handler, + + phantom: PhantomData, + } + } + + /// 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 spec_id(mut self, spec_id: SpecId) -> Self { + self.handler = self.handler.change_spec_id(spec_id); + EvmBuilder { + evm: self.evm, + external: self.external, + handler: self.handler, + + phantom: PhantomData, + } + } + + /// Allows modification of Evm Database. + pub fn modify_db(mut self, f: impl FnOnce(&mut DB)) -> Self { + f(&mut self.evm.db); + self + } + + /// Allows modification of external context. + pub fn modify_external_context(mut self, f: impl FnOnce(&mut EXT)) -> Self { + f(&mut self.external); + self + } + + /// Allows modification of Evm Environment. + pub fn modify_env(mut self, f: impl FnOnce(&mut Env)) -> Self { + f(&mut self.evm.env); + self + } + + /// Allows modification of Evm's Transaction Environment. + pub fn modify_tx_env(mut self, f: impl FnOnce(&mut TxEnv)) -> Self { + f(&mut self.evm.env.tx); + self + } + + /// Allows modification of Evm's Block Environment. + pub fn modify_block_env(mut self, f: impl FnOnce(&mut BlockEnv)) -> Self { + f(&mut self.evm.env.block); + self + } + + /// Allows modification of Evm's Config Environment. + pub fn modify_cfg_env(mut self, f: impl FnOnce(&mut CfgEnv)) -> Self { + f(&mut self.evm.env.cfg); + self + } + + /// Clears Environment of EVM. + pub fn with_clear_env(mut self) -> Self { + self.evm.env.clear(); + self + } + + /// Clears Transaction environment of EVM. + pub fn with_clear_tx_env(mut self) -> Self { + self.evm.env.tx.clear(); + self + } + /// Clears Block environment of EVM. + pub fn with_clear_block_env(mut self) -> Self { + self.evm.env.block.clear(); + self + } +} + +#[cfg(test)] +mod test { + use super::SpecId; + use crate::{ + db::EmptyDB, inspector::inspector_handle_register, inspectors::NoOpInspector, Context, Evm, + EvmContext, + }; + + #[test] + fn simple_build() { + // build without external with latest spec + Evm::builder().build(); + // build with empty db + Evm::builder().with_empty_db().build(); + // build with_db + Evm::builder().with_db(EmptyDB::default()).build(); + // build with empty external + Evm::builder().with_empty_db().build(); + // build with some external + Evm::builder() + .with_empty_db() + .with_external_context(()) + .build(); + // build with spec + Evm::builder() + .with_empty_db() + .spec_id(SpecId::HOMESTEAD) + .build(); + + // with with Env change in multiple places + Evm::builder() + .with_empty_db() + .modify_tx_env(|tx| tx.gas_limit = 10) + .build(); + Evm::builder().modify_tx_env(|tx| tx.gas_limit = 10).build(); + Evm::builder() + .with_empty_db() + .modify_tx_env(|tx| tx.gas_limit = 10) + .build(); + Evm::builder() + .with_empty_db() + .modify_tx_env(|tx| tx.gas_limit = 10) + .build(); + + // with inspector handle + Evm::builder() + .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_external_context(NoOpInspector) + .append_handler_register(inspector_handle_register) + // this would not compile + // .with_db(..) + .build(); + + let Context { + external, + evm: EvmContext { db, .. }, + } = evm.into_context(); + let _ = (external, db); + } + + #[test] + fn build_modify_build() { + // build evm + let evm = Evm::builder() + .with_empty_db() + .spec_id(SpecId::HOMESTEAD) + .build(); + + // modify evm + let evm = evm.modify().spec_id(SpecId::FRONTIER).build(); + let _ = evm + .modify() + .modify_tx_env(|tx| tx.chain_id = Some(2)) + .build(); + } +} diff --git a/crates/revm/src/evm_context.rs b/crates/revm/src/context.rs similarity index 75% rename from crates/revm/src/evm_context.rs rename to crates/revm/src/context.rs index a643adfda8..52a3e97abe 100644 --- a/crates/revm/src/evm_context.rs +++ b/crates/revm/src/context.rs @@ -1,5 +1,5 @@ use crate::{ - db::Database, + db::{Database, EmptyDB}, interpreter::{ analysis::to_analysed, gas, return_ok, CallInputs, Contract, CreateInputs, Gas, InstructionResult, Interpreter, InterpreterResult, MAX_CODE_SIZE, @@ -7,24 +7,52 @@ use crate::{ journaled_state::JournaledState, precompile::{Precompile, Precompiles}, primitives::{ - keccak256, Address, AnalysisKind, Bytecode, Bytes, CreateScheme, EVMError, Env, Spec, - SpecId::*, B256, U256, + keccak256, Address, AnalysisKind, Bytecode, Bytes, CreateScheme, EVMError, Env, HashSet, + Spec, SpecId, SpecId::*, B256, U256, }, - CallStackFrame, CALL_STACK_LIMIT, + CallStackFrame, FrameData, FrameOrResult, JournalCheckpoint, CALL_STACK_LIMIT, }; use alloc::boxed::Box; use core::ops::Range; -/// EVM Data contains all the data that EVM needs to execute. +/// Main Context structure that contains both EvmContext and External context. +pub struct Context { + /// Evm Context. + pub evm: EvmContext, + /// External contexts. + pub external: EXT, +} + +impl Context { + /// Creates empty context. This is useful for testing. + pub fn new_empty() -> Context<(), EmptyDB> { + Context { + evm: EvmContext::new(EmptyDB::new()), + external: (), + } + } +} + +impl Context<(), DB> { + /// Creates new context with database. + pub fn new_with_db(db: DB) -> Context<(), DB> { + Context { + evm: EvmContext::new_with_env(db, Box::default()), + external: (), + } + } +} + +/// EVM contexts contains data that EVM needs for execution. #[derive(Debug)] -pub struct EvmContext<'a, DB: Database> { +pub struct EvmContext { /// EVM Environment contains all the information about config, block and transaction that /// evm needs. - pub env: &'a mut Env, + pub env: Box, /// EVM State with journaling support. pub journaled_state: JournaledState, /// Database to load data from. - pub db: &'a mut DB, + pub db: DB, /// Error that happened during execution. pub error: Option, /// Precompiles that are available for evm. @@ -34,7 +62,50 @@ pub struct EvmContext<'a, DB: Database> { pub l1_block_info: Option, } -impl<'a, DB: Database> EvmContext<'a, DB> { +impl EvmContext { + pub fn with_db(self, db: ODB) -> EvmContext { + EvmContext { + env: self.env, + journaled_state: self.journaled_state, + db, + error: None, + precompiles: self.precompiles, + #[cfg(feature = "optimism")] + l1_block_info: self.l1_block_info, + } + } + pub fn new(db: DB) -> Self { + Self { + env: Box::default(), + journaled_state: JournaledState::new(SpecId::LATEST, HashSet::new()), + db, + error: None, + precompiles: Precompiles::default(), + #[cfg(feature = "optimism")] + l1_block_info: None, + } + } + + /// New context with database and environment. + pub fn new_with_env(db: DB, env: Box) -> Self { + Self { + env, + journaled_state: JournaledState::new(SpecId::LATEST, HashSet::new()), + db, + error: None, + precompiles: Precompiles::default(), + #[cfg(feature = "optimism")] + l1_block_info: None, + } + } + + /// Sets precompiles + pub fn set_precompiles(&mut self, precompiles: Precompiles) { + self.journaled_state.warm_preloaded_addresses = + precompiles.addresses().cloned().collect::>(); + self.precompiles = precompiles; + } + /// Load access list for berlin hard fork. /// /// Loading of accounts/storages is needed to make them warm. @@ -42,7 +113,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { pub fn load_access_list(&mut self) -> Result<(), EVMError> { for (address, slots) in self.env.tx.access_list.iter() { self.journaled_state - .initial_account_load(*address, slots, self.db) + .initial_account_load(*address, slots, &mut self.db) .map_err(EVMError::Database)?; } Ok(()) @@ -50,7 +121,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { /// Return environment. pub fn env(&mut self) -> &mut Env { - self.env + &mut self.env } /// Fetch block hash from database. @@ -64,7 +135,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { /// Load account and return flags (is_cold, exists) pub fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { self.journaled_state - .load_account_exist(address, self.db) + .load_account_exist(address, &mut self.db) .map_err(|e| self.error = Some(e)) .ok() } @@ -82,7 +153,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { pub fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { let (acc, is_cold) = self .journaled_state - .load_code(address, self.db) + .load_code(address, &mut self.db) .map_err(|e| self.error = Some(e)) .ok()?; Some((acc.info.code.clone().unwrap(), is_cold)) @@ -106,7 +177,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { pub fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { // account is always warm. reference on that statement https://eips.ethereum.org/EIPS/eip-2929 see `Note 2:` self.journaled_state - .sload(address, index, self.db) + .sload(address, index, &mut self.db) .map_err(|e| self.error = Some(e)) .ok() } @@ -119,7 +190,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { value: U256, ) -> Option<(U256, U256, U256, bool)> { self.journaled_state - .sstore(address, index, value, self.db) + .sstore(address, index, value, &mut self.db) .map_err(|e| self.error = Some(e)) .ok() } @@ -135,15 +206,12 @@ impl<'a, DB: Database> EvmContext<'a, DB> { } /// Make create frame. - pub fn make_create_frame( - &mut self, - inputs: &CreateInputs, - ) -> Result, InterpreterResult> { + pub fn make_create_frame(&mut self, spec_id: SpecId, inputs: &CreateInputs) -> FrameOrResult { // Prepare crate. let gas = Gas::new(inputs.gas_limit); let return_error = |e| { - Err(InterpreterResult { + FrameOrResult::Result(InterpreterResult { result: e, gas, output: Bytes::new(), @@ -186,7 +254,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { // Load account so it needs to be marked as warm for access list. if self .journaled_state - .load_account(created_address, self.db) + .load_account(created_address, &mut self.db) .map_err(|e| self.error = Some(e)) .is_err() { @@ -194,10 +262,11 @@ impl<'a, DB: Database> EvmContext<'a, DB> { } // create account, transfer funds and make the journal checkpoint. - let checkpoint = match self.journaled_state.create_account_checkpoint::( + let checkpoint = match self.journaled_state.create_account_checkpoint( inputs.caller, created_address, inputs.value, + spec_id, ) { Ok(checkpoint) => checkpoint, Err(e) => { @@ -216,25 +285,23 @@ impl<'a, DB: Database> EvmContext<'a, DB> { inputs.value, )); - Ok(Box::new(CallStackFrame { - is_create: true, + FrameOrResult::new_frame(CallStackFrame { checkpoint, - created_address: Some(created_address), - subcall_return_memory_range: 0..0, + frame_data: FrameData::Create { created_address }, interpreter: Interpreter::new(contract, gas.limit(), false), - })) + }) } /// Make call frame pub fn make_call_frame( &mut self, inputs: &CallInputs, - return_memory_offset: Range, - ) -> Result, InterpreterResult> { + return_memory_range: Range, + ) -> FrameOrResult { let gas = Gas::new(inputs.gas_limit); let return_result = |instruction_result: InstructionResult| { - Err(InterpreterResult { + FrameOrResult::Result(InterpreterResult { result: instruction_result, gas, output: Bytes::new(), @@ -246,7 +313,10 @@ impl<'a, DB: Database> EvmContext<'a, DB> { return return_result(InstructionResult::CallTooDeep); } - let account = match self.journaled_state.load_code(inputs.contract, self.db) { + let account = match self + .journaled_state + .load_code(inputs.contract, &mut self.db) + { Ok((account, _)) => account, Err(e) => { self.error = Some(e); @@ -270,7 +340,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { &inputs.transfer.source, &inputs.transfer.target, inputs.transfer.value, - self.db, + &mut self.db, ) { self.journaled_state.checkpoint_revert(checkpoint); return return_result(e); @@ -283,7 +353,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { } else { self.journaled_state.checkpoint_revert(checkpoint); } - Err(result) + FrameOrResult::Result(result) } else if !bytecode.is_empty() { let contract = Box::new(Contract::new_with_context( inputs.input.clone(), @@ -292,13 +362,13 @@ impl<'a, DB: Database> EvmContext<'a, DB> { &inputs.context, )); // Create interpreter and execute subcall and push new CallStackFrame. - Ok(Box::new(CallStackFrame { - is_create: false, + FrameOrResult::new_frame(CallStackFrame { checkpoint, - created_address: None, - subcall_return_memory_range: return_memory_offset, + frame_data: FrameData::Call { + return_memory_range, + }, interpreter: Interpreter::new(contract, gas.limit(), inputs.is_static), - })) + }) } else { self.journaled_state.checkpoint_commit(); return_result(InstructionResult::Stop) @@ -350,13 +420,13 @@ impl<'a, DB: Database> EvmContext<'a, DB> { pub fn call_return( &mut self, interpreter_result: InterpreterResult, - frame: Box, + journal_checkpoint: JournalCheckpoint, ) -> InterpreterResult { // revert changes or not. if matches!(interpreter_result.result, return_ok!()) { self.journaled_state.checkpoint_commit(); } else { - self.journaled_state.checkpoint_revert(frame.checkpoint); + self.journaled_state.checkpoint_revert(journal_checkpoint); } interpreter_result } @@ -366,13 +436,14 @@ impl<'a, DB: Database> EvmContext<'a, DB> { pub fn create_return( &mut self, mut interpreter_result: InterpreterResult, - frame: Box, - ) -> (InterpreterResult, Address) { - let address = frame.created_address.unwrap(); + address: Address, + journal_checkpoint: JournalCheckpoint, + ) -> InterpreterResult { + //let address = frame.created_address.unwrap(); // if return is not ok revert and return. if !matches!(interpreter_result.result, return_ok!()) { - self.journaled_state.checkpoint_revert(frame.checkpoint); - return (interpreter_result, address); + self.journaled_state.checkpoint_revert(journal_checkpoint); + return interpreter_result; } // Host error if present on execution // if ok, check contract creation limit and calculate gas deduction on output len. @@ -382,9 +453,9 @@ impl<'a, DB: Database> EvmContext<'a, DB> { && !interpreter_result.output.is_empty() && interpreter_result.output.first() == Some(&0xEF) { - self.journaled_state.checkpoint_revert(frame.checkpoint); + self.journaled_state.checkpoint_revert(journal_checkpoint); interpreter_result.result = InstructionResult::CreateContractStartingWithEF; - return (interpreter_result, address); + return interpreter_result; } // EIP-170: Contract code size limit @@ -397,9 +468,9 @@ impl<'a, DB: Database> EvmContext<'a, DB> { .limit_contract_code_size .unwrap_or(MAX_CODE_SIZE) { - self.journaled_state.checkpoint_revert(frame.checkpoint); + self.journaled_state.checkpoint_revert(journal_checkpoint); interpreter_result.result = InstructionResult::CreateContractSizeLimit; - return (interpreter_result, frame.created_address.unwrap()); + return interpreter_result; } let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT; if !interpreter_result.gas.record_cost(gas_for_code) { @@ -408,9 +479,9 @@ impl<'a, DB: Database> EvmContext<'a, DB> { // 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(frame.checkpoint); + self.journaled_state.checkpoint_revert(journal_checkpoint); interpreter_result.result = InstructionResult::OutOfGas; - return (interpreter_result, address); + return interpreter_result; } else { interpreter_result.output = Bytes::new(); } @@ -433,10 +504,9 @@ impl<'a, DB: Database> EvmContext<'a, DB> { self.journaled_state.set_code(address, bytecode); interpreter_result.result = InstructionResult::Return; - (interpreter_result, address) + interpreter_result } } - /// Test utilities for the [`EvmContext`]. #[cfg(any(test, feature = "test-utils"))] pub(crate) mod test_utils { @@ -474,11 +544,11 @@ pub(crate) mod test_utils { /// 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<'a>( - env: &'a mut Env, - db: &'a mut CacheDB, + pub fn create_cache_db_evm_context_with_balance( + env: Box, + mut db: CacheDB, balance: U256, - ) -> EvmContext<'a, CacheDB> { + ) -> EvmContext> { db.insert_account_info( test_utils::MOCK_CALLER, crate::primitives::AccountInfo { @@ -492,13 +562,13 @@ pub(crate) mod test_utils { } /// Creates a cached db evm context. - pub fn create_cache_db_evm_context<'a>( - env: &'a mut Env, - db: &'a mut CacheDB, - ) -> EvmContext<'a, CacheDB> { + pub fn create_cache_db_evm_context( + env: Box, + db: CacheDB, + ) -> EvmContext> { EvmContext { env, - journaled_state: JournaledState::new(SpecId::CANCUN, vec![]), + journaled_state: JournaledState::new(SpecId::CANCUN, HashSet::new()), db, error: None, precompiles: Precompiles::default(), @@ -508,13 +578,10 @@ pub(crate) mod test_utils { } /// Returns a new `EvmContext` with an empty journaled state. - pub fn create_empty_evm_context<'a>( - env: &'a mut Env, - db: &'a mut EmptyDB, - ) -> EvmContext<'a, EmptyDB> { + pub fn create_empty_evm_context(env: Box, db: EmptyDB) -> EvmContext { EvmContext { env, - journaled_state: JournaledState::new(SpecId::CANCUN, vec![]), + journaled_state: JournaledState::new(SpecId::CANCUN, HashSet::new()), db, error: None, precompiles: Precompiles::default(), @@ -536,14 +603,16 @@ mod tests { // call stack is too deep. #[test] fn test_make_call_frame_stack_too_deep() { - let mut env = Env::default(); - let mut db = EmptyDB::default(); - let mut evm_context = test_utils::create_empty_evm_context(&mut env, &mut db); + let env = Env::default(); + let db = EmptyDB::default(); + let mut evm_context = test_utils::create_empty_evm_context(Box::new(env), db); evm_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 = evm_context.make_call_frame(&call_inputs, 0..0); - let err = res.unwrap_err(); + let FrameOrResult::Result(err) = res else { + panic!("Expected FrameOrResult::Result"); + }; assert_eq!(err.result, InstructionResult::CallTooDeep); } @@ -552,14 +621,16 @@ mod tests { // checkpointed on the journaled state correctly. #[test] fn test_make_call_frame_transfer_revert() { - let mut env = Env::default(); - let mut db = EmptyDB::default(); - let mut evm_context = test_utils::create_empty_evm_context(&mut env, &mut db); + let env = Env::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.transfer.value = U256::from(1); let res = evm_context.make_call_frame(&call_inputs, 0..0); - let err = res.unwrap_err(); + let FrameOrResult::Result(err) = res else { + panic!("Expected FrameOrResult::Result"); + }; assert_eq!(err.result, InstructionResult::OutOfFund); let checkpointed = vec![vec![JournalEntry::AccountLoaded { address: contract }]]; assert_eq!(evm_context.journaled_state.journal, checkpointed); @@ -568,19 +639,22 @@ mod tests { #[test] fn test_make_call_frame_missing_code_context() { - let mut env = Env::default(); - let mut cdb = CacheDB::new(EmptyDB::default()); + let env = Env::default(); + let cdb = CacheDB::new(EmptyDB::default()); let bal = U256::from(3_000_000_000_u128); - let mut evm_context = create_cache_db_evm_context_with_balance(&mut env, &mut cdb, bal); + let mut evm_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 = evm_context.make_call_frame(&call_inputs, 0..0); - assert_eq!(res.unwrap_err().result, InstructionResult::Stop); + let FrameOrResult::Result(res) = res else { + panic!("Expected FrameOrResult::Result"); + }; + assert_eq!(res.result, InstructionResult::Stop); } #[test] fn test_make_call_frame_succeeds() { - let mut env = Env::default(); + let env = Env::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])); @@ -594,12 +668,18 @@ mod tests { code: Some(by), }, ); - let mut evm_context = create_cache_db_evm_context_with_balance(&mut env, &mut cdb, bal); + 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, 0..0); - let frame = res.unwrap(); - assert!(!frame.is_create); - assert_eq!(frame.created_address, None); - assert_eq!(frame.subcall_return_memory_range, 0..0); + let FrameOrResult::Frame(frame) = res else { + panic!("Expected FrameOrResult::Frame"); + }; + assert!(!frame.is_create()); + assert_eq!( + frame.frame_data, + FrameData::Call { + return_memory_range: 0..0 + } + ); } } diff --git a/crates/revm/src/db.rs b/crates/revm/src/db.rs index 9d6494701e..f891358034 100644 --- a/crates/revm/src/db.rs +++ b/crates/revm/src/db.rs @@ -16,8 +16,3 @@ pub use states::{ OriginalValuesKnown, PlainAccount, RevertToSlot, State, StateBuilder, StateDBBox, StorageWithOriginalValues, TransitionAccount, TransitionState, }; - -#[cfg(all(not(feature = "ethersdb"), feature = "web3db"))] -compile_error!( - "`web3db` feature is deprecated, drop-in replacement can be found with feature `ethersdb`" -); diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index be73045315..521c754242 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,181 +1,362 @@ use crate::{ - db::{Database, DatabaseCommit, DatabaseRef}, - evm_impl::{new_evm, Transact}, - primitives::{db::WrapDatabaseRef, EVMError, EVMResult, Env, ExecutionResult, ResultAndState}, - Inspector, + builder::{EvmBuilder, HandlerStage, SetGenericStage}, + db::{Database, DatabaseCommit, EmptyDB}, + handler::Handler, + interpreter::{ + opcode::InstructionTables, Host, Interpreter, InterpreterAction, InterpreterResult, + SelfDestructResult, SharedMemory, + }, + primitives::{ + specification::SpecId, Address, Bytecode, Bytes, EVMError, EVMResult, Env, ExecutionResult, + Log, Output, ResultAndState, TransactTo, B256, U256, + }, + CallStackFrame, Context, FrameOrResult, }; +use alloc::{boxed::Box, vec::Vec}; +use core::fmt; -/// Struct that takes Database and enabled transact to update state directly to database. -/// additionally it allows user to set all environment parameters. -/// -/// Parameters that can be set are divided between Config, Block and Transaction(tx) -/// -/// For transacting on EVM you can call transact_commit that will automatically apply changes to db. -/// -/// You can do a lot with rust and traits. For Database abstractions that we need you can implement, -/// Database, DatabaseRef or Database+DatabaseCommit and they enable functionality depending on what kind of -/// handling of struct you want. -/// * Database trait has mutable self in its functions. It is usefully if on get calls you want to modify -/// your cache or update some statistics. They enable `transact` and `inspect` functions -/// * DatabaseRef takes reference on object, this is useful if you only have reference on state and don't -/// want to update anything on it. It enabled `transact_ref` and `inspect_ref` functions -/// * Database+DatabaseCommit allow directly committing changes of transaction. it enabled `transact_commit` -/// and `inspect_commit` -/// -/// /// # Example -/// -/// ``` -/// # use revm::EVM; // Assuming this struct is in 'your_crate_name' -/// # struct SomeDatabase; // Mocking a database type for the purpose of this example -/// # struct Env; // Assuming the type Env is defined somewhere -/// -/// let evm: EVM = EVM::new(); -/// assert!(evm.db.is_none()); -/// ``` -/// -#[derive(Clone, Debug)] -pub struct EVM { - pub env: Env, - pub db: Option, -} +/// EVM call stack limit. +pub const CALL_STACK_LIMIT: u64 = 1024; -pub fn new() -> EVM { - EVM::new() +/// 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, EXT, DB: Database> { + /// Context of execution, containing both EVM and external context. + pub context: Context, + /// Handler of EVM that contains all the logic. Handler contains specification id + /// and it different depending on the specified fork. + pub handler: Handler<'a, Self, EXT, DB>, } -impl Default for EVM { - fn default() -> Self { - Self::new() +impl fmt::Debug for Evm<'_, EXT, DB> +where + EXT: fmt::Debug, + DB: Database + fmt::Debug, + DB::Error: fmt::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 { - /// Execute transaction and apply result to database +impl Evm<'_, EXT, DB> { + /// Commit the changes to the database. pub fn transact_commit(&mut self) -> Result> { let ResultAndState { result, state } = self.transact()?; - self.db.as_mut().unwrap().commit(state); + self.context.evm.db.commit(state); Ok(result) } +} - /// Inspect transaction and commit changes to database. - pub fn inspect_commit>( - &mut self, - inspector: INSP, - ) -> Result> { - let ResultAndState { result, state } = self.inspect(inspector)?; - self.db.as_mut().unwrap().commit(state); - Ok(result) +impl<'a> Evm<'a, (), EmptyDB> { + /// Returns evm builder with empty database and empty external context. + pub fn builder() -> EvmBuilder<'a, SetGenericStage, (), EmptyDB> { + EvmBuilder::default() + } +} + +impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { + /// Create new EVM. + pub fn new( + mut context: Context, + handler: Handler<'a, Self, EXT, DB>, + ) -> Evm<'a, EXT, DB> { + context.evm.journaled_state.set_spec_id(handler.spec_id); + Evm { context, handler } + } + + /// Allow for evm setting to be modified by feeding current evm + /// into the builder for modifications. + pub fn modify(self) -> EvmBuilder<'a, HandlerStage, EXT, DB> { + EvmBuilder::new(self) } } -impl EVM { - /// Do checks that could make transaction fail before call/create +impl Evm<'_, EXT, DB> { + /// Returns specification (hardfork) that the EVM is instanced with. + /// + /// SpecId depends on the handler. + pub fn spec_id(&self) -> SpecId { + self.handler.spec_id + } + + /// 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) -> Result<(), EVMError> { - if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, None).preverify_transaction() - } else { - panic!("Database needs to be set"); - } + self.handler.validation().env(&self.context.evm.env)?; + self.handler + .validation() + .initial_tx_gas(&self.context.evm.env)?; + self.handler + .validation() + .tx_against_state(&mut self.context)?; + Ok(()) } - /// Skip preverification steps and execute transaction without writing to DB, return change - /// state. + /// Transact pre-verified transaction + /// + /// This function will not validate the transaction. + #[inline] pub fn transact_preverified(&mut self) -> EVMResult { - if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, None).transact_preverified() - } else { - panic!("Database needs to be set"); - } + let initial_gas_spend = self + .handler + .validation() + .initial_tx_gas(&self.context.evm.env)?; + let output = self.transact_preverified_inner(initial_gas_spend); + self.handler.post_execution().end(&mut self.context, output) } - /// Execute transaction without writing to DB, return change state. + /// Transact transaction + /// + /// This function will validate the transaction. + #[inline] pub fn transact(&mut self) -> EVMResult { - if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, None).transact() - } else { - panic!("Database needs to be set"); - } + self.handler.validation().env(&self.context.evm.env)?; + let initial_gas_spend = self + .handler + .validation() + .initial_tx_gas(&self.context.evm.env)?; + self.handler + .validation() + .tx_against_state(&mut self.context)?; + + let output = self.transact_preverified_inner(initial_gas_spend); + self.handler.post_execution().end(&mut self.context, output) } - /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect>(&mut self, mut inspector: INSP) -> EVMResult { - if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, Some(&mut inspector)).transact() - } else { - panic!("Database needs to be set"); + /// Modify spec id, this will create new EVM that matches this spec id. + pub fn modify_spec_id(self, spec_id: SpecId) -> Self { + if self.spec_id() == spec_id { + return self; } + self.modify().spec_id(spec_id).build() } -} -impl<'a, DB: DatabaseRef> EVM { - /// Do checks that could make transaction fail before call/create - pub fn preverify_transaction_ref(&self) -> Result<(), EVMError> { - if let Some(db) = self.db.as_ref() { - new_evm::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None) - .preverify_transaction() - } else { - panic!("Database needs to be set"); - } + /// Returns internal database and external struct. + #[inline] + pub fn into_context(self) -> Context { + self.context } - /// Skip preverification steps and execute transaction - /// without writing to DB, return change state. - pub fn transact_preverified_ref(&self) -> EVMResult { - if let Some(db) = self.db.as_ref() { - new_evm::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None) - .transact_preverified() - } else { - panic!("Database needs to be set"); - } + /// Start the main loop. + pub fn start_the_loop( + &mut self, + first_stack_frame: FrameOrResult, + ) -> (InterpreterResult, Output) { + // Created address will be something only if it is create. + let mut created_address = None; + + // start main loop if CallStackFrame is created correctly + let result = match first_stack_frame { + FrameOrResult::Frame(first_stack_frame) => { + created_address = first_stack_frame.created_address(); + // take instruction talbe + let table = self + .handler + .take_instruction_table() + .expect("Instruction table should be present"); + + // run main loop + let output = match &table { + InstructionTables::Plain(table) => self.run_the_loop(table, first_stack_frame), + InstructionTables::Boxed(table) => self.run_the_loop(table, first_stack_frame), + }; + + // return back instruction table + self.handler.set_instruction_table(table); + + output + } + FrameOrResult::Result(interpreter_result) => interpreter_result, + }; + + // output of execution + let main_output = match self.context.evm.env.tx.transact_to { + TransactTo::Call(_) => Output::Call(result.output.clone()), + TransactTo::Create(_) => Output::Create(result.output.clone(), created_address), + }; + + (result, main_output) } - /// Execute transaction without writing to DB, return change state. - pub fn transact_ref(&self) -> EVMResult { - if let Some(db) = self.db.as_ref() { - new_evm::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None).transact() - } else { - panic!("Database needs to be set"); + /// Runs main call loop. + #[inline] + pub fn run_the_loop( + &mut self, + instruction_table: &[FN; 256], + first_frame: Box, + ) -> InterpreterResult + where + FN: Fn(&mut Interpreter, &mut Self), + { + 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 last stack frame. + let mut stack_frame = call_stack.last_mut().unwrap(); + + loop { + // run interpreter + let action = stack_frame + .interpreter + .run(shared_memory, instruction_table, self); + // take shared memory back. + shared_memory = stack_frame.interpreter.take_memory(); + + let new_frame = match action { + InterpreterAction::SubCall { + inputs, + return_memory_offset, + } => self.handler.execution_loop().sub_call( + &mut self.context, + inputs, + stack_frame, + &mut shared_memory, + return_memory_offset, + ), + InterpreterAction::Create { inputs } => { + self.handler + .execution_loop() + .sub_create(&mut self.context, stack_frame, inputs) + } + InterpreterAction::Return { result } => { + // free memory context. + shared_memory.free_context(); + + let child = call_stack.pop().unwrap(); + let parent = call_stack.last_mut(); + + if let Some(result) = self.handler.execution_loop().frame_return( + &mut self.context, + child, + parent, + &mut shared_memory, + result, + ) { + return result; + } + stack_frame = call_stack.last_mut().unwrap(); + continue; + } + }; + if let Some(new_frame) = new_frame { + shared_memory.new_context(); + call_stack.push(new_frame); + } + stack_frame = call_stack.last_mut().unwrap(); } } - /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect_ref>>( - &'a self, - mut inspector: I, - ) -> EVMResult { - if let Some(db) = self.db.as_ref() { - new_evm( - &mut self.env.clone(), - &mut WrapDatabaseRef(db), - Some(&mut inspector), - ) - .transact() - } else { - panic!("Database needs to be set"); - } + /// Transact pre-verified transaction. + fn transact_preverified_inner(&mut self, initial_gas_spend: u64) -> EVMResult { + let hndl = &mut self.handler; + let ctx = &mut self.context; + + // load access list and beneficiary if needed. + hndl.pre_execution().load_accounts(ctx)?; + + // load precompiles + let precompiles = hndl.pre_execution().load_precompiles(); + ctx.evm.set_precompiles(precompiles); + + // deduce caller balance with its limit. + hndl.pre_execution().deduct_caller(ctx)?; + // gas limit used in calls. + let first_frame = hndl + .execution_loop() + .create_first_frame(ctx, ctx.evm.env.tx.gas_limit - initial_gas_spend); + + // Starts the main running loop. + let (result, main_output) = self.start_the_loop(first_frame); + + let hndl = &mut self.handler; + let ctx = &mut self.context; + + // handle output of call/create calls. + let gas = hndl + .execution_loop() + .first_frame_return(&ctx.evm.env, result.result, result.gas); + // Reimburse the caller + hndl.post_execution().reimburse_caller(ctx, &gas)?; + // Reward beneficiary + hndl.post_execution().reward_beneficiary(ctx, &gas)?; + // Returns output of transaction. + hndl.post_execution() + .output(ctx, result.result, main_output, &gas) } } -impl EVM { - /// Creates a new [EVM] instance with the default environment, - pub fn new() -> Self { - Self::with_env(Default::default()) +impl Host for Evm<'_, EXT, DB> { + fn env(&mut self) -> &mut Env { + self.context.evm.env() + } + + fn block_hash(&mut self, number: U256) -> Option { + self.context.evm.block_hash(number) + } + + fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { + self.context.evm.load_account(address) + } + + fn balance(&mut self, address: Address) -> Option<(U256, bool)> { + self.context.evm.balance(address) + } + + fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { + self.context.evm.code(address) + } + + fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { + self.context.evm.code_hash(address) + } + + fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { + self.context.evm.sload(address, index) + } + + fn sstore( + &mut self, + address: Address, + index: U256, + value: U256, + ) -> Option<(U256, U256, U256, bool)> { + self.context.evm.sstore(address, index, value) } - /// Creates a new [EVM] instance with the given environment. - pub fn with_env(env: Env) -> Self { - Self { env, db: None } + fn tload(&mut self, address: Address, index: U256) -> U256 { + self.context.evm.tload(address, index) } - pub fn database(&mut self, db: DB) { - self.db = Some(db); + fn tstore(&mut self, address: Address, index: U256, value: U256) { + self.context.evm.tstore(address, index, value) } - pub fn db(&mut self) -> Option<&mut DB> { - self.db.as_mut() + fn log(&mut self, address: Address, topics: Vec, data: Bytes) { + self.context.evm.journaled_state.log(Log { + address, + topics, + data, + }); } - pub fn take_db(&mut self) -> DB { - core::mem::take(&mut self.db).unwrap() + fn selfdestruct(&mut self, address: Address, target: Address) -> Option { + self.context + .evm + .journaled_state + .selfdestruct(address, target, &mut self.context.evm.db) + .map_err(|e| self.context.evm.error = Some(e)) + .ok() } } diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs deleted file mode 100644 index 0b7a4a2c66..0000000000 --- a/crates/revm/src/evm_impl.rs +++ /dev/null @@ -1,832 +0,0 @@ -use crate::{ - db::Database, - handler::Handler, - inspector_instruction, - interpreter::{ - gas::initial_tx_gas, - opcode::{make_boxed_instruction_table, make_instruction_table, InstructionTables}, - CallContext, CallInputs, CallScheme, CreateInputs, Host, Interpreter, InterpreterAction, - InterpreterResult, SelfDestructResult, SharedMemory, Transfer, - }, - journaled_state::JournaledState, - precompile::Precompiles, - primitives::{ - specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, - Output, Spec, SpecId::*, TransactTo, B256, U256, - }, - CallStackFrame, EvmContext, Inspector, -}; -use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use auto_impl::auto_impl; -use core::{fmt, marker::PhantomData, ops::Range}; - -#[cfg(feature = "optimism")] -use crate::optimism; - -/// EVM call stack limit. -pub const CALL_STACK_LIMIT: u64 = 1024; - -pub struct EVMImpl<'a, SPEC: Spec, DB: Database> { - pub context: EvmContext<'a, DB>, - pub inspector: Option<&'a mut dyn Inspector>, - pub instruction_table: InstructionTables<'a, Self>, - pub handler: Handler, - _phantomdata: PhantomData, -} - -impl fmt::Debug for EVMImpl<'_, SPEC, DB> -where - SPEC: Spec, - DB: Database + fmt::Debug, - DB::Error: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("EVMImpl") - .field("data", &self.context) - .finish_non_exhaustive() - } -} - -#[cfg(feature = "optimism")] -impl<'a, SPEC: Spec, DB: Database> EVMImpl<'a, SPEC, DB> { - /// 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. - fn remove_l1_cost( - is_deposit: bool, - tx_caller: Address, - l1_cost: U256, - db: &mut DB, - journal: &mut JournaledState, - ) -> Result<(), EVMError> { - if is_deposit { - return Ok(()); - } - let acc = journal - .load_account(tx_caller, db) - .map_err(EVMError::Database)? - .0; - if l1_cost.gt(&acc.info.balance) { - return Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(l1_cost), - balance: Box::new(acc.info.balance), - }, - )); - } - acc.info.balance = acc.info.balance.saturating_sub(l1_cost); - Ok(()) - } - - /// 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. - fn commit_mint_value( - tx_caller: Address, - tx_mint: Option, - db: &mut DB, - journal: &mut JournaledState, - ) -> Result<(), EVMError> { - if let Some(mint) = tx_mint { - journal - .load_account(tx_caller, db) - .map_err(EVMError::Database)? - .0 - .info - .balance += U256::from(mint); - journal.checkpoint(); - } - Ok(()) - } -} - -impl<'a, SPEC: Spec + 'static, DB: Database> EVMImpl<'a, SPEC, DB> { - pub fn new_with_spec( - db: &'a mut DB, - env: &'a mut Env, - inspector: Option<&'a mut dyn Inspector>, - precompiles: Precompiles, - ) -> Self { - let journaled_state = - JournaledState::new(SPEC::SPEC_ID, precompiles.addresses().copied().collect()); - // If T is present it should be a generic T that modifies handler. - let instruction_table = if inspector.is_some() { - let instruction_table = make_boxed_instruction_table::( - make_instruction_table::(), - inspector_instruction, - ); - InstructionTables::Boxed(Arc::new(instruction_table)) - } else { - InstructionTables::Plain(Arc::new(make_instruction_table::())) - }; - #[cfg(feature = "optimism")] - let mut handler = if env.cfg.optimism { - Handler::optimism::() - } else { - Handler::mainnet::() - }; - #[cfg(not(feature = "optimism"))] - let mut handler = Handler::mainnet::(); - - if env.cfg.is_beneficiary_reward_disabled() { - // do nothing - handler.reward_beneficiary = |_, _| Ok(()); - } - - Self { - context: EvmContext { - env, - journaled_state, - db, - error: None, - precompiles, - #[cfg(feature = "optimism")] - l1_block_info: None, - }, - inspector, - instruction_table, - handler, - _phantomdata: PhantomData {}, - } - } - - #[inline] - pub fn run( - &mut self, - instruction_table: &[FN; 256], - first_frame: Box, - ) -> InterpreterResult - where - FN: Fn(&mut Interpreter, &mut Self), - { - 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.env.cfg.memory_limit); - #[cfg(not(feature = "memory_limit"))] - let mut shared_memory = SharedMemory::new(); - - shared_memory.new_context(); - - let mut stack_frame = call_stack.first_mut().unwrap(); - - loop { - // run interpreter - let action = stack_frame - .interpreter - .run(shared_memory, instruction_table, self); - // take shared memory back. - shared_memory = stack_frame.interpreter.take_memory(); - - let new_frame = match action { - InterpreterAction::SubCall { - inputs, - return_memory_offset, - } => self.handle_sub_call( - inputs, - stack_frame, - return_memory_offset, - &mut shared_memory, - ), - InterpreterAction::Create { inputs } => self.handle_sub_create(inputs, stack_frame), - InterpreterAction::Return { result } => { - // free memory context. - shared_memory.free_context(); - - let child = call_stack.pop().unwrap(); - let parent = call_stack.last_mut(); - - if let Some(result) = - self.handle_frame_return(child, parent, &mut shared_memory, result) - { - return result; - } - stack_frame = call_stack.last_mut().unwrap(); - continue; - } - }; - if let Some(new_frame) = new_frame { - shared_memory.new_context(); - call_stack.push(new_frame); - } - stack_frame = call_stack.last_mut().unwrap(); - } - } - - fn handle_frame_return( - &mut self, - mut child_stack_frame: Box, - parent_stack_frame: Option<&mut Box>, - shared_memory: &mut SharedMemory, - mut result: InterpreterResult, - ) -> Option { - if let Some(inspector) = self.inspector.as_mut() { - result = if child_stack_frame.is_create { - let (result, address) = inspector.create_end( - &mut self.context, - result, - child_stack_frame.created_address, - ); - child_stack_frame.created_address = address; - result - } else { - inspector.call_end(&mut self.context, result) - }; - } - - // break from loop if this is last CallStackFrame. - let Some(parent_stack_frame) = parent_stack_frame else { - let result = if child_stack_frame.is_create { - self.context - .create_return::(result, child_stack_frame) - .0 - } else { - self.context.call_return(result, child_stack_frame) - }; - - return Some(result); - }; - - if child_stack_frame.is_create { - let (result, address) = self - .context - .create_return::(result, child_stack_frame); - parent_stack_frame - .interpreter - .insert_create_output(result, Some(address)) - } else { - let subcall_memory_return_offset = - child_stack_frame.subcall_return_memory_range.clone(); - let result = self.context.call_return(result, child_stack_frame); - - parent_stack_frame.interpreter.insert_call_output( - shared_memory, - result, - subcall_memory_return_offset, - ) - } - None - } - - /// Handle Action for new sub create call, return None if there is no need - /// to add new stack frame. - #[inline] - fn handle_sub_create( - &mut self, - mut inputs: Box, - curent_stack_frame: &mut CallStackFrame, - ) -> Option> { - // Call inspector if it is some. - if let Some(inspector) = self.inspector.as_mut() { - if let Some((result, address)) = inspector.create(&mut self.context, &mut inputs) { - curent_stack_frame - .interpreter - .insert_create_output(result, address); - return None; - } - } - - match self.context.make_create_frame::(&inputs) { - Ok(new_frame) => Some(new_frame), - Err(mut result) => { - let mut address = None; - if let Some(inspector) = self.inspector.as_mut() { - let ret = inspector.create_end( - &mut self.context, - result, - curent_stack_frame.created_address, - ); - result = ret.0; - address = ret.1; - } - // insert result of the failed creation of create CallStackFrame. - curent_stack_frame - .interpreter - .insert_create_output(result, address); - None - } - } - } - - /// Handles action for new sub call, return None if there is no need to add - /// new stack frame. - #[inline] - fn handle_sub_call( - &mut self, - mut inputs: Box, - curent_stake_frame: &mut CallStackFrame, - return_memory_offset: Range, - shared_memory: &mut SharedMemory, - ) -> Option> { - // Call inspector if it is some. - if let Some(inspector) = self.inspector.as_mut() { - if let Some((result, range)) = inspector.call(&mut self.context, &mut inputs) { - curent_stake_frame - .interpreter - .insert_call_output(shared_memory, result, range); - return None; - } - } - match self - .context - .make_call_frame(&inputs, return_memory_offset.clone()) - { - Ok(new_frame) => Some(new_frame), - Err(mut result) => { - if let Some(inspector) = &mut self.inspector { - result = inspector.call_end(&mut self.context, result); - } - curent_stake_frame.interpreter.insert_call_output( - shared_memory, - result, - return_memory_offset, - ); - None - } - } - } - - /// Pre verify transaction. - pub fn preverify_transaction_inner(&mut self) -> Result<(), EVMError> { - let env = self.env(); - - // Important: validate block before tx. - env.validate_block_env::()?; - env.validate_tx::()?; - - let initial_gas_spend = initial_tx_gas::( - &env.tx.data, - env.tx.transact_to.is_create(), - &env.tx.access_list, - ); - - // Additional check to see if limit is big enough to cover initial gas. - if initial_gas_spend > env.tx.gas_limit { - return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into()); - } - - // load acc - let tx_caller = env.tx.caller; - let (caller_account, _) = self - .context - .journaled_state - .load_account(tx_caller, self.context.db) - .map_err(EVMError::Database)?; - - self.context - .env - .validate_tx_against_state(caller_account) - .map_err(Into::into) - } - - /// Transact preverified transaction. - pub fn transact_preverified_inner(&mut self) -> EVMResult { - let env = &self.context.env; - let tx_caller = env.tx.caller; - let tx_value = env.tx.value; - let tx_data = env.tx.data.clone(); - let tx_gas_limit = env.tx.gas_limit; - - // the L1-cost fee is only computed for Optimism non-deposit transactions. - #[cfg(feature = "optimism")] - let tx_l1_cost = if env.cfg.optimism && env.tx.optimism.source_hash.is_none() { - let l1_block_info = - optimism::L1BlockInfo::try_fetch(self.context.db).map_err(EVMError::Database)?; - - let Some(enveloped_tx) = &env.tx.optimism.enveloped_tx else { - panic!("[OPTIMISM] Failed to load enveloped transaction."); - }; - let tx_l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx); - - // storage l1 block info for later use. - self.context.l1_block_info = Some(l1_block_info); - - tx_l1_cost - } else { - U256::ZERO - }; - - let initial_gas_spend = initial_tx_gas::( - &tx_data, - env.tx.transact_to.is_create(), - &env.tx.access_list, - ); - - // load coinbase - // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm - if SPEC::enabled(SHANGHAI) { - self.context - .journaled_state - .initial_account_load(self.context.env.block.coinbase, &[], self.context.db) - .map_err(EVMError::Database)?; - } - - self.context.load_access_list()?; - - // load acc - let journal = &mut self.context.journaled_state; - - #[cfg(feature = "optimism")] - if self.context.env.cfg.optimism { - EVMImpl::::commit_mint_value( - tx_caller, - self.context.env.tx.optimism.mint, - self.context.db, - journal, - )?; - - let is_deposit = self.context.env.tx.optimism.source_hash.is_some(); - EVMImpl::::remove_l1_cost( - is_deposit, - tx_caller, - tx_l1_cost, - self.context.db, - journal, - )?; - } - - let (caller_account, _) = journal - .load_account(tx_caller, self.context.db) - .map_err(EVMError::Database)?; - - // 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(tx_gas_limit).saturating_mul(self.context.env.effective_gas_price()); - - // EIP-4844 - if SPEC::enabled(CANCUN) { - let data_fee = self.context.env.calc_data_fee().expect("already checked"); - gas_cost = gas_cost.saturating_add(data_fee); - } - - caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); - - // touch account so we know it is changed. - caller_account.mark_touch(); - - let transact_gas_limit = tx_gas_limit - initial_gas_spend; - - // call inner handling of call/create - let first_stack_frame = match self.context.env.tx.transact_to { - TransactTo::Call(address) => { - // Nonce is already checked - caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); - - self.context.make_call_frame( - &CallInputs { - contract: address, - transfer: Transfer { - source: tx_caller, - target: address, - value: tx_value, - }, - input: tx_data, - gas_limit: transact_gas_limit, - context: CallContext { - caller: tx_caller, - address, - code_address: address, - apparent_value: tx_value, - scheme: CallScheme::Call, - }, - is_static: false, - }, - 0..0, - ) - } - TransactTo::Create(scheme) => self.context.make_create_frame::(&CreateInputs { - caller: tx_caller, - scheme, - value: tx_value, - init_code: tx_data, - gas_limit: transact_gas_limit, - }), - }; - // Some only if it is create. - let mut created_address = None; - - // start main loop if CallStackFrame is created correctly - let interpreter_result = match first_stack_frame { - Ok(first_stack_frame) => { - created_address = first_stack_frame.created_address; - let table = self.instruction_table.clone(); - match table { - InstructionTables::Plain(table) => self.run(&table, first_stack_frame), - InstructionTables::Boxed(table) => self.run(&table, first_stack_frame), - } - } - Err(interpreter_result) => interpreter_result, - }; - - let handler = &self.handler; - let data = &mut self.context; - - // handle output of call/create calls. - let mut gas = - handler.call_return(data.env, interpreter_result.result, interpreter_result.gas); - - // set refund. Refund amount depends on hardfork. - gas.set_refund(handler.calculate_gas_refund(data.env, &gas) as i64); - - // Reimburse the caller - handler.reimburse_caller(data, &gas)?; - - // Reward beneficiary - handler.reward_beneficiary(data, &gas)?; - - // output of execution - let output = match data.env.tx.transact_to { - TransactTo::Call(_) => Output::Call(interpreter_result.output), - TransactTo::Create(_) => Output::Create(interpreter_result.output, created_address), - }; - - // main return - handler.main_return(data, interpreter_result.result, output, &gas) - } -} - -/// EVM transaction interface. -#[auto_impl(&mut, Box)] -pub trait Transact { - /// Run checks that could make transaction fail before call/create. - fn preverify_transaction(&mut self) -> Result<(), EVMError>; - - /// Skip pre-verification steps and execute the transaction. - fn transact_preverified(&mut self) -> EVMResult; - - /// Execute transaction by running pre-verification steps and then transaction itself. - fn transact(&mut self) -> EVMResult; -} - -impl<'a, SPEC: Spec + 'static, DB: Database> Transact for EVMImpl<'a, SPEC, DB> { - #[inline] - fn preverify_transaction(&mut self) -> Result<(), EVMError> { - self.preverify_transaction_inner() - } - - #[inline] - fn transact_preverified(&mut self) -> EVMResult { - let output = self.transact_preverified_inner(); - self.handler.end(&mut self.context, output) - } - - #[inline] - fn transact(&mut self) -> EVMResult { - let output = self - .preverify_transaction_inner() - .and_then(|()| self.transact_preverified_inner()); - self.handler.end(&mut self.context, output) - } -} - -impl<'a, SPEC: Spec + 'static, DB: Database> Host for EVMImpl<'a, SPEC, DB> { - fn env(&mut self) -> &mut Env { - self.context.env() - } - - fn block_hash(&mut self, number: U256) -> Option { - self.context.block_hash(number) - } - - fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { - self.context.load_account(address) - } - - fn balance(&mut self, address: Address) -> Option<(U256, bool)> { - self.context.balance(address) - } - - fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { - self.context.code(address) - } - - /// Get code hash of address. - fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { - self.context.code_hash(address) - } - - fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { - self.context.sload(address, index) - } - - fn sstore( - &mut self, - address: Address, - index: U256, - value: U256, - ) -> Option<(U256, U256, U256, bool)> { - self.context.sstore(address, index, value) - } - - fn tload(&mut self, address: Address, index: U256) -> U256 { - self.context.tload(address, index) - } - - fn tstore(&mut self, address: Address, index: U256, value: U256) { - self.context.tstore(address, index, value) - } - - fn log(&mut self, address: Address, topics: Vec, data: Bytes) { - if let Some(inspector) = self.inspector.as_mut() { - inspector.log(&mut self.context, &address, &topics, &data); - } - let log = Log { - address, - topics, - data, - }; - self.context.journaled_state.log(log); - } - - fn selfdestruct(&mut self, address: Address, target: Address) -> Option { - if let Some(inspector) = self.inspector.as_mut() { - let acc = self.context.journaled_state.state.get(&address).unwrap(); - inspector.selfdestruct(address, target, acc.info.balance); - } - self.context - .journaled_state - .selfdestruct(address, target, self.context.db) - .map_err(|e| self.context.error = Some(e)) - .ok() - } -} - -/// Creates new EVM instance with erased types. -pub fn new_evm<'a, DB: Database>( - env: &'a mut Env, - db: &'a mut DB, - insp: Option<&'a mut dyn Inspector>, -) -> Box + 'a> { - macro_rules! create_evm { - ($spec:ident) => { - Box::new(EVMImpl::<'a, $spec, DB>::new_with_spec( - db, - env, - insp, - Precompiles::new(revm_precompile::SpecId::from_spec_id($spec::SPEC_ID)).clone(), - )) - }; - } - - use specification::*; - match env.cfg.spec_id { - SpecId::FRONTIER | SpecId::FRONTIER_THAWING => create_evm!(FrontierSpec), - SpecId::HOMESTEAD | SpecId::DAO_FORK => create_evm!(HomesteadSpec), - SpecId::TANGERINE => create_evm!(TangerineSpec), - SpecId::SPURIOUS_DRAGON => create_evm!(SpuriousDragonSpec), - SpecId::BYZANTIUM => create_evm!(ByzantiumSpec), - SpecId::PETERSBURG | SpecId::CONSTANTINOPLE => create_evm!(PetersburgSpec), - SpecId::ISTANBUL | SpecId::MUIR_GLACIER => create_evm!(IstanbulSpec), - SpecId::BERLIN => create_evm!(BerlinSpec), - SpecId::LONDON | SpecId::ARROW_GLACIER | SpecId::GRAY_GLACIER => { - create_evm!(LondonSpec) - } - SpecId::MERGE => create_evm!(MergeSpec), - SpecId::SHANGHAI => create_evm!(ShanghaiSpec), - SpecId::CANCUN => create_evm!(CancunSpec), - SpecId::LATEST => create_evm!(LatestSpec), - #[cfg(feature = "optimism")] - SpecId::BEDROCK => create_evm!(BedrockSpec), - #[cfg(feature = "optimism")] - SpecId::REGOLITH => create_evm!(RegolithSpec), - #[cfg(feature = "optimism")] - SpecId::CANYON => create_evm!(CanyonSpec), - } -} - -#[cfg(feature = "optimism")] -#[cfg(test)] -mod tests { - use super::*; - - use crate::db::InMemoryDB; - use crate::primitives::{specification::BedrockSpec, state::AccountInfo, SpecId}; - - #[test] - fn test_commit_mint_value() { - let caller = Address::ZERO; - let mint_value = Some(1u128); - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert!(EVMImpl::::commit_mint_value( - caller, - mint_value, - &mut db, - &mut journal - ) - .is_ok(),); - - // Check the account balance is updated. - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(101)); - - // No mint value should be a no-op. - assert!(EVMImpl::::commit_mint_value( - caller, - None, - &mut db, - &mut journal - ) - .is_ok(),); - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(101)); - } - - #[test] - fn test_remove_l1_cost_non_deposit() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - let slots = &[U256::from(100)]; - journal - .initial_account_load(caller, slots, &mut db) - .unwrap(); - assert!(EVMImpl::::remove_l1_cost( - true, - caller, - U256::ZERO, - &mut db, - &mut journal - ) - .is_ok(),); - } - - #[test] - fn test_remove_l1_cost() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert!(EVMImpl::::remove_l1_cost( - false, - caller, - U256::from(1), - &mut db, - &mut journal - ) - .is_ok(),); - - // Check the account balance is updated. - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(99)); - } - - #[test] - fn test_remove_l1_cost_lack_of_funds() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert_eq!( - EVMImpl::::remove_l1_cost( - false, - caller, - U256::from(101), - &mut db, - &mut journal - ), - Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(U256::from(101)), - balance: Box::new(U256::from(100)), - }, - )) - ); - } -} diff --git a/crates/revm/src/frame.rs b/crates/revm/src/frame.rs index fc1ff95e18..9397732d12 100644 --- a/crates/revm/src/frame.rs +++ b/crates/revm/src/frame.rs @@ -1,18 +1,62 @@ -use crate::{interpreter::Interpreter, primitives::Address, JournalCheckpoint}; +use crate::{ + interpreter::{Interpreter, InterpreterResult}, + primitives::Address, + JournalCheckpoint, +}; +use alloc::boxed::Box; use core::ops::Range; /// Call CallStackFrame. #[derive(Debug)] pub struct CallStackFrame { - /// True if it is create false if it is call. - /// TODO make a enum for this. - pub is_create: bool, /// Journal checkpoint pub checkpoint: JournalCheckpoint, - /// temporary. If it is create it should have address. - pub created_address: Option
, - /// temporary. Call range - pub subcall_return_memory_range: Range, /// Interpreter pub interpreter: Interpreter, + /// Frame data + pub frame_data: FrameData, +} + +/// Specific data for call or create frame. +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum FrameData { + /// Call frame has return memory range where output will be stored. + Call { return_memory_range: Range }, + /// Create frame has a created address. + Create { created_address: Address }, +} + +/// Contains either a frame or a result. +pub enum FrameOrResult { + /// Boxed stack frame + Frame(Box), + /// Interpreter result + Result(InterpreterResult), +} + +impl CallStackFrame { + /// Returns true if frame is call frame. + pub fn is_call(&self) -> bool { + matches!(self.frame_data, FrameData::Call { .. }) + } + + /// Returns true if frame is create frame. + pub fn is_create(&self) -> bool { + matches!(self.frame_data, FrameData::Create { .. }) + } + + /// Returns created address if frame is create otherwise returns None. + pub fn created_address(&self) -> Option
{ + match self.frame_data { + FrameData::Create { created_address } => Some(created_address), + _ => None, + } + } +} + +impl FrameOrResult { + /// Returns new frame. + pub fn new_frame(frame: CallStackFrame) -> Self { + Self::Frame(Box::new(frame)) + } } diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index eef328bd41..c43b3c3b2a 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -1,137 +1,128 @@ +// Modules. +mod handle_types; pub mod mainnet; -#[cfg(feature = "optimism")] -pub mod optimism; +pub mod register; +// Exports. +pub use handle_types::*; + +// Includes. use crate::{ - interpreter::{Gas, InstructionResult}, - primitives::{db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec}, - EvmContext, + interpreter::{ + opcode::{make_instruction_table, InstructionTables}, + Host, + }, + primitives::{db::Database, spec_to_generic, Spec, SpecId}, }; - -/// Handle call return and return final gas value. -type CallReturnHandle = fn(&Env, InstructionResult, Gas) -> Gas; - -/// Reimburse the caller with ethereum it didn't spent. -type ReimburseCallerHandle = - fn(&mut EvmContext<'_, DB>, &Gas) -> EVMResultGeneric<(), ::Error>; - -/// Reward beneficiary with transaction rewards. -type RewardBeneficiaryHandle = ReimburseCallerHandle; - -/// Calculate gas refund for transaction. -type CalculateGasRefundHandle = fn(&Env, &Gas) -> u64; - -/// Main return handle, takes state from journal and transforms internal result to external. -type MainReturnHandle = fn( - &mut EvmContext<'_, DB>, - InstructionResult, - Output, - &Gas, -) -> Result::Error>>; - -/// 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. -type EndHandle = fn( - &mut EvmContext<'_, DB>, - evm_output: Result::Error>>, -) -> Result::Error>>; +use alloc::vec::Vec; +use register::{EvmHandler, HandleRegisters}; /// 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 { - // Uses env, call result and returned gas from the call to determine the gas - // that is returned from transaction execution.. - pub call_return: CallReturnHandle, - /// Reimburse the caller with ethereum it didn't spent. - pub reimburse_caller: ReimburseCallerHandle, - /// Reward the beneficiary with caller fee. - pub reward_beneficiary: RewardBeneficiaryHandle, - /// Calculate gas refund for transaction. - /// Some chains have it disabled. - pub calculate_gas_refund: CalculateGasRefundHandle, - /// Main return handle, returns the output of the transact. - pub main_return: MainReturnHandle, - /// End handle. - pub end: EndHandle, +pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { + /// Specification ID. + pub spec_id: SpecId, + /// Instruction table type. + pub instruction_table: Option>, + /// Registers that will be called on initialization. + pub registers: Vec>, + /// Validity handles. + pub validation: ValidationHandler<'a, EXT, DB>, + /// Pre execution handle + pub pre_execution: PreExecutionHandler<'a, EXT, DB>, + /// post Execution handle + pub post_execution: PostExecutionHandler<'a, EXT, DB>, + /// Execution loop that handles frames. + pub execution_loop: ExecutionLoopHandler<'a, EXT, DB>, } -impl Handler { +impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// Handler for the mainnet - pub fn mainnet() -> Self { + pub fn mainnet() -> Self { Self { - call_return: mainnet::handle_call_return::, - calculate_gas_refund: mainnet::calculate_gas_refund::, - reimburse_caller: mainnet::handle_reimburse_caller::, - reward_beneficiary: mainnet::reward_beneficiary::, - main_return: mainnet::main_return::, - end: mainnet::end_handle::, + spec_id: SPEC::SPEC_ID, + instruction_table: Some(InstructionTables::Plain(make_instruction_table::())), + registers: Vec::new(), + validation: ValidationHandler::new::(), + pre_execution: PreExecutionHandler::new::(), + post_execution: PostExecutionHandler::new::(), + execution_loop: ExecutionLoopHandler::new::(), } } - /// Handler for the optimism - #[cfg(feature = "optimism")] - pub fn optimism() -> Self { - Self { - call_return: optimism::handle_call_return::, - // we reinburse caller the same was as in mainnet. - // Refund is calculated differently then mainnet. - reimburse_caller: mainnet::handle_reimburse_caller::, - calculate_gas_refund: optimism::calculate_gas_refund::, - reward_beneficiary: optimism::reward_beneficiary::, - // In case of halt of deposit transaction return Error. - main_return: optimism::main_return::, - end: optimism::end_handle::, - } + /// Creates handler with variable spec id, inside it will call `mainnet::` for + /// appropriate spec. + pub fn mainnet_with_spec(spec_id: SpecId) -> Self { + spec_to_generic!(spec_id, Self::mainnet::()) + } + + /// Specification ID. + pub fn spec_id(&self) -> SpecId { + self.spec_id } - /// Handle call return, depending on instruction result gas will be reimbursed or not. - pub fn call_return(&self, env: &Env, call_result: InstructionResult, returned_gas: Gas) -> Gas { - (self.call_return)(env, call_result, returned_gas) + /// Take instruction table. + pub fn take_instruction_table(&mut self) -> Option> { + self.instruction_table.take() } - /// Reimburse the caller with gas that were not spend. - pub fn reimburse_caller( - &self, - context: &mut EvmContext<'_, DB>, - gas: &Gas, - ) -> Result<(), EVMError> { - (self.reimburse_caller)(context, gas) + /// Set instruction table. + pub fn set_instruction_table(&mut self, table: InstructionTables<'a, H>) { + self.instruction_table = Some(table); } - /// Calculate gas refund for transaction. Some chains have it disabled. - pub fn calculate_gas_refund(&self, env: &Env, gas: &Gas) -> u64 { - (self.calculate_gas_refund)(env, gas) + /// Returns reference to pre execution handler. + pub fn pre_execution(&self) -> &PreExecutionHandler<'a, EXT, DB> { + &self.pre_execution } - /// Reward beneficiary - pub fn reward_beneficiary( - &self, - context: &mut EvmContext<'_, DB>, - gas: &Gas, - ) -> Result<(), EVMError> { - (self.reward_beneficiary)(context, gas) + /// Returns reference to pre execution handler. + pub fn post_execution(&self) -> &PostExecutionHandler<'a, EXT, DB> { + &self.post_execution } - /// Main return. - pub fn main_return( - &self, - context: &mut EvmContext<'_, DB>, - call_result: InstructionResult, - output: Output, - gas: &Gas, - ) -> Result> { - (self.main_return)(context, call_result, output, gas) + /// Returns reference to frame handler. + pub fn execution_loop(&self) -> &ExecutionLoopHandler<'a, EXT, DB> { + &self.execution_loop } - /// End handler. - pub fn end( - &self, - context: &mut EvmContext<'_, DB>, - end_output: Result>, - ) -> Result> { - (self.end)(context, end_output) + /// Returns reference to validation handler. + pub fn validation(&self) -> &ValidationHandler<'a, EXT, DB> { + &self.validation + } +} + +impl<'a, EXT: 'a, DB: Database + 'a> EvmHandler<'a, EXT, DB> { + /// Append handle register. + pub fn append_handle_register(&mut self, register: HandleRegisters<'a, EXT, DB>) { + register.register(self); + self.registers.push(register); + } + + /// Creates the Handler with Generic Spec. + pub fn create_handle_generic(&mut self) -> EvmHandler<'a, EXT, DB> { + let registers = core::mem::take(&mut self.registers); + let mut base_handler = Handler::mainnet::(); + // apply all registers to default handeler and raw mainnet instruction table. + for register in registers { + base_handler.append_handle_register(register) + } + base_handler + } + + /// Creates the Handler with variable SpecId, inside it will call function with Generic Spec. + pub fn change_spec_id(mut self, spec_id: SpecId) -> EvmHandler<'a, EXT, DB> { + if self.spec_id == spec_id { + return self; + } + + let registers = core::mem::take(&mut self.registers); + let mut handler = Handler::mainnet_with_spec(spec_id); + // apply all registers to default handeler and raw mainnet instruction table. + for register in registers { + handler.append_handle_register(register) + } + handler } } diff --git a/crates/revm/src/handler/handle_types.rs b/crates/revm/src/handler/handle_types.rs new file mode 100644 index 0000000000..a37458c76a --- /dev/null +++ b/crates/revm/src/handler/handle_types.rs @@ -0,0 +1,25 @@ +// Modules + +pub mod execution_loop; +pub mod post_execution; +pub mod pre_execution; +pub mod validation; + +// Exports + +pub use validation::{ + ValidateEnvHandle, ValidateInitialTxGasHandle, ValidateTxEnvAgainstState, ValidationHandler, +}; + +pub use execution_loop::{ + CreateFirstFrameHandle, ExecutionLoopHandler, FrameReturnHandle, FrameSubCallHandle, + FrameSubCreateHandle, +}; + +pub use pre_execution::{ + DeductCallerHandle, LoadAccountsHandle, LoadPrecompilesHandle, PreExecutionHandler, +}; + +pub use post_execution::{ + EndHandle, OutputHandle, PostExecutionHandler, ReimburseCallerHandle, RewardBeneficiaryHandle, +}; diff --git a/crates/revm/src/handler/handle_types/execution_loop.rs b/crates/revm/src/handler/handle_types/execution_loop.rs new file mode 100644 index 0000000000..41a2ae9065 --- /dev/null +++ b/crates/revm/src/handler/handle_types/execution_loop.rs @@ -0,0 +1,154 @@ +use crate::{ + handler::mainnet, + interpreter::{ + CallInputs, CreateInputs, Gas, InstructionResult, InterpreterResult, SharedMemory, + }, + primitives::{db::Database, Env, Spec}, + CallStackFrame, Context, FrameOrResult, +}; +use alloc::{boxed::Box, sync::Arc}; +use core::ops::Range; + +/// Creates the first frame. +pub type CreateFirstFrameHandle<'a, EXT, DB> = + Arc, u64) -> FrameOrResult + 'a>; + +/// Handles first frame return handle. +pub type FirstFrameReturnHandle<'a> = Arc Gas + 'a>; + +/// After subcall is finished, call this function to handle return result. +/// +/// Return Some if we want to halt execution. This can be done on any stack frame. +pub type FrameReturnHandle<'a, EXT, DB> = Arc< + dyn Fn( + // context + &mut Context, + // returned frame + Box, + // parent frame if it exist. + Option<&mut Box>, + // shared memory to insert output of the call. + &mut SharedMemory, + // output of frame execution. + InterpreterResult, + ) -> Option + + 'a, +>; + +/// Handle sub call. +pub type FrameSubCallHandle<'a, EXT, DB> = Arc< + dyn Fn( + &mut Context, + Box, + &mut CallStackFrame, + &mut SharedMemory, + Range, + ) -> Option> + + 'a, +>; + +/// Handle sub create. +pub type FrameSubCreateHandle<'a, EXT, DB> = Arc< + dyn Fn( + &mut Context, + &mut CallStackFrame, + Box, + ) -> Option> + + 'a, +>; + +/// Handles related to stack frames. +pub struct ExecutionLoopHandler<'a, EXT, DB: Database> { + /// Create Main frame + pub create_first_frame: CreateFirstFrameHandle<'a, EXT, DB>, + /// Validate Transaction against the state. + /// Uses env, call result and returned gas from the call to determine the gas + /// that is returned from transaction execution.. + pub first_frame_return: FirstFrameReturnHandle<'a>, + /// Frame return + pub frame_return: FrameReturnHandle<'a, EXT, DB>, + /// Frame sub call + pub sub_call: FrameSubCallHandle<'a, EXT, DB>, + /// Frame sub crate + pub sub_create: FrameSubCreateHandle<'a, EXT, DB>, +} + +impl<'a, EXT: 'a, DB: Database + 'a> ExecutionLoopHandler<'a, EXT, DB> { + /// Creates mainnet ExecutionLoopHandler.. + pub fn new() -> Self { + Self { + create_first_frame: Arc::new(mainnet::create_first_frame::), + first_frame_return: Arc::new(mainnet::first_frame_return::), + frame_return: Arc::new(mainnet::frame_return::), + sub_call: Arc::new(mainnet::sub_call::), + sub_create: Arc::new(mainnet::sub_create::), + } + } +} + +impl<'a, EXT, DB: Database> ExecutionLoopHandler<'a, EXT, DB> { + /// Create first call frame. + pub fn create_first_frame( + &self, + context: &mut Context, + gas_limit: u64, + ) -> FrameOrResult { + (self.create_first_frame)(context, gas_limit) + } + + /// Handle call return, depending on instruction result gas will be reimbursed or not. + pub fn first_frame_return( + &self, + env: &Env, + call_result: InstructionResult, + returned_gas: Gas, + ) -> Gas { + (self.first_frame_return)(env, call_result, returned_gas) + } + + /// Call frame sub call handler. + pub fn sub_call( + &self, + context: &mut Context, + inputs: Box, + curent_stack_frame: &mut CallStackFrame, + shared_memory: &mut SharedMemory, + return_memory_offset: Range, + ) -> Option> { + (self.sub_call)( + context, + inputs, + curent_stack_frame, + shared_memory, + return_memory_offset, + ) + } + + /// Create sub frame + pub fn sub_create( + &self, + context: &mut Context, + curent_stack_frame: &mut CallStackFrame, + inputs: Box, + ) -> Option> { + (self.sub_create)(context, curent_stack_frame, inputs) + } + + /// Frame return + pub fn frame_return( + &self, + context: &mut Context, + child_stack_frame: Box, + parent_stack_frame: Option<&mut Box>, + shared_memory: &mut SharedMemory, + result: InterpreterResult, + ) -> Option { + (self.frame_return)( + context, + child_stack_frame, + parent_stack_frame, + shared_memory, + result, + ) + } +} diff --git a/crates/revm/src/handler/handle_types/post_execution.rs b/crates/revm/src/handler/handle_types/post_execution.rs new file mode 100644 index 0000000000..c665a30b75 --- /dev/null +++ b/crates/revm/src/handler/handle_types/post_execution.rs @@ -0,0 +1,101 @@ +// Includes. +use crate::{ + handler::mainnet, + interpreter::{Gas, InstructionResult}, + primitives::{db::Database, EVMError, EVMResultGeneric, Output, ResultAndState, Spec}, + Context, +}; +use alloc::sync::Arc; + +/// Reimburse the caller with ethereum it didn't spent. +pub type ReimburseCallerHandle<'a, EXT, DB> = + Arc, &Gas) -> EVMResultGeneric<(), ::Error> + 'a>; + +/// Reward beneficiary with transaction rewards. +pub type RewardBeneficiaryHandle<'a, EXT, DB> = ReimburseCallerHandle<'a, EXT, DB>; + +/// Main return handle, takes state from journal and transforms internal result to external. +pub type OutputHandle<'a, EXT, DB> = Arc< + dyn Fn( + &mut Context, + InstructionResult, + Output, + &Gas, + ) -> Result::Error>> + + '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, EXT, DB> = Arc< + dyn Fn( + &mut Context, + Result::Error>>, + ) -> Result::Error>> + + 'a, +>; + +/// Handles related to post execution after the stack loop is finished. +pub struct PostExecutionHandler<'a, EXT, DB: Database> { + /// Reimburse the caller with ethereum it didn't spent. + pub reimburse_caller: ReimburseCallerHandle<'a, EXT, DB>, + /// Reward the beneficiary with caller fee. + pub reward_beneficiary: RewardBeneficiaryHandle<'a, EXT, DB>, + /// Main return handle, returns the output of the transact. + pub output: OutputHandle<'a, EXT, DB>, + /// End handle. + pub end: EndHandle<'a, EXT, DB>, +} + +impl<'a, EXT: 'a, DB: Database + 'a> PostExecutionHandler<'a, EXT, DB> { + /// Creates mainnet MainHandles. + pub fn new() -> Self { + Self { + reimburse_caller: Arc::new(mainnet::reimburse_caller::), + reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), + output: Arc::new(mainnet::output::), + end: Arc::new(mainnet::end::), + } + } +} + +impl<'a, EXT, DB: Database> PostExecutionHandler<'a, EXT, DB> { + /// Reimburse the caller with gas that were not spend. + pub fn reimburse_caller( + &self, + context: &mut Context, + gas: &Gas, + ) -> Result<(), EVMError> { + (self.reimburse_caller)(context, gas) + } + /// Reward beneficiary + pub fn reward_beneficiary( + &self, + context: &mut Context, + gas: &Gas, + ) -> Result<(), EVMError> { + (self.reward_beneficiary)(context, gas) + } + + /// Returns the output of transaction. + pub fn output( + &self, + context: &mut Context, + call_result: InstructionResult, + output: Output, + gas: &Gas, + ) -> Result> { + (self.output)(context, call_result, output, gas) + } + + /// End handler. + pub fn end( + &self, + context: &mut Context, + end_output: Result>, + ) -> Result> { + (self.end)(context, end_output) + } +} diff --git a/crates/revm/src/handler/handle_types/pre_execution.rs b/crates/revm/src/handler/handle_types/pre_execution.rs new file mode 100644 index 0000000000..0872a22e4d --- /dev/null +++ b/crates/revm/src/handler/handle_types/pre_execution.rs @@ -0,0 +1,59 @@ +// Includes. +use crate::{ + handler::mainnet, + primitives::{db::Database, EVMError, EVMResultGeneric, Spec}, + Context, +}; +use alloc::sync::Arc; +use revm_precompile::Precompiles; + +/// Loads precompiles into Evm +pub type LoadPrecompilesHandle<'a> = Arc Precompiles + '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, EXT, DB> = + Arc) -> Result<(), EVMError<::Error>> + 'a>; + +/// Deduct the caller to its limit. +pub type DeductCallerHandle<'a, EXT, DB> = + Arc) -> EVMResultGeneric<(), ::Error> + 'a>; + +/// Handles related to pre execution before the stack loop is started. +pub struct PreExecutionHandler<'a, EXT, DB: Database> { + /// Load precompiles + pub load_precompiles: LoadPrecompilesHandle<'a>, + /// Main load handle + pub load_accounts: LoadAccountsHandle<'a, EXT, DB>, + /// Deduct max value from the caller. + pub deduct_caller: DeductCallerHandle<'a, EXT, DB>, +} + +impl<'a, EXT: 'a, DB: Database + 'a> PreExecutionHandler<'a, EXT, DB> { + /// Creates mainnet MainHandles. + pub fn new() -> Self { + Self { + load_precompiles: Arc::new(mainnet::load_precompiles::), + load_accounts: Arc::new(mainnet::load::), + deduct_caller: Arc::new(mainnet::deduct_caller::), + } + } +} + +impl<'a, EXT, DB: Database> PreExecutionHandler<'a, EXT, DB> { + /// Deduct caller to its limit. + pub fn deduct_caller(&self, context: &mut Context) -> Result<(), EVMError> { + (self.deduct_caller)(context) + } + + /// Main load + pub fn load_accounts(&self, context: &mut Context) -> Result<(), EVMError> { + (self.load_accounts)(context) + } + + /// Load precompiles + pub fn load_precompiles(&self) -> Precompiles { + (self.load_precompiles)() + } +} diff --git a/crates/revm/src/handler/handle_types/validation.rs b/crates/revm/src/handler/handle_types/validation.rs new file mode 100644 index 0000000000..8516960d28 --- /dev/null +++ b/crates/revm/src/handler/handle_types/validation.rs @@ -0,0 +1,60 @@ +use crate::{ + handler::mainnet, + primitives::{db::Database, EVMError, Env, Spec}, + Context, +}; +use alloc::sync::Arc; + +/// Handle that validates env. +pub type ValidateEnvHandle<'a, DB> = + Arc Result<(), EVMError<::Error>> + 'a>; + +/// Handle that validates transaction environment against the state. +/// Second parametar is initial gas. +pub type ValidateTxEnvAgainstState<'a, EXT, DB> = + Arc) -> Result<(), EVMError<::Error>> + 'a>; + +/// Initial gas calculation handle +pub type ValidateInitialTxGasHandle<'a, DB> = + Arc Result::Error>> + 'a>; + +/// Handles related to validation. +pub struct ValidationHandler<'a, EXT, DB: Database> { + /// Validate and calculate initial transaction gas. + pub initial_tx_gas: ValidateInitialTxGasHandle<'a, DB>, + /// Validate transactions against state data. + pub tx_against_state: ValidateTxEnvAgainstState<'a, EXT, DB>, + /// Validate Env. + pub env: ValidateEnvHandle<'a, DB>, +} + +impl<'a, EXT: 'a, DB: Database + 'a> ValidationHandler<'a, EXT, DB> { + /// 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, EXT, DB: Database> ValidationHandler<'a, EXT, DB> { + /// Validate env. + pub fn env(&self, env: &Env) -> Result<(), EVMError> { + (self.env)(env) + } + + /// Initial gas + pub fn initial_tx_gas(&self, env: &Env) -> Result> { + (self.initial_tx_gas)(env) + } + + /// Validate ttansaction against the state. + pub fn tx_against_state( + &self, + context: &mut Context, + ) -> Result<(), EVMError> { + (self.tx_against_state)(context) + } +} diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index c39293391f..cf92067938 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -1,210 +1,14 @@ //! Mainnet related handlers. -use crate::{ - interpreter::{return_ok, return_revert, Gas, InstructionResult, SuccessOrHalt}, - primitives::{ - db::Database, EVMError, Env, ExecutionResult, Output, ResultAndState, Spec, SpecId::LONDON, - U256, - }, - EvmContext, +mod execution_loop; +mod post_execution; +mod pre_execution; +mod validation; + +pub use execution_loop::{ + create_first_frame, first_frame_return, frame_return, frame_return_with_refund_flag, sub_call, + sub_create, }; - -/// Handle output of the transaction -#[inline] -pub fn handle_call_return( - env: &Env, - call_result: InstructionResult, - returned_gas: Gas, -) -> Gas { - let tx_gas_limit = env.tx.gas_limit; - // Spend the gas limit. Gas is reimbursed when the tx returns successfully. - let mut gas = Gas::new(tx_gas_limit); - gas.record_cost(tx_gas_limit); - - match call_result { - return_ok!() => { - gas.erase_cost(returned_gas.remaining()); - gas.record_refund(returned_gas.refunded()); - } - return_revert!() => { - gas.erase_cost(returned_gas.remaining()); - } - _ => {} - } - gas -} - -#[inline] -pub fn handle_reimburse_caller( - context: &mut EvmContext<'_, DB>, - gas: &Gas, -) -> Result<(), EVMError> { - let caller = context.env.tx.caller; - let effective_gas_price = context.env.effective_gas_price(); - - // return balance of not spend gas. - let (caller_account, _) = context - .journaled_state - .load_account(caller, context.db) - .map_err(EVMError::Database)?; - - caller_account.info.balance = caller_account - .info - .balance - .saturating_add(effective_gas_price * U256::from(gas.remaining() + gas.refunded() as u64)); - - Ok(()) -} - -/// Reward beneficiary with gas fee. -#[inline] -pub fn reward_beneficiary( - context: &mut EvmContext<'_, DB>, - gas: &Gas, -) -> Result<(), EVMError> { - let beneficiary = context.env.block.coinbase; - let effective_gas_price = context.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(LONDON) { - effective_gas_price.saturating_sub(context.env.block.basefee) - } else { - effective_gas_price - }; - - let (coinbase_account, _) = context - .journaled_state - .load_account(beneficiary, context.db) - .map_err(EVMError::Database)?; - - coinbase_account.mark_touch(); - coinbase_account.info.balance = coinbase_account - .info - .balance - .saturating_add(coinbase_gas_price * U256::from(gas.spend() - gas.refunded() as u64)); - - Ok(()) -} - -/// Calculate gas refund for transaction. -/// -/// If config is set to disable gas refund, it will return 0. -/// -/// 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) -#[inline] -pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { - if env.cfg.is_gas_refund_disabled() { - 0 - } else { - // EIP-3529: Reduction in refunds - let max_refund_quotient = if SPEC::enabled(LONDON) { 5 } else { 2 }; - (gas.refunded() as u64).min(gas.spend() / max_refund_quotient) - } -} - -//pub fn main_first_call - -/// Main return handle, returns the output of the transaction. -#[inline] -pub fn main_return( - context: &mut EvmContext<'_, DB>, - call_result: InstructionResult, - output: Output, - gas: &Gas, -) -> Result> { - // used gas with refund calculated. - let gas_refunded = gas.refunded() as u64; - let final_gas_used = gas.spend() - gas_refunded; - - // reset journal and return present state. - let (state, logs) = context.journaled_state.finalize(); - - let result = match call_result.into() { - 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: match output { - Output::Call(return_value) => return_value, - Output::Create(return_value, _) => return_value, - }, - }, - SuccessOrHalt::Halt(reason) => ExecutionResult::Halt { - reason, - gas_used: final_gas_used, - }, - SuccessOrHalt::FatalExternalError => { - return Err(EVMError::Database(context.error.take().unwrap())); - } - // Only two internal return flags. - SuccessOrHalt::InternalContinue | SuccessOrHalt::InternalCallOrCreate => { - panic!("Internal return flags should remain internal {call_result:?}") - } - }; - - Ok(ResultAndState { result, state }) -} - -/// Mainnet end handle does not change the output. -#[inline] -pub fn end_handle( - _context: &mut EvmContext<'_, DB>, - evm_output: Result>, -) -> Result> { - evm_output -} - -#[cfg(test)] -mod tests { - use revm_interpreter::primitives::CancunSpec; - - use super::*; - - #[test] - fn test_consume_gas() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let gas = handle_call_return::(&env, InstructionResult::Stop, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_consume_gas_with_refund() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let mut return_gas = Gas::new(90); - return_gas.record_refund(30); - - let gas = handle_call_return::(&env, InstructionResult::Stop, return_gas); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 30); - - let gas = handle_call_return::(&env, InstructionResult::Revert, return_gas); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_revert_gas() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let gas = handle_call_return::(&env, InstructionResult::Revert, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } -} +pub use post_execution::{end, output, reimburse_caller, reward_beneficiary}; +pub use pre_execution::{deduct_caller, deduct_caller_inner, load, load_precompiles}; +pub use validation::{validate_env, validate_initial_tx_gas, validate_tx_against_state}; diff --git a/crates/revm/src/handler/mainnet/execution_loop.rs b/crates/revm/src/handler/mainnet/execution_loop.rs new file mode 100644 index 0000000000..9de9d2e37d --- /dev/null +++ b/crates/revm/src/handler/mainnet/execution_loop.rs @@ -0,0 +1,210 @@ +use crate::{ + db::Database, + interpreter::{ + return_ok, return_revert, CallInputs, CreateInputs, Gas, InstructionResult, + InterpreterResult, SharedMemory, + }, + primitives::{Env, Spec, TransactTo}, + CallStackFrame, Context, FrameData, FrameOrResult, +}; +use alloc::boxed::Box; +use core::ops::Range; + +/// Creates first frame. +#[inline] +pub fn create_first_frame( + context: &mut Context, + gas_limit: u64, +) -> FrameOrResult { + // call inner handling of call/create + match context.evm.env.tx.transact_to { + TransactTo::Call(_) => context.evm.make_call_frame( + &CallInputs::new(&context.evm.env.tx, gas_limit).unwrap(), + 0..0, + ), + TransactTo::Create(_) => context.evm.make_create_frame( + SPEC::SPEC_ID, + &CreateInputs::new(&context.evm.env.tx, gas_limit).unwrap(), + ), + } +} + +/// Helper function called inside [`first_frame_return`] +#[inline] +pub fn frame_return_with_refund_flag( + env: &Env, + call_result: InstructionResult, + returned_gas: Gas, + refund_enabled: bool, +) -> Gas { + // Spend the gas limit. Gas is reimbursed when the tx returns successfully. + let mut gas = Gas::new(env.tx.gas_limit); + gas.record_cost(env.tx.gas_limit); + + match call_result { + return_ok!() => { + gas.erase_cost(returned_gas.remaining()); + gas.record_refund(returned_gas.refunded()); + } + return_revert!() => { + gas.erase_cost(returned_gas.remaining()); + } + _ => {} + } + // Calculate gas refund for transaction. + // If config is set to disable gas refund, it will return 0. + // 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) + if refund_enabled { + // EIP-3529: Reduction in refunds + gas.set_final_refund::() + }; + + gas +} + +/// Handle output of the transaction +#[inline] +pub fn first_frame_return( + env: &Env, + call_result: InstructionResult, + returned_gas: Gas, +) -> Gas { + frame_return_with_refund_flag::(env, call_result, returned_gas, true) +} + +/// Handle frame return. +#[inline] +pub fn frame_return( + context: &mut Context, + child_stack_frame: Box, + parent_stack_frame: Option<&mut Box>, + shared_memory: &mut SharedMemory, + result: InterpreterResult, +) -> Option { + match child_stack_frame.frame_data { + FrameData::Create { created_address } => { + let result = context.evm.create_return::( + result, + created_address, + child_stack_frame.checkpoint, + ); + let Some(parent_stack_frame) = parent_stack_frame else { + return Some(result); + }; + parent_stack_frame + .interpreter + .insert_create_output(result, Some(created_address)) + } + FrameData::Call { + return_memory_range, + } => { + let result = context + .evm + .call_return(result, child_stack_frame.checkpoint); + let Some(parent_stack_frame) = parent_stack_frame else { + return Some(result); + }; + + parent_stack_frame.interpreter.insert_call_output( + shared_memory, + result, + return_memory_range, + ) + } + } + None +} + +/// Handle frame sub call. +#[inline] +pub fn sub_call( + context: &mut Context, + inputs: Box, + curent_stack_frame: &mut CallStackFrame, + shared_memory: &mut SharedMemory, + return_memory_offset: Range, +) -> Option> { + match context + .evm + .make_call_frame(&inputs, return_memory_offset.clone()) + { + FrameOrResult::Frame(new_frame) => Some(new_frame), + FrameOrResult::Result(result) => { + curent_stack_frame.interpreter.insert_call_output( + shared_memory, + result, + return_memory_offset, + ); + None + } + } +} + +/// Handle frame sub create. +#[inline] +pub fn sub_create( + context: &mut Context, + curent_stack_frame: &mut CallStackFrame, + inputs: Box, +) -> Option> { + match context.evm.make_create_frame(SPEC::SPEC_ID, &inputs) { + FrameOrResult::Frame(new_frame) => Some(new_frame), + FrameOrResult::Result(result) => { + // insert result of the failed creation of create CallStackFrame. + curent_stack_frame + .interpreter + .insert_create_output(result, None); + None + } + } +} + +#[cfg(test)] +mod tests { + use revm_interpreter::primitives::CancunSpec; + + use super::*; + + #[test] + fn test_consume_gas() { + let mut env = Env::default(); + env.tx.gas_limit = 100; + + let gas = first_frame_return::(&env, InstructionResult::Stop, Gas::new(90)); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 0); + } + + // TODO + #[test] + fn test_consume_gas_with_refund() { + let mut env = Env::default(); + env.tx.gas_limit = 100; + + let mut return_gas = Gas::new(90); + return_gas.record_refund(30); + + let gas = first_frame_return::(&env, InstructionResult::Stop, return_gas); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 2); + + let gas = first_frame_return::(&env, InstructionResult::Revert, return_gas); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 0); + } + + #[test] + fn test_revert_gas() { + let mut env = Env::default(); + env.tx.gas_limit = 100; + + let gas = first_frame_return::(&env, InstructionResult::Revert, Gas::new(90)); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 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 new file mode 100644 index 0000000000..7f31290e27 --- /dev/null +++ b/crates/revm/src/handler/mainnet/post_execution.rs @@ -0,0 +1,117 @@ +use crate::{ + interpreter::{Gas, InstructionResult, SuccessOrHalt}, + primitives::{ + db::Database, EVMError, ExecutionResult, Output, ResultAndState, Spec, SpecId::LONDON, U256, + }, + Context, +}; + +/// Mainnet end handle does not change the output. +#[inline] +pub fn end( + _context: &mut Context, + evm_output: Result>, +) -> Result> { + evm_output +} + +/// Reward beneficiary with gas fee. +#[inline] +pub fn reward_beneficiary( + context: &mut Context, + gas: &Gas, +) -> Result<(), EVMError> { + 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(LONDON) { + effective_gas_price.saturating_sub(context.evm.env.block.basefee) + } else { + effective_gas_price + }; + + let (coinbase_account, _) = context + .evm + .journaled_state + .load_account(beneficiary, &mut context.evm.db) + .map_err(EVMError::Database)?; + + coinbase_account.mark_touch(); + coinbase_account.info.balance = coinbase_account + .info + .balance + .saturating_add(coinbase_gas_price * U256::from(gas.spend() - gas.refunded() as u64)); + + Ok(()) +} + +#[inline] +pub fn reimburse_caller( + context: &mut Context, + gas: &Gas, +) -> Result<(), EVMError> { + let caller = context.evm.env.tx.caller; + let effective_gas_price = context.evm.env.effective_gas_price(); + + // return balance of not spend gas. + let (caller_account, _) = context + .evm + .journaled_state + .load_account(caller, &mut context.evm.db) + .map_err(EVMError::Database)?; + + caller_account.info.balance = caller_account + .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, + call_result: InstructionResult, + output: Output, + gas: &Gas, +) -> Result> { + // used gas with refund calculated. + let gas_refunded = gas.refunded() as u64; + let final_gas_used = gas.spend() - gas_refunded; + + // reset journal and return present state. + let (state, logs) = context.evm.journaled_state.finalize(); + + let result = match call_result.into() { + 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: match output { + Output::Call(return_value) => return_value, + Output::Create(return_value, _) => return_value, + }, + }, + SuccessOrHalt::Halt(reason) => ExecutionResult::Halt { + reason, + gas_used: final_gas_used, + }, + SuccessOrHalt::FatalExternalError => { + return Err(EVMError::Database(context.evm.error.take().unwrap())); + } + // Only two internal return flags. + SuccessOrHalt::InternalContinue | SuccessOrHalt::InternalCallOrCreate => { + panic!("Internal return flags should remain internal {call_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 new file mode 100644 index 0000000000..84bd2952f8 --- /dev/null +++ b/crates/revm/src/handler/mainnet/pre_execution.rs @@ -0,0 +1,96 @@ +//! 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::{ + precompile::{PrecompileSpecId, Precompiles}, + primitives::{ + db::Database, + Account, EVMError, Env, Spec, + SpecId::{CANCUN, SHANGHAI}, + TransactTo, U256, + }, + Context, +}; + +/// Main precompile load +#[inline] +pub fn load_precompiles() -> Precompiles { + Precompiles::new(PrecompileSpecId::from_spec_id(SPEC::SPEC_ID)).clone() +} + +/// Main load handle +#[inline] +pub fn load( + context: &mut Context, +) -> Result<(), EVMError> { + // set journaling state flag. + context.evm.journaled_state.set_spec_id(SPEC::SPEC_ID); + + // the L1-cost fee is only computed for Optimism non-deposit transactions. + #[cfg(feature = "optimism")] + if context.evm.env.cfg.optimism && context.evm.env.tx.optimism.source_hash.is_none() { + let l1_block_info = crate::optimism::L1BlockInfo::try_fetch(&mut context.evm.db) + .map_err(EVMError::Database)?; + + // storage l1 block info for later use. + context.evm.l1_block_info = Some(l1_block_info); + } + + // load coinbase + // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm + if SPEC::enabled(SHANGHAI) { + context + .evm + .journaled_state + .initial_account_load(context.evm.env.block.coinbase, &[], &mut context.evm.db) + .map_err(EVMError::Database)?; + } + + context.evm.load_access_list()?; + Ok(()) +} + +/// Helper function that deducts the caller balance. +#[inline] +pub fn deduct_caller_inner(caller_account: &mut Account, env: &Env) { + // 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.gas_limit).saturating_mul(env.effective_gas_price()); + + // EIP-4844 + if SPEC::enabled(CANCUN) { + let data_fee = env.calc_data_fee().expect("already checked"); + 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 matches!(env.tx.transact_to, TransactTo::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, +) -> Result<(), EVMError> { + // load caller's account. + let (caller_account, _) = context + .evm + .journaled_state + .load_account(context.evm.env.tx.caller, &mut context.evm.db) + .map_err(EVMError::Database)?; + + // deduct gas cost from caller's account. + deduct_caller_inner::(caller_account, &context.evm.env); + + Ok(()) +} diff --git a/crates/revm/src/handler/mainnet/validation.rs b/crates/revm/src/handler/mainnet/validation.rs new file mode 100644 index 0000000000..aef1d032c5 --- /dev/null +++ b/crates/revm/src/handler/mainnet/validation.rs @@ -0,0 +1,52 @@ +use revm_interpreter::gas; + +use crate::{ + primitives::{db::Database, EVMError, Env, InvalidTransaction, Spec}, + Context, +}; + +/// Validate environment for the mainnet. +pub fn validate_env(env: &Env) -> Result<(), EVMError> { + // Important: validate block before tx. + env.validate_block_env::()?; + env.validate_tx::()?; + Ok(()) +} + +/// Validates transaction against the state. +pub fn validate_tx_against_state( + context: &mut Context, +) -> Result<(), EVMError> { + // load acc + let tx_caller = context.evm.env.tx.caller; + let (caller_account, _) = context + .evm + .journaled_state + .load_account(tx_caller, &mut context.evm.db) + .map_err(EVMError::Database)?; + + context + .evm + .env + .validate_tx_against_state::(caller_account) + .map_err(EVMError::Transaction)?; + + Ok(()) +} + +/// Validate initial transaction gas. +pub fn validate_initial_tx_gas( + env: &Env, +) -> Result> { + let input = &env.tx.data; + let is_create = env.tx.transact_to.is_create(); + let access_list = &env.tx.access_list; + + let initial_gas_spend = gas::validate_initial_tx_gas::(input, is_create, access_list); + + // Additional check to see if limit is big enough to cover initial gas. + if initial_gas_spend > env.tx.gas_limit { + return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into()); + } + Ok(initial_gas_spend) +} diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs new file mode 100644 index 0000000000..2a5e9df89e --- /dev/null +++ b/crates/revm/src/handler/register.rs @@ -0,0 +1,31 @@ +use crate::{db::Database, handler::Handler, interpreter::opcode::InstructionTables, Evm}; +use alloc::boxed::Box; + +/// EVM Handler +pub type EvmHandler<'a, EXT, DB> = Handler<'a, Evm<'a, EXT, DB>, EXT, DB>; + +/// EVM Instruction Tables +pub type EvmInstructionTables<'a, EXT, DB> = InstructionTables<'a, Evm<'a, EXT, DB>>; + +// Handle register +pub type HandleRegister<'a, EXT, DB> = fn(&mut EvmHandler<'a, EXT, DB>); + +// Boxed handle register +pub type HandleRegisterBox<'a, EXT, DB> = Box)>; + +pub enum HandleRegisters<'a, EXT, DB: Database> { + /// Plain function register + Plain(HandleRegister<'a, EXT, DB>), + /// Boxed function register. + Box(HandleRegisterBox<'a, EXT, DB>), +} + +impl<'a, EXT, DB: Database> HandleRegisters<'a, EXT, DB> { + /// Call register function to modify EvmHandler. + pub fn register(&self, handler: &mut EvmHandler<'a, EXT, DB>) { + match self { + HandleRegisters::Plain(f) => f(handler), + HandleRegisters::Box(f) => f(handler), + } + } +} diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index 0f9435fa85..60ce286ce6 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -2,7 +2,7 @@ use core::ops::Range; use crate::{ interpreter::{CallInputs, CreateInputs, Interpreter}, - primitives::{db::Database, Address, Bytes, B256, U256}, + primitives::{db::Database, Address, Log, U256}, EvmContext, }; use auto_impl::auto_impl; @@ -12,10 +12,12 @@ mod customprinter; #[cfg(all(feature = "std", feature = "serde"))] mod eip3155; mod gas; -mod instruction; +mod handler_register; mod noop; -pub use instruction::inspector_instruction; +// Exports. + +pub use handler_register::{inspector_handle_register, inspector_instruction, GetInspector}; use revm_interpreter::InterpreterResult; /// [Inspector] implementations. pub mod inspectors { @@ -35,7 +37,7 @@ pub trait Inspector { /// If `interp.instruction_result` is set to anything other than [crate::interpreter::InstructionResult::Continue] then the execution of the interpreter /// is skipped. #[inline] - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let _ = interp; let _ = context; } @@ -49,43 +51,35 @@ pub trait Inspector { /// /// To get the current opcode, use `interp.current_opcode()`. #[inline] - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let _ = interp; let _ = context; } - /// Called when a log is emitted. - #[inline] - fn log( - &mut self, - context: &mut EvmContext<'_, DB>, - address: &Address, - topics: &[B256], - data: &Bytes, - ) { - let _ = context; - let _ = address; - let _ = topics; - let _ = data; - } - /// Called after `step` when the instruction has been executed. /// /// Setting `interp.instruction_result` to anything other than [crate::interpreter::InstructionResult::Continue] alters the execution /// of the interpreter. #[inline] - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let _ = interp; let _ = context; } + /// Called when a log is emitted. + #[inline] + fn log(&mut self, context: &mut EvmContext, log: &Log) { + let _ = context; + let _ = log; + } + /// Called whenever a call to a contract is about to start. /// /// InstructionResulting anything other than [crate::interpreter::InstructionResult::Continue] overrides the result of the call. #[inline] fn call( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, inputs: &mut CallInputs, ) -> Option<(InterpreterResult, Range)> { let _ = context; @@ -100,7 +94,7 @@ pub trait Inspector { #[inline] fn call_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, ) -> InterpreterResult { let _ = context; @@ -113,7 +107,7 @@ pub trait Inspector { #[inline] fn create( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, inputs: &mut CreateInputs, ) -> Option<(InterpreterResult, Option
)> { let _ = context; @@ -128,7 +122,7 @@ pub trait Inspector { #[inline] fn create_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs index 9ea86d5f9b..fc65544b56 100644 --- a/crates/revm/src/inspector/customprinter.rs +++ b/crates/revm/src/inspector/customprinter.rs @@ -7,7 +7,7 @@ use crate::{ inspectors::GasInspector, interpreter::{opcode, CallInputs, CreateInputs, Interpreter, InterpreterResult}, primitives::{Address, U256}, - Database, EvmContext, Inspector, + Database, EvmContext, GetInspector, Inspector, }; /// Custom print [Inspector], it has step level information of execution. @@ -18,14 +18,20 @@ pub struct CustomPrintTracer { gas_inspector: GasInspector, } +impl<'a, DB: Database> GetInspector<'a, DB> for CustomPrintTracer { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } +} + impl Inspector for CustomPrintTracer { - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + 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<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let opcode = interp.current_opcode(); let opcode_str = opcode::OPCODE_JUMPMAP[opcode as usize]; @@ -50,13 +56,13 @@ impl Inspector for CustomPrintTracer { self.gas_inspector.step(interp, context); } - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + 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<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, ) -> InterpreterResult { self.gas_inspector.call_end(context, result) @@ -64,7 +70,7 @@ impl Inspector for CustomPrintTracer { fn create_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { @@ -73,7 +79,7 @@ impl Inspector for CustomPrintTracer { fn call( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, inputs: &mut CallInputs, ) -> Option<(InterpreterResult, Range)> { println!( @@ -89,7 +95,7 @@ impl Inspector for CustomPrintTracer { fn create( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, inputs: &mut CreateInputs, ) -> Option<(InterpreterResult, Option
)> { println!( @@ -109,31 +115,42 @@ impl Inspector for CustomPrintTracer { #[cfg(test)] mod test { + use crate::{ + inspector_handle_register, + inspectors::CustomPrintTracer, + primitives::{address, bytes, SpecId}, + Evm, InMemoryDB, + }; #[test] - #[cfg(not(feature = "optimism"))] fn gas_calculation_underflow() { - use crate::primitives::{address, bytes}; + let callee = address!("5fdcca53617f4d2b9134b29090c87d01058e27e9"); // https://github.com/bluealloy/revm/issues/277 // checks this use case - let mut evm = crate::new(); - let mut database = crate::InMemoryDB::default(); - let code = bytes!("5b597fb075978b6c412c64d169d56d839a8fe01b3f4607ed603b2c78917ce8be1430fe6101e8527ffe64706ecad72a2f5c97a95e006e279dc57081902029ce96af7edae5de116fec610208527f9fc1ef09d4dd80683858ae3ea18869fe789ddc365d8d9d800e26c9872bac5e5b6102285260276102485360d461024953601661024a53600e61024b53607d61024c53600961024d53600b61024e5360b761024f5360596102505360796102515360a061025253607261025353603a6102545360fb61025553601261025653602861025753600761025853606f61025953601761025a53606161025b53606061025c5360a661025d53602b61025e53608961025f53607a61026053606461026153608c6102625360806102635360d56102645360826102655360ae61026653607f6101e8610146610220677a814b184591c555735fdcca53617f4d2b9134b29090c87d01058e27e962047654f259595947443b1b816b65cdb6277f4b59c10a36f4e7b8658f5a5e6f5561"); - - let acc_info = crate::primitives::AccountInfo { - balance: "0x100c5d668240db8e00".parse().unwrap(), - code_hash: crate::primitives::keccak256(&code), - code: Some(crate::primitives::Bytecode::new_raw(code.clone())), - nonce: 1, - }; - let callee = address!("5fdcca53617f4d2b9134b29090c87d01058e27e9"); - database.insert_account_info(callee, acc_info); - evm.database(database); - evm.env.tx.caller = address!("5fdcca53617f4d2b9134b29090c87d01058e27e0"); - evm.env.tx.transact_to = crate::primitives::TransactTo::Call(callee); - evm.env.tx.data = crate::primitives::Bytes::new(); - evm.env.tx.value = crate::primitives::U256::ZERO; - let _ = evm.inspect_commit(super::CustomPrintTracer::default()); + let mut evm = Evm::builder() + .with_db(InMemoryDB::default()) + .modify_db(|db| { + let code = bytes!("5b597fb075978b6c412c64d169d56d839a8fe01b3f4607ed603b2c78917ce8be1430fe6101e8527ffe64706ecad72a2f5c97a95e006e279dc57081902029ce96af7edae5de116fec610208527f9fc1ef09d4dd80683858ae3ea18869fe789ddc365d8d9d800e26c9872bac5e5b6102285260276102485360d461024953601661024a53600e61024b53607d61024c53600961024d53600b61024e5360b761024f5360596102505360796102515360a061025253607261025353603a6102545360fb61025553601261025653602861025753600761025853606f61025953601761025a53606161025b53606061025c5360a661025d53602b61025e53608961025f53607a61026053606461026153608c6102625360806102635360d56102645360826102655360ae61026653607f6101e8610146610220677a814b184591c555735fdcca53617f4d2b9134b29090c87d01058e27e962047654f259595947443b1b816b65cdb6277f4b59c10a36f4e7b8658f5a5e6f5561"); + let info = crate::primitives::AccountInfo { + balance: "0x100c5d668240db8e00".parse().unwrap(), + code_hash: crate::primitives::keccak256(&code), + code: Some(crate::primitives::Bytecode::new_raw(code.clone())), + nonce: 1, + }; + db.insert_account_info(callee, info); + }) + .modify_tx_env(|tx| { + tx.caller = address!("5fdcca53617f4d2b9134b29090c87d01058e27e0"); + tx.transact_to = crate::primitives::TransactTo::Call(callee); + tx.data = crate::primitives::Bytes::new(); + tx.value = crate::primitives::U256::ZERO; + }) + .with_external_context(CustomPrintTracer::default()) + .spec_id(SpecId::BERLIN) + .append_handler_register(inspector_handle_register) + .build(); + + evm.transact().expect("Transaction to work"); } } diff --git a/crates/revm/src/inspector/eip3155.rs b/crates/revm/src/inspector/eip3155.rs index a10dbf265a..22269082a3 100644 --- a/crates/revm/src/inspector/eip3155.rs +++ b/crates/revm/src/inspector/eip3155.rs @@ -2,7 +2,7 @@ use crate::{ inspectors::GasInspector, interpreter::{opcode, CallInputs, CreateInputs, Interpreter, InterpreterResult}, primitives::{db::Database, hex, Address, U256}, - EvmContext, Inspector, + EvmContext, GetInspector, Inspector, }; use core::ops::Range; use serde_json::json; @@ -26,6 +26,19 @@ pub struct TracerEip3155 { skip: bool, } +impl TracerEip3155 { + /// Sets the writer to use for the output. + pub fn set_writer(&mut self, writer: Box) { + self.output = writer; + } +} + +impl<'a, DB: Database> GetInspector<'a, DB> for TracerEip3155 { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } +} + impl TracerEip3155 { pub fn new(output: Box, trace_mem: bool, trace_return_data: bool) -> Self { Self { @@ -44,13 +57,13 @@ impl TracerEip3155 { } impl Inspector for TracerEip3155 { - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + 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<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.step(interp, context); self.stack = interp.stack.data().clone(); self.pc = interp.program_counter(); @@ -59,7 +72,7 @@ impl Inspector for TracerEip3155 { self.gas = interp.gas.remaining(); } - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.step_end(interp, context); if self.skip { self.skip = false; @@ -74,7 +87,7 @@ impl Inspector for TracerEip3155 { fn call( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, _inputs: &mut CallInputs, ) -> Option<(InterpreterResult, Range)> { None @@ -82,7 +95,7 @@ impl Inspector for TracerEip3155 { fn call_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, ) -> InterpreterResult { let result = self.gas_inspector.call_end(context, result); @@ -103,7 +116,7 @@ impl Inspector for TracerEip3155 { fn create( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, _inputs: &mut CreateInputs, ) -> Option<(InterpreterResult, Option
)> { None @@ -111,7 +124,7 @@ impl Inspector for TracerEip3155 { fn create_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 3c5462265e..f1266177e5 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -28,7 +28,7 @@ impl Inspector for GasInspector { fn initialize_interp( &mut self, interp: &mut crate::interpreter::Interpreter, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, ) { self.gas_remaining = interp.gas.limit(); } @@ -36,7 +36,7 @@ impl Inspector for GasInspector { fn step_end( &mut self, interp: &mut crate::interpreter::Interpreter, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, ) { let last_gas = core::mem::replace(&mut self.gas_remaining, interp.gas.remaining()); self.last_gas_cost = last_gas.saturating_sub(self.last_gas_cost); @@ -44,7 +44,7 @@ impl Inspector for GasInspector { fn call_end( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, mut result: InterpreterResult, ) -> InterpreterResult { if result.result.is_error() { @@ -56,7 +56,7 @@ impl Inspector for GasInspector { fn create_end( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { @@ -66,14 +66,14 @@ impl Inspector for GasInspector { #[cfg(test)] mod tests { - use core::ops::Range; - use crate::{ + inspector::GetInspector, inspectors::GasInspector, interpreter::{CallInputs, CreateInputs, Interpreter, InterpreterResult}, - primitives::{Address, Bytes, B256}, + primitives::{Address, Log}, Database, EvmContext, Inspector, }; + use core::ops::Range; #[derive(Default, Debug)] struct StackInspector { @@ -82,31 +82,27 @@ mod tests { gas_remaining_steps: Vec<(usize, u64)>, } + impl GetInspector<'_, DB> for StackInspector { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } + } + impl Inspector for StackInspector { - fn initialize_interp( - &mut self, - interp: &mut Interpreter, - context: &mut EvmContext<'_, DB>, - ) { + 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<'_, DB>) { + 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, - context: &mut EvmContext<'_, DB>, - address: &Address, - topics: &[B256], - data: &Bytes, - ) { - self.gas_inspector.log(context, address, topics, data); + fn log(&mut self, context: &mut EvmContext, log: &Log) { + self.gas_inspector.log(context, log); } - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + 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())); @@ -114,7 +110,7 @@ mod tests { fn call( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, call: &mut CallInputs, ) -> Option<(InterpreterResult, Range)> { self.gas_inspector.call(context, call) @@ -122,7 +118,7 @@ mod tests { fn call_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, ) -> InterpreterResult { self.gas_inspector.call_end(context, result) @@ -130,7 +126,7 @@ mod tests { fn create( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, call: &mut CreateInputs, ) -> Option<(InterpreterResult, Option
)> { self.gas_inspector.create(context, call); @@ -139,7 +135,7 @@ mod tests { fn create_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { @@ -148,12 +144,13 @@ mod tests { } #[test] - #[cfg(not(feature = "optimism"))] fn test_gas_inspector() { use crate::{ db::BenchmarkDB, + inspector::inspector_handle_register, interpreter::opcode, primitives::{address, Bytecode, Bytes, TransactTo}, + Evm, }; let contract_data: Bytes = Bytes::from(vec![ @@ -173,15 +170,23 @@ mod tests { ]); let bytecode = Bytecode::new_raw(contract_data); - let mut evm = crate::new(); - evm.database(BenchmarkDB::new_bytecode(bytecode.clone())); - evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); - evm.env.tx.transact_to = - TransactTo::Call(address!("0000000000000000000000000000000000000000")); - evm.env.tx.gas_limit = 21100; - - let mut inspector = StackInspector::default(); - evm.inspect(&mut inspector).unwrap(); + let mut evm: Evm<'_, StackInspector, BenchmarkDB> = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) + .with_external_context(StackInspector::default()) + .modify_tx_env(|tx| { + tx.clear(); + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = + TransactTo::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![ diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs new file mode 100644 index 0000000000..d46d52a8fe --- /dev/null +++ b/crates/revm/src/inspector/handler_register.rs @@ -0,0 +1,436 @@ +use crate::{ + db::Database, + handler::register::{EvmHandler, EvmInstructionTables}, + interpreter::{ + opcode, opcode::BoxedInstruction, CallInputs, InstructionResult, Interpreter, + InterpreterResult, + }, + primitives::TransactTo, + CallStackFrame, Evm, FrameData, FrameOrResult, Inspector, JournalEntry, +}; +use alloc::{boxed::Box, sync::Arc, vec::Vec}; +use revm_interpreter::CreateInputs; + +pub trait GetInspector<'a, DB: Database> { + fn get_inspector(&mut self) -> &mut dyn Inspector; +} + +/// Register Inspector handles that interact with Inspector instance. +/// +/// +/// # Note +/// +/// Handles that are overwritten: +/// * SubCreate +/// * SubCall +/// * CreateFirstFrame +/// +/// +/// 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. +/// +/// `frame_return` is also wrapped so that Inspector could call `call_end` or `create_end`. +/// +/// `create_first_frame` is also wrapped so that Inspector could call `call` and `crate` on it. +/// While return for first frame is handled by `frame_return`. +pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( + handler: &mut EvmHandler<'a, EXT, DB>, +) { + let spec_id = handler.spec_id; + // Every instruction inside flat table that is going to be wrapped by inspector calls. + let table = handler + .instruction_table + .take() + .expect("Handler must have instruction table"); + let mut table = match table { + EvmInstructionTables::Plain(table) => table + .into_iter() + .map(|i| inspector_instruction(i)) + .collect::>(), + EvmInstructionTables::Boxed(table) => table + .into_iter() + .map(|i| inspector_instruction(i)) + .collect::>(), + }; + + // Register inspector Log instruction. + let mut inspect_log = |index: u8| { + if let Some(i) = table.get_mut(index as usize) { + let old = core::mem::replace(i, Box::new(|_, _| ())); + *i = Box::new( + move |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { + let old_log_len = host.context.evm.journaled_state.logs.len(); + old(interpreter, host); + // check if log was added. It is possible that revert happened + // cause of gas or stack underflow. + if host.context.evm.journaled_state.logs.len() == old_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 + .context + .evm + .journaled_state + .logs + .last() + .unwrap() + .clone(); + // call Inspector + host.context + .external + .get_inspector() + .log(&mut host.context.evm, &last_log); + } + }, + ) + } + }; + + inspect_log(opcode::LOG0); + inspect_log(opcode::LOG1); + inspect_log(opcode::LOG2); + inspect_log(opcode::LOG3); + inspect_log(opcode::LOG4); + + // wrap first frame create and main frame return. + handler.execution_loop.create_first_frame = + Arc::new(move |context, gas_limit| -> FrameOrResult { + // call inner handling of call/create + let mut first_frame = match context.evm.env.tx.transact_to { + TransactTo::Call(_) => { + let mut call_inputs = CallInputs::new(&context.evm.env.tx, gas_limit).unwrap(); + // call inspector and return of inspector returns result. + if let Some(output) = context + .external + .get_inspector() + .call(&mut context.evm, &mut call_inputs) + { + return FrameOrResult::Result(output.0); + } + // first call frame does not have return range. + context.evm.make_call_frame(&call_inputs, 0..0) + } + TransactTo::Create(_) => { + let mut create_inputs = + CreateInputs::new(&context.evm.env.tx, gas_limit).unwrap(); + if let Some(output) = context + .external + .get_inspector() + .create(&mut context.evm, &mut create_inputs) + { + return FrameOrResult::Result(output.0); + }; + context.evm.make_create_frame(spec_id, &create_inputs) + } + }; + + // call initialize interpreter from inspector. + if let FrameOrResult::Frame(ref mut frame) = first_frame { + context + .external + .get_inspector() + .initialize_interp(&mut frame.interpreter, &mut context.evm); + } + + first_frame + }); + + // register selfdestruct function. + if let Some(i) = table.get_mut(opcode::SELFDESTRUCT as usize) { + let old = core::mem::replace(i, Box::new(|_, _| ())); + *i = Box::new( + move |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { + // execute selfdestruct + old(interpreter, host); + // check if selfdestruct was successful and if journal entry is made. + if let Some(JournalEntry::AccountDestroyed { + address, + target, + had_balance, + .. + }) = host + .context + .evm + .journaled_state + .journal + .last() + .unwrap() + .last() + { + host.context.external.get_inspector().selfdestruct( + *address, + *target, + *had_balance, + ); + } + }, + ) + } + + // cast vector to array. + handler.instruction_table = Some(EvmInstructionTables::Boxed( + table.try_into().unwrap_or_else(|_| unreachable!()), + )); + + // handle sub create + handler.execution_loop.sub_create = Arc::new( + move |context, frame, mut inputs| -> Option> { + let inspector = context.external.get_inspector(); + if let Some((result, address)) = inspector.create(&mut context.evm, &mut inputs) { + frame.interpreter.insert_create_output(result, address); + return None; + } + + match context.evm.make_create_frame(spec_id, &inputs) { + FrameOrResult::Frame(mut new_frame) => { + inspector.initialize_interp(&mut new_frame.interpreter, &mut context.evm); + Some(new_frame) + } + FrameOrResult::Result(result) => { + let (result, address) = + inspector.create_end(&mut context.evm, result, frame.created_address()); + // insert result of the failed creation of create CallStackFrame. + frame.interpreter.insert_create_output(result, address); + None + } + } + }, + ); + + // handle sub call + handler.execution_loop.sub_call = Arc::new( + move |context, mut inputs, frame, memory, return_memory_offset| -> Option> { + // inspector handle + let inspector = context.external.get_inspector(); + if let Some((result, range)) = inspector.call(&mut context.evm, &mut inputs) { + frame.interpreter.insert_call_output(memory, result, range); + return None; + } + match context + .evm + .make_call_frame(&inputs, return_memory_offset.clone()) + { + FrameOrResult::Frame(mut new_frame) => { + inspector.initialize_interp(&mut new_frame.interpreter, &mut context.evm); + Some(new_frame) + } + FrameOrResult::Result(result) => { + // inspector handle + let result = inspector.call_end(&mut context.evm, result); + frame + .interpreter + .insert_call_output(memory, result, return_memory_offset); + None + } + } + }, + ); + + // return frame handle + let old_handle = handler.execution_loop.frame_return.clone(); + handler.execution_loop.frame_return = Arc::new( + move |context, mut child, parent, memory, mut result| -> Option { + let inspector = &mut context.external.get_inspector(); + result = match &mut child.frame_data { + FrameData::Create { created_address } => { + let (result, address) = + inspector.create_end(&mut context.evm, result, Some(*created_address)); + if let Some(address) = address { + *created_address = address; + } + result + } + FrameData::Call { .. } => inspector.call_end(&mut context.evm, result), + }; + old_handle(context, child, parent, memory, result) + }, + ); +} + +/// Outer closure that calls Inspector for every instruction. +pub fn inspector_instruction< + 'a, + INSP: GetInspector<'a, DB>, + DB: Database, + Instruction: Fn(&mut Interpreter, &mut Evm<'a, INSP, DB>) + 'a, +>( + instruction: Instruction, +) -> BoxedInstruction<'a, Evm<'a, INSP, DB>> { + Box::new( + move |interpreter: &mut Interpreter, host: &mut Evm<'a, INSP, DB>| { + // 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) }; + + host.context + .external + .get_inspector() + .step(interpreter, &mut host.context.evm); + if interpreter.instruction_result != InstructionResult::Continue { + return; + } + + // return PC to old value + interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.add(1) }; + + // execute instruction. + instruction(interpreter, host); + + host.context + .external + .get_inspector() + .step_end(interpreter, &mut host.context.evm); + }, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + db::EmptyDB, + inspector::GetInspector, + inspectors::NoOpInspector, + interpreter::{opcode::*, CallInputs, CreateInputs, Interpreter, InterpreterResult}, + primitives::{Address, BerlinSpec}, + Database, Evm, EvmContext, Inspector, + }; + use core::ops::Range; + + #[test] + fn test_make_boxed_instruction_table() { + // test that this pattern builds. + let inst: InstructionTable> = + make_instruction_table::, BerlinSpec>(); + let _test: BoxedInstructionTable<'_, Evm<'_, _, _>> = + make_boxed_instruction_table::<'_, Evm<'_, NoOpInspector, EmptyDB>, BerlinSpec, _>( + inst, + inspector_instruction, + ); + } + + #[derive(Default, Debug)] + struct StackInspector { + initialize_interp_called: bool, + step: u32, + step_end: u32, + call: bool, + call_end: bool, + } + + impl GetInspector<'_, DB> for StackInspector { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } + } + + 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<(InterpreterResult, Range)> { + if self.call { + unreachable!("call should not be called twice") + } + self.call = true; + None + } + + fn call_end( + &mut self, + _context: &mut EvmContext, + result: InterpreterResult, + ) -> InterpreterResult { + if self.call_end { + unreachable!("call_end should not be called twice") + } + self.call_end = true; + result + } + + fn create( + &mut self, + _context: &mut EvmContext, + _call: &mut CreateInputs, + ) -> Option<(InterpreterResult, Option
)> { + None + } + + fn create_end( + &mut self, + _context: &mut EvmContext, + result: InterpreterResult, + address: Option
, + ) -> (InterpreterResult, Option
) { + (result, address) + } + } + + #[test] + fn test_gas_inspector() { + use crate::{ + db::BenchmarkDB, + inspector::inspector_handle_register, + interpreter::opcode, + primitives::{address, Bytecode, Bytes, TransactTo}, + Evm, + }; + + 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<'_, StackInspector, BenchmarkDB> = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) + .with_external_context(StackInspector::default()) + .modify_tx_env(|tx| { + tx.clear(); + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = + TransactTo::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); + } +} diff --git a/crates/revm/src/inspector/instruction.rs b/crates/revm/src/inspector/instruction.rs deleted file mode 100644 index b78be6ccea..0000000000 --- a/crates/revm/src/inspector/instruction.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::EVMImpl; -use alloc::boxed::Box; -use revm_interpreter::{ - opcode::{BoxedInstruction, Instruction}, - primitives::{db::Database, Spec}, - InstructionResult, Interpreter, -}; - -/// Outer closure that calls Inspector for every instruction. -pub fn inspector_instruction<'a, SPEC: Spec + 'static, DB: Database>( - instruction: Instruction>, -) -> BoxedInstruction<'a, EVMImpl<'a, SPEC, DB>> { - Box::new( - move |interpreter: &mut Interpreter, host: &mut EVMImpl<'a, SPEC, DB>| { - if let Some(inspector) = host.inspector.as_mut() { - // 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) }; - - inspector.step(interpreter, &mut host.context); - if interpreter.instruction_result != InstructionResult::Continue { - return; - } - - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.add(1) }; - } - - // execute instruction. - instruction(interpreter, host); - - // step ends - if let Some(inspector) = host.inspector.as_mut() { - inspector.step_end(interpreter, &mut host.context); - } - }, - ) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{db::EmptyDB, interpreter::opcode::*, primitives::BerlinSpec, EVMImpl}; - - #[test] - fn test_make_boxed_instruction_table() { - // test that this pattern builds. - let inst: InstructionTable> = - make_instruction_table::, BerlinSpec>(); - let _test: BoxedInstructionTable<'_, EVMImpl<'_, BerlinSpec, _>> = - make_boxed_instruction_table::<'_, EVMImpl<'_, BerlinSpec, EmptyDB>, BerlinSpec, _>( - inst, - inspector_instruction, - ); - } -} diff --git a/crates/revm/src/inspector/noop.rs b/crates/revm/src/inspector/noop.rs index 05d7292fc4..70d32b133b 100644 --- a/crates/revm/src/inspector/noop.rs +++ b/crates/revm/src/inspector/noop.rs @@ -1,7 +1,13 @@ +use super::GetInspector; use crate::{Database, Inspector}; - /// Dummy [Inspector], helpful as standalone replacement. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct NoOpInspector; impl Inspector for NoOpInspector {} + +impl<'a, DB: Database> GetInspector<'a, DB> for NoOpInspector { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } +} diff --git a/crates/revm/src/journaled_state.rs b/crates/revm/src/journaled_state.rs index 845ff985cb..8aa4d51fd5 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/revm/src/journaled_state.rs @@ -1,6 +1,6 @@ use crate::interpreter::{InstructionResult, SelfDestructResult}; use crate::primitives::{ - db::Database, hash_map::Entry, Account, Address, Bytecode, HashMap, Log, Spec, SpecId::*, + db::Database, hash_map::Entry, Account, Address, Bytecode, HashMap, HashSet, Log, SpecId::*, State, StorageSlot, TransientStorage, KECCAK_EMPTY, PRECOMPILE3, U256, }; use alloc::vec::Vec; @@ -26,26 +26,28 @@ pub struct JournaledState { /// Spec is needed for two things SpuriousDragon's `EIP-161 State clear`, /// and for Cancun's `EIP-6780: SELFDESTRUCT in same transaction` pub spec: SpecId, - /// Precompiles addresses are used to check if loaded address - /// should be considered cold or hot loaded. It is cloned from - /// EvmContext to be directly accessed from JournaledState. + /// Warm loaded addresses are used to check if loaded address + /// should be considered cold or warm loaded when the account + /// is first accessed. /// - /// Note that addresses are sorted. - pub precompile_addresses: Vec
, + /// Note that this not include newly loaded accounts, account and storage + /// is considered warm if it is found in the `State`. + pub warm_preloaded_addresses: HashSet
, } impl JournaledState { /// Create new JournaledState. /// - /// precompile_addresses is used to determine if address is precompile or not. + /// warm_preloaded_addresses is used to determine if address is considered warm loaded. + /// In ordinary case this is precompile or beneficiary. /// /// Note: This function will journal state after Spurious Dragon fork. /// And will not take into account if account is not existing or empty. /// /// # Note /// - /// Precompile addresses should be sorted. - pub fn new(spec: SpecId, precompile_addresses: Vec
) -> JournaledState { + /// + pub fn new(spec: SpecId, warm_preloaded_addresses: HashSet
) -> JournaledState { Self { state: HashMap::new(), transient_storage: TransientStorage::default(), @@ -53,7 +55,7 @@ impl JournaledState { journal: vec![vec![]], depth: 0, spec, - precompile_addresses, + warm_preloaded_addresses, } } @@ -63,6 +65,12 @@ impl JournaledState { &mut self.state } + /// Sets SpecId. + #[inline] + pub fn set_spec_id(&mut self, spec: SpecId) { + self.spec = spec; + } + /// Mark account as touched as only touched accounts will be added to state. /// This is especially important for state clear where touched empty accounts needs to /// be removed from state. @@ -207,11 +215,12 @@ impl JournaledState { /// Panics if the caller is not loaded inside of the EVM state. /// This is should have been done inside `create_inner`. #[inline] - pub fn create_account_checkpoint( + pub fn create_account_checkpoint( &mut self, caller: Address, address: Address, balance: U256, + spec_id: SpecId, ) -> Result { // Enter subroutine let checkpoint = self.checkpoint(); @@ -226,7 +235,7 @@ impl JournaledState { // Account is not precompile. if account.info.code_hash != KECCAK_EMPTY || account.info.nonce != 0 - || self.precompile_addresses.binary_search(&address).is_ok() + || self.warm_preloaded_addresses.contains(&address) { self.checkpoint_revert(checkpoint); return Err(InstructionResult::CreateCollision); @@ -260,7 +269,7 @@ impl JournaledState { account.info.balance = new_balance; // EIP-161: State trie clearing (invariant-preserving alternative) - if SPEC::enabled(SPURIOUS_DRAGON) { + if spec_id.is_enabled_in(SPURIOUS_DRAGON) { // nonce is going to be reset to zero in AccountCreated journal entry. account.info.nonce = 1; } @@ -539,7 +548,7 @@ impl JournaledState { .push(JournalEntry::AccountLoaded { address }); // precompiles are warm loaded so we need to take that into account - let is_cold = self.precompile_addresses.binary_search(&address).is_err(); + let is_cold = !self.warm_preloaded_addresses.contains(&address); (vac.insert(account), is_cold) } diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 3ffd746be6..120a47102a 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -6,56 +6,51 @@ #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(all(feature = "with-serde", not(feature = "serde")))] -compile_error!("`with-serde` feature has been renamed to `serde`."); - #[macro_use] extern crate alloc; +// Define modules. + +mod builder; +mod context; + #[cfg(any(test, feature = "test-utils"))] pub mod test_utils; pub mod db; mod evm; -mod evm_context; -mod evm_impl; mod frame; pub mod handler; mod inspector; mod journaled_state; - #[cfg(feature = "optimism")] pub mod optimism; -pub type DummyStateDB = InMemoryDB; +// Export items. + +pub use builder::EvmBuilder; +pub use context::{Context, EvmContext}; #[cfg(feature = "std")] pub use db::{ CacheState, DBBox, State, StateBuilder, StateDBBox, TransitionAccount, TransitionState, }; pub use db::{Database, DatabaseCommit, DatabaseRef, InMemoryDB}; -pub use evm::{new, EVM}; -pub use evm_context::EvmContext; -pub use evm_impl::{new_evm, EVMImpl, Transact, CALL_STACK_LIMIT}; -pub use frame::CallStackFrame; +pub use evm::{Evm, CALL_STACK_LIMIT}; +pub use frame::{CallStackFrame, FrameData, FrameOrResult}; +pub use handler::Handler; +pub use inspector::{ + inspector_handle_register, inspector_instruction, inspectors, GetInspector, Inspector, +}; pub use journaled_state::{JournalCheckpoint, JournalEntry, JournaledState}; +// export Optimism types, helpers, and constants +#[cfg(feature = "optimism")] +pub use optimism::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; -// reexport `revm_precompiles` -#[doc(inline)] -pub use revm_precompile as precompile; +// Reexport libraries -// reexport `revm_interpreter` #[doc(inline)] pub use revm_interpreter as interpreter; - -// reexport `revm_primitives` #[doc(inline)] pub use revm_interpreter::primitives; - -// reexport inspector implementations -pub use inspector::{inspector_instruction, inspectors, Inspector}; - -// export Optimism types, helpers, and constants -#[cfg(feature = "optimism")] -pub use optimism::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; - -pub use handler::Handler; +#[doc(inline)] +pub use revm_precompile as precompile; diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index a2d511b3b3..01276e4f2b 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -1,170 +1,9 @@ //! Optimism-specific constants, types, and helpers. -use crate::primitives::{address, db::Database, Address, Spec, SpecId, U256}; -use core::ops::Mul; +mod handler_register; +mod l1block; -const ZERO_BYTE_COST: u64 = 4; -const NON_ZERO_BYTE_COST: u64 = 16; - -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]); - -/// The address of L1 fee recipient. -pub const L1_FEE_RECIPIENT: Address = address!("420000000000000000000000000000000000001A"); - -/// The address of the base fee recipient. -pub const BASE_FEE_RECIPIENT: Address = address!("4200000000000000000000000000000000000019"); - -/// The address of the L1Block contract. -pub const L1_BLOCK_CONTRACT: Address = address!("4200000000000000000000000000000000000015"); - -/// L1 block info -/// -/// We can extract L1 epoch data from each L2 block, by looking at the `setL1BlockValues` -/// transaction data. This data is then used to calculate the L1 cost of a transaction. -/// -/// Here is the format of the `setL1BlockValues` transaction data: -/// -/// setL1BlockValues(uint64 _number, uint64 _timestamp, uint256 _basefee, bytes32 _hash, -/// uint64 _sequenceNumber, bytes32 _batcherHash, uint256 _l1FeeOverhead, uint256 _l1FeeScalar) -/// -/// For now, we only care about the fields necessary for L1 cost calculation. -#[derive(Clone, Debug)] -pub struct L1BlockInfo { - /// The base fee of the L1 origin block. - pub l1_base_fee: U256, - /// The current L1 fee overhead. - pub l1_fee_overhead: U256, - /// The current L1 fee scalar. - pub l1_fee_scalar: U256, -} - -impl L1BlockInfo { - /// Try to fetch the L1 block info from the database. - pub fn try_fetch(db: &mut DB) -> Result { - let l1_base_fee = db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?; - let l1_fee_overhead = db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?; - let l1_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?; - - Ok(L1BlockInfo { - l1_base_fee, - l1_fee_overhead, - l1_fee_scalar, - }) - } - - /// Calculate the data gas for posting the transaction on L1. Calldata costs 16 gas per non-zero - /// byte and 4 gas per zero byte. - /// - /// 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]) -> U256 { - let mut rollup_data_gas_cost = U256::from(input.iter().fold(0, |acc, byte| { - acc + if *byte == 0x00 { - ZERO_BYTE_COST - } else { - NON_ZERO_BYTE_COST - } - })); - - // Prior to regolith, an extra 68 non zero bytes were included in the rollup data costs. - if !SPEC::enabled(SpecId::REGOLITH) { - rollup_data_gas_cost += U256::from(NON_ZERO_BYTE_COST).mul(U256::from(68)); - } - - rollup_data_gas_cost - } - - /// Calculate the gas cost of a transaction based on L1 block data posted on L2 - pub fn calculate_tx_l1_cost(&self, input: &[u8]) -> U256 { - // If the input is not a deposit transaction, the default value is zero. - if input.is_empty() || input.first() == Some(&0x7F) { - return U256::ZERO; - } - - let rollup_data_gas_cost = self.data_gas::(input); - rollup_data_gas_cost - .saturating_add(self.l1_fee_overhead) - .saturating_mul(self.l1_base_fee) - .saturating_mul(self.l1_fee_scalar) - / U256::from(1_000_000) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::primitives::{bytes, specification::*}; - - #[test] - fn test_data_gas_non_zero_bytes() { - let l1_block_info = L1BlockInfo { - l1_base_fee: U256::from(1_000_000), - l1_fee_overhead: U256::from(1_000_000), - l1_fee_scalar: U256::from(1_000_000), - }; - - // 0xFACADE = 6 nibbles = 3 bytes - // 0xFACADE = 1111 1010 . 1100 1010 . 1101 1110 - - // Pre-regolith (ie bedrock) has an extra 68 non-zero bytes - // 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); - 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); - assert_eq!(regolith_data_gas, U256::from(48)); - } - - #[test] - fn test_data_gas_zero_bytes() { - let l1_block_info = L1BlockInfo { - l1_base_fee: U256::from(1_000_000), - l1_fee_overhead: U256::from(1_000_000), - l1_fee_scalar: U256::from(1_000_000), - }; - - // 0xFA00CA00DE = 10 nibbles = 5 bytes - // 0xFA00CA00DE = 1111 1010 . 0000 0000 . 1100 1010 . 0000 0000 . 1101 1110 - - // Pre-regolith (ie bedrock) has an extra 68 non-zero bytes - // 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); - 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); - assert_eq!(regolith_data_gas, U256::from(56)); - } - - #[test] - fn test_calculate_tx_l1_cost() { - let l1_block_info = L1BlockInfo { - l1_base_fee: U256::from(1_000), - l1_fee_overhead: U256::from(1_000), - l1_fee_scalar: U256::from(1_000), - }; - - let input = bytes!("FACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost::(&input); - 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); - 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); - assert_eq!(gas_cost, U256::ZERO); - } -} +pub use handler_register::{ + deduct_caller, end, handle_call_return, optimism_handle_register, output, reward_beneficiary, +}; +pub use l1block::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; diff --git a/crates/revm/src/handler/optimism.rs b/crates/revm/src/optimism/handler_register.rs similarity index 51% rename from crates/revm/src/handler/optimism.rs rename to crates/revm/src/optimism/handler_register.rs index cbb248b3f7..e3ff387fda 100644 --- a/crates/revm/src/handler/optimism.rs +++ b/crates/revm/src/optimism/handler_register.rs @@ -1,17 +1,33 @@ //! Handler related to Optimism chain -use super::mainnet; use crate::{ + handler::{ + mainnet::{self, deduct_caller_inner}, + register::EvmHandler, + }, interpreter::{return_ok, return_revert, Gas, InstructionResult}, optimism, primitives::{ - db::Database, Account, EVMError, Env, ExecutionResult, HaltReason, HashMap, - InvalidTransaction, Output, ResultAndState, Spec, SpecId::REGOLITH, U256, + db::Database, spec_to_generic, Account, EVMError, Env, ExecutionResult, HaltReason, + HashMap, InvalidTransaction, Output, ResultAndState, Spec, SpecId, SpecId::REGOLITH, U256, }, - EvmContext, + Context, }; +use alloc::sync::Arc; use core::ops::Mul; +pub fn optimism_handle_register(handler: &mut EvmHandler<'_, EXT, DB>) { + spec_to_generic!(handler.spec_id, { + // Refund is calculated differently then mainnet. + handler.execution_loop.first_frame_return = Arc::new(handle_call_return::); + // we reimburse caller the same was as in mainnet. + 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::); + }); +} + /// Handle output of the transaction #[inline] pub fn handle_call_return( @@ -73,53 +89,97 @@ pub fn handle_call_return( } _ => {} } + // Prior to Regolith, deposit transactions did not receive gas refunds. + if !is_deposit && SPEC::enabled(REGOLITH) { + gas.set_final_refund::() + } + gas } +/// Deduct max balance from caller #[inline] -pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { - let is_deposit = env.cfg.optimism && env.tx.optimism.source_hash.is_some(); +pub fn deduct_caller( + context: &mut Context, +) -> Result<(), EVMError> { + // load caller's account. + let (caller_account, _) = context + .evm + .journaled_state + .load_account(context.evm.env.tx.caller, &mut context.evm.db) + .map_err(EVMError::Database)?; - // Prior to Regolith, deposit transactions did not receive gas refunds. - let is_gas_refund_disabled = env.cfg.optimism && is_deposit && !SPEC::enabled(REGOLITH); - if is_gas_refund_disabled { - 0 - } else { - mainnet::calculate_gas_refund::(env, gas) + // 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 let Some(mint) = context.evm.env.tx.optimism.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, &context.evm.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 context.evm.env.tx.optimism.source_hash.is_none() { + // get envelope + let Some(enveloped_tx) = context.evm.env.tx.optimism.enveloped_tx.clone() else { + panic!("[OPTIMISM] Failed to load enveloped transaction."); + }; + + let tx_l1_cost = context + .evm + .l1_block_info + .as_ref() + .expect("L1BlockInfo should be loaded") + .calculate_tx_l1_cost(&enveloped_tx, SPEC::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(), + }, + )); + } + 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 EvmContext<'_, DB>, +pub fn reward_beneficiary( + context: &mut Context, gas: &Gas, ) -> Result<(), EVMError> { - let is_deposit = context.env.cfg.optimism && context.env.tx.optimism.source_hash.is_some(); - let disable_coinbase_tip = context.env.cfg.optimism && is_deposit; + let is_deposit = + context.evm.env.cfg.optimism && context.evm.env.tx.optimism.source_hash.is_some(); + let disable_coinbase_tip = context.evm.env.cfg.optimism && is_deposit; // transfer fee to coinbase/beneficiary. if !disable_coinbase_tip { - mainnet::reward_beneficiary::(context, gas)?; + mainnet::reward_beneficiary::(context, gas)?; } - if context.env.cfg.optimism && !is_deposit { + if context.evm.env.cfg.optimism && !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 Some(l1_block_info) = context.l1_block_info.clone() else { + let Some(l1_block_info) = context.evm.l1_block_info.clone() else { panic!("[OPTIMISM] Failed to load L1 block information."); }; - let Some(enveloped_tx) = &context.env.tx.optimism.enveloped_tx else { + let Some(enveloped_tx) = &context.evm.env.tx.optimism.enveloped_tx else { panic!("[OPTIMISM] Failed to load enveloped transaction."); }; - let l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx); + let l1_cost = l1_block_info.calculate_tx_l1_cost(enveloped_tx, SPEC::SPEC_ID); // Send the L1 cost of the transaction to the L1 Fee Vault. let Ok((l1_fee_vault_account, _)) = context + .evm .journaled_state - .load_account(optimism::L1_FEE_RECIPIENT, context.db) + .load_account(optimism::L1_FEE_RECIPIENT, &mut context.evm.db) else { panic!("[OPTIMISM] Failed to load L1 Fee Vault account"); }; @@ -128,13 +188,15 @@ pub fn reward_beneficiary( // Send the base fee of the transaction to the Base Fee Vault. let Ok((base_fee_vault_account, _)) = context + .evm .journaled_state - .load_account(optimism::BASE_FEE_RECIPIENT, context.db) + .load_account(optimism::BASE_FEE_RECIPIENT, &mut context.evm.db) else { panic!("[OPTIMISM] Failed to load Base Fee Vault account"); }; base_fee_vault_account.mark_touch(); base_fee_vault_account.info.balance += context + .evm .env .block .basefee @@ -145,20 +207,20 @@ pub fn reward_beneficiary( /// Main return handle, returns the output of the transaction. #[inline] -pub fn main_return( - context: &mut EvmContext<'_, DB>, +pub fn output( + context: &mut Context, call_result: InstructionResult, output: Output, gas: &Gas, ) -> Result> { - let result = mainnet::main_return::(context, call_result, output, gas)?; + let result = mainnet::output::(context, call_result, output, gas)?; if result.result.is_halt() { - // Post-regolith, if the transaction is a deposit transaction and it haults, + // 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.env.tx.optimism.source_hash.is_some(); - let optimism_regolith = context.env.cfg.optimism && SPEC::enabled(REGOLITH); + let is_deposit = context.evm.env.tx.optimism.source_hash.is_some(); + let optimism_regolith = context.evm.env.cfg.optimism && SPEC::enabled(REGOLITH); if is_deposit && optimism_regolith { return Err(EVMError::Transaction( InvalidTransaction::HaltedDepositPostRegolith, @@ -170,14 +232,14 @@ pub fn main_return( /// 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_handle( - context: &mut EvmContext<'_, DB>, +pub fn end( + context: &mut Context, evm_output: Result>, ) -> Result> { evm_output.or_else(|err| { if matches!(err, EVMError::Transaction(_)) - && context.env().cfg.optimism - && context.env().tx.optimism.source_hash.is_some() + && context.evm.env().cfg.optimism + && context.evm.env().tx.optimism.source_hash.is_some() { // If the transaction is a deposit transaction and it failed // for any reason, the caller nonce must be bumped, and the @@ -185,13 +247,14 @@ pub fn end_handle( // also returned as a special Halt variant so that consumers can more // easily distinguish between a failed deposit and a failed // normal transaction. - let caller = context.env().tx.caller; + let caller = context.evm.env().tx.caller; // 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 .db .basic(caller) .unwrap_or_default() @@ -201,7 +264,7 @@ pub fn end_handle( acc.info.balance = acc .info .balance - .saturating_add(U256::from(context.env().tx.optimism.mint.unwrap_or(0))); + .saturating_add(U256::from(context.evm.env().tx.optimism.mint.unwrap_or(0))); acc.mark_touch(); acc }; @@ -212,13 +275,14 @@ pub fn end_handle( // of the transaction for non system transactions and 0 for system // transactions. let is_system_tx = context + .evm .env() .tx .optimism .is_system_transaction .unwrap_or(false); let gas_used = if SPEC::enabled(REGOLITH) || !is_system_tx { - context.env().tx.gas_limit + context.evm.env().tx.gas_limit } else { 0 }; @@ -238,9 +302,12 @@ pub fn end_handle( #[cfg(test)] mod tests { - use crate::primitives::{BedrockSpec, RegolithSpec, B256}; - use super::*; + use crate::{ + db::InMemoryDB, + primitives::{bytes, state::AccountInfo, Address, BedrockSpec, Env, RegolithSpec, B256}, + L1BlockInfo, + }; #[test] fn test_revert_gas() { @@ -315,4 +382,134 @@ mod tests { assert_eq!(gas.spend(), 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<(), InMemoryDB> = Context::new_with_db(db); + context.evm.l1_block_info = Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }); + // Enveloped needs to be some but it will deduce zero fee. + context.evm.env.tx.optimism.enveloped_tx = Some(bytes!("")); + // added mint value is 10. + context.evm.env.tx.optimism.mint = Some(10); + + deduct_caller::(&mut context).unwrap(); + + // Check the account balance is updated. + let (account, _) = context + .evm + .journaled_state + .load_account(caller, &mut context.evm.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<(), InMemoryDB> = Context::new_with_db(db); + context.evm.l1_block_info = Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }); + // l1block cost is 1048 fee. + context.evm.env.tx.optimism.enveloped_tx = Some(bytes!("FACADE")); + // added mint value is 10. + context.evm.env.tx.optimism.mint = Some(10); + // Putting source_hash to some makes it a deposit transaction. + // so enveloped_tx gas cost is ignored. + context.evm.env.tx.optimism.source_hash = Some(B256::ZERO); + + deduct_caller::(&mut context).unwrap(); + + // Check the account balance is updated. + let (account, _) = context + .evm + .journaled_state + .load_account(caller, &mut context.evm.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<(), InMemoryDB> = Context::new_with_db(db); + context.evm.l1_block_info = Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }); + // l1block cost is 1048 fee. + context.evm.env.tx.optimism.enveloped_tx = Some(bytes!("FACADE")); + deduct_caller::(&mut context).unwrap(); + + // Check the account balance is updated. + let (account, _) = context + .evm + .journaled_state + .load_account(caller, &mut context.evm.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<(), InMemoryDB> = Context::new_with_db(db); + context.evm.l1_block_info = Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }); + // l1block cost is 1048 fee. + context.evm.env.tx.optimism.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)), + }, + )) + ); + } } diff --git a/crates/revm/src/optimism/l1block.rs b/crates/revm/src/optimism/l1block.rs new file mode 100644 index 0000000000..85f8c7a620 --- /dev/null +++ b/crates/revm/src/optimism/l1block.rs @@ -0,0 +1,168 @@ +use crate::primitives::{address, db::Database, Address, SpecId, U256}; +use core::ops::Mul; + +const ZERO_BYTE_COST: u64 = 4; +const NON_ZERO_BYTE_COST: u64 = 16; + +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]); + +/// The address of L1 fee recipient. +pub const L1_FEE_RECIPIENT: Address = address!("420000000000000000000000000000000000001A"); + +/// The address of the base fee recipient. +pub const BASE_FEE_RECIPIENT: Address = address!("4200000000000000000000000000000000000019"); + +/// The address of the L1Block contract. +pub const L1_BLOCK_CONTRACT: Address = address!("4200000000000000000000000000000000000015"); + +/// L1 block info +/// +/// We can extract L1 epoch data from each L2 block, by looking at the `setL1BlockValues` +/// transaction data. This data is then used to calculate the L1 cost of a transaction. +/// +/// Here is the format of the `setL1BlockValues` transaction data: +/// +/// setL1BlockValues(uint64 _number, uint64 _timestamp, uint256 _basefee, bytes32 _hash, +/// uint64 _sequenceNumber, bytes32 _batcherHash, uint256 _l1FeeOverhead, uint256 _l1FeeScalar) +/// +/// For now, we only care about the fields necessary for L1 cost calculation. +#[derive(Clone, Debug)] +pub struct L1BlockInfo { + /// The base fee of the L1 origin block. + pub l1_base_fee: U256, + /// The current L1 fee overhead. + pub l1_fee_overhead: U256, + /// The current L1 fee scalar. + pub l1_fee_scalar: U256, +} + +impl L1BlockInfo { + /// Try to fetch the L1 block info from the database. + pub fn try_fetch(db: &mut DB) -> Result { + let l1_base_fee = db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?; + let l1_fee_overhead = db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?; + let l1_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?; + + Ok(L1BlockInfo { + l1_base_fee, + l1_fee_overhead, + l1_fee_scalar, + }) + } + + /// Calculate the data gas for posting the transaction on L1. Calldata costs 16 gas per non-zero + /// byte and 4 gas per zero byte. + /// + /// 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: SpecId) -> U256 { + let mut rollup_data_gas_cost = U256::from(input.iter().fold(0, |acc, byte| { + acc + if *byte == 0x00 { + ZERO_BYTE_COST + } else { + NON_ZERO_BYTE_COST + } + })); + + // Prior to regolith, an extra 68 non zero bytes were included in the rollup data costs. + if !spec_id.is_enabled_in(SpecId::REGOLITH) { + rollup_data_gas_cost += U256::from(NON_ZERO_BYTE_COST).mul(U256::from(68)); + } + + rollup_data_gas_cost + } + + /// Calculate the gas cost of a transaction based on L1 block data posted on L2 + pub fn calculate_tx_l1_cost(&self, input: &[u8], spec_id: SpecId) -> U256 { + // If the input is not a deposit transaction, the default value is zero. + if input.is_empty() || input.first() == Some(&0x7F) { + return U256::ZERO; + } + + let rollup_data_gas_cost = self.data_gas(input, spec_id); + rollup_data_gas_cost + .saturating_add(self.l1_fee_overhead) + .saturating_mul(self.l1_base_fee) + .saturating_mul(self.l1_fee_scalar) + / U256::from(1_000_000) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::primitives::bytes; + + #[test] + fn test_data_gas_non_zero_bytes() { + let l1_block_info = L1BlockInfo { + l1_base_fee: U256::from(1_000_000), + l1_fee_overhead: U256::from(1_000_000), + l1_fee_scalar: U256::from(1_000_000), + }; + + // 0xFACADE = 6 nibbles = 3 bytes + // 0xFACADE = 1111 1010 . 1100 1010 . 1101 1110 + + // Pre-regolith (ie bedrock) has an extra 68 non-zero bytes + // 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, SpecId::BEDROCK); + 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, SpecId::REGOLITH); + assert_eq!(regolith_data_gas, U256::from(48)); + } + + #[test] + fn test_data_gas_zero_bytes() { + let l1_block_info = L1BlockInfo { + l1_base_fee: U256::from(1_000_000), + l1_fee_overhead: U256::from(1_000_000), + l1_fee_scalar: U256::from(1_000_000), + }; + + // 0xFA00CA00DE = 10 nibbles = 5 bytes + // 0xFA00CA00DE = 1111 1010 . 0000 0000 . 1100 1010 . 0000 0000 . 1101 1110 + + // Pre-regolith (ie bedrock) has an extra 68 non-zero bytes + // 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, SpecId::BEDROCK); + 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, SpecId::REGOLITH); + assert_eq!(regolith_data_gas, U256::from(56)); + } + + #[test] + fn test_calculate_tx_l1_cost() { + let l1_block_info = L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }; + + let input = bytes!("FACADE"); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); + 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, SpecId::REGOLITH); + 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, SpecId::REGOLITH); + assert_eq!(gas_cost, U256::ZERO); + } +} diff --git a/crates/revm/src/test_utils.rs b/crates/revm/src/test_utils.rs index 46801c3dd6..98fa21378f 100644 --- a/crates/revm/src/test_utils.rs +++ b/crates/revm/src/test_utils.rs @@ -1,2 +1,2 @@ #[doc(hidden)] -pub use crate::evm_context::test_utils::*; +pub use crate::context::test_utils::*; diff --git a/documentation/src/SUMMARY.md b/documentation/src/SUMMARY.md index 9cc423b5b3..bfa087b036 100644 --- a/documentation/src/SUMMARY.md +++ b/documentation/src/SUMMARY.md @@ -3,9 +3,10 @@ - [Introduction](./introduction.md) - [Revm](./crates/revm.md) - [evm](./crates/revm/evm.md) - - [evm_impl](./crates/revm/evm_impl.md) - - [The Host Trait](./crates/revm/host_trait.md) + - [builder](./crates/revm/builder.md) + - [handler](./crates/revm/handler.md) - [inspector](./crates/revm/inspector.md) + - [state](./crates/revm/state.md) - [journaled_state](./crates/revm/journaled_state.md) - [Interpreter](./crates/interpreter.md) - [gas](./crates/interpreter/gas.md) diff --git a/documentation/src/crates/interpreter/host.md b/documentation/src/crates/interpreter/host.md index a3a7f0ab39..90752f3a7d 100644 --- a/documentation/src/crates/interpreter/host.md +++ b/documentation/src/crates/interpreter/host.md @@ -2,11 +2,10 @@ The `host.rs` module in this Rust EVM implementation defines a crucial trait `Host`. The `Host` trait outlines an interface for the interaction of the EVM interpreter with its environment (or "host"), encompassing essential operations such as account and storage access, creating logs, and invoking transactions. +The [`Evm`](./../revm/evm.md) struct implements this `Host` trait. ## Trait Methods -- `step` & `step_end`: These methods manage the execution of EVM opcodes. The `step` method is invoked before executing an opcode, while `step_end` is invoked after. These methods can modify the EVM state or halt execution based on certain conditions. - - `env`: This method provides access to the EVM environment, including information about the current block and transaction. - `load_account`: Retrieves information about a given Ethereum account. @@ -21,7 +20,5 @@ The `host.rs` module in this Rust EVM implementation defines a crucial trait `Ho - `selfdestruct`: Marks an Ethereum account to be self-destructed, transferring its funds to a target account. -- `create` & `call`: These methods handle the creation of new smart contracts and the invocation of smart contract functions, respectively. - The `Host` trait provides a standard interface that any host environment for the EVM must implement. This abstraction allows the EVM code to interact with the state of the Ethereum network in a generic way, thereby enhancing modularity and interoperability. Different implementations of the `Host` trait can be used to simulate different environments for testing or for connecting to different Ethereum-like networks. \ No newline at end of file diff --git a/documentation/src/crates/revm.md b/documentation/src/crates/revm.md index adee43a5ea..80b7d64a27 100644 --- a/documentation/src/crates/revm.md +++ b/documentation/src/crates/revm.md @@ -1,19 +1,16 @@ # Rust Ethereum Virtual Machine (revm) -The `crate` is focused on the implementation of Ethereum Virtual Machine (EVM) including database handling, state journaling, and an inspection system for observing and logging the execution of EVM. This crate pulls together everything described prior to deliver the rust evm. +The `crate` is focused on the implementation of Ethereum Virtual Machine (EVM) including call loop and host implementation, database handling, state journaling and powerful logic handlers that can be overwritten. This crate pulls Primitives, Interpreter and Precompiles together to deliver the rust evm. -Modules: +Starting point of the documentation should be a [`Evm`](./revm/evm.md) that is main structure of EVM. Then i would recomend reading about the `EvmBuilder` that is used to create the `Evm` and modify. After that you can read about the `Handler` that is used to modify the logic of the Evm and it will tie with how introspection of Evm can be done. And lastly you can read about the `Inspector` that is legacy interface for inspecting execution that is repurposed as one of example of handler registers. -- `db`: This module includes structures and functions for database interaction. -- `evm`: This module contains a Struct that takes Database and enabled transact to update state directly to database. Additionally it allows user to set all environment parameters. -- `evm_impl`: This module includes more specific implementations related to the EVM regarding state transitions. -- `inspector`: This module introduces the `Inspector` trait and its implementations for observing the EVM execution. +Modules: +- `evm`: This is main module that executed EVM calls. +- `builder`: This modules build the Evm, sets database, handlers and other parameters. Here is where we set handlers for specific fork or external state for inspection. +- `db`: This module includes structures and functions for database interaction. It is a glue between EVM and database. It transforms or aggregates the EVM changes. +- `inspector`: This module introduces the `Inspector` trait and its implementations for observing the EVM execution. This was main way to inspect EVM execution before the Builder and Handlers were introduced. It is still enabled through the Builder. - `journaled_state`: This module manages the state of the EVM and implements a journaling system to handle changes and reverts. -External Crates: - -- `alloc`: The alloc crate is used to provide the ability to allocate memory on the heap. It's a part of Rust's standard library that can be used in environments without a full host OS. - Re-exported Crates: - `revm_precompile`: This crate is re-exported, providing the precompiled contracts used in the EVM implementation. diff --git a/documentation/src/crates/revm/builder.md b/documentation/src/crates/revm/builder.md new file mode 100644 index 0000000000..dad889ba5b --- /dev/null +++ b/documentation/src/crates/revm/builder.md @@ -0,0 +1,103 @@ + +# Evm Builder + +It build or modifies the EVM and applies different handler, and allows setting external context and registering handler custom logic. + +`Evm` inside revm consist of the few parts `Context` and `Handler`. `Context` is additionally split between `EvmContext` that contains generic `Database` and `External` context that is generic without restrain. Read here for more information on [`Evm`](./evm.md) internals. + +Builder ties dependencies between generic `Database`, `External` context and `Spec` and allows handle registers to be added that implement logic on those generics. As they are interconected setting `Database` or `ExternalContext` wound reset handle registers, and builder stages are introduced to mitigate those misuses. + +Simple example of using `EvmBuilder`: + +```rust,ignore + use crate::evm::Evm; + + // build Evm with default values. + let mut evm = Evm::builder().build(); + let output = evm.transact(); +``` + +## Builder Stages + +There are two builder stages that are used to mitigate potential misuse of the builder: + +* `SetGenericStage`: Initial stage that allows setting the database and external context. +* `HandlerStage`: Allows setting the handler registers but is explicit about setting new generic type as it will void the handler registers. + +Functions from one stage are just a renamed function from other stage, it is made so that user is more aware of what underlying function does. Example of this, in `SettingDbStage` we have `with_db` function while in `HandlerStage` we have `reset_handler_with_db`, both of them set the database but the latter also resets the handler. There are multiple functions that are common to both stages as in `build`. + +There is naming convention for the functions that can be found in builder: +* In both stages we have: + * `build` is to create the Evm. + * `spec_id` is used to set the spec id, this is common to both stages and will create new mainnet handler and reapply all the handler registers. + * `modify_*` are used to modify the database, external context or Env. + * `clear_*` allows setting default values for Environment. + * `append_handler_register_*` are used to push handler registers. This will transition the builder to the `HandlerStage`. +* `SetGenericStage` have: + * `with_*` are found in `SetGenericStage` and are used to set the generics. +* `HandlerStage` have: + * `reset_handler_with_*` is used if we want to change some of the generic types this will reset the handler registers. This will transition the builder to the `SetGenericStage`. + +# Creating and modification of Evm + +Evm implements function that allows using of EvmBuilder without even knowing that it exist. Most obvious one is `Evm::builder()` that would create a new builder with default values. + +Additionally function that is very important is `evm.modify()` that allows modifying the Evm. It will return the builder and will allow user to modify the Evm. + +# Examples + +Example of using builder to create Evm with inspector: +```rust,ignore + use crate::{ + db::EmptyDB, Context, EvmContext, inspector::inspector_handle_register, inspectors::NoOpInspector, Evm, + }; + + // Create the evm. + let evm = Evm::builder() + .with_db(EmptyDB::default()) + .with_external_context(NoOpInspector) + // Register will modify Handler and call NoOpInspector. + .append_handler_register(inspector_handle_register) + // .with_db(..) would not compile as we already locked the builder generics, + // alternative fn is reset_handler_with_db(..) + .build(); + + // Execute the evm. + let output = evm.transact(); + + // Extract evm context. + let Context { + external, + evm: EvmContext { db, .. }, + } = evm.into_context(); +``` + +Example of changing spec id and Environment of already build evm. +```rust,ignore + use crate::{Evm,SpecId::BERLIN}; + + // Create default evm. + let evm = Evm::builder().build(); + + // Modify evm spec. + let evm = evm.modify().spec_id(BERLIN).build(); + + // Shortcut for above. + let mut evm = evm.modify_spec_id(BERLIN); + + // Execute the evm. + let output1 = evm.transact(); + + // Example of modifying the tx env. + let mut evm = evm.modify().modify_tx_env(|env| env.gas_price = 0.into()).build(); + + // Execute the evm with modified tx env. + let output2 = evm.transact(); +``` + +## Appending handler registers + +Handler registers are simple function that allow modifying the `Handler` logic by replacing +the handler functions. They are used to add custom logic to the evm execution but as they are free to modify the `Handler` in any form they want there can be conflicts if handlers that override the same function are added. + +Most common use case for adding new logic to `Handler` is `Inspector` that is used to inspect the execution of the evm. Example of this can be found in [`Inspector`](./inspector.md) documentation. \ No newline at end of file diff --git a/documentation/src/crates/revm/evm.md b/documentation/src/crates/revm/evm.md index b314ead3c1..ec6434747b 100644 --- a/documentation/src/crates/revm/evm.md +++ b/documentation/src/crates/revm/evm.md @@ -1,17 +1,36 @@ -# EVM Module Documentation +# EVM -This document provides the documentation for the `EVM` module. +`Evm` is the primary structure that implements the Ethereum Virtual Machine (EVM), a stack-based virtual machine that executes Ethereum smart contracts. -## The `EVM` +## What is inside -The primary struct in this module is `EVM`. The EVM struct is generic over a type DB. This means that when you use the EVM struct, you can specify the type that DB should represent. It adds flexibility to the struct, allowing it to store different types of databases or data structures in the db field depending on the use case. The `EVM` struct enables `transact` to update the state directly to the database. Additionally, it allows the user to set all environment parameters. +It is consisting of two main parts the `Context` and the `Handler`. `Context` represent the state that is needed for execution and `Handler` contains list of functions that act as a logic. -The parameters that can be set are divided between `Config`, `Block`, and `Transaction` (tx). For transacting on the EVM, you can call `transact_commit` that will automatically apply changes to the database. +`Context` is additionally split between `EvmContext` and `External` context. `EvmContext` is internal and contains `Database`, `Environment`, `JournaledState` and `Precompiles`. And `External` context is fully generic without any trait restrains and its purpose is to allow custom handlers to save state in runtime or allows hooks to be added (For example external contexts can be a Inspector), more on its usage can be seen in [`EvmBuilder`](./builder.md). -## Database Abstractions +`Evm` implements the [`Host`](./../interpreter/host.md) trait, which defines an interface for the interaction of the EVM Interpreter with its environment (or "host"), encompassing essential operations such as account and storage access, creating logs, and invoking sub calls and selfdestruct. -You can implement the traits Database, DatabaseRef or Database + DatabaseCommit depending on the desired handling of the struct. +Data structures of block and transaction can be found inside `Environment`. And more information on journaled state can be found in [`JournaledState`](../revm/journaled_state.md) documentation. -- `Database`: Has mutable `self` in its functions. It's useful if you want to modify your cache or update some statistics on `get` calls. This trait enables `preverify_transaction`, `transact_preverified`, `transact` and `inspect` functions. -- `DatabaseRef`: Takes a reference on the object, this is useful if you only have a reference on the state and don't want to update anything on it. It enables `previerify_transaction`, `transact_preverified_ref`, `transact_ref` and `inspect_ref` functions. -- `Database + DatabaseCommit`: Allows directly committing changes of a transaction. It enables `transact_commit` and `inspect_commit` functions. +## Runtime + +Runtime consist of list of functions from `Handler` that are called in predefined order. They are grouped by functionality on `Verification`, `PreExecution`, `ExecutionLoop`, `PostExecution` and `Instruction` functions. Verification function are related to the preverification of set `Environment` data. Pre/Post execution function are function that deduct and reward caller beneficiary. And `ExecutionLoop` functions handles initial call and creates and sub calls. `Instruction` functions are instruction table that is used inside Interpreter to execute opcodes. + +`Evm` execution runs **two** loops. First loop is call loop that everything starts with, it creates call frames, handles subcalls and its return outputs and call Interpreter loop to execute bytecode instructions it is handled by `ExecutionLoopHandler`. Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction from `InstructionTable`. + +First loop, the call loop, implements stack of `Frames` and it is responsible for handling sub calls and its return outputs. At the start Evm creates first `Frame` that contains `Interpreter` and starts the loop. `Interpreter` returns the `InterpreterAction` and action can be a `Return` of a call this means this interpreter finished its run or `SubCall`/`SubCreate` that means that new `Frame` needs to be created and pushed to the stack. When `Interpreter` returns `Return` action `Frame` is popped from the stack and its return value is pushed to the parent `Frame` stack. When `Interpreter` returns `SubCall`/`SubCreate` action new `Frame` is created and pushed to the stack and the loop continues. When the stack is empty the loop finishes. + +Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction. It is called from the call loop and it is responsible for executing bytecode instructions. It is implemented in [`Interpreter`](../interpreter.md) crate. + +To dive deeper into the `Evm` logic check [`Handler`](./handler.md) documentation. + +# Functionalities + +Function of Evm is to start execution but setting up what Evm is going to execute is done by `EvmBuilder`. + +Main function inside evm are: +* `preverify` - that only preverifies transaction information. +* `transact preverified` - is next step after preverification that executes transaction. +* `transact` - it calls both preverifies and it executes transaction. +* `builder` and `modify` function - allows building or modifying the Evm, more on this can be found in [`EvmBuilder`](./builder.md) documentation. `builder` is main way of creating Evm and `modify` allows you to modify parts of it without dissolving `Evm`. +* `into_context` - is used we want to get the `Context` from the Evm. \ No newline at end of file diff --git a/documentation/src/crates/revm/evm_impl.md b/documentation/src/crates/revm/evm_impl.md deleted file mode 100644 index afb0a0ee99..0000000000 --- a/documentation/src/crates/revm/evm_impl.md +++ /dev/null @@ -1,35 +0,0 @@ -# EVM Implementation - -This module implements the Ethereum Virtual Machine (EVM), a stack-based virtual machine that executes Ethereum smart contracts. The following methods are exposed through the `EVMImpl` struct. - -## Methods - -- `run_interpreter` - - This method is responsible for setting up and running the interpreter for a specific contract. - - - `contract`: A `Contract` instance that the interpreter will execute. - - `gas_limit`: A `u64` that determines the maximum amount of gas that the execution can consume. - - `is_static`: A boolean flag indicating if the execution is static. Static executions cannot modify the state. - - The method returns a tuple containing the result of the execution and the interpreter instance. The result is an `InstructionResult` enumeration value that indicates if the execution was successful or if an error occurred. - - This creates a contract with a specific bytecode and a gas price, then runs the interpreter on this contract with a specified gas limit. The is_static flag is set to false which means the execution can modify the state. The stack machine implements the following instructions: - -- `call_precompile` - - This method handles the execution of precompiled contracts. These are a special set of contracts that are part of the Ethereum protocol and implemented in native code for efficiency. - - - `gas`: A `Gas` instance representing the amount of gas available for execution. - - `contract`: The address of the precompiled contract in the form of a `B160` instance. - - `input_data`: The input data for the contract as a `Bytes` instance. - - The method returns a tuple containing the result of the contract execution, the remaining gas, and any output data as a `Bytes` instance. - -- `call_inner` - - This method performs a contract call within the EVM. - - - `inputs`: A mutable reference to a `CallInputs` instance, which contains all the necessary information for the contract call. - - The method returns a tuple containing the result of the call (as an `InstructionResult`), the remaining gas (as a `Gas` instance), and any output data from the call (as a `Bytes` instance). diff --git a/documentation/src/crates/revm/handler.md b/documentation/src/crates/revm/handler.md new file mode 100644 index 0000000000..5db00daee2 --- /dev/null +++ b/documentation/src/crates/revm/handler.md @@ -0,0 +1,100 @@ +# Handler + +Is logic part of the Evm, it contains the Specification ID, list of functions that do the logic and list of registers that can change behavior of the Handler when `Handler` is build. + +Functions can be grouped in five categories and are marked in that way in the code: +* Validation functions: `ValidateHandler` +* Pre execution functions: `PreExecutionHandler` +* Execution loop functions: `LoopExecutionHandler` +* Post execution functions: `PostExecutionHandler` +* Instruction table: `InstructionTable` + +### Handle Registers + +Simple function that is used to modify handler functions. Amazing thing about them is that they can be done over generic external type. For example this allows to have a register over trait that would allow to add hooks to the any type that implements the trait, that trait can be a GetInspector trait so anyone that implement it would be able to register inspector related functions. It is used inside the `EvmBuilder` to change behavior of the default mainnet Handler. + +Handle registers are set in `EvmBuilder`. + +Order of the registers is important as they are called in the order they are registered. And it matters if register overrides the previous handle or just wraps it, overriding handle can disrupt the logic of previous registered handles. + +Registers are very powerful as they allow modification of any part of the Evm and with additional of the `External` context it becomes a powerful combo. Simple example would be to register new precompiles for the Evm. + +### ValidationHandler + +Consist of functions that are used to validate transaction and block data. They are called before the execution of the transaction and they are used to check if the data (`Environment`) is valid. They are called in the following order: + +* validate_env + + That verifies if all data is set in `Environment` and if they are valid, for example if `gas_limit` is smaller than block `gas_limit`. + +* validate_initial_tx_gas + + It calculated initial gas needed for transaction to be executed and checks if it is less them the transaction gas_limit. Note that this does not touch the `Database` or state + +* validate_tx_against_state + + It loads the caller account and checks those information. Among them the nonce, if there is enough balance to pay for max gas spent and balance transferred. + +### PreExecutionHandler + +Consist of functions that are called before execution loop. They are called in the following order: + +* load + + Loads access list and beneficiary from `Database`. Cold load is done here. + +* load precompiles + + Load precompiles. + +* deduct_caller: + + Deducts values from the caller to the maximum amount of gas that can be spent on the transaction. This loads the caller account from the `Database`. + +### ExecutionLoopHandler + +Consist of the function that handles the call stack and the first loop. They are called in the following order: + +* create_first_frame + + This handler crates first frame of the call stack. It is called only once per transaction. + +* first_frame_return + + This handler is called after the first frame is executed. It is used to calculate the gas that is returned from the first frame. + +* frame_return + + This handler is called after every frame is executed (Expect first), it will calculate the gas that is returned from the frame and apply output to the parent frame. + +* sub_call + Create new call frame or return the Interpreter result if the call is not possible (has a error) or it is precompile call. + +* sub_crate + + Create new create call frame, create new account and execute bytecode that outputs the code of the new account. + +### InstructionTable + +Is a list of 256 function pointers that are used to execute instructions. They have two types, first is simple function that is faster and second is BoxedInstraction that has a small performance penalty but allows to capture the data. Look at the Interpreter documentation for more information. + +### PostExecutionHandler + +Is a list of functions that are called after the execution loop. They are called in the following order: + +* reimburse_caller + + Reimburse the caller with gas that was not spent during the execution of the transaction. + Or balance of gas that needs to be refunded. + +* reward_beneficiary + + At the end of every transaction beneficiary needs to be rewarded with the fee. + +* output + + It returns the changes state and the result of the execution. + +* end + + It will be called always as the last function of the handler. \ No newline at end of file diff --git a/documentation/src/crates/revm/host_trait.md b/documentation/src/crates/revm/host_trait.md deleted file mode 100644 index 4abf291f5d..0000000000 --- a/documentation/src/crates/revm/host_trait.md +++ /dev/null @@ -1,57 +0,0 @@ -# Host Implementation - -The `Host` trait provides an interface that allows the EVM to interact with the external world. It contains methods to access environmental information, manipulate account balances, and interact with contract code and storage. - -The [`EVMImpl`](./evm_impl.md) struct implements this `Host` trait. - -- `step` & `step_end` - - These methods are used to control the interpreter's execution. They move the interpreter forward one step, allowing the user to inspect the state of the interpreter after each individual operation. These control the execution of the interpreter, allowing step-by-step execution and inspection. - -- `env` - - This method returns a mutable reference to the environment information that the EVM uses for its execution. The `Env` struct contains details about the current block, such as the timestamp, block number, difficulty, and gas limit. - -- `block_hash` - - This method retrieves the hash of a block given its number. It's typically used within smart contracts for actions like random number generation. - -- `load_account` - - This method loads the account associated with a given address and returns information about the account's existence and if it's a contract. - -- `balance` - - This method retrieves the balance of an Ethereum account given its address. It returns a tuple containing the balance and a boolean indicating whether the account was "cold" (accessed for the first time in the current transaction). - -- `code` - - This method retrieves the bytecode of a given address. It returns a tuple containing the bytecode and a boolean indicating whether the account was "cold". - -- `code_hash` - - This method retrieves the code_hash at a given address. It returns a tuple containing the hash and a boolean indicating whether the account was "cold". - -- `sload` & `sstore` - - These methods interact with the contract storage. The `sload` method retrieves a value from contract storage, while `sstore` sets a value in contract storage. - -- `tload` & `tstore` - - As defined in [EIP1153](https://eips.ethereum.org/EIPS/eip-1153), for transient storage reads and writes. - -- `log` - - This method is used to create log entries, which are a way for contracts to produce output that external observers (like dapps or the frontend of a blockchain explorer) can listen for and react to. - -- `selfdestruct` - - The selfdestruct method attempts to terminate the specified address, transferring its remaining balance to a given target address. If the `Inspector` is `Some`, the self-destruction event is observed or logged via an inspector. The method returns an Option, encapsulating the outcome of the operation: Some(SelfDestructResult) on success and None if an error occurs, with the error being stored internally for later reference. - -- `create` - - The create method initiates the creation of a contract with the provided CreateInputs. If the `Inspector` is `Some`, the creation process is observed or logged using an inspector, both at the start and end of the creation. The method returns a tuple consisting of the operation's result (InstructionResult), the optional address (Option) of the newly created contract, the amount of gas consumed (Gas), and the output data (Bytes). If the inspector intervenes and determines the instruction shouldn't continue, an early return occurs with the observed outcomes. - -- `call` - - The call method manages a contract invocation using the provided CallInputs. If the `Inspector` is `Some`, the call event is observed or logged via an inspector before execution. The method yields a tuple representing the outcome of the call: the result status (InstructionResult), the consumed gas (Gas), and the output data (Bytes). If the inspector suggests early termination, the method returns immediately with the observed results. Otherwise, the main call execution is processed, and the outcomes, either raw or observed, are returned accordingly. \ No newline at end of file diff --git a/documentation/src/crates/revm/state.md b/documentation/src/crates/revm/state.md new file mode 100644 index 0000000000..0dc646b088 --- /dev/null +++ b/documentation/src/crates/revm/state.md @@ -0,0 +1,11 @@ +# State implementations + +State inheritis the `Database` trait and implements fetching of external state and storage. and various functionality on output of the EVM execution, most notable caching changes while execution multiple transactions. + +## Database Abstractions + +You can implement the traits Database, DatabaseRef or Database + DatabaseCommit depending on the desired handling of the struct. + +- `Database`: Has mutable `self` in its functions. It's useful if you want to modify your cache or update some statistics on `get` calls. This trait enables `preverify_transaction`, `transact_preverified`, `transact` and `inspect` functions. +- `DatabaseRef`: Takes a reference on the object, this is useful if you only have a reference on the state and don't want to update anything on it. It enables `previerify_transaction`, `transact_preverified_ref`, `transact_ref` and `inspect_ref` functions. +- `Database + DatabaseCommit`: Allows directly committing changes of a transaction. It enables `transact_commit` and `inspect_commit` functions. \ No newline at end of file diff --git a/examples/fork_ref_transact.rs b/examples/fork_ref_transact.rs index 8abcacfd5c..9086467720 100644 --- a/examples/fork_ref_transact.rs +++ b/examples/fork_ref_transact.rs @@ -4,7 +4,7 @@ use ethers_providers::{Http, Provider}; use revm::{ db::{CacheDB, EmptyDB, EthersDB}, primitives::{address, ExecutionResult, Output, TransactTo, U256}, - Database, EVM, + Database, Evm, }; use std::sync::Arc; @@ -65,23 +65,23 @@ async fn main() -> anyhow::Result<()> { .unwrap(); // initialise an empty (default) EVM - let mut evm = EVM::new(); - - // insert pre-built database from above - evm.database(cache_db); - - // fill in missing bits of env struct - // change that to whatever caller you want to be - evm.env.tx.caller = address!("0000000000000000000000000000000000000000"); - // account you want to transact with - evm.env.tx.transact_to = TransactTo::Call(pool_address); - // calldata formed via abigen - evm.env.tx.data = encoded.0.into(); - // transaction value in wei - evm.env.tx.value = U256::from(0); + let mut evm = Evm::builder() + .with_db(cache_db) + .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 = TransactTo::Call(pool_address); + // calldata formed via abigen + tx.data = encoded.0.into(); + // transaction value in wei + tx.value = U256::from(0); + }) + .build(); // execute transaction without writing to the DB - let ref_tx = evm.transact_ref().unwrap(); + let ref_tx = evm.transact().unwrap(); // select ExecutionResult struct let result = ref_tx.result; diff --git a/examples/generate_block_traces.rs b/examples/generate_block_traces.rs index 37579b7f2c..ed71988a85 100644 --- a/examples/generate_block_traces.rs +++ b/examples/generate_block_traces.rs @@ -6,8 +6,8 @@ use ethers_providers::{Http, Provider}; use indicatif::ProgressBar; use revm::db::{CacheDB, EthersDB, StateBuilder}; use revm::inspectors::TracerEip3155; -use revm::primitives::{Address, Env, TransactTo, U256}; -use revm::EVM; +use revm::primitives::{Address, TransactTo, U256}; +use revm::{inspector_handle_register, Evm}; use std::fs::OpenOptions; use std::io::BufWriter; use std::io::Write; @@ -74,25 +74,27 @@ async fn main() -> anyhow::Result<()> { let state_db = EthersDB::new(Arc::clone(&client), Some(prev_id)).expect("panic"); let cache_db: CacheDB>> = CacheDB::new(state_db); let mut state = StateBuilder::new_with_database(cache_db).build(); - let mut evm = EVM::new(); - evm.database(&mut state); - - let mut env = Env::default(); - if let Some(number) = block.number { - let nn = number.0[0]; - env.block.number = U256::from(nn); - } - local_fill!(env.block.coinbase, block.author); - local_fill!(env.block.timestamp, Some(block.timestamp), U256::from_limbs); - local_fill!( - env.block.difficulty, - Some(block.difficulty), - U256::from_limbs - ); - local_fill!(env.block.gas_limit, Some(block.gas_limit), U256::from_limbs); - if let Some(base_fee) = block.base_fee_per_gas { - local_fill!(env.block.basefee, Some(base_fee), U256::from_limbs); - } + let mut evm = Evm::builder() + .with_db(&mut state) + .with_external_context(TracerEip3155::new(Box::new(std::io::stdout()), true, true)) + .modify_block_env(|b| { + if let Some(number) = block.number { + let nn = number.0[0]; + b.number = U256::from(nn); + } + local_fill!(b.coinbase, block.author); + local_fill!(b.timestamp, Some(block.timestamp), U256::from_limbs); + local_fill!(b.difficulty, Some(block.difficulty), U256::from_limbs); + local_fill!(b.gas_limit, Some(block.gas_limit), U256::from_limbs); + if let Some(base_fee) = block.base_fee_per_gas { + local_fill!(b.basefee, Some(base_fee), U256::from_limbs); + } + }) + .modify_cfg_env(|c| { + c.chain_id = chain_id; + }) + .append_handler_register(inspector_handle_register) + .build(); let txs = block.transactions.len(); println!("Found {txs} transactions."); @@ -104,45 +106,49 @@ async fn main() -> anyhow::Result<()> { std::fs::create_dir_all("traces").expect("Failed to create traces directory"); // Fill in CfgEnv - env.cfg.chain_id = chain_id; for tx in block.transactions { - env.tx.caller = Address::from(tx.from.as_fixed_bytes()); - env.tx.gas_limit = tx.gas.as_u64(); - local_fill!(env.tx.gas_price, tx.gas_price, U256::from_limbs); - local_fill!(env.tx.value, Some(tx.value), U256::from_limbs); - env.tx.data = tx.input.0.into(); - let mut gas_priority_fee = U256::ZERO; - local_fill!( - gas_priority_fee, - tx.max_priority_fee_per_gas, - U256::from_limbs - ); - env.tx.gas_priority_fee = Some(gas_priority_fee); - env.tx.chain_id = Some(chain_id); - env.tx.nonce = Some(tx.nonce.as_u64()); - if let Some(access_list) = tx.access_list { - env.tx.access_list = access_list - .0 - .into_iter() - .map(|item| { - let new_keys: Vec = item - .storage_keys + evm = evm + .modify() + .modify_tx_env(|etx| { + etx.caller = Address::from(tx.from.as_fixed_bytes()); + etx.gas_limit = tx.gas.as_u64(); + local_fill!(etx.gas_price, tx.gas_price, U256::from_limbs); + local_fill!(etx.value, Some(tx.value), U256::from_limbs); + etx.data = tx.input.0.into(); + let mut gas_priority_fee = U256::ZERO; + local_fill!( + gas_priority_fee, + tx.max_priority_fee_per_gas, + U256::from_limbs + ); + etx.gas_priority_fee = Some(gas_priority_fee); + etx.chain_id = Some(chain_id); + etx.nonce = Some(tx.nonce.as_u64()); + if let Some(access_list) = tx.access_list { + etx.access_list = access_list + .0 .into_iter() - .map(|h256| U256::from_le_bytes(h256.0)) + .map(|item| { + let new_keys: Vec = item + .storage_keys + .into_iter() + .map(|h256| U256::from_le_bytes(h256.0)) + .collect(); + (Address::from(item.address.as_fixed_bytes()), new_keys) + }) .collect(); - (Address::from(item.address.as_fixed_bytes()), new_keys) - }) - .collect(); - } else { - env.tx.access_list = Default::default(); - } - - env.tx.transact_to = match tx.to { - Some(to_address) => TransactTo::Call(Address::from(to_address.as_fixed_bytes())), - None => TransactTo::create(), - }; - - evm.env = env.clone(); + } else { + etx.access_list = Default::default(); + } + + etx.transact_to = match tx.to { + Some(to_address) => { + TransactTo::Call(Address::from(to_address.as_fixed_bytes())) + } + None => TransactTo::create(), + }; + }) + .build(); // Construct the file writer to write the trace to let tx_number = tx.transaction_index.unwrap().0[0]; @@ -154,8 +160,8 @@ async fn main() -> anyhow::Result<()> { let writer = FlushWriter::new(Arc::clone(&inner)); // Inspect and commit the transaction to the EVM - let inspector = TracerEip3155::new(Box::new(writer), true, true); - if let Err(error) = evm.inspect_commit(inspector) { + evm.context.external.set_writer(Box::new(writer)); + if let Err(error) = evm.transact_commit() { println!("Got error: {:?}", error); } From 69a1a59e73eaea13ebce2275c73fb1a56ec75b83 Mon Sep 17 00:00:00 2001 From: Waylon Jepsen <57912727+0xJepsen@users.noreply.github.com> Date: Fri, 12 Jan 2024 08:56:43 -0700 Subject: [PATCH 56/66] added release-plz config (#912) --- .github/workflows/release-plz.yml | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/release-plz.yml diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml new file mode 100644 index 0000000000..65af8c7cdf --- /dev/null +++ b/.github/workflows/release-plz.yml @@ -0,0 +1,41 @@ +# Documentation: https://release-plz.ieni.dev/docs +name: Release-plz + +permissions: + pull-requests: write + contents: write + +on: + push: + branches: + - main + +jobs: + release-plz: + name: Release-plz + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + # Commits the cargo lock (generally a good practice for upstream libraries) + - name: Commit Cargo.lock + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add Cargo.lock + git commit -m "Update Cargo.lock" || echo "No changes to commit" + # This will run the release-plz action + # The action dectect API breaking changes detection with cargo-semver-checks. + # Semver is auto incremneted based on this + # A PR with the semver bump is created with a new release tag and changelog + # if you configure the cargo registry token, the action will also publish the new version to crates.io + - name: Run release-plz + uses: MarcoIeni/release-plz-action@v0.5 + env: + # The admin of the repository with need to configure the following secrets: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} \ No newline at end of file From 867c5bae6d87b8ff246ec7370ce22f0f54a6769d Mon Sep 17 00:00:00 2001 From: rakita Date: Fri, 12 Jan 2024 21:58:28 +0100 Subject: [PATCH 57/66] refactor(interpreter): refactor sstore_cost (#974) * Refactored logic * Fixed if condition to be istanbul specific + some refactoring in calculate function * Removed nested loop * Changed name and added inline * Refactored if-else flow * Removed needless return for clippy errors * refactor * fix clippy --------- Co-authored-by: Mihir Wadekar --- crates/interpreter/src/gas/calc.rs | 83 ++++++++++++---------- crates/interpreter/src/gas/constants.rs | 3 + crates/revm/src/db/states/cache_account.rs | 6 +- tests | 1 + 4 files changed, 53 insertions(+), 40 deletions(-) create mode 160000 tests diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index 1200bf9709..e4d85ae095 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -176,7 +176,7 @@ pub fn sload_cost(is_cold: bool) -> u64 { } } else if SPEC::enabled(ISTANBUL) { // EIP-1884: Repricing for trie-size-dependent opcodes - 800 + INSTANBUL_SLOAD_GAS } else if SPEC::enabled(TANGERINE) { // EIP-150: Gas cost changes for IO-heavy operations 200 @@ -193,47 +193,56 @@ pub fn sstore_cost( gas: u64, is_cold: bool, ) -> Option { - // TODO untangle this mess and make it more elegant - let (gas_sload, gas_sstore_reset) = if SPEC::enabled(BERLIN) { - (WARM_STORAGE_READ_COST, SSTORE_RESET - COLD_SLOAD_COST) - } else { - (sload_cost::(is_cold), SSTORE_RESET) - }; + // EIP-1706 Disable SSTORE with gasleft lower than call stipend + if SPEC::enabled(ISTANBUL) && gas <= CALL_STIPEND { + return None; + } - // https://eips.ethereum.org/EIPS/eip-2200 - // It’s a combined version of EIP-1283 and EIP-1706 - let gas_cost = if SPEC::enabled(ISTANBUL) { - // EIP-1706 - if gas <= CALL_STIPEND { - return None; - } + if SPEC::enabled(BERLIN) { + // Berlin specification logic + let mut gas_cost = istanbul_sstore_cost::( + original, current, new, + ); - // EIP-1283 - if new == current { - gas_sload - } else { - if original == current { - if original == U256::ZERO { - SSTORE_SET - } else { - gas_sstore_reset - } - } else { - gas_sload - } + if is_cold { + gas_cost += COLD_SLOAD_COST; } + Some(gas_cost) + } else if SPEC::enabled(ISTANBUL) { + // Istanbul logic + Some(istanbul_sstore_cost::( + original, current, new, + )) } else { - if current == U256::ZERO && new != U256::ZERO { - SSTORE_SET - } else { - gas_sstore_reset - } - }; - // In EIP-2929 we charge extra if the slot has not been used yet in this transaction - if SPEC::enabled(BERLIN) && is_cold { - Some(gas_cost + COLD_SLOAD_COST) + // Frontier logic + Some(frontier_sstore_cost(current, new)) + } +} + +/// EIP-2200: Structured Definitions for Net Gas Metering +#[inline(always)] +fn istanbul_sstore_cost( + original: U256, + current: U256, + new: U256, +) -> u64 { + if new == current { + SLOAD_GAS + } else if original == current && original == U256::ZERO { + SSTORE_SET + } else if original == current { + SSTORE_RESET_GAS } else { - Some(gas_cost) + SLOAD_GAS + } +} + +/// Frontier sstore cost just had two cases set and reset values +fn frontier_sstore_cost(current: U256, new: U256) -> u64 { + if current == U256::ZERO && new != U256::ZERO { + SSTORE_SET + } else { + SSTORE_RESET } } diff --git a/crates/interpreter/src/gas/constants.rs b/crates/interpreter/src/gas/constants.rs index b8bd7d0544..ed3c7aa38a 100644 --- a/crates/interpreter/src/gas/constants.rs +++ b/crates/interpreter/src/gas/constants.rs @@ -20,6 +20,8 @@ pub const COPY: u64 = 3; pub const BLOCKHASH: u64 = 20; pub const CODEDEPOSIT: u64 = 200; +/// EIP-1884: Repricing for trie-size-dependent opcodes +pub const INSTANBUL_SLOAD_GAS: u64 = 800; pub const SSTORE_SET: u64 = 20000; pub const SSTORE_RESET: u64 = 5000; pub const REFUND_SSTORE_CLEARS: i64 = 15000; @@ -34,6 +36,7 @@ pub const ACCESS_LIST_STORAGE_KEY: u64 = 1900; pub const COLD_SLOAD_COST: u64 = 2100; pub const COLD_ACCOUNT_ACCESS_COST: u64 = 2600; pub const WARM_STORAGE_READ_COST: u64 = 100; +pub const WARM_SSTORE_RESET: u64 = SSTORE_RESET - COLD_SLOAD_COST; /// EIP-3860 : Limit and meter initcode pub const INITCODE_WORD_COST: u64 = 2; diff --git a/crates/revm/src/db/states/cache_account.rs b/crates/revm/src/db/states/cache_account.rs index 126ce2ffe7..bfe326e5e7 100644 --- a/crates/revm/src/db/states/cache_account.rs +++ b/crates/revm/src/db/states/cache_account.rs @@ -100,7 +100,7 @@ impl CacheAccount { /// Fetch account info if it exist. pub fn account_info(&self) -> Option { - self.account.as_ref().map(|a| a.info.clone()) + self.account.clone().map(|a| a.info) } /// Dissolve account into components. @@ -282,7 +282,7 @@ impl CacheAccount { storage: StorageWithOriginalValues, ) -> TransitionAccount { let previous_status = self.status; - let previous_info = self.account.as_ref().map(|a| a.info.clone()); + let previous_info = self.account.clone().map(|a| a.info); let mut this_storage = self .account .take() @@ -303,7 +303,7 @@ impl CacheAccount { self.account = Some(changed_account); TransitionAccount { - info: self.account.as_ref().map(|a| a.info.clone()), + info: self.account.clone().map(|a| a.info), status: self.status, previous_info, previous_status, diff --git a/tests b/tests new file mode 160000 index 0000000000..e89fcb01c7 --- /dev/null +++ b/tests @@ -0,0 +1 @@ +Subproject commit e89fcb01c7cff52cf7ac04951742ffe99d60bb6a From 7dce1010a487735b9ed6625e3e55b02ff96b09be Mon Sep 17 00:00:00 2001 From: rakita Date: Sat, 13 Jan 2024 16:02:40 +0100 Subject: [PATCH 58/66] remove test link (#976) --- tests | 1 - 1 file changed, 1 deletion(-) delete mode 160000 tests diff --git a/tests b/tests deleted file mode 160000 index e89fcb01c7..0000000000 --- a/tests +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e89fcb01c7cff52cf7ac04951742ffe99d60bb6a From f5db6531dce7caf0971a4ff381009c896a0d0088 Mon Sep 17 00:00:00 2001 From: Iaroslav Mazur Date: Mon, 15 Jan 2024 10:33:51 +0200 Subject: [PATCH 59/66] refactor: enhance readability (#968) --- crates/interpreter/src/instruction_result.rs | 46 +++++++++---------- .../src/instructions/arithmetic.rs | 2 +- crates/interpreter/src/instructions/opcode.rs | 2 +- crates/primitives/src/result.rs | 4 +- crates/revm/src/context.rs | 4 +- crates/revm/src/journaled_state.rs | 2 +- 6 files changed, 29 insertions(+), 31 deletions(-) diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index 4833bd6f4f..8d79eed668 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -14,7 +14,7 @@ pub enum InstructionResult { // revert codes Revert = 0x10, // revert opcode CallTooDeep, - OutOfFund, + OutOfFunds, // Actions CallOrCreate = 0x20, @@ -62,11 +62,13 @@ impl From for InstructionResult { impl From for InstructionResult { fn from(value: HaltReason) -> Self { match value { - HaltReason::OutOfGas(OutOfGasError::BasicOutOfGas) => Self::OutOfGas, - HaltReason::OutOfGas(OutOfGasError::InvalidOperand) => Self::InvalidOperandOOG, - HaltReason::OutOfGas(OutOfGasError::Memory) => Self::MemoryOOG, - HaltReason::OutOfGas(OutOfGasError::MemoryLimit) => Self::MemoryLimitOOG, - HaltReason::OutOfGas(OutOfGasError::Precompile) => Self::PrecompileOOG, + HaltReason::OutOfGas(error) => match error { + OutOfGasError::Basic => Self::OutOfGas, + OutOfGasError::InvalidOperand => Self::InvalidOperandOOG, + OutOfGasError::Memory => Self::MemoryOOG, + OutOfGasError::MemoryLimit => Self::MemoryLimitOOG, + OutOfGasError::Precompile => Self::PrecompileOOG, + }, HaltReason::OpcodeNotFound => Self::OpcodeNotFound, HaltReason::InvalidFEOpcode => Self::InvalidFEOpcode, HaltReason::InvalidJump => Self::InvalidJump, @@ -83,7 +85,7 @@ impl From for InstructionResult { HaltReason::OverflowPayment => Self::OverflowPayment, HaltReason::StateChangeDuringStaticCall => Self::StateChangeDuringStaticCall, HaltReason::CallNotAllowedInsideStatic => Self::CallNotAllowedInsideStatic, - HaltReason::OutOfFund => Self::OutOfFund, + HaltReason::OutOfFunds => Self::OutOfFunds, HaltReason::CallTooDeep => Self::CallTooDeep, #[cfg(feature = "optimism")] HaltReason::FailedDeposit => Self::FatalExternalError, @@ -104,7 +106,7 @@ macro_rules! return_ok { #[macro_export] macro_rules! return_revert { () => { - InstructionResult::Revert | InstructionResult::CallTooDeep | InstructionResult::OutOfFund + InstructionResult::Revert | InstructionResult::CallTooDeep | InstructionResult::OutOfFunds }; } @@ -216,22 +218,18 @@ impl From for SuccessOrHalt { InstructionResult::Revert => Self::Revert, InstructionResult::CallOrCreate => Self::InternalCallOrCreate, // used only in interpreter loop InstructionResult::CallTooDeep => Self::Halt(HaltReason::CallTooDeep), // not gonna happen for first call - InstructionResult::OutOfFund => Self::Halt(HaltReason::OutOfFund), // Check for first call is done separately. - InstructionResult::OutOfGas => Self::Halt(HaltReason::OutOfGas( - revm_primitives::OutOfGasError::BasicOutOfGas, - )), - InstructionResult::MemoryLimitOOG => Self::Halt(HaltReason::OutOfGas( - revm_primitives::OutOfGasError::MemoryLimit, - )), - InstructionResult::MemoryOOG => { - Self::Halt(HaltReason::OutOfGas(revm_primitives::OutOfGasError::Memory)) + InstructionResult::OutOfFunds => Self::Halt(HaltReason::OutOfFunds), // Check for first call is done separately. + InstructionResult::OutOfGas => Self::Halt(HaltReason::OutOfGas(OutOfGasError::Basic)), + InstructionResult::MemoryLimitOOG => { + Self::Halt(HaltReason::OutOfGas(OutOfGasError::MemoryLimit)) + } + InstructionResult::MemoryOOG => Self::Halt(HaltReason::OutOfGas(OutOfGasError::Memory)), + InstructionResult::PrecompileOOG => { + Self::Halt(HaltReason::OutOfGas(OutOfGasError::Precompile)) + } + InstructionResult::InvalidOperandOOG => { + Self::Halt(HaltReason::OutOfGas(OutOfGasError::InvalidOperand)) } - InstructionResult::PrecompileOOG => Self::Halt(HaltReason::OutOfGas( - revm_primitives::OutOfGasError::Precompile, - )), - InstructionResult::InvalidOperandOOG => Self::Halt(HaltReason::OutOfGas( - revm_primitives::OutOfGasError::InvalidOperand, - )), InstructionResult::OpcodeNotFound => Self::Halt(HaltReason::OpcodeNotFound), InstructionResult::CallNotAllowedInsideStatic => { Self::Halt(HaltReason::CallNotAllowedInsideStatic) @@ -293,7 +291,7 @@ mod tests { let revert_results = vec![ InstructionResult::Revert, InstructionResult::CallTooDeep, - InstructionResult::OutOfFund, + InstructionResult::OutOfFunds, ]; for result in revert_results { diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index e0700ee3f3..724f5ff9c3 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -5,7 +5,7 @@ use crate::{ Host, InstructionResult, Interpreter, }; -pub fn wrapped_add(interpreter: &mut Interpreter, _host: &mut H) { +pub fn wrapping_add(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); *op2 = op1.wrapping_add(*op2); diff --git a/crates/interpreter/src/instructions/opcode.rs b/crates/interpreter/src/instructions/opcode.rs index f6265d122b..116353803c 100644 --- a/crates/interpreter/src/instructions/opcode.rs +++ b/crates/interpreter/src/instructions/opcode.rs @@ -94,7 +94,7 @@ where opcodes! { 0x00 => STOP => control::stop, - 0x01 => ADD => arithmetic::wrapped_add, + 0x01 => ADD => arithmetic::wrapping_add, 0x02 => MUL => arithmetic::wrapping_mul, 0x03 => SUB => arithmetic::wrapping_sub, 0x04 => DIV => arithmetic::div, diff --git a/crates/primitives/src/result.rs b/crates/primitives/src/result.rs index 666cfca11a..c91c9052eb 100644 --- a/crates/primitives/src/result.rs +++ b/crates/primitives/src/result.rs @@ -388,7 +388,7 @@ pub enum HaltReason { OverflowPayment, StateChangeDuringStaticCall, CallNotAllowedInsideStatic, - OutOfFund, + OutOfFunds, CallTooDeep, /* Optimism errors */ @@ -400,7 +400,7 @@ pub enum HaltReason { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum OutOfGasError { // Basic OOG error - BasicOutOfGas, + Basic, // Tried to expand past REVM limit MemoryLimit, // Basic OOG error from memory expansion diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index 52a3e97abe..b0c07472c7 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -230,7 +230,7 @@ impl EvmContext { // Check if caller has enough balance to send to the created contract. if caller_balance < inputs.value { - return return_error(InstructionResult::OutOfFund); + return return_error(InstructionResult::OutOfFunds); } // Increase nonce of caller and check if it overflows @@ -631,7 +631,7 @@ mod tests { let FrameOrResult::Result(err) = res else { panic!("Expected FrameOrResult::Result"); }; - assert_eq!(err.result, InstructionResult::OutOfFund); + assert_eq!(err.result, InstructionResult::OutOfFunds); let checkpointed = vec![vec![JournalEntry::AccountLoaded { address: contract }]]; assert_eq!(evm_context.journaled_state.journal, checkpointed); assert_eq!(evm_context.journaled_state.depth, 0); diff --git a/crates/revm/src/journaled_state.rs b/crates/revm/src/journaled_state.rs index 8aa4d51fd5..185b5c00c2 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/revm/src/journaled_state.rs @@ -176,7 +176,7 @@ impl JournaledState { let from_balance = &mut from_account.info.balance; *from_balance = from_balance .checked_sub(balance) - .ok_or(InstructionResult::OutOfFund)?; + .ok_or(InstructionResult::OutOfFunds)?; // add balance to let to_account = &mut self.state.get_mut(to).unwrap(); From 6a8b44d6b691ca7dc18f9c82ff770268a0cb63ae Mon Sep 17 00:00:00 2001 From: rakita Date: Mon, 15 Jan 2024 14:13:48 +0100 Subject: [PATCH 60/66] refactor(EvmBuilder): Remove unnecessary BuilderStage trait (#979) --- crates/revm/src/builder.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index 64bfcbffd5..d26b3efe5a 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -9,25 +9,20 @@ use core::marker::PhantomData; /// 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, Stage: BuilderStage, EXT, DB: Database> { +pub struct EvmBuilder<'a, BuilderStage, EXT, DB: Database> { evm: EvmContext, external: EXT, handler: Handler<'a, Evm<'a, EXT, DB>, EXT, DB>, - phantom: PhantomData, + phantom: PhantomData, } -/// Trait that unlocks builder stages. -pub trait BuilderStage {} - /// First stage of the builder allows setting generic variables. /// Generic variables are database and external context. pub struct SetGenericStage; -impl BuilderStage for SetGenericStage {} /// Second stage of the builder allows appending handler registers. /// Requires the database and external context to be set. pub struct HandlerStage; -impl BuilderStage for HandlerStage {} impl<'a> Default for EvmBuilder<'a, SetGenericStage, (), EmptyDB> { fn default() -> Self { @@ -155,7 +150,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { } } -impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> { +impl<'a, BuilderStage, EXT, DB: Database> EvmBuilder<'a, BuilderStage, EXT, DB> { /// Builds the [`Evm`]. pub fn build(self) -> Evm<'a, EXT, DB> { Evm::new( From fecb5072fe80e6a970a7a4555a0c509650a7e3fe Mon Sep 17 00:00:00 2001 From: slordua <124523276+slordua@users.noreply.github.com> Date: Mon, 15 Jan 2024 15:15:26 -0700 Subject: [PATCH 61/66] refactor(log): use alloy_primitives::Log (#975) * refactor(log): use alloy_primitives::Log * clippy + fmt * .toml fmt * refactor Host::log * refactor Host::log v2 --- Cargo.lock | 1 - bins/revme/Cargo.toml | 3 ++- crates/interpreter/src/host.rs | 7 +++---- crates/interpreter/src/host/dummy.rs | 10 +++------- crates/interpreter/src/instructions/host.rs | 9 +++++++-- crates/precompile/src/lib.rs | 9 +-------- crates/primitives/Cargo.toml | 2 -- crates/primitives/src/lib.rs | 4 +--- crates/primitives/src/log.rs | 11 ----------- crates/revm/src/evm.rs | 12 ++++-------- 10 files changed, 21 insertions(+), 47 deletions(-) delete mode 100644 crates/primitives/src/log.rs diff --git a/Cargo.lock b/Cargo.lock index 32520521e0..50b43a5275 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2417,7 +2417,6 @@ name = "revm-primitives" version = "1.3.0" dependencies = [ "alloy-primitives", - "alloy-rlp", "auto_impl", "bitflags 2.4.1", "bitvec", diff --git a/bins/revme/Cargo.toml b/bins/revme/Cargo.toml index 03f34cb563..fad281a9ea 100644 --- a/bins/revme/Cargo.toml +++ b/bins/revme/Cargo.toml @@ -21,7 +21,8 @@ revm = { path = "../../crates/revm", version = "3.5.0", default-features = false "c-kzg", ] } alloy-rlp = { version = "0.3", default-features = false, features = [ - "arrayvec", + "arrayvec", + "derive", ] } serde = { version = "1.0", features = ["derive", "rc"] } serde_json = { version = "1.0", features = ["preserve_order"] } diff --git a/crates/interpreter/src/host.rs b/crates/interpreter/src/host.rs index 8170f1e339..87000e7aaa 100644 --- a/crates/interpreter/src/host.rs +++ b/crates/interpreter/src/host.rs @@ -1,8 +1,7 @@ use crate::{ - primitives::{Address, Bytecode, Bytes, Env, B256, U256}, + primitives::{Address, Bytecode, Env, Log, B256, U256}, SelfDestructResult, }; -use alloc::vec::Vec; mod dummy; pub use dummy::DummyHost; @@ -48,8 +47,8 @@ pub trait Host { /// 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 `topics` and `data`. - fn log(&mut self, address: Address, topics: Vec, data: Bytes); + /// 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; diff --git a/crates/interpreter/src/host/dummy.rs b/crates/interpreter/src/host/dummy.rs index d6b1f57c06..da365a550a 100644 --- a/crates/interpreter/src/host/dummy.rs +++ b/crates/interpreter/src/host/dummy.rs @@ -1,4 +1,4 @@ -use crate::primitives::{hash_map::Entry, Bytecode, Bytes, HashMap, U256}; +use crate::primitives::{hash_map::Entry, Bytecode, HashMap, U256}; use crate::{ primitives::{Address, Env, Log, B256, KECCAK_EMPTY}, Host, SelfDestructResult, @@ -106,12 +106,8 @@ impl Host for DummyHost { } #[inline] - fn log(&mut self, address: Address, topics: Vec, data: Bytes) { - self.log.push(Log { - address, - topics, - data, - }) + fn log(&mut self, log: Log) { + self.log.push(log) } #[inline] diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 9cdfbccb8e..59b119e2a4 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -1,7 +1,7 @@ use crate::{ gas::{self, COLD_ACCOUNT_ACCESS_COST, WARM_STORAGE_READ_COST}, interpreter::{Interpreter, InterpreterAction}, - primitives::{Address, Bytes, Spec, SpecId::*, B256, U256}, + primitives::{Address, Bytes, Log, LogData, Spec, SpecId::*, B256, U256}, CallContext, CallInputs, CallScheme, CreateInputs, CreateScheme, Host, InstructionResult, Transfer, MAX_INITCODE_SIZE, }; @@ -211,7 +211,12 @@ pub fn log(interpreter: &mut Interpreter, host: &mut H) topics.push(B256::from(unsafe { interpreter.stack.pop_unsafe() })); } - host.log(interpreter.contract.address, topics, data); + let log = Log { + address: interpreter.contract.address, + data: LogData::new(topics, data).expect("LogData should have <=4 topics"), + }; + + host.log(log); } pub fn selfdestruct(interpreter: &mut Interpreter, host: &mut H) { diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index aa99a81776..6addef49e2 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -27,7 +27,7 @@ use once_cell::race::OnceBox; pub use revm_primitives as primitives; pub use revm_primitives::{ precompile::{PrecompileError as Error, *}, - Address, Bytes, HashMap, B256, + Address, Bytes, HashMap, Log, B256, }; pub fn calc_linear_cost_u32(len: usize, base: u64, word: u64) -> u64 { @@ -41,13 +41,6 @@ pub struct PrecompileOutput { pub logs: Vec, } -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] -pub struct Log { - pub address: Address, - pub topics: Vec, - pub data: Bytes, -} - impl PrecompileOutput { pub fn without_logs(cost: u64, output: Vec) -> Self { Self { diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index f57f7001f9..e20e00f541 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -20,7 +20,6 @@ rustdoc-args = ["--cfg", "docsrs"] alloy-primitives = { version = "0.6", default-features = false, features = [ "rlp", ] } -alloy-rlp = { version = "0.3", default-features = false, features = ["derive"] } hashbrown = "0.14" auto_impl = "1.1" bitvec = { version = "1", default-features = false, features = ["alloc"] } @@ -44,7 +43,6 @@ hex = "0.4" default = ["std", "c-kzg"] std = [ "alloy-primitives/std", - "alloy-rlp/std", "hex/std", "bitvec/std", "bitflags/std", diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index d721b4bef8..f2a6c049cc 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -15,7 +15,6 @@ pub mod db; pub mod env; #[cfg(feature = "c-kzg")] pub mod kzg; -mod log; pub mod precompile; pub mod result; pub mod specification; @@ -24,7 +23,7 @@ pub mod utilities; pub use alloy_primitives::{ self, address, b256, bytes, fixed_bytes, hex, hex_literal, ruint, uint, Address, Bytes, - FixedBytes, B256, I256, U256, + FixedBytes, Log, LogData, B256, I256, U256, }; pub use bitvec; pub use bytecode::*; @@ -33,7 +32,6 @@ pub use env::*; pub use hashbrown::{hash_map, hash_set, HashMap, HashSet}; #[cfg(feature = "c-kzg")] pub use kzg::{EnvKzgSettings, KzgSettings}; -pub use log::*; pub use precompile::*; pub use result::*; pub use specification::*; diff --git a/crates/primitives/src/log.rs b/crates/primitives/src/log.rs deleted file mode 100644 index d359fbe3af..0000000000 --- a/crates/primitives/src/log.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::{Address, Bytes, B256}; -use alloc::vec::Vec; -use alloy_rlp::{RlpDecodable, RlpEncodable}; - -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, RlpDecodable, RlpEncodable)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Log { - pub address: Address, - pub topics: Vec, - pub data: Bytes, -} diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 521c754242..ce0dd8d453 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -7,8 +7,8 @@ use crate::{ SelfDestructResult, SharedMemory, }, primitives::{ - specification::SpecId, Address, Bytecode, Bytes, EVMError, EVMResult, Env, ExecutionResult, - Log, Output, ResultAndState, TransactTo, B256, U256, + specification::SpecId, Address, Bytecode, EVMError, EVMResult, Env, ExecutionResult, Log, + Output, ResultAndState, TransactTo, B256, U256, }, CallStackFrame, Context, FrameOrResult, }; @@ -343,12 +343,8 @@ impl Host for Evm<'_, EXT, DB> { self.context.evm.tstore(address, index, value) } - fn log(&mut self, address: Address, topics: Vec, data: Bytes) { - self.context.evm.journaled_state.log(Log { - address, - topics, - data, - }); + fn log(&mut self, log: Log) { + self.context.evm.journaled_state.log(log); } fn selfdestruct(&mut self, address: Address, target: Address) -> Option { From 9d4256262a033ab87c1e7855045d6e1eff44aacc Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Tue, 16 Jan 2024 03:10:04 -0500 Subject: [PATCH 62/66] fix: use maximum possible data fee for 4844 balance checks (#981) * fix: use maximum possible data fee for 4844 balance checks * fix docs * use unwrap_or_default to cover non-blob txs --- crates/primitives/src/env.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index 054175cc97..f51c6f56e6 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -47,6 +47,19 @@ impl Env { }) } + /// 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 { + self.tx.max_fee_per_blob_gas.map(|max_fee_per_blob_gas| { + max_fee_per_blob_gas.saturating_mul(U256::from(self.tx.get_total_blob_gas())) + }) + } + /// Validate the block environment. #[inline] pub fn validate_block_env(&self) -> Result<(), InvalidHeader> { @@ -218,7 +231,8 @@ impl Env { .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; if SPEC::enabled(SpecId::CANCUN) { - let data_fee = self.calc_data_fee().expect("already checked"); + // if the tx is not a blob tx, this will be None, so we add zero + let data_fee = self.calc_max_data_fee().unwrap_or_default(); balance_check = balance_check .checked_add(U256::from(data_fee)) .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; @@ -560,7 +574,7 @@ pub struct TxEnv { } impl TxEnv { - /// See [EIP-4844] and [`Env::calc_data_fee`]. + /// See [EIP-4844], [`Env::calc_data_fee`], and [`Env::calc_max_data_fee`]. /// /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 #[inline] From 5e6546e2a57bbc7661469fa606ea3c6569fe1838 Mon Sep 17 00:00:00 2001 From: John Smith Date: Tue, 16 Jan 2024 20:15:22 +0800 Subject: [PATCH 63/66] fix: cast overflow in 32-bits OS (#978) * fix: cast overflow in 32-bits OS * Fix type conversion issues in bitwise.rs, host.rs, host_env.rs, macros.rs, and system.rs * Fix error in as_usize_or_fail macro * Update CI configuration and add Cross.toml * Fix command arguments in ethereum-tests.yml * Update Ethereum tests workflow * Update Cross.toml with pre-build commands for i686 target --- .github/workflows/ci.yml | 2 +- .github/workflows/ethereum-tests.yml | 8 +++++--- Cross.toml | 10 ++++++++++ crates/interpreter/src/instructions/macros.rs | 8 ++++++-- 4 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 Cross.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index af2b3a4127..c4d7e3ddde 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: - run: | cd crates/revm cargo check --no-default-features - + check-serde: name: check serde runs-on: ubuntu-latest diff --git a/.github/workflows/ethereum-tests.yml b/.github/workflows/ethereum-tests.yml index 032a632346..328cff78e1 100644 --- a/.github/workflows/ethereum-tests.yml +++ b/.github/workflows/ethereum-tests.yml @@ -10,7 +10,6 @@ on: pull_request: branches: [main, "release/**"] - jobs: tests-stable: name: Ethereum Tests (Stable) @@ -19,6 +18,7 @@ jobs: strategy: matrix: profile: [ethtests, release] + target: [i686-unknown-linux-gnu, x86_64-unknown-linux-gnu] steps: - name: Checkout sources uses: actions/checkout@v3 @@ -37,11 +37,13 @@ jobs: with: cache-on-failure: true + - name: Install cross + run: cargo install cross + - name: Run Ethereum tests run: | - cargo run --profile ${{ matrix.profile }} -p revme -- statetest \ + cross run --target ${{matrix.target}} --profile ${{ matrix.profile }} -p revme -- statetest \ ethtests/GeneralStateTests/ \ ethtests/LegacyTests/Constantinople/GeneralStateTests/ \ ethtests/EIPTests/StateTests/stEIP1153-transientStorage/ \ ethtests/EIPTests/StateTests/stEIP4844-blobtransactions/ \ - ethtests/EIPTests/StateTests/stEIP5656-MCOPY/ diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000000..d25de22494 --- /dev/null +++ b/Cross.toml @@ -0,0 +1,10 @@ +[build] +pre-build = [ + "apt-get update && apt-get install --assume-yes --no-install-recommends llvm-dev clang libclang-dev", +] + +[target.i686-unknown-linux-gnu] +image = "ghcr.io/cross-rs/i686-unknown-linux-gnu:main" + +[target.x86_64-unknown-linux-gnu] +image = "ghcr.io/cross-rs/x86_64-unknown-linux-gnu:main" diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index 7c4c222555..92189707bf 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -190,7 +190,7 @@ macro_rules! as_u64_saturated { macro_rules! as_usize_saturated { ($v:expr) => { - as_u64_saturated!($v) as usize + ::core::convert::TryInto::::try_into(as_u64_saturated!($v)).unwrap_or(usize::MAX) }; } @@ -205,6 +205,10 @@ macro_rules! as_usize_or_fail { $interp.instruction_result = $reason; return; } - x[0] as usize + let Ok(val) = ::core::convert::TryInto::::try_into(x[0]) else { + $interp.instruction_result = $reason; + return; + }; + val }}; } From ed524cb5ee3b1bd340020190f8e63a1555ead3b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 12:02:48 +0100 Subject: [PATCH 64/66] chore(deps): bump bitflags from 2.4.1 to 2.4.2 (#983) Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.4.1 to 2.4.2. - [Release notes](https://github.com/bitflags/bitflags/releases) - [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md) - [Commits](https://github.com/bitflags/bitflags/compare/2.4.1...2.4.2) --- updated-dependencies: - dependency-name: bitflags dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 12 ++++++------ crates/primitives/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 50b43a5275..73ae27baa8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -361,7 +361,7 @@ version = "0.66.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cexpr", "clang-sys", "lazy_static", @@ -401,9 +401,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" dependencies = [ "arbitrary", "serde", @@ -2189,7 +2189,7 @@ checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.4.1", + "bitflags 2.4.2", "lazy_static", "num-traits", "rand", @@ -2418,7 +2418,7 @@ version = "1.3.0" dependencies = [ "alloy-primitives", "auto_impl", - "bitflags 2.4.1", + "bitflags 2.4.2", "bitvec", "c-kzg", "derive_more", @@ -2600,7 +2600,7 @@ version = "0.38.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index e20e00f541..7b55f557ab 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -23,7 +23,7 @@ alloy-primitives = { version = "0.6", default-features = false, features = [ hashbrown = "0.14" auto_impl = "1.1" bitvec = { version = "1", default-features = false, features = ["alloc"] } -bitflags = { version = "2.4.1", default-features = false } +bitflags = { version = "2.4.2", default-features = false } # For setting the CfgEnv KZGSettings. Enabled by c-kzg flag. c-kzg = { version = "0.4.0", default-features = false, optional = true } From 4aa835a23df041e76304299ab666293d7f94d756 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc <11177808+kekonen@users.noreply.github.com> Date: Wed, 17 Jan 2024 13:50:29 +0100 Subject: [PATCH 65/66] feat: Convert optimism panic into graceful error (#982) * feat(crates): custom evm error targeting optimism * fix(crates): alloc String * Update crates/primitives/src/result.rs Co-authored-by: rakita --------- Co-authored-by: rakita --- crates/primitives/src/result.rs | 7 ++++++- crates/revm/src/optimism/handler_register.rs | 20 +++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/crates/primitives/src/result.rs b/crates/primitives/src/result.rs index c91c9052eb..b765dd7aef 100644 --- a/crates/primitives/src/result.rs +++ b/crates/primitives/src/result.rs @@ -1,5 +1,5 @@ use crate::{Address, Bytes, Log, State, U256}; -use alloc::{boxed::Box, vec::Vec}; +use alloc::{boxed::Box, string::String, vec::Vec}; use core::fmt; /// Result of EVM execution. @@ -135,6 +135,10 @@ pub enum EVMError { Header(InvalidHeader), /// Database error. Database(DBError), + /// Custom error. + /// + /// Useful for handler registers where custom logic would want to return their own custom error. + Custom(String), } #[cfg(feature = "std")] @@ -146,6 +150,7 @@ impl fmt::Display for EVMError { EVMError::Transaction(e) => write!(f, "Transaction error: {e:?}"), EVMError::Header(e) => write!(f, "Header error: {e:?}"), EVMError::Database(e) => write!(f, "Database error: {e}"), + EVMError::Custom(e) => write!(f, "Custom error: {e}"), } } } diff --git a/crates/revm/src/optimism/handler_register.rs b/crates/revm/src/optimism/handler_register.rs index e3ff387fda..b0a5c91cdd 100644 --- a/crates/revm/src/optimism/handler_register.rs +++ b/crates/revm/src/optimism/handler_register.rs @@ -125,7 +125,9 @@ pub fn deduct_caller( if context.evm.env.tx.optimism.source_hash.is_none() { // get envelope let Some(enveloped_tx) = context.evm.env.tx.optimism.enveloped_tx.clone() else { - panic!("[OPTIMISM] Failed to load enveloped transaction."); + return Err(EVMError::Custom( + "[OPTIMISM] Failed to load enveloped transaction.".to_string(), + )); }; let tx_l1_cost = context @@ -166,11 +168,15 @@ pub fn reward_beneficiary( // 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 Some(l1_block_info) = context.evm.l1_block_info.clone() else { - panic!("[OPTIMISM] Failed to load L1 block information."); + return Err(EVMError::Custom( + "[OPTIMISM] Failed to load L1 block information.".to_string(), + )); }; let Some(enveloped_tx) = &context.evm.env.tx.optimism.enveloped_tx else { - panic!("[OPTIMISM] Failed to load enveloped transaction."); + return Err(EVMError::Custom( + "[OPTIMISM] Failed to load enveloped transaction.".to_string(), + )); }; let l1_cost = l1_block_info.calculate_tx_l1_cost(enveloped_tx, SPEC::SPEC_ID); @@ -181,7 +187,9 @@ pub fn reward_beneficiary( .journaled_state .load_account(optimism::L1_FEE_RECIPIENT, &mut context.evm.db) else { - panic!("[OPTIMISM] Failed to load L1 Fee Vault account"); + return Err(EVMError::Custom( + "[OPTIMISM] Failed to load L1 Fee Vault account.".to_string(), + )); }; l1_fee_vault_account.mark_touch(); l1_fee_vault_account.info.balance += l1_cost; @@ -192,7 +200,9 @@ pub fn reward_beneficiary( .journaled_state .load_account(optimism::BASE_FEE_RECIPIENT, &mut context.evm.db) else { - panic!("[OPTIMISM] Failed to load Base Fee Vault account"); + return Err(EVMError::Custom( + "[OPTIMISM] Failed to load Base Fee Vault account.".to_string(), + )); }; base_fee_vault_account.mark_touch(); base_fee_vault_account.info.balance += context From 0629883f5a40e913a5d9498fa37886348c858c70 Mon Sep 17 00:00:00 2001 From: Luca Provini Date: Wed, 17 Jan 2024 13:55:32 +0100 Subject: [PATCH 66/66] refactor(Inspector): Add CreateOutcome in create/create_end return (#980) * rename main Evm structs, introduce wip external type * tests * Split evm and external context * continue previous commit * wip inspector handle register * add few more handlers for frame and host * Add instruction handle * add instruction handler registration and Inspector wrap * Rm Spec generic, more handlers, start factory * move towards the builder, allow EVM modify * wip on EvmBuilder and modify functionality * EvmBuilder with stages wip * Add wip stages for builer * make handle register simple function, add raw instruction table, split external data from registers * wip on simple builder functions and handler registry * Examples and cleanup * fix lifetime and fmt * Add more handlers, deduct caller, validate tx agains state * All handlers counted, started on docs, some cleanup * renaming and docs * Support all Inspector functionality with Handler * Handler restructured. Documentation added * more docs on registers * integrate builder, fmt, move optimism l1block * add utility builder stage functions * add precompiles, fix bugs with journal spec * spec to generic, optimism build * fix optimism test * fuck macros * clippy and fmt * fix trace block example * ci fixes * Flatten builder stages to generic and handler stage * EvmBuilder doc and refactor fn access * ignore rust code in book * make revme compile, will refactor this in future * Rename handles to Pre/Post Execution and ExecutionLoop * fix optimism clippy * small rename * FrameData and docs * check links mdbook * comments and cleanup * comment * Add initialize interepreter to first frame * clippy * clippy2 * feat: create outcome * fix: createOutcome properties to pub and removed wrapper functions * review: fixed some review comments and added some improvement over instruction results * fix: removed unused is_revert method * review: adjusted comments, moved create_outcome to its own file * fix: no std check --------- Co-authored-by: rakita --- crates/interpreter/src/create_outcome.rs | 68 +++++++++++++++++++ crates/interpreter/src/interpreter.rs | 53 +++++++++++---- crates/interpreter/src/lib.rs | 2 + .../src/handler/mainnet/execution_loop.rs | 8 ++- crates/revm/src/inspector.rs | 8 +-- crates/revm/src/inspector/customprinter.rs | 5 +- crates/revm/src/inspector/eip3155.rs | 5 +- crates/revm/src/inspector/gas.rs | 12 ++-- crates/revm/src/inspector/handler_register.rs | 26 ++++--- 9 files changed, 149 insertions(+), 38 deletions(-) create mode 100644 crates/interpreter/src/create_outcome.rs diff --git a/crates/interpreter/src/create_outcome.rs b/crates/interpreter/src/create_outcome.rs new file mode 100644 index 0000000000..41c456b173 --- /dev/null +++ b/crates/interpreter/src/create_outcome.rs @@ -0,0 +1,68 @@ +use crate::{Gas, InstructionResult, InterpreterResult}; +use revm_primitives::{Address, Bytes}; + +/// Represents the outcome of a create operation in an interpreter. +/// +/// This struct holds the result of the operation along with an optional address. +/// It provides methods to determine the next action based on the result of the operation. +pub struct CreateOutcome { + // The result of the interpreter operation. + pub result: InterpreterResult, + // An optional address associated with the create operation. + pub address: Option
, +} + +impl CreateOutcome { + /// Constructs a new `CreateOutcome`. + /// + /// # Arguments + /// + /// * `result` - An `InterpreterResult` representing the result of the interpreter operation. + /// * `address` - An optional `Address` associated with the create operation. + /// + /// # Returns + /// + /// A new `CreateOutcome` instance. + pub fn new(result: InterpreterResult, address: Option
) -> Self { + Self { result, address } + } + + /// Retrieves a reference to the `InstructionResult` from the `InterpreterResult`. + /// + /// This method provides access to the `InstructionResult` which represents the + /// outcome of the instruction execution. It encapsulates the result information + /// such as whether the instruction was executed successfully, resulted in a revert, + /// or encountered a fatal error. + /// + /// # Returns + /// + /// A reference to the `InstructionResult`. + pub fn instruction_result(&self) -> &InstructionResult { + &self.result.result + } + + /// Retrieves a reference to the output bytes from the `InterpreterResult`. + /// + /// This method returns the output of the interpreted operation. The output is + /// typically used when the operation successfully completes and returns data. + /// + /// # Returns + /// + /// A reference to the output `Bytes`. + pub fn output(&self) -> &Bytes { + &self.result.output + } + + /// Retrieves a reference to the `Gas` details from the `InterpreterResult`. + /// + /// This method provides access to the gas details of the operation, which includes + /// information about gas used, remaining, and refunded. It is essential for + /// understanding the gas consumption of the operation. + /// + /// # Returns + /// + /// A reference to the `Gas` details. + pub fn gas(&self) -> &Gas { + &self.result.gas + } +} diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 1caed8a239..da6433b325 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -8,14 +8,15 @@ pub use contract::Contract; pub use shared_memory::{next_multiple_of_32, SharedMemory}; pub use stack::{Stack, STACK_LIMIT}; +use crate::alloc::borrow::ToOwned; use crate::{ - primitives::Bytes, push, push_b256, return_ok, return_revert, CallInputs, CreateInputs, Gas, - Host, InstructionResult, + primitives::Bytes, push, push_b256, return_ok, return_revert, CallInputs, CreateInputs, + CreateOutcome, Gas, Host, InstructionResult, }; use alloc::boxed::Box; use core::cmp::min; use core::ops::Range; -use revm_primitives::{Address, U256}; +use revm_primitives::U256; pub use self::shared_memory::EMPTY_SHARED_MEMORY; @@ -93,24 +94,52 @@ impl Interpreter { } } - /// When sub create call returns we can insert output of that call into this interpreter. - pub fn insert_create_output(&mut self, result: InterpreterResult, address: Option
) { - self.return_data_buffer = match result.result { + /// 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) { + 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 - return_revert!() => result.output, + create_outcome.output().to_owned() + } else { // Otherwise clear it - _ => Bytes::new(), + Bytes::new() }; - match result.result { + match instruction_result { return_ok!() => { + let address = create_outcome.address; push_b256!(self, address.unwrap_or_default().into_word()); - self.gas.erase_cost(result.gas.remaining()); - self.gas.record_refund(result.gas.refunded()); + 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(result.gas.remaining()); + self.gas.erase_cost(create_outcome.gas().remaining()); } InstructionResult::FatalExternalError => { self.instruction_result = InstructionResult::FatalExternalError; diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index eac96926bc..e63cbb3a1b 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -12,6 +12,7 @@ extern crate alloc; #[macro_use] mod macros; +mod create_outcome; pub mod gas; mod host; mod inner_models; @@ -20,6 +21,7 @@ pub mod instructions; mod interpreter; // Reexport primary types. +pub use create_outcome::CreateOutcome; pub use gas::Gas; pub use host::{DummyHost, Host}; pub use inner_models::*; diff --git a/crates/revm/src/handler/mainnet/execution_loop.rs b/crates/revm/src/handler/mainnet/execution_loop.rs index 9de9d2e37d..b9a1c4c87f 100644 --- a/crates/revm/src/handler/mainnet/execution_loop.rs +++ b/crates/revm/src/handler/mainnet/execution_loop.rs @@ -1,7 +1,7 @@ use crate::{ db::Database, interpreter::{ - return_ok, return_revert, CallInputs, CreateInputs, Gas, InstructionResult, + return_ok, return_revert, CallInputs, CreateInputs, CreateOutcome, Gas, InstructionResult, InterpreterResult, SharedMemory, }, primitives::{Env, Spec, TransactTo}, @@ -92,9 +92,10 @@ pub fn frame_return( let Some(parent_stack_frame) = parent_stack_frame else { return Some(result); }; + let create_outcome = CreateOutcome::new(result, Some(created_address)); parent_stack_frame .interpreter - .insert_create_output(result, Some(created_address)) + .insert_create_outcome(create_outcome) } FrameData::Call { return_memory_range, @@ -151,10 +152,11 @@ pub fn sub_create( match context.evm.make_create_frame(SPEC::SPEC_ID, &inputs) { FrameOrResult::Frame(new_frame) => Some(new_frame), FrameOrResult::Result(result) => { + let create_outcome = CreateOutcome::new(result, None); // insert result of the failed creation of create CallStackFrame. curent_stack_frame .interpreter - .insert_create_output(result, None); + .insert_create_outcome(create_outcome); None } } diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index 60ce286ce6..ea4c87243a 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -18,7 +18,7 @@ mod noop; // Exports. pub use handler_register::{inspector_handle_register, inspector_instruction, GetInspector}; -use revm_interpreter::InterpreterResult; +use revm_interpreter::{CreateOutcome, InterpreterResult}; /// [Inspector] implementations. pub mod inspectors { #[cfg(feature = "std")] @@ -109,7 +109,7 @@ pub trait Inspector { &mut self, context: &mut EvmContext, inputs: &mut CreateInputs, - ) -> Option<(InterpreterResult, Option
)> { + ) -> Option { let _ = context; let _ = inputs; None @@ -125,9 +125,9 @@ pub trait Inspector { context: &mut EvmContext, result: InterpreterResult, address: Option
, - ) -> (InterpreterResult, Option
) { + ) -> CreateOutcome { let _ = context; - (result, address) + CreateOutcome::new(result, address) } /// Called when a contract has been self-destructed with funds transferred to target. diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs index fc65544b56..0ad14995c7 100644 --- a/crates/revm/src/inspector/customprinter.rs +++ b/crates/revm/src/inspector/customprinter.rs @@ -2,6 +2,7 @@ //! It is a great tool if some debugging is needed. use core::ops::Range; +use revm_interpreter::CreateOutcome; use crate::{ inspectors::GasInspector, @@ -73,7 +74,7 @@ impl Inspector for CustomPrintTracer { context: &mut EvmContext, result: InterpreterResult, address: Option
, - ) -> (InterpreterResult, Option
) { + ) -> CreateOutcome { self.gas_inspector.create_end(context, result, address) } @@ -97,7 +98,7 @@ impl Inspector for CustomPrintTracer { &mut self, _context: &mut EvmContext, inputs: &mut CreateInputs, - ) -> Option<(InterpreterResult, Option
)> { + ) -> Option { println!( "CREATE CALL: caller:{:?}, scheme:{:?}, value:{:?}, init_code:{:?}, gas:{:?}", inputs.caller, inputs.scheme, inputs.value, inputs.init_code, inputs.gas_limit diff --git a/crates/revm/src/inspector/eip3155.rs b/crates/revm/src/inspector/eip3155.rs index 22269082a3..7ec60d51ea 100644 --- a/crates/revm/src/inspector/eip3155.rs +++ b/crates/revm/src/inspector/eip3155.rs @@ -5,6 +5,7 @@ use crate::{ EvmContext, GetInspector, Inspector, }; use core::ops::Range; +use revm_interpreter::CreateOutcome; use serde_json::json; use std::io::Write; @@ -118,7 +119,7 @@ impl Inspector for TracerEip3155 { &mut self, _context: &mut EvmContext, _inputs: &mut CreateInputs, - ) -> Option<(InterpreterResult, Option
)> { + ) -> Option { None } @@ -127,7 +128,7 @@ impl Inspector for TracerEip3155 { context: &mut EvmContext, result: InterpreterResult, address: Option
, - ) -> (InterpreterResult, Option
) { + ) -> CreateOutcome { self.gas_inspector.create_end(context, result, address) } } diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index f1266177e5..1a83afae35 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -1,5 +1,7 @@ //! GasIspector. Helper Inspector to calculate gas for others. +use revm_interpreter::CreateOutcome; + use crate::{ interpreter::InterpreterResult, primitives::{db::Database, Address}, @@ -59,13 +61,15 @@ impl Inspector for GasInspector { _context: &mut EvmContext, result: InterpreterResult, address: Option
, - ) -> (InterpreterResult, Option
) { - (result, address) + ) -> CreateOutcome { + CreateOutcome::new(result, address) } } #[cfg(test)] mod tests { + use revm_interpreter::CreateOutcome; + use crate::{ inspector::GetInspector, inspectors::GasInspector, @@ -128,7 +132,7 @@ mod tests { &mut self, context: &mut EvmContext, call: &mut CreateInputs, - ) -> Option<(InterpreterResult, Option
)> { + ) -> Option { self.gas_inspector.create(context, call); None } @@ -138,7 +142,7 @@ mod tests { context: &mut EvmContext, result: InterpreterResult, address: Option
, - ) -> (InterpreterResult, Option
) { + ) -> CreateOutcome { self.gas_inspector.create_end(context, result, address) } } diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index d46d52a8fe..ea66a88c94 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -119,7 +119,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( .get_inspector() .create(&mut context.evm, &mut create_inputs) { - return FrameOrResult::Result(output.0); + return FrameOrResult::Result(output.result); }; context.evm.make_create_frame(spec_id, &create_inputs) } @@ -177,8 +177,8 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( handler.execution_loop.sub_create = Arc::new( move |context, frame, mut inputs| -> Option> { let inspector = context.external.get_inspector(); - if let Some((result, address)) = inspector.create(&mut context.evm, &mut inputs) { - frame.interpreter.insert_create_output(result, address); + if let Some(create_outcome) = inspector.create(&mut context.evm, &mut inputs) { + frame.interpreter.insert_create_outcome(create_outcome); return None; } @@ -188,10 +188,10 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( Some(new_frame) } FrameOrResult::Result(result) => { - let (result, address) = + let create_outcome = inspector.create_end(&mut context.evm, result, frame.created_address()); // insert result of the failed creation of create CallStackFrame. - frame.interpreter.insert_create_output(result, address); + frame.interpreter.insert_create_outcome(create_outcome); None } } @@ -234,9 +234,12 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( let inspector = &mut context.external.get_inspector(); result = match &mut child.frame_data { FrameData::Create { created_address } => { - let (result, address) = - inspector.create_end(&mut context.evm, result, Some(*created_address)); - if let Some(address) = address { + let create_outcome = inspector.create_end( + &mut context.evm, + result.clone(), + Some(*created_address), + ); + if let Some(address) = create_outcome.address { *created_address = address; } result @@ -297,6 +300,7 @@ mod tests { Database, Evm, EvmContext, Inspector, }; use core::ops::Range; + use revm_interpreter::CreateOutcome; #[test] fn test_make_boxed_instruction_table() { @@ -369,7 +373,7 @@ mod tests { &mut self, _context: &mut EvmContext, _call: &mut CreateInputs, - ) -> Option<(InterpreterResult, Option
)> { + ) -> Option { None } @@ -378,8 +382,8 @@ mod tests { _context: &mut EvmContext, result: InterpreterResult, address: Option
, - ) -> (InterpreterResult, Option
) { - (result, address) + ) -> CreateOutcome { + CreateOutcome::new(result, address) } }