Full Code of vorot93/evmodin for AI

master 7afaf52855e7 cached
38 files
292.9 KB
77.2k tokens
640 symbols
1 requests
Download .txt
Showing preview only (306K chars total). Download the full file or copy to clipboard to get everything.
Repository: vorot93/evmodin
Branch: master
Commit: 7afaf52855e7
Files: 38
Total size: 292.9 KB

Directory structure:
gitextract_wugh22sf/

├── .github/
│   └── workflows/
│       └── main.yml
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── src/
│   ├── common.rs
│   ├── continuation/
│   │   ├── interrupt.rs
│   │   ├── interrupt_data.rs
│   │   ├── mod.rs
│   │   └── resume_data.rs
│   ├── evmc.rs
│   ├── host.rs
│   ├── instructions/
│   │   ├── arithmetic.rs
│   │   ├── bitwise.rs
│   │   ├── boolean.rs
│   │   ├── call.rs
│   │   ├── control.rs
│   │   ├── external.rs
│   │   ├── instruction_table.rs
│   │   ├── memory.rs
│   │   ├── mod.rs
│   │   ├── properties.rs
│   │   └── stack_manip.rs
│   ├── interpreter.rs
│   ├── lib.rs
│   ├── opcode.rs
│   ├── state.rs
│   ├── tracing/
│   │   └── mod.rs
│   └── util/
│       ├── bytecode.rs
│       ├── mocked_host.rs
│       ├── mod.rs
│       └── tester.rs
└── tests/
    ├── basefee.rs
    ├── call.rs
    ├── eip2929.rs
    ├── execute.rs
    ├── other.rs
    └── state.rs

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/main.yml
================================================
on:
  pull_request:
  push:
    branches:
      - master

name: CI

jobs:
  ci:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macOS-latest]

    steps:
      - uses: actions/checkout@v2
        with:
          submodules: recursive

      - uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: nightly
          override: true
          components: rustfmt, clippy

      - uses: actions-rs/cargo@v1
        with:
          command: fmt
          args: --all -- --check

      - uses: actions-rs/cargo@v1
        with:
          command: install
          args: cargo-hack

      - uses: actions-rs/cargo@v1
        with:
          command: hack
          args: check --all --ignore-private --each-feature --no-dev-deps

      - uses: actions-rs/cargo@v1
        with:
          command: check
          args: --all --all-targets --all-features

      - uses: actions-rs/cargo@v1
        with:
          command: test

      - uses: actions-rs/cargo@v1
        with:
          command: clippy
          args: -- -D warnings


================================================
FILE: .gitignore
================================================
/target
Cargo.lock


================================================
FILE: Cargo.toml
================================================
[package]
name = "evmodin"
version = "0.1.0"
edition = "2021"
license = "Apache-2.0"
description = "Fast EVM implementation with support for resumability."

[dependencies]
arrayvec = { version = "0.7", default-features = false, features = ["serde"] }
bytes = { version = "1", default-features = false, features = ["serde"] }
derive_more = "0.99"
educe = { version = "0.4", default-features = false, features = ["Debug"] }
enum-as-inner = "0.3"
evmc-declare = { git = "https://github.com/ethereum/evmc", tag = "v10.0.0-alpha.1", optional = true }
evmc-vm = { git = "https://github.com/ethereum/evmc", tag = "v10.0.0-alpha.1", optional = true }
i256 = { git = "https://github.com/vorot93/rust-i256" }
ethereum-types = { version = "0.12", default-features = false }
genawaiter = { git = "https://github.com/cuviper/genawaiter", branch = "unhacked" }
getset = "0.1"
hex = "0.4"
hex-literal = { version = "0.3", optional = true }
num-traits = { version = "0.2", default-features = false }
once_cell = "1"
parking_lot = { version = "0.11", optional = true }
primitive-types = { version = "0.10", default-features = false, features = [
    "serde",
] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sha3 = "0.10"
strum_macros = "0.23"

[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2.3", features = ["js"] }

[dev-dependencies]
evmodin-test = { path = ".", package = "evmodin", features = ["util"] }
hex-literal = "0.3"
rand = { version = "0.8", features = ["std"] }

[features]
evmc = ["evmc-declare", "evmc-vm"]
util = ["hex-literal", "parking_lot"]

[lib]
name = "evmodin"
path = "src/lib.rs"
crate-type = ["lib", "staticlib", "cdylib"]


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.md
================================================
# evmodin

Fast EVM implementation with support for resumability. Port of [evmone](https://github.com/ethereum/evmone) to Rust.

## Usage
```rust
use evmodin::{*, host::*, util::*, tracing::*};
use ethereum_types::*;
use hex_literal::hex;

let my_code = Bytecode::new()
    .mstore8_value(0, b'h')
    .mstore8_value(1, b'e')
    .mstore8_value(2, b'l')
    .mstore8_value(3, b'l')
    .mstore8_value(4, b'o')
    .ret(0, 5)
    .build();

let message = Message {
    kind: CallKind::Call,
    is_static: true,
    depth: 0,
    gas: 200,
    recipient: Address::zero(),
    code_address: Address::zero(),
    sender: Address::zero(),
    input_data: vec![].into(),
    value: U256::zero(),
};

assert_eq!(
    AnalyzedCode::analyze(my_code)
        .execute(&mut DummyHost, &mut NoopTracer, None, message, Revision::latest()),
    Output {
        status_code: StatusCode::Success,
        gas_left: 146,
        output_data: b"hello".to_vec().into(),
        create_address: None,
    }
)
```

## Host / interpreter separation
`evmodin` is not a standalone execution implementation - it is only an EVM interpreter with gas metering that must be coupled with Host, as defined in EVMC, for state access and inducing sub-calls. `MockedHost` is shipped in `evmodin`, but is only useful in tests.

[Akula](https://github.com/akula-bft/akula), a fully-fledged Ethereum implementation, features its own version of Host for execution. Akula+evmodin pairing is considered to be the reference execution implementation which passes all Ethereum consensus tests.

## Resumability
`evmodin` is an interpreter loop that runs until host interaction/data is necessary. Then it exits with an interrupt. Each interrupt contains a value to be supplied to the host, and `resume` method which may accept data from Host, depending on interrupt. `AnalyzedCode::execute` simply loops, using data from `Host` to resume interrupts. You can make your own reactor that will handle interrupts instead, please see `ExecutionStartInterrupt::run_to_completion_with_host` for reference implementation.


================================================
FILE: src/common.rs
================================================
use bytes::Bytes;
use ethereum_types::*;
use serde::Serialize;
use strum_macros::Display;

/// EVM revision.
#[derive(Clone, Copy, Debug, Display, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
pub enum Revision {
    /// The Frontier revision.
    /// The one Ethereum launched with.
    Frontier = 0,

    /// [The Homestead revision.](https://eips.ethereum.org/EIPS/eip-606)
    Homestead = 1,

    /// [The Tangerine Whistle revision.](https://eips.ethereum.org/EIPS/eip-608)
    Tangerine = 2,

    /// [The Spurious Dragon revision.](https://eips.ethereum.org/EIPS/eip-607)
    Spurious = 3,

    /// [The Byzantium revision.](https://eips.ethereum.org/EIPS/eip-609)
    Byzantium = 4,

    /// [The Constantinople revision.](https://eips.ethereum.org/EIPS/eip-1013)
    Constantinople = 5,

    /// [The Petersburg revision.](https://eips.ethereum.org/EIPS/eip-1716)
    Petersburg = 6,

    /// [The Istanbul revision.](https://eips.ethereum.org/EIPS/eip-1679)
    Istanbul = 7,

    /// [The Berlin revision.](https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md)
    Berlin = 8,

    /// [The London revision.](https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/london.md)
    London = 9,

    /// The Shanghai revision.
    Shanghai = 10,
}

impl Revision {
    pub fn iter() -> impl IntoIterator<Item = Self> {
        [
            Self::Frontier,
            Self::Homestead,
            Self::Tangerine,
            Self::Spurious,
            Self::Byzantium,
            Self::Constantinople,
            Self::Petersburg,
            Self::Istanbul,
            Self::Berlin,
            Self::London,
            Self::Shanghai,
        ]
    }

    pub const fn latest() -> Self {
        Self::Shanghai
    }

    pub const fn len() -> usize {
        Self::latest() as usize + 1
    }
}

/// Message status code.
#[must_use]
#[derive(Clone, Debug, Display, PartialEq)]
pub enum StatusCode {
    /// Execution finished with success.
    #[strum(serialize = "success")]
    Success,

    /// Generic execution failure.
    #[strum(serialize = "failure")]
    Failure,

    /// Execution terminated with REVERT opcode.
    ///
    /// In this case the amount of gas left MAY be non-zero and additional output
    /// data MAY be provided in ::evmc_result.
    #[strum(serialize = "revert")]
    Revert,

    /// The execution has run out of gas.
    #[strum(serialize = "out of gas")]
    OutOfGas,

    /// The designated INVALID instruction has been hit during execution.
    ///
    /// [EIP-141](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-141.md)
    /// defines the instruction 0xfe as INVALID instruction to indicate execution
    /// abortion coming from high-level languages. This status code is reported
    /// in case this INVALID instruction has been encountered.
    #[strum(serialize = "invalid instruction")]
    InvalidInstruction,

    /// An undefined instruction has been encountered.
    #[strum(serialize = "undefined instruction")]
    UndefinedInstruction,

    /// The execution has attempted to put more items on the EVM stack
    /// than the specified limit.
    #[strum(serialize = "stack overflow")]
    StackOverflow,

    /// Execution of an opcode has required more items on the EVM stack.
    #[strum(serialize = "stack underflow")]
    StackUnderflow,

    /// Execution has violated the jump destination restrictions.
    #[strum(serialize = "bad jump destination")]
    BadJumpDestination,

    /// Tried to read outside memory bounds.
    ///
    /// An example is RETURNDATACOPY reading past the available buffer.
    #[strum(serialize = "invalid memory access")]
    InvalidMemoryAccess,

    /// Call depth has exceeded the limit (if any)
    #[strum(serialize = "call depth exceeded")]
    CallDepthExceeded,

    /// Tried to execute an operation which is restricted in static mode.
    #[strum(serialize = "static mode violation")]
    StaticModeViolation,

    /// A call to a precompiled or system contract has ended with a failure.
    ///
    /// An example: elliptic curve functions handed invalid EC points.
    #[strum(serialize = "precompile failure")]
    PrecompileFailure,

    /// Contract validation has failed.
    #[strum(serialize = "contract validation failure")]
    ContractValidationFailure,

    /// An argument to a state accessing method has a value outside of the
    /// accepted range of values.
    #[strum(serialize = "argument out of range")]
    ArgumentOutOfRange,

    /// The caller does not have enough funds for value transfer.
    #[strum(serialize = "insufficient balance")]
    InsufficientBalance,

    /// EVM implementation generic internal error.
    #[strum(serialize = "internal error")]
    InternalError(String),
}

/// The kind of call-like instruction.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CallKind {
    Call,
    DelegateCall,
    CallCode,
    Create,
    Create2 { salt: U256 },
}

/// The message describing an EVM call,
/// including a zero-depth call from transaction origin.
#[derive(Clone, Debug, PartialEq)]
pub struct Message {
    /// The kind of the call. For zero-depth calls `CallKind::Call` SHOULD be used.
    pub kind: CallKind,

    /// Static call mode.
    pub is_static: bool,

    /// The call depth.
    pub depth: i32,

    /// The amount of gas for message execution.
    pub gas: i64,

    /// The destination (recipient) of the message.
    pub recipient: Address,

    /// The sender of the message.
    pub sender: Address,

    /// Message input data.
    pub input_data: Bytes,

    /// The amount of Ether transferred with the message.
    pub value: U256,

    /// The address of the code to be executed.
    ///
    /// May be different from the evmc_message::destination (recipient)
    /// in case of `CallKind::CallCode` or `CallKind::DelegateCall`.
    pub code_address: Address,
}

#[derive(Clone, Debug, PartialEq)]
pub struct CreateMessage {
    pub salt: Option<U256>,
    pub gas: i64,
    pub depth: i32,
    pub initcode: Bytes,
    pub sender: Address,
    pub endowment: U256,
}

impl From<CreateMessage> for Message {
    fn from(msg: CreateMessage) -> Self {
        Self {
            kind: if let Some(salt) = msg.salt {
                CallKind::Create2 { salt }
            } else {
                CallKind::Create
            },
            is_static: false,
            depth: msg.depth,
            gas: msg.gas,
            recipient: Address::zero(),
            code_address: Address::zero(),
            sender: msg.sender,
            input_data: msg.initcode,
            value: msg.endowment,
        }
    }
}

/// Output of EVM execution.
#[derive(Clone, Debug, PartialEq)]
pub struct Output {
    /// EVM exited with this status code.
    pub status_code: StatusCode,
    /// How much gas was left after execution
    pub gas_left: i64,
    /// Output data returned.
    pub output_data: Bytes,
    /// Contract creation address.
    pub create_address: Option<Address>,
}

/// EVM execution output if no error has occurred.
#[derive(Clone, Debug, PartialEq)]
pub struct SuccessfulOutput {
    /// Indicates if revert was requested.
    pub reverted: bool,
    /// How much gas was left after execution.
    pub gas_left: i64,
    /// Output data returned.
    pub output_data: Bytes,
}

impl From<SuccessfulOutput> for Output {
    fn from(
        SuccessfulOutput {
            reverted,
            gas_left,
            output_data,
        }: SuccessfulOutput,
    ) -> Self {
        Self {
            status_code: if reverted {
                StatusCode::Revert
            } else {
                StatusCode::Success
            },
            gas_left,
            output_data,
            create_address: None,
        }
    }
}

pub(crate) fn u256_to_address(v: U256) -> Address {
    H256(v.into()).into()
}

pub(crate) fn address_to_u256(v: Address) -> U256 {
    U256::from_big_endian(&v.0)
}


================================================
FILE: src/continuation/interrupt.rs
================================================
use super::*;

macro_rules! interrupt {
    ( $(#[$outer:meta])* $name:ident, $data:ty => $resume_with:ty) => {
		$(#[$outer])*
        pub struct $name {
            pub(crate) inner: ::core::pin::Pin<
                Box<
                    dyn Coroutine<
                            Yield = InterruptDataVariant,
                            Resume = ResumeDataVariant,
                            Return = Result<SuccessfulOutput, StatusCode>,
                        > + Send
                        + Sync
                        + Unpin,
                >,
            >,
            pub(crate) data: $data,
        }

        impl sealed::Sealed for $name {}

        impl Interrupt for $name {
            type InterruptData = $data;
            type ResumeData = $resume_with;

            fn data(&self) -> &Self::InterruptData {
                &self.data
            }

            fn resume(self, resume_data: $resume_with) -> InterruptVariant {
                resume_interrupt(self.inner, resume_data.into())
            }
        }
    };
}

interrupt! {
    /// EVM has just been created. Resume this interrupt to start execution.
    ExecutionStartInterrupt,
    () => ()
}
interrupt! {
    /// New instruction has been encountered.
    InstructionStartInterrupt,
    Box<InstructionStart> => StateModifier
}
interrupt! {
    /// Does this account exist?
    AccountExistsInterrupt,
    AccountExists => AccountExistsStatus
}
interrupt! {
    /// Need this storage key.
    GetStorageInterrupt,
    GetStorage => StorageValue
}
interrupt! {
    /// Set this storage key.
    SetStorageInterrupt,
    SetStorage => StorageStatusInfo
}
interrupt! {
    /// Get balance of this account.
    GetBalanceInterrupt,
    GetBalance => Balance
}
interrupt! {
    /// Get code size of this account.
    GetCodeSizeInterrupt,
    GetCodeSize => CodeSize
}
interrupt! {
    /// Get code hash of this account.
    GetCodeHashInterrupt,
    GetCodeHash => CodeHash
}
interrupt! {
    /// Get code of this account.
    CopyCodeInterrupt,
    CopyCode => Code
}
interrupt! {
    /// Selfdestruct this account.
    SelfdestructInterrupt,
    Selfdestruct => ()
}
interrupt! {
    /// Execute this message as a new call.
    CallInterrupt,
    Call => CallOutput
}
interrupt! {
    /// Get `TxContext` for this call.
    GetTxContextInterrupt,
    () => TxContextData
}
interrupt! {
    /// Get block hash for this account.
    GetBlockHashInterrupt,
    GetBlockHash => BlockHash
}
interrupt! {
    /// Emit log message.
    EmitLogInterrupt,
    EmitLog => ()
}
interrupt! {
    /// Access this account and return its status.
    AccessAccountInterrupt,
    AccessAccount => AccessAccountStatus
}
interrupt! {
    /// Access this storage key and return its status.
    AccessStorageInterrupt,
    AccessStorage => AccessStorageStatus
}

/// Collection of all possible interrupts. Match on this to get the specific interrupt returned.
#[derive(From)]
pub enum InterruptVariant {
    InstructionStart(InstructionStartInterrupt),
    AccountExists(AccountExistsInterrupt),
    GetStorage(GetStorageInterrupt),
    SetStorage(SetStorageInterrupt),
    GetBalance(GetBalanceInterrupt),
    GetCodeSize(GetCodeSizeInterrupt),
    GetCodeHash(GetCodeHashInterrupt),
    CopyCode(CopyCodeInterrupt),
    Selfdestruct(SelfdestructInterrupt),
    Call(CallInterrupt),
    GetTxContext(GetTxContextInterrupt),
    GetBlockHash(GetBlockHashInterrupt),
    EmitLog(EmitLogInterrupt),
    AccessAccount(AccessAccountInterrupt),
    AccessStorage(AccessStorageInterrupt),
    Complete(Result<SuccessfulOutput, StatusCode>),
}


================================================
FILE: src/continuation/interrupt_data.rs
================================================
use super::*;

#[derive(Debug)]
pub struct InstructionStart {
    pub pc: usize,
    pub opcode: OpCode,
    pub state: ExecutionState,
}

#[derive(Debug)]
pub struct AccountExists {
    pub address: Address,
}

#[derive(Debug)]
pub struct GetStorage {
    pub address: Address,
    pub key: U256,
}

#[derive(Debug)]
pub struct SetStorage {
    pub address: Address,
    pub key: U256,
    pub value: U256,
}

#[derive(Debug)]
pub struct GetBalance {
    pub address: Address,
}

#[derive(Debug)]
pub struct GetCodeSize {
    pub address: Address,
}

#[derive(Debug)]
pub struct GetCodeHash {
    pub address: Address,
}

#[derive(Debug)]
pub struct CopyCode {
    pub address: Address,
    pub offset: usize,
    pub max_size: usize,
}

#[derive(Debug)]
pub struct Selfdestruct {
    pub address: Address,
    pub beneficiary: Address,
}

#[derive(Debug)]
pub enum Call {
    Call(Message),
    Create(CreateMessage),
}

#[derive(Debug)]
pub struct GetBlockHash {
    pub block_number: u64,
}

#[derive(Debug)]
pub struct EmitLog {
    pub address: Address,
    pub data: Bytes,
    pub topics: ArrayVec<U256, 4>,
}

#[derive(Debug)]
pub struct AccessAccount {
    pub address: Address,
}

#[derive(Debug)]
pub struct AccessStorage {
    pub address: Address,
    pub key: U256,
}

#[derive(Debug)]
pub enum InterruptDataVariant {
    InstructionStart(Box<InstructionStart>),
    AccountExists(AccountExists),
    GetStorage(GetStorage),
    SetStorage(SetStorage),
    GetBalance(GetBalance),
    GetCodeSize(GetCodeSize),
    GetCodeHash(GetCodeHash),
    CopyCode(CopyCode),
    Selfdestruct(Selfdestruct),
    Call(Call),
    GetTxContext,
    GetBlockHash(GetBlockHash),
    EmitLog(EmitLog),
    AccessAccount(AccessAccount),
    AccessStorage(AccessStorage),
}


================================================
FILE: src/continuation/mod.rs
================================================
use self::{interrupt::*, interrupt_data::*, resume_data::*};
use crate::{
    common::*,
    host::{AccessStatus, StorageStatus, TxContext},
    state::ExecutionState,
    *,
};
use arrayvec::ArrayVec;
use derive_more::From;
use enum_as_inner::EnumAsInner;
use ethereum_types::*;
use genawaiter::{Coroutine, GeneratorState};
use std::{convert::Infallible, pin::Pin};

mod sealed {
    pub trait Sealed {}
}

/// Interrupts.
pub mod interrupt;
/// Data attached to interrupts.
pub mod interrupt_data;
/// Data required for resume.
pub mod resume_data;

/// Paused EVM with full state inside.
pub trait Interrupt: sealed::Sealed {
    /// Interrupt data returned.
    type InterruptData;
    /// Data required to resume execution.
    type ResumeData;

    /// Get interrupt data.
    fn data(&self) -> &Self::InterruptData;
    /// Resume execution until the next interrupt.
    fn resume(self, resume_data: Self::ResumeData) -> InterruptVariant;
}

type InnerCoroutine = Pin<
    Box<
        dyn Coroutine<
                Yield = InterruptDataVariant,
                Resume = ResumeDataVariant,
                Return = Result<SuccessfulOutput, StatusCode>,
            > + Send
            + Sync
            + Unpin,
    >,
>;

fn resume_interrupt(mut inner: InnerCoroutine, resume_data: ResumeDataVariant) -> InterruptVariant {
    match Pin::new(&mut *inner).resume_with(resume_data) {
        GeneratorState::Yielded(interrupt) => match interrupt {
            InterruptDataVariant::InstructionStart(data) => {
                InstructionStartInterrupt { inner, data }.into()
            }
            InterruptDataVariant::AccountExists(data) => {
                AccountExistsInterrupt { inner, data }.into()
            }
            InterruptDataVariant::GetStorage(data) => GetStorageInterrupt { inner, data }.into(),
            InterruptDataVariant::SetStorage(data) => SetStorageInterrupt { inner, data }.into(),
            InterruptDataVariant::GetBalance(data) => GetBalanceInterrupt { inner, data }.into(),
            InterruptDataVariant::GetCodeSize(data) => GetCodeSizeInterrupt { inner, data }.into(),
            InterruptDataVariant::GetCodeHash(data) => GetCodeHashInterrupt { inner, data }.into(),
            InterruptDataVariant::CopyCode(data) => CopyCodeInterrupt { inner, data }.into(),
            InterruptDataVariant::Selfdestruct(data) => {
                SelfdestructInterrupt { inner, data }.into()
            }
            InterruptDataVariant::Call(data) => CallInterrupt { inner, data }.into(),
            InterruptDataVariant::GetTxContext => GetTxContextInterrupt { inner, data: () }.into(),
            InterruptDataVariant::GetBlockHash(data) => {
                GetBlockHashInterrupt { inner, data }.into()
            }
            InterruptDataVariant::EmitLog(data) => EmitLogInterrupt { inner, data }.into(),
            InterruptDataVariant::AccessAccount(data) => {
                AccessAccountInterrupt { inner, data }.into()
            }
            InterruptDataVariant::AccessStorage(data) => {
                AccessStorageInterrupt { inner, data }.into()
            }
        },
        GeneratorState::Complete(res) => InterruptVariant::Complete(res),
    }
}


================================================
FILE: src/continuation/resume_data.rs
================================================
use super::*;
use educe::Educe;
use std::sync::Arc;

pub type StateModifier = Option<Arc<dyn Fn(&mut ExecutionState) + Send + Sync>>;

#[derive(Debug)]
pub struct AccountExistsStatus {
    pub exists: bool,
}

#[derive(Debug)]
pub struct Balance {
    pub balance: U256,
}

#[derive(Debug)]
pub struct CodeSize {
    pub code_size: U256,
}

#[derive(Debug)]
pub struct StorageValue {
    pub value: U256,
}

#[derive(Debug)]
pub struct StorageStatusInfo {
    pub status: StorageStatus,
}

#[derive(Debug)]
pub struct CodeHash {
    pub hash: U256,
}

#[derive(Debug)]
pub struct BlockHash {
    pub hash: U256,
}

#[derive(Debug)]
pub struct TxContextData {
    pub context: TxContext,
}

#[derive(Debug)]
pub struct Code {
    pub code: Bytes,
}

#[derive(Debug)]
pub struct CallOutput {
    pub output: Output,
}

#[derive(Debug)]
pub struct AccessAccountStatus {
    pub status: AccessStatus,
}

#[derive(Debug)]
pub struct AccessStorageStatus {
    pub status: AccessStatus,
}

/// All resumed data variants.
#[derive(Educe, EnumAsInner, From)]
#[educe(Debug)]
pub(crate) enum ResumeDataVariant {
    #[from(ignore)]
    Empty,
    StateModifier(#[educe(Debug(false))] StateModifier),
    AccountExistsStatus(AccountExistsStatus),
    Balance(Balance),
    CodeSize(CodeSize),
    StorageValue(StorageValue),
    StorageStatusInfo(StorageStatusInfo),
    CodeHash(CodeHash),
    BlockHash(BlockHash),
    TxContextData(TxContextData),
    Code(Code),
    CallOutput(CallOutput),
    AccessAccountStatus(AccessAccountStatus),
    AccessStorageStatus(AccessStorageStatus),
    Done(Infallible),
}

impl From<()> for ResumeDataVariant {
    fn from(_: ()) -> Self {
        Self::Empty
    }
}


================================================
FILE: src/evmc.rs
================================================
use crate::{common::*, host::*, tracing::*, AnalyzedCode};
use ::evmc_vm;
use ::evmc_vm::{ffi::*, EvmcVm, ExecutionContext, ExecutionMessage, MessageFlags, MessageKind};
use arrayvec::ArrayVec;
use bytes::Bytes;
use ethereum_types::*;
use evmc_vm::ExecutionResult;
use std::convert::TryInto;

pub(crate) trait Convert {
    type Into;

    fn convert(self) -> Self::Into;
}

impl Convert for Address {
    type Into = evmc_address;

    fn convert(self) -> Self::Into {
        evmc_address {
            bytes: self.to_fixed_bytes(),
        }
    }
}

impl Convert for H256 {
    type Into = evmc_bytes32;

    fn convert(self) -> Self::Into {
        evmc_bytes32 {
            bytes: self.to_fixed_bytes(),
        }
    }
}

impl Convert for U256 {
    type Into = evmc_uint256be;

    fn convert(self) -> Self::Into {
        evmc_uint256be { bytes: self.into() }
    }
}

impl From<evmc_access_status> for AccessStatus {
    fn from(s: evmc_access_status) -> Self {
        match s {
            evmc_access_status::EVMC_ACCESS_COLD => Self::Cold,
            evmc_access_status::EVMC_ACCESS_WARM => Self::Warm,
        }
    }
}

impl From<Message> for ExecutionMessage {
    fn from(msg: Message) -> Self {
        let mut create2_salt = evmc_bytes32::default();
        let kind = match msg.kind {
            crate::CallKind::Call => MessageKind::EVMC_CALL,
            crate::CallKind::DelegateCall => MessageKind::EVMC_DELEGATECALL,
            crate::CallKind::CallCode => MessageKind::EVMC_CALLCODE,
            crate::CallKind::Create => MessageKind::EVMC_CREATE,
            crate::CallKind::Create2 { salt } => {
                create2_salt = salt.convert();
                MessageKind::EVMC_CREATE2
            }
        };
        let flags = if msg.is_static {
            MessageFlags::EVMC_STATIC as u32
        } else {
            0
        };

        ExecutionMessage::new(
            kind,
            flags,
            msg.depth,
            msg.gas,
            msg.recipient.convert(),
            msg.sender.convert(),
            (!msg.input_data.is_empty()).then(|| &*msg.input_data),
            msg.value.convert(),
            create2_salt,
            msg.code_address.convert(),
        )
    }
}

impl Message {
    fn from_evmc(msg: &ExecutionMessage) -> Self {
        let kind = match msg.kind() {
            evmc_call_kind::EVMC_CALL => CallKind::Call,
            evmc_call_kind::EVMC_DELEGATECALL => CallKind::DelegateCall,
            evmc_call_kind::EVMC_CALLCODE => CallKind::CallCode,
            evmc_call_kind::EVMC_CREATE => CallKind::Create,
            evmc_call_kind::EVMC_CREATE2 => CallKind::Create2 {
                salt: msg.create2_salt().bytes.into(),
            },
        };

        Self {
            kind,
            is_static: msg.flags() == evmc_vm::ffi::evmc_flags::EVMC_STATIC as u32,
            depth: msg.depth(),
            gas: msg.gas(),
            recipient: msg.recipient().bytes.into(),
            sender: msg.sender().bytes.into(),
            input_data: msg
                .input()
                .map(|v| v.clone().into())
                .unwrap_or_else(Bytes::new),
            value: msg.value().bytes.into(),
            code_address: msg.code_address().bytes.into(),
        }
    }
}

impl From<evmc_status_code> for StatusCode {
    fn from(status: evmc_status_code) -> Self {
        match status {
            evmc_status_code::EVMC_SUCCESS => StatusCode::Success,
            evmc_status_code::EVMC_FAILURE => StatusCode::Failure,
            evmc_status_code::EVMC_REVERT => StatusCode::Revert,
            evmc_status_code::EVMC_OUT_OF_GAS => StatusCode::OutOfGas,
            evmc_status_code::EVMC_INVALID_INSTRUCTION => StatusCode::InvalidInstruction,
            evmc_status_code::EVMC_UNDEFINED_INSTRUCTION => StatusCode::UndefinedInstruction,
            evmc_status_code::EVMC_STACK_OVERFLOW => StatusCode::StackOverflow,
            evmc_status_code::EVMC_STACK_UNDERFLOW => StatusCode::StackUnderflow,
            evmc_status_code::EVMC_BAD_JUMP_DESTINATION => StatusCode::BadJumpDestination,
            evmc_status_code::EVMC_INVALID_MEMORY_ACCESS => StatusCode::InvalidMemoryAccess,
            evmc_status_code::EVMC_CALL_DEPTH_EXCEEDED => StatusCode::CallDepthExceeded,
            evmc_status_code::EVMC_STATIC_MODE_VIOLATION => StatusCode::StaticModeViolation,
            evmc_status_code::EVMC_PRECOMPILE_FAILURE => StatusCode::PrecompileFailure,
            evmc_status_code::EVMC_CONTRACT_VALIDATION_FAILURE => {
                StatusCode::ContractValidationFailure
            }
            evmc_status_code::EVMC_ARGUMENT_OUT_OF_RANGE => StatusCode::ArgumentOutOfRange,
            evmc_status_code::EVMC_WASM_UNREACHABLE_INSTRUCTION => {
                StatusCode::InternalError("WasmUnreachableInstruction".into())
            }
            evmc_status_code::EVMC_WASM_TRAP => StatusCode::InternalError("WasmTrap".into()),
            evmc_status_code::EVMC_INSUFFICIENT_BALANCE => StatusCode::InsufficientBalance,
            evmc_status_code::EVMC_INTERNAL_ERROR => StatusCode::InternalError(String::new()),
            evmc_status_code::EVMC_REJECTED => StatusCode::InternalError("Rejected".into()),
            evmc_status_code::EVMC_OUT_OF_MEMORY => StatusCode::InternalError("OutOfMemory".into()),
        }
    }
}

impl From<StatusCode> for evmc_status_code {
    fn from(status: StatusCode) -> Self {
        match status {
            StatusCode::Success => evmc_status_code::EVMC_SUCCESS,
            StatusCode::Failure => evmc_status_code::EVMC_FAILURE,
            StatusCode::Revert => evmc_status_code::EVMC_REVERT,
            StatusCode::OutOfGas => evmc_status_code::EVMC_OUT_OF_GAS,
            StatusCode::InvalidInstruction => evmc_status_code::EVMC_INVALID_INSTRUCTION,
            StatusCode::UndefinedInstruction => evmc_status_code::EVMC_UNDEFINED_INSTRUCTION,
            StatusCode::StackOverflow => evmc_status_code::EVMC_STACK_OVERFLOW,
            StatusCode::StackUnderflow => evmc_status_code::EVMC_STACK_UNDERFLOW,
            StatusCode::BadJumpDestination => evmc_status_code::EVMC_BAD_JUMP_DESTINATION,
            StatusCode::InvalidMemoryAccess => evmc_status_code::EVMC_INVALID_MEMORY_ACCESS,
            StatusCode::CallDepthExceeded => evmc_status_code::EVMC_CALL_DEPTH_EXCEEDED,
            StatusCode::StaticModeViolation => evmc_status_code::EVMC_STATIC_MODE_VIOLATION,
            StatusCode::PrecompileFailure => evmc_status_code::EVMC_PRECOMPILE_FAILURE,
            StatusCode::ContractValidationFailure => {
                evmc_status_code::EVMC_CONTRACT_VALIDATION_FAILURE
            }
            StatusCode::ArgumentOutOfRange => evmc_status_code::EVMC_ARGUMENT_OUT_OF_RANGE,
            StatusCode::InsufficientBalance => evmc_status_code::EVMC_INSUFFICIENT_BALANCE,
            StatusCode::InternalError(_) => evmc_status_code::EVMC_INTERNAL_ERROR,
        }
    }
}

impl<'a> Host for ExecutionContext<'a> {
    fn account_exists(&self, address: Address) -> bool {
        ExecutionContext::account_exists(self, &address.convert())
    }

    fn get_storage(&self, address: Address, key: U256) -> U256 {
        ExecutionContext::get_storage(self, &address.convert(), &key.convert())
            .bytes
            .into()
    }

    fn set_storage(&mut self, address: Address, key: U256, value: U256) -> StorageStatus {
        match ExecutionContext::set_storage(
            self,
            &address.convert(),
            &key.convert(),
            &value.convert(),
        ) {
            evmc_storage_status::EVMC_STORAGE_UNCHANGED => StorageStatus::Unchanged,
            evmc_storage_status::EVMC_STORAGE_MODIFIED => StorageStatus::Modified,
            evmc_storage_status::EVMC_STORAGE_MODIFIED_AGAIN => StorageStatus::ModifiedAgain,
            evmc_storage_status::EVMC_STORAGE_ADDED => StorageStatus::Added,
            evmc_storage_status::EVMC_STORAGE_DELETED => StorageStatus::Deleted,
        }
    }

    fn get_balance(&self, address: Address) -> U256 {
        ExecutionContext::get_balance(self, &address.convert())
            .bytes
            .into()
    }

    fn get_code_size(&self, address: Address) -> U256 {
        ExecutionContext::get_code_size(self, &address.convert()).into()
    }

    fn get_code_hash(&self, address: Address) -> U256 {
        ExecutionContext::get_code_hash(self, &address.convert())
            .bytes
            .into()
    }

    fn copy_code(&self, address: Address, offset: usize, buffer: &mut [u8]) -> usize {
        ExecutionContext::copy_code(self, &address.convert(), offset, buffer)
    }

    fn selfdestruct(&mut self, address: Address, beneficiary: Address) {
        ExecutionContext::selfdestruct(self, &address.convert(), &beneficiary.convert())
    }

    fn call(&mut self, msg: &Message) -> Output {
        let execution_result = ExecutionContext::call(self, &msg.clone().into());

        Output {
            status_code: execution_result.status_code().into(),
            gas_left: execution_result.gas_left(),
            output_data: execution_result
                .output()
                .map(|v| v.to_vec().into())
                .unwrap_or_else(Bytes::new),
            create_address: execution_result.create_address().map(|a| a.bytes.into()),
        }
    }

    fn get_tx_context(&self) -> TxContext {
        let c = ExecutionContext::get_tx_context(self);

        TxContext {
            tx_gas_price: c.tx_gas_price.bytes.into(),
            tx_origin: c.tx_origin.bytes.into(),
            block_coinbase: c.block_coinbase.bytes.into(),
            block_number: c.block_number.try_into().unwrap(),
            block_timestamp: c.block_timestamp.try_into().unwrap(),
            block_gas_limit: c.block_gas_limit.try_into().unwrap(),
            block_difficulty: c.block_difficulty.bytes.into(),
            chain_id: c.chain_id.bytes.into(),
            block_base_fee: c.block_base_fee.bytes.into(),
        }
    }

    fn get_block_hash(&self, block_number: u64) -> U256 {
        ExecutionContext::get_block_hash(self, block_number.try_into().unwrap())
            .bytes
            .into()
    }

    fn emit_log(&mut self, address: Address, data: &[u8], topics: &[U256]) {
        ExecutionContext::emit_log(
            self,
            &address.convert(),
            data,
            &topics
                .iter()
                .map(|topic| topic.convert())
                .collect::<ArrayVec<_, 4>>(),
        )
    }

    fn access_account(&mut self, address: Address) -> AccessStatus {
        ExecutionContext::access_account(self, &address.convert()).into()
    }

    fn access_storage(&mut self, address: Address, key: U256) -> AccessStatus {
        ExecutionContext::access_storage(self, &address.convert(), &key.convert()).into()
    }
}

impl From<evmc_vm::Revision> for Revision {
    fn from(rev: evmc_vm::Revision) -> Self {
        match rev {
            evmc_revision::EVMC_FRONTIER => Revision::Frontier,
            evmc_revision::EVMC_HOMESTEAD => Revision::Homestead,
            evmc_revision::EVMC_TANGERINE_WHISTLE => Revision::Tangerine,
            evmc_revision::EVMC_SPURIOUS_DRAGON => Revision::Spurious,
            evmc_revision::EVMC_BYZANTIUM => Revision::Byzantium,
            evmc_revision::EVMC_CONSTANTINOPLE => Revision::Constantinople,
            evmc_revision::EVMC_PETERSBURG => Revision::Petersburg,
            evmc_revision::EVMC_ISTANBUL => Revision::Istanbul,
            evmc_revision::EVMC_BERLIN => Revision::Berlin,
            evmc_revision::EVMC_LONDON => Revision::London,
            evmc_revision::EVMC_SHANGHAI => Revision::Shanghai,
        }
    }
}

impl From<Revision> for evmc_vm::Revision {
    fn from(rev: Revision) -> Self {
        match rev {
            Revision::Frontier => evmc_revision::EVMC_FRONTIER,
            Revision::Homestead => evmc_revision::EVMC_HOMESTEAD,
            Revision::Tangerine => evmc_revision::EVMC_TANGERINE_WHISTLE,
            Revision::Spurious => evmc_revision::EVMC_SPURIOUS_DRAGON,
            Revision::Byzantium => evmc_revision::EVMC_BYZANTIUM,
            Revision::Constantinople => evmc_revision::EVMC_CONSTANTINOPLE,
            Revision::Petersburg => evmc_revision::EVMC_PETERSBURG,
            Revision::Istanbul => evmc_revision::EVMC_ISTANBUL,
            Revision::Berlin => evmc_revision::EVMC_BERLIN,
            Revision::London => evmc_revision::EVMC_LONDON,
            Revision::Shanghai => evmc_revision::EVMC_SHANGHAI,
        }
    }
}

#[evmc_declare::evmc_declare_vm("evmodin", "evm", "0.1.0")]
pub struct EvmOdin;

impl EvmcVm for EvmOdin {
    fn init() -> Self {
        Self
    }

    fn execute<'a>(
        &self,
        revision: evmc_vm::Revision,
        code: &'a [u8],
        message: &'a ExecutionMessage,
        context: Option<&'a mut ExecutionContext<'a>>,
    ) -> ExecutionResult {
        let code = AnalyzedCode::analyze(code);

        let output = if let Some(context) = context {
            code.execute(
                context,
                &mut NoopTracer,
                None,
                Message::from_evmc(message),
                revision.into(),
            )
        } else {
            code.execute(
                &mut DummyHost,
                &mut NoopTracer,
                None,
                Message::from_evmc(message),
                revision.into(),
            )
        };

        ExecutionResult::new(
            output.status_code.clone().into(),
            output.gas_left,
            (!output.output_data.is_empty()).then(|| &*output.output_data),
        )
    }
}


================================================
FILE: src/host.rs
================================================
use crate::common::{Message, Output};
use ethereum_types::*;

/// State access status (EIP-2929).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AccessStatus {
    Cold,
    Warm,
}

impl Default for AccessStatus {
    fn default() -> Self {
        Self::Cold
    }
}

#[derive(Clone, Copy, Debug)]
pub enum StorageStatus {
    /// The value of a storage item has been left unchanged: 0 -> 0 and X -> X.
    Unchanged,
    /// The value of a storage item has been modified: X -> Y.
    Modified,
    /// A storage item has been modified after being modified before: X -> Y -> Z.
    ModifiedAgain,
    /// A new storage item has been added: 0 -> X.
    Added,
    /// A storage item has been deleted: X -> 0.
    Deleted,
}

/// The transaction and block data for execution.
#[derive(Clone, Debug)]
pub struct TxContext {
    /// The transaction gas price.
    pub tx_gas_price: U256,
    /// The transaction origin account.
    pub tx_origin: Address,
    /// The miner of the block.
    pub block_coinbase: Address,
    /// The block number.
    pub block_number: u64,
    /// The block timestamp.
    pub block_timestamp: u64,
    /// The block gas limit.
    pub block_gas_limit: u64,
    /// The block difficulty.
    pub block_difficulty: U256,
    /// The blockchain's ChainID.
    pub chain_id: U256,
    /// The block base fee per gas (EIP-1559, EIP-3198).
    pub block_base_fee: U256,
}

/// Abstraction that exposes host context to EVM.
pub trait Host {
    /// Check if an account exists.
    fn account_exists(&self, address: Address) -> bool;
    /// Get value of a storage key.
    ///
    /// Returns `Ok(U256::zero())` if does not exist.
    fn get_storage(&self, address: Address, key: U256) -> U256;
    /// Set value of a storage key.
    fn set_storage(&mut self, address: Address, key: U256, value: U256) -> StorageStatus;
    /// Get balance of an account.
    ///
    /// Returns `Ok(0)` if account does not exist.
    fn get_balance(&self, address: Address) -> U256;
    /// Get code size of an account.
    ///
    /// Returns `Ok(0)` if account does not exist.
    fn get_code_size(&self, address: Address) -> U256;
    /// Get code hash of an account.
    ///
    /// Returns `Ok(0)` if account does not exist.
    fn get_code_hash(&self, address: Address) -> U256;
    /// Copy code of an account.
    ///
    /// Returns `Ok(0)` if offset is invalid.
    fn copy_code(&self, address: Address, offset: usize, buffer: &mut [u8]) -> usize;
    /// Self-destruct account.
    fn selfdestruct(&mut self, address: Address, beneficiary: Address);
    /// Call to another account.
    fn call(&mut self, msg: &Message) -> Output;
    /// Retrieve transaction context.
    fn get_tx_context(&self) -> TxContext;
    /// Get block hash.
    ///
    /// Returns `Ok(U256::zero())` if block does not exist.
    fn get_block_hash(&self, block_number: u64) -> U256;
    /// Emit a log.
    fn emit_log(&mut self, address: Address, data: &[u8], topics: &[U256]);
    /// Mark account as warm, return previous access status.
    ///
    /// Returns `Ok(AccessStatus::Cold)` if account does not exist.
    fn access_account(&mut self, address: Address) -> AccessStatus;
    /// Mark storage key as warm, return previous access status.
    ///
    /// Returns `Ok(AccessStatus::Cold)` if account does not exist.
    fn access_storage(&mut self, address: Address, key: U256) -> AccessStatus;
}

/// Host that does not support any ops.
pub struct DummyHost;

impl Host for DummyHost {
    fn account_exists(&self, _: Address) -> bool {
        todo!()
    }

    fn get_storage(&self, _: Address, _: U256) -> U256 {
        todo!()
    }

    fn set_storage(&mut self, _: Address, _: U256, _: U256) -> StorageStatus {
        todo!()
    }

    fn get_balance(&self, _: Address) -> U256 {
        todo!()
    }

    fn get_code_size(&self, _: Address) -> U256 {
        todo!()
    }

    fn get_code_hash(&self, _: Address) -> U256 {
        todo!()
    }

    fn copy_code(&self, _: Address, _: usize, _: &mut [u8]) -> usize {
        todo!()
    }

    fn selfdestruct(&mut self, _: Address, _: Address) {
        todo!()
    }

    fn call(&mut self, _: &Message) -> Output {
        todo!()
    }

    fn get_tx_context(&self) -> TxContext {
        todo!()
    }

    fn get_block_hash(&self, _: u64) -> U256 {
        todo!()
    }

    fn emit_log(&mut self, _: Address, _: &[u8], _: &[U256]) {
        todo!()
    }

    fn access_account(&mut self, _: Address) -> AccessStatus {
        todo!()
    }

    fn access_storage(&mut self, _: Address, _: U256) -> AccessStatus {
        todo!()
    }
}


================================================
FILE: src/instructions/arithmetic.rs
================================================
use crate::{state::*, Revision, StatusCode};
use core::convert::TryInto;
use ethereum_types::{U256, U512};
use i256::I256;

pub(crate) fn add(stack: &mut Stack) {
    let a = stack.pop();
    let b = stack.pop();
    stack.push(a.overflowing_add(b).0);
}

pub(crate) fn mul(stack: &mut Stack) {
    let a = stack.pop();
    let b = stack.pop();
    stack.push(a.overflowing_mul(b).0);
}

pub(crate) fn sub(stack: &mut Stack) {
    let a = stack.pop();
    let b = stack.pop();
    stack.push(a.overflowing_sub(b).0);
}

pub(crate) fn div(stack: &mut Stack) {
    let a = stack.pop();
    let b = stack.pop();
    stack.push(if b.is_zero() { U256::zero() } else { a / b });
}

pub(crate) fn sdiv(stack: &mut Stack) {
    let a = I256::from(stack.pop());
    let b = I256::from(stack.pop());
    let v = a / b;
    stack.push(v.into());
}

pub(crate) fn modulo(stack: &mut Stack) {
    let a = stack.pop();
    let b = stack.pop();
    let v = if b.is_zero() { U256::zero() } else { a % b };
    stack.push(v);
}

pub(crate) fn smod(stack: &mut Stack) {
    let a = stack.pop();
    let b = stack.pop();

    let v = if b.is_zero() {
        U256::zero()
    } else {
        let v = I256::from(a) % I256::from(b);
        v.into()
    };

    stack.push(v);
}

pub(crate) fn addmod(stack: &mut Stack) {
    let a = U512::from(stack.pop());
    let b = U512::from(stack.pop());
    let c = U512::from(stack.pop());

    let v = if c.is_zero() {
        U256::zero()
    } else {
        let v = (a + b) % c;
        v.try_into().unwrap()
    };

    stack.push(v);
}

pub(crate) fn mulmod(stack: &mut Stack) {
    let a = U512::from(stack.pop());
    let b = U512::from(stack.pop());
    let c = U512::from(stack.pop());

    let v = if c.is_zero() {
        U256::zero()
    } else {
        let v = (a * b) % c;
        v.try_into().unwrap()
    };

    stack.push(v);
}

fn log2floor(value: U256) -> u64 {
    assert!(value != U256::zero());
    let mut l: u64 = 256;
    for i in 0..4 {
        let i = 3 - i;
        if value.0[i] == 0u64 {
            l -= 64;
        } else {
            l -= value.0[i].leading_zeros() as u64;
            if l == 0 {
                return l;
            } else {
                return l - 1;
            }
        }
    }
    l
}

pub(crate) fn exp(state: &mut ExecutionState) -> Result<(), StatusCode> {
    let mut base = state.stack.pop();
    let mut power = state.stack.pop();

    if !power.is_zero() {
        let additional_gas = if state.evm_revision >= Revision::Spurious {
            50
        } else {
            10
        } * (log2floor(power) / 8 + 1);

        state.gas_left -= additional_gas as i64;

        if state.gas_left < 0 {
            return Err(StatusCode::OutOfGas);
        }
    }

    let mut v = U256::one();

    while !power.is_zero() {
        if !(power & U256::one()).is_zero() {
            v = v.overflowing_mul(base).0;
        }
        power >>= 1;
        base = base.overflowing_mul(base).0;
    }

    state.stack.push(v);

    Ok(())
}

pub(crate) fn signextend(stack: &mut Stack) {
    let a = stack.pop();
    let b = stack.pop();

    let v = if a < U256::from(32) {
        // `low_u32` works since op1 < 32
        let bit_index = (8 * a.low_u32() + 7) as usize;
        let bit = b.bit(bit_index);
        let mask = (U256::one() << bit_index) - U256::one();
        if bit {
            b | !mask
        } else {
            b & mask
        }
    } else {
        b
    };

    stack.push(v);
}


================================================
FILE: src/instructions/bitwise.rs
================================================
use crate::state::Stack;
use ethereum_types::U256;
use i256::{Sign, I256};

pub(crate) fn byte(stack: &mut Stack) {
    let a = stack.pop();
    let b = stack.pop();

    let mut ret = U256::zero();

    for i in 0..256 {
        if i < 8 && a < 32.into() {
            let o: usize = a.as_usize();
            let t = 255 - (7 - i + 8 * o);
            let bit_mask = U256::one() << t;
            let value = (b & bit_mask) >> t;
            ret = ret.overflowing_add(value << i).0;
        }
    }

    stack.push(ret)
}

pub(crate) fn shl(stack: &mut Stack) {
    let shift = stack.pop();
    let value = stack.pop();

    let ret = if value.is_zero() || shift >= U256::from(256) {
        U256::zero()
    } else {
        value << shift.as_usize()
    };

    stack.push(ret)
}

pub(crate) fn shr(stack: &mut Stack) {
    let shift = stack.pop();
    let value = stack.pop();

    let ret = if value.is_zero() || shift >= U256::from(256) {
        U256::zero()
    } else {
        value >> shift.as_usize()
    };

    stack.push(ret)
}

pub(crate) fn sar(stack: &mut Stack) {
    let shift = stack.pop();
    let value = I256::from(stack.pop());

    let ret = if value == I256::zero() || shift >= U256::from(256) {
        match value.0 {
            // value is 0 or >=1, pushing 0
            Sign::Plus | Sign::NoSign => U256::zero(),
            // value is <0, pushing -1
            Sign::Minus => I256(Sign::Minus, U256::one()).into(),
        }
    } else {
        let shift = shift.as_usize();

        match value.0 {
            Sign::Plus | Sign::NoSign => value.1 >> shift,
            Sign::Minus => {
                let shifted = ((value.1.overflowing_sub(U256::one()).0) >> shift)
                    .overflowing_add(U256::one())
                    .0;
                I256(Sign::Minus, shifted).into()
            }
        }
    };

    stack.push(ret)
}


================================================
FILE: src/instructions/boolean.rs
================================================
use crate::state::*;
use ethereum_types::U256;
use i256::I256;

pub(crate) fn lt(stack: &mut Stack) {
    let a = stack.pop();
    let b = stack.pop();

    stack.push(if a.lt(&b) { U256::one() } else { U256::zero() })
}

pub(crate) fn gt(stack: &mut Stack) {
    let a = stack.pop();
    let b = stack.pop();

    stack.push(if a.gt(&b) { U256::one() } else { U256::zero() })
}

pub(crate) fn slt(stack: &mut Stack) {
    let a: I256 = stack.pop().into();
    let b: I256 = stack.pop().into();

    stack.push(if a.lt(&b) { U256::one() } else { U256::zero() })
}

pub(crate) fn sgt(stack: &mut Stack) {
    let a: I256 = stack.pop().into();
    let b: I256 = stack.pop().into();

    stack.push(if a.gt(&b) { U256::one() } else { U256::zero() })
}

pub(crate) fn eq(stack: &mut Stack) {
    let a = stack.pop();
    let b = stack.pop();

    stack.push(if a.eq(&b) { U256::one() } else { U256::zero() })
}

pub(crate) fn iszero(stack: &mut Stack) {
    let a = stack.pop();
    stack.push(if a.is_zero() {
        U256::one()
    } else {
        U256::zero()
    })
}

pub(crate) fn and(stack: &mut Stack) {
    let a = stack.pop();
    let b = stack.pop();
    stack.push(a & b);
}

pub(crate) fn or(stack: &mut Stack) {
    let a = stack.pop();
    let b = stack.pop();
    stack.push(a | b);
}

pub(crate) fn xor(stack: &mut Stack) {
    let a = stack.pop();
    let b = stack.pop();
    stack.push(a ^ b);
}

pub(crate) fn not(stack: &mut Stack) {
    let a = stack.get_mut(0);
    *a = !*a;
}


================================================
FILE: src/instructions/call.rs
================================================
#[doc(hidden)]
#[macro_export]
macro_rules! do_call {
    ($co:expr, $state:expr, $kind:expr, $is_static:expr) => {{
        use std::cmp::min;
        use $crate::{
            common::u256_to_address,
            continuation::{interrupt_data::*, resume_data::*},
            host::AccessStatus,
            instructions::{memory::MemoryRegion, properties::*},
            CallKind, Message,
        };

        let gas = $state.stack.pop();
        let dst = u256_to_address($state.stack.pop());
        let value = if $is_static || matches!($kind, CallKind::DelegateCall) {
            U256::zero()
        } else {
            $state.stack.pop()
        };
        let has_value = !value.is_zero();
        let input_offset = $state.stack.pop();
        let input_size = $state.stack.pop();
        let output_offset = $state.stack.pop();
        let output_size = $state.stack.pop();

        $state.stack.push(U256::zero()); // Assume failure.

        if $state.evm_revision >= Revision::Berlin
            && ResumeDataVariant::into_access_account_status(
                $co.yield_(InterruptDataVariant::AccessAccount(AccessAccount {
                    address: dst,
                }))
                .await,
            )
            .unwrap()
            .status
                == AccessStatus::Cold
        {
            $state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
            if $state.gas_left < 0 {
                return Err(StatusCode::OutOfGas);
            }
        }

        let input_region = memory::verify_memory_region($state, input_offset, input_size)
            .map_err(|_| StatusCode::OutOfGas)?;
        let output_region = memory::verify_memory_region($state, output_offset, output_size)
            .map_err(|_| StatusCode::OutOfGas)?;

        let mut msg = Message {
            kind: $kind,
            is_static: $is_static || $state.message.is_static,
            depth: $state.message.depth + 1,
            recipient: if matches!($kind, CallKind::Call) {
                dst
            } else {
                $state.message.recipient
            },
            code_address: dst,
            sender: if matches!($kind, CallKind::DelegateCall) {
                $state.message.sender
            } else {
                $state.message.recipient
            },
            gas: i64::MAX,
            value: if matches!($kind, CallKind::DelegateCall) {
                $state.message.value
            } else {
                value
            },
            input_data: input_region
                .map(|MemoryRegion { offset, size }| {
                    $state.memory[offset..offset + size.get()].to_vec().into()
                })
                .unwrap_or_default(),
        };

        let mut cost = if has_value { 9000 } else { 0 };

        if matches!($kind, CallKind::Call) {
            if has_value && $state.message.is_static {
                return Err(StatusCode::StaticModeViolation);
            }

            if (has_value || $state.evm_revision < Revision::Spurious)
                && !ResumeDataVariant::into_account_exists_status(
                    $co.yield_(InterruptDataVariant::AccountExists(AccountExists {
                        address: dst,
                    }))
                    .await,
                )
                .unwrap()
                .exists
            {
                cost += 25000;
            }
        }
        $state.gas_left -= cost;
        if $state.gas_left < 0 {
            return Err(StatusCode::OutOfGas);
        }

        if gas < msg.gas.into() {
            msg.gas = gas.as_usize() as i64;
        }

        if $state.evm_revision >= Revision::Tangerine {
            // TODO: Always true for STATICCALL.
            msg.gas = min(msg.gas, $state.gas_left - $state.gas_left / 64);
        } else if msg.gas > $state.gas_left {
            return Err(StatusCode::OutOfGas);
        }

        if has_value {
            msg.gas += 2300; // Add stipend.
            $state.gas_left += 2300;
        }

        $state.return_data.clear();

        if $state.message.depth < 1024
            && !(has_value
                && ResumeDataVariant::into_balance(
                    $co.yield_(InterruptDataVariant::GetBalance(GetBalance {
                        address: $state.message.recipient,
                    }))
                    .await,
                )
                .unwrap()
                .balance
                    < value)
        {
            let msg_gas = msg.gas;
            let result = ResumeDataVariant::into_call_output(
                $co.yield_(InterruptDataVariant::Call(Call::Call(msg)))
                    .await,
            )
            .unwrap()
            .output;
            $state.return_data = result.output_data.clone();
            *$state.stack.get_mut(0) = if matches!(result.status_code, StatusCode::Success) {
                U256::one()
            } else {
                U256::zero()
            };

            if let Some(MemoryRegion { offset, size }) = output_region {
                let copy_size = min(size.get(), result.output_data.len());
                if copy_size > 0 {
                    $state.memory[offset..offset + copy_size]
                        .copy_from_slice(&result.output_data[..copy_size]);
                }
            }

            let gas_used = msg_gas - result.gas_left;
            $state.gas_left -= gas_used;
        }
    }};
}

#[doc(hidden)]
#[macro_export]
macro_rules! do_create {
    ($co:expr, $state:expr, $create2:expr) => {{
        use ethereum_types::U256;
        use $crate::{
            common::*,
            continuation::{interrupt_data::*, resume_data::*},
            CreateMessage,
        };

        if $state.message.is_static {
            return Err(StatusCode::StaticModeViolation);
        }

        let endowment = $state.stack.pop();
        let init_code_offset = $state.stack.pop();
        let init_code_size = $state.stack.pop();

        let region = memory::verify_memory_region($state, init_code_offset, init_code_size)
            .map_err(|_| StatusCode::OutOfGas)?;

        let salt = if $create2 {
            let salt = $state.stack.pop();

            if let Some(region) = &region {
                let salt_cost = memory::num_words(region.size.get()) * 6;
                $state.gas_left -= salt_cost;
                if $state.gas_left < 0 {
                    return Err(StatusCode::OutOfGas);
                }
            }

            Some(salt)
        } else {
            None
        };

        $state.stack.push(U256::zero());
        $state.return_data.clear();

        if $state.message.depth < 1024
            && !(!endowment.is_zero()
                && ResumeDataVariant::into_balance(
                    $co.yield_(InterruptDataVariant::GetBalance(GetBalance {
                        address: $state.message.recipient,
                    }))
                    .await,
                )
                .unwrap()
                .balance
                    < endowment)
        {
            let msg = CreateMessage {
                gas: if $state.evm_revision >= Revision::Tangerine {
                    $state.gas_left - $state.gas_left / 64
                } else {
                    $state.gas_left
                },

                salt,
                initcode: if !init_code_size.is_zero() {
                    $state.memory[init_code_offset.as_usize()
                        ..init_code_offset.as_usize() + init_code_size.as_usize()]
                        .to_vec()
                        .into()
                } else {
                    Bytes::new()
                },
                sender: $state.message.recipient,
                depth: $state.message.depth + 1,
                endowment,
            };
            let msg_gas = msg.gas;
            let result = ResumeDataVariant::into_call_output(
                $co.yield_(InterruptDataVariant::Call(Call::Create(msg)))
                    .await,
            )
            .unwrap()
            .output;
            $state.gas_left -= msg_gas - result.gas_left;

            $state.return_data = result.output_data;
            if result.status_code == StatusCode::Success {
                *$state.stack.get_mut(0) =
                    address_to_u256(result.create_address.expect("expected create address"));
            }
        }
    }};
}


================================================
FILE: src/instructions/control.rs
================================================
use crate::state::ExecutionState;
use crate::{interpreter::JumpdestMap, StatusCode};
use ethereum_types::U256;

pub(crate) fn ret(state: &mut ExecutionState) -> Result<(), StatusCode> {
    let offset = *state.stack.get(0);
    let size = *state.stack.get(1);

    if let Some(region) = super::memory::verify_memory_region(state, offset, size)
        .map_err(|_| StatusCode::OutOfGas)?
    {
        state.output_data = state.memory[region.offset..region.offset + region.size.get()]
            .to_vec()
            .into();
    }

    Ok(())
}

pub(crate) fn op_jump(
    state: &mut ExecutionState,
    jumpdest_map: &JumpdestMap,
) -> Result<usize, StatusCode> {
    let dst = state.stack.pop();
    if !jumpdest_map.contains(dst) {
        return Err(StatusCode::BadJumpDestination);
    }

    Ok(dst.as_usize())
}

pub(crate) fn calldataload(state: &mut ExecutionState) {
    let index = state.stack.pop();

    let input_len = state.message.input_data.len();

    state.stack.push({
        if index > U256::from(input_len) {
            U256::zero()
        } else {
            let index_usize = index.as_usize();
            let end = core::cmp::min(index_usize + 32, input_len);

            let mut data = [0; 32];
            data[..end - index_usize].copy_from_slice(&state.message.input_data[index_usize..end]);

            data.into()
        }
    });
}

pub(crate) fn calldatasize(state: &mut ExecutionState) {
    state.stack.push(state.message.input_data.len().into());
}


================================================
FILE: src/instructions/external.rs
================================================
use crate::{common::address_to_u256, host::*, state::ExecutionState};
use ethereum_types::U256;

pub(crate) fn address(state: &mut ExecutionState) {
    state.stack.push(address_to_u256(state.message.recipient));
}

pub(crate) fn caller(state: &mut ExecutionState) {
    state.stack.push(address_to_u256(state.message.sender));
}

pub(crate) fn callvalue(state: &mut ExecutionState) {
    state.stack.push(state.message.value);
}

#[doc(hidden)]
#[macro_export]
macro_rules! balance {
    ($co:expr, $state:expr) => {
        use crate::{
            common::*,
            continuation::{interrupt_data::*, resume_data::*},
            host::*,
            instructions::properties::*,
        };

        let address = u256_to_address($state.stack.pop());

        if $state.evm_revision >= Revision::Berlin {
            let access_status = ResumeDataVariant::into_access_account_status(
                $co.yield_(InterruptDataVariant::AccessAccount(AccessAccount {
                    address,
                }))
                .await,
            )
            .unwrap()
            .status;
            if access_status == AccessStatus::Cold {
                $state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
                if $state.gas_left < 0 {
                    return Err(StatusCode::OutOfGas);
                }
            }
        }

        let balance = ResumeDataVariant::into_balance(
            $co.yield_(InterruptDataVariant::GetBalance(GetBalance { address }))
                .await,
        )
        .unwrap()
        .balance;

        $state.stack.push(balance);
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! extcodesize {
    ($co:expr, $state:expr) => {
        use crate::{
            common::*,
            continuation::{interrupt_data::*, resume_data::*},
            host::*,
            instructions::properties::*,
        };

        let address = u256_to_address($state.stack.pop());

        if $state.evm_revision >= Revision::Berlin {
            let access_account = ResumeDataVariant::into_access_account_status(
                $co.yield_(InterruptDataVariant::AccessAccount(AccessAccount {
                    address,
                }))
                .await,
            )
            .unwrap()
            .status;
            if access_account == AccessStatus::Cold {
                $state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
                if $state.gas_left < 0 {
                    return Err(StatusCode::OutOfGas);
                }
            }
        }

        let code_size = ResumeDataVariant::into_code_size(
            $co.yield_(InterruptDataVariant::GetCodeSize(GetCodeSize { address }))
                .await,
        )
        .unwrap()
        .code_size;
        $state.stack.push(code_size);
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! push_txcontext {
    ($co:expr, $state:expr, $accessor:expr) => {
        use $crate::continuation::{interrupt_data::*, resume_data::*};

        let tx_context = ResumeDataVariant::into_tx_context_data(
            $co.yield_(InterruptDataVariant::GetTxContext).await,
        )
        .unwrap()
        .context;

        $state.stack.push($accessor(tx_context));
    };
}

pub(crate) fn origin_accessor(tx_context: TxContext) -> U256 {
    address_to_u256(tx_context.tx_origin)
}

pub(crate) fn coinbase_accessor(tx_context: TxContext) -> U256 {
    address_to_u256(tx_context.block_coinbase)
}

pub(crate) fn gasprice_accessor(tx_context: TxContext) -> U256 {
    tx_context.tx_gas_price
}

pub(crate) fn timestamp_accessor(tx_context: TxContext) -> U256 {
    tx_context.block_timestamp.into()
}

pub(crate) fn number_accessor(tx_context: TxContext) -> U256 {
    tx_context.block_number.into()
}

pub(crate) fn gaslimit_accessor(tx_context: TxContext) -> U256 {
    tx_context.block_gas_limit.into()
}

pub(crate) fn difficulty_accessor(tx_context: TxContext) -> U256 {
    tx_context.block_difficulty
}

pub(crate) fn chainid_accessor(tx_context: TxContext) -> U256 {
    tx_context.chain_id
}

pub(crate) fn basefee_accessor(tx_context: TxContext) -> U256 {
    tx_context.block_base_fee
}

#[doc(hidden)]
#[macro_export]
macro_rules! selfbalance {
    ($co:expr, $state:expr) => {{
        use $crate::continuation::{interrupt_data::*, resume_data::*};

        let balance = ResumeDataVariant::into_balance(
            $co.yield_(InterruptDataVariant::GetBalance(GetBalance {
                address: $state.message.recipient,
            }))
            .await,
        )
        .unwrap()
        .balance;

        $state.stack.push(balance);
    }};
}

#[doc(hidden)]
#[macro_export]
macro_rules! blockhash {
    ($co:expr, $state:expr) => {
        use $crate::continuation::{interrupt_data::*, resume_data::*};

        let number = $state.stack.pop();

        let upper_bound = ResumeDataVariant::into_tx_context_data(
            $co.yield_(InterruptDataVariant::GetTxContext).await,
        )
        .unwrap()
        .context
        .block_number;
        let lower_bound = upper_bound.saturating_sub(256);

        let mut header = U256::zero();
        if number <= u64::MAX.into() {
            let n = number.as_u64();
            if (lower_bound..upper_bound).contains(&n) {
                header = ResumeDataVariant::into_block_hash(
                    $co.yield_(InterruptDataVariant::GetBlockHash(GetBlockHash {
                        block_number: n,
                    }))
                    .await,
                )
                .unwrap()
                .hash;
            }
        }

        $state.stack.push(header);
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! do_log {
    ($co:expr, $state:expr, $num_topics:expr) => {{
        use arrayvec::ArrayVec;
        use $crate::continuation::{interrupt_data::*, resume_data::*};

        if $state.message.is_static {
            return Err(StatusCode::StaticModeViolation);
        }

        let offset = $state.stack.pop();
        let size = $state.stack.pop();

        let region =
            memory::verify_memory_region($state, offset, size).map_err(|_| StatusCode::OutOfGas)?;

        if let Some(region) = &region {
            let cost = region.size.get() as i64 * 8;
            $state.gas_left -= cost;
            if cost < 0 {
                return Err(StatusCode::OutOfGas);
            }
        }

        let mut topics = ArrayVec::new();
        for _ in 0..$num_topics {
            topics.push($state.stack.pop());
        }

        let data = if let Some(region) = region {
            &$state.memory[region.offset..region.offset + region.size.get()]
        } else {
            &[]
        };
        let r = $co
            .yield_(InterruptDataVariant::EmitLog(EmitLog {
                address: $state.message.recipient,
                data: data.to_vec().into(),
                topics,
            }))
            .await;

        assert!(matches!(r, ResumeDataVariant::Empty));
    }};
}

#[doc(hidden)]
#[macro_export]
macro_rules! sload {
    ($co:expr, $state:expr) => {{
        use $crate::{
            continuation::{interrupt_data::*, resume_data::*},
            host::*,
            instructions::properties::{COLD_SLOAD_COST, WARM_STORAGE_READ_COST},
        };

        let key = $state.stack.pop();

        if $state.evm_revision >= Revision::Berlin {
            let access_status = ResumeDataVariant::into_access_storage_status(
                $co.yield_(InterruptDataVariant::AccessStorage(AccessStorage {
                    address: $state.message.recipient,
                    key,
                }))
                .await,
            )
            .unwrap()
            .status;
            if access_status == AccessStatus::Cold {
                // The warm storage access cost is already applied (from the cost table).
                // Here we need to apply additional cold storage access cost.
                const ADDITIONAL_COLD_SLOAD_COST: u16 = COLD_SLOAD_COST - WARM_STORAGE_READ_COST;
                $state.gas_left -= i64::from(ADDITIONAL_COLD_SLOAD_COST);
                if $state.gas_left < 0 {
                    return Err(StatusCode::OutOfGas);
                }
            }
        }

        let storage = ResumeDataVariant::into_storage_value(
            $co.yield_(InterruptDataVariant::GetStorage(GetStorage {
                address: $state.message.recipient,
                key,
            }))
            .await,
        )
        .unwrap()
        .value;

        $state.stack.push(storage);
    }};
}

#[doc(hidden)]
#[macro_export]
macro_rules! sstore {
    ($co:expr, $state:expr) => {{
        use $crate::{
            continuation::{interrupt_data::*, resume_data::*},
            host::*,
            instructions::properties::{COLD_SLOAD_COST, WARM_STORAGE_READ_COST},
        };

        if $state.message.is_static {
            return Err(StatusCode::StaticModeViolation);
        }

        if $state.evm_revision >= Revision::Istanbul && $state.gas_left <= 2300 {
            return Err(StatusCode::OutOfGas);
        }

        let key = $state.stack.pop();
        let value = $state.stack.pop();

        let mut cost = 0;
        if $state.evm_revision >= Revision::Berlin {
            let access_status = ResumeDataVariant::into_access_storage_status(
                $co.yield_(InterruptDataVariant::AccessStorage(AccessStorage {
                    address: $state.message.recipient,
                    key,
                }))
                .await,
            )
            .unwrap()
            .status;

            if access_status == AccessStatus::Cold {
                cost = COLD_SLOAD_COST;
            }
        }

        let status = ResumeDataVariant::into_storage_status_info(
            $co.yield_(InterruptDataVariant::SetStorage(SetStorage {
                address: $state.message.recipient,
                key,
                value,
            }))
            .await,
        )
        .unwrap()
        .status;

        cost = match status {
            StorageStatus::Unchanged | StorageStatus::ModifiedAgain => {
                if $state.evm_revision >= Revision::Berlin {
                    cost + WARM_STORAGE_READ_COST
                } else if $state.evm_revision == Revision::Istanbul {
                    800
                } else if $state.evm_revision == Revision::Constantinople {
                    200
                } else {
                    5000
                }
            }
            StorageStatus::Modified | StorageStatus::Deleted => {
                if $state.evm_revision >= Revision::Berlin {
                    cost + 5000 - COLD_SLOAD_COST
                } else {
                    5000
                }
            }
            StorageStatus::Added => cost + 20000,
        };
        $state.gas_left -= i64::from(cost);
        if $state.gas_left < 0 {
            return Err(StatusCode::OutOfGas);
        }
    }};
}

#[doc(hidden)]
#[macro_export]
macro_rules! selfdestruct {
    ($co:expr, $state:expr) => {{
        use crate::{
            common::*,
            continuation::{interrupt_data::*, resume_data::*},
            host::*,
            instructions::properties::*,
        };

        if $state.message.is_static {
            return Err(StatusCode::StaticModeViolation);
        }

        let beneficiary = u256_to_address($state.stack.pop());

        if $state.evm_revision >= Revision::Berlin {
            let access_status = ResumeDataVariant::into_access_account_status(
                $co.yield_(InterruptDataVariant::AccessAccount(AccessAccount {
                    address: beneficiary,
                }))
                .await,
            )
            .unwrap()
            .status;
            if access_status == AccessStatus::Cold {
                $state.gas_left -= i64::from(COLD_ACCOUNT_ACCESS_COST);
                if $state.gas_left < 0 {
                    return Err(StatusCode::OutOfGas);
                }
            }
        }

        if $state.evm_revision >= Revision::Tangerine
            && ($state.evm_revision == Revision::Tangerine
                || !{
                    ResumeDataVariant::into_balance(
                        $co.yield_(InterruptDataVariant::GetBalance(GetBalance {
                            address: $state.message.recipient,
                        }))
                        .await,
                    )
                    .unwrap()
                    .balance
                    .is_zero()
                })
        {
            // After TANGERINE_WHISTLE apply additional cost of
            // sending value to a non-existing account.
            if !ResumeDataVariant::into_account_exists_status(
                $co.yield_(InterruptDataVariant::AccountExists(AccountExists {
                    address: beneficiary,
                }))
                .await,
            )
            .unwrap()
            .exists
            {
                $state.gas_left -= 25000;
                if $state.gas_left < 0 {
                    return Err(StatusCode::OutOfGas);
                }
            }
        }

        assert!(matches!(
            $co.yield_(InterruptDataVariant::Selfdestruct(Selfdestruct {
                address: $state.message.recipient,
                beneficiary,
            }))
            .await,
            ResumeDataVariant::Empty
        ));
    }};
}

#[cfg(test)]
mod tests {
    use crate::common::u256_to_address;
    use ethereum_types::Address;
    use hex_literal::hex;

    #[test]
    fn u256_to_address_conversion() {
        assert_eq!(
            u256_to_address(0x42.into()),
            Address::from(hex!("0000000000000000000000000000000000000042"))
        );
    }
}


================================================
FILE: src/instructions/instruction_table.rs
================================================
use crate::{instructions::properties, Revision};
use once_cell::sync::Lazy;

#[derive(Clone, Copy, Debug)]
pub struct InstructionTableEntry {
    pub gas_cost: u16,
    pub stack_height_required: u8,
    pub can_overflow_stack: bool,
}

pub type InstructionTable = [Option<InstructionTableEntry>; 256];
pub type InstructionTables = [InstructionTable; Revision::len()];

pub static INSTRUCTION_TABLES: Lazy<InstructionTables> = Lazy::new(|| {
    let mut table = [[None; 256]; Revision::len()];

    for revision in Revision::iter() {
        for (opcode, &cost) in properties::gas_costs(revision).iter().enumerate() {
            if let Some(cost) = cost {
                let stack_height_required = properties::PROPERTIES[opcode]
                    .unwrap()
                    .stack_height_required;

                // Because any instruction can increase stack height at most of 1,
                // stack overflow can only happen if stack height is already at the limit.
                assert!(properties::PROPERTIES[opcode].unwrap().stack_height_change <= 1);

                table[revision as usize][opcode] = Some(InstructionTableEntry {
                    gas_cost: cost,
                    stack_height_required,
                    can_overflow_stack: properties::PROPERTIES[opcode].unwrap().stack_height_change
                        > 0,
                });
            }
        }
    }
    table
});

pub fn get_baseline_instruction_table(revision: Revision) -> &'static InstructionTable {
    &INSTRUCTION_TABLES[revision as usize]
}


================================================
FILE: src/instructions/memory.rs
================================================
use crate::{common::*, state::*};
use ethereum_types::U256;
use sha3::{Digest, Keccak256};
use std::{cmp::min, num::NonZeroUsize};

pub(crate) const MAX_BUFFER_SIZE: u32 = u32::MAX;

/// The size of the EVM 256-bit word.
const WORD_SIZE: i64 = 32;

/// Returns number of words what would fit to provided number of bytes,
/// i.e. it rounds up the number bytes to number of words.
pub(crate) fn num_words(size_in_bytes: usize) -> i64 {
    ((size_in_bytes as i64) + (WORD_SIZE - 1)) / WORD_SIZE
}

pub(crate) fn mload(state: &mut ExecutionState) -> Result<(), StatusCode> {
    let index = state.stack.pop();

    let region = verify_memory_region_u64(state, index, NonZeroUsize::new(32).unwrap())
        .map_err(|_| StatusCode::OutOfGas)?;

    let value =
        U256::from_big_endian(&state.memory[region.offset..region.offset + region.size.get()]);

    state.stack.push(value);

    Ok(())
}

pub(crate) fn mstore(state: &mut ExecutionState) -> Result<(), StatusCode> {
    let index = state.stack.pop();
    let value = state.stack.pop();

    let region = verify_memory_region_u64(state, index, NonZeroUsize::new(32).unwrap())
        .map_err(|_| StatusCode::OutOfGas)?;

    let mut b = [0; 32];
    value.to_big_endian(&mut b);
    state.memory[region.offset..region.offset + 32].copy_from_slice(&b);

    Ok(())
}

pub(crate) fn mstore8(state: &mut ExecutionState) -> Result<(), StatusCode> {
    let index = state.stack.pop();
    let value = state.stack.pop();

    let region = verify_memory_region_u64(state, index, NonZeroUsize::new(1).unwrap())
        .map_err(|_| StatusCode::OutOfGas)?;

    let value = (value.low_u32() & 0xff) as u8;

    state.memory[region.offset] = value;

    Ok(())
}

pub(crate) fn msize(state: &mut ExecutionState) {
    state.stack.push(state.memory.len().into());
}

pub(crate) fn verify_memory_region_u64(
    state: &mut ExecutionState,
    offset: U256,
    size: NonZeroUsize,
) -> Result<MemoryRegion, ()> {
    if offset > U256::from(MAX_BUFFER_SIZE) {
        return Err(());
    }

    let new_size = offset.as_usize() + size.get();
    let current_size = state.memory.len();
    if new_size > current_size {
        let new_words = num_words(new_size);
        let current_words = (current_size / 32) as i64;
        let new_cost = 3 * new_words + new_words * new_words / 512;
        let current_cost = 3 * current_words + current_words * current_words / 512;
        let cost = new_cost - current_cost;

        state.gas_left -= cost;

        if state.gas_left < 0 {
            return Err(());
        }

        state
            .memory
            .resize((new_words * WORD_SIZE) as usize, Default::default());
    }

    Ok(MemoryRegion {
        offset: offset.as_usize(),
        size,
    })
}

pub(crate) struct MemoryRegion {
    pub offset: usize,
    pub size: NonZeroUsize,
}

pub(crate) fn verify_memory_region(
    state: &mut ExecutionState,
    offset: U256,
    size: U256,
) -> Result<Option<MemoryRegion>, ()> {
    if size.is_zero() {
        return Ok(None);
    }

    if size > U256::from(MAX_BUFFER_SIZE) {
        return Err(());
    }

    verify_memory_region_u64(state, offset, NonZeroUsize::new(size.as_usize()).unwrap()).map(Some)
}

pub(crate) fn calldatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> {
    let mem_index = state.stack.pop();
    let input_index = state.stack.pop();
    let size = state.stack.pop();

    let region = verify_memory_region(state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?;

    if let Some(region) = &region {
        let copy_cost = num_words(region.size.get()) * 3;
        state.gas_left -= copy_cost;
        if state.gas_left < 0 {
            return Err(StatusCode::OutOfGas);
        }

        let input_len = state.message.input_data.len().into();

        let src = core::cmp::min(input_len, input_index).as_usize();
        let copy_size = core::cmp::min(size, input_len - src).as_usize();

        if copy_size > 0 {
            state.memory[region.offset..region.offset + copy_size]
                .copy_from_slice(&state.message.input_data[src..src + copy_size]);
        }

        if region.size.get() - copy_size > 0 {
            state.memory[region.offset + copy_size..region.offset + region.size.get()].fill(0);
        }
    }

    Ok(())
}

pub(crate) fn keccak256(state: &mut ExecutionState) -> Result<(), StatusCode> {
    let index = state.stack.pop();
    let size = state.stack.pop();

    let region = verify_memory_region(state, index, size).map_err(|_| StatusCode::OutOfGas)?;

    state.stack.push(U256::from_big_endian(&*Keccak256::digest(
        if let Some(region) = region {
            let w = num_words(region.size.get());
            let cost = w * 6;
            state.gas_left -= cost;
            if state.gas_left < 0 {
                return Err(StatusCode::OutOfGas);
            }

            &state.memory[region.offset..region.offset + region.size.get()]
        } else {
            &[]
        },
    )));

    Ok(())
}

pub(crate) fn codesize(stack: &mut Stack, code: &[u8]) {
    stack.push(code.len().into())
}

pub(crate) fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCode> {
    // TODO: Similar to calldatacopy().

    let mem_index = state.stack.pop();
    let input_index = state.stack.pop();
    let size = state.stack.pop();

    let region = verify_memory_region(state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?;

    if let Some(region) = region {
        let src = min(U256::from(code.len()), input_index).as_usize();
        let copy_size = min(region.size.get(), code.len() - src);

        let copy_cost = num_words(region.size.get()) * 3;
        state.gas_left -= copy_cost;
        if state.gas_left < 0 {
            return Err(StatusCode::OutOfGas);
        }

        // TODO: Add unit tests for each combination of conditions.
        if copy_size > 0 {
            state.memory[region.offset..region.offset + copy_size]
                .copy_from_slice(&code[src..src + copy_size]);
        }

        if region.size.get() - copy_size > 0 {
            state.memory[region.offset + copy_size..region.offset + region.size.get()].fill(0);
        }
    }

    Ok(())
}

#[doc(hidden)]
#[macro_export]
macro_rules! extcodecopy {
    ($co:expr, $state:expr) => {
        use crate::{
            common::*,
            continuation::{interrupt_data::*, resume_data::*},
            host::*,
            instructions::{memory::*, properties::*},
        };
        use core::cmp::min;

        let addr = u256_to_address($state.stack.pop());
        let mem_index = $state.stack.pop();
        let input_index = $state.stack.pop();
        let size = $state.stack.pop();

        let region =
            verify_memory_region($state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?;

        if let Some(region) = &region {
            let copy_cost = num_words(region.size.get()) * 3;
            $state.gas_left -= copy_cost;
            if $state.gas_left < 0 {
                return Err(StatusCode::OutOfGas);
            }
        }

        if $state.evm_revision >= Revision::Berlin
            && ResumeDataVariant::into_access_account_status(
                $co.yield_(InterruptDataVariant::AccessAccount(AccessAccount {
                    address: addr,
                }))
                .await,
            )
            .unwrap()
            .status
                == AccessStatus::Cold
        {
            $state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
            if $state.gas_left < 0 {
                return Err(StatusCode::OutOfGas);
            }
        }

        if let Some(region) = region {
            let src = min(U256::from(MAX_BUFFER_SIZE), input_index).as_usize();

            let r = &mut $state.memory[region.offset..region.offset + region.size.get()];
            let code = ResumeDataVariant::into_code(
                $co.yield_(InterruptDataVariant::CopyCode(CopyCode {
                    address: addr,
                    offset: src,
                    max_size: r.len(),
                }))
                .await,
            )
            .unwrap()
            .code;

            r[..code.len()].copy_from_slice(&code);
            if region.size.get() - code.len() > 0 {
                $state.memory[region.offset + code.len()..region.offset + region.size.get()]
                    .fill(0);
            }
        }
    };
}

pub(crate) fn returndatasize(state: &mut ExecutionState) {
    state.stack.push(state.return_data.len().into());
}

pub(crate) fn returndatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> {
    let mem_index = state.stack.pop();
    let input_index = state.stack.pop();
    let size = state.stack.pop();

    let region = verify_memory_region(state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?;

    if input_index > U256::from(state.return_data.len()) {
        return Err(StatusCode::InvalidMemoryAccess);
    }
    let src = input_index.as_usize();

    if src + region.as_ref().map(|r| r.size.get()).unwrap_or(0) > state.return_data.len() {
        return Err(StatusCode::InvalidMemoryAccess);
    }

    if let Some(region) = region {
        let copy_cost = num_words(region.size.get()) * 3;
        state.gas_left -= copy_cost;
        if state.gas_left < 0 {
            return Err(StatusCode::OutOfGas);
        }

        state.memory[region.offset..region.offset + region.size.get()]
            .copy_from_slice(&state.return_data[src..src + region.size.get()]);
    }

    Ok(())
}

#[doc(hidden)]
#[macro_export]
macro_rules! extcodehash {
    ($co:expr, $state:expr) => {
        use crate::{
            common::*,
            continuation::{interrupt_data::*, resume_data::*},
            host::*,
            instructions::properties::*,
        };

        let addr = u256_to_address($state.stack.pop());

        if $state.evm_revision >= Revision::Berlin
            && ResumeDataVariant::into_access_account_status(
                $co.yield_(InterruptDataVariant::AccessAccount(AccessAccount {
                    address: addr,
                }))
                .await,
            )
            .unwrap()
            .status
                == AccessStatus::Cold
        {
            $state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
            if $state.gas_left < 0 {
                return Err(StatusCode::OutOfGas);
            }
        }

        $state.stack.push(
            ResumeDataVariant::into_code_hash(
                $co.yield_(InterruptDataVariant::GetCodeHash(GetCodeHash {
                    address: addr,
                }))
                .await,
            )
            .unwrap()
            .hash,
        );
    };
}


================================================
FILE: src/instructions/mod.rs
================================================
pub(crate) mod arithmetic;
pub(crate) mod bitwise;
pub(crate) mod boolean;
pub(crate) mod call;
pub(crate) mod control;
pub(crate) mod external;
pub(crate) mod instruction_table;
pub(crate) mod memory;
pub(crate) mod properties;
pub(crate) mod stack_manip;

pub use properties::PROPERTIES;


================================================
FILE: src/instructions/properties.rs
================================================
use once_cell::sync::Lazy;

use crate::{common::Revision, opcode::*};

pub(crate) const COLD_SLOAD_COST: u16 = 2100;
pub(crate) const COLD_ACCOUNT_ACCESS_COST: u16 = 2600;
pub(crate) const WARM_STORAGE_READ_COST: u16 = 100;

/// Additional cold account access cost.
///
/// The warm access cost is unconditionally applied for every account access instruction.
/// If the access turns out to be cold, this cost must be applied additionally.
pub(crate) const ADDITIONAL_COLD_ACCOUNT_ACCESS_COST: u16 =
    COLD_ACCOUNT_ACCESS_COST - WARM_STORAGE_READ_COST;

/// EVM instruction properties
#[derive(Clone, Copy, Debug)]
pub struct Properties {
    /// The instruction name
    pub name: &'static str,
    /// The number of stack items the instruction accesses during execution.
    pub stack_height_required: u8,
    /// The stack height change caused by the instruction execution. Can be negative.
    pub stack_height_change: i8,
}

impl Properties {
    fn new(name: &'static str, stack_height_required: u8, stack_height_change: i8) -> Self {
        Self {
            name,
            stack_height_required,
            stack_height_change,
        }
    }
}

pub static PROPERTIES: Lazy<[Option<Properties>; 256]> = Lazy::new(|| {
    let mut table = [None; 256];

    table[OpCode::STOP.to_usize()] = Some(Properties::new("STOP", 0, 0));
    table[OpCode::ADD.to_usize()] = Some(Properties::new("ADD", 2, -1));
    table[OpCode::MUL.to_usize()] = Some(Properties::new("MUL", 2, -1));
    table[OpCode::SUB.to_usize()] = Some(Properties::new("SUB", 2, -1));
    table[OpCode::DIV.to_usize()] = Some(Properties::new("DIV", 2, -1));
    table[OpCode::SDIV.to_usize()] = Some(Properties::new("SDIV", 2, -1));
    table[OpCode::MOD.to_usize()] = Some(Properties::new("MOD", 2, -1));
    table[OpCode::SMOD.to_usize()] = Some(Properties::new("SMOD", 2, -1));
    table[OpCode::ADDMOD.to_usize()] = Some(Properties::new("ADDMOD", 3, -2));
    table[OpCode::MULMOD.to_usize()] = Some(Properties::new("MULMOD", 3, -2));
    table[OpCode::EXP.to_usize()] = Some(Properties::new("EXP", 2, -1));
    table[OpCode::SIGNEXTEND.to_usize()] = Some(Properties::new("SIGNEXTEND", 2, -1));

    table[OpCode::LT.to_usize()] = Some(Properties::new("LT", 2, -1));
    table[OpCode::GT.to_usize()] = Some(Properties::new("GT", 2, -1));
    table[OpCode::SLT.to_usize()] = Some(Properties::new("SLT", 2, -1));
    table[OpCode::SGT.to_usize()] = Some(Properties::new("SGT", 2, -1));
    table[OpCode::EQ.to_usize()] = Some(Properties::new("EQ", 2, -1));
    table[OpCode::ISZERO.to_usize()] = Some(Properties::new("ISZERO", 1, 0));
    table[OpCode::AND.to_usize()] = Some(Properties::new("AND", 2, -1));
    table[OpCode::OR.to_usize()] = Some(Properties::new("OR", 2, -1));
    table[OpCode::XOR.to_usize()] = Some(Properties::new("XOR", 2, -1));
    table[OpCode::NOT.to_usize()] = Some(Properties::new("NOT", 1, 0));
    table[OpCode::BYTE.to_usize()] = Some(Properties::new("BYTE", 2, -1));
    table[OpCode::SHL.to_usize()] = Some(Properties::new("SHL", 2, -1));
    table[OpCode::SHR.to_usize()] = Some(Properties::new("SHR", 2, -1));
    table[OpCode::SAR.to_usize()] = Some(Properties::new("SAR", 2, -1));

    table[OpCode::KECCAK256.to_usize()] = Some(Properties::new("KECCAK256", 2, -1));

    table[OpCode::ADDRESS.to_usize()] = Some(Properties::new("ADDRESS", 0, 1));
    table[OpCode::BALANCE.to_usize()] = Some(Properties::new("BALANCE", 1, 0));
    table[OpCode::ORIGIN.to_usize()] = Some(Properties::new("ORIGIN", 0, 1));
    table[OpCode::CALLER.to_usize()] = Some(Properties::new("CALLER", 0, 1));
    table[OpCode::CALLVALUE.to_usize()] = Some(Properties::new("CALLVALUE", 0, 1));
    table[OpCode::CALLDATALOAD.to_usize()] = Some(Properties::new("CALLDATALOAD", 1, 0));
    table[OpCode::CALLDATASIZE.to_usize()] = Some(Properties::new("CALLDATASIZE", 0, 1));
    table[OpCode::CALLDATACOPY.to_usize()] = Some(Properties::new("CALLDATACOPY", 3, -3));
    table[OpCode::CODESIZE.to_usize()] = Some(Properties::new("CODESIZE", 0, 1));
    table[OpCode::CODECOPY.to_usize()] = Some(Properties::new("CODECOPY", 3, -3));
    table[OpCode::GASPRICE.to_usize()] = Some(Properties::new("GASPRICE", 0, 1));
    table[OpCode::EXTCODESIZE.to_usize()] = Some(Properties::new("EXTCODESIZE", 1, 0));
    table[OpCode::EXTCODECOPY.to_usize()] = Some(Properties::new("EXTCODECOPY", 4, -4));
    table[OpCode::RETURNDATASIZE.to_usize()] = Some(Properties::new("RETURNDATASIZE", 0, 1));
    table[OpCode::RETURNDATACOPY.to_usize()] = Some(Properties::new("RETURNDATACOPY", 3, -3));
    table[OpCode::EXTCODEHASH.to_usize()] = Some(Properties::new("EXTCODEHASH", 1, 0));

    table[OpCode::BLOCKHASH.to_usize()] = Some(Properties::new("BLOCKHASH", 1, 0));
    table[OpCode::COINBASE.to_usize()] = Some(Properties::new("COINBASE", 0, 1));
    table[OpCode::TIMESTAMP.to_usize()] = Some(Properties::new("TIMESTAMP", 0, 1));
    table[OpCode::NUMBER.to_usize()] = Some(Properties::new("NUMBER", 0, 1));
    table[OpCode::DIFFICULTY.to_usize()] = Some(Properties::new("DIFFICULTY", 0, 1));
    table[OpCode::GASLIMIT.to_usize()] = Some(Properties::new("GASLIMIT", 0, 1));
    table[OpCode::CHAINID.to_usize()] = Some(Properties::new("CHAINID", 0, 1));
    table[OpCode::SELFBALANCE.to_usize()] = Some(Properties::new("SELFBALANCE", 0, 1));
    table[OpCode::BASEFEE.to_usize()] = Some(Properties::new("BASEFEE", 0, 1));

    table[OpCode::POP.to_usize()] = Some(Properties::new("POP", 1, -1));
    table[OpCode::MLOAD.to_usize()] = Some(Properties::new("MLOAD", 1, 0));
    table[OpCode::MSTORE.to_usize()] = Some(Properties::new("MSTORE", 2, -2));
    table[OpCode::MSTORE8.to_usize()] = Some(Properties::new("MSTORE8", 2, -2));
    table[OpCode::SLOAD.to_usize()] = Some(Properties::new("SLOAD", 1, 0));
    table[OpCode::SSTORE.to_usize()] = Some(Properties::new("SSTORE", 2, -2));
    table[OpCode::JUMP.to_usize()] = Some(Properties::new("JUMP", 1, -1));
    table[OpCode::JUMPI.to_usize()] = Some(Properties::new("JUMPI", 2, -2));
    table[OpCode::PC.to_usize()] = Some(Properties::new("PC", 0, 1));
    table[OpCode::MSIZE.to_usize()] = Some(Properties::new("MSIZE", 0, 1));
    table[OpCode::GAS.to_usize()] = Some(Properties::new("GAS", 0, 1));
    table[OpCode::JUMPDEST.to_usize()] = Some(Properties::new("JUMPDEST", 0, 0));

    table[OpCode::PUSH1.to_usize()] = Some(Properties::new("PUSH1", 0, 1));
    table[OpCode::PUSH2.to_usize()] = Some(Properties::new("PUSH2", 0, 1));
    table[OpCode::PUSH3.to_usize()] = Some(Properties::new("PUSH3", 0, 1));
    table[OpCode::PUSH4.to_usize()] = Some(Properties::new("PUSH4", 0, 1));
    table[OpCode::PUSH5.to_usize()] = Some(Properties::new("PUSH5", 0, 1));
    table[OpCode::PUSH6.to_usize()] = Some(Properties::new("PUSH6", 0, 1));
    table[OpCode::PUSH7.to_usize()] = Some(Properties::new("PUSH7", 0, 1));
    table[OpCode::PUSH8.to_usize()] = Some(Properties::new("PUSH8", 0, 1));
    table[OpCode::PUSH9.to_usize()] = Some(Properties::new("PUSH9", 0, 1));
    table[OpCode::PUSH10.to_usize()] = Some(Properties::new("PUSH10", 0, 1));
    table[OpCode::PUSH11.to_usize()] = Some(Properties::new("PUSH11", 0, 1));
    table[OpCode::PUSH12.to_usize()] = Some(Properties::new("PUSH12", 0, 1));
    table[OpCode::PUSH13.to_usize()] = Some(Properties::new("PUSH13", 0, 1));
    table[OpCode::PUSH14.to_usize()] = Some(Properties::new("PUSH14", 0, 1));
    table[OpCode::PUSH15.to_usize()] = Some(Properties::new("PUSH15", 0, 1));
    table[OpCode::PUSH16.to_usize()] = Some(Properties::new("PUSH16", 0, 1));
    table[OpCode::PUSH17.to_usize()] = Some(Properties::new("PUSH17", 0, 1));
    table[OpCode::PUSH18.to_usize()] = Some(Properties::new("PUSH18", 0, 1));
    table[OpCode::PUSH19.to_usize()] = Some(Properties::new("PUSH19", 0, 1));
    table[OpCode::PUSH20.to_usize()] = Some(Properties::new("PUSH20", 0, 1));
    table[OpCode::PUSH21.to_usize()] = Some(Properties::new("PUSH21", 0, 1));
    table[OpCode::PUSH22.to_usize()] = Some(Properties::new("PUSH22", 0, 1));
    table[OpCode::PUSH23.to_usize()] = Some(Properties::new("PUSH23", 0, 1));
    table[OpCode::PUSH24.to_usize()] = Some(Properties::new("PUSH24", 0, 1));
    table[OpCode::PUSH25.to_usize()] = Some(Properties::new("PUSH25", 0, 1));
    table[OpCode::PUSH26.to_usize()] = Some(Properties::new("PUSH26", 0, 1));
    table[OpCode::PUSH27.to_usize()] = Some(Properties::new("PUSH27", 0, 1));
    table[OpCode::PUSH28.to_usize()] = Some(Properties::new("PUSH28", 0, 1));
    table[OpCode::PUSH29.to_usize()] = Some(Properties::new("PUSH29", 0, 1));
    table[OpCode::PUSH30.to_usize()] = Some(Properties::new("PUSH30", 0, 1));
    table[OpCode::PUSH31.to_usize()] = Some(Properties::new("PUSH31", 0, 1));
    table[OpCode::PUSH32.to_usize()] = Some(Properties::new("PUSH32", 0, 1));

    table[OpCode::DUP1.to_usize()] = Some(Properties::new("DUP1", 1, 1));
    table[OpCode::DUP2.to_usize()] = Some(Properties::new("DUP2", 2, 1));
    table[OpCode::DUP3.to_usize()] = Some(Properties::new("DUP3", 3, 1));
    table[OpCode::DUP4.to_usize()] = Some(Properties::new("DUP4", 4, 1));
    table[OpCode::DUP5.to_usize()] = Some(Properties::new("DUP5", 5, 1));
    table[OpCode::DUP6.to_usize()] = Some(Properties::new("DUP6", 6, 1));
    table[OpCode::DUP7.to_usize()] = Some(Properties::new("DUP7", 7, 1));
    table[OpCode::DUP8.to_usize()] = Some(Properties::new("DUP8", 8, 1));
    table[OpCode::DUP9.to_usize()] = Some(Properties::new("DUP9", 9, 1));
    table[OpCode::DUP10.to_usize()] = Some(Properties::new("DUP10", 10, 1));
    table[OpCode::DUP11.to_usize()] = Some(Properties::new("DUP11", 11, 1));
    table[OpCode::DUP12.to_usize()] = Some(Properties::new("DUP12", 12, 1));
    table[OpCode::DUP13.to_usize()] = Some(Properties::new("DUP13", 13, 1));
    table[OpCode::DUP14.to_usize()] = Some(Properties::new("DUP14", 14, 1));
    table[OpCode::DUP15.to_usize()] = Some(Properties::new("DUP15", 15, 1));
    table[OpCode::DUP16.to_usize()] = Some(Properties::new("DUP16", 16, 1));

    table[OpCode::SWAP1.to_usize()] = Some(Properties::new("SWAP1", 2, 0));
    table[OpCode::SWAP2.to_usize()] = Some(Properties::new("SWAP2", 3, 0));
    table[OpCode::SWAP3.to_usize()] = Some(Properties::new("SWAP3", 4, 0));
    table[OpCode::SWAP4.to_usize()] = Some(Properties::new("SWAP4", 5, 0));
    table[OpCode::SWAP5.to_usize()] = Some(Properties::new("SWAP5", 6, 0));
    table[OpCode::SWAP6.to_usize()] = Some(Properties::new("SWAP6", 7, 0));
    table[OpCode::SWAP7.to_usize()] = Some(Properties::new("SWAP7", 8, 0));
    table[OpCode::SWAP8.to_usize()] = Some(Properties::new("SWAP8", 9, 0));
    table[OpCode::SWAP9.to_usize()] = Some(Properties::new("SWAP9", 10, 0));
    table[OpCode::SWAP10.to_usize()] = Some(Properties::new("SWAP10", 11, 0));
    table[OpCode::SWAP11.to_usize()] = Some(Properties::new("SWAP11", 12, 0));
    table[OpCode::SWAP12.to_usize()] = Some(Properties::new("SWAP12", 13, 0));
    table[OpCode::SWAP13.to_usize()] = Some(Properties::new("SWAP13", 14, 0));
    table[OpCode::SWAP14.to_usize()] = Some(Properties::new("SWAP14", 15, 0));
    table[OpCode::SWAP15.to_usize()] = Some(Properties::new("SWAP15", 16, 0));
    table[OpCode::SWAP16.to_usize()] = Some(Properties::new("SWAP16", 17, 0));

    table[OpCode::LOG0.to_usize()] = Some(Properties::new("LOG0", 2, -2));
    table[OpCode::LOG1.to_usize()] = Some(Properties::new("LOG1", 3, -3));
    table[OpCode::LOG2.to_usize()] = Some(Properties::new("LOG2", 4, -4));
    table[OpCode::LOG3.to_usize()] = Some(Properties::new("LOG3", 5, -5));
    table[OpCode::LOG4.to_usize()] = Some(Properties::new("LOG4", 6, -6));

    table[OpCode::CREATE.to_usize()] = Some(Properties::new("CREATE", 3, -2));
    table[OpCode::CALL.to_usize()] = Some(Properties::new("CALL", 7, -6));
    table[OpCode::CALLCODE.to_usize()] = Some(Properties::new("CALLCODE", 7, -6));
    table[OpCode::RETURN.to_usize()] = Some(Properties::new("RETURN", 2, -2));
    table[OpCode::DELEGATECALL.to_usize()] = Some(Properties::new("DELEGATECALL", 6, -5));
    table[OpCode::CREATE2.to_usize()] = Some(Properties::new("CREATE2", 4, -3));
    table[OpCode::STATICCALL.to_usize()] = Some(Properties::new("STATICCALL", 6, -5));
    table[OpCode::REVERT.to_usize()] = Some(Properties::new("REVERT", 2, -2));
    table[OpCode::INVALID.to_usize()] = Some(Properties::new("INVALID", 0, 0));
    table[OpCode::SELFDESTRUCT.to_usize()] = Some(Properties::new("SELFDESTRUCT", 1, -1));

    table
});

#[allow(clippy::needless_range_loop)]
static FRONTIER_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
    let mut table = [None; 256];

    table[OpCode::STOP.to_usize()] = Some(0);
    table[OpCode::ADD.to_usize()] = Some(3);
    table[OpCode::MUL.to_usize()] = Some(5);
    table[OpCode::SUB.to_usize()] = Some(3);
    table[OpCode::DIV.to_usize()] = Some(5);
    table[OpCode::SDIV.to_usize()] = Some(5);
    table[OpCode::MOD.to_usize()] = Some(5);
    table[OpCode::SMOD.to_usize()] = Some(5);
    table[OpCode::ADDMOD.to_usize()] = Some(8);
    table[OpCode::MULMOD.to_usize()] = Some(8);
    table[OpCode::EXP.to_usize()] = Some(10);
    table[OpCode::SIGNEXTEND.to_usize()] = Some(5);
    table[OpCode::LT.to_usize()] = Some(3);
    table[OpCode::GT.to_usize()] = Some(3);
    table[OpCode::SLT.to_usize()] = Some(3);
    table[OpCode::SGT.to_usize()] = Some(3);
    table[OpCode::EQ.to_usize()] = Some(3);
    table[OpCode::ISZERO.to_usize()] = Some(3);
    table[OpCode::AND.to_usize()] = Some(3);
    table[OpCode::OR.to_usize()] = Some(3);
    table[OpCode::XOR.to_usize()] = Some(3);
    table[OpCode::NOT.to_usize()] = Some(3);
    table[OpCode::BYTE.to_usize()] = Some(3);
    table[OpCode::KECCAK256.to_usize()] = Some(30);
    table[OpCode::ADDRESS.to_usize()] = Some(2);
    table[OpCode::BALANCE.to_usize()] = Some(20);
    table[OpCode::ORIGIN.to_usize()] = Some(2);
    table[OpCode::CALLER.to_usize()] = Some(2);
    table[OpCode::CALLVALUE.to_usize()] = Some(2);
    table[OpCode::CALLDATALOAD.to_usize()] = Some(3);
    table[OpCode::CALLDATASIZE.to_usize()] = Some(2);
    table[OpCode::CALLDATACOPY.to_usize()] = Some(3);
    table[OpCode::CODESIZE.to_usize()] = Some(2);
    table[OpCode::CODECOPY.to_usize()] = Some(3);
    table[OpCode::GASPRICE.to_usize()] = Some(2);
    table[OpCode::EXTCODESIZE.to_usize()] = Some(20);
    table[OpCode::EXTCODECOPY.to_usize()] = Some(20);
    table[OpCode::BLOCKHASH.to_usize()] = Some(20);
    table[OpCode::COINBASE.to_usize()] = Some(2);
    table[OpCode::TIMESTAMP.to_usize()] = Some(2);
    table[OpCode::NUMBER.to_usize()] = Some(2);
    table[OpCode::DIFFICULTY.to_usize()] = Some(2);
    table[OpCode::GASLIMIT.to_usize()] = Some(2);
    table[OpCode::POP.to_usize()] = Some(2);
    table[OpCode::MLOAD.to_usize()] = Some(3);
    table[OpCode::MSTORE.to_usize()] = Some(3);
    table[OpCode::MSTORE8.to_usize()] = Some(3);
    table[OpCode::SLOAD.to_usize()] = Some(50);
    table[OpCode::SSTORE.to_usize()] = Some(0);
    table[OpCode::JUMP.to_usize()] = Some(8);
    table[OpCode::JUMPI.to_usize()] = Some(10);
    table[OpCode::PC.to_usize()] = Some(2);
    table[OpCode::MSIZE.to_usize()] = Some(2);

    table[OpCode::GAS.to_usize()] = Some(2);
    table[OpCode::JUMPDEST.to_usize()] = Some(1);

    for op in OpCode::PUSH1.to_usize()..=OpCode::PUSH32.to_usize() {
        table[op] = Some(3);
    }

    for op in OpCode::DUP1.to_usize()..=OpCode::DUP16.to_usize() {
        table[op] = Some(3);
    }

    for op in OpCode::SWAP1.to_usize()..=OpCode::SWAP16.to_usize() {
        table[op] = Some(3);
    }

    for (i, op) in (OpCode::LOG0.to_usize()..=OpCode::LOG4.to_usize())
        .into_iter()
        .enumerate()
    {
        table[op] = Some((1 + i as u16) * 375);
    }

    table[OpCode::CREATE.to_usize()] = Some(32000);
    table[OpCode::CALL.to_usize()] = Some(40);
    table[OpCode::CALLCODE.to_usize()] = Some(40);
    table[OpCode::RETURN.to_usize()] = Some(0);
    table[OpCode::INVALID.to_usize()] = Some(0);
    table[OpCode::SELFDESTRUCT.to_usize()] = Some(0);

    table
});

static HOMESTEAD_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
    let mut table = *FRONTIER_GAS_COSTS;
    table[OpCode::DELEGATECALL.to_usize()] = Some(40);
    table
});

static TANGERINE_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
    let mut table = *HOMESTEAD_GAS_COSTS;
    table[OpCode::BALANCE.to_usize()] = Some(400);
    table[OpCode::EXTCODESIZE.to_usize()] = Some(700);
    table[OpCode::EXTCODECOPY.to_usize()] = Some(700);
    table[OpCode::SLOAD.to_usize()] = Some(200);
    table[OpCode::CALL.to_usize()] = Some(700);
    table[OpCode::CALLCODE.to_usize()] = Some(700);
    table[OpCode::DELEGATECALL.to_usize()] = Some(700);
    table[OpCode::SELFDESTRUCT.to_usize()] = Some(5000);
    table
});

static SPURIOUS_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| *TANGERINE_GAS_COSTS);

static BYZANTIUM_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
    let mut table = *SPURIOUS_GAS_COSTS;
    table[OpCode::RETURNDATASIZE.to_usize()] = Some(2);
    table[OpCode::RETURNDATACOPY.to_usize()] = Some(3);
    table[OpCode::STATICCALL.to_usize()] = Some(700);
    table[OpCode::REVERT.to_usize()] = Some(0);
    table
});

static CONSTANTINOPLE_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
    let mut table = *BYZANTIUM_GAS_COSTS;
    table[OpCode::SHL.to_usize()] = Some(3);
    table[OpCode::SHR.to_usize()] = Some(3);
    table[OpCode::SAR.to_usize()] = Some(3);
    table[OpCode::EXTCODEHASH.to_usize()] = Some(400);
    table[OpCode::CREATE2.to_usize()] = Some(32000);
    table
});

static PETERSBURG_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| *CONSTANTINOPLE_GAS_COSTS);

static ISTANBUL_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
    let mut table = *PETERSBURG_GAS_COSTS;
    table[OpCode::BALANCE.to_usize()] = Some(700);
    table[OpCode::CHAINID.to_usize()] = Some(2);
    table[OpCode::EXTCODEHASH.to_usize()] = Some(700);
    table[OpCode::SELFBALANCE.to_usize()] = Some(5);
    table[OpCode::SLOAD.to_usize()] = Some(800);
    table
});

static BERLIN_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
    let mut table = *ISTANBUL_GAS_COSTS;
    table[OpCode::EXTCODESIZE.to_usize()] = Some(WARM_STORAGE_READ_COST);
    table[OpCode::EXTCODECOPY.to_usize()] = Some(WARM_STORAGE_READ_COST);
    table[OpCode::EXTCODEHASH.to_usize()] = Some(WARM_STORAGE_READ_COST);
    table[OpCode::BALANCE.to_usize()] = Some(WARM_STORAGE_READ_COST);
    table[OpCode::CALL.to_usize()] = Some(WARM_STORAGE_READ_COST);
    table[OpCode::CALLCODE.to_usize()] = Some(WARM_STORAGE_READ_COST);
    table[OpCode::DELEGATECALL.to_usize()] = Some(WARM_STORAGE_READ_COST);
    table[OpCode::STATICCALL.to_usize()] = Some(WARM_STORAGE_READ_COST);
    table[OpCode::SLOAD.to_usize()] = Some(WARM_STORAGE_READ_COST);
    table
});

static LONDON_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
    let mut table = *BERLIN_GAS_COSTS;
    table[OpCode::BASEFEE.to_usize()] = Some(2);
    table
});

static SHANGHAI_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| *LONDON_GAS_COSTS);

pub fn gas_costs(revision: Revision) -> &'static [Option<u16>; 256] {
    match revision {
        Revision::Frontier => &FRONTIER_GAS_COSTS,
        Revision::Homestead => &HOMESTEAD_GAS_COSTS,
        Revision::Tangerine => &TANGERINE_GAS_COSTS,
        Revision::Spurious => &SPURIOUS_GAS_COSTS,
        Revision::Byzantium => &BYZANTIUM_GAS_COSTS,
        Revision::Constantinople => &CONSTANTINOPLE_GAS_COSTS,
        Revision::Petersburg => &PETERSBURG_GAS_COSTS,
        Revision::Istanbul => &ISTANBUL_GAS_COSTS,
        Revision::Berlin => &BERLIN_GAS_COSTS,
        Revision::London => &LONDON_GAS_COSTS,
        Revision::Shanghai => &SHANGHAI_GAS_COSTS,
    }
}


================================================
FILE: src/instructions/stack_manip.rs
================================================
use crate::state::*;
use ethereum_types::U256;

pub(crate) fn push(stack: &mut Stack, code: &[u8], push_len: usize) {
    stack.push(U256::from_big_endian(&code[..push_len]));
}

pub(crate) fn dup(stack: &mut Stack, height: usize) {
    stack.push(*stack.get(height - 1));
}

pub(crate) fn swap(stack: &mut Stack, height: usize) {
    stack.swap_top(height);
}

pub(crate) fn pop(stack: &mut Stack) {
    stack.pop();
}


================================================
FILE: src/interpreter.rs
================================================
use self::instruction_table::*;
use crate::{
    common::*,
    continuation::{interrupt::*, interrupt_data::*, resume_data::*, *},
    instructions::{control::*, stack_manip::*, *},
    state::*,
    tracing::Tracer,
    *,
};
use ethereum_types::U256;
use genawaiter::sync::*;
use std::sync::Arc;

fn check_requirements(
    instruction_table: &InstructionTable,
    state: &mut ExecutionState,
    op: OpCode,
) -> Result<(), StatusCode> {
    let metrics = &instruction_table[op.to_usize()].ok_or(StatusCode::UndefinedInstruction)?;

    state.gas_left -= metrics.gas_cost as i64;
    if state.gas_left < 0 {
        return Err(StatusCode::OutOfGas);
    }

    let stack_size = state.stack.len();
    if stack_size == Stack::limit() {
        if metrics.can_overflow_stack {
            return Err(StatusCode::StackOverflow);
        }
    } else if stack_size < metrics.stack_height_required.into() {
        return Err(StatusCode::StackUnderflow);
    }

    Ok(())
}

#[derive(Clone, Debug)]
pub struct JumpdestMap(Arc<[bool]>);

impl JumpdestMap {
    pub fn contains(&self, dst: U256) -> bool {
        dst < self.0.len().into() && self.0[dst.as_usize()]
    }
}

/// Code with analysis.
#[derive(Clone, Debug)]
pub struct AnalyzedCode {
    jumpdest_map: JumpdestMap,
    code: Bytes,
    padded_code: Bytes,
}

impl AnalyzedCode {
    /// Analyze code and prepare it for execution.
    pub fn analyze(code: impl Into<Vec<u8>>) -> Self {
        let code = code.into();
        let mut jumpdest_map = vec![false; code.len()];

        let mut i = 0;
        while i < code.len() {
            let opcode = OpCode(code[i]);
            i += match opcode {
                OpCode::JUMPDEST => {
                    jumpdest_map[i] = true;
                    1
                }
                OpCode::PUSH1
                | OpCode::PUSH2
                | OpCode::PUSH3
                | OpCode::PUSH4
                | OpCode::PUSH5
                | OpCode::PUSH6
                | OpCode::PUSH7
                | OpCode::PUSH8
                | OpCode::PUSH9
                | OpCode::PUSH10
                | OpCode::PUSH11
                | OpCode::PUSH12
                | OpCode::PUSH13
                | OpCode::PUSH14
                | OpCode::PUSH15
                | OpCode::PUSH16
                | OpCode::PUSH17
                | OpCode::PUSH18
                | OpCode::PUSH19
                | OpCode::PUSH20
                | OpCode::PUSH21
                | OpCode::PUSH22
                | OpCode::PUSH23
                | OpCode::PUSH24
                | OpCode::PUSH25
                | OpCode::PUSH26
                | OpCode::PUSH27
                | OpCode::PUSH28
                | OpCode::PUSH29
                | OpCode::PUSH30
                | OpCode::PUSH31
                | OpCode::PUSH32 => opcode.to_usize() - OpCode::PUSH1.to_usize() + 2,
                _ => 1,
            }
        }

        let code_len = code.len();

        let mut padded_code = code;
        padded_code.resize(i + 1, OpCode::STOP.to_u8());

        let jumpdest_map = JumpdestMap(jumpdest_map.into());
        let padded_code = Bytes::from(padded_code);
        let mut code = padded_code.clone();
        code.truncate(code_len);

        Self {
            jumpdest_map,
            code,
            padded_code,
        }
    }

    /// Execute analyzed EVM bytecode using provided `Host` context. Optionally modify the state after each instruction using provided closure.
    pub fn execute<H: Host, T: Tracer>(
        &self,
        host: &mut H,
        tracer: &mut T,
        state_modifier: StateModifier,
        message: Message,
        revision: Revision,
    ) -> Output {
        if !T::DUMMY {
            tracer.notify_execution_start(revision, message.clone(), self.code.clone());
        }

        let output = self
            .execute_resumable(!T::DUMMY || state_modifier.is_some(), message, revision)
            .run_to_completion_with_host(host, tracer, state_modifier);

        if !T::DUMMY {
            tracer.notify_execution_end(&output);
        }

        output
    }

    /// Execute in resumable EVM.
    pub fn execute_resumable(
        &self,
        trace: bool,
        message: Message,
        revision: Revision,
    ) -> ExecutionStartInterrupt {
        let code = self.clone();
        let inner = Box::pin(Gen::new(move |co| {
            interpreter_producer(co, code, ExecutionState::new(message, revision), trace)
        }));

        ExecutionStartInterrupt { inner, data: () }
    }
}

impl ExecutionStartInterrupt {
    pub fn run_to_completion_with_host<H: Host, T: Tracer>(
        self,
        host: &mut H,
        tracer: &mut T,
        state_modifier: StateModifier,
    ) -> Output {
        let mut interrupt = self.resume(());

        loop {
            interrupt = match interrupt {
                InterruptVariant::InstructionStart(i) => {
                    tracer.notify_instruction_start(i.data().pc, i.data().opcode, &i.data().state);
                    i.resume(state_modifier.clone())
                }
                InterruptVariant::AccountExists(i) => {
                    let exists = host.account_exists(i.data().address);
                    i.resume(AccountExistsStatus { exists })
                }
                InterruptVariant::GetBalance(i) => {
                    let balance = host.get_balance(i.data().address);
                    i.resume(Balance { balance })
                }
                InterruptVariant::GetCodeSize(i) => {
                    let code_size = host.get_code_size(i.data().address);
                    i.resume(CodeSize { code_size })
                }
                InterruptVariant::GetStorage(i) => {
                    let value = host.get_storage(i.data().address, i.data().key);
                    i.resume(StorageValue { value })
                }
                InterruptVariant::SetStorage(i) => {
                    let status = host.set_storage(i.data().address, i.data().key, i.data().value);
                    i.resume(StorageStatusInfo { status })
                }
                InterruptVariant::GetCodeHash(i) => {
                    let hash = host.get_code_hash(i.data().address);
                    i.resume(CodeHash { hash })
                }
                InterruptVariant::CopyCode(i) => {
                    let mut code = vec![0; i.data().max_size];
                    let copied = host.copy_code(i.data().address, i.data().offset, &mut code[..]);
                    if copied > code.len() {
                        return Output {
                            status_code: StatusCode::InternalError(format!(
                                "copy code: copied {} > max size {}",
                                copied,
                                code.len()
                            )),
                            gas_left: 0,
                            output_data: Bytes::new(),
                            create_address: None,
                        };
                    }
                    code.truncate(copied);
                    let code = code.into();
                    i.resume(Code { code })
                }
                InterruptVariant::Selfdestruct(i) => {
                    host.selfdestruct(i.data().address, i.data().beneficiary);
                    i.resume(())
                }
                InterruptVariant::Call(i) => {
                    let message = match i.data() {
                        Call::Call(message) => message.clone(),
                        Call::Create(message) => message.clone().into(),
                    };
                    let output = host.call(&message);
                    i.resume(CallOutput { output })
                }
                InterruptVariant::GetTxContext(i) => {
                    let context = host.get_tx_context();
                    i.resume(TxContextData { context })
                }
                InterruptVariant::GetBlockHash(i) => {
                    let hash = host.get_block_hash(i.data().block_number);
                    i.resume(BlockHash { hash })
                }
                InterruptVariant::EmitLog(i) => {
                    host.emit_log(
                        i.data().address,
                        &*i.data().data,
                        i.data().topics.as_slice(),
                    );
                    i.resume(())
                }
                InterruptVariant::AccessAccount(i) => {
                    let status = host.access_account(i.data().address);
                    i.resume(AccessAccountStatus { status })
                }
                InterruptVariant::AccessStorage(i) => {
                    let status = host.access_storage(i.data().address, i.data().key);
                    i.resume(AccessStorageStatus { status })
                }
                InterruptVariant::Complete(i) => {
                    let output = match i {
                        Ok(output) => output.into(),
                        Err(status_code) => Output {
                            status_code,
                            gas_left: 0,
                            output_data: Bytes::new(),
                            create_address: None,
                        },
                    };

                    return output;
                }
            };
        }
    }
}

async fn interpreter_producer(
    mut co: Co<InterruptDataVariant, ResumeDataVariant>,
    s: AnalyzedCode,
    mut state: ExecutionState,
    trace: bool,
) -> Result<SuccessfulOutput, StatusCode> {
    let state = &mut state;

    let instruction_table = get_baseline_instruction_table(state.evm_revision);

    let mut reverted = false;

    let mut pc = 0;

    loop {
        let op = OpCode(s.padded_code[pc]);

        // Do not print stop on the final STOP
        if trace && pc < s.code.len() {
            if let Some(modifier) = co
                .yield_(InterruptDataVariant::InstructionStart(Box::new(
                    InstructionStart {
                        pc,
                        opcode: op,
                        state: state.clone(),
                    },
                )))
                .await
                .as_state_modifier()
                .unwrap()
            {
                (modifier)(state)
            }
        }

        check_requirements(instruction_table, state, op)?;

        match op {
            OpCode::STOP => {
                break;
            }
            OpCode::ADD => {
                arithmetic::add(&mut state.stack);
            }
            OpCode::MUL => {
                arithmetic::mul(&mut state.stack);
            }
            OpCode::SUB => {
                arithmetic::sub(&mut state.stack);
            }
            OpCode::DIV => {
                arithmetic::div(&mut state.stack);
            }
            OpCode::SDIV => {
                arithmetic::sdiv(&mut state.stack);
            }
            OpCode::MOD => {
                arithmetic::modulo(&mut state.stack);
            }
            OpCode::SMOD => {
                arithmetic::smod(&mut state.stack);
            }
            OpCode::ADDMOD => {
                arithmetic::addmod(&mut state.stack);
            }
            OpCode::MULMOD => {
                arithmetic::mulmod(&mut state.stack);
            }
            OpCode::EXP => {
                arithmetic::exp(state)?;
            }
            OpCode::SIGNEXTEND => {
                arithmetic::signextend(&mut state.stack);
            }
            OpCode::LT => {
                boolean::lt(&mut state.stack);
            }
            OpCode::GT => {
                boolean::gt(&mut state.stack);
            }
            OpCode::SLT => {
                boolean::slt(&mut state.stack);
            }
            OpCode::SGT => {
                boolean::sgt(&mut state.stack);
            }
            OpCode::EQ => {
                boolean::eq(&mut state.stack);
            }
            OpCode::ISZERO => {
                boolean::iszero(&mut state.stack);
            }
            OpCode::AND => {
                boolean::and(&mut state.stack);
            }
            OpCode::OR => {
                boolean::or(&mut state.stack);
            }
            OpCode::XOR => {
                boolean::xor(&mut state.stack);
            }
            OpCode::NOT => {
                boolean::not(&mut state.stack);
            }
            OpCode::BYTE => {
                bitwise::byte(&mut state.stack);
            }
            OpCode::SHL => {
                bitwise::shl(&mut state.stack);
            }
            OpCode::SHR => {
                bitwise::shr(&mut state.stack);
            }
            OpCode::SAR => {
                bitwise::sar(&mut state.stack);
            }

            OpCode::KECCAK256 => {
                memory::keccak256(state)?;
            }
            OpCode::ADDRESS => {
                external::address(state);
            }
            OpCode::BALANCE => {
                balance!(co, state);
            }
            OpCode::CALLER => {
                external::caller(state);
            }
            OpCode::CALLVALUE => {
                external::callvalue(state);
            }
            OpCode::CALLDATALOAD => {
                calldataload(state);
            }
            OpCode::CALLDATASIZE => {
                calldatasize(state);
            }
            OpCode::CALLDATACOPY => {
                memory::calldatacopy(state)?;
            }
            OpCode::CODESIZE => {
                memory::codesize(&mut state.stack, &s.code[..]);
            }
            OpCode::CODECOPY => {
                memory::codecopy(state, &s.code[..])?;
            }
            OpCode::EXTCODESIZE => {
                extcodesize!(co, state);
            }
            OpCode::EXTCODECOPY => {
                extcodecopy!(co, state);
            }
            OpCode::RETURNDATASIZE => {
                memory::returndatasize(state);
            }
            OpCode::RETURNDATACOPY => {
                memory::returndatacopy(state)?;
            }
            OpCode::EXTCODEHASH => {
                extcodehash!(co, state);
            }
            OpCode::BLOCKHASH => {
                blockhash!(co, state);
            }
            OpCode::ORIGIN
            | OpCode::COINBASE
            | OpCode::GASPRICE
            | OpCode::TIMESTAMP
            | OpCode::NUMBER
            | OpCode::DIFFICULTY
            | OpCode::GASLIMIT
            | OpCode::CHAINID
            | OpCode::BASEFEE => {
                push_txcontext!(
                    co,
                    state,
                    match op {
                        OpCode::ORIGIN => external::origin_accessor,
                        OpCode::COINBASE => external::coinbase_accessor,
                        OpCode::GASPRICE => external::gasprice_accessor,
                        OpCode::TIMESTAMP => external::timestamp_accessor,
                        OpCode::NUMBER => external::number_accessor,
                        OpCode::DIFFICULTY => external::difficulty_accessor,
                        OpCode::GASLIMIT => external::gaslimit_accessor,
                        OpCode::CHAINID => external::chainid_accessor,
                        OpCode::BASEFEE => external::basefee_accessor,
                        _ => unreachable!(),
                    }
                );
            }
            OpCode::SELFBALANCE => {
                selfbalance!(co, state);
            }
            OpCode::POP => {
                stack_manip::pop(&mut state.stack);
            }
            OpCode::MLOAD => {
                memory::mload(state)?;
            }
            OpCode::MSTORE => {
                memory::mstore(state)?;
            }
            OpCode::MSTORE8 => {
                memory::mstore8(state)?;
            }
            OpCode::JUMP => {
                pc = op_jump(state, &s.jumpdest_map)?;

                continue;
            }
            OpCode::JUMPI => {
                if !state.stack.get(1).is_zero() {
                    pc = op_jump(state, &s.jumpdest_map)?;
                    state.stack.pop();

                    continue;
                } else {
                    state.stack.pop();
                    state.stack.pop();
                }
            }
            OpCode::PC => state.stack.push(pc.into()),
            OpCode::MSIZE => memory::msize(state),
            OpCode::SLOAD => {
                sload!(co, state);
            }
            OpCode::SSTORE => {
                sstore!(co, state);
            }
            OpCode::GAS => state.stack.push(state.gas_left.into()),
            OpCode::JUMPDEST => {}
            OpCode::PUSH1
            | OpCode::PUSH2
            | OpCode::PUSH3
            | OpCode::PUSH4
            | OpCode::PUSH5
            | OpCode::PUSH6
            | OpCode::PUSH7
            | OpCode::PUSH8
            | OpCode::PUSH9
            | OpCode::PUSH10
            | OpCode::PUSH11
            | OpCode::PUSH12
            | OpCode::PUSH13
            | OpCode::PUSH14
            | OpCode::PUSH15
            | OpCode::PUSH16
            | OpCode::PUSH17
            | OpCode::PUSH18
            | OpCode::PUSH19
            | OpCode::PUSH20
            | OpCode::PUSH21
            | OpCode::PUSH22
            | OpCode::PUSH23
            | OpCode::PUSH24
            | OpCode::PUSH25
            | OpCode::PUSH26
            | OpCode::PUSH27
            | OpCode::PUSH28
            | OpCode::PUSH29
            | OpCode::PUSH30
            | OpCode::PUSH31
            | OpCode::PUSH32 => {
                let push_len = op.push_size().unwrap().into();
                push(&mut state.stack, &s.padded_code[pc + 1..], push_len);
                pc += push_len;
            }

            OpCode::DUP1
            | OpCode::DUP2
            | OpCode::DUP3
            | OpCode::DUP4
            | OpCode::DUP5
            | OpCode::DUP6
            | OpCode::DUP7
            | OpCode::DUP8
            | OpCode::DUP9
            | OpCode::DUP10
            | OpCode::DUP11
            | OpCode::DUP12
            | OpCode::DUP13
            | OpCode::DUP14
            | OpCode::DUP15
            | OpCode::DUP16 => {
                dup(
                    &mut state.stack,
                    op.to_usize() - OpCode::DUP1.to_usize() + 1,
                );
            }

            OpCode::SWAP1
            | OpCode::SWAP2
            | OpCode::SWAP3
            | OpCode::SWAP4
            | OpCode::SWAP5
            | OpCode::SWAP6
            | OpCode::SWAP7
            | OpCode::SWAP8
            | OpCode::SWAP9
            | OpCode::SWAP10
            | OpCode::SWAP11
            | OpCode::SWAP12
            | OpCode::SWAP13
            | OpCode::SWAP14
            | OpCode::SWAP15
            | OpCode::SWAP16 => swap(
                &mut state.stack,
                op.to_usize() - OpCode::SWAP1.to_usize() + 1,
            ),

            OpCode::LOG0 | OpCode::LOG1 | OpCode::LOG2 | OpCode::LOG3 | OpCode::LOG4 => {
                do_log!(co, state, op.to_usize() - OpCode::LOG0.to_usize());
            }
            OpCode::CREATE | OpCode::CREATE2 => {
                do_create!(co, state, op == OpCode::CREATE2);
            }
            OpCode::CALL | OpCode::CALLCODE | OpCode::DELEGATECALL | OpCode::STATICCALL => {
                do_call!(
                    co,
                    state,
                    match op {
                        OpCode::CALL | OpCode::STATICCALL => CallKind::Call,
                        OpCode::CALLCODE => CallKind::CallCode,
                        OpCode::DELEGATECALL => CallKind::DelegateCall,
                        _ => unreachable!(),
                    },
                    op == OpCode::STATICCALL
                );
            }
            OpCode::RETURN | OpCode::REVERT => {
                ret(state)?;
                reverted = op == OpCode::REVERT;
                break;
            }
            OpCode::INVALID => {
                return Err(StatusCode::InvalidInstruction);
            }
            OpCode::SELFDESTRUCT => {
                selfdestruct!(co, state);
                break;
            }
            other => {
                unreachable!("reached unhandled opcode: {}", other);
            }
        }

        pc += 1;
    }

    let output = SuccessfulOutput {
        reverted,
        gas_left: state.gas_left,
        output_data: state.output_data.clone(),
    };

    Ok(output)
}


================================================
FILE: src/lib.rs
================================================
#![doc = include_str!("../README.md")]
use bytes::Bytes;
pub use common::{
    CallKind, CreateMessage, Message, Output, Revision, StatusCode, SuccessfulOutput,
};
pub use host::Host;
pub use interpreter::AnalyzedCode;
pub use opcode::OpCode;
pub use state::{ExecutionState, Stack};

/// Maximum allowed EVM bytecode size.
pub const MAX_CODE_SIZE: usize = 0x6000;

mod common;
pub mod host;
#[doc(hidden)]
pub mod instructions;
mod interpreter;
pub mod opcode;
mod state;
pub mod tracing;

pub mod continuation;
#[cfg(feature = "util")]
pub mod util;

#[cfg(feature = "evmc")]
pub mod evmc;


================================================
FILE: src/opcode.rs
================================================
use std::{borrow::Cow, fmt::Display};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct OpCode(pub u8);

impl OpCode {
    #[inline(always)]
    pub const fn to_u8(self) -> u8 {
        self.0
    }

    #[inline(always)]
    pub const fn to_usize(self) -> usize {
        self.to_u8() as usize
    }
}

impl OpCode {
    pub const STOP: OpCode = OpCode(0x00);
    pub const ADD: OpCode = OpCode(0x01);
    pub const MUL: OpCode = OpCode(0x02);
    pub const SUB: OpCode = OpCode(0x03);
    pub const DIV: OpCode = OpCode(0x04);
    pub const SDIV: OpCode = OpCode(0x05);
    pub const MOD: OpCode = OpCode(0x06);
    pub const SMOD: OpCode = OpCode(0x07);
    pub const ADDMOD: OpCode = OpCode(0x08);
    pub const MULMOD: OpCode = OpCode(0x09);
    pub const EXP: OpCode = OpCode(0x0a);
    pub const SIGNEXTEND: OpCode = OpCode(0x0b);

    pub const LT: OpCode = OpCode(0x10);
    pub const GT: OpCode = OpCode(0x11);
    pub const SLT: OpCode = OpCode(0x12);
    pub const SGT: OpCode = OpCode(0x13);
    pub const EQ: OpCode = OpCode(0x14);
    pub const ISZERO: OpCode = OpCode(0x15);
    pub const AND: OpCode = OpCode(0x16);
    pub const OR: OpCode = OpCode(0x17);
    pub const XOR: OpCode = OpCode(0x18);
    pub const NOT: OpCode = OpCode(0x19);
    pub const BYTE: OpCode = OpCode(0x1a);
    pub const SHL: OpCode = OpCode(0x1b);
    pub const SHR: OpCode = OpCode(0x1c);
    pub const SAR: OpCode = OpCode(0x1d);

    pub const KECCAK256: OpCode = OpCode(0x20);

    pub const ADDRESS: OpCode = OpCode(0x30);
    pub const BALANCE: OpCode = OpCode(0x31);
    pub const ORIGIN: OpCode = OpCode(0x32);
    pub const CALLER: OpCode = OpCode(0x33);
    pub const CALLVALUE: OpCode = OpCode(0x34);
    pub const CALLDATALOAD: OpCode = OpCode(0x35);
    pub const CALLDATASIZE: OpCode = OpCode(0x36);
    pub const CALLDATACOPY: OpCode = OpCode(0x37);
    pub const CODESIZE: OpCode = OpCode(0x38);
    pub const CODECOPY: OpCode = OpCode(0x39);
    pub const GASPRICE: OpCode = OpCode(0x3a);
    pub const EXTCODESIZE: OpCode = OpCode(0x3b);
    pub const EXTCODECOPY: OpCode = OpCode(0x3c);
    pub const RETURNDATASIZE: OpCode = OpCode(0x3d);
    pub const RETURNDATACOPY: OpCode = OpCode(0x3e);
    pub const EXTCODEHASH: OpCode = OpCode(0x3f);

    pub const BLOCKHASH: OpCode = OpCode(0x40);
    pub const COINBASE: OpCode = OpCode(0x41);
    pub const TIMESTAMP: OpCode = OpCode(0x42);
    pub const NUMBER: OpCode = OpCode(0x43);
    pub const DIFFICULTY: OpCode = OpCode(0x44);
    pub const GASLIMIT: OpCode = OpCode(0x45);
    pub const CHAINID: OpCode = OpCode(0x46);
    pub const SELFBALANCE: OpCode = OpCode(0x47);
    pub const BASEFEE: OpCode = OpCode(0x48);

    pub const POP: OpCode = OpCode(0x50);
    pub const MLOAD: OpCode = OpCode(0x51);
    pub const MSTORE: OpCode = OpCode(0x52);
    pub const MSTORE8: OpCode = OpCode(0x53);
    pub const SLOAD: OpCode = OpCode(0x54);
    pub const SSTORE: OpCode = OpCode(0x55);
    pub const JUMP: OpCode = OpCode(0x56);
    pub const JUMPI: OpCode = OpCode(0x57);
    pub const PC: OpCode = OpCode(0x58);
    pub const MSIZE: OpCode = OpCode(0x59);
    pub const GAS: OpCode = OpCode(0x5a);
    pub const JUMPDEST: OpCode = OpCode(0x5b);

    pub const PUSH1: OpCode = OpCode(0x60);
    pub const PUSH2: OpCode = OpCode(0x61);
    pub const PUSH3: OpCode = OpCode(0x62);
    pub const PUSH4: OpCode = OpCode(0x63);
    pub const PUSH5: OpCode = OpCode(0x64);
    pub const PUSH6: OpCode = OpCode(0x65);
    pub const PUSH7: OpCode = OpCode(0x66);
    pub const PUSH8: OpCode = OpCode(0x67);
    pub const PUSH9: OpCode = OpCode(0x68);
    pub const PUSH10: OpCode = OpCode(0x69);
    pub const PUSH11: OpCode = OpCode(0x6a);
    pub const PUSH12: OpCode = OpCode(0x6b);
    pub const PUSH13: OpCode = OpCode(0x6c);
    pub const PUSH14: OpCode = OpCode(0x6d);
    pub const PUSH15: OpCode = OpCode(0x6e);
    pub const PUSH16: OpCode = OpCode(0x6f);
    pub const PUSH17: OpCode = OpCode(0x70);
    pub const PUSH18: OpCode = OpCode(0x71);
    pub const PUSH19: OpCode = OpCode(0x72);
    pub const PUSH20: OpCode = OpCode(0x73);
    pub const PUSH21: OpCode = OpCode(0x74);
    pub const PUSH22: OpCode = OpCode(0x75);
    pub const PUSH23: OpCode = OpCode(0x76);
    pub const PUSH24: OpCode = OpCode(0x77);
    pub const PUSH25: OpCode = OpCode(0x78);
    pub const PUSH26: OpCode = OpCode(0x79);
    pub const PUSH27: OpCode = OpCode(0x7a);
    pub const PUSH28: OpCode = OpCode(0x7b);
    pub const PUSH29: OpCode = OpCode(0x7c);
    pub const PUSH30: OpCode = OpCode(0x7d);
    pub const PUSH31: OpCode = OpCode(0x7e);
    pub const PUSH32: OpCode = OpCode(0x7f);
    pub const DUP1: OpCode = OpCode(0x80);
    pub const DUP2: OpCode = OpCode(0x81);
    pub const DUP3: OpCode = OpCode(0x82);
    pub const DUP4: OpCode = OpCode(0x83);
    pub const DUP5: OpCode = OpCode(0x84);
    pub const DUP6: OpCode = OpCode(0x85);
    pub const DUP7: OpCode = OpCode(0x86);
    pub const DUP8: OpCode = OpCode(0x87);
    pub const DUP9: OpCode = OpCode(0x88);
    pub const DUP10: OpCode = OpCode(0x89);
    pub const DUP11: OpCode = OpCode(0x8a);
    pub const DUP12: OpCode = OpCode(0x8b);
    pub const DUP13: OpCode = OpCode(0x8c);
    pub const DUP14: OpCode = OpCode(0x8d);
    pub const DUP15: OpCode = OpCode(0x8e);
    pub const DUP16: OpCode = OpCode(0x8f);
    pub const SWAP1: OpCode = OpCode(0x90);
    pub const SWAP2: OpCode = OpCode(0x91);
    pub const SWAP3: OpCode = OpCode(0x92);
    pub const SWAP4: OpCode = OpCode(0x93);
    pub const SWAP5: OpCode = OpCode(0x94);
    pub const SWAP6: OpCode = OpCode(0x95);
    pub const SWAP7: OpCode = OpCode(0x96);
    pub const SWAP8: OpCode = OpCode(0x97);
    pub const SWAP9: OpCode = OpCode(0x98);
    pub const SWAP10: OpCode = OpCode(0x99);
    pub const SWAP11: OpCode = OpCode(0x9a);
    pub const SWAP12: OpCode = OpCode(0x9b);
    pub const SWAP13: OpCode = OpCode(0x9c);
    pub const SWAP14: OpCode = OpCode(0x9d);
    pub const SWAP15: OpCode = OpCode(0x9e);
    pub const SWAP16: OpCode = OpCode(0x9f);
    pub const LOG0: OpCode = OpCode(0xa0);
    pub const LOG1: OpCode = OpCode(0xa1);
    pub const LOG2: OpCode = OpCode(0xa2);
    pub const LOG3: OpCode = OpCode(0xa3);
    pub const LOG4: OpCode = OpCode(0xa4);

    pub const CREATE: OpCode = OpCode(0xf0);
    pub const CALL: OpCode = OpCode(0xf1);
    pub const CALLCODE: OpCode = OpCode(0xf2);
    pub const RETURN: OpCode = OpCode(0xf3);
    pub const DELEGATECALL: OpCode = OpCode(0xf4);
    pub const CREATE2: OpCode = OpCode(0xf5);

    pub const STATICCALL: OpCode = OpCode(0xfa);

    pub const REVERT: OpCode = OpCode(0xfd);
    pub const INVALID: OpCode = OpCode(0xfe);
    pub const SELFDESTRUCT: OpCode = OpCode(0xff);
}

impl OpCode {
    pub const fn name(&self) -> &'static str {
        match *self {
            OpCode::STOP => "STOP",
            OpCode::ADD => "ADD",
            OpCode::MUL => "MUL",
            OpCode::SUB => "SUB",
            OpCode::DIV => "DIV",
            OpCode::SDIV => "SDIV",
            OpCode::MOD => "MOD",
            OpCode::SMOD => "SMOD",
            OpCode::ADDMOD => "ADDMOD",
            OpCode::MULMOD => "MULMOD",
            OpCode::EXP => "EXP",
            OpCode::SIGNEXTEND => "SIGNEXTEND",
            OpCode::LT => "LT",
            OpCode::GT => "GT",
            OpCode::SLT => "SLT",
            OpCode::SGT => "SGT",
            OpCode::EQ => "EQ",
            OpCode::ISZERO => "ISZERO",
            OpCode::AND => "AND",
            OpCode::OR => "OR",
            OpCode::XOR => "XOR",
            OpCode::NOT => "NOT",
            OpCode::BYTE => "BYTE",
            OpCode::SHL => "SHL",
            OpCode::SHR => "SHR",
            OpCode::SAR => "SAR",
            OpCode::KECCAK256 => "KECCAK256",
            OpCode::ADDRESS => "ADDRESS",
            OpCode::BALANCE => "BALANCE",
            OpCode::ORIGIN => "ORIGIN",
            OpCode::CALLER => "CALLER",
            OpCode::CALLVALUE => "CALLVALUE",
            OpCode::CALLDATALOAD => "CALLDATALOAD",
            OpCode::CALLDATASIZE => "CALLDATASIZE",
            OpCode::CALLDATACOPY => "CALLDATACOPY",
            OpCode::CODESIZE => "CODESIZE",
            OpCode::CODECOPY => "CODECOPY",
            OpCode::GASPRICE => "GASPRICE",
            OpCode::EXTCODESIZE => "EXTCODESIZE",
            OpCode::EXTCODECOPY => "EXTCODECOPY",
            OpCode::RETURNDATASIZE => "RETURNDATASIZE",
            OpCode::RETURNDATACOPY => "RETURNDATACOPY",
            OpCode::EXTCODEHASH => "EXTCODEHASH",
            OpCode::BLOCKHASH => "BLOCKHASH",
            OpCode::COINBASE => "COINBASE",
            OpCode::TIMESTAMP => "TIMESTAMP",
            OpCode::NUMBER => "NUMBER",
            OpCode::DIFFICULTY => "DIFFICULTY",
            OpCode::GASLIMIT => "GASLIMIT",
            OpCode::CHAINID => "CHAINID",
            OpCode::SELFBALANCE => "SELFBALANCE",
            OpCode::BASEFEE => "BASEFEE",
            OpCode::POP => "POP",
            OpCode::MLOAD => "MLOAD",
            OpCode::MSTORE => "MSTORE",
            OpCode::MSTORE8 => "MSTORE8",
            OpCode::SLOAD => "SLOAD",
            OpCode::SSTORE => "SSTORE",
            OpCode::JUMP => "JUMP",
            OpCode::JUMPI => "JUMPI",
            OpCode::PC => "PC",
            OpCode::MSIZE => "MSIZE",
            OpCode::GAS => "GAS",
            OpCode::JUMPDEST => "JUMPDEST",
            OpCode::PUSH1 => "PUSH1",
            OpCode::PUSH2 => "PUSH2",
            OpCode::PUSH3 => "PUSH3",
            OpCode::PUSH4 => "PUSH4",
            OpCode::PUSH5 => "PUSH5",
            OpCode::PUSH6 => "PUSH6",
            OpCode::PUSH7 => "PUSH7",
            OpCode::PUSH8 => "PUSH8",
            OpCode::PUSH9 => "PUSH9",
            OpCode::PUSH10 => "PUSH10",
            OpCode::PUSH11 => "PUSH11",
            OpCode::PUSH12 => "PUSH12",
            OpCode::PUSH13 => "PUSH13",
            OpCode::PUSH14 => "PUSH14",
            OpCode::PUSH15 => "PUSH15",
            OpCode::PUSH16 => "PUSH16",
            OpCode::PUSH17 => "PUSH17",
            OpCode::PUSH18 => "PUSH18",
            OpCode::PUSH19 => "PUSH19",
            OpCode::PUSH20 => "PUSH20",
            OpCode::PUSH21 => "PUSH21",
            OpCode::PUSH22 => "PUSH22",
            OpCode::PUSH23 => "PUSH23",
            OpCode::PUSH24 => "PUSH24",
            OpCode::PUSH25 => "PUSH25",
            OpCode::PUSH26 => "PUSH26",
            OpCode::PUSH27 => "PUSH27",
            OpCode::PUSH28 => "PUSH28",
            OpCode::PUSH29 => "PUSH29",
            OpCode::PUSH30 => "PUSH30",
            OpCode::PUSH31 => "PUSH31",
            OpCode::PUSH32 => "PUSH32",
            OpCode::DUP1 => "DUP1",
            OpCode::DUP2 => "DUP2",
            OpCode::DUP3 => "DUP3",
            OpCode::DUP4 => "DUP4",
            OpCode::DUP5 => "DUP5",
            OpCode::DUP6 => "DUP6",
            OpCode::DUP7 => "DUP7",
            OpCode::DUP8 => "DUP8",
            OpCode::DUP9 => "DUP9",
            OpCode::DUP10 => "DUP10",
            OpCode::DUP11 => "DUP11",
            OpCode::DUP12 => "DUP12",
            OpCode::DUP13 => "DUP13",
            OpCode::DUP14 => "DUP14",
            OpCode::DUP15 => "DUP15",
            OpCode::DUP16 => "DUP16",
            OpCode::SWAP1 => "SWAP1",
            OpCode::SWAP2 => "SWAP2",
            OpCode::SWAP3 => "SWAP3",
            OpCode::SWAP4 => "SWAP4",
            OpCode::SWAP5 => "SWAP5",
            OpCode::SWAP6 => "SWAP6",
            OpCode::SWAP7 => "SWAP7",
            OpCode::SWAP8 => "SWAP8",
            OpCode::SWAP9 => "SWAP9",
            OpCode::SWAP10 => "SWAP10",
            OpCode::SWAP11 => "SWAP11",
            OpCode::SWAP12 => "SWAP12",
            OpCode::SWAP13 => "SWAP13",
            OpCode::SWAP14 => "SWAP14",
            OpCode::SWAP15 => "SWAP15",
            OpCode::SWAP16 => "SWAP16",
            OpCode::LOG0 => "LOG0",
            OpCode::LOG1 => "LOG1",
            OpCode::LOG2 => "LOG2",
            OpCode::LOG3 => "LOG3",
            OpCode::LOG4 => "LOG4",
            OpCode::CREATE => "CREATE",
            OpCode::CALL => "CALL",
            OpCode::CALLCODE => "CALLCODE",
            OpCode::RETURN => "RETURN",
            OpCode::DELEGATECALL => "DELEGATECALL",
            OpCode::CREATE2 => "CREATE2",
            OpCode::STATICCALL => "STATICCALL",
            OpCode::REVERT => "REVERT",
            OpCode::INVALID => "INVALID",
            OpCode::SELFDESTRUCT => "SELFDESTRUCT",
            _ => "UNDEFINED",
        }
    }

    pub fn push_size(self) -> Option<u8> {
        (self.to_u8() >= OpCode::PUSH1.to_u8() && self.to_u8() <= OpCode::PUSH32.to_u8())
            .then(|| self.to_u8() - OpCode::PUSH1.to_u8() + 1)
    }
}

impl Display for OpCode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let name = self.name();

        let n = if name == "UNDEFINED" {
            Cow::Owned(format!("UNDEFINED(0x{:02x})", self.0))
        } else {
            Cow::Borrowed(name)
        };
        write!(f, "{}", n)
    }
}


================================================
FILE: src/state.rs
================================================
use crate::common::{Message, Revision};
use arrayvec::ArrayVec;
use bytes::Bytes;
use ethereum_types::U256;
use getset::{Getters, MutGetters};
use serde::Serialize;

const SIZE: usize = 1024;

/// EVM stack.
#[derive(Clone, Debug, Default, Serialize)]
pub struct Stack(pub ArrayVec<U256, SIZE>);

impl Stack {
    pub const fn limit() -> usize {
        SIZE
    }

    fn get_pos(&self, pos: usize) -> usize {
        self.len() - 1 - pos
    }

    pub fn get(&self, pos: usize) -> &U256 {
        &self.0[self.get_pos(pos)]
    }

    pub fn get_mut(&mut self, pos: usize) -> &mut U256 {
        let pos = self.get_pos(pos);
        &mut self.0[pos]
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn push(&mut self, v: U256) {
        unsafe { self.0.push_unchecked(v) }
    }

    pub fn pop(&mut self) -> U256 {
        self.0.pop().expect("underflow")
    }

    pub fn swap_top(&mut self, pos: usize) {
        let top = self.0.len() - 1;
        let pos = self.get_pos(pos);
        self.0.swap(top, pos);
    }
}

pub type Memory = Vec<u8>;

/// EVM execution state.
#[derive(Clone, Debug, Getters, MutGetters)]
pub struct ExecutionState {
    #[getset(get = "pub", get_mut = "pub")]
    pub(crate) gas_left: i64,
    #[getset(get = "pub", get_mut = "pub")]
    pub(crate) stack: Stack,
    #[getset(get = "pub", get_mut = "pub")]
    pub(crate) memory: Memory,
    pub(crate) message: Message,
    pub(crate) evm_revision: Revision,
    #[getset(get = "pub", get_mut = "pub")]
    pub(crate) return_data: Bytes,
    pub(crate) output_data: Bytes,
}

impl ExecutionState {
    pub fn new(message: Message, evm_revision: Revision) -> Self {
        Self {
            gas_left: message.gas,
            stack: Default::default(),
            memory: Memory::with_capacity(4 * 1024),
            message,
            evm_revision,
            return_data: Default::default(),
            output_data: Bytes::new(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn stack() {
        let mut stack = Stack::default();

        let items = [0xde, 0xad, 0xbe, 0xef];

        for (i, item) in items.iter().copied().enumerate() {
            stack.push(item.into());
            assert_eq!(stack.len(), i + 1);
        }

        assert_eq!(*stack.get(2), 0xad.into());

        assert_eq!(stack.pop(), 0xef.into());

        assert_eq!(*stack.get(2), 0xde.into());
    }
}


================================================
FILE: src/tracing/mod.rs
================================================
use super::*;
use crate::state::*;
use serde::Serialize;

/// Passed into execution context to collect metrics.
pub trait Tracer {
    #[doc(hidden)]
    const DUMMY: bool = false;

    /// Called when execution starts.
    fn notify_execution_start(&mut self, revision: Revision, message: Message, code: Bytes);
    /// Called on each instruction.
    fn notify_instruction_start(&mut self, pc: usize, opcode: OpCode, state: &ExecutionState);
    /// Called when execution ends.
    fn notify_execution_end(&mut self, output: &Output);
}

/// Tracer which does nothing.
pub struct NoopTracer;

impl Tracer for NoopTracer {
    const DUMMY: bool = true;

    fn notify_execution_start(&mut self, _: Revision, _: Message, _: Bytes) {}

    fn notify_instruction_start(&mut self, _: usize, _: OpCode, _: &ExecutionState) {}

    fn notify_execution_end(&mut self, _: &Output) {}
}

#[derive(Serialize)]
struct ExecutionStart {
    pub depth: i32,
    pub rev: Revision,
    #[serde(rename = "static")]
    pub is_static: bool,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct InstructionStart {
    pub pc: usize,
    pub op: u8,
    pub op_name: &'static str,
    pub gas: i64,
    pub stack: Stack,
    pub memory_size: usize,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ExecutionEnd {
    pub error: Option<String>,
    pub gas: i64,
    pub gas_used: i64,
    pub output: String,
}

struct TracerContext {
    message: Message,
    code: Bytes,
}

/// Tracer which prints to stdout.
#[derive(Default)]
pub struct StdoutTracer {
    execution_stack: Vec<TracerContext>,
}

impl Tracer for StdoutTracer {
    fn notify_execution_start(&mut self, revision: Revision, message: Message, code: Bytes) {
        println!(
            "{}",
            serde_json::to_string(&ExecutionStart {
                depth: message.depth,
                rev: revision,
                is_static: message.is_static,
            })
            .unwrap()
        );
        self.execution_stack.push(TracerContext { message, code });
    }

    fn notify_instruction_start(&mut self, pc: usize, _: OpCode, state: &ExecutionState) {
        let context = self.execution_stack.last().unwrap();
        let opcode = OpCode(context.code[pc]);
        println!(
            "{}",
            serde_json::to_string(&InstructionStart {
                pc,
                op: opcode.0,
                op_name: opcode.name(),
                gas: state.gas_left,
                stack: state.stack.clone(),
                memory_size: state.memory.len()
            })
            .unwrap()
        )
    }

    fn notify_execution_end(&mut self, output: &Output) {
        let context = self.execution_stack.pop().unwrap();
        let error = match &output.status_code {
            StatusCode::Success => None,
            other => Some(other.to_string()),
        };
        let (gas_left, gas_used) = if error.is_none() {
            (output.gas_left, context.message.gas - output.gas_left)
        } else {
            (0, context.message.gas)
        };

        println!(
            "{}",
            serde_json::to_string(&ExecutionEnd {
                error,
                gas: gas_left,
                gas_used,
                output: hex::encode(&output.output_data),
            })
            .unwrap()
        )
    }
}


================================================
FILE: src/util/bytecode.rs
================================================
use crate::opcode::*;
use core::iter::repeat;
use ethereum_types::U256;
use std::ops::{Add, Mul};

/// EVM bytecode builder.
#[derive(Clone, Debug, PartialEq)]
pub struct Bytecode {
    inner: Vec<u8>,
}

impl Bytecode {
    pub const fn new() -> Self {
        Self { inner: Vec::new() }
    }

    pub fn append(mut self, b: impl IntoIterator<Item = u8>) -> Self {
        self.inner.append(&mut b.into_iter().collect::<Vec<_>>());
        self
    }

    pub fn append_bc(mut self, b: impl Into<Self>) -> Self {
        self.inner.append(&mut b.into().build());
        self
    }

    pub fn repeat(mut self, n: usize) -> Self {
        self.inner = repeat(self.inner.into_iter()).take(n).flatten().collect();
        self
    }

    pub fn pushv(self, value: impl Into<U256>) -> Self {
        let value = value.into();
        let b = <[u8; 32]>::from(value)
            .iter()
            .skip_while(|&&v| v == 0)
            .copied()
            .collect::<Vec<_>>();

        self.pushb(b)
    }

    pub fn pushb(mut self, b: impl IntoIterator<Item = u8>) -> Self {
        let mut b = b.into_iter().collect::<Vec<_>>();

        if b.is_empty() {
            b.push(0);
        }

        self.inner
            .extend_from_slice(&[(b.len() + OpCode::PUSH1.to_usize() - 1) as u8]);
        self.inner.append(&mut b);

        self
    }

    pub fn opcode(mut self, opcode: OpCode) -> Self {
        self.inner.push(opcode.to_u8());
        self
    }

    pub fn ret(mut self, index: impl Into<U256>, size: impl Into<U256>) -> Self {
        self = self.pushv(size);
        self = self.pushv(index);
        self = self.opcode(OpCode::RETURN);
        self
    }

    pub fn revert(mut self, index: impl Into<U256>, size: impl Into<U256>) -> Self {
        self = self.pushv(index);
        self = self.pushv(size);
        self = self.opcode(OpCode::REVERT);
        self
    }

    pub fn mstore(mut self, index: impl Into<U256>) -> Self {
        self = self.pushv(index);
        self = self.opcode(OpCode::MSTORE);
        self
    }

    pub fn mstore_value(mut self, index: impl Into<U256>, value: impl Into<U256>) -> Self {
        self = self.pushv(value);
        self = self.pushv(index);
        self = self.opcode(OpCode::MSTORE);
        self
    }

    pub fn mstore8(mut self, index: impl Into<U256>) -> Self {
        self = self.pushv(index);
        self = self.opcode(OpCode::MSTORE8);
        self
    }

    pub fn mstore8_value(mut self, index: impl Into<U256>, value: impl Into<U256>) -> Self {
        self = self.pushv(value);
        self = self.pushv(index);
        self = self.opcode(OpCode::MSTORE8);
        self
    }

    pub fn ret_top(self) -> Self {
        self.mstore(0).ret(0, 0x20)
    }

    pub fn jump(self, target: impl Into<U256>) -> Self {
        self.pushv(target).opcode(OpCode::JUMP)
    }

    pub fn jumpi(self, target: impl Into<Bytecode>, condition: impl Into<Bytecode>) -> Self {
        self.append(condition.into().build())
            .append(target.into().build())
            .opcode(OpCode::JUMPI)
    }

    pub fn sstore(self, index: impl Into<U256>, value: impl Into<U256>) -> Self {
        self.pushv(value).pushv(index).opcode(OpCode::SSTORE)
    }

    pub fn sload(self, index: impl Into<U256>) -> Self {
        self.pushv(index).opcode(OpCode::SLOAD)
    }

    pub fn build(self) -> Vec<u8> {
        self.inner
    }

    pub fn len(&self) -> usize {
        self.inner.len()
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

impl From<U256> for Bytecode {
    fn from(value: U256) -> Self {
        Self::new().pushv(value)
    }
}

impl From<OpCode> for Bytecode {
    fn from(opcode: OpCode) -> Self {
        Self::new().opcode(opcode)
    }
}

impl<const N: usize> From<[u8; N]> for Bytecode {
    fn from(inner: [u8; N]) -> Self {
        Self {
            inner: Vec::from(&inner as &[u8]),
        }
    }
}

impl From<Vec<u8>> for Bytecode {
    fn from(inner: Vec<u8>) -> Self {
        Self { inner }
    }
}

impl AsRef<[u8]> for Bytecode {
    fn as_ref(&self) -> &[u8] {
        &self.inner
    }
}

impl IntoIterator for Bytecode {
    type Item = u8;
    type IntoIter = <Vec<u8> as IntoIterator>::IntoIter;

    fn into_iter(self) -> Self::IntoIter {
        self.inner.into_iter()
    }
}

impl Mul<Bytecode> for usize {
    type Output = Bytecode;

    fn mul(self, rhs: Bytecode) -> Self::Output {
        repeat(rhs)
            .take(self)
            .fold(Bytecode::new(), |acc, b| acc.append_bc(b))
    }
}

impl Mul<OpCode> for usize {
    type Output = Bytecode;

    fn mul(self, rhs: OpCode) -> Self::Output {
        self.mul(Bytecode::from(rhs))
    }
}

impl<T: Into<Bytecode>> Add<T> for Bytecode {
    type Output = Bytecode;

    fn add(self, rhs: T) -> Self::Output {
        self.append_bc(rhs)
    }
}

pub struct CallInstruction {
    op: OpCode,
    address: U256,
    gas: U256,
    value: U256,
    input: U256,
    input_size: U256,
    output: U256,
    output_size: U256,
}

impl CallInstruction {
    fn new(op: OpCode, address: impl Into<U256>) -> Self {
        Self {
            op,
            address: address.into(),
            gas: 0.into(),
            value: 0.into(),
            input: 0.into(),
            input_size: 0.into(),
            output: 0.into(),
            output_size: 0.into(),
        }
    }

    pub fn delegatecall(address: impl Into<U256>) -> Self {
        Self::new(OpCode::DELEGATECALL, address)
    }

    pub fn staticcall(address: impl Into<U256>) -> Self {
        Self::new(OpCode::STATICCALL, address)
    }

    pub fn call(address: impl Into<U256>) -> Self {
        Self::new(OpCode::CALL, address)
    }

    pub fn callcode(address: impl Into<U256>) -> Self {
        Self::new(OpCode::CALLCODE, address)
    }

    pub fn opcode(&self) -> OpCode {
        self.op
    }

    pub fn gas(mut self, gas: impl Into<U256>) -> Self {
        self.gas = gas.into();
        self
    }

    pub fn value(mut self, value: impl Into<U256>) -> Self {
        self.value = value.into();
        self
    }

    pub fn input(mut self, index: impl Into<U256>, size: impl Into<U256>) -> Self {
        self.input = index.into();
        self.input_size = size.into();
        self
    }

    pub fn output(mut self, index: impl Into<U256>, size: impl Into<U256>) -> Self {
        self.output = index.into();
        self.output_size = size.into();
        self
    }
}

impl From<CallInstruction> for Bytecode {
    fn from(call: CallInstruction) -> Self {
        let mut b = Bytecode::new()
            .pushv(call.output_size)
            .pushv(call.output)
            .pushv(call.input_size)
            .pushv(call.input);
        if call.op == OpCode::CALL || call.op == OpCode::CALLCODE {
            b = b.pushv(call.value);
        }
        b.pushv(call.address).pushv(call.gas).opcode(call.op)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn multiply_bytecode() {
        assert_eq!(
            3 * Bytecode::new().opcode(OpCode::POP),
            Bytecode::new()
                .opcode(OpCode::POP)
                .opcode(OpCode::POP)
                .opcode(OpCode::POP)
        )
    }
}


================================================
FILE: src/util/mocked_host.rs
================================================
use crate::{host::*, *};
use bytes::Bytes;
use ethereum_types::*;
use hex_literal::hex;
use parking_lot::Mutex;
use std::{cmp::min, collections::HashMap};

/// LOG record.
#[derive(Clone, Debug, PartialEq)]
pub struct LogRecord {
    /// The address of the account which created the log.
    pub creator: Address,

    /// The data attached to the log.
    pub data: Bytes,

    /// The log topics.
    pub topics: Vec<U256>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct SelfdestructRecord {
    /// The address of the account which has self-destructed.
    pub selfdestructed: Address,

    /// The address of the beneficiary account.
    pub beneficiary: Address,
}

#[derive(Clone, Debug, Default)]
pub struct StorageValue {
    pub value: U256,
    pub dirty: bool,
    pub access_status: AccessStatus,
}

#[derive(Clone, Debug, Default)]
pub struct Account {
    /// The account nonce.
    pub nonce: u64,
    /// The account code.
    pub code: Bytes,
    /// The code hash. Can be a value not related to the actual code.
    pub code_hash: U256,
    /// The account balance.
    pub balance: U256,
    /// The account storage map.
    pub storage: HashMap<U256, StorageValue>,
}

const MAX_RECORDED_ACCOUNT_ACCESSES: usize = 200;
const MAX_RECORDED_CALLS: usize = 100;

#[derive(Clone, Debug, Default)]
pub struct Records {
    /// The copy of call inputs for the recorded_calls record.
    pub call_inputs: Vec<Bytes>,

    pub blockhashes: Vec<u64>,
    pub account_accesses: Vec<Address>,
    pub calls: Vec<Message>,
    pub logs: Vec<LogRecord>,
    pub selfdestructs: Vec<SelfdestructRecord>,
}

#[derive(Debug)]
pub struct MockedHost {
    pub accounts: HashMap<Address, Account>,
    pub tx_context: TxContext,
    pub block_hash: U256,
    pub call_result: Output,
    pub recorded: Mutex<Records>,
}

impl Clone for MockedHost {
    fn clone(&self) -> Self {
        Self {
            accounts: self.accounts.clone(),
            tx_context: self.tx_context.clone(),
            block_hash: self.block_hash,
            call_result: self.call_result.clone(),
            recorded: Mutex::new(self.recorded.lock().clone()),
        }
    }
}

impl Default for MockedHost {
    fn default() -> Self {
        Self {
            accounts: Default::default(),
            tx_context: TxContext {
                tx_gas_price: U256::zero(),
                tx_origin: Address::zero(),
                block_coinbase: Address::zero(),
                block_number: 0,
                block_timestamp: 0,
                block_gas_limit: 0,
                block_difficulty: U256::zero(),
                chain_id: U256::zero(),
                block_base_fee: U256::zero(),
            },
            block_hash: U256::zero(),
            call_result: Output {
                status_code: StatusCode::Success,
                gas_left: 0,
                output_data: Bytes::new(),
                create_address: Some(Address::zero()),
            },
            recorded: Default::default(),
        }
    }
}

impl Records {
    fn record_account_access(&mut self, address: Address) {
        if self.account_accesses.len() < MAX_RECORDED_ACCOUNT_ACCESSES {
            self.account_accesses.push(address)
        }
    }
}

impl crate::Host for MockedHost {
    fn account_exists(&self, address: ethereum_types::Address) -> bool {
        self.recorded.lock().record_account_access(address);
        self.accounts.contains_key(&address)
    }

    fn get_storage(&self, address: ethereum_types::Address, key: U256) -> U256 {
        self.recorded.lock().record_account_access(address);

        self.accounts
            .get(&address)
            .and_then(|account| account.storage.get(&key).map(|value| value.value))
            .unwrap_or_else(U256::zero)
    }

    fn set_storage(
        &mut self,
        address: ethereum_types::Address,
        key: U256,
        value: U256,
    ) -> StorageStatus {
        self.recorded.lock().record_account_access(address);

        // Get the reference to the old value.
        // This will create the account in case it was not present.
        // This is convenient for unit testing and standalone EVM execution to preserve the
        // storage values after the execution terminates.
        let old = self
            .accounts
            .entry(address)
            .or_default()
            .storage
            .entry(key)
            .or_default();

        // Follow https://eips.ethereum.org/EIPS/eip-1283 specification.
        // WARNING! This is not complete implementation as refund is not handled here.

        if old.value == value {
            return StorageStatus::Unchanged;
        }

        let status = if !old.dirty {
            old.dirty = true;
            if old.value.is_zero() {
                StorageStatus::Added
            } else if !value.is_zero() {
                StorageStatus::Modified
            } else {
                StorageStatus::Deleted
            }
        } else {
            StorageStatus::ModifiedAgain
        };

        old.value = value;

        status
    }

    fn get_balance(&self, address: ethereum_types::Address) -> ethereum_types::U256 {
        self.recorded.lock().record_account_access(address);

        self.accounts
            .get(&address)
            .map(|acc| acc.balance)
            .unwrap_or_else(U256::zero)
    }

    fn get_code_size(&self, address: ethereum_types::Address) -> ethereum_types::U256 {
        self.recorded.lock().record_account_access(address);

        self.accounts
            .get(&address)
            .map(|acc| acc.code.len().into())
            .unwrap_or_else(U256::zero)
    }

    fn get_code_hash(&self, address: ethereum_types::Address) -> U256 {
        self.recorded.lock().record_account_access(address);

        self.accounts
            .get(&address)
            .map(|acc| acc.code_hash)
            .unwrap_or_else(U256::zero)
    }

    fn copy_code(&self, address: Address, code_offset: usize, buffer: &mut [u8]) -> usize {
        self.recorded.lock().record_account_access(address);

        self.accounts
            .get(&address)
            .map(|acc| {
                let code = &acc.code;

                if code_offset >= code.len() {
                    return 0;
                }

                let n = min(buffer.len(), code.len() - code_offset);

                buffer[..n].copy_from_slice(&code[code_offset..code_offset + n]);

                n
            })
            .unwrap_or(0)
    }

    fn selfdestruct(
        &mut self,
        address: ethereum_types::Address,
        beneficiary: ethereum_types::Address,
    ) {
        let mut r = self.recorded.lock();

        r.record_account_access(address);
        r.selfdestructs.push(SelfdestructRecord {
            selfdestructed: address,
            beneficiary,
        });
    }

    fn call(&mut self, msg: &Message) -> Output {
        let mut r = self.recorded.lock();

        r.record_account_access(msg.recipient);

        if r.calls.len() < MAX_RECORDED_CALLS {
            r.calls.push(msg.clone());
            let call_msg = msg;
            if !call_msg.input_data.is_empty() {
                r.call_inputs.push(call_msg.input_data.clone());
            }
        }
        self.call_result.clone()
    }

    fn get_tx_context(&self) -> TxContext {
        self.tx_context.clone()
    }

    fn get_block_hash(&self, block_number: u64) -> U256 {
        self.recorded.lock().blockhashes.push(block_number);
        self.block_hash
    }

    fn emit_log(&mut self, address: ethereum_types::Address, data: &[u8], topics: &[U256]) {
        self.recorded.lock().logs.push(LogRecord {
            creator: address,
            data: data.to_vec().into(),
            topics: topics.to_vec(),
        });
    }

    fn access_account(&mut self, address: ethereum_types::Address) -> AccessStatus {
        let mut r = self.recorded.lock();

        // Check if the address have been already accessed.
        let already_accessed = r.account_accesses.iter().any(|&a| a == address);

        r.record_account_access(address);

        if address.0 >= hex!("0000000000000000000000000000000000000001")
            && address.0 <= hex!("0000000000000000000000000000000000000009")
        {
            return AccessStatus::Warm;
        }

        if already_accessed {
            AccessStatus::Warm
        } else {
            AccessStatus::Cold
        }
    }

    fn access_storage(&mut self, address: ethereum_types::Address, key: U256) -> AccessStatus {
        let value = self
            .accounts
            .entry(address)
            .or_default()
            .storage
            .entry(key)
            .or_default();
        let access_status = value.access_status;
        value.access_status = AccessStatus::Warm;
        access_status
    }
}


================================================
FILE: src/util/mod.rs
================================================
mod bytecode;
pub mod mocked_host;
mod tester;

pub use bytecode::*;
pub use tester::*;


================================================
FILE: src/util/tester.rs
================================================
use crate::{
    tracing::*,
    util::{mocked_host::*, *},
    *,
};
use bytes::Bytes;
use educe::Educe;
use ethereum_types::{Address, U256};
use std::sync::Arc;

fn exec(
    host: &mut MockedHost,
    revision: Revision,
    message: Message,
    code: Vec<u8>,
    collect_traces: bool,
) -> Output {
    // Add EIP-2929 tweak.
    if revision >= Revision::Berlin {
        host.access_account(message.sender);
        host.access_account(message.recipient);
    }
    let code = AnalyzedCode::analyze(code);

    if collect_traces {
        code.execute(host, &mut StdoutTracer::default(), None, message, revision)
    } else {
        code.execute(host, &mut NoopTracer, None, message, revision)
    }
}

#[derive(Clone, Copy, Debug)]
enum GasCheck {
    Used(i64),
    Left(i64),
}

/// Tester that executes EVM bytecode with `MockedHost` context and runs set checks.
#[derive(Clone, Educe)]
#[educe(Debug)]
#[must_use]
pub struct EvmTester {
    host: MockedHost,
    #[educe(Debug(ignore))]
    apply_host_fns: Vec<Arc<dyn Fn(&mut MockedHost, &Message) + 'static>>,
    #[educe(Debug(ignore))]
    inspect_output_fn: Arc<dyn Fn(&[u8]) + 'static>,
    #[educe(Debug(ignore))]
    inspect_host_fn: Arc<dyn Fn(&MockedHost, &Message) + 'static>,
    #[educe(Debug(ignore))]
    inspect_fn: Arc<dyn Fn(&MockedHost, &Message, &[u8]) + 'static>,
    revision: Revision,
    message: Message,
    code: Vec<u8>,
    gas_check: Option<GasCheck>,
    expected_status_codes: Option<Vec<StatusCode>>,
    expected_output_data: Option<Vec<u8>>,
    collect_traces: bool,
}

impl Default for EvmTester {
    fn default() -> Self {
        Self::new()
    }
}

impl EvmTester {
    /// Create new `EvmTester`.
    pub fn new() -> Self {
        Self {
            host: MockedHost::default(),
            apply_host_fns: vec![],
            inspect_output_fn: Arc::new(|_| ()),
            inspect_host_fn: Arc::new(|_, _| ()),
            inspect_fn: Arc::new(|_, _, _| ()),
            revision: Revision::Byzantium,
            message: Message {
                kind: CallKind::Call,
                is_static: false,
                depth: 0,
                gas: i64::MAX,
                recipient: Address::zero(),
                code_address: Address::zero(),
                sender: Address::zero(),
                input_data: Bytes::new(),
                value: 0.into(),
            },
            code: Vec::new(),
            gas_check: None,
            expected_status_codes: None,
            expected_output_data: None,
            collect_traces: false,
        }
    }

    /// Set code to be executed.
    pub fn code(mut self, code: impl Into<Bytecode>) -> Self {
        self.code = code.into().build();
        self
    }

    /// Queue function that will modify the host before execution.
    pub fn apply_host_fn(mut self, host_fn: impl Fn(&mut MockedHost, &Message) + 'static) -> Self {
        self.apply_host_fns.push(Arc::new(host_fn));
        self
    }

    /// Set EVM revision for this tester.
    pub fn revision(mut self, revision: Revision) -> Self {
        self.revision = revision;
        self
    }

    /// Set message depth.
    pub fn depth(mut self, depth: u16) -> Self {
        self.message.depth = depth.into();
        self
    }

    /// Set provided gas.
    pub fn gas(mut self, gas: i64) -> Self {
        self.message.gas = gas;
        self
    }

    /// Set static message flag.
    pub fn set_static(mut self, is_static: bool) -> Self {
        self.message.is_static = is_static;
        self
    }

    /// Set message destination.
    pub fn destination(mut self, destination: impl Into<Address>) -> Self {
        self.message.recipient = destination.into();
        self
    }

    /// Set message sender.
    pub fn sender(mut self, sender: impl Into<Address>) -> Self {
        self.message.sender = sender.into();
        self
    }

    /// Set message sender.
    pub fn value(mut self, value: impl Into<U256>) -> Self {
        self.message.value = value.into();
        self
    }

    /// Check how much gas will be used. Mutually exclusive with `EvmTester::gas_left`.
    pub fn gas_used(mut self, expected_gas_used: i64) -> Self {
        self.gas_check = Some(GasCheck::Used(expected_gas_used));
        self
    }

    /// Check how much gas will be left after execution. Mutually exclusive with `EvmTester::gas_used`.
    pub fn gas_left(mut self, expected_gas_left: i64) -> Self {
        self.gas_check = Some(GasCheck::Left(expected_gas_left));
        self
    }

    /// Set provided input data.
    pub fn input(mut self, input: impl Into<Bytes>) -> Self {
        self.message.input_data = input.into();
        self
    }

    /// Check returned status.
    pub fn status(mut self, expected_status_code: StatusCode) -> Self {
        self.expected_status_codes = Some(vec![expected_status_code]);
        self
    }

    /// Check returned status to be one of these.
    pub fn status_one_of<const N: usize>(mut self, expected_status_code: [StatusCode; N]) -> Self {
        self.expected_status_codes = Some(expected_status_code.to_vec());
        self
    }

    /// Check output to be equal to provided integer.
    pub fn output_value(mut self, expected_output_data: impl Into<U256>) -> Self {
        let mut data = [0; 32];
        expected_output_data.into().to_big_endian(&mut data);
        self.expected_output_data = Some(data.to_vec());
        self
    }

    /// Check output data to be equal to provided byte string.
    pub fn output_data(mut self, expected_output_data: impl Into<Vec<u8>>) -> Self {
        self.expected_output_data = Some(expected_output_data.into());
        self
    }

    /// Inspect output with provided function.
    pub fn inspect_output(mut self, inspect_output_fn: impl Fn(&[u8]) + 'static) -> Self {
        self.inspect_output_fn = Arc::new(inspect_output_fn);
        self
    }

    /// Inspect host with provided function.
    pub fn inspect_host(mut self, f: impl Fn(&MockedHost, &Message) + 'static) -> Self {
        self.inspect_host_fn = Arc::new(f);
        self
    }

    /// Inspect host and output with provided function.
    pub fn inspect(mut self, f: impl Fn(&MockedHost, &Message, &[u8]) + 'static) -> Self {
        self.inspect_fn = Arc::new(f);
        self
    }

    pub fn collect_traces(mut self, doit: bool) -> Self {
        self.collect_traces = doit;
        self
    }

    /// Execute provided code, run checks and return bytecode returned by EVM.
    pub fn check_and_get_result(self) -> Output {
        if self.collect_traces {
            println!("Executing code: {}", hex::encode(&self.code));
        }
        let mut host = self.host;
        for f in self.apply_host_fns {
            (f)(&mut host, &self.message);
        }
        let output = exec(
            &mut host,
            self.revision,
            self.message.clone(),
            self.code,
            self.collect_traces,
        );

        if let Some(status_codes) = self.expected_status_codes {
            assert!(
                status_codes.iter().any(|s| *s == output.status_code),
                "Status code mismatch: {}, but must be one of {:?}",
                output.status_code,
                status_codes
            );
        }

        if let Some(gas_check) = self.gas_check {
            match gas_check {
                GasCheck::Used(used) => assert_eq!(self.message.gas - output.gas_left, used),
                GasCheck::Left(left) => assert_eq!(output.gas_left, left),
            }
        }

        if let Some(expected_data) = &self.expected_output_data {
            assert_eq!(&*output.output_data, expected_data);
        }

        (self.inspect_output_fn)(&*output.output_data);
        (self.inspect_host_fn)(&host, &self.message);
        (self.inspect_fn)(&host, &self.message, &*output.output_data);

        output
    }

    /// Execute provided code and run checks.
    pub fn check(self) {
        self.check_and_get_result();
    }
}


================================================
FILE: tests/basefee.rs
================================================
use evmodin::{opcode::*, util::*, *};

#[test]
fn basefee_pre_london() {
    EvmTester::new()
        .revision(Revision::Berlin)
        .code(Bytecode::new().opcode(OpCode::BASEFEE))
        .status(StatusCode::UndefinedInstruction)
        .check()
}

#[test]
fn basefee_nominal_case() {
    // https://eips.ethereum.org/EIPS/eip-3198#nominal-case
    let t = EvmTester::new()
        .revision(Revision::London)
        .apply_host_fn(|host, _| {
            host.tx_context.block_base_fee = 7.into();
        });
    t.clone()
        .code(Bytecode::new().opcode(OpCode::BASEFEE).opcode(OpCode::STOP))
        .status(StatusCode::Success)
        .gas_used(2)
        .check();

    t.code(Bytecode::new().opcode(OpCode::BASEFEE).ret_top())
        .status(StatusCode::Success)
        .gas_used(17)
        .output_value(7)
        .check()
}


================================================
FILE: tests/call.rs
================================================
use bytes::Bytes;
use core::iter::repeat_with;
use ethereum_types::*;
use evmodin::{opcode::*, util::*, *};
use hex_literal::hex;

#[test]
fn delegatecall() {
    let mut value = H256::zero();
    value.0[17] = 0xfe;

    EvmTester::new()
        .code(
            Bytecode::new()
                .append(hex!("6001600003600052")) // m[0] = 0xffffff...
                .append(hex!("600560046003600260016103e8f4")) // DELEGATECALL(1000, 0x01, ...)
                .append(hex!("60086000f3")),
        )
        .apply_host_fn(|host, _| {
            host.call_result.output_data = (&hex!("0a0b0c") as &[u8]).into();
            host.call_result.gas_left = 1;
        })
        .value(value.0)
        .gas(1700)
        .gas_used(1690)
        .status(StatusCode::Success)
        .output_data(hex!("ffffffff0a0b0cff"))
        .inspect_host(move |host, _| {
            let gas_left = 1700 - 736;

            let r = host.recorded.lock();

            assert_eq!(r.calls.len(), 1);
            let call_msg = r.calls.last().unwrap();
            assert_eq!(call_msg.gas, gas_left - gas_left / 64);
            assert_eq!(call_msg.input_data.len(), 3);
            assert_eq!(<[u8; 32]>::from(call_msg.value)[17], 0xfe);
        })
        .check()
}

/// Checks if DELEGATECALL forwards the "static" flag.
#[test]
fn delegatecall_static() {
    EvmTester::new()
        .set_static(true)
        .code(Bytecode::new().append_bc(CallInstruction::delegatecall(0).gas(1)))
        .status(StatusCode::Success)
        .gas_used(719)
        .inspect_host(|host, _| {
            let r = host.recorded.lock();

            assert_eq!(r.calls.len(), 1);
            let call_msg = r.calls.last().unwrap();
            assert_eq!(call_msg.gas, 1);
            assert!(call_msg.is_static);
        })
        .check()
}

#[test]
fn delegatecall_oog_depth_limit() {
    let t = EvmTester::new()
        .revision(Revision::Homestead)
        .depth(1024)
        .code(
            Bytecode::new()
                .append_bc(CallInstruction::delegatecall(0).gas(16))
                .ret_top(),
        );

    t.clone()
        .status(StatusCode::Success)
        .gas_used(73)
        .output_value(0)
        .check();

    t.gas(73).status(StatusCode::OutOfGas).check();
}

#[test]
fn create() {
    let address = Address::zero();

    EvmTester::new()
        .apply_host_fn(move |host, _| {
            host.accounts.entry(address).or_default().balance = 1.into();

            host.call_result.output_data = (&hex!("0a0b0c") as &[u8]).into();
            host.call_result
                .create_address
                .get_or_insert_with(Address::zero)
                .0[10] = 0xcc;
            host.call_result.gas_left = 200000;
        })
        .gas(300000)
        .code(hex!("602060006001f0600155"))
        .gas_used(115816)
        .status(StatusCode::Success)
        .inspect_host(move |host, _| {
            let key = 1.into();
            assert_eq!(
                H256(host.accounts[&address].storage[&key].value.into()).0[22],
                0xcc
            );

            let r = host.recorded.lock();
            assert_eq!(r.calls.len(), 1);
            assert_eq!(r.calls.last().unwrap().input_data.len(), 0x20);
        })
        .check()
}

#[test]
fn create_gas() {
    for rev in [Revision::Homestead, Revision::Tangerine] {
        EvmTester::new()
            .revision(rev)
            .gas(50000)
            .code(hex!("60008080f0"))
            .status(StatusCode::Success)
            .gas_used(if rev == Revision::Homestead {
                50000
            } else {
                49719
            })
            .inspect_host(move |host, _| {
                let r = host.recorded.lock();
                assert_eq!(r.calls.len(), 1);
                assert_eq!(
                    r.calls.last().unwrap().gas,
                    if rev == Revision::Homestead {
                        17991
                    } else {
                        17710
                    }
                );
            })
            .check()
    }
}

#[test]
fn create2() {
    let address = Address::zero();
    EvmTester::new()
        .revision(Revision::Constantinople)
        .apply_host_fn(move |host, _| {
            host.accounts.entry(address).or_default().balance = 1.into();

            host.call_result.output_data = (&hex!("0a0b0c") as &[u8]).into();
            host.call_result
                .create_address
                .get_or_insert_with(Address::zero)
                .0[10] = 0xc2;
            host.call_result.gas_left = 200000;
        })
        .gas(300000)
        .code(hex!("605a604160006001f5600155"))
        .gas_used(115817)
        .status(StatusCode::Success)
        .inspect_host(move |host, _| {
            let r = host.recorded.lock();

            assert_eq!(r.calls.len(), 1);

            let call_msg = r.calls.last().unwrap();
            assert_eq!(call_msg.kind, CallKind::Create2 { salt: 0x5a.into() });
            assert_eq!(call_msg.gas, 263775);

            assert_eq!(
                H256(host.accounts[&address].storage[&1.into()].value.into()).0[22],
                0xc2
            );

            assert_eq!(call_msg.input_data.len(), 0x41);
        })
        .check()
}

#[test]
fn create2_salt_cost() {
    let t = EvmTester::new()
        .revision(Revision::Constantinople)
        .code(hex!("600060208180f5"));

    t.clone()
        .gas(32021)
        .status(StatusCode::Success)
        .gas_left(0)
        .inspect_host(|host, _| {
            let r = host.recorded.lock();

            assert_eq!(r.calls.len(), 1);
            assert_eq!(
                r.calls.last().unwrap().kind,
                CallKind::Create2 { salt: U256::zero() }
            );
            assert_eq!(r.calls.last().unwrap().depth, 1);
        })
        .check();

    t.gas(32021 - 1)
        .status(StatusCode::OutOfGas)
        .gas_left(0)
        .inspect_host(|host, _| {
            // No another CREATE2.
            assert_eq!(host.recorded.lock().calls.len(), 0)
        })
        .check()
}

#[test]
fn create_balance_too_low() {
    for op in [OpCode::CREATE, OpCode::CREATE2] {
        EvmTester::new()
            .revision(Revision::Constantinople)
            .apply_host_fn(|host, _| {
                host.accounts.entry(Address::zero()).or_default().balance = 1.into();
            })
            .code(
                Bytecode::new()
                    .pushv(2)
                    .opcode(OpCode::DUP1)
                    .opcode(OpCode::DUP1)
                    .opcode(OpCode::DUP1)
                    .opcode(op)
                    .ret_top(),
            )
            .status(StatusCode::Success)
            .output_value(0)
            .inspect_host(|host, _| {
                assert_eq!(host.recorded.lock().calls, []);
            })
            .check()
    }
}

#[test]
fn create_failure() {
    for op in [OpCode::CREATE, OpCode::CREATE2] {
        let mut create_address = Address::zero();
        create_address.0[19] = 0xce;
        let t = EvmTester::new()
            .apply_host_fn(move |host, _| {
                host.call_result.create_address = Some(create_address);
            })
            .revision(Revision::Constantinople)
            .code(
                Bytecode::new()
                    .pushv(0)
                    .opcode(OpCode::DUP1)
                    .opcode(OpCode::DUP1)
                    .opcode(OpCode::DUP1)
                    .opcode(op)
                    .ret_top(),
            );

        t.clone()
            .apply_host_fn(|host, _| {
                host.call_result.status_code = StatusCode::Success;
            })
            .status(StatusCode::Success)
            .output_data(H256::from(create_address).to_fixed_bytes())
            .inspect_host(move |host, _| {
                let r = host.recorded.lock();

                assert_eq!(r.calls.len(), 1);
                assert_eq!(
                    r.calls.last().unwrap().kind,
                    if op == OpCode::CREATE {
                        CallKind::Create
                    } else {
                        CallKind::Create2 { salt: U256::zero() }
                    }
                );
            })
            .check();

        t.clone()
            .apply_host_fn(|host, _| {
                host.call_result.status_code = StatusCode::Revert;
            })
            .status(StatusCode::Success)
            .output_value(0)
            .inspect_host(move |host, _| {
                let r = host.recorded.lock();

                assert_eq!(r.calls.len(), 1);
                assert_eq!(
                    r.calls.last().unwrap().kind,
                    if op == OpCode::CREATE {
                        CallKind::Create
                    } else {
                        CallKind::Create2 { salt: U256::zero() }
                    }
                );
            })
            .check();

        t.clone()
            .apply_host_fn(|host, _| {
                host.call_result.status_code = StatusCode::Failure;
            })
            .status(StatusCode::Success)
            .output_value(0)
            .inspect_host(move |host, _| {
                let r = host.recorded.lock();

                assert_eq!(r.calls.len(), 1);
                assert_eq!(
                    r.calls.last().unwrap().kind,
                    if op == OpCode::CREATE {
                        CallKind::Create
                    } else {
                        CallKind::Create2 { salt: U256::zero() }
                    }
                );
            })
            .check();
    }
}

#[test]
fn call_failing_with_value() {
    for op in [OpCode::CALL, OpCode::CALLCODE] {
        let t = EvmTester::new()
            .apply_host_fn(|host, _| {
                host.accounts
                    .entry(hex!("00000000000000000000000000000000000000aa").into())
                    .or_default();
            })
            .code(
                Bytecode::new()
                    .pushv(0xff)
                    .pushv(0)
                    .opcode(OpCode::DUP2)
                    .opcode(OpCode::DUP2)
                    .pushv(1)
                    .pushv(0xaa)
                    .pushv(0x8000)
                    .opcode(op)
                    .opcode(OpCode::POP),
            );

        // Fails on balance check.
        t.clone()
            .gas(12000)
            .status(StatusCode::Success)
            .gas_used(7447)
            .inspect_host(|host, _| {
                // There was no call().
                assert_eq!(host.recorded.lock().calls, []);
            })
            .check();

        // Fails on value transfer additional cost - minimum gas limit that triggers this condition.
        t.clone()
            .gas(747)
            .status(StatusCode::OutOfGas)
            .inspect_host(|host, _| {
                // There was no call().
                assert_eq!(host.recorded.lock().calls, []);
            })
            .check();

        // Fails on value transfer additional cost - maximum gas limit that triggers this condition.
        t.clone()
            .gas(744 + 9000)
            .status(StatusCode::OutOfGas)
            .inspect_host(|host, _| {
                // There was no call().
                assert_eq!(host.recorded.lock().calls, []);
            })
            .check();
    }
}

#[test]
fn call_with_value() {
    let call_sender = hex!("5e4d00000000000000000000000000000000d4e5").into();
    let call_dst = hex!("00000000000000000000000000000000000000aa").into();

    EvmTester::new()
        .code(hex!("60ff600060ff6000600160aa618000f150"))
        .destination(call_sender)
        .apply_host_fn(move |host, msg| {
            host.accounts.entry(msg.recipient).or_default().balance = 1.into();
            host.accounts.entry(call_dst).or_default();
            host.call_result.gas_left = 1.into();
        })
        .gas(40000)
        .gas_used(7447 + 32082)
        .status(StatusCode::Success)
        .inspect_host(move |host, _| {
            let r = host.recorded.lock();
            assert_eq!(r.calls.len(), 1);
            let call_msg = &r.calls[0];
            assert_eq!(call_msg.kind, CallKind::Call);
            assert_eq!(call_msg.depth, 1);
            assert_eq!(call_msg.gas, 32083);
            assert_eq!(call_msg.recipient, call_dst);
            assert_eq!(call_msg.sender, call_sender);
        })
        .check()
}

#[test]
fn call_with_value_depth_limit() {
    let mut call_dst = Address::zero();
    call_dst.0[19] = 0xaa;

    EvmTester::new()
        .depth(1024)
        .apply_host_fn(move |host, _| {
            host.accounts.entry(call_dst).or_default();
        })
        .code(hex!("60ff600060ff6000600160aa618000f150"))
        .gas_used(7447)
        .status(StatusCode::Success)
        .inspect_host(|host, _| {
            assert_eq!(host.recorded.lock().calls, []);
        })
        .check()
}

#[test]
fn call_depth_limit() {
    for op in [
        OpCode::CALL,
        OpCode::CALLCODE,
        OpCode::DELEGATECALL,
        OpCode::STATICCALL,
        OpCode::CREATE,
        OpCode::CREATE2,
    ] {
        EvmTester::new()
            .revision(Revision::Constantinople)
            .depth(1024)
            .code(
                Bytecode::new()
                    .pushv(0)
                    .opcode(OpCode::DUP1)
                    .opcode(OpCode::DUP1)
                    .opcode(OpCode::DUP1)
                    .opcode(OpCode::DUP1)
                    .opcode(OpCode::DUP1)
                    .opcode(OpCode::DUP1)
                    .opcode(op)
                    .ret_top()
                    .opcode(OpCode::INVALID),
            )
            .status(StatusCode::Success)
            .inspect_host(|host, _| {
                assert_eq!(host.recorded.lock().calls, []);
            })
            .output_value(0)
            .check()
    }
}

#[test]
fn call_output() {
    for op in [
        OpCode::CALL,
        OpCode::CALLCODE,
        OpCode
Download .txt
gitextract_wugh22sf/

├── .github/
│   └── workflows/
│       └── main.yml
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── src/
│   ├── common.rs
│   ├── continuation/
│   │   ├── interrupt.rs
│   │   ├── interrupt_data.rs
│   │   ├── mod.rs
│   │   └── resume_data.rs
│   ├── evmc.rs
│   ├── host.rs
│   ├── instructions/
│   │   ├── arithmetic.rs
│   │   ├── bitwise.rs
│   │   ├── boolean.rs
│   │   ├── call.rs
│   │   ├── control.rs
│   │   ├── external.rs
│   │   ├── instruction_table.rs
│   │   ├── memory.rs
│   │   ├── mod.rs
│   │   ├── properties.rs
│   │   └── stack_manip.rs
│   ├── interpreter.rs
│   ├── lib.rs
│   ├── opcode.rs
│   ├── state.rs
│   ├── tracing/
│   │   └── mod.rs
│   └── util/
│       ├── bytecode.rs
│       ├── mocked_host.rs
│       ├── mod.rs
│       └── tester.rs
└── tests/
    ├── basefee.rs
    ├── call.rs
    ├── eip2929.rs
    ├── execute.rs
    ├── other.rs
    └── state.rs
Download .txt
SYMBOL INDEX (640 symbols across 30 files)

FILE: src/common.rs
  type Revision (line 8) | pub enum Revision {
    method iter (line 45) | pub fn iter() -> impl IntoIterator<Item = Self> {
    method latest (line 61) | pub const fn latest() -> Self {
    method len (line 65) | pub const fn len() -> usize {
  type StatusCode (line 73) | pub enum StatusCode {
  type CallKind (line 159) | pub enum CallKind {
  type Message (line 170) | pub struct Message {
    method from (line 213) | fn from(msg: CreateMessage) -> Self {
  type CreateMessage (line 203) | pub struct CreateMessage {
  type Output (line 234) | pub struct Output {
    method from (line 257) | fn from(
  type SuccessfulOutput (line 247) | pub struct SuccessfulOutput {
  function u256_to_address (line 277) | pub(crate) fn u256_to_address(v: U256) -> Address {
  function address_to_u256 (line 281) | pub(crate) fn address_to_u256(v: Address) -> U256 {

FILE: src/continuation/interrupt.rs
  type InterruptVariant (line 121) | pub enum InterruptVariant {

FILE: src/continuation/interrupt_data.rs
  type InstructionStart (line 4) | pub struct InstructionStart {
  type AccountExists (line 11) | pub struct AccountExists {
  type GetStorage (line 16) | pub struct GetStorage {
  type SetStorage (line 22) | pub struct SetStorage {
  type GetBalance (line 29) | pub struct GetBalance {
  type GetCodeSize (line 34) | pub struct GetCodeSize {
  type GetCodeHash (line 39) | pub struct GetCodeHash {
  type CopyCode (line 44) | pub struct CopyCode {
  type Selfdestruct (line 51) | pub struct Selfdestruct {
  type Call (line 57) | pub enum Call {
  type GetBlockHash (line 63) | pub struct GetBlockHash {
  type EmitLog (line 68) | pub struct EmitLog {
  type AccessAccount (line 75) | pub struct AccessAccount {
  type AccessStorage (line 80) | pub struct AccessStorage {
  type InterruptDataVariant (line 86) | pub enum InterruptDataVariant {

FILE: src/continuation/mod.rs
  type Sealed (line 16) | pub trait Sealed {}
  type Interrupt (line 27) | pub trait Interrupt: sealed::Sealed {
    method data (line 34) | fn data(&self) -> &Self::InterruptData;
    method resume (line 36) | fn resume(self, resume_data: Self::ResumeData) -> InterruptVariant;
  type InnerCoroutine (line 39) | type InnerCoroutine = Pin<
  function resume_interrupt (line 51) | fn resume_interrupt(mut inner: InnerCoroutine, resume_data: ResumeDataVa...

FILE: src/continuation/resume_data.rs
  type StateModifier (line 5) | pub type StateModifier = Option<Arc<dyn Fn(&mut ExecutionState) + Send +...
  type AccountExistsStatus (line 8) | pub struct AccountExistsStatus {
  type Balance (line 13) | pub struct Balance {
  type CodeSize (line 18) | pub struct CodeSize {
  type StorageValue (line 23) | pub struct StorageValue {
  type StorageStatusInfo (line 28) | pub struct StorageStatusInfo {
  type CodeHash (line 33) | pub struct CodeHash {
  type BlockHash (line 38) | pub struct BlockHash {
  type TxContextData (line 43) | pub struct TxContextData {
  type Code (line 48) | pub struct Code {
  type CallOutput (line 53) | pub struct CallOutput {
  type AccessAccountStatus (line 58) | pub struct AccessAccountStatus {
  type AccessStorageStatus (line 63) | pub struct AccessStorageStatus {
  type ResumeDataVariant (line 70) | pub(crate) enum ResumeDataVariant {
    method from (line 90) | fn from(_: ()) -> Self {

FILE: src/evmc.rs
  type Convert (line 10) | pub(crate) trait Convert {
    method convert (line 13) | fn convert(self) -> Self::Into;
    type Into (line 17) | type Into = evmc_address;
    method convert (line 19) | fn convert(self) -> Self::Into {
    type Into (line 27) | type Into = evmc_bytes32;
    method convert (line 29) | fn convert(self) -> Self::Into {
    type Into (line 37) | type Into = evmc_uint256be;
    method convert (line 39) | fn convert(self) -> Self::Into {
  method from (line 45) | fn from(s: evmc_access_status) -> Self {
  method from (line 54) | fn from(msg: Message) -> Self {
  method from_evmc (line 88) | fn from_evmc(msg: &ExecutionMessage) -> Self {
  method from (line 117) | fn from(status: evmc_status_code) -> Self {
  method from (line 149) | fn from(status: StatusCode) -> Self {
  method account_exists (line 175) | fn account_exists(&self, address: Address) -> bool {
  method get_storage (line 179) | fn get_storage(&self, address: Address, key: U256) -> U256 {
  method set_storage (line 185) | fn set_storage(&mut self, address: Address, key: U256, value: U256) -> S...
  method get_balance (line 200) | fn get_balance(&self, address: Address) -> U256 {
  method get_code_size (line 206) | fn get_code_size(&self, address: Address) -> U256 {
  method get_code_hash (line 210) | fn get_code_hash(&self, address: Address) -> U256 {
  method copy_code (line 216) | fn copy_code(&self, address: Address, offset: usize, buffer: &mut [u8]) ...
  method selfdestruct (line 220) | fn selfdestruct(&mut self, address: Address, beneficiary: Address) {
  method call (line 224) | fn call(&mut self, msg: &Message) -> Output {
  method get_tx_context (line 238) | fn get_tx_context(&self) -> TxContext {
  method get_block_hash (line 254) | fn get_block_hash(&self, block_number: u64) -> U256 {
  method emit_log (line 260) | fn emit_log(&mut self, address: Address, data: &[u8], topics: &[U256]) {
  method access_account (line 272) | fn access_account(&mut self, address: Address) -> AccessStatus {
  method access_storage (line 276) | fn access_storage(&mut self, address: Address, key: U256) -> AccessStatus {
  method from (line 282) | fn from(rev: evmc_vm::Revision) -> Self {
  function from (line 300) | fn from(rev: Revision) -> Self {
  type EvmOdin (line 318) | pub struct EvmOdin;
  method init (line 321) | fn init() -> Self {
  method execute (line 325) | fn execute<'a>(

FILE: src/host.rs
  type AccessStatus (line 6) | pub enum AccessStatus {
  method default (line 12) | fn default() -> Self {
  type StorageStatus (line 18) | pub enum StorageStatus {
  type TxContext (line 33) | pub struct TxContext {
  type Host (line 55) | pub trait Host {
    method account_exists (line 57) | fn account_exists(&self, address: Address) -> bool;
    method get_storage (line 61) | fn get_storage(&self, address: Address, key: U256) -> U256;
    method set_storage (line 63) | fn set_storage(&mut self, address: Address, key: U256, value: U256) ->...
    method get_balance (line 67) | fn get_balance(&self, address: Address) -> U256;
    method get_code_size (line 71) | fn get_code_size(&self, address: Address) -> U256;
    method get_code_hash (line 75) | fn get_code_hash(&self, address: Address) -> U256;
    method copy_code (line 79) | fn copy_code(&self, address: Address, offset: usize, buffer: &mut [u8]...
    method selfdestruct (line 81) | fn selfdestruct(&mut self, address: Address, beneficiary: Address);
    method call (line 83) | fn call(&mut self, msg: &Message) -> Output;
    method get_tx_context (line 85) | fn get_tx_context(&self) -> TxContext;
    method get_block_hash (line 89) | fn get_block_hash(&self, block_number: u64) -> U256;
    method emit_log (line 91) | fn emit_log(&mut self, address: Address, data: &[u8], topics: &[U256]);
    method access_account (line 95) | fn access_account(&mut self, address: Address) -> AccessStatus;
    method access_storage (line 99) | fn access_storage(&mut self, address: Address, key: U256) -> AccessSta...
    method account_exists (line 106) | fn account_exists(&self, _: Address) -> bool {
    method get_storage (line 110) | fn get_storage(&self, _: Address, _: U256) -> U256 {
    method set_storage (line 114) | fn set_storage(&mut self, _: Address, _: U256, _: U256) -> StorageStat...
    method get_balance (line 118) | fn get_balance(&self, _: Address) -> U256 {
    method get_code_size (line 122) | fn get_code_size(&self, _: Address) -> U256 {
    method get_code_hash (line 126) | fn get_code_hash(&self, _: Address) -> U256 {
    method copy_code (line 130) | fn copy_code(&self, _: Address, _: usize, _: &mut [u8]) -> usize {
    method selfdestruct (line 134) | fn selfdestruct(&mut self, _: Address, _: Address) {
    method call (line 138) | fn call(&mut self, _: &Message) -> Output {
    method get_tx_context (line 142) | fn get_tx_context(&self) -> TxContext {
    method get_block_hash (line 146) | fn get_block_hash(&self, _: u64) -> U256 {
    method emit_log (line 150) | fn emit_log(&mut self, _: Address, _: &[u8], _: &[U256]) {
    method access_account (line 154) | fn access_account(&mut self, _: Address) -> AccessStatus {
    method access_storage (line 158) | fn access_storage(&mut self, _: Address, _: U256) -> AccessStatus {
  type DummyHost (line 103) | pub struct DummyHost;

FILE: src/instructions/arithmetic.rs
  function add (line 6) | pub(crate) fn add(stack: &mut Stack) {
  function mul (line 12) | pub(crate) fn mul(stack: &mut Stack) {
  function sub (line 18) | pub(crate) fn sub(stack: &mut Stack) {
  function div (line 24) | pub(crate) fn div(stack: &mut Stack) {
  function sdiv (line 30) | pub(crate) fn sdiv(stack: &mut Stack) {
  function modulo (line 37) | pub(crate) fn modulo(stack: &mut Stack) {
  function smod (line 44) | pub(crate) fn smod(stack: &mut Stack) {
  function addmod (line 58) | pub(crate) fn addmod(stack: &mut Stack) {
  function mulmod (line 73) | pub(crate) fn mulmod(stack: &mut Stack) {
  function log2floor (line 88) | fn log2floor(value: U256) -> u64 {
  function exp (line 107) | pub(crate) fn exp(state: &mut ExecutionState) -> Result<(), StatusCode> {
  function signextend (line 140) | pub(crate) fn signextend(stack: &mut Stack) {

FILE: src/instructions/bitwise.rs
  function byte (line 5) | pub(crate) fn byte(stack: &mut Stack) {
  function shl (line 24) | pub(crate) fn shl(stack: &mut Stack) {
  function shr (line 37) | pub(crate) fn shr(stack: &mut Stack) {
  function sar (line 50) | pub(crate) fn sar(stack: &mut Stack) {

FILE: src/instructions/boolean.rs
  function lt (line 5) | pub(crate) fn lt(stack: &mut Stack) {
  function gt (line 12) | pub(crate) fn gt(stack: &mut Stack) {
  function slt (line 19) | pub(crate) fn slt(stack: &mut Stack) {
  function sgt (line 26) | pub(crate) fn sgt(stack: &mut Stack) {
  function eq (line 33) | pub(crate) fn eq(stack: &mut Stack) {
  function iszero (line 40) | pub(crate) fn iszero(stack: &mut Stack) {
  function and (line 49) | pub(crate) fn and(stack: &mut Stack) {
  function or (line 55) | pub(crate) fn or(stack: &mut Stack) {
  function xor (line 61) | pub(crate) fn xor(stack: &mut Stack) {
  function not (line 67) | pub(crate) fn not(stack: &mut Stack) {

FILE: src/instructions/control.rs
  function ret (line 5) | pub(crate) fn ret(state: &mut ExecutionState) -> Result<(), StatusCode> {
  function op_jump (line 20) | pub(crate) fn op_jump(
  function calldataload (line 32) | pub(crate) fn calldataload(state: &mut ExecutionState) {
  function calldatasize (line 52) | pub(crate) fn calldatasize(state: &mut ExecutionState) {

FILE: src/instructions/external.rs
  function address (line 4) | pub(crate) fn address(state: &mut ExecutionState) {
  function caller (line 8) | pub(crate) fn caller(state: &mut ExecutionState) {
  function callvalue (line 12) | pub(crate) fn callvalue(state: &mut ExecutionState) {
  function origin_accessor (line 113) | pub(crate) fn origin_accessor(tx_context: TxContext) -> U256 {
  function coinbase_accessor (line 117) | pub(crate) fn coinbase_accessor(tx_context: TxContext) -> U256 {
  function gasprice_accessor (line 121) | pub(crate) fn gasprice_accessor(tx_context: TxContext) -> U256 {
  function timestamp_accessor (line 125) | pub(crate) fn timestamp_accessor(tx_context: TxContext) -> U256 {
  function number_accessor (line 129) | pub(crate) fn number_accessor(tx_context: TxContext) -> U256 {
  function gaslimit_accessor (line 133) | pub(crate) fn gaslimit_accessor(tx_context: TxContext) -> U256 {
  function difficulty_accessor (line 137) | pub(crate) fn difficulty_accessor(tx_context: TxContext) -> U256 {
  function chainid_accessor (line 141) | pub(crate) fn chainid_accessor(tx_context: TxContext) -> U256 {
  function basefee_accessor (line 145) | pub(crate) fn basefee_accessor(tx_context: TxContext) -> U256 {
  function u256_to_address_conversion (line 458) | fn u256_to_address_conversion() {

FILE: src/instructions/instruction_table.rs
  type InstructionTableEntry (line 5) | pub struct InstructionTableEntry {
  type InstructionTable (line 11) | pub type InstructionTable = [Option<InstructionTableEntry>; 256];
  type InstructionTables (line 12) | pub type InstructionTables = [InstructionTable; Revision::len()];
  function get_baseline_instruction_table (line 40) | pub fn get_baseline_instruction_table(revision: Revision) -> &'static In...

FILE: src/instructions/memory.rs
  constant MAX_BUFFER_SIZE (line 6) | pub(crate) const MAX_BUFFER_SIZE: u32 = u32::MAX;
  constant WORD_SIZE (line 9) | const WORD_SIZE: i64 = 32;
  function num_words (line 13) | pub(crate) fn num_words(size_in_bytes: usize) -> i64 {
  function mload (line 17) | pub(crate) fn mload(state: &mut ExecutionState) -> Result<(), StatusCode> {
  function mstore (line 31) | pub(crate) fn mstore(state: &mut ExecutionState) -> Result<(), StatusCod...
  function mstore8 (line 45) | pub(crate) fn mstore8(state: &mut ExecutionState) -> Result<(), StatusCo...
  function msize (line 59) | pub(crate) fn msize(state: &mut ExecutionState) {
  function verify_memory_region_u64 (line 63) | pub(crate) fn verify_memory_region_u64(
  type MemoryRegion (line 98) | pub(crate) struct MemoryRegion {
  function verify_memory_region (line 103) | pub(crate) fn verify_memory_region(
  function calldatacopy (line 119) | pub(crate) fn calldatacopy(state: &mut ExecutionState) -> Result<(), Sta...
  function keccak256 (line 151) | pub(crate) fn keccak256(state: &mut ExecutionState) -> Result<(), Status...
  function codesize (line 175) | pub(crate) fn codesize(stack: &mut Stack, code: &[u8]) {
  function codecopy (line 179) | pub(crate) fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Resul...
  function returndatasize (line 281) | pub(crate) fn returndatasize(state: &mut ExecutionState) {
  function returndatacopy (line 285) | pub(crate) fn returndatacopy(state: &mut ExecutionState) -> Result<(), S...

FILE: src/instructions/properties.rs
  constant COLD_SLOAD_COST (line 5) | pub(crate) const COLD_SLOAD_COST: u16 = 2100;
  constant COLD_ACCOUNT_ACCESS_COST (line 6) | pub(crate) const COLD_ACCOUNT_ACCESS_COST: u16 = 2600;
  constant WARM_STORAGE_READ_COST (line 7) | pub(crate) const WARM_STORAGE_READ_COST: u16 = 100;
  constant ADDITIONAL_COLD_ACCOUNT_ACCESS_COST (line 13) | pub(crate) const ADDITIONAL_COLD_ACCOUNT_ACCESS_COST: u16 =
  type Properties (line 18) | pub struct Properties {
    method new (line 28) | fn new(name: &'static str, stack_height_required: u8, stack_height_cha...
  function gas_costs (line 361) | pub fn gas_costs(revision: Revision) -> &'static [Option<u16>; 256] {

FILE: src/instructions/stack_manip.rs
  function push (line 4) | pub(crate) fn push(stack: &mut Stack, code: &[u8], push_len: usize) {
  function dup (line 8) | pub(crate) fn dup(stack: &mut Stack, height: usize) {
  function swap (line 12) | pub(crate) fn swap(stack: &mut Stack, height: usize) {
  function pop (line 16) | pub(crate) fn pop(stack: &mut Stack) {

FILE: src/interpreter.rs
  function check_requirements (line 14) | fn check_requirements(
  type JumpdestMap (line 39) | pub struct JumpdestMap(Arc<[bool]>);
    method contains (line 42) | pub fn contains(&self, dst: U256) -> bool {
  type AnalyzedCode (line 49) | pub struct AnalyzedCode {
    method analyze (line 57) | pub fn analyze(code: impl Into<Vec<u8>>) -> Self {
    method execute (line 123) | pub fn execute<H: Host, T: Tracer>(
    method execute_resumable (line 147) | pub fn execute_resumable(
  method run_to_completion_with_host (line 163) | pub fn run_to_completion_with_host<H: Host, T: Tracer>(
  function interpreter_producer (line 274) | async fn interpreter_producer(

FILE: src/lib.rs
  constant MAX_CODE_SIZE (line 12) | pub const MAX_CODE_SIZE: usize = 0x6000;

FILE: src/opcode.rs
  type OpCode (line 4) | pub struct OpCode(pub u8);
    method to_u8 (line 8) | pub const fn to_u8(self) -> u8 {
    method to_usize (line 13) | pub const fn to_usize(self) -> usize {
    constant STOP (line 19) | pub const STOP: OpCode = OpCode(0x00);
    constant ADD (line 20) | pub const ADD: OpCode = OpCode(0x01);
    constant MUL (line 21) | pub const MUL: OpCode = OpCode(0x02);
    constant SUB (line 22) | pub const SUB: OpCode = OpCode(0x03);
    constant DIV (line 23) | pub const DIV: OpCode = OpCode(0x04);
    constant SDIV (line 24) | pub const SDIV: OpCode = OpCode(0x05);
    constant MOD (line 25) | pub const MOD: OpCode = OpCode(0x06);
    constant SMOD (line 26) | pub const SMOD: OpCode = OpCode(0x07);
    constant ADDMOD (line 27) | pub const ADDMOD: OpCode = OpCode(0x08);
    constant MULMOD (line 28) | pub const MULMOD: OpCode = OpCode(0x09);
    constant EXP (line 29) | pub const EXP: OpCode = OpCode(0x0a);
    constant SIGNEXTEND (line 30) | pub const SIGNEXTEND: OpCode = OpCode(0x0b);
    constant LT (line 32) | pub const LT: OpCode = OpCode(0x10);
    constant GT (line 33) | pub const GT: OpCode = OpCode(0x11);
    constant SLT (line 34) | pub const SLT: OpCode = OpCode(0x12);
    constant SGT (line 35) | pub const SGT: OpCode = OpCode(0x13);
    constant EQ (line 36) | pub const EQ: OpCode = OpCode(0x14);
    constant ISZERO (line 37) | pub const ISZERO: OpCode = OpCode(0x15);
    constant AND (line 38) | pub const AND: OpCode = OpCode(0x16);
    constant OR (line 39) | pub const OR: OpCode = OpCode(0x17);
    constant XOR (line 40) | pub const XOR: OpCode = OpCode(0x18);
    constant NOT (line 41) | pub const NOT: OpCode = OpCode(0x19);
    constant BYTE (line 42) | pub const BYTE: OpCode = OpCode(0x1a);
    constant SHL (line 43) | pub const SHL: OpCode = OpCode(0x1b);
    constant SHR (line 44) | pub const SHR: OpCode = OpCode(0x1c);
    constant SAR (line 45) | pub const SAR: OpCode = OpCode(0x1d);
    constant KECCAK256 (line 47) | pub const KECCAK256: OpCode = OpCode(0x20);
    constant ADDRESS (line 49) | pub const ADDRESS: OpCode = OpCode(0x30);
    constant BALANCE (line 50) | pub const BALANCE: OpCode = OpCode(0x31);
    constant ORIGIN (line 51) | pub const ORIGIN: OpCode = OpCode(0x32);
    constant CALLER (line 52) | pub const CALLER: OpCode = OpCode(0x33);
    constant CALLVALUE (line 53) | pub const CALLVALUE: OpCode = OpCode(0x34);
    constant CALLDATALOAD (line 54) | pub const CALLDATALOAD: OpCode = OpCode(0x35);
    constant CALLDATASIZE (line 55) | pub const CALLDATASIZE: OpCode = OpCode(0x36);
    constant CALLDATACOPY (line 56) | pub const CALLDATACOPY: OpCode = OpCode(0x37);
    constant CODESIZE (line 57) | pub const CODESIZE: OpCode = OpCode(0x38);
    constant CODECOPY (line 58) | pub const CODECOPY: OpCode = OpCode(0x39);
    constant GASPRICE (line 59) | pub const GASPRICE: OpCode = OpCode(0x3a);
    constant EXTCODESIZE (line 60) | pub const EXTCODESIZE: OpCode = OpCode(0x3b);
    constant EXTCODECOPY (line 61) | pub const EXTCODECOPY: OpCode = OpCode(0x3c);
    constant RETURNDATASIZE (line 62) | pub const RETURNDATASIZE: OpCode = OpCode(0x3d);
    constant RETURNDATACOPY (line 63) | pub const RETURNDATACOPY: OpCode = OpCode(0x3e);
    constant EXTCODEHASH (line 64) | pub const EXTCODEHASH: OpCode = OpCode(0x3f);
    constant BLOCKHASH (line 66) | pub const BLOCKHASH: OpCode = OpCode(0x40);
    constant COINBASE (line 67) | pub const COINBASE: OpCode = OpCode(0x41);
    constant TIMESTAMP (line 68) | pub const TIMESTAMP: OpCode = OpCode(0x42);
    constant NUMBER (line 69) | pub const NUMBER: OpCode = OpCode(0x43);
    constant DIFFICULTY (line 70) | pub const DIFFICULTY: OpCode = OpCode(0x44);
    constant GASLIMIT (line 71) | pub const GASLIMIT: OpCode = OpCode(0x45);
    constant CHAINID (line 72) | pub const CHAINID: OpCode = OpCode(0x46);
    constant SELFBALANCE (line 73) | pub const SELFBALANCE: OpCode = OpCode(0x47);
    constant BASEFEE (line 74) | pub const BASEFEE: OpCode = OpCode(0x48);
    constant POP (line 76) | pub const POP: OpCode = OpCode(0x50);
    constant MLOAD (line 77) | pub const MLOAD: OpCode = OpCode(0x51);
    constant MSTORE (line 78) | pub const MSTORE: OpCode = OpCode(0x52);
    constant MSTORE8 (line 79) | pub const MSTORE8: OpCode = OpCode(0x53);
    constant SLOAD (line 80) | pub const SLOAD: OpCode = OpCode(0x54);
    constant SSTORE (line 81) | pub const SSTORE: OpCode = OpCode(0x55);
    constant JUMP (line 82) | pub const JUMP: OpCode = OpCode(0x56);
    constant JUMPI (line 83) | pub const JUMPI: OpCode = OpCode(0x57);
    constant PC (line 84) | pub const PC: OpCode = OpCode(0x58);
    constant MSIZE (line 85) | pub const MSIZE: OpCode = OpCode(0x59);
    constant GAS (line 86) | pub const GAS: OpCode = OpCode(0x5a);
    constant JUMPDEST (line 87) | pub const JUMPDEST: OpCode = OpCode(0x5b);
    constant PUSH1 (line 89) | pub const PUSH1: OpCode = OpCode(0x60);
    constant PUSH2 (line 90) | pub const PUSH2: OpCode = OpCode(0x61);
    constant PUSH3 (line 91) | pub const PUSH3: OpCode = OpCode(0x62);
    constant PUSH4 (line 92) | pub const PUSH4: OpCode = OpCode(0x63);
    constant PUSH5 (line 93) | pub const PUSH5: OpCode = OpCode(0x64);
    constant PUSH6 (line 94) | pub const PUSH6: OpCode = OpCode(0x65);
    constant PUSH7 (line 95) | pub const PUSH7: OpCode = OpCode(0x66);
    constant PUSH8 (line 96) | pub const PUSH8: OpCode = OpCode(0x67);
    constant PUSH9 (line 97) | pub const PUSH9: OpCode = OpCode(0x68);
    constant PUSH10 (line 98) | pub const PUSH10: OpCode = OpCode(0x69);
    constant PUSH11 (line 99) | pub const PUSH11: OpCode = OpCode(0x6a);
    constant PUSH12 (line 100) | pub const PUSH12: OpCode = OpCode(0x6b);
    constant PUSH13 (line 101) | pub const PUSH13: OpCode = OpCode(0x6c);
    constant PUSH14 (line 102) | pub const PUSH14: OpCode = OpCode(0x6d);
    constant PUSH15 (line 103) | pub const PUSH15: OpCode = OpCode(0x6e);
    constant PUSH16 (line 104) | pub const PUSH16: OpCode = OpCode(0x6f);
    constant PUSH17 (line 105) | pub const PUSH17: OpCode = OpCode(0x70);
    constant PUSH18 (line 106) | pub const PUSH18: OpCode = OpCode(0x71);
    constant PUSH19 (line 107) | pub const PUSH19: OpCode = OpCode(0x72);
    constant PUSH20 (line 108) | pub const PUSH20: OpCode = OpCode(0x73);
    constant PUSH21 (line 109) | pub const PUSH21: OpCode = OpCode(0x74);
    constant PUSH22 (line 110) | pub const PUSH22: OpCode = OpCode(0x75);
    constant PUSH23 (line 111) | pub const PUSH23: OpCode = OpCode(0x76);
    constant PUSH24 (line 112) | pub const PUSH24: OpCode = OpCode(0x77);
    constant PUSH25 (line 113) | pub const PUSH25: OpCode = OpCode(0x78);
    constant PUSH26 (line 114) | pub const PUSH26: OpCode = OpCode(0x79);
    constant PUSH27 (line 115) | pub const PUSH27: OpCode = OpCode(0x7a);
    constant PUSH28 (line 116) | pub const PUSH28: OpCode = OpCode(0x7b);
    constant PUSH29 (line 117) | pub const PUSH29: OpCode = OpCode(0x7c);
    constant PUSH30 (line 118) | pub const PUSH30: OpCode = OpCode(0x7d);
    constant PUSH31 (line 119) | pub const PUSH31: OpCode = OpCode(0x7e);
    constant PUSH32 (line 120) | pub const PUSH32: OpCode = OpCode(0x7f);
    constant DUP1 (line 121) | pub const DUP1: OpCode = OpCode(0x80);
    constant DUP2 (line 122) | pub const DUP2: OpCode = OpCode(0x81);
    constant DUP3 (line 123) | pub const DUP3: OpCode = OpCode(0x82);
    constant DUP4 (line 124) | pub const DUP4: OpCode = OpCode(0x83);
    constant DUP5 (line 125) | pub const DUP5: OpCode = OpCode(0x84);
    constant DUP6 (line 126) | pub const DUP6: OpCode = OpCode(0x85);
    constant DUP7 (line 127) | pub const DUP7: OpCode = OpCode(0x86);
    constant DUP8 (line 128) | pub const DUP8: OpCode = OpCode(0x87);
    constant DUP9 (line 129) | pub const DUP9: OpCode = OpCode(0x88);
    constant DUP10 (line 130) | pub const DUP10: OpCode = OpCode(0x89);
    constant DUP11 (line 131) | pub const DUP11: OpCode = OpCode(0x8a);
    constant DUP12 (line 132) | pub const DUP12: OpCode = OpCode(0x8b);
    constant DUP13 (line 133) | pub const DUP13: OpCode = OpCode(0x8c);
    constant DUP14 (line 134) | pub const DUP14: OpCode = OpCode(0x8d);
    constant DUP15 (line 135) | pub const DUP15: OpCode = OpCode(0x8e);
    constant DUP16 (line 136) | pub const DUP16: OpCode = OpCode(0x8f);
    constant SWAP1 (line 137) | pub const SWAP1: OpCode = OpCode(0x90);
    constant SWAP2 (line 138) | pub const SWAP2: OpCode = OpCode(0x91);
    constant SWAP3 (line 139) | pub const SWAP3: OpCode = OpCode(0x92);
    constant SWAP4 (line 140) | pub const SWAP4: OpCode = OpCode(0x93);
    constant SWAP5 (line 141) | pub const SWAP5: OpCode = OpCode(0x94);
    constant SWAP6 (line 142) | pub const SWAP6: OpCode = OpCode(0x95);
    constant SWAP7 (line 143) | pub const SWAP7: OpCode = OpCode(0x96);
    constant SWAP8 (line 144) | pub const SWAP8: OpCode = OpCode(0x97);
    constant SWAP9 (line 145) | pub const SWAP9: OpCode = OpCode(0x98);
    constant SWAP10 (line 146) | pub const SWAP10: OpCode = OpCode(0x99);
    constant SWAP11 (line 147) | pub const SWAP11: OpCode = OpCode(0x9a);
    constant SWAP12 (line 148) | pub const SWAP12: OpCode = OpCode(0x9b);
    constant SWAP13 (line 149) | pub const SWAP13: OpCode = OpCode(0x9c);
    constant SWAP14 (line 150) | pub const SWAP14: OpCode = OpCode(0x9d);
    constant SWAP15 (line 151) | pub const SWAP15: OpCode = OpCode(0x9e);
    constant SWAP16 (line 152) | pub const SWAP16: OpCode = OpCode(0x9f);
    constant LOG0 (line 153) | pub const LOG0: OpCode = OpCode(0xa0);
    constant LOG1 (line 154) | pub const LOG1: OpCode = OpCode(0xa1);
    constant LOG2 (line 155) | pub const LOG2: OpCode = OpCode(0xa2);
    constant LOG3 (line 156) | pub const LOG3: OpCode = OpCode(0xa3);
    constant LOG4 (line 157) | pub const LOG4: OpCode = OpCode(0xa4);
    constant CREATE (line 159) | pub const CREATE: OpCode = OpCode(0xf0);
    constant CALL (line 160) | pub const CALL: OpCode = OpCode(0xf1);
    constant CALLCODE (line 161) | pub const CALLCODE: OpCode = OpCode(0xf2);
    constant RETURN (line 162) | pub const RETURN: OpCode = OpCode(0xf3);
    constant DELEGATECALL (line 163) | pub const DELEGATECALL: OpCode = OpCode(0xf4);
    constant CREATE2 (line 164) | pub const CREATE2: OpCode = OpCode(0xf5);
    constant STATICCALL (line 166) | pub const STATICCALL: OpCode = OpCode(0xfa);
    constant REVERT (line 168) | pub const REVERT: OpCode = OpCode(0xfd);
    constant INVALID (line 169) | pub const INVALID: OpCode = OpCode(0xfe);
    constant SELFDESTRUCT (line 170) | pub const SELFDESTRUCT: OpCode = OpCode(0xff);
    method name (line 174) | pub const fn name(&self) -> &'static str {
    method push_size (line 323) | pub fn push_size(self) -> Option<u8> {
  method fmt (line 330) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

FILE: src/state.rs
  constant SIZE (line 8) | const SIZE: usize = 1024;
  type Stack (line 12) | pub struct Stack(pub ArrayVec<U256, SIZE>);
    method limit (line 15) | pub const fn limit() -> usize {
    method get_pos (line 19) | fn get_pos(&self, pos: usize) -> usize {
    method get (line 23) | pub fn get(&self, pos: usize) -> &U256 {
    method get_mut (line 27) | pub fn get_mut(&mut self, pos: usize) -> &mut U256 {
    method len (line 32) | pub fn len(&self) -> usize {
    method is_empty (line 36) | pub fn is_empty(&self) -> bool {
    method push (line 40) | pub fn push(&mut self, v: U256) {
    method pop (line 44) | pub fn pop(&mut self) -> U256 {
    method swap_top (line 48) | pub fn swap_top(&mut self, pos: usize) {
  type Memory (line 55) | pub type Memory = Vec<u8>;
  type ExecutionState (line 59) | pub struct ExecutionState {
    method new (line 74) | pub fn new(message: Message, evm_revision: Revision) -> Self {
  function stack (line 92) | fn stack() {

FILE: src/tracing/mod.rs
  type Tracer (line 6) | pub trait Tracer {
    constant DUMMY (line 8) | const DUMMY: bool = false;
    method notify_execution_start (line 11) | fn notify_execution_start(&mut self, revision: Revision, message: Mess...
    method notify_instruction_start (line 13) | fn notify_instruction_start(&mut self, pc: usize, opcode: OpCode, stat...
    method notify_execution_end (line 15) | fn notify_execution_end(&mut self, output: &Output);
    constant DUMMY (line 22) | const DUMMY: bool = true;
    method notify_execution_start (line 24) | fn notify_execution_start(&mut self, _: Revision, _: Message, _: Bytes...
    method notify_instruction_start (line 26) | fn notify_instruction_start(&mut self, _: usize, _: OpCode, _: &Execut...
    method notify_execution_end (line 28) | fn notify_execution_end(&mut self, _: &Output) {}
    method notify_execution_start (line 71) | fn notify_execution_start(&mut self, revision: Revision, message: Mess...
    method notify_instruction_start (line 84) | fn notify_instruction_start(&mut self, pc: usize, _: OpCode, state: &E...
    method notify_execution_end (line 101) | fn notify_execution_end(&mut self, output: &Output) {
  type NoopTracer (line 19) | pub struct NoopTracer;
  type ExecutionStart (line 32) | struct ExecutionStart {
  type InstructionStart (line 41) | pub(crate) struct InstructionStart {
  type ExecutionEnd (line 52) | pub(crate) struct ExecutionEnd {
  type TracerContext (line 59) | struct TracerContext {
  type StdoutTracer (line 66) | pub struct StdoutTracer {

FILE: src/util/bytecode.rs
  type Bytecode (line 8) | pub struct Bytecode {
    method new (line 13) | pub const fn new() -> Self {
    method append (line 17) | pub fn append(mut self, b: impl IntoIterator<Item = u8>) -> Self {
    method append_bc (line 22) | pub fn append_bc(mut self, b: impl Into<Self>) -> Self {
    method repeat (line 27) | pub fn repeat(mut self, n: usize) -> Self {
    method pushv (line 32) | pub fn pushv(self, value: impl Into<U256>) -> Self {
    method pushb (line 43) | pub fn pushb(mut self, b: impl IntoIterator<Item = u8>) -> Self {
    method opcode (line 57) | pub fn opcode(mut self, opcode: OpCode) -> Self {
    method ret (line 62) | pub fn ret(mut self, index: impl Into<U256>, size: impl Into<U256>) ->...
    method revert (line 69) | pub fn revert(mut self, index: impl Into<U256>, size: impl Into<U256>)...
    method mstore (line 76) | pub fn mstore(mut self, index: impl Into<U256>) -> Self {
    method mstore_value (line 82) | pub fn mstore_value(mut self, index: impl Into<U256>, value: impl Into...
    method mstore8 (line 89) | pub fn mstore8(mut self, index: impl Into<U256>) -> Self {
    method mstore8_value (line 95) | pub fn mstore8_value(mut self, index: impl Into<U256>, value: impl Int...
    method ret_top (line 102) | pub fn ret_top(self) -> Self {
    method jump (line 106) | pub fn jump(self, target: impl Into<U256>) -> Self {
    method jumpi (line 110) | pub fn jumpi(self, target: impl Into<Bytecode>, condition: impl Into<B...
    method sstore (line 116) | pub fn sstore(self, index: impl Into<U256>, value: impl Into<U256>) ->...
    method sload (line 120) | pub fn sload(self, index: impl Into<U256>) -> Self {
    method build (line 124) | pub fn build(self) -> Vec<u8> {
    method len (line 128) | pub fn len(&self) -> usize {
    method is_empty (line 132) | pub fn is_empty(&self) -> bool {
    method from (line 138) | fn from(value: U256) -> Self {
    method from (line 144) | fn from(opcode: OpCode) -> Self {
    method from (line 150) | fn from(inner: [u8; N]) -> Self {
    method from (line 158) | fn from(inner: Vec<u8>) -> Self {
    method as_ref (line 164) | fn as_ref(&self) -> &[u8] {
    type Output (line 197) | type Output = Bytecode;
    method add (line 199) | fn add(self, rhs: T) -> Self::Output {
    method from (line 273) | fn from(call: CallInstruction) -> Self {
  type Item (line 170) | type Item = u8;
  type IntoIter (line 171) | type IntoIter = <Vec<u8> as IntoIterator>::IntoIter;
  method into_iter (line 173) | fn into_iter(self) -> Self::IntoIter {
  type Output (line 179) | type Output = Bytecode;
  function mul (line 181) | fn mul(self, rhs: Bytecode) -> Self::Output {
  type Output (line 189) | type Output = Bytecode;
  function mul (line 191) | fn mul(self, rhs: OpCode) -> Self::Output {
  type CallInstruction (line 204) | pub struct CallInstruction {
    method new (line 216) | fn new(op: OpCode, address: impl Into<U256>) -> Self {
    method delegatecall (line 229) | pub fn delegatecall(address: impl Into<U256>) -> Self {
    method staticcall (line 233) | pub fn staticcall(address: impl Into<U256>) -> Self {
    method call (line 237) | pub fn call(address: impl Into<U256>) -> Self {
    method callcode (line 241) | pub fn callcode(address: impl Into<U256>) -> Self {
    method opcode (line 245) | pub fn opcode(&self) -> OpCode {
    method gas (line 249) | pub fn gas(mut self, gas: impl Into<U256>) -> Self {
    method value (line 254) | pub fn value(mut self, value: impl Into<U256>) -> Self {
    method input (line 259) | pub fn input(mut self, index: impl Into<U256>, size: impl Into<U256>) ...
    method output (line 265) | pub fn output(mut self, index: impl Into<U256>, size: impl Into<U256>)...
  function multiply_bytecode (line 291) | fn multiply_bytecode() {

FILE: src/util/mocked_host.rs
  type LogRecord (line 10) | pub struct LogRecord {
  type SelfdestructRecord (line 22) | pub struct SelfdestructRecord {
  type StorageValue (line 31) | pub struct StorageValue {
  type Account (line 38) | pub struct Account {
  constant MAX_RECORDED_ACCOUNT_ACCESSES (line 51) | const MAX_RECORDED_ACCOUNT_ACCESSES: usize = 200;
  constant MAX_RECORDED_CALLS (line 52) | const MAX_RECORDED_CALLS: usize = 100;
  type Records (line 55) | pub struct Records {
    method record_account_access (line 115) | fn record_account_access(&mut self, address: Address) {
  type MockedHost (line 67) | pub struct MockedHost {
    method account_exists (line 123) | fn account_exists(&self, address: ethereum_types::Address) -> bool {
    method get_storage (line 128) | fn get_storage(&self, address: ethereum_types::Address, key: U256) -> ...
    method set_storage (line 137) | fn set_storage(
    method get_balance (line 182) | fn get_balance(&self, address: ethereum_types::Address) -> ethereum_ty...
    method get_code_size (line 191) | fn get_code_size(&self, address: ethereum_types::Address) -> ethereum_...
    method get_code_hash (line 200) | fn get_code_hash(&self, address: ethereum_types::Address) -> U256 {
    method copy_code (line 209) | fn copy_code(&self, address: Address, code_offset: usize, buffer: &mut...
    method selfdestruct (line 230) | fn selfdestruct(
    method call (line 244) | fn call(&mut self, msg: &Message) -> Output {
    method get_tx_context (line 259) | fn get_tx_context(&self) -> TxContext {
    method get_block_hash (line 263) | fn get_block_hash(&self, block_number: u64) -> U256 {
    method emit_log (line 268) | fn emit_log(&mut self, address: ethereum_types::Address, data: &[u8], ...
    method access_account (line 276) | fn access_account(&mut self, address: ethereum_types::Address) -> Acce...
    method access_storage (line 297) | fn access_storage(&mut self, address: ethereum_types::Address, key: U2...
  method clone (line 76) | fn clone(&self) -> Self {
  method default (line 88) | fn default() -> Self {

FILE: src/util/tester.rs
  function exec (line 11) | fn exec(
  type GasCheck (line 33) | enum GasCheck {
  type EvmTester (line 42) | pub struct EvmTester {
    method new (line 69) | pub fn new() -> Self {
    method code (line 97) | pub fn code(mut self, code: impl Into<Bytecode>) -> Self {
    method apply_host_fn (line 103) | pub fn apply_host_fn(mut self, host_fn: impl Fn(&mut MockedHost, &Mess...
    method revision (line 109) | pub fn revision(mut self, revision: Revision) -> Self {
    method depth (line 115) | pub fn depth(mut self, depth: u16) -> Self {
    method gas (line 121) | pub fn gas(mut self, gas: i64) -> Self {
    method set_static (line 127) | pub fn set_static(mut self, is_static: bool) -> Self {
    method destination (line 133) | pub fn destination(mut self, destination: impl Into<Address>) -> Self {
    method sender (line 139) | pub fn sender(mut self, sender: impl Into<Address>) -> Self {
    method value (line 145) | pub fn value(mut self, value: impl Into<U256>) -> Self {
    method gas_used (line 151) | pub fn gas_used(mut self, expected_gas_used: i64) -> Self {
    method gas_left (line 157) | pub fn gas_left(mut self, expected_gas_left: i64) -> Self {
    method input (line 163) | pub fn input(mut self, input: impl Into<Bytes>) -> Self {
    method status (line 169) | pub fn status(mut self, expected_status_code: StatusCode) -> Self {
    method status_one_of (line 175) | pub fn status_one_of<const N: usize>(mut self, expected_status_code: [...
    method output_value (line 181) | pub fn output_value(mut self, expected_output_data: impl Into<U256>) -...
    method output_data (line 189) | pub fn output_data(mut self, expected_output_data: impl Into<Vec<u8>>)...
    method inspect_output (line 195) | pub fn inspect_output(mut self, inspect_output_fn: impl Fn(&[u8]) + 's...
    method inspect_host (line 201) | pub fn inspect_host(mut self, f: impl Fn(&MockedHost, &Message) + 'sta...
    method inspect (line 207) | pub fn inspect(mut self, f: impl Fn(&MockedHost, &Message, &[u8]) + 's...
    method collect_traces (line 212) | pub fn collect_traces(mut self, doit: bool) -> Self {
    method check_and_get_result (line 218) | pub fn check_and_get_result(self) -> Output {
    method check (line 262) | pub fn check(self) {
  method default (line 62) | fn default() -> Self {

FILE: tests/basefee.rs
  function basefee_pre_london (line 4) | fn basefee_pre_london() {
  function basefee_nominal_case (line 13) | fn basefee_nominal_case() {

FILE: tests/call.rs
  function delegatecall (line 8) | fn delegatecall() {
  function delegatecall_static (line 44) | fn delegatecall_static() {
  function delegatecall_oog_depth_limit (line 62) | fn delegatecall_oog_depth_limit() {
  function create (line 82) | fn create() {
  function create_gas (line 115) | fn create_gas() {
  function create2 (line 144) | fn create2() {
  function create2_salt_cost (line 182) | fn create2_salt_cost() {
  function create_balance_too_low (line 214) | fn create_balance_too_low() {
  function create_failure (line 240) | fn create_failure() {
  function call_failing_with_value (line 325) | fn call_failing_with_value() {
  function call_with_value (line 380) | fn call_with_value() {
  function call_with_value_depth_limit (line 409) | fn call_with_value_depth_limit() {
  function call_depth_limit (line 428) | fn call_depth_limit() {
  function call_output (line 463) | fn call_output() {
  function call_high_gas (line 533) | fn call_high_gas() {
  function call_value_zero_to_nonexistent_account (line 561) | fn call_value_zero_to_nonexistent_account() {
  function call_new_account_creation_cost (line 603) | fn call_new_account_creation_cost() {
  function callcode_new_account_create (line 729) | fn callcode_new_account_create() {
  function call_then_oog (line 758) | fn call_then_oog() {
  function callcode_then_oog (line 798) | fn callcode_then_oog() {
  function delegatecall_then_oog (line 836) | fn delegatecall_then_oog() {
  function staticcall_then_oog (line 875) | fn staticcall_then_oog() {
  function staticcall_input (line 913) | fn staticcall_input() {
  function call_with_value_low_gas (line 931) | fn call_with_value_low_gas() {
  function call_oog_after_balance_check (line 958) | fn call_oog_after_balance_check() {
  function call_oog_after_depth_check (line 985) | fn call_oog_after_depth_check() {
  function create_oog_after (line 1031) | fn create_oog_after() {
  function returndatasize_before_call (line 1051) | fn returndatasize_before_call() {
  function returndatasize (line 1060) | fn returndatasize() {
  function returndatacopy (line 1110) | fn returndatacopy() {
  function returndatacopy_empty (line 1124) | fn returndatacopy_empty() {
  function returndatacopy_cost (line 1133) | fn returndatacopy_cost() {
  function returndatacopy_outofrange (line 1144) | fn returndatacopy_outofrange() {

FILE: tests/eip2929.rs
  function eip2929_case1 (line 5) | fn eip2929_case1() {
  function eip2929_case2 (line 51) | fn eip2929_case2() {
  function eip2929_case3 (line 79) | fn eip2929_case3() {
  function eip2929_case4 (line 93) | fn eip2929_case4() {
  function eip2929_op_oog (line 109) | fn eip2929_op_oog() {
  function eip2929_extcodecopy_oog (line 134) | fn eip2929_extcodecopy_oog() {
  function eip2929_sload_cold (line 157) | fn eip2929_sload_cold() {
  function eip2929_sload_two_slots (line 194) | fn eip2929_sload_two_slots() {
  function eip2929_sload_warm (line 226) | fn eip2929_sload_warm() {
  function eip2929_sstore_modify_cold (line 262) | fn eip2929_sstore_modify_cold() {
  function eip2929_selfdestruct_cold_beneficiary (line 305) | fn eip2929_selfdestruct_cold_beneficiary() {
  function eip2929_selfdestruct_warm_beneficiary (line 323) | fn eip2929_selfdestruct_warm_beneficiary() {
  function eip2929_delegatecall_cold (line 344) | fn eip2929_delegatecall_cold() {

FILE: tests/execute.rs
  function empty_code (line 10) | fn empty_code() {
  function invalid_push (line 22) | fn invalid_push() {
  function push_and_pop (line 30) | fn push_and_pop() {
  function stack_underflow (line 46) | fn stack_underflow() {
  function add (line 67) | fn add() {
  function dup (line 78) | fn dup() {
  function dup_all_1 (line 94) | fn dup_all_1() {
  function dup_stack_overflow (line 109) | fn dup_stack_overflow() {
  function dup_stack_underflow (line 127) | fn dup_stack_underflow() {
  function sub_and_swap (line 142) | fn sub_and_swap() {
  function memory_and_not (line 153) | fn memory_and_not() {
  function msize (line 164) | fn msize() {
  function gas (line 175) | fn gas() {
  function arith (line 186) | fn arith() {
  function comparison (line 210) | fn comparison() {
  function bitwise (line 232) | fn bitwise() {
  function jump (line 249) | fn jump() {
  function jumpi (line 270) | fn jumpi() {
  function jumpi_else (line 286) | fn jumpi_else() {
  function jumpi_at_the_end (line 302) | fn jumpi_at_the_end() {
  function bad_jumpdest (line 312) | fn bad_jumpdest() {
  function jump_to_block_beginning (line 330) | fn jump_to_block_beginning() {
  function jumpi_stack (line 338) | fn jumpi_stack() {
  function jump_over_jumpdest (line 355) | fn jump_over_jumpdest() {
  function jump_to_missing_push_data (line 371) | fn jump_to_missing_push_data() {
  function jump_to_missing_push_data2 (line 383) | fn jump_to_missing_push_data2() {
  function pc_sum (line 397) | fn pc_sum() {
  function pc_after_jump_1 (line 416) | fn pc_after_jump_1() {
  function pc_after_jump_2 (line 432) | fn pc_after_jump_2() {
  function byte (line 458) | fn byte() {
  function byte_overflow (line 487) | fn byte_overflow() {
  function addmod_mulmod (line 514) | fn addmod_mulmod() {
  function divmod (line 550) | fn divmod() {
  function div_by_zero (line 571) | fn div_by_zero() {
  function mod_by_zero (line 589) | fn mod_by_zero() {
  function addmod_mulmod_by_zero (line 607) | fn addmod_mulmod_by_zero() {
  function signextend (line 620) | fn signextend() {
  function signextend_31 (line 648) | fn signextend_31() {
  function exp (line 670) | fn exp() {
  function exp_1_0 (line 690) | fn exp_1_0() {
  function exp_0_0 (line 707) | fn exp_0_0() {
  function exp_oog (line 724) | fn exp_oog() {
  function exp_pre_spurious_dragon (line 741) | fn exp_pre_spurious_dragon() {
  function calldataload (line 762) | fn calldataload() {
  function calldataload_outofrange (line 779) | fn calldataload_outofrange() {
  function calldatacopy (line 793) | fn calldatacopy() {
  function address (line 820) | fn address() {
  function caller_callvalue (line 836) | fn caller_callvalue() {
  function undefined (line 855) | fn undefined() {
  function invalid (line 865) | fn invalid() {
  function inner_stop (line 875) | fn inner_stop() {
  function inner_return (line 890) | fn inner_return() {
  function inner_revert (line 900) | fn inner_revert() {
  function inner_invalid (line 910) | fn inner_invalid() {
  function inner_selfdestruct (line 926) | fn inner_selfdestruct() {
  function keccak256 (line 942) | fn keccak256() {
  function keccak256_empty (line 954) | fn keccak256_empty() {
  function revert (line 970) | fn revert() {
  function return_empty_buffer_at_offset_0 (line 984) | fn return_empty_buffer_at_offset_0() {
  function return_empty_buffer_at_high_offset (line 997) | fn return_empty_buffer_at_high_offset() {
  function shl (line 1019) | fn shl() {
  function shr (line 1030) | fn shr() {
  function sar (line 1041) | fn sar() {
  function sar_01 (line 1052) | fn sar_01() {
  function shift_overflow (line 1063) | fn shift_overflow() {
  function undefined_instruction_analysis_overflow (line 1086) | fn undefined_instruction_analysis_overflow() {
  function abort (line 1095) | fn abort() {
  function staticmode (line 1106) | fn staticmode() {
  function memory_big_allocation (line 1135) | fn memory_big_allocation() {
  function memory_grow_mstore8 (line 1145) | fn memory_grow_mstore8() {
  function mstore8_memory_cost (line 1187) | fn mstore8_memory_cost() {
  function keccak256_memory_cost (line 1198) | fn keccak256_memory_cost() {
  function calldatacopy_memory_cost (line 1209) | fn calldatacopy_memory_cost() {
  constant MAX_CODE_SIZE (line 1225) | const MAX_CODE_SIZE: usize = 0x6000;
  function max_code_size_push1 (line 1228) | fn max_code_size_push1() {
  function reverse_16_stack_items (line 1247) | fn reverse_16_stack_items() {
  function memory_access (line 1303) | fn memory_access() {

FILE: tests/other.rs
  function loop_full_of_jumpdests (line 4) | fn loop_full_of_jumpdests() {
  function jumpdest_with_high_offset (line 44) | fn jumpdest_with_high_offset() {

FILE: tests/state.rs
  function code (line 10) | fn code() {
  function codecopy_combinations (line 21) | fn codecopy_combinations() {
  function storage (line 62) | fn storage() {
  function sstore_pop_stack (line 79) | fn sstore_pop_stack() {
  function sload_cost_pre_tangerine_whistle (line 89) | fn sload_cost_pre_tangerine_whistle() {
  function sstore_out_of_block_gas (line 106) | fn sstore_out_of_block_gas() {
  function sstore_cost (line 126) | fn sstore_cost() {
  function sstore_below_stipend (line 256) | fn sstore_below_stipend() {
  function tx_context (line 280) | fn tx_context() {
  function balance (line 332) | fn balance() {
  function account_info_homestead (line 351) | fn account_info_homestead() {
  function selfbalance (line 400) | fn selfbalance() {
  function log (line 428) | fn log() {
  function log0_empty (line 467) | fn log0_empty() {
  function log_data_cost (line 486) | fn log_data_cost() {
  function selfdestruct (line 515) | fn selfdestruct() {
  function selfdestruct_with_balance (line 575) | fn selfdestruct_with_balance() {
  function blockhash (line 890) | fn blockhash() {
  function extcode (line 941) | fn extcode() {
  function extcodesize (line 968) | fn extcodesize() {
  function extcodecopy_big_index (line 987) | fn extcodecopy_big_index() {
  function extcodehash (line 1005) | fn extcodehash() {
  function codecopy_empty (line 1031) | fn codecopy_empty() {
  function extcodecopy_empty (line 1048) | fn extcodecopy_empty() {
  function codecopy_memory_cost (line 1066) | fn codecopy_memory_cost() {
  function extcodecopy_memory_cost (line 1081) | fn extcodecopy_memory_cost() {
  function extcodecopy_nonzero_index (line 1096) | fn extcodecopy_nonzero_index() {
  function extcodecopy_fill_tail (line 1130) | fn extcodecopy_fill_tail() {
  function extcodecopy_buffer_overflow (line 1158) | fn extcodecopy_buffer_overflow() {
Condensed preview — 38 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (313K chars).
[
  {
    "path": ".github/workflows/main.yml",
    "chars": 1103,
    "preview": "on:\n  pull_request:\n  push:\n    branches:\n      - master\n\nname: CI\n\njobs:\n  ci:\n    runs-on: ${{ matrix.os }}\n    strate"
  },
  {
    "path": ".gitignore",
    "chars": 19,
    "preview": "/target\nCargo.lock\n"
  },
  {
    "path": "Cargo.toml",
    "chars": 1686,
    "preview": "[package]\nname = \"evmodin\"\nversion = \"0.1.0\"\nedition = \"2021\"\nlicense = \"Apache-2.0\"\ndescription = \"Fast EVM implementat"
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 2071,
    "preview": "# evmodin\n\nFast EVM implementation with support for resumability. Port of [evmone](https://github.com/ethereum/evmone) t"
  },
  {
    "path": "src/common.rs",
    "chars": 8000,
    "preview": "use bytes::Bytes;\nuse ethereum_types::*;\nuse serde::Serialize;\nuse strum_macros::Display;\n\n/// EVM revision.\n#[derive(Cl"
  },
  {
    "path": "src/continuation/interrupt.rs",
    "chars": 3614,
    "preview": "use super::*;\n\nmacro_rules! interrupt {\n    ( $(#[$outer:meta])* $name:ident, $data:ty => $resume_with:ty) => {\n\t\t$(#[$o"
  },
  {
    "path": "src/continuation/interrupt_data.rs",
    "chars": 1770,
    "preview": "use super::*;\n\n#[derive(Debug)]\npub struct InstructionStart {\n    pub pc: usize,\n    pub opcode: OpCode,\n    pub state: "
  },
  {
    "path": "src/continuation/mod.rs",
    "chars": 3228,
    "preview": "use self::{interrupt::*, interrupt_data::*, resume_data::*};\nuse crate::{\n    common::*,\n    host::{AccessStatus, Storag"
  },
  {
    "path": "src/continuation/resume_data.rs",
    "chars": 1696,
    "preview": "use super::*;\nuse educe::Educe;\nuse std::sync::Arc;\n\npub type StateModifier = Option<Arc<dyn Fn(&mut ExecutionState) + S"
  },
  {
    "path": "src/evmc.rs",
    "chars": 13685,
    "preview": "use crate::{common::*, host::*, tracing::*, AnalyzedCode};\nuse ::evmc_vm;\nuse ::evmc_vm::{ffi::*, EvmcVm, ExecutionConte"
  },
  {
    "path": "src/host.rs",
    "chars": 4627,
    "preview": "use crate::common::{Message, Output};\nuse ethereum_types::*;\n\n/// State access status (EIP-2929).\n#[derive(Clone, Copy, "
  },
  {
    "path": "src/instructions/arithmetic.rs",
    "chars": 3498,
    "preview": "use crate::{state::*, Revision, StatusCode};\nuse core::convert::TryInto;\nuse ethereum_types::{U256, U512};\nuse i256::I25"
  },
  {
    "path": "src/instructions/bitwise.rs",
    "chars": 1886,
    "preview": "use crate::state::Stack;\nuse ethereum_types::U256;\nuse i256::{Sign, I256};\n\npub(crate) fn byte(stack: &mut Stack) {\n    "
  },
  {
    "path": "src/instructions/boolean.rs",
    "chars": 1500,
    "preview": "use crate::state::*;\nuse ethereum_types::U256;\nuse i256::I256;\n\npub(crate) fn lt(stack: &mut Stack) {\n    let a = stack."
  },
  {
    "path": "src/instructions/call.rs",
    "chars": 8454,
    "preview": "#[doc(hidden)]\n#[macro_export]\nmacro_rules! do_call {\n    ($co:expr, $state:expr, $kind:expr, $is_static:expr) => {{\n   "
  },
  {
    "path": "src/instructions/control.rs",
    "chars": 1496,
    "preview": "use crate::state::ExecutionState;\nuse crate::{interpreter::JumpdestMap, StatusCode};\nuse ethereum_types::U256;\n\npub(crat"
  },
  {
    "path": "src/instructions/external.rs",
    "chars": 13802,
    "preview": "use crate::{common::address_to_u256, host::*, state::ExecutionState};\nuse ethereum_types::U256;\n\npub(crate) fn address(s"
  },
  {
    "path": "src/instructions/instruction_table.rs",
    "chars": 1560,
    "preview": "use crate::{instructions::properties, Revision};\nuse once_cell::sync::Lazy;\n\n#[derive(Clone, Copy, Debug)]\npub struct In"
  },
  {
    "path": "src/instructions/memory.rs",
    "chars": 10782,
    "preview": "use crate::{common::*, state::*};\nuse ethereum_types::U256;\nuse sha3::{Digest, Keccak256};\nuse std::{cmp::min, num::NonZ"
  },
  {
    "path": "src/instructions/mod.rs",
    "chars": 290,
    "preview": "pub(crate) mod arithmetic;\npub(crate) mod bitwise;\npub(crate) mod boolean;\npub(crate) mod call;\npub(crate) mod control;\n"
  },
  {
    "path": "src/instructions/properties.rs",
    "chars": 19765,
    "preview": "use once_cell::sync::Lazy;\n\nuse crate::{common::Revision, opcode::*};\n\npub(crate) const COLD_SLOAD_COST: u16 = 2100;\npub"
  },
  {
    "path": "src/instructions/stack_manip.rs",
    "chars": 420,
    "preview": "use crate::state::*;\nuse ethereum_types::U256;\n\npub(crate) fn push(stack: &mut Stack, code: &[u8], push_len: usize) {\n  "
  },
  {
    "path": "src/interpreter.rs",
    "chars": 20652,
    "preview": "use self::instruction_table::*;\nuse crate::{\n    common::*,\n    continuation::{interrupt::*, interrupt_data::*, resume_d"
  },
  {
    "path": "src/lib.rs",
    "chars": 591,
    "preview": "#![doc = include_str!(\"../README.md\")]\nuse bytes::Bytes;\npub use common::{\n    CallKind, CreateMessage, Message, Output,"
  },
  {
    "path": "src/opcode.rs",
    "chars": 13080,
    "preview": "use std::{borrow::Cow, fmt::Display};\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub struct OpCode(pub u8);\n\nimpl OpC"
  },
  {
    "path": "src/state.rs",
    "chars": 2506,
    "preview": "use crate::common::{Message, Revision};\nuse arrayvec::ArrayVec;\nuse bytes::Bytes;\nuse ethereum_types::U256;\nuse getset::"
  },
  {
    "path": "src/tracing/mod.rs",
    "chars": 3380,
    "preview": "use super::*;\nuse crate::state::*;\nuse serde::Serialize;\n\n/// Passed into execution context to collect metrics.\npub trai"
  },
  {
    "path": "src/util/bytecode.rs",
    "chars": 7232,
    "preview": "use crate::opcode::*;\nuse core::iter::repeat;\nuse ethereum_types::U256;\nuse std::ops::{Add, Mul};\n\n/// EVM bytecode buil"
  },
  {
    "path": "src/util/mocked_host.rs",
    "chars": 8861,
    "preview": "use crate::{host::*, *};\nuse bytes::Bytes;\nuse ethereum_types::*;\nuse hex_literal::hex;\nuse parking_lot::Mutex;\nuse std:"
  },
  {
    "path": "src/util/mod.rs",
    "chars": 88,
    "preview": "mod bytecode;\npub mod mocked_host;\nmod tester;\n\npub use bytecode::*;\npub use tester::*;\n"
  },
  {
    "path": "src/util/tester.rs",
    "chars": 8024,
    "preview": "use crate::{\n    tracing::*,\n    util::{mocked_host::*, *},\n    *,\n};\nuse bytes::Bytes;\nuse educe::Educe;\nuse ethereum_t"
  },
  {
    "path": "tests/basefee.rs",
    "chars": 850,
    "preview": "use evmodin::{opcode::*, util::*, *};\n\n#[test]\nfn basefee_pre_london() {\n    EvmTester::new()\n        .revision(Revision"
  },
  {
    "path": "tests/call.rs",
    "chars": 34513,
    "preview": "use bytes::Bytes;\nuse core::iter::repeat_with;\nuse ethereum_types::*;\nuse evmodin::{opcode::*, util::*, *};\nuse hex_lite"
  },
  {
    "path": "tests/eip2929.rs",
    "chars": 11552,
    "preview": "use evmodin::{host::*, opcode::*, util::*, *};\nuse hex_literal::hex;\n\n#[test]\nfn eip2929_case1() {\n    // https://gist.g"
  },
  {
    "path": "tests/execute.rs",
    "chars": 37619,
    "preview": "#![allow(clippy::needless_range_loop)]\n\nuse core::iter::repeat;\nuse ethereum_types::U256;\nuse evmodin::{opcode::*, util:"
  },
  {
    "path": "tests/other.rs",
    "chars": 1573,
    "preview": "use evmodin::{opcode::*, util::*, *};\n\n#[test]\nfn loop_full_of_jumpdests() {\n    // The code is a simple loop with a cou"
  },
  {
    "path": "tests/state.rs",
    "chars": 33115,
    "preview": "use ethereum_types::*;\nuse evmodin::{\n    opcode::*,\n    util::{mocked_host::*, *},\n    *,\n};\nuse hex_literal::hex;\n\n#[t"
  }
]

About this extraction

This page contains the full source code of the vorot93/evmodin GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 38 files (292.9 KB), approximately 77.2k tokens, and a symbol index with 640 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!