[
  {
    "path": ".github/workflows/main.yml",
    "content": "on:\n  pull_request:\n  push:\n    branches:\n      - master\n\nname: CI\n\njobs:\n  ci:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macOS-latest]\n\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          submodules: recursive\n\n      - uses: actions-rs/toolchain@v1\n        with:\n          profile: minimal\n          toolchain: nightly\n          override: true\n          components: rustfmt, clippy\n\n      - uses: actions-rs/cargo@v1\n        with:\n          command: fmt\n          args: --all -- --check\n\n      - uses: actions-rs/cargo@v1\n        with:\n          command: install\n          args: cargo-hack\n\n      - uses: actions-rs/cargo@v1\n        with:\n          command: hack\n          args: check --all --ignore-private --each-feature --no-dev-deps\n\n      - uses: actions-rs/cargo@v1\n        with:\n          command: check\n          args: --all --all-targets --all-features\n\n      - uses: actions-rs/cargo@v1\n        with:\n          command: test\n\n      - uses: actions-rs/cargo@v1\n        with:\n          command: clippy\n          args: -- -D warnings\n"
  },
  {
    "path": ".gitignore",
    "content": "/target\nCargo.lock\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"evmodin\"\nversion = \"0.1.0\"\nedition = \"2021\"\nlicense = \"Apache-2.0\"\ndescription = \"Fast EVM implementation with support for resumability.\"\n\n[dependencies]\narrayvec = { version = \"0.7\", default-features = false, features = [\"serde\"] }\nbytes = { version = \"1\", default-features = false, features = [\"serde\"] }\nderive_more = \"0.99\"\neduce = { version = \"0.4\", default-features = false, features = [\"Debug\"] }\nenum-as-inner = \"0.3\"\nevmc-declare = { git = \"https://github.com/ethereum/evmc\", tag = \"v10.0.0-alpha.1\", optional = true }\nevmc-vm = { git = \"https://github.com/ethereum/evmc\", tag = \"v10.0.0-alpha.1\", optional = true }\ni256 = { git = \"https://github.com/vorot93/rust-i256\" }\nethereum-types = { version = \"0.12\", default-features = false }\ngenawaiter = { git = \"https://github.com/cuviper/genawaiter\", branch = \"unhacked\" }\ngetset = \"0.1\"\nhex = \"0.4\"\nhex-literal = { version = \"0.3\", optional = true }\nnum-traits = { version = \"0.2\", default-features = false }\nonce_cell = \"1\"\nparking_lot = { version = \"0.11\", optional = true }\nprimitive-types = { version = \"0.10\", default-features = false, features = [\n    \"serde\",\n] }\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1\"\nsha3 = \"0.10\"\nstrum_macros = \"0.23\"\n\n[target.'cfg(target_arch = \"wasm32\")'.dependencies]\ngetrandom = { version = \"0.2.3\", features = [\"js\"] }\n\n[dev-dependencies]\nevmodin-test = { path = \".\", package = \"evmodin\", features = [\"util\"] }\nhex-literal = \"0.3\"\nrand = { version = \"0.8\", features = [\"std\"] }\n\n[features]\nevmc = [\"evmc-declare\", \"evmc-vm\"]\nutil = [\"hex-literal\", \"parking_lot\"]\n\n[lib]\nname = \"evmodin\"\npath = \"src/lib.rs\"\ncrate-type = [\"lib\", \"staticlib\", \"cdylib\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# evmodin\n\nFast EVM implementation with support for resumability. Port of [evmone](https://github.com/ethereum/evmone) to Rust.\n\n## Usage\n```rust\nuse evmodin::{*, host::*, util::*, tracing::*};\nuse ethereum_types::*;\nuse hex_literal::hex;\n\nlet my_code = Bytecode::new()\n    .mstore8_value(0, b'h')\n    .mstore8_value(1, b'e')\n    .mstore8_value(2, b'l')\n    .mstore8_value(3, b'l')\n    .mstore8_value(4, b'o')\n    .ret(0, 5)\n    .build();\n\nlet message = Message {\n    kind: CallKind::Call,\n    is_static: true,\n    depth: 0,\n    gas: 200,\n    recipient: Address::zero(),\n    code_address: Address::zero(),\n    sender: Address::zero(),\n    input_data: vec![].into(),\n    value: U256::zero(),\n};\n\nassert_eq!(\n    AnalyzedCode::analyze(my_code)\n        .execute(&mut DummyHost, &mut NoopTracer, None, message, Revision::latest()),\n    Output {\n        status_code: StatusCode::Success,\n        gas_left: 146,\n        output_data: b\"hello\".to_vec().into(),\n        create_address: None,\n    }\n)\n```\n\n## Host / interpreter separation\n`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.\n\n[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.\n\n## Resumability\n`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.\n"
  },
  {
    "path": "src/common.rs",
    "content": "use bytes::Bytes;\nuse ethereum_types::*;\nuse serde::Serialize;\nuse strum_macros::Display;\n\n/// EVM revision.\n#[derive(Clone, Copy, Debug, Display, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]\npub enum Revision {\n    /// The Frontier revision.\n    /// The one Ethereum launched with.\n    Frontier = 0,\n\n    /// [The Homestead revision.](https://eips.ethereum.org/EIPS/eip-606)\n    Homestead = 1,\n\n    /// [The Tangerine Whistle revision.](https://eips.ethereum.org/EIPS/eip-608)\n    Tangerine = 2,\n\n    /// [The Spurious Dragon revision.](https://eips.ethereum.org/EIPS/eip-607)\n    Spurious = 3,\n\n    /// [The Byzantium revision.](https://eips.ethereum.org/EIPS/eip-609)\n    Byzantium = 4,\n\n    /// [The Constantinople revision.](https://eips.ethereum.org/EIPS/eip-1013)\n    Constantinople = 5,\n\n    /// [The Petersburg revision.](https://eips.ethereum.org/EIPS/eip-1716)\n    Petersburg = 6,\n\n    /// [The Istanbul revision.](https://eips.ethereum.org/EIPS/eip-1679)\n    Istanbul = 7,\n\n    /// [The Berlin revision.](https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md)\n    Berlin = 8,\n\n    /// [The London revision.](https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/london.md)\n    London = 9,\n\n    /// The Shanghai revision.\n    Shanghai = 10,\n}\n\nimpl Revision {\n    pub fn iter() -> impl IntoIterator<Item = Self> {\n        [\n            Self::Frontier,\n            Self::Homestead,\n            Self::Tangerine,\n            Self::Spurious,\n            Self::Byzantium,\n            Self::Constantinople,\n            Self::Petersburg,\n            Self::Istanbul,\n            Self::Berlin,\n            Self::London,\n            Self::Shanghai,\n        ]\n    }\n\n    pub const fn latest() -> Self {\n        Self::Shanghai\n    }\n\n    pub const fn len() -> usize {\n        Self::latest() as usize + 1\n    }\n}\n\n/// Message status code.\n#[must_use]\n#[derive(Clone, Debug, Display, PartialEq)]\npub enum StatusCode {\n    /// Execution finished with success.\n    #[strum(serialize = \"success\")]\n    Success,\n\n    /// Generic execution failure.\n    #[strum(serialize = \"failure\")]\n    Failure,\n\n    /// Execution terminated with REVERT opcode.\n    ///\n    /// In this case the amount of gas left MAY be non-zero and additional output\n    /// data MAY be provided in ::evmc_result.\n    #[strum(serialize = \"revert\")]\n    Revert,\n\n    /// The execution has run out of gas.\n    #[strum(serialize = \"out of gas\")]\n    OutOfGas,\n\n    /// The designated INVALID instruction has been hit during execution.\n    ///\n    /// [EIP-141](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-141.md)\n    /// defines the instruction 0xfe as INVALID instruction to indicate execution\n    /// abortion coming from high-level languages. This status code is reported\n    /// in case this INVALID instruction has been encountered.\n    #[strum(serialize = \"invalid instruction\")]\n    InvalidInstruction,\n\n    /// An undefined instruction has been encountered.\n    #[strum(serialize = \"undefined instruction\")]\n    UndefinedInstruction,\n\n    /// The execution has attempted to put more items on the EVM stack\n    /// than the specified limit.\n    #[strum(serialize = \"stack overflow\")]\n    StackOverflow,\n\n    /// Execution of an opcode has required more items on the EVM stack.\n    #[strum(serialize = \"stack underflow\")]\n    StackUnderflow,\n\n    /// Execution has violated the jump destination restrictions.\n    #[strum(serialize = \"bad jump destination\")]\n    BadJumpDestination,\n\n    /// Tried to read outside memory bounds.\n    ///\n    /// An example is RETURNDATACOPY reading past the available buffer.\n    #[strum(serialize = \"invalid memory access\")]\n    InvalidMemoryAccess,\n\n    /// Call depth has exceeded the limit (if any)\n    #[strum(serialize = \"call depth exceeded\")]\n    CallDepthExceeded,\n\n    /// Tried to execute an operation which is restricted in static mode.\n    #[strum(serialize = \"static mode violation\")]\n    StaticModeViolation,\n\n    /// A call to a precompiled or system contract has ended with a failure.\n    ///\n    /// An example: elliptic curve functions handed invalid EC points.\n    #[strum(serialize = \"precompile failure\")]\n    PrecompileFailure,\n\n    /// Contract validation has failed.\n    #[strum(serialize = \"contract validation failure\")]\n    ContractValidationFailure,\n\n    /// An argument to a state accessing method has a value outside of the\n    /// accepted range of values.\n    #[strum(serialize = \"argument out of range\")]\n    ArgumentOutOfRange,\n\n    /// The caller does not have enough funds for value transfer.\n    #[strum(serialize = \"insufficient balance\")]\n    InsufficientBalance,\n\n    /// EVM implementation generic internal error.\n    #[strum(serialize = \"internal error\")]\n    InternalError(String),\n}\n\n/// The kind of call-like instruction.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum CallKind {\n    Call,\n    DelegateCall,\n    CallCode,\n    Create,\n    Create2 { salt: U256 },\n}\n\n/// The message describing an EVM call,\n/// including a zero-depth call from transaction origin.\n#[derive(Clone, Debug, PartialEq)]\npub struct Message {\n    /// The kind of the call. For zero-depth calls `CallKind::Call` SHOULD be used.\n    pub kind: CallKind,\n\n    /// Static call mode.\n    pub is_static: bool,\n\n    /// The call depth.\n    pub depth: i32,\n\n    /// The amount of gas for message execution.\n    pub gas: i64,\n\n    /// The destination (recipient) of the message.\n    pub recipient: Address,\n\n    /// The sender of the message.\n    pub sender: Address,\n\n    /// Message input data.\n    pub input_data: Bytes,\n\n    /// The amount of Ether transferred with the message.\n    pub value: U256,\n\n    /// The address of the code to be executed.\n    ///\n    /// May be different from the evmc_message::destination (recipient)\n    /// in case of `CallKind::CallCode` or `CallKind::DelegateCall`.\n    pub code_address: Address,\n}\n\n#[derive(Clone, Debug, PartialEq)]\npub struct CreateMessage {\n    pub salt: Option<U256>,\n    pub gas: i64,\n    pub depth: i32,\n    pub initcode: Bytes,\n    pub sender: Address,\n    pub endowment: U256,\n}\n\nimpl From<CreateMessage> for Message {\n    fn from(msg: CreateMessage) -> Self {\n        Self {\n            kind: if let Some(salt) = msg.salt {\n                CallKind::Create2 { salt }\n            } else {\n                CallKind::Create\n            },\n            is_static: false,\n            depth: msg.depth,\n            gas: msg.gas,\n            recipient: Address::zero(),\n            code_address: Address::zero(),\n            sender: msg.sender,\n            input_data: msg.initcode,\n            value: msg.endowment,\n        }\n    }\n}\n\n/// Output of EVM execution.\n#[derive(Clone, Debug, PartialEq)]\npub struct Output {\n    /// EVM exited with this status code.\n    pub status_code: StatusCode,\n    /// How much gas was left after execution\n    pub gas_left: i64,\n    /// Output data returned.\n    pub output_data: Bytes,\n    /// Contract creation address.\n    pub create_address: Option<Address>,\n}\n\n/// EVM execution output if no error has occurred.\n#[derive(Clone, Debug, PartialEq)]\npub struct SuccessfulOutput {\n    /// Indicates if revert was requested.\n    pub reverted: bool,\n    /// How much gas was left after execution.\n    pub gas_left: i64,\n    /// Output data returned.\n    pub output_data: Bytes,\n}\n\nimpl From<SuccessfulOutput> for Output {\n    fn from(\n        SuccessfulOutput {\n            reverted,\n            gas_left,\n            output_data,\n        }: SuccessfulOutput,\n    ) -> Self {\n        Self {\n            status_code: if reverted {\n                StatusCode::Revert\n            } else {\n                StatusCode::Success\n            },\n            gas_left,\n            output_data,\n            create_address: None,\n        }\n    }\n}\n\npub(crate) fn u256_to_address(v: U256) -> Address {\n    H256(v.into()).into()\n}\n\npub(crate) fn address_to_u256(v: Address) -> U256 {\n    U256::from_big_endian(&v.0)\n}\n"
  },
  {
    "path": "src/continuation/interrupt.rs",
    "content": "use super::*;\n\nmacro_rules! interrupt {\n    ( $(#[$outer:meta])* $name:ident, $data:ty => $resume_with:ty) => {\n\t\t$(#[$outer])*\n        pub struct $name {\n            pub(crate) inner: ::core::pin::Pin<\n                Box<\n                    dyn Coroutine<\n                            Yield = InterruptDataVariant,\n                            Resume = ResumeDataVariant,\n                            Return = Result<SuccessfulOutput, StatusCode>,\n                        > + Send\n                        + Sync\n                        + Unpin,\n                >,\n            >,\n            pub(crate) data: $data,\n        }\n\n        impl sealed::Sealed for $name {}\n\n        impl Interrupt for $name {\n            type InterruptData = $data;\n            type ResumeData = $resume_with;\n\n            fn data(&self) -> &Self::InterruptData {\n                &self.data\n            }\n\n            fn resume(self, resume_data: $resume_with) -> InterruptVariant {\n                resume_interrupt(self.inner, resume_data.into())\n            }\n        }\n    };\n}\n\ninterrupt! {\n    /// EVM has just been created. Resume this interrupt to start execution.\n    ExecutionStartInterrupt,\n    () => ()\n}\ninterrupt! {\n    /// New instruction has been encountered.\n    InstructionStartInterrupt,\n    Box<InstructionStart> => StateModifier\n}\ninterrupt! {\n    /// Does this account exist?\n    AccountExistsInterrupt,\n    AccountExists => AccountExistsStatus\n}\ninterrupt! {\n    /// Need this storage key.\n    GetStorageInterrupt,\n    GetStorage => StorageValue\n}\ninterrupt! {\n    /// Set this storage key.\n    SetStorageInterrupt,\n    SetStorage => StorageStatusInfo\n}\ninterrupt! {\n    /// Get balance of this account.\n    GetBalanceInterrupt,\n    GetBalance => Balance\n}\ninterrupt! {\n    /// Get code size of this account.\n    GetCodeSizeInterrupt,\n    GetCodeSize => CodeSize\n}\ninterrupt! {\n    /// Get code hash of this account.\n    GetCodeHashInterrupt,\n    GetCodeHash => CodeHash\n}\ninterrupt! {\n    /// Get code of this account.\n    CopyCodeInterrupt,\n    CopyCode => Code\n}\ninterrupt! {\n    /// Selfdestruct this account.\n    SelfdestructInterrupt,\n    Selfdestruct => ()\n}\ninterrupt! {\n    /// Execute this message as a new call.\n    CallInterrupt,\n    Call => CallOutput\n}\ninterrupt! {\n    /// Get `TxContext` for this call.\n    GetTxContextInterrupt,\n    () => TxContextData\n}\ninterrupt! {\n    /// Get block hash for this account.\n    GetBlockHashInterrupt,\n    GetBlockHash => BlockHash\n}\ninterrupt! {\n    /// Emit log message.\n    EmitLogInterrupt,\n    EmitLog => ()\n}\ninterrupt! {\n    /// Access this account and return its status.\n    AccessAccountInterrupt,\n    AccessAccount => AccessAccountStatus\n}\ninterrupt! {\n    /// Access this storage key and return its status.\n    AccessStorageInterrupt,\n    AccessStorage => AccessStorageStatus\n}\n\n/// Collection of all possible interrupts. Match on this to get the specific interrupt returned.\n#[derive(From)]\npub enum InterruptVariant {\n    InstructionStart(InstructionStartInterrupt),\n    AccountExists(AccountExistsInterrupt),\n    GetStorage(GetStorageInterrupt),\n    SetStorage(SetStorageInterrupt),\n    GetBalance(GetBalanceInterrupt),\n    GetCodeSize(GetCodeSizeInterrupt),\n    GetCodeHash(GetCodeHashInterrupt),\n    CopyCode(CopyCodeInterrupt),\n    Selfdestruct(SelfdestructInterrupt),\n    Call(CallInterrupt),\n    GetTxContext(GetTxContextInterrupt),\n    GetBlockHash(GetBlockHashInterrupt),\n    EmitLog(EmitLogInterrupt),\n    AccessAccount(AccessAccountInterrupt),\n    AccessStorage(AccessStorageInterrupt),\n    Complete(Result<SuccessfulOutput, StatusCode>),\n}\n"
  },
  {
    "path": "src/continuation/interrupt_data.rs",
    "content": "use super::*;\n\n#[derive(Debug)]\npub struct InstructionStart {\n    pub pc: usize,\n    pub opcode: OpCode,\n    pub state: ExecutionState,\n}\n\n#[derive(Debug)]\npub struct AccountExists {\n    pub address: Address,\n}\n\n#[derive(Debug)]\npub struct GetStorage {\n    pub address: Address,\n    pub key: U256,\n}\n\n#[derive(Debug)]\npub struct SetStorage {\n    pub address: Address,\n    pub key: U256,\n    pub value: U256,\n}\n\n#[derive(Debug)]\npub struct GetBalance {\n    pub address: Address,\n}\n\n#[derive(Debug)]\npub struct GetCodeSize {\n    pub address: Address,\n}\n\n#[derive(Debug)]\npub struct GetCodeHash {\n    pub address: Address,\n}\n\n#[derive(Debug)]\npub struct CopyCode {\n    pub address: Address,\n    pub offset: usize,\n    pub max_size: usize,\n}\n\n#[derive(Debug)]\npub struct Selfdestruct {\n    pub address: Address,\n    pub beneficiary: Address,\n}\n\n#[derive(Debug)]\npub enum Call {\n    Call(Message),\n    Create(CreateMessage),\n}\n\n#[derive(Debug)]\npub struct GetBlockHash {\n    pub block_number: u64,\n}\n\n#[derive(Debug)]\npub struct EmitLog {\n    pub address: Address,\n    pub data: Bytes,\n    pub topics: ArrayVec<U256, 4>,\n}\n\n#[derive(Debug)]\npub struct AccessAccount {\n    pub address: Address,\n}\n\n#[derive(Debug)]\npub struct AccessStorage {\n    pub address: Address,\n    pub key: U256,\n}\n\n#[derive(Debug)]\npub enum InterruptDataVariant {\n    InstructionStart(Box<InstructionStart>),\n    AccountExists(AccountExists),\n    GetStorage(GetStorage),\n    SetStorage(SetStorage),\n    GetBalance(GetBalance),\n    GetCodeSize(GetCodeSize),\n    GetCodeHash(GetCodeHash),\n    CopyCode(CopyCode),\n    Selfdestruct(Selfdestruct),\n    Call(Call),\n    GetTxContext,\n    GetBlockHash(GetBlockHash),\n    EmitLog(EmitLog),\n    AccessAccount(AccessAccount),\n    AccessStorage(AccessStorage),\n}\n"
  },
  {
    "path": "src/continuation/mod.rs",
    "content": "use self::{interrupt::*, interrupt_data::*, resume_data::*};\nuse crate::{\n    common::*,\n    host::{AccessStatus, StorageStatus, TxContext},\n    state::ExecutionState,\n    *,\n};\nuse arrayvec::ArrayVec;\nuse derive_more::From;\nuse enum_as_inner::EnumAsInner;\nuse ethereum_types::*;\nuse genawaiter::{Coroutine, GeneratorState};\nuse std::{convert::Infallible, pin::Pin};\n\nmod sealed {\n    pub trait Sealed {}\n}\n\n/// Interrupts.\npub mod interrupt;\n/// Data attached to interrupts.\npub mod interrupt_data;\n/// Data required for resume.\npub mod resume_data;\n\n/// Paused EVM with full state inside.\npub trait Interrupt: sealed::Sealed {\n    /// Interrupt data returned.\n    type InterruptData;\n    /// Data required to resume execution.\n    type ResumeData;\n\n    /// Get interrupt data.\n    fn data(&self) -> &Self::InterruptData;\n    /// Resume execution until the next interrupt.\n    fn resume(self, resume_data: Self::ResumeData) -> InterruptVariant;\n}\n\ntype InnerCoroutine = Pin<\n    Box<\n        dyn Coroutine<\n                Yield = InterruptDataVariant,\n                Resume = ResumeDataVariant,\n                Return = Result<SuccessfulOutput, StatusCode>,\n            > + Send\n            + Sync\n            + Unpin,\n    >,\n>;\n\nfn resume_interrupt(mut inner: InnerCoroutine, resume_data: ResumeDataVariant) -> InterruptVariant {\n    match Pin::new(&mut *inner).resume_with(resume_data) {\n        GeneratorState::Yielded(interrupt) => match interrupt {\n            InterruptDataVariant::InstructionStart(data) => {\n                InstructionStartInterrupt { inner, data }.into()\n            }\n            InterruptDataVariant::AccountExists(data) => {\n                AccountExistsInterrupt { inner, data }.into()\n            }\n            InterruptDataVariant::GetStorage(data) => GetStorageInterrupt { inner, data }.into(),\n            InterruptDataVariant::SetStorage(data) => SetStorageInterrupt { inner, data }.into(),\n            InterruptDataVariant::GetBalance(data) => GetBalanceInterrupt { inner, data }.into(),\n            InterruptDataVariant::GetCodeSize(data) => GetCodeSizeInterrupt { inner, data }.into(),\n            InterruptDataVariant::GetCodeHash(data) => GetCodeHashInterrupt { inner, data }.into(),\n            InterruptDataVariant::CopyCode(data) => CopyCodeInterrupt { inner, data }.into(),\n            InterruptDataVariant::Selfdestruct(data) => {\n                SelfdestructInterrupt { inner, data }.into()\n            }\n            InterruptDataVariant::Call(data) => CallInterrupt { inner, data }.into(),\n            InterruptDataVariant::GetTxContext => GetTxContextInterrupt { inner, data: () }.into(),\n            InterruptDataVariant::GetBlockHash(data) => {\n                GetBlockHashInterrupt { inner, data }.into()\n            }\n            InterruptDataVariant::EmitLog(data) => EmitLogInterrupt { inner, data }.into(),\n            InterruptDataVariant::AccessAccount(data) => {\n                AccessAccountInterrupt { inner, data }.into()\n            }\n            InterruptDataVariant::AccessStorage(data) => {\n                AccessStorageInterrupt { inner, data }.into()\n            }\n        },\n        GeneratorState::Complete(res) => InterruptVariant::Complete(res),\n    }\n}\n"
  },
  {
    "path": "src/continuation/resume_data.rs",
    "content": "use super::*;\nuse educe::Educe;\nuse std::sync::Arc;\n\npub type StateModifier = Option<Arc<dyn Fn(&mut ExecutionState) + Send + Sync>>;\n\n#[derive(Debug)]\npub struct AccountExistsStatus {\n    pub exists: bool,\n}\n\n#[derive(Debug)]\npub struct Balance {\n    pub balance: U256,\n}\n\n#[derive(Debug)]\npub struct CodeSize {\n    pub code_size: U256,\n}\n\n#[derive(Debug)]\npub struct StorageValue {\n    pub value: U256,\n}\n\n#[derive(Debug)]\npub struct StorageStatusInfo {\n    pub status: StorageStatus,\n}\n\n#[derive(Debug)]\npub struct CodeHash {\n    pub hash: U256,\n}\n\n#[derive(Debug)]\npub struct BlockHash {\n    pub hash: U256,\n}\n\n#[derive(Debug)]\npub struct TxContextData {\n    pub context: TxContext,\n}\n\n#[derive(Debug)]\npub struct Code {\n    pub code: Bytes,\n}\n\n#[derive(Debug)]\npub struct CallOutput {\n    pub output: Output,\n}\n\n#[derive(Debug)]\npub struct AccessAccountStatus {\n    pub status: AccessStatus,\n}\n\n#[derive(Debug)]\npub struct AccessStorageStatus {\n    pub status: AccessStatus,\n}\n\n/// All resumed data variants.\n#[derive(Educe, EnumAsInner, From)]\n#[educe(Debug)]\npub(crate) enum ResumeDataVariant {\n    #[from(ignore)]\n    Empty,\n    StateModifier(#[educe(Debug(false))] StateModifier),\n    AccountExistsStatus(AccountExistsStatus),\n    Balance(Balance),\n    CodeSize(CodeSize),\n    StorageValue(StorageValue),\n    StorageStatusInfo(StorageStatusInfo),\n    CodeHash(CodeHash),\n    BlockHash(BlockHash),\n    TxContextData(TxContextData),\n    Code(Code),\n    CallOutput(CallOutput),\n    AccessAccountStatus(AccessAccountStatus),\n    AccessStorageStatus(AccessStorageStatus),\n    Done(Infallible),\n}\n\nimpl From<()> for ResumeDataVariant {\n    fn from(_: ()) -> Self {\n        Self::Empty\n    }\n}\n"
  },
  {
    "path": "src/evmc.rs",
    "content": "use crate::{common::*, host::*, tracing::*, AnalyzedCode};\nuse ::evmc_vm;\nuse ::evmc_vm::{ffi::*, EvmcVm, ExecutionContext, ExecutionMessage, MessageFlags, MessageKind};\nuse arrayvec::ArrayVec;\nuse bytes::Bytes;\nuse ethereum_types::*;\nuse evmc_vm::ExecutionResult;\nuse std::convert::TryInto;\n\npub(crate) trait Convert {\n    type Into;\n\n    fn convert(self) -> Self::Into;\n}\n\nimpl Convert for Address {\n    type Into = evmc_address;\n\n    fn convert(self) -> Self::Into {\n        evmc_address {\n            bytes: self.to_fixed_bytes(),\n        }\n    }\n}\n\nimpl Convert for H256 {\n    type Into = evmc_bytes32;\n\n    fn convert(self) -> Self::Into {\n        evmc_bytes32 {\n            bytes: self.to_fixed_bytes(),\n        }\n    }\n}\n\nimpl Convert for U256 {\n    type Into = evmc_uint256be;\n\n    fn convert(self) -> Self::Into {\n        evmc_uint256be { bytes: self.into() }\n    }\n}\n\nimpl From<evmc_access_status> for AccessStatus {\n    fn from(s: evmc_access_status) -> Self {\n        match s {\n            evmc_access_status::EVMC_ACCESS_COLD => Self::Cold,\n            evmc_access_status::EVMC_ACCESS_WARM => Self::Warm,\n        }\n    }\n}\n\nimpl From<Message> for ExecutionMessage {\n    fn from(msg: Message) -> Self {\n        let mut create2_salt = evmc_bytes32::default();\n        let kind = match msg.kind {\n            crate::CallKind::Call => MessageKind::EVMC_CALL,\n            crate::CallKind::DelegateCall => MessageKind::EVMC_DELEGATECALL,\n            crate::CallKind::CallCode => MessageKind::EVMC_CALLCODE,\n            crate::CallKind::Create => MessageKind::EVMC_CREATE,\n            crate::CallKind::Create2 { salt } => {\n                create2_salt = salt.convert();\n                MessageKind::EVMC_CREATE2\n            }\n        };\n        let flags = if msg.is_static {\n            MessageFlags::EVMC_STATIC as u32\n        } else {\n            0\n        };\n\n        ExecutionMessage::new(\n            kind,\n            flags,\n            msg.depth,\n            msg.gas,\n            msg.recipient.convert(),\n            msg.sender.convert(),\n            (!msg.input_data.is_empty()).then(|| &*msg.input_data),\n            msg.value.convert(),\n            create2_salt,\n            msg.code_address.convert(),\n        )\n    }\n}\n\nimpl Message {\n    fn from_evmc(msg: &ExecutionMessage) -> Self {\n        let kind = match msg.kind() {\n            evmc_call_kind::EVMC_CALL => CallKind::Call,\n            evmc_call_kind::EVMC_DELEGATECALL => CallKind::DelegateCall,\n            evmc_call_kind::EVMC_CALLCODE => CallKind::CallCode,\n            evmc_call_kind::EVMC_CREATE => CallKind::Create,\n            evmc_call_kind::EVMC_CREATE2 => CallKind::Create2 {\n                salt: msg.create2_salt().bytes.into(),\n            },\n        };\n\n        Self {\n            kind,\n            is_static: msg.flags() == evmc_vm::ffi::evmc_flags::EVMC_STATIC as u32,\n            depth: msg.depth(),\n            gas: msg.gas(),\n            recipient: msg.recipient().bytes.into(),\n            sender: msg.sender().bytes.into(),\n            input_data: msg\n                .input()\n                .map(|v| v.clone().into())\n                .unwrap_or_else(Bytes::new),\n            value: msg.value().bytes.into(),\n            code_address: msg.code_address().bytes.into(),\n        }\n    }\n}\n\nimpl From<evmc_status_code> for StatusCode {\n    fn from(status: evmc_status_code) -> Self {\n        match status {\n            evmc_status_code::EVMC_SUCCESS => StatusCode::Success,\n            evmc_status_code::EVMC_FAILURE => StatusCode::Failure,\n            evmc_status_code::EVMC_REVERT => StatusCode::Revert,\n            evmc_status_code::EVMC_OUT_OF_GAS => StatusCode::OutOfGas,\n            evmc_status_code::EVMC_INVALID_INSTRUCTION => StatusCode::InvalidInstruction,\n            evmc_status_code::EVMC_UNDEFINED_INSTRUCTION => StatusCode::UndefinedInstruction,\n            evmc_status_code::EVMC_STACK_OVERFLOW => StatusCode::StackOverflow,\n            evmc_status_code::EVMC_STACK_UNDERFLOW => StatusCode::StackUnderflow,\n            evmc_status_code::EVMC_BAD_JUMP_DESTINATION => StatusCode::BadJumpDestination,\n            evmc_status_code::EVMC_INVALID_MEMORY_ACCESS => StatusCode::InvalidMemoryAccess,\n            evmc_status_code::EVMC_CALL_DEPTH_EXCEEDED => StatusCode::CallDepthExceeded,\n            evmc_status_code::EVMC_STATIC_MODE_VIOLATION => StatusCode::StaticModeViolation,\n            evmc_status_code::EVMC_PRECOMPILE_FAILURE => StatusCode::PrecompileFailure,\n            evmc_status_code::EVMC_CONTRACT_VALIDATION_FAILURE => {\n                StatusCode::ContractValidationFailure\n            }\n            evmc_status_code::EVMC_ARGUMENT_OUT_OF_RANGE => StatusCode::ArgumentOutOfRange,\n            evmc_status_code::EVMC_WASM_UNREACHABLE_INSTRUCTION => {\n                StatusCode::InternalError(\"WasmUnreachableInstruction\".into())\n            }\n            evmc_status_code::EVMC_WASM_TRAP => StatusCode::InternalError(\"WasmTrap\".into()),\n            evmc_status_code::EVMC_INSUFFICIENT_BALANCE => StatusCode::InsufficientBalance,\n            evmc_status_code::EVMC_INTERNAL_ERROR => StatusCode::InternalError(String::new()),\n            evmc_status_code::EVMC_REJECTED => StatusCode::InternalError(\"Rejected\".into()),\n            evmc_status_code::EVMC_OUT_OF_MEMORY => StatusCode::InternalError(\"OutOfMemory\".into()),\n        }\n    }\n}\n\nimpl From<StatusCode> for evmc_status_code {\n    fn from(status: StatusCode) -> Self {\n        match status {\n            StatusCode::Success => evmc_status_code::EVMC_SUCCESS,\n            StatusCode::Failure => evmc_status_code::EVMC_FAILURE,\n            StatusCode::Revert => evmc_status_code::EVMC_REVERT,\n            StatusCode::OutOfGas => evmc_status_code::EVMC_OUT_OF_GAS,\n            StatusCode::InvalidInstruction => evmc_status_code::EVMC_INVALID_INSTRUCTION,\n            StatusCode::UndefinedInstruction => evmc_status_code::EVMC_UNDEFINED_INSTRUCTION,\n            StatusCode::StackOverflow => evmc_status_code::EVMC_STACK_OVERFLOW,\n            StatusCode::StackUnderflow => evmc_status_code::EVMC_STACK_UNDERFLOW,\n            StatusCode::BadJumpDestination => evmc_status_code::EVMC_BAD_JUMP_DESTINATION,\n            StatusCode::InvalidMemoryAccess => evmc_status_code::EVMC_INVALID_MEMORY_ACCESS,\n            StatusCode::CallDepthExceeded => evmc_status_code::EVMC_CALL_DEPTH_EXCEEDED,\n            StatusCode::StaticModeViolation => evmc_status_code::EVMC_STATIC_MODE_VIOLATION,\n            StatusCode::PrecompileFailure => evmc_status_code::EVMC_PRECOMPILE_FAILURE,\n            StatusCode::ContractValidationFailure => {\n                evmc_status_code::EVMC_CONTRACT_VALIDATION_FAILURE\n            }\n            StatusCode::ArgumentOutOfRange => evmc_status_code::EVMC_ARGUMENT_OUT_OF_RANGE,\n            StatusCode::InsufficientBalance => evmc_status_code::EVMC_INSUFFICIENT_BALANCE,\n            StatusCode::InternalError(_) => evmc_status_code::EVMC_INTERNAL_ERROR,\n        }\n    }\n}\n\nimpl<'a> Host for ExecutionContext<'a> {\n    fn account_exists(&self, address: Address) -> bool {\n        ExecutionContext::account_exists(self, &address.convert())\n    }\n\n    fn get_storage(&self, address: Address, key: U256) -> U256 {\n        ExecutionContext::get_storage(self, &address.convert(), &key.convert())\n            .bytes\n            .into()\n    }\n\n    fn set_storage(&mut self, address: Address, key: U256, value: U256) -> StorageStatus {\n        match ExecutionContext::set_storage(\n            self,\n            &address.convert(),\n            &key.convert(),\n            &value.convert(),\n        ) {\n            evmc_storage_status::EVMC_STORAGE_UNCHANGED => StorageStatus::Unchanged,\n            evmc_storage_status::EVMC_STORAGE_MODIFIED => StorageStatus::Modified,\n            evmc_storage_status::EVMC_STORAGE_MODIFIED_AGAIN => StorageStatus::ModifiedAgain,\n            evmc_storage_status::EVMC_STORAGE_ADDED => StorageStatus::Added,\n            evmc_storage_status::EVMC_STORAGE_DELETED => StorageStatus::Deleted,\n        }\n    }\n\n    fn get_balance(&self, address: Address) -> U256 {\n        ExecutionContext::get_balance(self, &address.convert())\n            .bytes\n            .into()\n    }\n\n    fn get_code_size(&self, address: Address) -> U256 {\n        ExecutionContext::get_code_size(self, &address.convert()).into()\n    }\n\n    fn get_code_hash(&self, address: Address) -> U256 {\n        ExecutionContext::get_code_hash(self, &address.convert())\n            .bytes\n            .into()\n    }\n\n    fn copy_code(&self, address: Address, offset: usize, buffer: &mut [u8]) -> usize {\n        ExecutionContext::copy_code(self, &address.convert(), offset, buffer)\n    }\n\n    fn selfdestruct(&mut self, address: Address, beneficiary: Address) {\n        ExecutionContext::selfdestruct(self, &address.convert(), &beneficiary.convert())\n    }\n\n    fn call(&mut self, msg: &Message) -> Output {\n        let execution_result = ExecutionContext::call(self, &msg.clone().into());\n\n        Output {\n            status_code: execution_result.status_code().into(),\n            gas_left: execution_result.gas_left(),\n            output_data: execution_result\n                .output()\n                .map(|v| v.to_vec().into())\n                .unwrap_or_else(Bytes::new),\n            create_address: execution_result.create_address().map(|a| a.bytes.into()),\n        }\n    }\n\n    fn get_tx_context(&self) -> TxContext {\n        let c = ExecutionContext::get_tx_context(self);\n\n        TxContext {\n            tx_gas_price: c.tx_gas_price.bytes.into(),\n            tx_origin: c.tx_origin.bytes.into(),\n            block_coinbase: c.block_coinbase.bytes.into(),\n            block_number: c.block_number.try_into().unwrap(),\n            block_timestamp: c.block_timestamp.try_into().unwrap(),\n            block_gas_limit: c.block_gas_limit.try_into().unwrap(),\n            block_difficulty: c.block_difficulty.bytes.into(),\n            chain_id: c.chain_id.bytes.into(),\n            block_base_fee: c.block_base_fee.bytes.into(),\n        }\n    }\n\n    fn get_block_hash(&self, block_number: u64) -> U256 {\n        ExecutionContext::get_block_hash(self, block_number.try_into().unwrap())\n            .bytes\n            .into()\n    }\n\n    fn emit_log(&mut self, address: Address, data: &[u8], topics: &[U256]) {\n        ExecutionContext::emit_log(\n            self,\n            &address.convert(),\n            data,\n            &topics\n                .iter()\n                .map(|topic| topic.convert())\n                .collect::<ArrayVec<_, 4>>(),\n        )\n    }\n\n    fn access_account(&mut self, address: Address) -> AccessStatus {\n        ExecutionContext::access_account(self, &address.convert()).into()\n    }\n\n    fn access_storage(&mut self, address: Address, key: U256) -> AccessStatus {\n        ExecutionContext::access_storage(self, &address.convert(), &key.convert()).into()\n    }\n}\n\nimpl From<evmc_vm::Revision> for Revision {\n    fn from(rev: evmc_vm::Revision) -> Self {\n        match rev {\n            evmc_revision::EVMC_FRONTIER => Revision::Frontier,\n            evmc_revision::EVMC_HOMESTEAD => Revision::Homestead,\n            evmc_revision::EVMC_TANGERINE_WHISTLE => Revision::Tangerine,\n            evmc_revision::EVMC_SPURIOUS_DRAGON => Revision::Spurious,\n            evmc_revision::EVMC_BYZANTIUM => Revision::Byzantium,\n            evmc_revision::EVMC_CONSTANTINOPLE => Revision::Constantinople,\n            evmc_revision::EVMC_PETERSBURG => Revision::Petersburg,\n            evmc_revision::EVMC_ISTANBUL => Revision::Istanbul,\n            evmc_revision::EVMC_BERLIN => Revision::Berlin,\n            evmc_revision::EVMC_LONDON => Revision::London,\n            evmc_revision::EVMC_SHANGHAI => Revision::Shanghai,\n        }\n    }\n}\n\nimpl From<Revision> for evmc_vm::Revision {\n    fn from(rev: Revision) -> Self {\n        match rev {\n            Revision::Frontier => evmc_revision::EVMC_FRONTIER,\n            Revision::Homestead => evmc_revision::EVMC_HOMESTEAD,\n            Revision::Tangerine => evmc_revision::EVMC_TANGERINE_WHISTLE,\n            Revision::Spurious => evmc_revision::EVMC_SPURIOUS_DRAGON,\n            Revision::Byzantium => evmc_revision::EVMC_BYZANTIUM,\n            Revision::Constantinople => evmc_revision::EVMC_CONSTANTINOPLE,\n            Revision::Petersburg => evmc_revision::EVMC_PETERSBURG,\n            Revision::Istanbul => evmc_revision::EVMC_ISTANBUL,\n            Revision::Berlin => evmc_revision::EVMC_BERLIN,\n            Revision::London => evmc_revision::EVMC_LONDON,\n            Revision::Shanghai => evmc_revision::EVMC_SHANGHAI,\n        }\n    }\n}\n\n#[evmc_declare::evmc_declare_vm(\"evmodin\", \"evm\", \"0.1.0\")]\npub struct EvmOdin;\n\nimpl EvmcVm for EvmOdin {\n    fn init() -> Self {\n        Self\n    }\n\n    fn execute<'a>(\n        &self,\n        revision: evmc_vm::Revision,\n        code: &'a [u8],\n        message: &'a ExecutionMessage,\n        context: Option<&'a mut ExecutionContext<'a>>,\n    ) -> ExecutionResult {\n        let code = AnalyzedCode::analyze(code);\n\n        let output = if let Some(context) = context {\n            code.execute(\n                context,\n                &mut NoopTracer,\n                None,\n                Message::from_evmc(message),\n                revision.into(),\n            )\n        } else {\n            code.execute(\n                &mut DummyHost,\n                &mut NoopTracer,\n                None,\n                Message::from_evmc(message),\n                revision.into(),\n            )\n        };\n\n        ExecutionResult::new(\n            output.status_code.clone().into(),\n            output.gas_left,\n            (!output.output_data.is_empty()).then(|| &*output.output_data),\n        )\n    }\n}\n"
  },
  {
    "path": "src/host.rs",
    "content": "use crate::common::{Message, Output};\nuse ethereum_types::*;\n\n/// State access status (EIP-2929).\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum AccessStatus {\n    Cold,\n    Warm,\n}\n\nimpl Default for AccessStatus {\n    fn default() -> Self {\n        Self::Cold\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\npub enum StorageStatus {\n    /// The value of a storage item has been left unchanged: 0 -> 0 and X -> X.\n    Unchanged,\n    /// The value of a storage item has been modified: X -> Y.\n    Modified,\n    /// A storage item has been modified after being modified before: X -> Y -> Z.\n    ModifiedAgain,\n    /// A new storage item has been added: 0 -> X.\n    Added,\n    /// A storage item has been deleted: X -> 0.\n    Deleted,\n}\n\n/// The transaction and block data for execution.\n#[derive(Clone, Debug)]\npub struct TxContext {\n    /// The transaction gas price.\n    pub tx_gas_price: U256,\n    /// The transaction origin account.\n    pub tx_origin: Address,\n    /// The miner of the block.\n    pub block_coinbase: Address,\n    /// The block number.\n    pub block_number: u64,\n    /// The block timestamp.\n    pub block_timestamp: u64,\n    /// The block gas limit.\n    pub block_gas_limit: u64,\n    /// The block difficulty.\n    pub block_difficulty: U256,\n    /// The blockchain's ChainID.\n    pub chain_id: U256,\n    /// The block base fee per gas (EIP-1559, EIP-3198).\n    pub block_base_fee: U256,\n}\n\n/// Abstraction that exposes host context to EVM.\npub trait Host {\n    /// Check if an account exists.\n    fn account_exists(&self, address: Address) -> bool;\n    /// Get value of a storage key.\n    ///\n    /// Returns `Ok(U256::zero())` if does not exist.\n    fn get_storage(&self, address: Address, key: U256) -> U256;\n    /// Set value of a storage key.\n    fn set_storage(&mut self, address: Address, key: U256, value: U256) -> StorageStatus;\n    /// Get balance of an account.\n    ///\n    /// Returns `Ok(0)` if account does not exist.\n    fn get_balance(&self, address: Address) -> U256;\n    /// Get code size of an account.\n    ///\n    /// Returns `Ok(0)` if account does not exist.\n    fn get_code_size(&self, address: Address) -> U256;\n    /// Get code hash of an account.\n    ///\n    /// Returns `Ok(0)` if account does not exist.\n    fn get_code_hash(&self, address: Address) -> U256;\n    /// Copy code of an account.\n    ///\n    /// Returns `Ok(0)` if offset is invalid.\n    fn copy_code(&self, address: Address, offset: usize, buffer: &mut [u8]) -> usize;\n    /// Self-destruct account.\n    fn selfdestruct(&mut self, address: Address, beneficiary: Address);\n    /// Call to another account.\n    fn call(&mut self, msg: &Message) -> Output;\n    /// Retrieve transaction context.\n    fn get_tx_context(&self) -> TxContext;\n    /// Get block hash.\n    ///\n    /// Returns `Ok(U256::zero())` if block does not exist.\n    fn get_block_hash(&self, block_number: u64) -> U256;\n    /// Emit a log.\n    fn emit_log(&mut self, address: Address, data: &[u8], topics: &[U256]);\n    /// Mark account as warm, return previous access status.\n    ///\n    /// Returns `Ok(AccessStatus::Cold)` if account does not exist.\n    fn access_account(&mut self, address: Address) -> AccessStatus;\n    /// Mark storage key as warm, return previous access status.\n    ///\n    /// Returns `Ok(AccessStatus::Cold)` if account does not exist.\n    fn access_storage(&mut self, address: Address, key: U256) -> AccessStatus;\n}\n\n/// Host that does not support any ops.\npub struct DummyHost;\n\nimpl Host for DummyHost {\n    fn account_exists(&self, _: Address) -> bool {\n        todo!()\n    }\n\n    fn get_storage(&self, _: Address, _: U256) -> U256 {\n        todo!()\n    }\n\n    fn set_storage(&mut self, _: Address, _: U256, _: U256) -> StorageStatus {\n        todo!()\n    }\n\n    fn get_balance(&self, _: Address) -> U256 {\n        todo!()\n    }\n\n    fn get_code_size(&self, _: Address) -> U256 {\n        todo!()\n    }\n\n    fn get_code_hash(&self, _: Address) -> U256 {\n        todo!()\n    }\n\n    fn copy_code(&self, _: Address, _: usize, _: &mut [u8]) -> usize {\n        todo!()\n    }\n\n    fn selfdestruct(&mut self, _: Address, _: Address) {\n        todo!()\n    }\n\n    fn call(&mut self, _: &Message) -> Output {\n        todo!()\n    }\n\n    fn get_tx_context(&self) -> TxContext {\n        todo!()\n    }\n\n    fn get_block_hash(&self, _: u64) -> U256 {\n        todo!()\n    }\n\n    fn emit_log(&mut self, _: Address, _: &[u8], _: &[U256]) {\n        todo!()\n    }\n\n    fn access_account(&mut self, _: Address) -> AccessStatus {\n        todo!()\n    }\n\n    fn access_storage(&mut self, _: Address, _: U256) -> AccessStatus {\n        todo!()\n    }\n}\n"
  },
  {
    "path": "src/instructions/arithmetic.rs",
    "content": "use crate::{state::*, Revision, StatusCode};\nuse core::convert::TryInto;\nuse ethereum_types::{U256, U512};\nuse i256::I256;\n\npub(crate) fn add(stack: &mut Stack) {\n    let a = stack.pop();\n    let b = stack.pop();\n    stack.push(a.overflowing_add(b).0);\n}\n\npub(crate) fn mul(stack: &mut Stack) {\n    let a = stack.pop();\n    let b = stack.pop();\n    stack.push(a.overflowing_mul(b).0);\n}\n\npub(crate) fn sub(stack: &mut Stack) {\n    let a = stack.pop();\n    let b = stack.pop();\n    stack.push(a.overflowing_sub(b).0);\n}\n\npub(crate) fn div(stack: &mut Stack) {\n    let a = stack.pop();\n    let b = stack.pop();\n    stack.push(if b.is_zero() { U256::zero() } else { a / b });\n}\n\npub(crate) fn sdiv(stack: &mut Stack) {\n    let a = I256::from(stack.pop());\n    let b = I256::from(stack.pop());\n    let v = a / b;\n    stack.push(v.into());\n}\n\npub(crate) fn modulo(stack: &mut Stack) {\n    let a = stack.pop();\n    let b = stack.pop();\n    let v = if b.is_zero() { U256::zero() } else { a % b };\n    stack.push(v);\n}\n\npub(crate) fn smod(stack: &mut Stack) {\n    let a = stack.pop();\n    let b = stack.pop();\n\n    let v = if b.is_zero() {\n        U256::zero()\n    } else {\n        let v = I256::from(a) % I256::from(b);\n        v.into()\n    };\n\n    stack.push(v);\n}\n\npub(crate) fn addmod(stack: &mut Stack) {\n    let a = U512::from(stack.pop());\n    let b = U512::from(stack.pop());\n    let c = U512::from(stack.pop());\n\n    let v = if c.is_zero() {\n        U256::zero()\n    } else {\n        let v = (a + b) % c;\n        v.try_into().unwrap()\n    };\n\n    stack.push(v);\n}\n\npub(crate) fn mulmod(stack: &mut Stack) {\n    let a = U512::from(stack.pop());\n    let b = U512::from(stack.pop());\n    let c = U512::from(stack.pop());\n\n    let v = if c.is_zero() {\n        U256::zero()\n    } else {\n        let v = (a * b) % c;\n        v.try_into().unwrap()\n    };\n\n    stack.push(v);\n}\n\nfn log2floor(value: U256) -> u64 {\n    assert!(value != U256::zero());\n    let mut l: u64 = 256;\n    for i in 0..4 {\n        let i = 3 - i;\n        if value.0[i] == 0u64 {\n            l -= 64;\n        } else {\n            l -= value.0[i].leading_zeros() as u64;\n            if l == 0 {\n                return l;\n            } else {\n                return l - 1;\n            }\n        }\n    }\n    l\n}\n\npub(crate) fn exp(state: &mut ExecutionState) -> Result<(), StatusCode> {\n    let mut base = state.stack.pop();\n    let mut power = state.stack.pop();\n\n    if !power.is_zero() {\n        let additional_gas = if state.evm_revision >= Revision::Spurious {\n            50\n        } else {\n            10\n        } * (log2floor(power) / 8 + 1);\n\n        state.gas_left -= additional_gas as i64;\n\n        if state.gas_left < 0 {\n            return Err(StatusCode::OutOfGas);\n        }\n    }\n\n    let mut v = U256::one();\n\n    while !power.is_zero() {\n        if !(power & U256::one()).is_zero() {\n            v = v.overflowing_mul(base).0;\n        }\n        power >>= 1;\n        base = base.overflowing_mul(base).0;\n    }\n\n    state.stack.push(v);\n\n    Ok(())\n}\n\npub(crate) fn signextend(stack: &mut Stack) {\n    let a = stack.pop();\n    let b = stack.pop();\n\n    let v = if a < U256::from(32) {\n        // `low_u32` works since op1 < 32\n        let bit_index = (8 * a.low_u32() + 7) as usize;\n        let bit = b.bit(bit_index);\n        let mask = (U256::one() << bit_index) - U256::one();\n        if bit {\n            b | !mask\n        } else {\n            b & mask\n        }\n    } else {\n        b\n    };\n\n    stack.push(v);\n}\n"
  },
  {
    "path": "src/instructions/bitwise.rs",
    "content": "use crate::state::Stack;\nuse ethereum_types::U256;\nuse i256::{Sign, I256};\n\npub(crate) fn byte(stack: &mut Stack) {\n    let a = stack.pop();\n    let b = stack.pop();\n\n    let mut ret = U256::zero();\n\n    for i in 0..256 {\n        if i < 8 && a < 32.into() {\n            let o: usize = a.as_usize();\n            let t = 255 - (7 - i + 8 * o);\n            let bit_mask = U256::one() << t;\n            let value = (b & bit_mask) >> t;\n            ret = ret.overflowing_add(value << i).0;\n        }\n    }\n\n    stack.push(ret)\n}\n\npub(crate) fn shl(stack: &mut Stack) {\n    let shift = stack.pop();\n    let value = stack.pop();\n\n    let ret = if value.is_zero() || shift >= U256::from(256) {\n        U256::zero()\n    } else {\n        value << shift.as_usize()\n    };\n\n    stack.push(ret)\n}\n\npub(crate) fn shr(stack: &mut Stack) {\n    let shift = stack.pop();\n    let value = stack.pop();\n\n    let ret = if value.is_zero() || shift >= U256::from(256) {\n        U256::zero()\n    } else {\n        value >> shift.as_usize()\n    };\n\n    stack.push(ret)\n}\n\npub(crate) fn sar(stack: &mut Stack) {\n    let shift = stack.pop();\n    let value = I256::from(stack.pop());\n\n    let ret = if value == I256::zero() || shift >= U256::from(256) {\n        match value.0 {\n            // value is 0 or >=1, pushing 0\n            Sign::Plus | Sign::NoSign => U256::zero(),\n            // value is <0, pushing -1\n            Sign::Minus => I256(Sign::Minus, U256::one()).into(),\n        }\n    } else {\n        let shift = shift.as_usize();\n\n        match value.0 {\n            Sign::Plus | Sign::NoSign => value.1 >> shift,\n            Sign::Minus => {\n                let shifted = ((value.1.overflowing_sub(U256::one()).0) >> shift)\n                    .overflowing_add(U256::one())\n                    .0;\n                I256(Sign::Minus, shifted).into()\n            }\n        }\n    };\n\n    stack.push(ret)\n}\n"
  },
  {
    "path": "src/instructions/boolean.rs",
    "content": "use crate::state::*;\nuse ethereum_types::U256;\nuse i256::I256;\n\npub(crate) fn lt(stack: &mut Stack) {\n    let a = stack.pop();\n    let b = stack.pop();\n\n    stack.push(if a.lt(&b) { U256::one() } else { U256::zero() })\n}\n\npub(crate) fn gt(stack: &mut Stack) {\n    let a = stack.pop();\n    let b = stack.pop();\n\n    stack.push(if a.gt(&b) { U256::one() } else { U256::zero() })\n}\n\npub(crate) fn slt(stack: &mut Stack) {\n    let a: I256 = stack.pop().into();\n    let b: I256 = stack.pop().into();\n\n    stack.push(if a.lt(&b) { U256::one() } else { U256::zero() })\n}\n\npub(crate) fn sgt(stack: &mut Stack) {\n    let a: I256 = stack.pop().into();\n    let b: I256 = stack.pop().into();\n\n    stack.push(if a.gt(&b) { U256::one() } else { U256::zero() })\n}\n\npub(crate) fn eq(stack: &mut Stack) {\n    let a = stack.pop();\n    let b = stack.pop();\n\n    stack.push(if a.eq(&b) { U256::one() } else { U256::zero() })\n}\n\npub(crate) fn iszero(stack: &mut Stack) {\n    let a = stack.pop();\n    stack.push(if a.is_zero() {\n        U256::one()\n    } else {\n        U256::zero()\n    })\n}\n\npub(crate) fn and(stack: &mut Stack) {\n    let a = stack.pop();\n    let b = stack.pop();\n    stack.push(a & b);\n}\n\npub(crate) fn or(stack: &mut Stack) {\n    let a = stack.pop();\n    let b = stack.pop();\n    stack.push(a | b);\n}\n\npub(crate) fn xor(stack: &mut Stack) {\n    let a = stack.pop();\n    let b = stack.pop();\n    stack.push(a ^ b);\n}\n\npub(crate) fn not(stack: &mut Stack) {\n    let a = stack.get_mut(0);\n    *a = !*a;\n}\n"
  },
  {
    "path": "src/instructions/call.rs",
    "content": "#[doc(hidden)]\n#[macro_export]\nmacro_rules! do_call {\n    ($co:expr, $state:expr, $kind:expr, $is_static:expr) => {{\n        use std::cmp::min;\n        use $crate::{\n            common::u256_to_address,\n            continuation::{interrupt_data::*, resume_data::*},\n            host::AccessStatus,\n            instructions::{memory::MemoryRegion, properties::*},\n            CallKind, Message,\n        };\n\n        let gas = $state.stack.pop();\n        let dst = u256_to_address($state.stack.pop());\n        let value = if $is_static || matches!($kind, CallKind::DelegateCall) {\n            U256::zero()\n        } else {\n            $state.stack.pop()\n        };\n        let has_value = !value.is_zero();\n        let input_offset = $state.stack.pop();\n        let input_size = $state.stack.pop();\n        let output_offset = $state.stack.pop();\n        let output_size = $state.stack.pop();\n\n        $state.stack.push(U256::zero()); // Assume failure.\n\n        if $state.evm_revision >= Revision::Berlin\n            && ResumeDataVariant::into_access_account_status(\n                $co.yield_(InterruptDataVariant::AccessAccount(AccessAccount {\n                    address: dst,\n                }))\n                .await,\n            )\n            .unwrap()\n            .status\n                == AccessStatus::Cold\n        {\n            $state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);\n            if $state.gas_left < 0 {\n                return Err(StatusCode::OutOfGas);\n            }\n        }\n\n        let input_region = memory::verify_memory_region($state, input_offset, input_size)\n            .map_err(|_| StatusCode::OutOfGas)?;\n        let output_region = memory::verify_memory_region($state, output_offset, output_size)\n            .map_err(|_| StatusCode::OutOfGas)?;\n\n        let mut msg = Message {\n            kind: $kind,\n            is_static: $is_static || $state.message.is_static,\n            depth: $state.message.depth + 1,\n            recipient: if matches!($kind, CallKind::Call) {\n                dst\n            } else {\n                $state.message.recipient\n            },\n            code_address: dst,\n            sender: if matches!($kind, CallKind::DelegateCall) {\n                $state.message.sender\n            } else {\n                $state.message.recipient\n            },\n            gas: i64::MAX,\n            value: if matches!($kind, CallKind::DelegateCall) {\n                $state.message.value\n            } else {\n                value\n            },\n            input_data: input_region\n                .map(|MemoryRegion { offset, size }| {\n                    $state.memory[offset..offset + size.get()].to_vec().into()\n                })\n                .unwrap_or_default(),\n        };\n\n        let mut cost = if has_value { 9000 } else { 0 };\n\n        if matches!($kind, CallKind::Call) {\n            if has_value && $state.message.is_static {\n                return Err(StatusCode::StaticModeViolation);\n            }\n\n            if (has_value || $state.evm_revision < Revision::Spurious)\n                && !ResumeDataVariant::into_account_exists_status(\n                    $co.yield_(InterruptDataVariant::AccountExists(AccountExists {\n                        address: dst,\n                    }))\n                    .await,\n                )\n                .unwrap()\n                .exists\n            {\n                cost += 25000;\n            }\n        }\n        $state.gas_left -= cost;\n        if $state.gas_left < 0 {\n            return Err(StatusCode::OutOfGas);\n        }\n\n        if gas < msg.gas.into() {\n            msg.gas = gas.as_usize() as i64;\n        }\n\n        if $state.evm_revision >= Revision::Tangerine {\n            // TODO: Always true for STATICCALL.\n            msg.gas = min(msg.gas, $state.gas_left - $state.gas_left / 64);\n        } else if msg.gas > $state.gas_left {\n            return Err(StatusCode::OutOfGas);\n        }\n\n        if has_value {\n            msg.gas += 2300; // Add stipend.\n            $state.gas_left += 2300;\n        }\n\n        $state.return_data.clear();\n\n        if $state.message.depth < 1024\n            && !(has_value\n                && ResumeDataVariant::into_balance(\n                    $co.yield_(InterruptDataVariant::GetBalance(GetBalance {\n                        address: $state.message.recipient,\n                    }))\n                    .await,\n                )\n                .unwrap()\n                .balance\n                    < value)\n        {\n            let msg_gas = msg.gas;\n            let result = ResumeDataVariant::into_call_output(\n                $co.yield_(InterruptDataVariant::Call(Call::Call(msg)))\n                    .await,\n            )\n            .unwrap()\n            .output;\n            $state.return_data = result.output_data.clone();\n            *$state.stack.get_mut(0) = if matches!(result.status_code, StatusCode::Success) {\n                U256::one()\n            } else {\n                U256::zero()\n            };\n\n            if let Some(MemoryRegion { offset, size }) = output_region {\n                let copy_size = min(size.get(), result.output_data.len());\n                if copy_size > 0 {\n                    $state.memory[offset..offset + copy_size]\n                        .copy_from_slice(&result.output_data[..copy_size]);\n                }\n            }\n\n            let gas_used = msg_gas - result.gas_left;\n            $state.gas_left -= gas_used;\n        }\n    }};\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! do_create {\n    ($co:expr, $state:expr, $create2:expr) => {{\n        use ethereum_types::U256;\n        use $crate::{\n            common::*,\n            continuation::{interrupt_data::*, resume_data::*},\n            CreateMessage,\n        };\n\n        if $state.message.is_static {\n            return Err(StatusCode::StaticModeViolation);\n        }\n\n        let endowment = $state.stack.pop();\n        let init_code_offset = $state.stack.pop();\n        let init_code_size = $state.stack.pop();\n\n        let region = memory::verify_memory_region($state, init_code_offset, init_code_size)\n            .map_err(|_| StatusCode::OutOfGas)?;\n\n        let salt = if $create2 {\n            let salt = $state.stack.pop();\n\n            if let Some(region) = &region {\n                let salt_cost = memory::num_words(region.size.get()) * 6;\n                $state.gas_left -= salt_cost;\n                if $state.gas_left < 0 {\n                    return Err(StatusCode::OutOfGas);\n                }\n            }\n\n            Some(salt)\n        } else {\n            None\n        };\n\n        $state.stack.push(U256::zero());\n        $state.return_data.clear();\n\n        if $state.message.depth < 1024\n            && !(!endowment.is_zero()\n                && ResumeDataVariant::into_balance(\n                    $co.yield_(InterruptDataVariant::GetBalance(GetBalance {\n                        address: $state.message.recipient,\n                    }))\n                    .await,\n                )\n                .unwrap()\n                .balance\n                    < endowment)\n        {\n            let msg = CreateMessage {\n                gas: if $state.evm_revision >= Revision::Tangerine {\n                    $state.gas_left - $state.gas_left / 64\n                } else {\n                    $state.gas_left\n                },\n\n                salt,\n                initcode: if !init_code_size.is_zero() {\n                    $state.memory[init_code_offset.as_usize()\n                        ..init_code_offset.as_usize() + init_code_size.as_usize()]\n                        .to_vec()\n                        .into()\n                } else {\n                    Bytes::new()\n                },\n                sender: $state.message.recipient,\n                depth: $state.message.depth + 1,\n                endowment,\n            };\n            let msg_gas = msg.gas;\n            let result = ResumeDataVariant::into_call_output(\n                $co.yield_(InterruptDataVariant::Call(Call::Create(msg)))\n                    .await,\n            )\n            .unwrap()\n            .output;\n            $state.gas_left -= msg_gas - result.gas_left;\n\n            $state.return_data = result.output_data;\n            if result.status_code == StatusCode::Success {\n                *$state.stack.get_mut(0) =\n                    address_to_u256(result.create_address.expect(\"expected create address\"));\n            }\n        }\n    }};\n}\n"
  },
  {
    "path": "src/instructions/control.rs",
    "content": "use crate::state::ExecutionState;\nuse crate::{interpreter::JumpdestMap, StatusCode};\nuse ethereum_types::U256;\n\npub(crate) fn ret(state: &mut ExecutionState) -> Result<(), StatusCode> {\n    let offset = *state.stack.get(0);\n    let size = *state.stack.get(1);\n\n    if let Some(region) = super::memory::verify_memory_region(state, offset, size)\n        .map_err(|_| StatusCode::OutOfGas)?\n    {\n        state.output_data = state.memory[region.offset..region.offset + region.size.get()]\n            .to_vec()\n            .into();\n    }\n\n    Ok(())\n}\n\npub(crate) fn op_jump(\n    state: &mut ExecutionState,\n    jumpdest_map: &JumpdestMap,\n) -> Result<usize, StatusCode> {\n    let dst = state.stack.pop();\n    if !jumpdest_map.contains(dst) {\n        return Err(StatusCode::BadJumpDestination);\n    }\n\n    Ok(dst.as_usize())\n}\n\npub(crate) fn calldataload(state: &mut ExecutionState) {\n    let index = state.stack.pop();\n\n    let input_len = state.message.input_data.len();\n\n    state.stack.push({\n        if index > U256::from(input_len) {\n            U256::zero()\n        } else {\n            let index_usize = index.as_usize();\n            let end = core::cmp::min(index_usize + 32, input_len);\n\n            let mut data = [0; 32];\n            data[..end - index_usize].copy_from_slice(&state.message.input_data[index_usize..end]);\n\n            data.into()\n        }\n    });\n}\n\npub(crate) fn calldatasize(state: &mut ExecutionState) {\n    state.stack.push(state.message.input_data.len().into());\n}\n"
  },
  {
    "path": "src/instructions/external.rs",
    "content": "use crate::{common::address_to_u256, host::*, state::ExecutionState};\nuse ethereum_types::U256;\n\npub(crate) fn address(state: &mut ExecutionState) {\n    state.stack.push(address_to_u256(state.message.recipient));\n}\n\npub(crate) fn caller(state: &mut ExecutionState) {\n    state.stack.push(address_to_u256(state.message.sender));\n}\n\npub(crate) fn callvalue(state: &mut ExecutionState) {\n    state.stack.push(state.message.value);\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! balance {\n    ($co:expr, $state:expr) => {\n        use crate::{\n            common::*,\n            continuation::{interrupt_data::*, resume_data::*},\n            host::*,\n            instructions::properties::*,\n        };\n\n        let address = u256_to_address($state.stack.pop());\n\n        if $state.evm_revision >= Revision::Berlin {\n            let access_status = ResumeDataVariant::into_access_account_status(\n                $co.yield_(InterruptDataVariant::AccessAccount(AccessAccount {\n                    address,\n                }))\n                .await,\n            )\n            .unwrap()\n            .status;\n            if access_status == AccessStatus::Cold {\n                $state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);\n                if $state.gas_left < 0 {\n                    return Err(StatusCode::OutOfGas);\n                }\n            }\n        }\n\n        let balance = ResumeDataVariant::into_balance(\n            $co.yield_(InterruptDataVariant::GetBalance(GetBalance { address }))\n                .await,\n        )\n        .unwrap()\n        .balance;\n\n        $state.stack.push(balance);\n    };\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! extcodesize {\n    ($co:expr, $state:expr) => {\n        use crate::{\n            common::*,\n            continuation::{interrupt_data::*, resume_data::*},\n            host::*,\n            instructions::properties::*,\n        };\n\n        let address = u256_to_address($state.stack.pop());\n\n        if $state.evm_revision >= Revision::Berlin {\n            let access_account = ResumeDataVariant::into_access_account_status(\n                $co.yield_(InterruptDataVariant::AccessAccount(AccessAccount {\n                    address,\n                }))\n                .await,\n            )\n            .unwrap()\n            .status;\n            if access_account == AccessStatus::Cold {\n                $state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);\n                if $state.gas_left < 0 {\n                    return Err(StatusCode::OutOfGas);\n                }\n            }\n        }\n\n        let code_size = ResumeDataVariant::into_code_size(\n            $co.yield_(InterruptDataVariant::GetCodeSize(GetCodeSize { address }))\n                .await,\n        )\n        .unwrap()\n        .code_size;\n        $state.stack.push(code_size);\n    };\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! push_txcontext {\n    ($co:expr, $state:expr, $accessor:expr) => {\n        use $crate::continuation::{interrupt_data::*, resume_data::*};\n\n        let tx_context = ResumeDataVariant::into_tx_context_data(\n            $co.yield_(InterruptDataVariant::GetTxContext).await,\n        )\n        .unwrap()\n        .context;\n\n        $state.stack.push($accessor(tx_context));\n    };\n}\n\npub(crate) fn origin_accessor(tx_context: TxContext) -> U256 {\n    address_to_u256(tx_context.tx_origin)\n}\n\npub(crate) fn coinbase_accessor(tx_context: TxContext) -> U256 {\n    address_to_u256(tx_context.block_coinbase)\n}\n\npub(crate) fn gasprice_accessor(tx_context: TxContext) -> U256 {\n    tx_context.tx_gas_price\n}\n\npub(crate) fn timestamp_accessor(tx_context: TxContext) -> U256 {\n    tx_context.block_timestamp.into()\n}\n\npub(crate) fn number_accessor(tx_context: TxContext) -> U256 {\n    tx_context.block_number.into()\n}\n\npub(crate) fn gaslimit_accessor(tx_context: TxContext) -> U256 {\n    tx_context.block_gas_limit.into()\n}\n\npub(crate) fn difficulty_accessor(tx_context: TxContext) -> U256 {\n    tx_context.block_difficulty\n}\n\npub(crate) fn chainid_accessor(tx_context: TxContext) -> U256 {\n    tx_context.chain_id\n}\n\npub(crate) fn basefee_accessor(tx_context: TxContext) -> U256 {\n    tx_context.block_base_fee\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! selfbalance {\n    ($co:expr, $state:expr) => {{\n        use $crate::continuation::{interrupt_data::*, resume_data::*};\n\n        let balance = ResumeDataVariant::into_balance(\n            $co.yield_(InterruptDataVariant::GetBalance(GetBalance {\n                address: $state.message.recipient,\n            }))\n            .await,\n        )\n        .unwrap()\n        .balance;\n\n        $state.stack.push(balance);\n    }};\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! blockhash {\n    ($co:expr, $state:expr) => {\n        use $crate::continuation::{interrupt_data::*, resume_data::*};\n\n        let number = $state.stack.pop();\n\n        let upper_bound = ResumeDataVariant::into_tx_context_data(\n            $co.yield_(InterruptDataVariant::GetTxContext).await,\n        )\n        .unwrap()\n        .context\n        .block_number;\n        let lower_bound = upper_bound.saturating_sub(256);\n\n        let mut header = U256::zero();\n        if number <= u64::MAX.into() {\n            let n = number.as_u64();\n            if (lower_bound..upper_bound).contains(&n) {\n                header = ResumeDataVariant::into_block_hash(\n                    $co.yield_(InterruptDataVariant::GetBlockHash(GetBlockHash {\n                        block_number: n,\n                    }))\n                    .await,\n                )\n                .unwrap()\n                .hash;\n            }\n        }\n\n        $state.stack.push(header);\n    };\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! do_log {\n    ($co:expr, $state:expr, $num_topics:expr) => {{\n        use arrayvec::ArrayVec;\n        use $crate::continuation::{interrupt_data::*, resume_data::*};\n\n        if $state.message.is_static {\n            return Err(StatusCode::StaticModeViolation);\n        }\n\n        let offset = $state.stack.pop();\n        let size = $state.stack.pop();\n\n        let region =\n            memory::verify_memory_region($state, offset, size).map_err(|_| StatusCode::OutOfGas)?;\n\n        if let Some(region) = &region {\n            let cost = region.size.get() as i64 * 8;\n            $state.gas_left -= cost;\n            if cost < 0 {\n                return Err(StatusCode::OutOfGas);\n            }\n        }\n\n        let mut topics = ArrayVec::new();\n        for _ in 0..$num_topics {\n            topics.push($state.stack.pop());\n        }\n\n        let data = if let Some(region) = region {\n            &$state.memory[region.offset..region.offset + region.size.get()]\n        } else {\n            &[]\n        };\n        let r = $co\n            .yield_(InterruptDataVariant::EmitLog(EmitLog {\n                address: $state.message.recipient,\n                data: data.to_vec().into(),\n                topics,\n            }))\n            .await;\n\n        assert!(matches!(r, ResumeDataVariant::Empty));\n    }};\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! sload {\n    ($co:expr, $state:expr) => {{\n        use $crate::{\n            continuation::{interrupt_data::*, resume_data::*},\n            host::*,\n            instructions::properties::{COLD_SLOAD_COST, WARM_STORAGE_READ_COST},\n        };\n\n        let key = $state.stack.pop();\n\n        if $state.evm_revision >= Revision::Berlin {\n            let access_status = ResumeDataVariant::into_access_storage_status(\n                $co.yield_(InterruptDataVariant::AccessStorage(AccessStorage {\n                    address: $state.message.recipient,\n                    key,\n                }))\n                .await,\n            )\n            .unwrap()\n            .status;\n            if access_status == AccessStatus::Cold {\n                // The warm storage access cost is already applied (from the cost table).\n                // Here we need to apply additional cold storage access cost.\n                const ADDITIONAL_COLD_SLOAD_COST: u16 = COLD_SLOAD_COST - WARM_STORAGE_READ_COST;\n                $state.gas_left -= i64::from(ADDITIONAL_COLD_SLOAD_COST);\n                if $state.gas_left < 0 {\n                    return Err(StatusCode::OutOfGas);\n                }\n            }\n        }\n\n        let storage = ResumeDataVariant::into_storage_value(\n            $co.yield_(InterruptDataVariant::GetStorage(GetStorage {\n                address: $state.message.recipient,\n                key,\n            }))\n            .await,\n        )\n        .unwrap()\n        .value;\n\n        $state.stack.push(storage);\n    }};\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! sstore {\n    ($co:expr, $state:expr) => {{\n        use $crate::{\n            continuation::{interrupt_data::*, resume_data::*},\n            host::*,\n            instructions::properties::{COLD_SLOAD_COST, WARM_STORAGE_READ_COST},\n        };\n\n        if $state.message.is_static {\n            return Err(StatusCode::StaticModeViolation);\n        }\n\n        if $state.evm_revision >= Revision::Istanbul && $state.gas_left <= 2300 {\n            return Err(StatusCode::OutOfGas);\n        }\n\n        let key = $state.stack.pop();\n        let value = $state.stack.pop();\n\n        let mut cost = 0;\n        if $state.evm_revision >= Revision::Berlin {\n            let access_status = ResumeDataVariant::into_access_storage_status(\n                $co.yield_(InterruptDataVariant::AccessStorage(AccessStorage {\n                    address: $state.message.recipient,\n                    key,\n                }))\n                .await,\n            )\n            .unwrap()\n            .status;\n\n            if access_status == AccessStatus::Cold {\n                cost = COLD_SLOAD_COST;\n            }\n        }\n\n        let status = ResumeDataVariant::into_storage_status_info(\n            $co.yield_(InterruptDataVariant::SetStorage(SetStorage {\n                address: $state.message.recipient,\n                key,\n                value,\n            }))\n            .await,\n        )\n        .unwrap()\n        .status;\n\n        cost = match status {\n            StorageStatus::Unchanged | StorageStatus::ModifiedAgain => {\n                if $state.evm_revision >= Revision::Berlin {\n                    cost + WARM_STORAGE_READ_COST\n                } else if $state.evm_revision == Revision::Istanbul {\n                    800\n                } else if $state.evm_revision == Revision::Constantinople {\n                    200\n                } else {\n                    5000\n                }\n            }\n            StorageStatus::Modified | StorageStatus::Deleted => {\n                if $state.evm_revision >= Revision::Berlin {\n                    cost + 5000 - COLD_SLOAD_COST\n                } else {\n                    5000\n                }\n            }\n            StorageStatus::Added => cost + 20000,\n        };\n        $state.gas_left -= i64::from(cost);\n        if $state.gas_left < 0 {\n            return Err(StatusCode::OutOfGas);\n        }\n    }};\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! selfdestruct {\n    ($co:expr, $state:expr) => {{\n        use crate::{\n            common::*,\n            continuation::{interrupt_data::*, resume_data::*},\n            host::*,\n            instructions::properties::*,\n        };\n\n        if $state.message.is_static {\n            return Err(StatusCode::StaticModeViolation);\n        }\n\n        let beneficiary = u256_to_address($state.stack.pop());\n\n        if $state.evm_revision >= Revision::Berlin {\n            let access_status = ResumeDataVariant::into_access_account_status(\n                $co.yield_(InterruptDataVariant::AccessAccount(AccessAccount {\n                    address: beneficiary,\n                }))\n                .await,\n            )\n            .unwrap()\n            .status;\n            if access_status == AccessStatus::Cold {\n                $state.gas_left -= i64::from(COLD_ACCOUNT_ACCESS_COST);\n                if $state.gas_left < 0 {\n                    return Err(StatusCode::OutOfGas);\n                }\n            }\n        }\n\n        if $state.evm_revision >= Revision::Tangerine\n            && ($state.evm_revision == Revision::Tangerine\n                || !{\n                    ResumeDataVariant::into_balance(\n                        $co.yield_(InterruptDataVariant::GetBalance(GetBalance {\n                            address: $state.message.recipient,\n                        }))\n                        .await,\n                    )\n                    .unwrap()\n                    .balance\n                    .is_zero()\n                })\n        {\n            // After TANGERINE_WHISTLE apply additional cost of\n            // sending value to a non-existing account.\n            if !ResumeDataVariant::into_account_exists_status(\n                $co.yield_(InterruptDataVariant::AccountExists(AccountExists {\n                    address: beneficiary,\n                }))\n                .await,\n            )\n            .unwrap()\n            .exists\n            {\n                $state.gas_left -= 25000;\n                if $state.gas_left < 0 {\n                    return Err(StatusCode::OutOfGas);\n                }\n            }\n        }\n\n        assert!(matches!(\n            $co.yield_(InterruptDataVariant::Selfdestruct(Selfdestruct {\n                address: $state.message.recipient,\n                beneficiary,\n            }))\n            .await,\n            ResumeDataVariant::Empty\n        ));\n    }};\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::common::u256_to_address;\n    use ethereum_types::Address;\n    use hex_literal::hex;\n\n    #[test]\n    fn u256_to_address_conversion() {\n        assert_eq!(\n            u256_to_address(0x42.into()),\n            Address::from(hex!(\"0000000000000000000000000000000000000042\"))\n        );\n    }\n}\n"
  },
  {
    "path": "src/instructions/instruction_table.rs",
    "content": "use crate::{instructions::properties, Revision};\nuse once_cell::sync::Lazy;\n\n#[derive(Clone, Copy, Debug)]\npub struct InstructionTableEntry {\n    pub gas_cost: u16,\n    pub stack_height_required: u8,\n    pub can_overflow_stack: bool,\n}\n\npub type InstructionTable = [Option<InstructionTableEntry>; 256];\npub type InstructionTables = [InstructionTable; Revision::len()];\n\npub static INSTRUCTION_TABLES: Lazy<InstructionTables> = Lazy::new(|| {\n    let mut table = [[None; 256]; Revision::len()];\n\n    for revision in Revision::iter() {\n        for (opcode, &cost) in properties::gas_costs(revision).iter().enumerate() {\n            if let Some(cost) = cost {\n                let stack_height_required = properties::PROPERTIES[opcode]\n                    .unwrap()\n                    .stack_height_required;\n\n                // Because any instruction can increase stack height at most of 1,\n                // stack overflow can only happen if stack height is already at the limit.\n                assert!(properties::PROPERTIES[opcode].unwrap().stack_height_change <= 1);\n\n                table[revision as usize][opcode] = Some(InstructionTableEntry {\n                    gas_cost: cost,\n                    stack_height_required,\n                    can_overflow_stack: properties::PROPERTIES[opcode].unwrap().stack_height_change\n                        > 0,\n                });\n            }\n        }\n    }\n    table\n});\n\npub fn get_baseline_instruction_table(revision: Revision) -> &'static InstructionTable {\n    &INSTRUCTION_TABLES[revision as usize]\n}\n"
  },
  {
    "path": "src/instructions/memory.rs",
    "content": "use crate::{common::*, state::*};\nuse ethereum_types::U256;\nuse sha3::{Digest, Keccak256};\nuse std::{cmp::min, num::NonZeroUsize};\n\npub(crate) const MAX_BUFFER_SIZE: u32 = u32::MAX;\n\n/// The size of the EVM 256-bit word.\nconst WORD_SIZE: i64 = 32;\n\n/// Returns number of words what would fit to provided number of bytes,\n/// i.e. it rounds up the number bytes to number of words.\npub(crate) fn num_words(size_in_bytes: usize) -> i64 {\n    ((size_in_bytes as i64) + (WORD_SIZE - 1)) / WORD_SIZE\n}\n\npub(crate) fn mload(state: &mut ExecutionState) -> Result<(), StatusCode> {\n    let index = state.stack.pop();\n\n    let region = verify_memory_region_u64(state, index, NonZeroUsize::new(32).unwrap())\n        .map_err(|_| StatusCode::OutOfGas)?;\n\n    let value =\n        U256::from_big_endian(&state.memory[region.offset..region.offset + region.size.get()]);\n\n    state.stack.push(value);\n\n    Ok(())\n}\n\npub(crate) fn mstore(state: &mut ExecutionState) -> Result<(), StatusCode> {\n    let index = state.stack.pop();\n    let value = state.stack.pop();\n\n    let region = verify_memory_region_u64(state, index, NonZeroUsize::new(32).unwrap())\n        .map_err(|_| StatusCode::OutOfGas)?;\n\n    let mut b = [0; 32];\n    value.to_big_endian(&mut b);\n    state.memory[region.offset..region.offset + 32].copy_from_slice(&b);\n\n    Ok(())\n}\n\npub(crate) fn mstore8(state: &mut ExecutionState) -> Result<(), StatusCode> {\n    let index = state.stack.pop();\n    let value = state.stack.pop();\n\n    let region = verify_memory_region_u64(state, index, NonZeroUsize::new(1).unwrap())\n        .map_err(|_| StatusCode::OutOfGas)?;\n\n    let value = (value.low_u32() & 0xff) as u8;\n\n    state.memory[region.offset] = value;\n\n    Ok(())\n}\n\npub(crate) fn msize(state: &mut ExecutionState) {\n    state.stack.push(state.memory.len().into());\n}\n\npub(crate) fn verify_memory_region_u64(\n    state: &mut ExecutionState,\n    offset: U256,\n    size: NonZeroUsize,\n) -> Result<MemoryRegion, ()> {\n    if offset > U256::from(MAX_BUFFER_SIZE) {\n        return Err(());\n    }\n\n    let new_size = offset.as_usize() + size.get();\n    let current_size = state.memory.len();\n    if new_size > current_size {\n        let new_words = num_words(new_size);\n        let current_words = (current_size / 32) as i64;\n        let new_cost = 3 * new_words + new_words * new_words / 512;\n        let current_cost = 3 * current_words + current_words * current_words / 512;\n        let cost = new_cost - current_cost;\n\n        state.gas_left -= cost;\n\n        if state.gas_left < 0 {\n            return Err(());\n        }\n\n        state\n            .memory\n            .resize((new_words * WORD_SIZE) as usize, Default::default());\n    }\n\n    Ok(MemoryRegion {\n        offset: offset.as_usize(),\n        size,\n    })\n}\n\npub(crate) struct MemoryRegion {\n    pub offset: usize,\n    pub size: NonZeroUsize,\n}\n\npub(crate) fn verify_memory_region(\n    state: &mut ExecutionState,\n    offset: U256,\n    size: U256,\n) -> Result<Option<MemoryRegion>, ()> {\n    if size.is_zero() {\n        return Ok(None);\n    }\n\n    if size > U256::from(MAX_BUFFER_SIZE) {\n        return Err(());\n    }\n\n    verify_memory_region_u64(state, offset, NonZeroUsize::new(size.as_usize()).unwrap()).map(Some)\n}\n\npub(crate) fn calldatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> {\n    let mem_index = state.stack.pop();\n    let input_index = state.stack.pop();\n    let size = state.stack.pop();\n\n    let region = verify_memory_region(state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?;\n\n    if let Some(region) = &region {\n        let copy_cost = num_words(region.size.get()) * 3;\n        state.gas_left -= copy_cost;\n        if state.gas_left < 0 {\n            return Err(StatusCode::OutOfGas);\n        }\n\n        let input_len = state.message.input_data.len().into();\n\n        let src = core::cmp::min(input_len, input_index).as_usize();\n        let copy_size = core::cmp::min(size, input_len - src).as_usize();\n\n        if copy_size > 0 {\n            state.memory[region.offset..region.offset + copy_size]\n                .copy_from_slice(&state.message.input_data[src..src + copy_size]);\n        }\n\n        if region.size.get() - copy_size > 0 {\n            state.memory[region.offset + copy_size..region.offset + region.size.get()].fill(0);\n        }\n    }\n\n    Ok(())\n}\n\npub(crate) fn keccak256(state: &mut ExecutionState) -> Result<(), StatusCode> {\n    let index = state.stack.pop();\n    let size = state.stack.pop();\n\n    let region = verify_memory_region(state, index, size).map_err(|_| StatusCode::OutOfGas)?;\n\n    state.stack.push(U256::from_big_endian(&*Keccak256::digest(\n        if let Some(region) = region {\n            let w = num_words(region.size.get());\n            let cost = w * 6;\n            state.gas_left -= cost;\n            if state.gas_left < 0 {\n                return Err(StatusCode::OutOfGas);\n            }\n\n            &state.memory[region.offset..region.offset + region.size.get()]\n        } else {\n            &[]\n        },\n    )));\n\n    Ok(())\n}\n\npub(crate) fn codesize(stack: &mut Stack, code: &[u8]) {\n    stack.push(code.len().into())\n}\n\npub(crate) fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCode> {\n    // TODO: Similar to calldatacopy().\n\n    let mem_index = state.stack.pop();\n    let input_index = state.stack.pop();\n    let size = state.stack.pop();\n\n    let region = verify_memory_region(state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?;\n\n    if let Some(region) = region {\n        let src = min(U256::from(code.len()), input_index).as_usize();\n        let copy_size = min(region.size.get(), code.len() - src);\n\n        let copy_cost = num_words(region.size.get()) * 3;\n        state.gas_left -= copy_cost;\n        if state.gas_left < 0 {\n            return Err(StatusCode::OutOfGas);\n        }\n\n        // TODO: Add unit tests for each combination of conditions.\n        if copy_size > 0 {\n            state.memory[region.offset..region.offset + copy_size]\n                .copy_from_slice(&code[src..src + copy_size]);\n        }\n\n        if region.size.get() - copy_size > 0 {\n            state.memory[region.offset + copy_size..region.offset + region.size.get()].fill(0);\n        }\n    }\n\n    Ok(())\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! extcodecopy {\n    ($co:expr, $state:expr) => {\n        use crate::{\n            common::*,\n            continuation::{interrupt_data::*, resume_data::*},\n            host::*,\n            instructions::{memory::*, properties::*},\n        };\n        use core::cmp::min;\n\n        let addr = u256_to_address($state.stack.pop());\n        let mem_index = $state.stack.pop();\n        let input_index = $state.stack.pop();\n        let size = $state.stack.pop();\n\n        let region =\n            verify_memory_region($state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?;\n\n        if let Some(region) = &region {\n            let copy_cost = num_words(region.size.get()) * 3;\n            $state.gas_left -= copy_cost;\n            if $state.gas_left < 0 {\n                return Err(StatusCode::OutOfGas);\n            }\n        }\n\n        if $state.evm_revision >= Revision::Berlin\n            && ResumeDataVariant::into_access_account_status(\n                $co.yield_(InterruptDataVariant::AccessAccount(AccessAccount {\n                    address: addr,\n                }))\n                .await,\n            )\n            .unwrap()\n            .status\n                == AccessStatus::Cold\n        {\n            $state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);\n            if $state.gas_left < 0 {\n                return Err(StatusCode::OutOfGas);\n            }\n        }\n\n        if let Some(region) = region {\n            let src = min(U256::from(MAX_BUFFER_SIZE), input_index).as_usize();\n\n            let r = &mut $state.memory[region.offset..region.offset + region.size.get()];\n            let code = ResumeDataVariant::into_code(\n                $co.yield_(InterruptDataVariant::CopyCode(CopyCode {\n                    address: addr,\n                    offset: src,\n                    max_size: r.len(),\n                }))\n                .await,\n            )\n            .unwrap()\n            .code;\n\n            r[..code.len()].copy_from_slice(&code);\n            if region.size.get() - code.len() > 0 {\n                $state.memory[region.offset + code.len()..region.offset + region.size.get()]\n                    .fill(0);\n            }\n        }\n    };\n}\n\npub(crate) fn returndatasize(state: &mut ExecutionState) {\n    state.stack.push(state.return_data.len().into());\n}\n\npub(crate) fn returndatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> {\n    let mem_index = state.stack.pop();\n    let input_index = state.stack.pop();\n    let size = state.stack.pop();\n\n    let region = verify_memory_region(state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?;\n\n    if input_index > U256::from(state.return_data.len()) {\n        return Err(StatusCode::InvalidMemoryAccess);\n    }\n    let src = input_index.as_usize();\n\n    if src + region.as_ref().map(|r| r.size.get()).unwrap_or(0) > state.return_data.len() {\n        return Err(StatusCode::InvalidMemoryAccess);\n    }\n\n    if let Some(region) = region {\n        let copy_cost = num_words(region.size.get()) * 3;\n        state.gas_left -= copy_cost;\n        if state.gas_left < 0 {\n            return Err(StatusCode::OutOfGas);\n        }\n\n        state.memory[region.offset..region.offset + region.size.get()]\n            .copy_from_slice(&state.return_data[src..src + region.size.get()]);\n    }\n\n    Ok(())\n}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! extcodehash {\n    ($co:expr, $state:expr) => {\n        use crate::{\n            common::*,\n            continuation::{interrupt_data::*, resume_data::*},\n            host::*,\n            instructions::properties::*,\n        };\n\n        let addr = u256_to_address($state.stack.pop());\n\n        if $state.evm_revision >= Revision::Berlin\n            && ResumeDataVariant::into_access_account_status(\n                $co.yield_(InterruptDataVariant::AccessAccount(AccessAccount {\n                    address: addr,\n                }))\n                .await,\n            )\n            .unwrap()\n            .status\n                == AccessStatus::Cold\n        {\n            $state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);\n            if $state.gas_left < 0 {\n                return Err(StatusCode::OutOfGas);\n            }\n        }\n\n        $state.stack.push(\n            ResumeDataVariant::into_code_hash(\n                $co.yield_(InterruptDataVariant::GetCodeHash(GetCodeHash {\n                    address: addr,\n                }))\n                .await,\n            )\n            .unwrap()\n            .hash,\n        );\n    };\n}\n"
  },
  {
    "path": "src/instructions/mod.rs",
    "content": "pub(crate) mod arithmetic;\npub(crate) mod bitwise;\npub(crate) mod boolean;\npub(crate) mod call;\npub(crate) mod control;\npub(crate) mod external;\npub(crate) mod instruction_table;\npub(crate) mod memory;\npub(crate) mod properties;\npub(crate) mod stack_manip;\n\npub use properties::PROPERTIES;\n"
  },
  {
    "path": "src/instructions/properties.rs",
    "content": "use once_cell::sync::Lazy;\n\nuse crate::{common::Revision, opcode::*};\n\npub(crate) const COLD_SLOAD_COST: u16 = 2100;\npub(crate) const COLD_ACCOUNT_ACCESS_COST: u16 = 2600;\npub(crate) const WARM_STORAGE_READ_COST: u16 = 100;\n\n/// Additional cold account access cost.\n///\n/// The warm access cost is unconditionally applied for every account access instruction.\n/// If the access turns out to be cold, this cost must be applied additionally.\npub(crate) const ADDITIONAL_COLD_ACCOUNT_ACCESS_COST: u16 =\n    COLD_ACCOUNT_ACCESS_COST - WARM_STORAGE_READ_COST;\n\n/// EVM instruction properties\n#[derive(Clone, Copy, Debug)]\npub struct Properties {\n    /// The instruction name\n    pub name: &'static str,\n    /// The number of stack items the instruction accesses during execution.\n    pub stack_height_required: u8,\n    /// The stack height change caused by the instruction execution. Can be negative.\n    pub stack_height_change: i8,\n}\n\nimpl Properties {\n    fn new(name: &'static str, stack_height_required: u8, stack_height_change: i8) -> Self {\n        Self {\n            name,\n            stack_height_required,\n            stack_height_change,\n        }\n    }\n}\n\npub static PROPERTIES: Lazy<[Option<Properties>; 256]> = Lazy::new(|| {\n    let mut table = [None; 256];\n\n    table[OpCode::STOP.to_usize()] = Some(Properties::new(\"STOP\", 0, 0));\n    table[OpCode::ADD.to_usize()] = Some(Properties::new(\"ADD\", 2, -1));\n    table[OpCode::MUL.to_usize()] = Some(Properties::new(\"MUL\", 2, -1));\n    table[OpCode::SUB.to_usize()] = Some(Properties::new(\"SUB\", 2, -1));\n    table[OpCode::DIV.to_usize()] = Some(Properties::new(\"DIV\", 2, -1));\n    table[OpCode::SDIV.to_usize()] = Some(Properties::new(\"SDIV\", 2, -1));\n    table[OpCode::MOD.to_usize()] = Some(Properties::new(\"MOD\", 2, -1));\n    table[OpCode::SMOD.to_usize()] = Some(Properties::new(\"SMOD\", 2, -1));\n    table[OpCode::ADDMOD.to_usize()] = Some(Properties::new(\"ADDMOD\", 3, -2));\n    table[OpCode::MULMOD.to_usize()] = Some(Properties::new(\"MULMOD\", 3, -2));\n    table[OpCode::EXP.to_usize()] = Some(Properties::new(\"EXP\", 2, -1));\n    table[OpCode::SIGNEXTEND.to_usize()] = Some(Properties::new(\"SIGNEXTEND\", 2, -1));\n\n    table[OpCode::LT.to_usize()] = Some(Properties::new(\"LT\", 2, -1));\n    table[OpCode::GT.to_usize()] = Some(Properties::new(\"GT\", 2, -1));\n    table[OpCode::SLT.to_usize()] = Some(Properties::new(\"SLT\", 2, -1));\n    table[OpCode::SGT.to_usize()] = Some(Properties::new(\"SGT\", 2, -1));\n    table[OpCode::EQ.to_usize()] = Some(Properties::new(\"EQ\", 2, -1));\n    table[OpCode::ISZERO.to_usize()] = Some(Properties::new(\"ISZERO\", 1, 0));\n    table[OpCode::AND.to_usize()] = Some(Properties::new(\"AND\", 2, -1));\n    table[OpCode::OR.to_usize()] = Some(Properties::new(\"OR\", 2, -1));\n    table[OpCode::XOR.to_usize()] = Some(Properties::new(\"XOR\", 2, -1));\n    table[OpCode::NOT.to_usize()] = Some(Properties::new(\"NOT\", 1, 0));\n    table[OpCode::BYTE.to_usize()] = Some(Properties::new(\"BYTE\", 2, -1));\n    table[OpCode::SHL.to_usize()] = Some(Properties::new(\"SHL\", 2, -1));\n    table[OpCode::SHR.to_usize()] = Some(Properties::new(\"SHR\", 2, -1));\n    table[OpCode::SAR.to_usize()] = Some(Properties::new(\"SAR\", 2, -1));\n\n    table[OpCode::KECCAK256.to_usize()] = Some(Properties::new(\"KECCAK256\", 2, -1));\n\n    table[OpCode::ADDRESS.to_usize()] = Some(Properties::new(\"ADDRESS\", 0, 1));\n    table[OpCode::BALANCE.to_usize()] = Some(Properties::new(\"BALANCE\", 1, 0));\n    table[OpCode::ORIGIN.to_usize()] = Some(Properties::new(\"ORIGIN\", 0, 1));\n    table[OpCode::CALLER.to_usize()] = Some(Properties::new(\"CALLER\", 0, 1));\n    table[OpCode::CALLVALUE.to_usize()] = Some(Properties::new(\"CALLVALUE\", 0, 1));\n    table[OpCode::CALLDATALOAD.to_usize()] = Some(Properties::new(\"CALLDATALOAD\", 1, 0));\n    table[OpCode::CALLDATASIZE.to_usize()] = Some(Properties::new(\"CALLDATASIZE\", 0, 1));\n    table[OpCode::CALLDATACOPY.to_usize()] = Some(Properties::new(\"CALLDATACOPY\", 3, -3));\n    table[OpCode::CODESIZE.to_usize()] = Some(Properties::new(\"CODESIZE\", 0, 1));\n    table[OpCode::CODECOPY.to_usize()] = Some(Properties::new(\"CODECOPY\", 3, -3));\n    table[OpCode::GASPRICE.to_usize()] = Some(Properties::new(\"GASPRICE\", 0, 1));\n    table[OpCode::EXTCODESIZE.to_usize()] = Some(Properties::new(\"EXTCODESIZE\", 1, 0));\n    table[OpCode::EXTCODECOPY.to_usize()] = Some(Properties::new(\"EXTCODECOPY\", 4, -4));\n    table[OpCode::RETURNDATASIZE.to_usize()] = Some(Properties::new(\"RETURNDATASIZE\", 0, 1));\n    table[OpCode::RETURNDATACOPY.to_usize()] = Some(Properties::new(\"RETURNDATACOPY\", 3, -3));\n    table[OpCode::EXTCODEHASH.to_usize()] = Some(Properties::new(\"EXTCODEHASH\", 1, 0));\n\n    table[OpCode::BLOCKHASH.to_usize()] = Some(Properties::new(\"BLOCKHASH\", 1, 0));\n    table[OpCode::COINBASE.to_usize()] = Some(Properties::new(\"COINBASE\", 0, 1));\n    table[OpCode::TIMESTAMP.to_usize()] = Some(Properties::new(\"TIMESTAMP\", 0, 1));\n    table[OpCode::NUMBER.to_usize()] = Some(Properties::new(\"NUMBER\", 0, 1));\n    table[OpCode::DIFFICULTY.to_usize()] = Some(Properties::new(\"DIFFICULTY\", 0, 1));\n    table[OpCode::GASLIMIT.to_usize()] = Some(Properties::new(\"GASLIMIT\", 0, 1));\n    table[OpCode::CHAINID.to_usize()] = Some(Properties::new(\"CHAINID\", 0, 1));\n    table[OpCode::SELFBALANCE.to_usize()] = Some(Properties::new(\"SELFBALANCE\", 0, 1));\n    table[OpCode::BASEFEE.to_usize()] = Some(Properties::new(\"BASEFEE\", 0, 1));\n\n    table[OpCode::POP.to_usize()] = Some(Properties::new(\"POP\", 1, -1));\n    table[OpCode::MLOAD.to_usize()] = Some(Properties::new(\"MLOAD\", 1, 0));\n    table[OpCode::MSTORE.to_usize()] = Some(Properties::new(\"MSTORE\", 2, -2));\n    table[OpCode::MSTORE8.to_usize()] = Some(Properties::new(\"MSTORE8\", 2, -2));\n    table[OpCode::SLOAD.to_usize()] = Some(Properties::new(\"SLOAD\", 1, 0));\n    table[OpCode::SSTORE.to_usize()] = Some(Properties::new(\"SSTORE\", 2, -2));\n    table[OpCode::JUMP.to_usize()] = Some(Properties::new(\"JUMP\", 1, -1));\n    table[OpCode::JUMPI.to_usize()] = Some(Properties::new(\"JUMPI\", 2, -2));\n    table[OpCode::PC.to_usize()] = Some(Properties::new(\"PC\", 0, 1));\n    table[OpCode::MSIZE.to_usize()] = Some(Properties::new(\"MSIZE\", 0, 1));\n    table[OpCode::GAS.to_usize()] = Some(Properties::new(\"GAS\", 0, 1));\n    table[OpCode::JUMPDEST.to_usize()] = Some(Properties::new(\"JUMPDEST\", 0, 0));\n\n    table[OpCode::PUSH1.to_usize()] = Some(Properties::new(\"PUSH1\", 0, 1));\n    table[OpCode::PUSH2.to_usize()] = Some(Properties::new(\"PUSH2\", 0, 1));\n    table[OpCode::PUSH3.to_usize()] = Some(Properties::new(\"PUSH3\", 0, 1));\n    table[OpCode::PUSH4.to_usize()] = Some(Properties::new(\"PUSH4\", 0, 1));\n    table[OpCode::PUSH5.to_usize()] = Some(Properties::new(\"PUSH5\", 0, 1));\n    table[OpCode::PUSH6.to_usize()] = Some(Properties::new(\"PUSH6\", 0, 1));\n    table[OpCode::PUSH7.to_usize()] = Some(Properties::new(\"PUSH7\", 0, 1));\n    table[OpCode::PUSH8.to_usize()] = Some(Properties::new(\"PUSH8\", 0, 1));\n    table[OpCode::PUSH9.to_usize()] = Some(Properties::new(\"PUSH9\", 0, 1));\n    table[OpCode::PUSH10.to_usize()] = Some(Properties::new(\"PUSH10\", 0, 1));\n    table[OpCode::PUSH11.to_usize()] = Some(Properties::new(\"PUSH11\", 0, 1));\n    table[OpCode::PUSH12.to_usize()] = Some(Properties::new(\"PUSH12\", 0, 1));\n    table[OpCode::PUSH13.to_usize()] = Some(Properties::new(\"PUSH13\", 0, 1));\n    table[OpCode::PUSH14.to_usize()] = Some(Properties::new(\"PUSH14\", 0, 1));\n    table[OpCode::PUSH15.to_usize()] = Some(Properties::new(\"PUSH15\", 0, 1));\n    table[OpCode::PUSH16.to_usize()] = Some(Properties::new(\"PUSH16\", 0, 1));\n    table[OpCode::PUSH17.to_usize()] = Some(Properties::new(\"PUSH17\", 0, 1));\n    table[OpCode::PUSH18.to_usize()] = Some(Properties::new(\"PUSH18\", 0, 1));\n    table[OpCode::PUSH19.to_usize()] = Some(Properties::new(\"PUSH19\", 0, 1));\n    table[OpCode::PUSH20.to_usize()] = Some(Properties::new(\"PUSH20\", 0, 1));\n    table[OpCode::PUSH21.to_usize()] = Some(Properties::new(\"PUSH21\", 0, 1));\n    table[OpCode::PUSH22.to_usize()] = Some(Properties::new(\"PUSH22\", 0, 1));\n    table[OpCode::PUSH23.to_usize()] = Some(Properties::new(\"PUSH23\", 0, 1));\n    table[OpCode::PUSH24.to_usize()] = Some(Properties::new(\"PUSH24\", 0, 1));\n    table[OpCode::PUSH25.to_usize()] = Some(Properties::new(\"PUSH25\", 0, 1));\n    table[OpCode::PUSH26.to_usize()] = Some(Properties::new(\"PUSH26\", 0, 1));\n    table[OpCode::PUSH27.to_usize()] = Some(Properties::new(\"PUSH27\", 0, 1));\n    table[OpCode::PUSH28.to_usize()] = Some(Properties::new(\"PUSH28\", 0, 1));\n    table[OpCode::PUSH29.to_usize()] = Some(Properties::new(\"PUSH29\", 0, 1));\n    table[OpCode::PUSH30.to_usize()] = Some(Properties::new(\"PUSH30\", 0, 1));\n    table[OpCode::PUSH31.to_usize()] = Some(Properties::new(\"PUSH31\", 0, 1));\n    table[OpCode::PUSH32.to_usize()] = Some(Properties::new(\"PUSH32\", 0, 1));\n\n    table[OpCode::DUP1.to_usize()] = Some(Properties::new(\"DUP1\", 1, 1));\n    table[OpCode::DUP2.to_usize()] = Some(Properties::new(\"DUP2\", 2, 1));\n    table[OpCode::DUP3.to_usize()] = Some(Properties::new(\"DUP3\", 3, 1));\n    table[OpCode::DUP4.to_usize()] = Some(Properties::new(\"DUP4\", 4, 1));\n    table[OpCode::DUP5.to_usize()] = Some(Properties::new(\"DUP5\", 5, 1));\n    table[OpCode::DUP6.to_usize()] = Some(Properties::new(\"DUP6\", 6, 1));\n    table[OpCode::DUP7.to_usize()] = Some(Properties::new(\"DUP7\", 7, 1));\n    table[OpCode::DUP8.to_usize()] = Some(Properties::new(\"DUP8\", 8, 1));\n    table[OpCode::DUP9.to_usize()] = Some(Properties::new(\"DUP9\", 9, 1));\n    table[OpCode::DUP10.to_usize()] = Some(Properties::new(\"DUP10\", 10, 1));\n    table[OpCode::DUP11.to_usize()] = Some(Properties::new(\"DUP11\", 11, 1));\n    table[OpCode::DUP12.to_usize()] = Some(Properties::new(\"DUP12\", 12, 1));\n    table[OpCode::DUP13.to_usize()] = Some(Properties::new(\"DUP13\", 13, 1));\n    table[OpCode::DUP14.to_usize()] = Some(Properties::new(\"DUP14\", 14, 1));\n    table[OpCode::DUP15.to_usize()] = Some(Properties::new(\"DUP15\", 15, 1));\n    table[OpCode::DUP16.to_usize()] = Some(Properties::new(\"DUP16\", 16, 1));\n\n    table[OpCode::SWAP1.to_usize()] = Some(Properties::new(\"SWAP1\", 2, 0));\n    table[OpCode::SWAP2.to_usize()] = Some(Properties::new(\"SWAP2\", 3, 0));\n    table[OpCode::SWAP3.to_usize()] = Some(Properties::new(\"SWAP3\", 4, 0));\n    table[OpCode::SWAP4.to_usize()] = Some(Properties::new(\"SWAP4\", 5, 0));\n    table[OpCode::SWAP5.to_usize()] = Some(Properties::new(\"SWAP5\", 6, 0));\n    table[OpCode::SWAP6.to_usize()] = Some(Properties::new(\"SWAP6\", 7, 0));\n    table[OpCode::SWAP7.to_usize()] = Some(Properties::new(\"SWAP7\", 8, 0));\n    table[OpCode::SWAP8.to_usize()] = Some(Properties::new(\"SWAP8\", 9, 0));\n    table[OpCode::SWAP9.to_usize()] = Some(Properties::new(\"SWAP9\", 10, 0));\n    table[OpCode::SWAP10.to_usize()] = Some(Properties::new(\"SWAP10\", 11, 0));\n    table[OpCode::SWAP11.to_usize()] = Some(Properties::new(\"SWAP11\", 12, 0));\n    table[OpCode::SWAP12.to_usize()] = Some(Properties::new(\"SWAP12\", 13, 0));\n    table[OpCode::SWAP13.to_usize()] = Some(Properties::new(\"SWAP13\", 14, 0));\n    table[OpCode::SWAP14.to_usize()] = Some(Properties::new(\"SWAP14\", 15, 0));\n    table[OpCode::SWAP15.to_usize()] = Some(Properties::new(\"SWAP15\", 16, 0));\n    table[OpCode::SWAP16.to_usize()] = Some(Properties::new(\"SWAP16\", 17, 0));\n\n    table[OpCode::LOG0.to_usize()] = Some(Properties::new(\"LOG0\", 2, -2));\n    table[OpCode::LOG1.to_usize()] = Some(Properties::new(\"LOG1\", 3, -3));\n    table[OpCode::LOG2.to_usize()] = Some(Properties::new(\"LOG2\", 4, -4));\n    table[OpCode::LOG3.to_usize()] = Some(Properties::new(\"LOG3\", 5, -5));\n    table[OpCode::LOG4.to_usize()] = Some(Properties::new(\"LOG4\", 6, -6));\n\n    table[OpCode::CREATE.to_usize()] = Some(Properties::new(\"CREATE\", 3, -2));\n    table[OpCode::CALL.to_usize()] = Some(Properties::new(\"CALL\", 7, -6));\n    table[OpCode::CALLCODE.to_usize()] = Some(Properties::new(\"CALLCODE\", 7, -6));\n    table[OpCode::RETURN.to_usize()] = Some(Properties::new(\"RETURN\", 2, -2));\n    table[OpCode::DELEGATECALL.to_usize()] = Some(Properties::new(\"DELEGATECALL\", 6, -5));\n    table[OpCode::CREATE2.to_usize()] = Some(Properties::new(\"CREATE2\", 4, -3));\n    table[OpCode::STATICCALL.to_usize()] = Some(Properties::new(\"STATICCALL\", 6, -5));\n    table[OpCode::REVERT.to_usize()] = Some(Properties::new(\"REVERT\", 2, -2));\n    table[OpCode::INVALID.to_usize()] = Some(Properties::new(\"INVALID\", 0, 0));\n    table[OpCode::SELFDESTRUCT.to_usize()] = Some(Properties::new(\"SELFDESTRUCT\", 1, -1));\n\n    table\n});\n\n#[allow(clippy::needless_range_loop)]\nstatic FRONTIER_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {\n    let mut table = [None; 256];\n\n    table[OpCode::STOP.to_usize()] = Some(0);\n    table[OpCode::ADD.to_usize()] = Some(3);\n    table[OpCode::MUL.to_usize()] = Some(5);\n    table[OpCode::SUB.to_usize()] = Some(3);\n    table[OpCode::DIV.to_usize()] = Some(5);\n    table[OpCode::SDIV.to_usize()] = Some(5);\n    table[OpCode::MOD.to_usize()] = Some(5);\n    table[OpCode::SMOD.to_usize()] = Some(5);\n    table[OpCode::ADDMOD.to_usize()] = Some(8);\n    table[OpCode::MULMOD.to_usize()] = Some(8);\n    table[OpCode::EXP.to_usize()] = Some(10);\n    table[OpCode::SIGNEXTEND.to_usize()] = Some(5);\n    table[OpCode::LT.to_usize()] = Some(3);\n    table[OpCode::GT.to_usize()] = Some(3);\n    table[OpCode::SLT.to_usize()] = Some(3);\n    table[OpCode::SGT.to_usize()] = Some(3);\n    table[OpCode::EQ.to_usize()] = Some(3);\n    table[OpCode::ISZERO.to_usize()] = Some(3);\n    table[OpCode::AND.to_usize()] = Some(3);\n    table[OpCode::OR.to_usize()] = Some(3);\n    table[OpCode::XOR.to_usize()] = Some(3);\n    table[OpCode::NOT.to_usize()] = Some(3);\n    table[OpCode::BYTE.to_usize()] = Some(3);\n    table[OpCode::KECCAK256.to_usize()] = Some(30);\n    table[OpCode::ADDRESS.to_usize()] = Some(2);\n    table[OpCode::BALANCE.to_usize()] = Some(20);\n    table[OpCode::ORIGIN.to_usize()] = Some(2);\n    table[OpCode::CALLER.to_usize()] = Some(2);\n    table[OpCode::CALLVALUE.to_usize()] = Some(2);\n    table[OpCode::CALLDATALOAD.to_usize()] = Some(3);\n    table[OpCode::CALLDATASIZE.to_usize()] = Some(2);\n    table[OpCode::CALLDATACOPY.to_usize()] = Some(3);\n    table[OpCode::CODESIZE.to_usize()] = Some(2);\n    table[OpCode::CODECOPY.to_usize()] = Some(3);\n    table[OpCode::GASPRICE.to_usize()] = Some(2);\n    table[OpCode::EXTCODESIZE.to_usize()] = Some(20);\n    table[OpCode::EXTCODECOPY.to_usize()] = Some(20);\n    table[OpCode::BLOCKHASH.to_usize()] = Some(20);\n    table[OpCode::COINBASE.to_usize()] = Some(2);\n    table[OpCode::TIMESTAMP.to_usize()] = Some(2);\n    table[OpCode::NUMBER.to_usize()] = Some(2);\n    table[OpCode::DIFFICULTY.to_usize()] = Some(2);\n    table[OpCode::GASLIMIT.to_usize()] = Some(2);\n    table[OpCode::POP.to_usize()] = Some(2);\n    table[OpCode::MLOAD.to_usize()] = Some(3);\n    table[OpCode::MSTORE.to_usize()] = Some(3);\n    table[OpCode::MSTORE8.to_usize()] = Some(3);\n    table[OpCode::SLOAD.to_usize()] = Some(50);\n    table[OpCode::SSTORE.to_usize()] = Some(0);\n    table[OpCode::JUMP.to_usize()] = Some(8);\n    table[OpCode::JUMPI.to_usize()] = Some(10);\n    table[OpCode::PC.to_usize()] = Some(2);\n    table[OpCode::MSIZE.to_usize()] = Some(2);\n\n    table[OpCode::GAS.to_usize()] = Some(2);\n    table[OpCode::JUMPDEST.to_usize()] = Some(1);\n\n    for op in OpCode::PUSH1.to_usize()..=OpCode::PUSH32.to_usize() {\n        table[op] = Some(3);\n    }\n\n    for op in OpCode::DUP1.to_usize()..=OpCode::DUP16.to_usize() {\n        table[op] = Some(3);\n    }\n\n    for op in OpCode::SWAP1.to_usize()..=OpCode::SWAP16.to_usize() {\n        table[op] = Some(3);\n    }\n\n    for (i, op) in (OpCode::LOG0.to_usize()..=OpCode::LOG4.to_usize())\n        .into_iter()\n        .enumerate()\n    {\n        table[op] = Some((1 + i as u16) * 375);\n    }\n\n    table[OpCode::CREATE.to_usize()] = Some(32000);\n    table[OpCode::CALL.to_usize()] = Some(40);\n    table[OpCode::CALLCODE.to_usize()] = Some(40);\n    table[OpCode::RETURN.to_usize()] = Some(0);\n    table[OpCode::INVALID.to_usize()] = Some(0);\n    table[OpCode::SELFDESTRUCT.to_usize()] = Some(0);\n\n    table\n});\n\nstatic HOMESTEAD_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {\n    let mut table = *FRONTIER_GAS_COSTS;\n    table[OpCode::DELEGATECALL.to_usize()] = Some(40);\n    table\n});\n\nstatic TANGERINE_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {\n    let mut table = *HOMESTEAD_GAS_COSTS;\n    table[OpCode::BALANCE.to_usize()] = Some(400);\n    table[OpCode::EXTCODESIZE.to_usize()] = Some(700);\n    table[OpCode::EXTCODECOPY.to_usize()] = Some(700);\n    table[OpCode::SLOAD.to_usize()] = Some(200);\n    table[OpCode::CALL.to_usize()] = Some(700);\n    table[OpCode::CALLCODE.to_usize()] = Some(700);\n    table[OpCode::DELEGATECALL.to_usize()] = Some(700);\n    table[OpCode::SELFDESTRUCT.to_usize()] = Some(5000);\n    table\n});\n\nstatic SPURIOUS_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| *TANGERINE_GAS_COSTS);\n\nstatic BYZANTIUM_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {\n    let mut table = *SPURIOUS_GAS_COSTS;\n    table[OpCode::RETURNDATASIZE.to_usize()] = Some(2);\n    table[OpCode::RETURNDATACOPY.to_usize()] = Some(3);\n    table[OpCode::STATICCALL.to_usize()] = Some(700);\n    table[OpCode::REVERT.to_usize()] = Some(0);\n    table\n});\n\nstatic CONSTANTINOPLE_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {\n    let mut table = *BYZANTIUM_GAS_COSTS;\n    table[OpCode::SHL.to_usize()] = Some(3);\n    table[OpCode::SHR.to_usize()] = Some(3);\n    table[OpCode::SAR.to_usize()] = Some(3);\n    table[OpCode::EXTCODEHASH.to_usize()] = Some(400);\n    table[OpCode::CREATE2.to_usize()] = Some(32000);\n    table\n});\n\nstatic PETERSBURG_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| *CONSTANTINOPLE_GAS_COSTS);\n\nstatic ISTANBUL_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {\n    let mut table = *PETERSBURG_GAS_COSTS;\n    table[OpCode::BALANCE.to_usize()] = Some(700);\n    table[OpCode::CHAINID.to_usize()] = Some(2);\n    table[OpCode::EXTCODEHASH.to_usize()] = Some(700);\n    table[OpCode::SELFBALANCE.to_usize()] = Some(5);\n    table[OpCode::SLOAD.to_usize()] = Some(800);\n    table\n});\n\nstatic BERLIN_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {\n    let mut table = *ISTANBUL_GAS_COSTS;\n    table[OpCode::EXTCODESIZE.to_usize()] = Some(WARM_STORAGE_READ_COST);\n    table[OpCode::EXTCODECOPY.to_usize()] = Some(WARM_STORAGE_READ_COST);\n    table[OpCode::EXTCODEHASH.to_usize()] = Some(WARM_STORAGE_READ_COST);\n    table[OpCode::BALANCE.to_usize()] = Some(WARM_STORAGE_READ_COST);\n    table[OpCode::CALL.to_usize()] = Some(WARM_STORAGE_READ_COST);\n    table[OpCode::CALLCODE.to_usize()] = Some(WARM_STORAGE_READ_COST);\n    table[OpCode::DELEGATECALL.to_usize()] = Some(WARM_STORAGE_READ_COST);\n    table[OpCode::STATICCALL.to_usize()] = Some(WARM_STORAGE_READ_COST);\n    table[OpCode::SLOAD.to_usize()] = Some(WARM_STORAGE_READ_COST);\n    table\n});\n\nstatic LONDON_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {\n    let mut table = *BERLIN_GAS_COSTS;\n    table[OpCode::BASEFEE.to_usize()] = Some(2);\n    table\n});\n\nstatic SHANGHAI_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| *LONDON_GAS_COSTS);\n\npub fn gas_costs(revision: Revision) -> &'static [Option<u16>; 256] {\n    match revision {\n        Revision::Frontier => &FRONTIER_GAS_COSTS,\n        Revision::Homestead => &HOMESTEAD_GAS_COSTS,\n        Revision::Tangerine => &TANGERINE_GAS_COSTS,\n        Revision::Spurious => &SPURIOUS_GAS_COSTS,\n        Revision::Byzantium => &BYZANTIUM_GAS_COSTS,\n        Revision::Constantinople => &CONSTANTINOPLE_GAS_COSTS,\n        Revision::Petersburg => &PETERSBURG_GAS_COSTS,\n        Revision::Istanbul => &ISTANBUL_GAS_COSTS,\n        Revision::Berlin => &BERLIN_GAS_COSTS,\n        Revision::London => &LONDON_GAS_COSTS,\n        Revision::Shanghai => &SHANGHAI_GAS_COSTS,\n    }\n}\n"
  },
  {
    "path": "src/instructions/stack_manip.rs",
    "content": "use crate::state::*;\nuse ethereum_types::U256;\n\npub(crate) fn push(stack: &mut Stack, code: &[u8], push_len: usize) {\n    stack.push(U256::from_big_endian(&code[..push_len]));\n}\n\npub(crate) fn dup(stack: &mut Stack, height: usize) {\n    stack.push(*stack.get(height - 1));\n}\n\npub(crate) fn swap(stack: &mut Stack, height: usize) {\n    stack.swap_top(height);\n}\n\npub(crate) fn pop(stack: &mut Stack) {\n    stack.pop();\n}\n"
  },
  {
    "path": "src/interpreter.rs",
    "content": "use self::instruction_table::*;\nuse crate::{\n    common::*,\n    continuation::{interrupt::*, interrupt_data::*, resume_data::*, *},\n    instructions::{control::*, stack_manip::*, *},\n    state::*,\n    tracing::Tracer,\n    *,\n};\nuse ethereum_types::U256;\nuse genawaiter::sync::*;\nuse std::sync::Arc;\n\nfn check_requirements(\n    instruction_table: &InstructionTable,\n    state: &mut ExecutionState,\n    op: OpCode,\n) -> Result<(), StatusCode> {\n    let metrics = &instruction_table[op.to_usize()].ok_or(StatusCode::UndefinedInstruction)?;\n\n    state.gas_left -= metrics.gas_cost as i64;\n    if state.gas_left < 0 {\n        return Err(StatusCode::OutOfGas);\n    }\n\n    let stack_size = state.stack.len();\n    if stack_size == Stack::limit() {\n        if metrics.can_overflow_stack {\n            return Err(StatusCode::StackOverflow);\n        }\n    } else if stack_size < metrics.stack_height_required.into() {\n        return Err(StatusCode::StackUnderflow);\n    }\n\n    Ok(())\n}\n\n#[derive(Clone, Debug)]\npub struct JumpdestMap(Arc<[bool]>);\n\nimpl JumpdestMap {\n    pub fn contains(&self, dst: U256) -> bool {\n        dst < self.0.len().into() && self.0[dst.as_usize()]\n    }\n}\n\n/// Code with analysis.\n#[derive(Clone, Debug)]\npub struct AnalyzedCode {\n    jumpdest_map: JumpdestMap,\n    code: Bytes,\n    padded_code: Bytes,\n}\n\nimpl AnalyzedCode {\n    /// Analyze code and prepare it for execution.\n    pub fn analyze(code: impl Into<Vec<u8>>) -> Self {\n        let code = code.into();\n        let mut jumpdest_map = vec![false; code.len()];\n\n        let mut i = 0;\n        while i < code.len() {\n            let opcode = OpCode(code[i]);\n            i += match opcode {\n                OpCode::JUMPDEST => {\n                    jumpdest_map[i] = true;\n                    1\n                }\n                OpCode::PUSH1\n                | OpCode::PUSH2\n                | OpCode::PUSH3\n                | OpCode::PUSH4\n                | OpCode::PUSH5\n                | OpCode::PUSH6\n                | OpCode::PUSH7\n                | OpCode::PUSH8\n                | OpCode::PUSH9\n                | OpCode::PUSH10\n                | OpCode::PUSH11\n                | OpCode::PUSH12\n                | OpCode::PUSH13\n                | OpCode::PUSH14\n                | OpCode::PUSH15\n                | OpCode::PUSH16\n                | OpCode::PUSH17\n                | OpCode::PUSH18\n                | OpCode::PUSH19\n                | OpCode::PUSH20\n                | OpCode::PUSH21\n                | OpCode::PUSH22\n                | OpCode::PUSH23\n                | OpCode::PUSH24\n                | OpCode::PUSH25\n                | OpCode::PUSH26\n                | OpCode::PUSH27\n                | OpCode::PUSH28\n                | OpCode::PUSH29\n                | OpCode::PUSH30\n                | OpCode::PUSH31\n                | OpCode::PUSH32 => opcode.to_usize() - OpCode::PUSH1.to_usize() + 2,\n                _ => 1,\n            }\n        }\n\n        let code_len = code.len();\n\n        let mut padded_code = code;\n        padded_code.resize(i + 1, OpCode::STOP.to_u8());\n\n        let jumpdest_map = JumpdestMap(jumpdest_map.into());\n        let padded_code = Bytes::from(padded_code);\n        let mut code = padded_code.clone();\n        code.truncate(code_len);\n\n        Self {\n            jumpdest_map,\n            code,\n            padded_code,\n        }\n    }\n\n    /// Execute analyzed EVM bytecode using provided `Host` context. Optionally modify the state after each instruction using provided closure.\n    pub fn execute<H: Host, T: Tracer>(\n        &self,\n        host: &mut H,\n        tracer: &mut T,\n        state_modifier: StateModifier,\n        message: Message,\n        revision: Revision,\n    ) -> Output {\n        if !T::DUMMY {\n            tracer.notify_execution_start(revision, message.clone(), self.code.clone());\n        }\n\n        let output = self\n            .execute_resumable(!T::DUMMY || state_modifier.is_some(), message, revision)\n            .run_to_completion_with_host(host, tracer, state_modifier);\n\n        if !T::DUMMY {\n            tracer.notify_execution_end(&output);\n        }\n\n        output\n    }\n\n    /// Execute in resumable EVM.\n    pub fn execute_resumable(\n        &self,\n        trace: bool,\n        message: Message,\n        revision: Revision,\n    ) -> ExecutionStartInterrupt {\n        let code = self.clone();\n        let inner = Box::pin(Gen::new(move |co| {\n            interpreter_producer(co, code, ExecutionState::new(message, revision), trace)\n        }));\n\n        ExecutionStartInterrupt { inner, data: () }\n    }\n}\n\nimpl ExecutionStartInterrupt {\n    pub fn run_to_completion_with_host<H: Host, T: Tracer>(\n        self,\n        host: &mut H,\n        tracer: &mut T,\n        state_modifier: StateModifier,\n    ) -> Output {\n        let mut interrupt = self.resume(());\n\n        loop {\n            interrupt = match interrupt {\n                InterruptVariant::InstructionStart(i) => {\n                    tracer.notify_instruction_start(i.data().pc, i.data().opcode, &i.data().state);\n                    i.resume(state_modifier.clone())\n                }\n                InterruptVariant::AccountExists(i) => {\n                    let exists = host.account_exists(i.data().address);\n                    i.resume(AccountExistsStatus { exists })\n                }\n                InterruptVariant::GetBalance(i) => {\n                    let balance = host.get_balance(i.data().address);\n                    i.resume(Balance { balance })\n                }\n                InterruptVariant::GetCodeSize(i) => {\n                    let code_size = host.get_code_size(i.data().address);\n                    i.resume(CodeSize { code_size })\n                }\n                InterruptVariant::GetStorage(i) => {\n                    let value = host.get_storage(i.data().address, i.data().key);\n                    i.resume(StorageValue { value })\n                }\n                InterruptVariant::SetStorage(i) => {\n                    let status = host.set_storage(i.data().address, i.data().key, i.data().value);\n                    i.resume(StorageStatusInfo { status })\n                }\n                InterruptVariant::GetCodeHash(i) => {\n                    let hash = host.get_code_hash(i.data().address);\n                    i.resume(CodeHash { hash })\n                }\n                InterruptVariant::CopyCode(i) => {\n                    let mut code = vec![0; i.data().max_size];\n                    let copied = host.copy_code(i.data().address, i.data().offset, &mut code[..]);\n                    if copied > code.len() {\n                        return Output {\n                            status_code: StatusCode::InternalError(format!(\n                                \"copy code: copied {} > max size {}\",\n                                copied,\n                                code.len()\n                            )),\n                            gas_left: 0,\n                            output_data: Bytes::new(),\n                            create_address: None,\n                        };\n                    }\n                    code.truncate(copied);\n                    let code = code.into();\n                    i.resume(Code { code })\n                }\n                InterruptVariant::Selfdestruct(i) => {\n                    host.selfdestruct(i.data().address, i.data().beneficiary);\n                    i.resume(())\n                }\n                InterruptVariant::Call(i) => {\n                    let message = match i.data() {\n                        Call::Call(message) => message.clone(),\n                        Call::Create(message) => message.clone().into(),\n                    };\n                    let output = host.call(&message);\n                    i.resume(CallOutput { output })\n                }\n                InterruptVariant::GetTxContext(i) => {\n                    let context = host.get_tx_context();\n                    i.resume(TxContextData { context })\n                }\n                InterruptVariant::GetBlockHash(i) => {\n                    let hash = host.get_block_hash(i.data().block_number);\n                    i.resume(BlockHash { hash })\n                }\n                InterruptVariant::EmitLog(i) => {\n                    host.emit_log(\n                        i.data().address,\n                        &*i.data().data,\n                        i.data().topics.as_slice(),\n                    );\n                    i.resume(())\n                }\n                InterruptVariant::AccessAccount(i) => {\n                    let status = host.access_account(i.data().address);\n                    i.resume(AccessAccountStatus { status })\n                }\n                InterruptVariant::AccessStorage(i) => {\n                    let status = host.access_storage(i.data().address, i.data().key);\n                    i.resume(AccessStorageStatus { status })\n                }\n                InterruptVariant::Complete(i) => {\n                    let output = match i {\n                        Ok(output) => output.into(),\n                        Err(status_code) => Output {\n                            status_code,\n                            gas_left: 0,\n                            output_data: Bytes::new(),\n                            create_address: None,\n                        },\n                    };\n\n                    return output;\n                }\n            };\n        }\n    }\n}\n\nasync fn interpreter_producer(\n    mut co: Co<InterruptDataVariant, ResumeDataVariant>,\n    s: AnalyzedCode,\n    mut state: ExecutionState,\n    trace: bool,\n) -> Result<SuccessfulOutput, StatusCode> {\n    let state = &mut state;\n\n    let instruction_table = get_baseline_instruction_table(state.evm_revision);\n\n    let mut reverted = false;\n\n    let mut pc = 0;\n\n    loop {\n        let op = OpCode(s.padded_code[pc]);\n\n        // Do not print stop on the final STOP\n        if trace && pc < s.code.len() {\n            if let Some(modifier) = co\n                .yield_(InterruptDataVariant::InstructionStart(Box::new(\n                    InstructionStart {\n                        pc,\n                        opcode: op,\n                        state: state.clone(),\n                    },\n                )))\n                .await\n                .as_state_modifier()\n                .unwrap()\n            {\n                (modifier)(state)\n            }\n        }\n\n        check_requirements(instruction_table, state, op)?;\n\n        match op {\n            OpCode::STOP => {\n                break;\n            }\n            OpCode::ADD => {\n                arithmetic::add(&mut state.stack);\n            }\n            OpCode::MUL => {\n                arithmetic::mul(&mut state.stack);\n            }\n            OpCode::SUB => {\n                arithmetic::sub(&mut state.stack);\n            }\n            OpCode::DIV => {\n                arithmetic::div(&mut state.stack);\n            }\n            OpCode::SDIV => {\n                arithmetic::sdiv(&mut state.stack);\n            }\n            OpCode::MOD => {\n                arithmetic::modulo(&mut state.stack);\n            }\n            OpCode::SMOD => {\n                arithmetic::smod(&mut state.stack);\n            }\n            OpCode::ADDMOD => {\n                arithmetic::addmod(&mut state.stack);\n            }\n            OpCode::MULMOD => {\n                arithmetic::mulmod(&mut state.stack);\n            }\n            OpCode::EXP => {\n                arithmetic::exp(state)?;\n            }\n            OpCode::SIGNEXTEND => {\n                arithmetic::signextend(&mut state.stack);\n            }\n            OpCode::LT => {\n                boolean::lt(&mut state.stack);\n            }\n            OpCode::GT => {\n                boolean::gt(&mut state.stack);\n            }\n            OpCode::SLT => {\n                boolean::slt(&mut state.stack);\n            }\n            OpCode::SGT => {\n                boolean::sgt(&mut state.stack);\n            }\n            OpCode::EQ => {\n                boolean::eq(&mut state.stack);\n            }\n            OpCode::ISZERO => {\n                boolean::iszero(&mut state.stack);\n            }\n            OpCode::AND => {\n                boolean::and(&mut state.stack);\n            }\n            OpCode::OR => {\n                boolean::or(&mut state.stack);\n            }\n            OpCode::XOR => {\n                boolean::xor(&mut state.stack);\n            }\n            OpCode::NOT => {\n                boolean::not(&mut state.stack);\n            }\n            OpCode::BYTE => {\n                bitwise::byte(&mut state.stack);\n            }\n            OpCode::SHL => {\n                bitwise::shl(&mut state.stack);\n            }\n            OpCode::SHR => {\n                bitwise::shr(&mut state.stack);\n            }\n            OpCode::SAR => {\n                bitwise::sar(&mut state.stack);\n            }\n\n            OpCode::KECCAK256 => {\n                memory::keccak256(state)?;\n            }\n            OpCode::ADDRESS => {\n                external::address(state);\n            }\n            OpCode::BALANCE => {\n                balance!(co, state);\n            }\n            OpCode::CALLER => {\n                external::caller(state);\n            }\n            OpCode::CALLVALUE => {\n                external::callvalue(state);\n            }\n            OpCode::CALLDATALOAD => {\n                calldataload(state);\n            }\n            OpCode::CALLDATASIZE => {\n                calldatasize(state);\n            }\n            OpCode::CALLDATACOPY => {\n                memory::calldatacopy(state)?;\n            }\n            OpCode::CODESIZE => {\n                memory::codesize(&mut state.stack, &s.code[..]);\n            }\n            OpCode::CODECOPY => {\n                memory::codecopy(state, &s.code[..])?;\n            }\n            OpCode::EXTCODESIZE => {\n                extcodesize!(co, state);\n            }\n            OpCode::EXTCODECOPY => {\n                extcodecopy!(co, state);\n            }\n            OpCode::RETURNDATASIZE => {\n                memory::returndatasize(state);\n            }\n            OpCode::RETURNDATACOPY => {\n                memory::returndatacopy(state)?;\n            }\n            OpCode::EXTCODEHASH => {\n                extcodehash!(co, state);\n            }\n            OpCode::BLOCKHASH => {\n                blockhash!(co, state);\n            }\n            OpCode::ORIGIN\n            | OpCode::COINBASE\n            | OpCode::GASPRICE\n            | OpCode::TIMESTAMP\n            | OpCode::NUMBER\n            | OpCode::DIFFICULTY\n            | OpCode::GASLIMIT\n            | OpCode::CHAINID\n            | OpCode::BASEFEE => {\n                push_txcontext!(\n                    co,\n                    state,\n                    match op {\n                        OpCode::ORIGIN => external::origin_accessor,\n                        OpCode::COINBASE => external::coinbase_accessor,\n                        OpCode::GASPRICE => external::gasprice_accessor,\n                        OpCode::TIMESTAMP => external::timestamp_accessor,\n                        OpCode::NUMBER => external::number_accessor,\n                        OpCode::DIFFICULTY => external::difficulty_accessor,\n                        OpCode::GASLIMIT => external::gaslimit_accessor,\n                        OpCode::CHAINID => external::chainid_accessor,\n                        OpCode::BASEFEE => external::basefee_accessor,\n                        _ => unreachable!(),\n                    }\n                );\n            }\n            OpCode::SELFBALANCE => {\n                selfbalance!(co, state);\n            }\n            OpCode::POP => {\n                stack_manip::pop(&mut state.stack);\n            }\n            OpCode::MLOAD => {\n                memory::mload(state)?;\n            }\n            OpCode::MSTORE => {\n                memory::mstore(state)?;\n            }\n            OpCode::MSTORE8 => {\n                memory::mstore8(state)?;\n            }\n            OpCode::JUMP => {\n                pc = op_jump(state, &s.jumpdest_map)?;\n\n                continue;\n            }\n            OpCode::JUMPI => {\n                if !state.stack.get(1).is_zero() {\n                    pc = op_jump(state, &s.jumpdest_map)?;\n                    state.stack.pop();\n\n                    continue;\n                } else {\n                    state.stack.pop();\n                    state.stack.pop();\n                }\n            }\n            OpCode::PC => state.stack.push(pc.into()),\n            OpCode::MSIZE => memory::msize(state),\n            OpCode::SLOAD => {\n                sload!(co, state);\n            }\n            OpCode::SSTORE => {\n                sstore!(co, state);\n            }\n            OpCode::GAS => state.stack.push(state.gas_left.into()),\n            OpCode::JUMPDEST => {}\n            OpCode::PUSH1\n            | OpCode::PUSH2\n            | OpCode::PUSH3\n            | OpCode::PUSH4\n            | OpCode::PUSH5\n            | OpCode::PUSH6\n            | OpCode::PUSH7\n            | OpCode::PUSH8\n            | OpCode::PUSH9\n            | OpCode::PUSH10\n            | OpCode::PUSH11\n            | OpCode::PUSH12\n            | OpCode::PUSH13\n            | OpCode::PUSH14\n            | OpCode::PUSH15\n            | OpCode::PUSH16\n            | OpCode::PUSH17\n            | OpCode::PUSH18\n            | OpCode::PUSH19\n            | OpCode::PUSH20\n            | OpCode::PUSH21\n            | OpCode::PUSH22\n            | OpCode::PUSH23\n            | OpCode::PUSH24\n            | OpCode::PUSH25\n            | OpCode::PUSH26\n            | OpCode::PUSH27\n            | OpCode::PUSH28\n            | OpCode::PUSH29\n            | OpCode::PUSH30\n            | OpCode::PUSH31\n            | OpCode::PUSH32 => {\n                let push_len = op.push_size().unwrap().into();\n                push(&mut state.stack, &s.padded_code[pc + 1..], push_len);\n                pc += push_len;\n            }\n\n            OpCode::DUP1\n            | OpCode::DUP2\n            | OpCode::DUP3\n            | OpCode::DUP4\n            | OpCode::DUP5\n            | OpCode::DUP6\n            | OpCode::DUP7\n            | OpCode::DUP8\n            | OpCode::DUP9\n            | OpCode::DUP10\n            | OpCode::DUP11\n            | OpCode::DUP12\n            | OpCode::DUP13\n            | OpCode::DUP14\n            | OpCode::DUP15\n            | OpCode::DUP16 => {\n                dup(\n                    &mut state.stack,\n                    op.to_usize() - OpCode::DUP1.to_usize() + 1,\n                );\n            }\n\n            OpCode::SWAP1\n            | OpCode::SWAP2\n            | OpCode::SWAP3\n            | OpCode::SWAP4\n            | OpCode::SWAP5\n            | OpCode::SWAP6\n            | OpCode::SWAP7\n            | OpCode::SWAP8\n            | OpCode::SWAP9\n            | OpCode::SWAP10\n            | OpCode::SWAP11\n            | OpCode::SWAP12\n            | OpCode::SWAP13\n            | OpCode::SWAP14\n            | OpCode::SWAP15\n            | OpCode::SWAP16 => swap(\n                &mut state.stack,\n                op.to_usize() - OpCode::SWAP1.to_usize() + 1,\n            ),\n\n            OpCode::LOG0 | OpCode::LOG1 | OpCode::LOG2 | OpCode::LOG3 | OpCode::LOG4 => {\n                do_log!(co, state, op.to_usize() - OpCode::LOG0.to_usize());\n            }\n            OpCode::CREATE | OpCode::CREATE2 => {\n                do_create!(co, state, op == OpCode::CREATE2);\n            }\n            OpCode::CALL | OpCode::CALLCODE | OpCode::DELEGATECALL | OpCode::STATICCALL => {\n                do_call!(\n                    co,\n                    state,\n                    match op {\n                        OpCode::CALL | OpCode::STATICCALL => CallKind::Call,\n                        OpCode::CALLCODE => CallKind::CallCode,\n                        OpCode::DELEGATECALL => CallKind::DelegateCall,\n                        _ => unreachable!(),\n                    },\n                    op == OpCode::STATICCALL\n                );\n            }\n            OpCode::RETURN | OpCode::REVERT => {\n                ret(state)?;\n                reverted = op == OpCode::REVERT;\n                break;\n            }\n            OpCode::INVALID => {\n                return Err(StatusCode::InvalidInstruction);\n            }\n            OpCode::SELFDESTRUCT => {\n                selfdestruct!(co, state);\n                break;\n            }\n            other => {\n                unreachable!(\"reached unhandled opcode: {}\", other);\n            }\n        }\n\n        pc += 1;\n    }\n\n    let output = SuccessfulOutput {\n        reverted,\n        gas_left: state.gas_left,\n        output_data: state.output_data.clone(),\n    };\n\n    Ok(output)\n}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "#![doc = include_str!(\"../README.md\")]\nuse bytes::Bytes;\npub use common::{\n    CallKind, CreateMessage, Message, Output, Revision, StatusCode, SuccessfulOutput,\n};\npub use host::Host;\npub use interpreter::AnalyzedCode;\npub use opcode::OpCode;\npub use state::{ExecutionState, Stack};\n\n/// Maximum allowed EVM bytecode size.\npub const MAX_CODE_SIZE: usize = 0x6000;\n\nmod common;\npub mod host;\n#[doc(hidden)]\npub mod instructions;\nmod interpreter;\npub mod opcode;\nmod state;\npub mod tracing;\n\npub mod continuation;\n#[cfg(feature = \"util\")]\npub mod util;\n\n#[cfg(feature = \"evmc\")]\npub mod evmc;\n"
  },
  {
    "path": "src/opcode.rs",
    "content": "use std::{borrow::Cow, fmt::Display};\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub struct OpCode(pub u8);\n\nimpl OpCode {\n    #[inline(always)]\n    pub const fn to_u8(self) -> u8 {\n        self.0\n    }\n\n    #[inline(always)]\n    pub const fn to_usize(self) -> usize {\n        self.to_u8() as usize\n    }\n}\n\nimpl OpCode {\n    pub const STOP: OpCode = OpCode(0x00);\n    pub const ADD: OpCode = OpCode(0x01);\n    pub const MUL: OpCode = OpCode(0x02);\n    pub const SUB: OpCode = OpCode(0x03);\n    pub const DIV: OpCode = OpCode(0x04);\n    pub const SDIV: OpCode = OpCode(0x05);\n    pub const MOD: OpCode = OpCode(0x06);\n    pub const SMOD: OpCode = OpCode(0x07);\n    pub const ADDMOD: OpCode = OpCode(0x08);\n    pub const MULMOD: OpCode = OpCode(0x09);\n    pub const EXP: OpCode = OpCode(0x0a);\n    pub const SIGNEXTEND: OpCode = OpCode(0x0b);\n\n    pub const LT: OpCode = OpCode(0x10);\n    pub const GT: OpCode = OpCode(0x11);\n    pub const SLT: OpCode = OpCode(0x12);\n    pub const SGT: OpCode = OpCode(0x13);\n    pub const EQ: OpCode = OpCode(0x14);\n    pub const ISZERO: OpCode = OpCode(0x15);\n    pub const AND: OpCode = OpCode(0x16);\n    pub const OR: OpCode = OpCode(0x17);\n    pub const XOR: OpCode = OpCode(0x18);\n    pub const NOT: OpCode = OpCode(0x19);\n    pub const BYTE: OpCode = OpCode(0x1a);\n    pub const SHL: OpCode = OpCode(0x1b);\n    pub const SHR: OpCode = OpCode(0x1c);\n    pub const SAR: OpCode = OpCode(0x1d);\n\n    pub const KECCAK256: OpCode = OpCode(0x20);\n\n    pub const ADDRESS: OpCode = OpCode(0x30);\n    pub const BALANCE: OpCode = OpCode(0x31);\n    pub const ORIGIN: OpCode = OpCode(0x32);\n    pub const CALLER: OpCode = OpCode(0x33);\n    pub const CALLVALUE: OpCode = OpCode(0x34);\n    pub const CALLDATALOAD: OpCode = OpCode(0x35);\n    pub const CALLDATASIZE: OpCode = OpCode(0x36);\n    pub const CALLDATACOPY: OpCode = OpCode(0x37);\n    pub const CODESIZE: OpCode = OpCode(0x38);\n    pub const CODECOPY: OpCode = OpCode(0x39);\n    pub const GASPRICE: OpCode = OpCode(0x3a);\n    pub const EXTCODESIZE: OpCode = OpCode(0x3b);\n    pub const EXTCODECOPY: OpCode = OpCode(0x3c);\n    pub const RETURNDATASIZE: OpCode = OpCode(0x3d);\n    pub const RETURNDATACOPY: OpCode = OpCode(0x3e);\n    pub const EXTCODEHASH: OpCode = OpCode(0x3f);\n\n    pub const BLOCKHASH: OpCode = OpCode(0x40);\n    pub const COINBASE: OpCode = OpCode(0x41);\n    pub const TIMESTAMP: OpCode = OpCode(0x42);\n    pub const NUMBER: OpCode = OpCode(0x43);\n    pub const DIFFICULTY: OpCode = OpCode(0x44);\n    pub const GASLIMIT: OpCode = OpCode(0x45);\n    pub const CHAINID: OpCode = OpCode(0x46);\n    pub const SELFBALANCE: OpCode = OpCode(0x47);\n    pub const BASEFEE: OpCode = OpCode(0x48);\n\n    pub const POP: OpCode = OpCode(0x50);\n    pub const MLOAD: OpCode = OpCode(0x51);\n    pub const MSTORE: OpCode = OpCode(0x52);\n    pub const MSTORE8: OpCode = OpCode(0x53);\n    pub const SLOAD: OpCode = OpCode(0x54);\n    pub const SSTORE: OpCode = OpCode(0x55);\n    pub const JUMP: OpCode = OpCode(0x56);\n    pub const JUMPI: OpCode = OpCode(0x57);\n    pub const PC: OpCode = OpCode(0x58);\n    pub const MSIZE: OpCode = OpCode(0x59);\n    pub const GAS: OpCode = OpCode(0x5a);\n    pub const JUMPDEST: OpCode = OpCode(0x5b);\n\n    pub const PUSH1: OpCode = OpCode(0x60);\n    pub const PUSH2: OpCode = OpCode(0x61);\n    pub const PUSH3: OpCode = OpCode(0x62);\n    pub const PUSH4: OpCode = OpCode(0x63);\n    pub const PUSH5: OpCode = OpCode(0x64);\n    pub const PUSH6: OpCode = OpCode(0x65);\n    pub const PUSH7: OpCode = OpCode(0x66);\n    pub const PUSH8: OpCode = OpCode(0x67);\n    pub const PUSH9: OpCode = OpCode(0x68);\n    pub const PUSH10: OpCode = OpCode(0x69);\n    pub const PUSH11: OpCode = OpCode(0x6a);\n    pub const PUSH12: OpCode = OpCode(0x6b);\n    pub const PUSH13: OpCode = OpCode(0x6c);\n    pub const PUSH14: OpCode = OpCode(0x6d);\n    pub const PUSH15: OpCode = OpCode(0x6e);\n    pub const PUSH16: OpCode = OpCode(0x6f);\n    pub const PUSH17: OpCode = OpCode(0x70);\n    pub const PUSH18: OpCode = OpCode(0x71);\n    pub const PUSH19: OpCode = OpCode(0x72);\n    pub const PUSH20: OpCode = OpCode(0x73);\n    pub const PUSH21: OpCode = OpCode(0x74);\n    pub const PUSH22: OpCode = OpCode(0x75);\n    pub const PUSH23: OpCode = OpCode(0x76);\n    pub const PUSH24: OpCode = OpCode(0x77);\n    pub const PUSH25: OpCode = OpCode(0x78);\n    pub const PUSH26: OpCode = OpCode(0x79);\n    pub const PUSH27: OpCode = OpCode(0x7a);\n    pub const PUSH28: OpCode = OpCode(0x7b);\n    pub const PUSH29: OpCode = OpCode(0x7c);\n    pub const PUSH30: OpCode = OpCode(0x7d);\n    pub const PUSH31: OpCode = OpCode(0x7e);\n    pub const PUSH32: OpCode = OpCode(0x7f);\n    pub const DUP1: OpCode = OpCode(0x80);\n    pub const DUP2: OpCode = OpCode(0x81);\n    pub const DUP3: OpCode = OpCode(0x82);\n    pub const DUP4: OpCode = OpCode(0x83);\n    pub const DUP5: OpCode = OpCode(0x84);\n    pub const DUP6: OpCode = OpCode(0x85);\n    pub const DUP7: OpCode = OpCode(0x86);\n    pub const DUP8: OpCode = OpCode(0x87);\n    pub const DUP9: OpCode = OpCode(0x88);\n    pub const DUP10: OpCode = OpCode(0x89);\n    pub const DUP11: OpCode = OpCode(0x8a);\n    pub const DUP12: OpCode = OpCode(0x8b);\n    pub const DUP13: OpCode = OpCode(0x8c);\n    pub const DUP14: OpCode = OpCode(0x8d);\n    pub const DUP15: OpCode = OpCode(0x8e);\n    pub const DUP16: OpCode = OpCode(0x8f);\n    pub const SWAP1: OpCode = OpCode(0x90);\n    pub const SWAP2: OpCode = OpCode(0x91);\n    pub const SWAP3: OpCode = OpCode(0x92);\n    pub const SWAP4: OpCode = OpCode(0x93);\n    pub const SWAP5: OpCode = OpCode(0x94);\n    pub const SWAP6: OpCode = OpCode(0x95);\n    pub const SWAP7: OpCode = OpCode(0x96);\n    pub const SWAP8: OpCode = OpCode(0x97);\n    pub const SWAP9: OpCode = OpCode(0x98);\n    pub const SWAP10: OpCode = OpCode(0x99);\n    pub const SWAP11: OpCode = OpCode(0x9a);\n    pub const SWAP12: OpCode = OpCode(0x9b);\n    pub const SWAP13: OpCode = OpCode(0x9c);\n    pub const SWAP14: OpCode = OpCode(0x9d);\n    pub const SWAP15: OpCode = OpCode(0x9e);\n    pub const SWAP16: OpCode = OpCode(0x9f);\n    pub const LOG0: OpCode = OpCode(0xa0);\n    pub const LOG1: OpCode = OpCode(0xa1);\n    pub const LOG2: OpCode = OpCode(0xa2);\n    pub const LOG3: OpCode = OpCode(0xa3);\n    pub const LOG4: OpCode = OpCode(0xa4);\n\n    pub const CREATE: OpCode = OpCode(0xf0);\n    pub const CALL: OpCode = OpCode(0xf1);\n    pub const CALLCODE: OpCode = OpCode(0xf2);\n    pub const RETURN: OpCode = OpCode(0xf3);\n    pub const DELEGATECALL: OpCode = OpCode(0xf4);\n    pub const CREATE2: OpCode = OpCode(0xf5);\n\n    pub const STATICCALL: OpCode = OpCode(0xfa);\n\n    pub const REVERT: OpCode = OpCode(0xfd);\n    pub const INVALID: OpCode = OpCode(0xfe);\n    pub const SELFDESTRUCT: OpCode = OpCode(0xff);\n}\n\nimpl OpCode {\n    pub const fn name(&self) -> &'static str {\n        match *self {\n            OpCode::STOP => \"STOP\",\n            OpCode::ADD => \"ADD\",\n            OpCode::MUL => \"MUL\",\n            OpCode::SUB => \"SUB\",\n            OpCode::DIV => \"DIV\",\n            OpCode::SDIV => \"SDIV\",\n            OpCode::MOD => \"MOD\",\n            OpCode::SMOD => \"SMOD\",\n            OpCode::ADDMOD => \"ADDMOD\",\n            OpCode::MULMOD => \"MULMOD\",\n            OpCode::EXP => \"EXP\",\n            OpCode::SIGNEXTEND => \"SIGNEXTEND\",\n            OpCode::LT => \"LT\",\n            OpCode::GT => \"GT\",\n            OpCode::SLT => \"SLT\",\n            OpCode::SGT => \"SGT\",\n            OpCode::EQ => \"EQ\",\n            OpCode::ISZERO => \"ISZERO\",\n            OpCode::AND => \"AND\",\n            OpCode::OR => \"OR\",\n            OpCode::XOR => \"XOR\",\n            OpCode::NOT => \"NOT\",\n            OpCode::BYTE => \"BYTE\",\n            OpCode::SHL => \"SHL\",\n            OpCode::SHR => \"SHR\",\n            OpCode::SAR => \"SAR\",\n            OpCode::KECCAK256 => \"KECCAK256\",\n            OpCode::ADDRESS => \"ADDRESS\",\n            OpCode::BALANCE => \"BALANCE\",\n            OpCode::ORIGIN => \"ORIGIN\",\n            OpCode::CALLER => \"CALLER\",\n            OpCode::CALLVALUE => \"CALLVALUE\",\n            OpCode::CALLDATALOAD => \"CALLDATALOAD\",\n            OpCode::CALLDATASIZE => \"CALLDATASIZE\",\n            OpCode::CALLDATACOPY => \"CALLDATACOPY\",\n            OpCode::CODESIZE => \"CODESIZE\",\n            OpCode::CODECOPY => \"CODECOPY\",\n            OpCode::GASPRICE => \"GASPRICE\",\n            OpCode::EXTCODESIZE => \"EXTCODESIZE\",\n            OpCode::EXTCODECOPY => \"EXTCODECOPY\",\n            OpCode::RETURNDATASIZE => \"RETURNDATASIZE\",\n            OpCode::RETURNDATACOPY => \"RETURNDATACOPY\",\n            OpCode::EXTCODEHASH => \"EXTCODEHASH\",\n            OpCode::BLOCKHASH => \"BLOCKHASH\",\n            OpCode::COINBASE => \"COINBASE\",\n            OpCode::TIMESTAMP => \"TIMESTAMP\",\n            OpCode::NUMBER => \"NUMBER\",\n            OpCode::DIFFICULTY => \"DIFFICULTY\",\n            OpCode::GASLIMIT => \"GASLIMIT\",\n            OpCode::CHAINID => \"CHAINID\",\n            OpCode::SELFBALANCE => \"SELFBALANCE\",\n            OpCode::BASEFEE => \"BASEFEE\",\n            OpCode::POP => \"POP\",\n            OpCode::MLOAD => \"MLOAD\",\n            OpCode::MSTORE => \"MSTORE\",\n            OpCode::MSTORE8 => \"MSTORE8\",\n            OpCode::SLOAD => \"SLOAD\",\n            OpCode::SSTORE => \"SSTORE\",\n            OpCode::JUMP => \"JUMP\",\n            OpCode::JUMPI => \"JUMPI\",\n            OpCode::PC => \"PC\",\n            OpCode::MSIZE => \"MSIZE\",\n            OpCode::GAS => \"GAS\",\n            OpCode::JUMPDEST => \"JUMPDEST\",\n            OpCode::PUSH1 => \"PUSH1\",\n            OpCode::PUSH2 => \"PUSH2\",\n            OpCode::PUSH3 => \"PUSH3\",\n            OpCode::PUSH4 => \"PUSH4\",\n            OpCode::PUSH5 => \"PUSH5\",\n            OpCode::PUSH6 => \"PUSH6\",\n            OpCode::PUSH7 => \"PUSH7\",\n            OpCode::PUSH8 => \"PUSH8\",\n            OpCode::PUSH9 => \"PUSH9\",\n            OpCode::PUSH10 => \"PUSH10\",\n            OpCode::PUSH11 => \"PUSH11\",\n            OpCode::PUSH12 => \"PUSH12\",\n            OpCode::PUSH13 => \"PUSH13\",\n            OpCode::PUSH14 => \"PUSH14\",\n            OpCode::PUSH15 => \"PUSH15\",\n            OpCode::PUSH16 => \"PUSH16\",\n            OpCode::PUSH17 => \"PUSH17\",\n            OpCode::PUSH18 => \"PUSH18\",\n            OpCode::PUSH19 => \"PUSH19\",\n            OpCode::PUSH20 => \"PUSH20\",\n            OpCode::PUSH21 => \"PUSH21\",\n            OpCode::PUSH22 => \"PUSH22\",\n            OpCode::PUSH23 => \"PUSH23\",\n            OpCode::PUSH24 => \"PUSH24\",\n            OpCode::PUSH25 => \"PUSH25\",\n            OpCode::PUSH26 => \"PUSH26\",\n            OpCode::PUSH27 => \"PUSH27\",\n            OpCode::PUSH28 => \"PUSH28\",\n            OpCode::PUSH29 => \"PUSH29\",\n            OpCode::PUSH30 => \"PUSH30\",\n            OpCode::PUSH31 => \"PUSH31\",\n            OpCode::PUSH32 => \"PUSH32\",\n            OpCode::DUP1 => \"DUP1\",\n            OpCode::DUP2 => \"DUP2\",\n            OpCode::DUP3 => \"DUP3\",\n            OpCode::DUP4 => \"DUP4\",\n            OpCode::DUP5 => \"DUP5\",\n            OpCode::DUP6 => \"DUP6\",\n            OpCode::DUP7 => \"DUP7\",\n            OpCode::DUP8 => \"DUP8\",\n            OpCode::DUP9 => \"DUP9\",\n            OpCode::DUP10 => \"DUP10\",\n            OpCode::DUP11 => \"DUP11\",\n            OpCode::DUP12 => \"DUP12\",\n            OpCode::DUP13 => \"DUP13\",\n            OpCode::DUP14 => \"DUP14\",\n            OpCode::DUP15 => \"DUP15\",\n            OpCode::DUP16 => \"DUP16\",\n            OpCode::SWAP1 => \"SWAP1\",\n            OpCode::SWAP2 => \"SWAP2\",\n            OpCode::SWAP3 => \"SWAP3\",\n            OpCode::SWAP4 => \"SWAP4\",\n            OpCode::SWAP5 => \"SWAP5\",\n            OpCode::SWAP6 => \"SWAP6\",\n            OpCode::SWAP7 => \"SWAP7\",\n            OpCode::SWAP8 => \"SWAP8\",\n            OpCode::SWAP9 => \"SWAP9\",\n            OpCode::SWAP10 => \"SWAP10\",\n            OpCode::SWAP11 => \"SWAP11\",\n            OpCode::SWAP12 => \"SWAP12\",\n            OpCode::SWAP13 => \"SWAP13\",\n            OpCode::SWAP14 => \"SWAP14\",\n            OpCode::SWAP15 => \"SWAP15\",\n            OpCode::SWAP16 => \"SWAP16\",\n            OpCode::LOG0 => \"LOG0\",\n            OpCode::LOG1 => \"LOG1\",\n            OpCode::LOG2 => \"LOG2\",\n            OpCode::LOG3 => \"LOG3\",\n            OpCode::LOG4 => \"LOG4\",\n            OpCode::CREATE => \"CREATE\",\n            OpCode::CALL => \"CALL\",\n            OpCode::CALLCODE => \"CALLCODE\",\n            OpCode::RETURN => \"RETURN\",\n            OpCode::DELEGATECALL => \"DELEGATECALL\",\n            OpCode::CREATE2 => \"CREATE2\",\n            OpCode::STATICCALL => \"STATICCALL\",\n            OpCode::REVERT => \"REVERT\",\n            OpCode::INVALID => \"INVALID\",\n            OpCode::SELFDESTRUCT => \"SELFDESTRUCT\",\n            _ => \"UNDEFINED\",\n        }\n    }\n\n    pub fn push_size(self) -> Option<u8> {\n        (self.to_u8() >= OpCode::PUSH1.to_u8() && self.to_u8() <= OpCode::PUSH32.to_u8())\n            .then(|| self.to_u8() - OpCode::PUSH1.to_u8() + 1)\n    }\n}\n\nimpl Display for OpCode {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let name = self.name();\n\n        let n = if name == \"UNDEFINED\" {\n            Cow::Owned(format!(\"UNDEFINED(0x{:02x})\", self.0))\n        } else {\n            Cow::Borrowed(name)\n        };\n        write!(f, \"{}\", n)\n    }\n}\n"
  },
  {
    "path": "src/state.rs",
    "content": "use crate::common::{Message, Revision};\nuse arrayvec::ArrayVec;\nuse bytes::Bytes;\nuse ethereum_types::U256;\nuse getset::{Getters, MutGetters};\nuse serde::Serialize;\n\nconst SIZE: usize = 1024;\n\n/// EVM stack.\n#[derive(Clone, Debug, Default, Serialize)]\npub struct Stack(pub ArrayVec<U256, SIZE>);\n\nimpl Stack {\n    pub const fn limit() -> usize {\n        SIZE\n    }\n\n    fn get_pos(&self, pos: usize) -> usize {\n        self.len() - 1 - pos\n    }\n\n    pub fn get(&self, pos: usize) -> &U256 {\n        &self.0[self.get_pos(pos)]\n    }\n\n    pub fn get_mut(&mut self, pos: usize) -> &mut U256 {\n        let pos = self.get_pos(pos);\n        &mut self.0[pos]\n    }\n\n    pub fn len(&self) -> usize {\n        self.0.len()\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    pub fn push(&mut self, v: U256) {\n        unsafe { self.0.push_unchecked(v) }\n    }\n\n    pub fn pop(&mut self) -> U256 {\n        self.0.pop().expect(\"underflow\")\n    }\n\n    pub fn swap_top(&mut self, pos: usize) {\n        let top = self.0.len() - 1;\n        let pos = self.get_pos(pos);\n        self.0.swap(top, pos);\n    }\n}\n\npub type Memory = Vec<u8>;\n\n/// EVM execution state.\n#[derive(Clone, Debug, Getters, MutGetters)]\npub struct ExecutionState {\n    #[getset(get = \"pub\", get_mut = \"pub\")]\n    pub(crate) gas_left: i64,\n    #[getset(get = \"pub\", get_mut = \"pub\")]\n    pub(crate) stack: Stack,\n    #[getset(get = \"pub\", get_mut = \"pub\")]\n    pub(crate) memory: Memory,\n    pub(crate) message: Message,\n    pub(crate) evm_revision: Revision,\n    #[getset(get = \"pub\", get_mut = \"pub\")]\n    pub(crate) return_data: Bytes,\n    pub(crate) output_data: Bytes,\n}\n\nimpl ExecutionState {\n    pub fn new(message: Message, evm_revision: Revision) -> Self {\n        Self {\n            gas_left: message.gas,\n            stack: Default::default(),\n            memory: Memory::with_capacity(4 * 1024),\n            message,\n            evm_revision,\n            return_data: Default::default(),\n            output_data: Bytes::new(),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn stack() {\n        let mut stack = Stack::default();\n\n        let items = [0xde, 0xad, 0xbe, 0xef];\n\n        for (i, item) in items.iter().copied().enumerate() {\n            stack.push(item.into());\n            assert_eq!(stack.len(), i + 1);\n        }\n\n        assert_eq!(*stack.get(2), 0xad.into());\n\n        assert_eq!(stack.pop(), 0xef.into());\n\n        assert_eq!(*stack.get(2), 0xde.into());\n    }\n}\n"
  },
  {
    "path": "src/tracing/mod.rs",
    "content": "use super::*;\nuse crate::state::*;\nuse serde::Serialize;\n\n/// Passed into execution context to collect metrics.\npub trait Tracer {\n    #[doc(hidden)]\n    const DUMMY: bool = false;\n\n    /// Called when execution starts.\n    fn notify_execution_start(&mut self, revision: Revision, message: Message, code: Bytes);\n    /// Called on each instruction.\n    fn notify_instruction_start(&mut self, pc: usize, opcode: OpCode, state: &ExecutionState);\n    /// Called when execution ends.\n    fn notify_execution_end(&mut self, output: &Output);\n}\n\n/// Tracer which does nothing.\npub struct NoopTracer;\n\nimpl Tracer for NoopTracer {\n    const DUMMY: bool = true;\n\n    fn notify_execution_start(&mut self, _: Revision, _: Message, _: Bytes) {}\n\n    fn notify_instruction_start(&mut self, _: usize, _: OpCode, _: &ExecutionState) {}\n\n    fn notify_execution_end(&mut self, _: &Output) {}\n}\n\n#[derive(Serialize)]\nstruct ExecutionStart {\n    pub depth: i32,\n    pub rev: Revision,\n    #[serde(rename = \"static\")]\n    pub is_static: bool,\n}\n\n#[derive(Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub(crate) struct InstructionStart {\n    pub pc: usize,\n    pub op: u8,\n    pub op_name: &'static str,\n    pub gas: i64,\n    pub stack: Stack,\n    pub memory_size: usize,\n}\n\n#[derive(Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub(crate) struct ExecutionEnd {\n    pub error: Option<String>,\n    pub gas: i64,\n    pub gas_used: i64,\n    pub output: String,\n}\n\nstruct TracerContext {\n    message: Message,\n    code: Bytes,\n}\n\n/// Tracer which prints to stdout.\n#[derive(Default)]\npub struct StdoutTracer {\n    execution_stack: Vec<TracerContext>,\n}\n\nimpl Tracer for StdoutTracer {\n    fn notify_execution_start(&mut self, revision: Revision, message: Message, code: Bytes) {\n        println!(\n            \"{}\",\n            serde_json::to_string(&ExecutionStart {\n                depth: message.depth,\n                rev: revision,\n                is_static: message.is_static,\n            })\n            .unwrap()\n        );\n        self.execution_stack.push(TracerContext { message, code });\n    }\n\n    fn notify_instruction_start(&mut self, pc: usize, _: OpCode, state: &ExecutionState) {\n        let context = self.execution_stack.last().unwrap();\n        let opcode = OpCode(context.code[pc]);\n        println!(\n            \"{}\",\n            serde_json::to_string(&InstructionStart {\n                pc,\n                op: opcode.0,\n                op_name: opcode.name(),\n                gas: state.gas_left,\n                stack: state.stack.clone(),\n                memory_size: state.memory.len()\n            })\n            .unwrap()\n        )\n    }\n\n    fn notify_execution_end(&mut self, output: &Output) {\n        let context = self.execution_stack.pop().unwrap();\n        let error = match &output.status_code {\n            StatusCode::Success => None,\n            other => Some(other.to_string()),\n        };\n        let (gas_left, gas_used) = if error.is_none() {\n            (output.gas_left, context.message.gas - output.gas_left)\n        } else {\n            (0, context.message.gas)\n        };\n\n        println!(\n            \"{}\",\n            serde_json::to_string(&ExecutionEnd {\n                error,\n                gas: gas_left,\n                gas_used,\n                output: hex::encode(&output.output_data),\n            })\n            .unwrap()\n        )\n    }\n}\n"
  },
  {
    "path": "src/util/bytecode.rs",
    "content": "use crate::opcode::*;\nuse core::iter::repeat;\nuse ethereum_types::U256;\nuse std::ops::{Add, Mul};\n\n/// EVM bytecode builder.\n#[derive(Clone, Debug, PartialEq)]\npub struct Bytecode {\n    inner: Vec<u8>,\n}\n\nimpl Bytecode {\n    pub const fn new() -> Self {\n        Self { inner: Vec::new() }\n    }\n\n    pub fn append(mut self, b: impl IntoIterator<Item = u8>) -> Self {\n        self.inner.append(&mut b.into_iter().collect::<Vec<_>>());\n        self\n    }\n\n    pub fn append_bc(mut self, b: impl Into<Self>) -> Self {\n        self.inner.append(&mut b.into().build());\n        self\n    }\n\n    pub fn repeat(mut self, n: usize) -> Self {\n        self.inner = repeat(self.inner.into_iter()).take(n).flatten().collect();\n        self\n    }\n\n    pub fn pushv(self, value: impl Into<U256>) -> Self {\n        let value = value.into();\n        let b = <[u8; 32]>::from(value)\n            .iter()\n            .skip_while(|&&v| v == 0)\n            .copied()\n            .collect::<Vec<_>>();\n\n        self.pushb(b)\n    }\n\n    pub fn pushb(mut self, b: impl IntoIterator<Item = u8>) -> Self {\n        let mut b = b.into_iter().collect::<Vec<_>>();\n\n        if b.is_empty() {\n            b.push(0);\n        }\n\n        self.inner\n            .extend_from_slice(&[(b.len() + OpCode::PUSH1.to_usize() - 1) as u8]);\n        self.inner.append(&mut b);\n\n        self\n    }\n\n    pub fn opcode(mut self, opcode: OpCode) -> Self {\n        self.inner.push(opcode.to_u8());\n        self\n    }\n\n    pub fn ret(mut self, index: impl Into<U256>, size: impl Into<U256>) -> Self {\n        self = self.pushv(size);\n        self = self.pushv(index);\n        self = self.opcode(OpCode::RETURN);\n        self\n    }\n\n    pub fn revert(mut self, index: impl Into<U256>, size: impl Into<U256>) -> Self {\n        self = self.pushv(index);\n        self = self.pushv(size);\n        self = self.opcode(OpCode::REVERT);\n        self\n    }\n\n    pub fn mstore(mut self, index: impl Into<U256>) -> Self {\n        self = self.pushv(index);\n        self = self.opcode(OpCode::MSTORE);\n        self\n    }\n\n    pub fn mstore_value(mut self, index: impl Into<U256>, value: impl Into<U256>) -> Self {\n        self = self.pushv(value);\n        self = self.pushv(index);\n        self = self.opcode(OpCode::MSTORE);\n        self\n    }\n\n    pub fn mstore8(mut self, index: impl Into<U256>) -> Self {\n        self = self.pushv(index);\n        self = self.opcode(OpCode::MSTORE8);\n        self\n    }\n\n    pub fn mstore8_value(mut self, index: impl Into<U256>, value: impl Into<U256>) -> Self {\n        self = self.pushv(value);\n        self = self.pushv(index);\n        self = self.opcode(OpCode::MSTORE8);\n        self\n    }\n\n    pub fn ret_top(self) -> Self {\n        self.mstore(0).ret(0, 0x20)\n    }\n\n    pub fn jump(self, target: impl Into<U256>) -> Self {\n        self.pushv(target).opcode(OpCode::JUMP)\n    }\n\n    pub fn jumpi(self, target: impl Into<Bytecode>, condition: impl Into<Bytecode>) -> Self {\n        self.append(condition.into().build())\n            .append(target.into().build())\n            .opcode(OpCode::JUMPI)\n    }\n\n    pub fn sstore(self, index: impl Into<U256>, value: impl Into<U256>) -> Self {\n        self.pushv(value).pushv(index).opcode(OpCode::SSTORE)\n    }\n\n    pub fn sload(self, index: impl Into<U256>) -> Self {\n        self.pushv(index).opcode(OpCode::SLOAD)\n    }\n\n    pub fn build(self) -> Vec<u8> {\n        self.inner\n    }\n\n    pub fn len(&self) -> usize {\n        self.inner.len()\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n}\n\nimpl From<U256> for Bytecode {\n    fn from(value: U256) -> Self {\n        Self::new().pushv(value)\n    }\n}\n\nimpl From<OpCode> for Bytecode {\n    fn from(opcode: OpCode) -> Self {\n        Self::new().opcode(opcode)\n    }\n}\n\nimpl<const N: usize> From<[u8; N]> for Bytecode {\n    fn from(inner: [u8; N]) -> Self {\n        Self {\n            inner: Vec::from(&inner as &[u8]),\n        }\n    }\n}\n\nimpl From<Vec<u8>> for Bytecode {\n    fn from(inner: Vec<u8>) -> Self {\n        Self { inner }\n    }\n}\n\nimpl AsRef<[u8]> for Bytecode {\n    fn as_ref(&self) -> &[u8] {\n        &self.inner\n    }\n}\n\nimpl IntoIterator for Bytecode {\n    type Item = u8;\n    type IntoIter = <Vec<u8> as IntoIterator>::IntoIter;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.inner.into_iter()\n    }\n}\n\nimpl Mul<Bytecode> for usize {\n    type Output = Bytecode;\n\n    fn mul(self, rhs: Bytecode) -> Self::Output {\n        repeat(rhs)\n            .take(self)\n            .fold(Bytecode::new(), |acc, b| acc.append_bc(b))\n    }\n}\n\nimpl Mul<OpCode> for usize {\n    type Output = Bytecode;\n\n    fn mul(self, rhs: OpCode) -> Self::Output {\n        self.mul(Bytecode::from(rhs))\n    }\n}\n\nimpl<T: Into<Bytecode>> Add<T> for Bytecode {\n    type Output = Bytecode;\n\n    fn add(self, rhs: T) -> Self::Output {\n        self.append_bc(rhs)\n    }\n}\n\npub struct CallInstruction {\n    op: OpCode,\n    address: U256,\n    gas: U256,\n    value: U256,\n    input: U256,\n    input_size: U256,\n    output: U256,\n    output_size: U256,\n}\n\nimpl CallInstruction {\n    fn new(op: OpCode, address: impl Into<U256>) -> Self {\n        Self {\n            op,\n            address: address.into(),\n            gas: 0.into(),\n            value: 0.into(),\n            input: 0.into(),\n            input_size: 0.into(),\n            output: 0.into(),\n            output_size: 0.into(),\n        }\n    }\n\n    pub fn delegatecall(address: impl Into<U256>) -> Self {\n        Self::new(OpCode::DELEGATECALL, address)\n    }\n\n    pub fn staticcall(address: impl Into<U256>) -> Self {\n        Self::new(OpCode::STATICCALL, address)\n    }\n\n    pub fn call(address: impl Into<U256>) -> Self {\n        Self::new(OpCode::CALL, address)\n    }\n\n    pub fn callcode(address: impl Into<U256>) -> Self {\n        Self::new(OpCode::CALLCODE, address)\n    }\n\n    pub fn opcode(&self) -> OpCode {\n        self.op\n    }\n\n    pub fn gas(mut self, gas: impl Into<U256>) -> Self {\n        self.gas = gas.into();\n        self\n    }\n\n    pub fn value(mut self, value: impl Into<U256>) -> Self {\n        self.value = value.into();\n        self\n    }\n\n    pub fn input(mut self, index: impl Into<U256>, size: impl Into<U256>) -> Self {\n        self.input = index.into();\n        self.input_size = size.into();\n        self\n    }\n\n    pub fn output(mut self, index: impl Into<U256>, size: impl Into<U256>) -> Self {\n        self.output = index.into();\n        self.output_size = size.into();\n        self\n    }\n}\n\nimpl From<CallInstruction> for Bytecode {\n    fn from(call: CallInstruction) -> Self {\n        let mut b = Bytecode::new()\n            .pushv(call.output_size)\n            .pushv(call.output)\n            .pushv(call.input_size)\n            .pushv(call.input);\n        if call.op == OpCode::CALL || call.op == OpCode::CALLCODE {\n            b = b.pushv(call.value);\n        }\n        b.pushv(call.address).pushv(call.gas).opcode(call.op)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn multiply_bytecode() {\n        assert_eq!(\n            3 * Bytecode::new().opcode(OpCode::POP),\n            Bytecode::new()\n                .opcode(OpCode::POP)\n                .opcode(OpCode::POP)\n                .opcode(OpCode::POP)\n        )\n    }\n}\n"
  },
  {
    "path": "src/util/mocked_host.rs",
    "content": "use crate::{host::*, *};\nuse bytes::Bytes;\nuse ethereum_types::*;\nuse hex_literal::hex;\nuse parking_lot::Mutex;\nuse std::{cmp::min, collections::HashMap};\n\n/// LOG record.\n#[derive(Clone, Debug, PartialEq)]\npub struct LogRecord {\n    /// The address of the account which created the log.\n    pub creator: Address,\n\n    /// The data attached to the log.\n    pub data: Bytes,\n\n    /// The log topics.\n    pub topics: Vec<U256>,\n}\n\n#[derive(Clone, Debug, PartialEq)]\npub struct SelfdestructRecord {\n    /// The address of the account which has self-destructed.\n    pub selfdestructed: Address,\n\n    /// The address of the beneficiary account.\n    pub beneficiary: Address,\n}\n\n#[derive(Clone, Debug, Default)]\npub struct StorageValue {\n    pub value: U256,\n    pub dirty: bool,\n    pub access_status: AccessStatus,\n}\n\n#[derive(Clone, Debug, Default)]\npub struct Account {\n    /// The account nonce.\n    pub nonce: u64,\n    /// The account code.\n    pub code: Bytes,\n    /// The code hash. Can be a value not related to the actual code.\n    pub code_hash: U256,\n    /// The account balance.\n    pub balance: U256,\n    /// The account storage map.\n    pub storage: HashMap<U256, StorageValue>,\n}\n\nconst MAX_RECORDED_ACCOUNT_ACCESSES: usize = 200;\nconst MAX_RECORDED_CALLS: usize = 100;\n\n#[derive(Clone, Debug, Default)]\npub struct Records {\n    /// The copy of call inputs for the recorded_calls record.\n    pub call_inputs: Vec<Bytes>,\n\n    pub blockhashes: Vec<u64>,\n    pub account_accesses: Vec<Address>,\n    pub calls: Vec<Message>,\n    pub logs: Vec<LogRecord>,\n    pub selfdestructs: Vec<SelfdestructRecord>,\n}\n\n#[derive(Debug)]\npub struct MockedHost {\n    pub accounts: HashMap<Address, Account>,\n    pub tx_context: TxContext,\n    pub block_hash: U256,\n    pub call_result: Output,\n    pub recorded: Mutex<Records>,\n}\n\nimpl Clone for MockedHost {\n    fn clone(&self) -> Self {\n        Self {\n            accounts: self.accounts.clone(),\n            tx_context: self.tx_context.clone(),\n            block_hash: self.block_hash,\n            call_result: self.call_result.clone(),\n            recorded: Mutex::new(self.recorded.lock().clone()),\n        }\n    }\n}\n\nimpl Default for MockedHost {\n    fn default() -> Self {\n        Self {\n            accounts: Default::default(),\n            tx_context: TxContext {\n                tx_gas_price: U256::zero(),\n                tx_origin: Address::zero(),\n                block_coinbase: Address::zero(),\n                block_number: 0,\n                block_timestamp: 0,\n                block_gas_limit: 0,\n                block_difficulty: U256::zero(),\n                chain_id: U256::zero(),\n                block_base_fee: U256::zero(),\n            },\n            block_hash: U256::zero(),\n            call_result: Output {\n                status_code: StatusCode::Success,\n                gas_left: 0,\n                output_data: Bytes::new(),\n                create_address: Some(Address::zero()),\n            },\n            recorded: Default::default(),\n        }\n    }\n}\n\nimpl Records {\n    fn record_account_access(&mut self, address: Address) {\n        if self.account_accesses.len() < MAX_RECORDED_ACCOUNT_ACCESSES {\n            self.account_accesses.push(address)\n        }\n    }\n}\n\nimpl crate::Host for MockedHost {\n    fn account_exists(&self, address: ethereum_types::Address) -> bool {\n        self.recorded.lock().record_account_access(address);\n        self.accounts.contains_key(&address)\n    }\n\n    fn get_storage(&self, address: ethereum_types::Address, key: U256) -> U256 {\n        self.recorded.lock().record_account_access(address);\n\n        self.accounts\n            .get(&address)\n            .and_then(|account| account.storage.get(&key).map(|value| value.value))\n            .unwrap_or_else(U256::zero)\n    }\n\n    fn set_storage(\n        &mut self,\n        address: ethereum_types::Address,\n        key: U256,\n        value: U256,\n    ) -> StorageStatus {\n        self.recorded.lock().record_account_access(address);\n\n        // Get the reference to the old value.\n        // This will create the account in case it was not present.\n        // This is convenient for unit testing and standalone EVM execution to preserve the\n        // storage values after the execution terminates.\n        let old = self\n            .accounts\n            .entry(address)\n            .or_default()\n            .storage\n            .entry(key)\n            .or_default();\n\n        // Follow https://eips.ethereum.org/EIPS/eip-1283 specification.\n        // WARNING! This is not complete implementation as refund is not handled here.\n\n        if old.value == value {\n            return StorageStatus::Unchanged;\n        }\n\n        let status = if !old.dirty {\n            old.dirty = true;\n            if old.value.is_zero() {\n                StorageStatus::Added\n            } else if !value.is_zero() {\n                StorageStatus::Modified\n            } else {\n                StorageStatus::Deleted\n            }\n        } else {\n            StorageStatus::ModifiedAgain\n        };\n\n        old.value = value;\n\n        status\n    }\n\n    fn get_balance(&self, address: ethereum_types::Address) -> ethereum_types::U256 {\n        self.recorded.lock().record_account_access(address);\n\n        self.accounts\n            .get(&address)\n            .map(|acc| acc.balance)\n            .unwrap_or_else(U256::zero)\n    }\n\n    fn get_code_size(&self, address: ethereum_types::Address) -> ethereum_types::U256 {\n        self.recorded.lock().record_account_access(address);\n\n        self.accounts\n            .get(&address)\n            .map(|acc| acc.code.len().into())\n            .unwrap_or_else(U256::zero)\n    }\n\n    fn get_code_hash(&self, address: ethereum_types::Address) -> U256 {\n        self.recorded.lock().record_account_access(address);\n\n        self.accounts\n            .get(&address)\n            .map(|acc| acc.code_hash)\n            .unwrap_or_else(U256::zero)\n    }\n\n    fn copy_code(&self, address: Address, code_offset: usize, buffer: &mut [u8]) -> usize {\n        self.recorded.lock().record_account_access(address);\n\n        self.accounts\n            .get(&address)\n            .map(|acc| {\n                let code = &acc.code;\n\n                if code_offset >= code.len() {\n                    return 0;\n                }\n\n                let n = min(buffer.len(), code.len() - code_offset);\n\n                buffer[..n].copy_from_slice(&code[code_offset..code_offset + n]);\n\n                n\n            })\n            .unwrap_or(0)\n    }\n\n    fn selfdestruct(\n        &mut self,\n        address: ethereum_types::Address,\n        beneficiary: ethereum_types::Address,\n    ) {\n        let mut r = self.recorded.lock();\n\n        r.record_account_access(address);\n        r.selfdestructs.push(SelfdestructRecord {\n            selfdestructed: address,\n            beneficiary,\n        });\n    }\n\n    fn call(&mut self, msg: &Message) -> Output {\n        let mut r = self.recorded.lock();\n\n        r.record_account_access(msg.recipient);\n\n        if r.calls.len() < MAX_RECORDED_CALLS {\n            r.calls.push(msg.clone());\n            let call_msg = msg;\n            if !call_msg.input_data.is_empty() {\n                r.call_inputs.push(call_msg.input_data.clone());\n            }\n        }\n        self.call_result.clone()\n    }\n\n    fn get_tx_context(&self) -> TxContext {\n        self.tx_context.clone()\n    }\n\n    fn get_block_hash(&self, block_number: u64) -> U256 {\n        self.recorded.lock().blockhashes.push(block_number);\n        self.block_hash\n    }\n\n    fn emit_log(&mut self, address: ethereum_types::Address, data: &[u8], topics: &[U256]) {\n        self.recorded.lock().logs.push(LogRecord {\n            creator: address,\n            data: data.to_vec().into(),\n            topics: topics.to_vec(),\n        });\n    }\n\n    fn access_account(&mut self, address: ethereum_types::Address) -> AccessStatus {\n        let mut r = self.recorded.lock();\n\n        // Check if the address have been already accessed.\n        let already_accessed = r.account_accesses.iter().any(|&a| a == address);\n\n        r.record_account_access(address);\n\n        if address.0 >= hex!(\"0000000000000000000000000000000000000001\")\n            && address.0 <= hex!(\"0000000000000000000000000000000000000009\")\n        {\n            return AccessStatus::Warm;\n        }\n\n        if already_accessed {\n            AccessStatus::Warm\n        } else {\n            AccessStatus::Cold\n        }\n    }\n\n    fn access_storage(&mut self, address: ethereum_types::Address, key: U256) -> AccessStatus {\n        let value = self\n            .accounts\n            .entry(address)\n            .or_default()\n            .storage\n            .entry(key)\n            .or_default();\n        let access_status = value.access_status;\n        value.access_status = AccessStatus::Warm;\n        access_status\n    }\n}\n"
  },
  {
    "path": "src/util/mod.rs",
    "content": "mod bytecode;\npub mod mocked_host;\nmod tester;\n\npub use bytecode::*;\npub use tester::*;\n"
  },
  {
    "path": "src/util/tester.rs",
    "content": "use crate::{\n    tracing::*,\n    util::{mocked_host::*, *},\n    *,\n};\nuse bytes::Bytes;\nuse educe::Educe;\nuse ethereum_types::{Address, U256};\nuse std::sync::Arc;\n\nfn exec(\n    host: &mut MockedHost,\n    revision: Revision,\n    message: Message,\n    code: Vec<u8>,\n    collect_traces: bool,\n) -> Output {\n    // Add EIP-2929 tweak.\n    if revision >= Revision::Berlin {\n        host.access_account(message.sender);\n        host.access_account(message.recipient);\n    }\n    let code = AnalyzedCode::analyze(code);\n\n    if collect_traces {\n        code.execute(host, &mut StdoutTracer::default(), None, message, revision)\n    } else {\n        code.execute(host, &mut NoopTracer, None, message, revision)\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\nenum GasCheck {\n    Used(i64),\n    Left(i64),\n}\n\n/// Tester that executes EVM bytecode with `MockedHost` context and runs set checks.\n#[derive(Clone, Educe)]\n#[educe(Debug)]\n#[must_use]\npub struct EvmTester {\n    host: MockedHost,\n    #[educe(Debug(ignore))]\n    apply_host_fns: Vec<Arc<dyn Fn(&mut MockedHost, &Message) + 'static>>,\n    #[educe(Debug(ignore))]\n    inspect_output_fn: Arc<dyn Fn(&[u8]) + 'static>,\n    #[educe(Debug(ignore))]\n    inspect_host_fn: Arc<dyn Fn(&MockedHost, &Message) + 'static>,\n    #[educe(Debug(ignore))]\n    inspect_fn: Arc<dyn Fn(&MockedHost, &Message, &[u8]) + 'static>,\n    revision: Revision,\n    message: Message,\n    code: Vec<u8>,\n    gas_check: Option<GasCheck>,\n    expected_status_codes: Option<Vec<StatusCode>>,\n    expected_output_data: Option<Vec<u8>>,\n    collect_traces: bool,\n}\n\nimpl Default for EvmTester {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl EvmTester {\n    /// Create new `EvmTester`.\n    pub fn new() -> Self {\n        Self {\n            host: MockedHost::default(),\n            apply_host_fns: vec![],\n            inspect_output_fn: Arc::new(|_| ()),\n            inspect_host_fn: Arc::new(|_, _| ()),\n            inspect_fn: Arc::new(|_, _, _| ()),\n            revision: Revision::Byzantium,\n            message: Message {\n                kind: CallKind::Call,\n                is_static: false,\n                depth: 0,\n                gas: i64::MAX,\n                recipient: Address::zero(),\n                code_address: Address::zero(),\n                sender: Address::zero(),\n                input_data: Bytes::new(),\n                value: 0.into(),\n            },\n            code: Vec::new(),\n            gas_check: None,\n            expected_status_codes: None,\n            expected_output_data: None,\n            collect_traces: false,\n        }\n    }\n\n    /// Set code to be executed.\n    pub fn code(mut self, code: impl Into<Bytecode>) -> Self {\n        self.code = code.into().build();\n        self\n    }\n\n    /// Queue function that will modify the host before execution.\n    pub fn apply_host_fn(mut self, host_fn: impl Fn(&mut MockedHost, &Message) + 'static) -> Self {\n        self.apply_host_fns.push(Arc::new(host_fn));\n        self\n    }\n\n    /// Set EVM revision for this tester.\n    pub fn revision(mut self, revision: Revision) -> Self {\n        self.revision = revision;\n        self\n    }\n\n    /// Set message depth.\n    pub fn depth(mut self, depth: u16) -> Self {\n        self.message.depth = depth.into();\n        self\n    }\n\n    /// Set provided gas.\n    pub fn gas(mut self, gas: i64) -> Self {\n        self.message.gas = gas;\n        self\n    }\n\n    /// Set static message flag.\n    pub fn set_static(mut self, is_static: bool) -> Self {\n        self.message.is_static = is_static;\n        self\n    }\n\n    /// Set message destination.\n    pub fn destination(mut self, destination: impl Into<Address>) -> Self {\n        self.message.recipient = destination.into();\n        self\n    }\n\n    /// Set message sender.\n    pub fn sender(mut self, sender: impl Into<Address>) -> Self {\n        self.message.sender = sender.into();\n        self\n    }\n\n    /// Set message sender.\n    pub fn value(mut self, value: impl Into<U256>) -> Self {\n        self.message.value = value.into();\n        self\n    }\n\n    /// Check how much gas will be used. Mutually exclusive with `EvmTester::gas_left`.\n    pub fn gas_used(mut self, expected_gas_used: i64) -> Self {\n        self.gas_check = Some(GasCheck::Used(expected_gas_used));\n        self\n    }\n\n    /// Check how much gas will be left after execution. Mutually exclusive with `EvmTester::gas_used`.\n    pub fn gas_left(mut self, expected_gas_left: i64) -> Self {\n        self.gas_check = Some(GasCheck::Left(expected_gas_left));\n        self\n    }\n\n    /// Set provided input data.\n    pub fn input(mut self, input: impl Into<Bytes>) -> Self {\n        self.message.input_data = input.into();\n        self\n    }\n\n    /// Check returned status.\n    pub fn status(mut self, expected_status_code: StatusCode) -> Self {\n        self.expected_status_codes = Some(vec![expected_status_code]);\n        self\n    }\n\n    /// Check returned status to be one of these.\n    pub fn status_one_of<const N: usize>(mut self, expected_status_code: [StatusCode; N]) -> Self {\n        self.expected_status_codes = Some(expected_status_code.to_vec());\n        self\n    }\n\n    /// Check output to be equal to provided integer.\n    pub fn output_value(mut self, expected_output_data: impl Into<U256>) -> Self {\n        let mut data = [0; 32];\n        expected_output_data.into().to_big_endian(&mut data);\n        self.expected_output_data = Some(data.to_vec());\n        self\n    }\n\n    /// Check output data to be equal to provided byte string.\n    pub fn output_data(mut self, expected_output_data: impl Into<Vec<u8>>) -> Self {\n        self.expected_output_data = Some(expected_output_data.into());\n        self\n    }\n\n    /// Inspect output with provided function.\n    pub fn inspect_output(mut self, inspect_output_fn: impl Fn(&[u8]) + 'static) -> Self {\n        self.inspect_output_fn = Arc::new(inspect_output_fn);\n        self\n    }\n\n    /// Inspect host with provided function.\n    pub fn inspect_host(mut self, f: impl Fn(&MockedHost, &Message) + 'static) -> Self {\n        self.inspect_host_fn = Arc::new(f);\n        self\n    }\n\n    /// Inspect host and output with provided function.\n    pub fn inspect(mut self, f: impl Fn(&MockedHost, &Message, &[u8]) + 'static) -> Self {\n        self.inspect_fn = Arc::new(f);\n        self\n    }\n\n    pub fn collect_traces(mut self, doit: bool) -> Self {\n        self.collect_traces = doit;\n        self\n    }\n\n    /// Execute provided code, run checks and return bytecode returned by EVM.\n    pub fn check_and_get_result(self) -> Output {\n        if self.collect_traces {\n            println!(\"Executing code: {}\", hex::encode(&self.code));\n        }\n        let mut host = self.host;\n        for f in self.apply_host_fns {\n            (f)(&mut host, &self.message);\n        }\n        let output = exec(\n            &mut host,\n            self.revision,\n            self.message.clone(),\n            self.code,\n            self.collect_traces,\n        );\n\n        if let Some(status_codes) = self.expected_status_codes {\n            assert!(\n                status_codes.iter().any(|s| *s == output.status_code),\n                \"Status code mismatch: {}, but must be one of {:?}\",\n                output.status_code,\n                status_codes\n            );\n        }\n\n        if let Some(gas_check) = self.gas_check {\n            match gas_check {\n                GasCheck::Used(used) => assert_eq!(self.message.gas - output.gas_left, used),\n                GasCheck::Left(left) => assert_eq!(output.gas_left, left),\n            }\n        }\n\n        if let Some(expected_data) = &self.expected_output_data {\n            assert_eq!(&*output.output_data, expected_data);\n        }\n\n        (self.inspect_output_fn)(&*output.output_data);\n        (self.inspect_host_fn)(&host, &self.message);\n        (self.inspect_fn)(&host, &self.message, &*output.output_data);\n\n        output\n    }\n\n    /// Execute provided code and run checks.\n    pub fn check(self) {\n        self.check_and_get_result();\n    }\n}\n"
  },
  {
    "path": "tests/basefee.rs",
    "content": "use evmodin::{opcode::*, util::*, *};\n\n#[test]\nfn basefee_pre_london() {\n    EvmTester::new()\n        .revision(Revision::Berlin)\n        .code(Bytecode::new().opcode(OpCode::BASEFEE))\n        .status(StatusCode::UndefinedInstruction)\n        .check()\n}\n\n#[test]\nfn basefee_nominal_case() {\n    // https://eips.ethereum.org/EIPS/eip-3198#nominal-case\n    let t = EvmTester::new()\n        .revision(Revision::London)\n        .apply_host_fn(|host, _| {\n            host.tx_context.block_base_fee = 7.into();\n        });\n    t.clone()\n        .code(Bytecode::new().opcode(OpCode::BASEFEE).opcode(OpCode::STOP))\n        .status(StatusCode::Success)\n        .gas_used(2)\n        .check();\n\n    t.code(Bytecode::new().opcode(OpCode::BASEFEE).ret_top())\n        .status(StatusCode::Success)\n        .gas_used(17)\n        .output_value(7)\n        .check()\n}\n"
  },
  {
    "path": "tests/call.rs",
    "content": "use bytes::Bytes;\nuse core::iter::repeat_with;\nuse ethereum_types::*;\nuse evmodin::{opcode::*, util::*, *};\nuse hex_literal::hex;\n\n#[test]\nfn delegatecall() {\n    let mut value = H256::zero();\n    value.0[17] = 0xfe;\n\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\"6001600003600052\")) // m[0] = 0xffffff...\n                .append(hex!(\"600560046003600260016103e8f4\")) // DELEGATECALL(1000, 0x01, ...)\n                .append(hex!(\"60086000f3\")),\n        )\n        .apply_host_fn(|host, _| {\n            host.call_result.output_data = (&hex!(\"0a0b0c\") as &[u8]).into();\n            host.call_result.gas_left = 1;\n        })\n        .value(value.0)\n        .gas(1700)\n        .gas_used(1690)\n        .status(StatusCode::Success)\n        .output_data(hex!(\"ffffffff0a0b0cff\"))\n        .inspect_host(move |host, _| {\n            let gas_left = 1700 - 736;\n\n            let r = host.recorded.lock();\n\n            assert_eq!(r.calls.len(), 1);\n            let call_msg = r.calls.last().unwrap();\n            assert_eq!(call_msg.gas, gas_left - gas_left / 64);\n            assert_eq!(call_msg.input_data.len(), 3);\n            assert_eq!(<[u8; 32]>::from(call_msg.value)[17], 0xfe);\n        })\n        .check()\n}\n\n/// Checks if DELEGATECALL forwards the \"static\" flag.\n#[test]\nfn delegatecall_static() {\n    EvmTester::new()\n        .set_static(true)\n        .code(Bytecode::new().append_bc(CallInstruction::delegatecall(0).gas(1)))\n        .status(StatusCode::Success)\n        .gas_used(719)\n        .inspect_host(|host, _| {\n            let r = host.recorded.lock();\n\n            assert_eq!(r.calls.len(), 1);\n            let call_msg = r.calls.last().unwrap();\n            assert_eq!(call_msg.gas, 1);\n            assert!(call_msg.is_static);\n        })\n        .check()\n}\n\n#[test]\nfn delegatecall_oog_depth_limit() {\n    let t = EvmTester::new()\n        .revision(Revision::Homestead)\n        .depth(1024)\n        .code(\n            Bytecode::new()\n                .append_bc(CallInstruction::delegatecall(0).gas(16))\n                .ret_top(),\n        );\n\n    t.clone()\n        .status(StatusCode::Success)\n        .gas_used(73)\n        .output_value(0)\n        .check();\n\n    t.gas(73).status(StatusCode::OutOfGas).check();\n}\n\n#[test]\nfn create() {\n    let address = Address::zero();\n\n    EvmTester::new()\n        .apply_host_fn(move |host, _| {\n            host.accounts.entry(address).or_default().balance = 1.into();\n\n            host.call_result.output_data = (&hex!(\"0a0b0c\") as &[u8]).into();\n            host.call_result\n                .create_address\n                .get_or_insert_with(Address::zero)\n                .0[10] = 0xcc;\n            host.call_result.gas_left = 200000;\n        })\n        .gas(300000)\n        .code(hex!(\"602060006001f0600155\"))\n        .gas_used(115816)\n        .status(StatusCode::Success)\n        .inspect_host(move |host, _| {\n            let key = 1.into();\n            assert_eq!(\n                H256(host.accounts[&address].storage[&key].value.into()).0[22],\n                0xcc\n            );\n\n            let r = host.recorded.lock();\n            assert_eq!(r.calls.len(), 1);\n            assert_eq!(r.calls.last().unwrap().input_data.len(), 0x20);\n        })\n        .check()\n}\n\n#[test]\nfn create_gas() {\n    for rev in [Revision::Homestead, Revision::Tangerine] {\n        EvmTester::new()\n            .revision(rev)\n            .gas(50000)\n            .code(hex!(\"60008080f0\"))\n            .status(StatusCode::Success)\n            .gas_used(if rev == Revision::Homestead {\n                50000\n            } else {\n                49719\n            })\n            .inspect_host(move |host, _| {\n                let r = host.recorded.lock();\n                assert_eq!(r.calls.len(), 1);\n                assert_eq!(\n                    r.calls.last().unwrap().gas,\n                    if rev == Revision::Homestead {\n                        17991\n                    } else {\n                        17710\n                    }\n                );\n            })\n            .check()\n    }\n}\n\n#[test]\nfn create2() {\n    let address = Address::zero();\n    EvmTester::new()\n        .revision(Revision::Constantinople)\n        .apply_host_fn(move |host, _| {\n            host.accounts.entry(address).or_default().balance = 1.into();\n\n            host.call_result.output_data = (&hex!(\"0a0b0c\") as &[u8]).into();\n            host.call_result\n                .create_address\n                .get_or_insert_with(Address::zero)\n                .0[10] = 0xc2;\n            host.call_result.gas_left = 200000;\n        })\n        .gas(300000)\n        .code(hex!(\"605a604160006001f5600155\"))\n        .gas_used(115817)\n        .status(StatusCode::Success)\n        .inspect_host(move |host, _| {\n            let r = host.recorded.lock();\n\n            assert_eq!(r.calls.len(), 1);\n\n            let call_msg = r.calls.last().unwrap();\n            assert_eq!(call_msg.kind, CallKind::Create2 { salt: 0x5a.into() });\n            assert_eq!(call_msg.gas, 263775);\n\n            assert_eq!(\n                H256(host.accounts[&address].storage[&1.into()].value.into()).0[22],\n                0xc2\n            );\n\n            assert_eq!(call_msg.input_data.len(), 0x41);\n        })\n        .check()\n}\n\n#[test]\nfn create2_salt_cost() {\n    let t = EvmTester::new()\n        .revision(Revision::Constantinople)\n        .code(hex!(\"600060208180f5\"));\n\n    t.clone()\n        .gas(32021)\n        .status(StatusCode::Success)\n        .gas_left(0)\n        .inspect_host(|host, _| {\n            let r = host.recorded.lock();\n\n            assert_eq!(r.calls.len(), 1);\n            assert_eq!(\n                r.calls.last().unwrap().kind,\n                CallKind::Create2 { salt: U256::zero() }\n            );\n            assert_eq!(r.calls.last().unwrap().depth, 1);\n        })\n        .check();\n\n    t.gas(32021 - 1)\n        .status(StatusCode::OutOfGas)\n        .gas_left(0)\n        .inspect_host(|host, _| {\n            // No another CREATE2.\n            assert_eq!(host.recorded.lock().calls.len(), 0)\n        })\n        .check()\n}\n\n#[test]\nfn create_balance_too_low() {\n    for op in [OpCode::CREATE, OpCode::CREATE2] {\n        EvmTester::new()\n            .revision(Revision::Constantinople)\n            .apply_host_fn(|host, _| {\n                host.accounts.entry(Address::zero()).or_default().balance = 1.into();\n            })\n            .code(\n                Bytecode::new()\n                    .pushv(2)\n                    .opcode(OpCode::DUP1)\n                    .opcode(OpCode::DUP1)\n                    .opcode(OpCode::DUP1)\n                    .opcode(op)\n                    .ret_top(),\n            )\n            .status(StatusCode::Success)\n            .output_value(0)\n            .inspect_host(|host, _| {\n                assert_eq!(host.recorded.lock().calls, []);\n            })\n            .check()\n    }\n}\n\n#[test]\nfn create_failure() {\n    for op in [OpCode::CREATE, OpCode::CREATE2] {\n        let mut create_address = Address::zero();\n        create_address.0[19] = 0xce;\n        let t = EvmTester::new()\n            .apply_host_fn(move |host, _| {\n                host.call_result.create_address = Some(create_address);\n            })\n            .revision(Revision::Constantinople)\n            .code(\n                Bytecode::new()\n                    .pushv(0)\n                    .opcode(OpCode::DUP1)\n                    .opcode(OpCode::DUP1)\n                    .opcode(OpCode::DUP1)\n                    .opcode(op)\n                    .ret_top(),\n            );\n\n        t.clone()\n            .apply_host_fn(|host, _| {\n                host.call_result.status_code = StatusCode::Success;\n            })\n            .status(StatusCode::Success)\n            .output_data(H256::from(create_address).to_fixed_bytes())\n            .inspect_host(move |host, _| {\n                let r = host.recorded.lock();\n\n                assert_eq!(r.calls.len(), 1);\n                assert_eq!(\n                    r.calls.last().unwrap().kind,\n                    if op == OpCode::CREATE {\n                        CallKind::Create\n                    } else {\n                        CallKind::Create2 { salt: U256::zero() }\n                    }\n                );\n            })\n            .check();\n\n        t.clone()\n            .apply_host_fn(|host, _| {\n                host.call_result.status_code = StatusCode::Revert;\n            })\n            .status(StatusCode::Success)\n            .output_value(0)\n            .inspect_host(move |host, _| {\n                let r = host.recorded.lock();\n\n                assert_eq!(r.calls.len(), 1);\n                assert_eq!(\n                    r.calls.last().unwrap().kind,\n                    if op == OpCode::CREATE {\n                        CallKind::Create\n                    } else {\n                        CallKind::Create2 { salt: U256::zero() }\n                    }\n                );\n            })\n            .check();\n\n        t.clone()\n            .apply_host_fn(|host, _| {\n                host.call_result.status_code = StatusCode::Failure;\n            })\n            .status(StatusCode::Success)\n            .output_value(0)\n            .inspect_host(move |host, _| {\n                let r = host.recorded.lock();\n\n                assert_eq!(r.calls.len(), 1);\n                assert_eq!(\n                    r.calls.last().unwrap().kind,\n                    if op == OpCode::CREATE {\n                        CallKind::Create\n                    } else {\n                        CallKind::Create2 { salt: U256::zero() }\n                    }\n                );\n            })\n            .check();\n    }\n}\n\n#[test]\nfn call_failing_with_value() {\n    for op in [OpCode::CALL, OpCode::CALLCODE] {\n        let t = EvmTester::new()\n            .apply_host_fn(|host, _| {\n                host.accounts\n                    .entry(hex!(\"00000000000000000000000000000000000000aa\").into())\n                    .or_default();\n            })\n            .code(\n                Bytecode::new()\n                    .pushv(0xff)\n                    .pushv(0)\n                    .opcode(OpCode::DUP2)\n                    .opcode(OpCode::DUP2)\n                    .pushv(1)\n                    .pushv(0xaa)\n                    .pushv(0x8000)\n                    .opcode(op)\n                    .opcode(OpCode::POP),\n            );\n\n        // Fails on balance check.\n        t.clone()\n            .gas(12000)\n            .status(StatusCode::Success)\n            .gas_used(7447)\n            .inspect_host(|host, _| {\n                // There was no call().\n                assert_eq!(host.recorded.lock().calls, []);\n            })\n            .check();\n\n        // Fails on value transfer additional cost - minimum gas limit that triggers this condition.\n        t.clone()\n            .gas(747)\n            .status(StatusCode::OutOfGas)\n            .inspect_host(|host, _| {\n                // There was no call().\n                assert_eq!(host.recorded.lock().calls, []);\n            })\n            .check();\n\n        // Fails on value transfer additional cost - maximum gas limit that triggers this condition.\n        t.clone()\n            .gas(744 + 9000)\n            .status(StatusCode::OutOfGas)\n            .inspect_host(|host, _| {\n                // There was no call().\n                assert_eq!(host.recorded.lock().calls, []);\n            })\n            .check();\n    }\n}\n\n#[test]\nfn call_with_value() {\n    let call_sender = hex!(\"5e4d00000000000000000000000000000000d4e5\").into();\n    let call_dst = hex!(\"00000000000000000000000000000000000000aa\").into();\n\n    EvmTester::new()\n        .code(hex!(\"60ff600060ff6000600160aa618000f150\"))\n        .destination(call_sender)\n        .apply_host_fn(move |host, msg| {\n            host.accounts.entry(msg.recipient).or_default().balance = 1.into();\n            host.accounts.entry(call_dst).or_default();\n            host.call_result.gas_left = 1.into();\n        })\n        .gas(40000)\n        .gas_used(7447 + 32082)\n        .status(StatusCode::Success)\n        .inspect_host(move |host, _| {\n            let r = host.recorded.lock();\n            assert_eq!(r.calls.len(), 1);\n            let call_msg = &r.calls[0];\n            assert_eq!(call_msg.kind, CallKind::Call);\n            assert_eq!(call_msg.depth, 1);\n            assert_eq!(call_msg.gas, 32083);\n            assert_eq!(call_msg.recipient, call_dst);\n            assert_eq!(call_msg.sender, call_sender);\n        })\n        .check()\n}\n\n#[test]\nfn call_with_value_depth_limit() {\n    let mut call_dst = Address::zero();\n    call_dst.0[19] = 0xaa;\n\n    EvmTester::new()\n        .depth(1024)\n        .apply_host_fn(move |host, _| {\n            host.accounts.entry(call_dst).or_default();\n        })\n        .code(hex!(\"60ff600060ff6000600160aa618000f150\"))\n        .gas_used(7447)\n        .status(StatusCode::Success)\n        .inspect_host(|host, _| {\n            assert_eq!(host.recorded.lock().calls, []);\n        })\n        .check()\n}\n\n#[test]\nfn call_depth_limit() {\n    for op in [\n        OpCode::CALL,\n        OpCode::CALLCODE,\n        OpCode::DELEGATECALL,\n        OpCode::STATICCALL,\n        OpCode::CREATE,\n        OpCode::CREATE2,\n    ] {\n        EvmTester::new()\n            .revision(Revision::Constantinople)\n            .depth(1024)\n            .code(\n                Bytecode::new()\n                    .pushv(0)\n                    .opcode(OpCode::DUP1)\n                    .opcode(OpCode::DUP1)\n                    .opcode(OpCode::DUP1)\n                    .opcode(OpCode::DUP1)\n                    .opcode(OpCode::DUP1)\n                    .opcode(OpCode::DUP1)\n                    .opcode(op)\n                    .ret_top()\n                    .opcode(OpCode::INVALID),\n            )\n            .status(StatusCode::Success)\n            .inspect_host(|host, _| {\n                assert_eq!(host.recorded.lock().calls, []);\n            })\n            .output_value(0)\n            .check()\n    }\n}\n\n#[test]\nfn call_output() {\n    for op in [\n        OpCode::CALL,\n        OpCode::CALLCODE,\n        OpCode::DELEGATECALL,\n        OpCode::STATICCALL,\n    ] {\n        let call_output = Bytes::from_static(&hex!(\"0a0b\"));\n\n        let t = EvmTester::new()\n            .apply_host_fn({\n                let call_output = call_output.clone();\n                move |host, _| {\n                    host.accounts.entry(Address::zero()).or_default().balance = 1.into();\n                    host.call_result.output_data = call_output.clone();\n                }\n            })\n            .inspect_host(move |host, _| {\n                assert_eq!(host.call_result.output_data, call_output);\n                assert!(core::ptr::eq(\n                    host.call_result.output_data.as_ptr(),\n                    call_output.as_ptr()\n                ));\n            });\n\n        let code_prefix_output_1 = Bytecode::new()\n            .pushv(1)\n            .opcode(OpCode::DUP1)\n            .opcode(OpCode::DUP1)\n            .opcode(OpCode::DUP1)\n            .opcode(OpCode::DUP1)\n            .opcode(OpCode::DUP1)\n            .opcode(OpCode::DUP1)\n            .pushb(hex!(\"7fffffffffffffff\"));\n        let code_prefix_output_0 = Bytecode::new()\n            .pushv(0)\n            .opcode(OpCode::DUP1)\n            .opcode(OpCode::DUP1)\n            .opcode(OpCode::DUP1)\n            .opcode(OpCode::DUP1)\n            .opcode(OpCode::DUP1)\n            .opcode(OpCode::DUP1)\n            .pushb(hex!(\"7fffffffffffffff\"));\n        let code_suffix = Bytecode::new().ret(0, 3);\n\n        t.clone()\n            .code(\n                Bytecode::new()\n                    .append_bc(code_prefix_output_1)\n                    .opcode(op)\n                    .append_bc(code_suffix.clone()),\n            )\n            .status(StatusCode::Success)\n            .output_data(hex!(\"000a00\"))\n            .check();\n\n        t.clone()\n            .code(\n                Bytecode::new()\n                    .append_bc(code_prefix_output_0)\n                    .opcode(op)\n                    .append_bc(code_suffix.clone()),\n            )\n            .status(StatusCode::Success)\n            .output_data(hex!(\"000000\"))\n            .check();\n    }\n}\n\n#[test]\nfn call_high_gas() {\n    for call_opcode in [OpCode::CALL, OpCode::CALLCODE, OpCode::DELEGATECALL] {\n        let mut call_dst = Address::zero();\n        call_dst.0[19] = 0xaa;\n\n        EvmTester::new()\n            .revision(Revision::Homestead)\n            .apply_host_fn(move |host, _| {\n                host.accounts.entry(call_dst).or_default();\n            })\n            .gas(5000)\n            .code(\n                Bytecode::new()\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(0xaa)\n                    .pushv(0x134c)\n                    .opcode(call_opcode),\n            )\n            .status(StatusCode::OutOfGas)\n            .check()\n    }\n}\n\n#[test]\nfn call_value_zero_to_nonexistent_account() {\n    let call_gas = 6000;\n\n    let gas_left = 1000;\n\n    EvmTester::new()\n        .apply_host_fn(move |host, _| {\n            host.call_result.gas_left = gas_left;\n        })\n        .code(\n            Bytecode::new()\n                .pushv(0x40)\n                .pushv(0)\n                .pushv(0x40)\n                .pushv(0)\n                .pushv(0)\n                .pushv(0xaa)\n                .pushv(call_gas)\n                .opcode(OpCode::CALL)\n                .opcode(OpCode::POP),\n        )\n        .gas(9000)\n        .gas_used(729 + (call_gas - gas_left))\n        .status(StatusCode::Success)\n        .inspect_host(|host, _| {\n            let recorded = host.recorded.lock();\n            assert_eq!(recorded.calls.len(), 1);\n            let call_msg = &recorded.calls[0];\n            assert_eq!(call_msg.kind, CallKind::Call);\n            assert_eq!(call_msg.depth, 1);\n            assert_eq!(call_msg.gas, 6000);\n            assert_eq!(call_msg.input_data.len(), 64);\n            assert_eq!(\n                call_msg.recipient,\n                hex!(\"00000000000000000000000000000000000000aa\").into()\n            );\n            assert_eq!(call_msg.value, 0.into());\n        })\n        .check()\n}\n\n#[test]\nfn call_new_account_creation_cost() {\n    let call_dst: Address = hex!(\"00000000000000000000000000000000000000ad\").into();\n    let destination: Address = hex!(\"0000000000000000000000000000000000000003\").into();\n\n    let t = EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .pushv(0)\n                .pushv(0)\n                .pushv(0)\n                .pushv(0)\n                .opcode(OpCode::CALLDATALOAD)\n                .pushb(call_dst.0)\n                .pushv(0)\n                .opcode(OpCode::CALL)\n                .ret_top(),\n        )\n        .destination(destination);\n\n    t.clone()\n        .revision(Revision::Tangerine)\n        .apply_host_fn(|host, msg| {\n            host.accounts.entry(msg.recipient).or_default().balance = 0.into();\n        })\n        .input(&hex!(\"00\") as &[u8])\n        .status(StatusCode::Success)\n        .gas_used(25000 + 739)\n        .output_value(1)\n        .inspect_host(move |host, _| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    call_dst, // Account exist?\n                    call_dst, // Call.\n                ]\n            );\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Tangerine)\n        .apply_host_fn(|host, msg| {\n            host.accounts.entry(msg.recipient).or_default().balance = 1.into();\n        })\n        .input(&hex!(\"0000000000000000000000000000000000000000000000000000000000000001\") as &[u8])\n        .status(StatusCode::Success)\n        .gas_used(25000 + 9000 + 739)\n        .output_value(1)\n        .inspect_host(move |host, msg| {\n            let r = host.recorded.lock();\n            assert_eq!(r.calls.len(), 1);\n            let call_msg = &r.calls[0];\n            assert_eq!(call_msg.recipient, call_dst);\n            assert_eq!(call_msg.gas, 2300);\n            assert_eq!(call_msg.sender, destination);\n            assert_eq!(call_msg.value, 1.into());\n            assert_eq!(call_msg.input_data, Bytes::new());\n            assert_eq!(\n                r.account_accesses,\n                [\n                    call_dst,      // Account exist?\n                    msg.recipient, // Balance.\n                    call_dst       // Call.\n                ]\n            )\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Spurious)\n        .apply_host_fn(|host, msg| {\n            host.accounts.entry(msg.recipient).or_default().balance = 0.into();\n        })\n        .input(&hex!(\"00\") as &[u8])\n        .status(StatusCode::Success)\n        .gas_used(739)\n        .output_value(1)\n        .inspect_host(move |host, _| {\n            let r = host.recorded.lock();\n            assert_eq!(r.calls.len(), 1);\n            let call_msg = &r.calls[0];\n            assert_eq!(call_msg.recipient, call_dst);\n            assert_eq!(call_msg.gas, 0);\n            assert_eq!(call_msg.sender, destination);\n            assert_eq!(call_msg.value, 0.into());\n            assert_eq!(call_msg.input_data, Bytes::new());\n            assert_eq!(\n                r.account_accesses,\n                [\n                call_dst         // Call.\n            ]\n            )\n        })\n        .check();\n\n    t.revision(Revision::Spurious)\n        .apply_host_fn(|host, msg| {\n            host.accounts.entry(msg.recipient).or_default().balance = 1.into();\n        })\n        .input(&hex!(\"0000000000000000000000000000000000000000000000000000000000000001\") as &[u8])\n        .status(StatusCode::Success)\n        .gas_used(25000 + 9000 + 739)\n        .output_value(1)\n        .inspect_host(move |host, msg| {\n            let r = host.recorded.lock();\n            assert_eq!(r.calls.len(), 1);\n            let call_msg = &r.calls[0];\n            assert_eq!(call_msg.recipient, call_dst);\n            assert_eq!(call_msg.gas, 2300);\n            assert_eq!(call_msg.sender, destination);\n            assert_eq!(call_msg.value, 1.into());\n            assert_eq!(call_msg.input_data, Bytes::new());\n            assert_eq!(\n                r.account_accesses,\n                [\n                    call_dst,      // Account exist?\n                    msg.recipient, // Balance.\n                    call_dst       // Call.\n                ]\n            )\n        })\n        .check()\n}\n\n#[test]\nfn callcode_new_account_create() {\n    let code = hex!(\"60008080806001600061c350f250\");\n    let call_sender = hex!(\"5e4d00000000000000000000000000000000d4e5\").into();\n\n    EvmTester::new()\n        .destination(call_sender)\n        .apply_host_fn(|host, msg| {\n            host.accounts.entry(msg.recipient).or_default().balance = 1.into();\n            host.call_result.gas_left = 1;\n        })\n        .gas(100000)\n        .code(code)\n        .gas_used(59722)\n        .status(StatusCode::Success)\n        .inspect_host(move |host, _| {\n            let recorded = host.recorded.lock();\n            assert_eq!(recorded.calls.len(), 1);\n            let call_msg = &recorded.calls[0];\n            assert_eq!(call_msg.kind, CallKind::CallCode);\n            assert_eq!(call_msg.depth, 1);\n            assert_eq!(call_msg.gas, 52_300);\n            assert_eq!(call_msg.sender, call_sender);\n            assert_eq!(call_msg.value, 1.into());\n        })\n        .check()\n}\n\n/// Performs a CALL then OOG in the same code block.\n#[test]\nfn call_then_oog() {\n    let call_dst = 0xaa;\n\n    let mut code = Bytecode::new().append_bc(\n        CallInstruction::call(call_dst)\n            .gas(254)\n            .value(0)\n            .input(0, 0x40)\n            .output(0, 0x40),\n    );\n\n    for _ in 0..4 {\n        code = code.opcode(OpCode::DUP1).opcode(OpCode::ADD);\n    }\n\n    code = code.opcode(OpCode::POP);\n\n    EvmTester::new()\n        .apply_host_fn(move |host, _| {\n            let mut address = Address::zero();\n            address.0[19] = call_dst;\n            host.accounts.entry(address).or_default();\n\n            host.call_result.status_code = StatusCode::Failure;\n            host.call_result.gas_left = 0;\n        })\n        .code(code)\n        .gas(1000)\n        .gas_used(1000)\n        .gas_left(0)\n        .status(StatusCode::OutOfGas)\n        .inspect_host(|host, _| {\n            assert_eq!(host.recorded.lock().calls.len(), 1);\n            assert_eq!(host.recorded.lock().calls[0].gas, 254);\n        })\n        .check()\n}\n\n/// Performs a CALLCODE then OOG in the same code block.\n#[test]\nfn callcode_then_oog() {\n    let call_dst = 0xaa;\n\n    let mut code = Bytecode::new().append_bc(\n        CallInstruction::callcode(call_dst)\n            .gas(100)\n            .value(0)\n            .input(0, 3)\n            .output(3, 9),\n    );\n\n    for _ in 0..4 {\n        code = code.opcode(OpCode::DUP1).opcode(OpCode::ADD);\n    }\n\n    code = code.opcode(OpCode::POP);\n\n    EvmTester::new()\n        .apply_host_fn(move |host, _| {\n            let mut address = Address::zero();\n            address.0[19] = call_dst;\n            host.accounts.entry(address).or_default();\n\n            host.call_result.status_code = StatusCode::Failure;\n            host.call_result.gas_left = 0;\n        })\n        .code(code)\n        .gas(825)\n        .status(StatusCode::OutOfGas)\n        .inspect_host(|host, _| {\n            assert_eq!(host.recorded.lock().calls.len(), 1);\n            assert_eq!(host.recorded.lock().calls[0].gas, 100);\n        })\n        .check()\n}\n\n/// Performs a CALL then OOG in the same code block.\n#[test]\nfn delegatecall_then_oog() {\n    let call_dst = 0xaa;\n\n    let mut code = Bytecode::new().append_bc(\n        CallInstruction::delegatecall(call_dst)\n            .gas(254)\n            .input(0, 64)\n            .output(0, 64),\n    );\n\n    for _ in 0..4 {\n        code = code.opcode(OpCode::DUP1).opcode(OpCode::ADD);\n    }\n\n    code = code.opcode(OpCode::POP);\n\n    EvmTester::new()\n        .apply_host_fn(move |host, _| {\n            let mut address = Address::zero();\n            address.0[19] = call_dst;\n            host.accounts.entry(address).or_default();\n\n            host.call_result.status_code = StatusCode::Failure;\n            host.call_result.gas_left = 0;\n        })\n        .code(code)\n        .gas(1000)\n        .gas_used(1000)\n        .gas_left(0)\n        .status(StatusCode::OutOfGas)\n        .inspect_host(|host, _| {\n            assert_eq!(host.recorded.lock().calls.len(), 1);\n            assert_eq!(host.recorded.lock().calls[0].gas, 254);\n        })\n        .check()\n}\n\n/// Performs a STATICCALL then OOG in the same code block.\n#[test]\nfn staticcall_then_oog() {\n    let call_dst = 0xaa;\n\n    let mut code = Bytecode::new().append_bc(\n        CallInstruction::staticcall(call_dst)\n            .gas(254)\n            .input(0, 0x40)\n            .output(0, 0x40),\n    );\n\n    for _ in 0..4 {\n        code = code.opcode(OpCode::DUP1).opcode(OpCode::ADD);\n    }\n\n    code = code.opcode(OpCode::POP);\n\n    EvmTester::new()\n        .apply_host_fn(move |host, _| {\n            let mut address = Address::zero();\n            address.0[19] = call_dst;\n            host.accounts.entry(address).or_default();\n\n            host.call_result.status_code = StatusCode::Failure;\n            host.call_result.gas_left = 0;\n        })\n        .code(code)\n        .gas(1000)\n        .status(StatusCode::OutOfGas)\n        .gas_used(1000)\n        .gas_left(0)\n        .inspect_host(|host, _| {\n            assert_eq!(host.recorded.lock().calls.len(), 1);\n            assert_eq!(host.recorded.lock().calls[0].gas, 254);\n        })\n        .check()\n}\n\n#[test]\nfn staticcall_input() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .mstore_value(3, 0x010203)\n                .append_bc(CallInstruction::staticcall(0).gas(0xee).input(32, 3)),\n        )\n        .inspect_host(|host, _| {\n            let r = host.recorded.lock();\n\n            assert_eq!(r.calls.len(), 1);\n            assert_eq!(r.calls[0].gas, 0xee);\n            assert_eq!(r.calls[0].input_data[..], hex!(\"010203\"));\n        })\n        .check()\n}\n\n#[test]\nfn call_with_value_low_gas() {\n    for op in [OpCode::CALL, OpCode::CALLCODE] {\n        EvmTester::new()\n            .apply_host_fn(|host, _| {\n                // Create the call destination account.\n                host.accounts.entry(Address::zero()).or_default();\n            })\n            .code(\n                Bytecode::new()\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(1)\n                    .pushv(0)\n                    .pushv(0)\n                    .opcode(op)\n                    .opcode(OpCode::POP),\n            )\n            .gas(9721)\n            .status(StatusCode::Success)\n            .gas_left(2300 - 2)\n            .check()\n    }\n}\n\n#[test]\nfn call_oog_after_balance_check() {\n    // Create the call destination account.\n    for op in [OpCode::CALL, OpCode::CALLCODE] {\n        EvmTester::new()\n            .apply_host_fn(|host, _| {\n                // Create the call destination account.\n                host.accounts.entry(Address::zero()).or_default();\n            })\n            .code(\n                Bytecode::new()\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(1)\n                    .pushv(0)\n                    .pushv(0)\n                    .opcode(op)\n                    .opcode(OpCode::SELFDESTRUCT),\n            )\n            .gas(12420)\n            .status(StatusCode::OutOfGas)\n            .check()\n    }\n}\n\n#[test]\nfn call_oog_after_depth_check() {\n    // Create the call destination account.\n    let t = EvmTester::new()\n        .apply_host_fn(|host, _| {\n            host.accounts.entry(Address::zero()).or_default();\n        })\n        .depth(1024);\n\n    for op in [OpCode::CALL, OpCode::CALLCODE] {\n        t.clone()\n            .code(\n                Bytecode::new()\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(1)\n                    .pushv(0)\n                    .pushv(0)\n                    .opcode(op)\n                    .opcode(OpCode::SELFDESTRUCT),\n            )\n            .gas(12420)\n            .status(StatusCode::OutOfGas)\n            .check()\n    }\n\n    let t = t.revision(Revision::Tangerine).code(\n        Bytecode::new()\n            .pushv(0)\n            .pushv(0)\n            .pushv(0)\n            .pushv(0)\n            .pushv(0)\n            .pushv(0)\n            .pushv(0)\n            .opcode(OpCode::CALL)\n            .opcode(OpCode::SELFDESTRUCT),\n    );\n\n    t.clone().gas(721).status(StatusCode::OutOfGas).check();\n\n    t.gas(721 + 5000 - 1).status(StatusCode::OutOfGas).check();\n}\n\n#[test]\nfn create_oog_after() {\n    for op in [OpCode::CREATE, OpCode::CREATE2] {\n        EvmTester::new()\n            .revision(Revision::Constantinople)\n            .code(\n                Bytecode::new()\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(0)\n                    .pushv(0)\n                    .opcode(op)\n                    .opcode(OpCode::SELFDESTRUCT),\n            )\n            .gas(39000)\n            .status(StatusCode::OutOfGas)\n            .check()\n    }\n}\n\n#[test]\nfn returndatasize_before_call() {\n    EvmTester::new()\n        .code(hex!(\"3d60005360016000f3\"))\n        .gas_used(17)\n        .output_data([0])\n        .check()\n}\n\n#[test]\nfn returndatasize() {\n    let call_res_output_len = 13;\n\n    let t = EvmTester::new()\n        .apply_host_fn(move |host, _| {\n            host.call_result.output_data = repeat_with(rand::random)\n                .take(call_res_output_len as usize)\n                .collect::<Vec<u8>>()\n                .into()\n        })\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::DELEGATECALL)\n                .opcode(OpCode::RETURNDATASIZE)\n                .mstore8(0)\n                .pushv(1)\n                .pushv(0)\n                .opcode(OpCode::RETURN),\n        );\n\n    t.clone()\n        .gas_used(735)\n        .output_data([call_res_output_len])\n        .check();\n\n    t.clone()\n        .apply_host_fn(|host, _| {\n            host.call_result.output_data = vec![0; 1].into();\n            host.call_result.status_code = StatusCode::Failure;\n        })\n        .gas_used(735)\n        .output_data([1])\n        .check();\n\n    t.apply_host_fn(|host, _| {\n        host.call_result.output_data = Bytes::new();\n        host.call_result.status_code = StatusCode::InternalError(String::new());\n    })\n    .gas_used(735)\n    .output_data([0])\n    .check();\n}\n\n#[test]\nfn returndatacopy() {\n    let call_output = hex!(\"0102030405060700000000000000000000000000000000000000000000000000\");\n\n    EvmTester::new()\n        .apply_host_fn(move |host, _| {\n            host.call_result.output_data = Bytes::from(call_output.to_vec());\n        })\n        .code(hex!(\"600080808060aa60fff4506020600060003e60206000f3\"))\n        .gas_used(999)\n        .output_data(call_output)\n        .check()\n}\n\n#[test]\nfn returndatacopy_empty() {\n    EvmTester::new()\n        .code(hex!(\"600080808060aa60fff4600080803e60016000f3\"))\n        .gas_used(994)\n        .output_data([0])\n        .check()\n}\n\n#[test]\nfn returndatacopy_cost() {\n    let t = EvmTester::new()\n        .code(hex!(\"60008080808080fa6001600060003e\"))\n        .apply_host_fn(|host, _| {\n            host.call_result.output_data = vec![0].into();\n        });\n    t.clone().gas(736).status(StatusCode::Success).check();\n    t.gas(735).status(StatusCode::OutOfGas).check();\n}\n\n#[test]\nfn returndatacopy_outofrange() {\n    for code in [\n        hex!(\"60008080808080fa6002600060003e\"),\n        hex!(\"60008080808080fa6001600160003e\"),\n        hex!(\"60008080808080fa6000600260003e\"),\n    ] {\n        EvmTester::new()\n            .apply_host_fn(|host, _| {\n                host.call_result.output_data = vec![0].into();\n            })\n            .code(code)\n            .gas(735)\n            .status(StatusCode::InvalidMemoryAccess)\n            .check()\n    }\n}\n"
  },
  {
    "path": "tests/eip2929.rs",
    "content": "use evmodin::{host::*, opcode::*, util::*, *};\nuse hex_literal::hex;\n\n#[test]\nfn eip2929_case1() {\n    // https://gist.github.com/holiman/174548cad102096858583c6fbbb0649a#case-1\n    EvmTester::new()\n        .revision(Revision::Berlin)\n        .sender(hex!(\"0000000000000000000000000000000000000000\"))\n        .destination(hex!(\"000000000000000000000000636F6E7472616374\"))\n        .gas(13653)\n        .code(hex!(\"60013f5060023b506003315060f13f5060f23b5060f3315060f23f5060f33b5060f1315032315030315000\"))\n        .status(StatusCode::Success)\n        .gas_used(8653)\n        .output_data([])\n        .inspect_host(|host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    msg.sender,\n                    msg.recipient,\n                    hex!(\"0000000000000000000000000000000000000001\").into(),\n                    hex!(\"0000000000000000000000000000000000000001\").into(),\n                    hex!(\"0000000000000000000000000000000000000002\").into(),\n                    hex!(\"0000000000000000000000000000000000000002\").into(),\n                    hex!(\"0000000000000000000000000000000000000003\").into(),\n                    hex!(\"0000000000000000000000000000000000000003\").into(),\n                    hex!(\"00000000000000000000000000000000000000f1\").into(),\n                    hex!(\"00000000000000000000000000000000000000f1\").into(),\n                    hex!(\"00000000000000000000000000000000000000f2\").into(),\n                    hex!(\"00000000000000000000000000000000000000f2\").into(),\n                    hex!(\"00000000000000000000000000000000000000f3\").into(),\n                    hex!(\"00000000000000000000000000000000000000f3\").into(),\n                    hex!(\"00000000000000000000000000000000000000f2\").into(),\n                    hex!(\"00000000000000000000000000000000000000f2\").into(),\n                    hex!(\"00000000000000000000000000000000000000f3\").into(),\n                    hex!(\"00000000000000000000000000000000000000f3\").into(),\n                    hex!(\"00000000000000000000000000000000000000f1\").into(),\n                    hex!(\"00000000000000000000000000000000000000f1\").into(),\n                    hex!(\"0000000000000000000000000000000000000000\").into(),\n                    hex!(\"0000000000000000000000000000000000000000\").into(),\n                    msg.recipient,\n                    msg.recipient,\n                ]\n            );\n        })\n        .check()\n}\n\n#[test]\nfn eip2929_case2() {\n    // https://gist.github.com/holiman/174548cad102096858583c6fbbb0649a#case-2\n    EvmTester::new()\n        .revision(Revision::Berlin)\n        .sender(hex!(\"0000000000000000000000000000000000000000\"))\n        .destination(hex!(\"000000000000000000000000636F6E7472616374\"))\n        .code(hex!(\n            \"60006000600060ff3c60006000600060ff3c600060006000303c00\"\n        ))\n        .status(StatusCode::Success)\n        .gas_used(2835)\n        .output_data([])\n        .inspect_host(|host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    msg.sender,\n                    msg.recipient,\n                    hex!(\"00000000000000000000000000000000000000ff\").into(),\n                    hex!(\"00000000000000000000000000000000000000ff\").into(),\n                    msg.recipient,\n                ]\n            );\n        })\n        .check()\n}\n\n#[test]\nfn eip2929_case3() {\n    // https://gist.github.com/holiman/174548cad102096858583c6fbbb0649a#case-3\n    EvmTester::new()\n        .revision(Revision::Berlin)\n        .sender(hex!(\"0000000000000000000000000000000000000000\"))\n        .destination(hex!(\"000000000000000000000000636F6E7472616374\"))\n        .code(hex!(\"60015450601160015560116002556011600255600254600154\"))\n        .status(StatusCode::Success)\n        .gas_used(44529)\n        .output_data([])\n        .check()\n}\n\n#[test]\nfn eip2929_case4() {\n    // https://gist.github.com/holiman/174548cad102096858583c6fbbb0649a#case-4\n    EvmTester::new()\n        .revision(Revision::Berlin)\n        .sender(hex!(\"0000000000000000000000000000000000000000\"))\n        .destination(hex!(\"000000000000000000000000636F6E7472616374\"))\n        .code(hex!(\n            \"60008080808060046000f15060008080808060ff6000f15060008080808060ff6000fa50\"\n        ))\n        .status(StatusCode::Success)\n        .gas_used(2869)\n        .output_data([])\n        .check()\n}\n\n#[test]\nfn eip2929_op_oog() {\n    for (op, gas) in [\n        (OpCode::BALANCE, 2603),\n        (OpCode::EXTCODESIZE, 2603),\n        (OpCode::EXTCODEHASH, 2603),\n    ] {\n        let t = EvmTester::new()\n            .revision(Revision::Berlin)\n            .code(Bytecode::new().pushv(0x0a).opcode(op));\n\n        t.clone()\n            .gas(gas)\n            .status(StatusCode::Success)\n            .gas_used(gas)\n            .check();\n\n        t.clone()\n            .gas(gas - 1)\n            .status(StatusCode::OutOfGas)\n            .gas_used(gas - 1)\n            .check();\n    }\n}\n\n#[test]\nfn eip2929_extcodecopy_oog() {\n    let t = EvmTester::new().revision(Revision::Berlin).code(\n        Bytecode::new()\n            .pushv(0)\n            .opcode(OpCode::DUP1)\n            .opcode(OpCode::DUP1)\n            .pushv(0xa)\n            .opcode(OpCode::EXTCODECOPY),\n    );\n\n    t.clone()\n        .gas(2612)\n        .status(StatusCode::Success)\n        .gas_used(2612)\n        .check();\n\n    t.gas(2611)\n        .status(StatusCode::OutOfGas)\n        .gas_used(2611)\n        .check();\n}\n\n#[test]\nfn eip2929_sload_cold() {\n    let key = 1.into();\n\n    let t = EvmTester::new()\n        .revision(Revision::Berlin)\n        .code(Bytecode::new().pushv(1).opcode(OpCode::SLOAD))\n        .apply_host_fn(move |host, msg| {\n            let mut st = host\n                .accounts\n                .entry(msg.recipient)\n                .or_default()\n                .storage\n                .entry(key)\n                .or_default();\n            st.value = 2.into();\n            assert_eq!(st.access_status, AccessStatus::Cold);\n        });\n\n    t.clone()\n        .gas(2103)\n        .status(StatusCode::Success)\n        .gas_used(2103)\n        .inspect_host(move |host, msg| {\n            assert_eq!(\n                host.accounts[&msg.recipient].storage[&key].access_status,\n                AccessStatus::Warm\n            );\n        })\n        .check();\n\n    t.gas(2102)\n        .status(StatusCode::OutOfGas)\n        .gas_used(2102)\n        .check();\n}\n\n#[test]\nfn eip2929_sload_two_slots() {\n    let key0 = 0.into();\n    let key1 = 1.into();\n\n    EvmTester::new()\n        .revision(Revision::Berlin)\n        .code(\n            Bytecode::new()\n                .pushv(key0)\n                .opcode(OpCode::SLOAD)\n                .opcode(OpCode::POP)\n                .pushv(key1)\n                .opcode(OpCode::SLOAD)\n                .opcode(OpCode::POP),\n        )\n        .gas(30000)\n        .status(StatusCode::Success)\n        .gas_used(4210)\n        .inspect_host(move |host, msg| {\n            assert_eq!(\n                host.accounts[&msg.recipient].storage[&key0].access_status,\n                AccessStatus::Warm\n            );\n            assert_eq!(\n                host.accounts[&msg.recipient].storage[&key1].access_status,\n                AccessStatus::Warm\n            );\n        })\n        .check()\n}\n\n#[test]\nfn eip2929_sload_warm() {\n    let key = 1.into();\n    let t = EvmTester::new()\n        .revision(Revision::Berlin)\n        .code(Bytecode::new().pushv(1).opcode(OpCode::SLOAD))\n        .apply_host_fn(move |host, msg| {\n            let st = host\n                .accounts\n                .entry(msg.recipient)\n                .or_default()\n                .storage\n                .entry(key)\n                .or_default();\n            st.value = 2.into();\n            st.access_status = AccessStatus::Warm;\n        });\n\n    t.clone()\n        .gas(103)\n        .status(StatusCode::Success)\n        .gas_used(103)\n        .inspect_host(move |host, msg| {\n            assert_eq!(\n                host.accounts[&msg.recipient].storage[&key].access_status,\n                AccessStatus::Warm\n            );\n        })\n        .check();\n\n    t.gas(102)\n        .status(StatusCode::OutOfGas)\n        .gas_used(102)\n        .check();\n}\n\n#[test]\nfn eip2929_sstore_modify_cold() {\n    let key = 1.into();\n    let t = EvmTester::new()\n        .revision(Revision::Berlin)\n        .code(Bytecode::new().sstore(1, 3))\n        .apply_host_fn(move |host, msg| {\n            host.accounts\n                .entry(msg.recipient)\n                .or_default()\n                .storage\n                .entry(key)\n                .or_default()\n                .value = 2.into();\n        });\n\n    t.clone()\n        .gas(5006)\n        .status(StatusCode::Success)\n        .gas_used(5006)\n        .inspect_host(move |host, msg| {\n            assert_eq!(host.accounts[&msg.recipient].storage[&key].value, 3.into());\n            assert_eq!(\n                host.accounts[&msg.recipient].storage[&key].access_status,\n                AccessStatus::Warm\n            );\n        })\n        .check();\n\n    t.gas(5005)\n        .status(StatusCode::OutOfGas)\n        .gas_used(5005)\n        .inspect_host(move |host, msg| {\n            // The storage will be modified anyway, because the cost is checked after.\n            assert_eq!(host.accounts[&msg.recipient].storage[&key].value, 3.into());\n            assert_eq!(\n                host.accounts[&msg.recipient].storage[&key].access_status,\n                AccessStatus::Warm\n            );\n        })\n        .check();\n}\n\n#[test]\nfn eip2929_selfdestruct_cold_beneficiary() {\n    let t = EvmTester::new()\n        .revision(Revision::Berlin)\n        .code(Bytecode::new().pushv(0xbe).opcode(OpCode::SELFDESTRUCT));\n\n    t.clone()\n        .gas(7603)\n        .status(StatusCode::Success)\n        .gas_used(7603)\n        .check();\n\n    t.gas(7602)\n        .status(StatusCode::OutOfGas)\n        .gas_used(7602)\n        .check();\n}\n\n#[test]\nfn eip2929_selfdestruct_warm_beneficiary() {\n    let t = EvmTester::new()\n        .revision(Revision::Berlin)\n        .code(Bytecode::new().pushv(0xbe).opcode(OpCode::SELFDESTRUCT))\n        .apply_host_fn(|host, _| {\n            host.access_account(hex!(\"00000000000000000000000000000000000000be\").into());\n        });\n\n    t.clone()\n        .gas(5003)\n        .status(StatusCode::Success)\n        .gas_used(5003)\n        .check();\n\n    t.gas(5002)\n        .status(StatusCode::OutOfGas)\n        .gas_used(5002)\n        .check();\n}\n\n#[test]\nfn eip2929_delegatecall_cold() {\n    let t = EvmTester::new()\n        .revision(Revision::Berlin)\n        .code(CallInstruction::delegatecall(0xde));\n\n    t.clone()\n        .gas(2618)\n        .status(StatusCode::Success)\n        .gas_used(2618)\n        .inspect_host(|host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    msg.sender,\n                    msg.recipient,\n                    hex!(\"00000000000000000000000000000000000000de\").into(),\n                    msg.sender,\n                ]\n            );\n        })\n        .check();\n\n    t.gas(2617)\n        .status(StatusCode::OutOfGas)\n        .gas_used(2617)\n        .inspect_host(|host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    msg.sender,\n                    msg.recipient,\n                    hex!(\"00000000000000000000000000000000000000de\").into(),\n                ]\n            );\n        })\n        .check();\n}\n"
  },
  {
    "path": "tests/execute.rs",
    "content": "#![allow(clippy::needless_range_loop)]\n\nuse core::iter::repeat;\nuse ethereum_types::U256;\nuse evmodin::{opcode::*, util::*, *};\nuse hex_literal::hex;\nuse std::cmp::max;\n\n#[test]\nfn empty_code() {\n    for gas in [0, 1] {\n        EvmTester::new()\n            .code(hex!(\"\"))\n            .gas(gas)\n            .gas_used(0)\n            .status(StatusCode::Success)\n            .check()\n    }\n}\n\n#[test]\nfn invalid_push() {\n    EvmTester::new()\n        .code(Bytecode::new().opcode(OpCode::PUSH1))\n        .status(StatusCode::Success)\n        .check();\n}\n\n#[test]\nfn push_and_pop() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushb(hex!(\"0102\"))\n                .opcode(OpCode::POP)\n                .pushb(hex!(\"010203040506070809\"))\n                .opcode(OpCode::POP),\n        )\n        .gas(11)\n        .gas_used(10)\n        .status(StatusCode::Success)\n        .check()\n}\n\n#[test]\nfn stack_underflow() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(1)\n                .opcode(OpCode::POP)\n                .pushv(1)\n                .opcode(OpCode::POP)\n                .opcode(OpCode::POP),\n        )\n        .gas(13)\n        .status(StatusCode::StackUnderflow)\n        .check();\n\n    EvmTester::new()\n        .code(Bytecode::new().opcode(OpCode::NOT))\n        .status(StatusCode::StackUnderflow)\n        .check();\n}\n\n#[test]\nfn add() {\n    EvmTester::new()\n        .code(hex!(\"6007600d0160005260206000f3\"))\n        .gas(25)\n        .gas_used(24)\n        .status(StatusCode::Success)\n        .output_value(20)\n        .check()\n}\n\n#[test]\nfn dup() {\n    // 0 7 3 5\n    // 0 7 3 5 3 5\n    // 0 7 3 5 3 5 5 7\n    // 0 7 3 5 20\n    // 0 7 3 5 (20 0)\n    // 0 7 3 5 3 0\n    EvmTester::new()\n        .code(hex!(\"6000600760036005818180850101018452602084f3\"))\n        .gas(48)\n        .status(StatusCode::Success)\n        .output_value(20)\n        .check()\n}\n\n#[test]\nfn dup_all_1() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(1)\n                .append(hex!(\"808182838485868788898a8b8c8d8e8f\"))\n                .append(hex!(\"01010101010101010101010101010101\"))\n                .ret_top(),\n        )\n        .status(StatusCode::Success)\n        .output_value(17)\n        .check()\n}\n\n#[test]\nfn dup_stack_overflow() {\n    let b = Bytecode::new()\n        .pushv(1)\n        .append(hex!(\"808182838485868788898a8b8c8d8e8f\"))\n        .append(repeat(0x8f).take(1024 - 17));\n\n    EvmTester::new()\n        .code(b.clone())\n        .status(StatusCode::Success)\n        .check();\n\n    EvmTester::new()\n        .code(b.append([0x8f]))\n        .status(StatusCode::StackOverflow)\n        .check();\n}\n\n#[test]\nfn dup_stack_underflow() {\n    for i in 0..16 {\n        EvmTester::new()\n            .code(\n                Bytecode::new()\n                    .pushv(0)\n                    .repeat(i)\n                    .opcode(OpCode(OpCode::DUP1.0 + i as u8)),\n            )\n            .status(StatusCode::StackUnderflow)\n            .check()\n    }\n}\n\n#[test]\nfn sub_and_swap() {\n    EvmTester::new()\n        .code(hex!(\"600180810380829052602090f3\"))\n        .gas(33)\n        .status(StatusCode::Success)\n        .gas_left(0)\n        .output_value(1)\n        .check()\n}\n\n#[test]\nfn memory_and_not() {\n    EvmTester::new()\n        .code(hex!(\"600060018019815381518252800190f3\"))\n        .gas(42)\n        .status(StatusCode::Success)\n        .gas_left(0)\n        .output_data(hex!(\"00fe\"))\n        .check()\n}\n\n#[test]\nfn msize() {\n    EvmTester::new()\n        .code(hex!(\"60aa6022535960005360016000f3\"))\n        .gas(29)\n        .status(StatusCode::Success)\n        .gas_left(0)\n        .output_data(hex!(\"40\"))\n        .check()\n}\n\n#[test]\nfn gas() {\n    EvmTester::new()\n        .code(hex!(\"5a5a5a010160005360016000f3\"))\n        .gas(40)\n        .status(StatusCode::Success)\n        .gas_left(13)\n        .output_data([38 + 36 + 34])\n        .check()\n}\n\n#[test]\nfn arith() {\n    // x = (0 - 1) * 3\n    // y = 17 s/ x\n    // z = 17 s% x\n    // a = 17 * x + z\n    // iszero\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\"60116001600003600302\")) // 17 -3\n                .append(hex!(\"808205\")) // 17 -3 -5\n                .append(hex!(\"818307\")) // 17 -3 -5 2\n                .append(hex!(\"910201\")) // 17 17\n                .append(hex!(\"0315\")) // 1\n                .append(hex!(\"60005360016000f3\")),\n        )\n        .gas(100)\n        .status(StatusCode::Success)\n        .gas_left(26)\n        .output_data([1])\n        .check()\n}\n\n#[test]\nfn comparison() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\"60006001808203808001\")) // 0 1 -1 -2\n                .append(hex!(\"828210600053\")) // m[0] = -1 < 1\n                .append(hex!(\"828211600153\")) // m[1] = -1 > 1\n                .append(hex!(\"828212600253\")) // m[2] = -1 s< 1\n                .append(hex!(\"828213600353\")) // m[3] = -1 s> 1\n                .append(hex!(\"828214600453\")) // m[4] = -1 == 1\n                .append(hex!(\"818112600553\")) // m[5] = -2 s< -1\n                .append(hex!(\"818113600653\")) // m[6] = -2 s> -1\n                .append(hex!(\"60076000f3\")),\n        )\n        .status(StatusCode::Success)\n        .gas_used(138)\n        .output_data(hex!(\"00010100000100\"))\n        .check()\n}\n\n#[allow(clippy::identity_op)]\n#[test]\nfn bitwise() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\"60aa60ff\")) // aa ff\n                .append(hex!(\"818116600053\")) // m[0] = aa & ff\n                .append(hex!(\"818117600153\")) // m[1] = aa | ff\n                .append(hex!(\"818118600253\")) // m[2] = aa ^ ff\n                .append(hex!(\"60036000f3\")),\n        )\n        .gas(60)\n        .gas_left(0)\n        .output_data([0xaa & 0xff, 0xaa | 0xff, 0xaa ^ 0xff])\n        .check()\n}\n\n#[test]\nfn jump() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\"60be600053\")) // m[0] = be\n                .append(hex!(\"60fa\")) // fa\n                .append(hex!(\"60055801\")) // PC + 5\n                .append(hex!(\"56\")) // JUMP\n                .append(hex!(\"5050\")) // POP x2\n                .append(hex!(\"5b\")) // JUMPDEST\n                .append(hex!(\"600153\")) // m[1] = fa\n                .append(hex!(\"60026000f3\")), // RETURN(0,2)\n        )\n        .gas(44)\n        .status(StatusCode::Success)\n        .gas_left(0)\n        .output_data(hex!(\"befa\"))\n        .check()\n}\n\n#[test]\nfn jumpi() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\"5a600557\")) // GAS 5 JUMPI\n                .append(hex!(\"00\")) // STOP\n                .append(hex!(\"5b60016000f3\")), // JUMPDEST RETURN(0,1)\n        )\n        .gas(25)\n        .status(StatusCode::Success)\n        .gas_left(0)\n        .output_data(hex!(\"00\"))\n        .check()\n}\n\n#[test]\nfn jumpi_else() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .opcode(OpCode::COINBASE)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::JUMPI),\n        )\n        .gas(16)\n        .status(StatusCode::Success)\n        .gas_used(15)\n        .output_data(hex!(\"\"))\n        .check()\n}\n\n#[test]\nfn jumpi_at_the_end() {\n    EvmTester::new()\n        .code(hex!(\"5b6001600057\"))\n        .gas(1000)\n        .status(StatusCode::OutOfGas)\n        .gas_used(1000)\n        .check()\n}\n\n#[test]\nfn bad_jumpdest() {\n    for opcode in [OpCode::JUMP, OpCode::JUMPI] {\n        for hex in [hex!(\"4345\"), hex!(\"4342\")] {\n            EvmTester::new()\n                .code(Bytecode::new().append(hex).opcode(opcode))\n                .apply_host_fn(|host, _| {\n                    host.tx_context.block_number = 1;\n                    host.tx_context.block_gas_limit = 0;\n                    host.tx_context.block_timestamp = 0x80000000;\n                })\n                .status(StatusCode::BadJumpDestination)\n                .gas_left(0)\n                .check();\n        }\n    }\n}\n\n#[test]\nfn jump_to_block_beginning() {\n    EvmTester::new()\n        .code(Bytecode::new().jumpi(U256::zero(), OpCode::MSIZE).jump(4))\n        .status(StatusCode::BadJumpDestination)\n        .check()\n}\n\n#[test]\nfn jumpi_stack() {\n    for input in [&hex!(\"\") as &[u8], &hex!(\"ee\") as &[u8]] {\n        EvmTester::new()\n            .code(\n                Bytecode::new()\n                    .pushv(0xde)\n                    .jumpi(U256::from(6), OpCode::CALLDATASIZE)\n                    .opcode(OpCode::JUMPDEST)\n                    .ret_top(),\n            )\n            .input(input)\n            .output_value(0xde)\n            .check()\n    }\n}\n\n#[test]\nfn jump_over_jumpdest() {\n    // The code contains 2 consecutive JUMPDESTs. The JUMP at the beginning lands on the second one.\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(4)\n                .opcode(OpCode::JUMP)\n                .opcode(OpCode::JUMPDEST)\n                .opcode(OpCode::JUMPDEST),\n        )\n        .status(StatusCode::Success)\n        .gas_used(3 + 8 + 1)\n        .check()\n}\n\n#[test]\nfn jump_to_missing_push_data() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(5)\n                .opcode(OpCode::JUMP)\n                .opcode(OpCode::PUSH1),\n        )\n        .status(StatusCode::BadJumpDestination)\n        .check()\n}\n#[test]\nfn jump_to_missing_push_data2() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(6)\n                .opcode(OpCode::JUMP)\n                .opcode(OpCode::PUSH2)\n                .append(hex!(\"ef\")),\n        )\n        .status(StatusCode::BadJumpDestination)\n        .check()\n}\n\n#[test]\nfn pc_sum() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .opcode(OpCode::PC)\n                .opcode(OpCode::PC)\n                .opcode(OpCode::PC)\n                .opcode(OpCode::PC)\n                .opcode(OpCode::ADD)\n                .opcode(OpCode::ADD)\n                .opcode(OpCode::ADD)\n                .ret_top(),\n        )\n        .status(StatusCode::Success)\n        .output_value(6)\n        .check()\n}\n\n#[test]\nfn pc_after_jump_1() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(3)\n                .opcode(OpCode::JUMP)\n                .opcode(OpCode::JUMPDEST)\n                .opcode(OpCode::PC)\n                .ret_top(),\n        )\n        .status(StatusCode::Success)\n        .output_value(4)\n        .check()\n}\n\n#[test]\nfn pc_after_jump_2() {\n    for (input, output) in [(&hex!(\"\") as &[u8], 6), (&hex!(\"ff\") as &[u8], 11)] {\n        EvmTester::new()\n            .code(\n                Bytecode::new()\n                    .opcode(OpCode::CALLDATASIZE)\n                    .pushv(9)\n                    .opcode(OpCode::JUMPI)\n                    .pushv(12)\n                    .opcode(OpCode::PC)\n                    .opcode(OpCode::SWAP1)\n                    .opcode(OpCode::JUMP)\n                    .opcode(OpCode::JUMPDEST)\n                    .opcode(OpCode::GAS)\n                    .opcode(OpCode::PC)\n                    .opcode(OpCode::JUMPDEST)\n                    .ret_top(),\n            )\n            .input(input)\n            .status(StatusCode::Success)\n            .output_value(output)\n            .check()\n    }\n}\n\n#[test]\nfn byte() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\"63aabbccdd\")) // aabbccdd\n                .append(hex!(\"8060001a\")) // DUP 1 BYTE\n                .append(hex!(\"600053\")) // m[0] = 00\n                .append(hex!(\"80601c1a\")) // DUP 28 BYTE\n                .append(hex!(\"600253\")) // m[2] = aa\n                .append(hex!(\"80601f1a\")) // DUP 31 BYTE\n                .append(hex!(\"600453\")) // m[4] = dd\n                .append(hex!(\"8060201a\")) // DUP 32 BYTE\n                .append(hex!(\"600653\")) // m[6] = 00\n                .append(hex!(\"60076000f3\")), // RETURN(0,7)\n        )\n        .gas(72)\n        .status(StatusCode::Success)\n        .gas_left(0)\n        .inspect_output(|output| {\n            assert_eq!(output.len(), 7);\n            assert_eq!(output[0], 0);\n            assert_eq!(output[2], 0xaa);\n            assert_eq!(output[4], 0xdd);\n            assert_eq!(output[6], 0);\n        })\n        .check()\n}\n\n#[test]\nfn byte_overflow() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .opcode(OpCode::NOT)\n                .pushv(32)\n                .opcode(OpCode::BYTE)\n                .ret_top(),\n        )\n        .output_value(0)\n        .check();\n\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .opcode(OpCode::NOT)\n                .pushb(hex!(\"ffffffffffffffffffffffffffffffffffff\"))\n                .opcode(OpCode::BYTE)\n                .ret_top(),\n        )\n        .output_value(0)\n        .check();\n}\n\n#[test]\nfn addmod_mulmod() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\n                    \"7fcdeb8272fc01d4d50a6ec165d2ea477af19b9b2c198459f59079583b97e88a66\"\n                ))\n                .append(hex!(\n                    \"7f52e7e7a03b86f534d2e338aa1bb05ba3539cb2f51304cdbce69ce2d422c456ca\"\n                ))\n                .append(hex!(\n                    \"7fe0f2f0cae05c220260e1724bdc66a0f83810bd1217bd105cb2da11e257c6cdf6\"\n                ))\n                .append(hex!(\"82828208\")) // DUP DUP DUP ADDMOD\n                .append(hex!(\"600052\")) // m[0..]\n                .append(hex!(\"82828209\")) // DUP DUP DUP MULMOD\n                .append(hex!(\"602052\")) // m[32..]\n                .append(hex!(\"60406000f3\")), // RETURN(0,64)\n        )\n        .gas(67)\n        .status(StatusCode::Success)\n        .gas_left(0)\n        .inspect_output(|output| {\n            assert_eq!(\n                &output[..32],\n                hex!(\"65ef55f81fe142622955e990252cb5209a11d4db113d842408fd9c7ae2a29a5a\")\n            );\n            assert_eq!(\n                &output[32..],\n                hex!(\"34e04890131a297202753cae4c72efd508962c9129aed8b08c8e87ab425b7258\")\n            );\n        })\n        .check()\n}\n\n#[test]\nfn divmod() {\n    // Div and mod the -1 by the input and return.\n    EvmTester::new()\n        .code(hex!(\"600035600160000381810460005281810660205260406000f3\"))\n        .input(&hex!(\"0d\") as &[u8])\n        .status(StatusCode::Success)\n        .gas_used(61)\n        .inspect_output(|output| {\n            assert_eq!(\n                &output[..32],\n                hex!(\"0000000000000000000000000000000000000000000000000000000000000013\")\n            );\n            assert_eq!(\n                &output[32..],\n                hex!(\"08ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\")\n            );\n        })\n        .check()\n}\n\n#[test]\nfn div_by_zero() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .opcode(OpCode::DUP1)\n                .pushv(0xff)\n                .opcode(OpCode::DIV)\n                .opcode(OpCode::SDIV)\n                .ret_top(),\n        )\n        .status(StatusCode::Success)\n        .gas_used(34)\n        .output_value(0)\n        .check()\n}\n\n#[test]\nfn mod_by_zero() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .opcode(OpCode::DUP1)\n                .pushv(0xeffe)\n                .opcode(OpCode::MOD)\n                .opcode(OpCode::SMOD)\n                .ret_top(),\n        )\n        .status(StatusCode::Success)\n        .gas_used(34)\n        .output_value(0)\n        .check()\n}\n\n#[test]\nfn addmod_mulmod_by_zero() {\n    EvmTester::new()\n        .code(hex!(\"6000358080808008091560005260206000f3\"))\n        .status(StatusCode::Success)\n        .gas_used(52)\n        .inspect_output(|output| {\n            assert_eq!(output.len(), 32);\n            assert_eq!(output[31], 1);\n        })\n        .check();\n}\n\n#[test]\nfn signextend() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\"62017ffe\")) // 017ffe\n                .append(hex!(\"8060000b\")) // DUP SIGNEXTEND(0)\n                .append(hex!(\"600052\")) // m[0..]\n                .append(hex!(\"8060010b\")) // DUP SIGNEXTEND(1)\n                .append(hex!(\"602052\")) // m[32..]\n                .append(hex!(\"60406000f3\")), // RETURN(0,64)\n        )\n        .gas(49)\n        .status(StatusCode::Success)\n        .gas_left(0)\n        .inspect_output(|output| {\n            assert_eq!(\n                &output[..32],\n                hex!(\"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\")\n            );\n            assert_eq!(\n                &output[32..],\n                hex!(\"0000000000000000000000000000000000000000000000000000000000007ffe\")\n            );\n        })\n        .check();\n}\n\n#[test]\nfn signextend_31() {\n    for (code, output) in [\n        (\n            hex!(\"61010160000360081c601e0b60005260206000f3\"),\n            &hex!(\"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\") as &[u8],\n        ),\n        (\n            hex!(\"61010160000360081c601f0b60005260206000f3\"),\n            &hex!(\"00fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\") as &[u8],\n        ),\n    ] {\n        EvmTester::new()\n            .code(code)\n            .revision(Revision::Constantinople)\n            .status(StatusCode::Success)\n            .gas_used(38)\n            .output_value(output)\n            .check();\n    }\n}\n\n#[test]\nfn exp() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\"612019\")) // 0x2019\n                .append(hex!(\"6003\")) // 3\n                .append(hex!(\"0a\")) // EXP\n                .append(hex!(\"600052\")) // m[0..]\n                .append(hex!(\"60206000f3\")), // RETURN(0,32)\n        )\n        .gas(131)\n        .status(StatusCode::Success)\n        .gas_left(0)\n        .output_data(hex!(\n            \"263cf24662b24c371a647c1340022619306e431bf3a4298d4b5998a3f1c1aaa3\"\n        ))\n        .check()\n}\n\n#[test]\nfn exp_1_0() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .pushv(1)\n                .opcode(OpCode::EXP)\n                .ret_top(),\n        )\n        .gas(31)\n        .status(StatusCode::Success)\n        .gas_used(31)\n        .output_value(1)\n        .check()\n}\n\n#[test]\nfn exp_0_0() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .pushv(0)\n                .opcode(OpCode::EXP)\n                .ret_top(),\n        )\n        .gas(31)\n        .status(StatusCode::Success)\n        .gas_used(31)\n        .output_value(1)\n        .check()\n}\n\n#[test]\nfn exp_oog() {\n    let code = hex!(\"6001600003800a\");\n    EvmTester::new()\n        .code(code)\n        .gas(1622)\n        .status(StatusCode::Success)\n        .gas_left(0)\n        .check();\n    EvmTester::new()\n        .code(code)\n        .gas(1621)\n        .status(StatusCode::OutOfGas)\n        .gas_left(0)\n        .check();\n}\n\n#[test]\nfn exp_pre_spurious_dragon() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\"62012019\")) // 0x012019\n                .append(hex!(\"6003\")) // 3\n                .append(hex!(\"0a\")) // EXP\n                .append(hex!(\"600052\")) // m[0..]\n                .append(hex!(\"60206000f3\")), // RETURN(0,32)\n        )\n        .revision(Revision::Tangerine)\n        .gas(131 - 70)\n        .status(StatusCode::Success)\n        .gas_left(0)\n        .output_data(hex!(\n            \"422ea3761c4f6517df7f102bb18b96abf4735099209ca21256a6b8ac4d1daaa3\"\n        ))\n        .check();\n}\n\n#[test]\nfn calldataload() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\"600335\")) // CALLDATALOAD(3)\n                .append(hex!(\"600052\")) // m[0..]\n                .append(hex!(\"600a6000f3\")), // RETURN(0,10)\n        )\n        .gas(21)\n        .input(&hex!(\"0102030405\") as &[u8])\n        .status(StatusCode::Success)\n        .gas_left(0)\n        .output_data(hex!(\"04050000000000000000\"))\n        .check()\n}\n\n#[test]\nfn calldataload_outofrange() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(1)\n                .opcode(OpCode::CALLDATALOAD)\n                .ret_top(),\n        )\n        .status(StatusCode::Success)\n        .output_value(U256::zero())\n        .check()\n}\n\n#[test]\nfn calldatacopy() {\n    let code = Bytecode::new()\n        .append(hex!(\"366001600037\")) // CALLDATASIZE 1 0 CALLDATACOPY\n        .append(hex!(\"600a6000f3\"));\n    EvmTester::new()\n        .code(code.clone())\n        .input(&hex!(\"0102030405\") as &[u8])\n        .status(StatusCode::Success)\n        .gas_used(23)\n        .output_data(hex!(\"02030405000000000000\"))\n        .check();\n\n    EvmTester::new()\n        .code(code)\n        .status(StatusCode::Success)\n        .gas_used(20)\n        .check();\n\n    EvmTester::new()\n        .code(hex!(\"60ff66fffffffffffffa60003760ff6000f3\"))\n        .status(StatusCode::Success)\n        .gas_used(66)\n        .output_data([0; 0xff])\n        .check();\n}\n\n#[test]\nfn address() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\"30600052\")) // ADDRESS MSTORE(0)\n                .append(hex!(\"600a600af3\")), // RETURN(10,10)\n        )\n        .destination(hex!(\"cc00000000000000000000000000000000000000\"))\n        .status(StatusCode::Success)\n        .gas(17)\n        .gas_left(0)\n        .output_data(hex!(\"0000cc00000000000000\"))\n        .check()\n}\n\n#[test]\nfn caller_callvalue() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\"333401600052\")) // CALLER CALLVALUE ADD MSTORE(0)\n                .append(hex!(\"600a600af3\")), // RETURN(10,10)\n        )\n        .sender(hex!(\"dd00000000000000000000000000000000000000\"))\n        .value(hex!(\n            \"00000000000000000000000000ee000000000000000000000000000000000000\"\n        ))\n        .status(StatusCode::Success)\n        .gas(22)\n        .gas_left(0)\n        .output_data(hex!(\"0000ddee000000000000\"))\n        .check()\n}\n\n#[test]\nfn undefined() {\n    EvmTester::new()\n        .code(hex!(\"2a\"))\n        .gas(1)\n        .status(StatusCode::UndefinedInstruction)\n        .gas_left(0)\n        .check()\n}\n\n#[test]\nfn invalid() {\n    EvmTester::new()\n        .code(hex!(\"fe\"))\n        .gas(1)\n        .status(StatusCode::InvalidInstruction)\n        .gas_left(0)\n        .check()\n}\n\n#[test]\nfn inner_stop() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .opcode(OpCode::STOP)\n                .opcode(OpCode::POP),\n        )\n        .gas(3)\n        .status(StatusCode::Success)\n        .gas_used(3)\n        .check()\n}\n\n#[test]\nfn inner_return() {\n    EvmTester::new()\n        .code(Bytecode::new().ret(0, 0).pushv(0))\n        .gas(6)\n        .status(StatusCode::Success)\n        .gas_used(6)\n        .check()\n}\n\n#[test]\nfn inner_revert() {\n    EvmTester::new()\n        .code(Bytecode::new().revert(0, 0).pushv(0))\n        .gas(6)\n        .status(StatusCode::Revert)\n        .gas_used(6)\n        .check()\n}\n\n#[test]\nfn inner_invalid() {\n    EvmTester::new()\n        .revision(Revision::Frontier)\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .append(hex!(\"fe\"))\n                .opcode(OpCode::POP),\n        )\n        .gas(5)\n        .status(StatusCode::InvalidInstruction)\n        .gas_left(0)\n        .check()\n}\n\n#[test]\nfn inner_selfdestruct() {\n    EvmTester::new()\n        .revision(Revision::Frontier)\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .opcode(OpCode::SELFDESTRUCT)\n                .pushv(0),\n        )\n        .gas(3)\n        .status(StatusCode::Success)\n        .gas_used(3)\n        .check()\n}\n\n#[test]\nfn keccak256() {\n    EvmTester::new()\n        .code(hex!(\"6108006103ff2060005260206000f3\"))\n        .status(StatusCode::Success)\n        .gas_used(738)\n        .output_data(hex!(\n            \"aeffb38c06e111d84216396baefeb7fed397f303d5cb84a33f1e8b485c4a22da\"\n        ))\n        .check()\n}\n\n#[test]\nfn keccak256_empty() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::KECCAK256)\n                .ret_top(),\n        )\n        .output_data(hex!(\n            \"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470\"\n        ))\n        .check()\n}\n\n#[test]\nfn revert() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .append(hex!(\"60ee8053\")) // m[ee] == e\n                .append(hex!(\"600260edfd\")), // REVERT(ee,1)\n        )\n        .gas_used(39)\n        .status(StatusCode::Revert)\n        .output_data(hex!(\"00ee\"))\n        .check()\n}\n\n#[test]\nfn return_empty_buffer_at_offset_0() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .opcode(OpCode::MSIZE)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::RETURN),\n        )\n        .gas_used(5)\n        .check()\n}\n\n#[test]\nfn return_empty_buffer_at_high_offset() {\n    for (opcode, status) in [\n        (OpCode::RETURN, StatusCode::Success),\n        (OpCode::REVERT, StatusCode::Revert),\n    ] {\n        EvmTester::new()\n            .code(\n                Bytecode::new()\n                    .pushv(0)\n                    .opcode(OpCode::DIFFICULTY)\n                    .opcode(opcode),\n            )\n            .apply_host_fn(|host, _| {\n                host.tx_context.block_difficulty =\n                    hex!(\"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1\").into()\n            })\n            .status(status)\n            .check();\n    }\n}\n\n#[test]\nfn shl() {\n    EvmTester::new()\n        .code(hex!(\"600560011b6000526001601ff3\"))\n        .revision(Revision::Constantinople)\n        .gas_used(24)\n        .status(StatusCode::Success)\n        .output_data([5 << 1])\n        .check()\n}\n\n#[test]\nfn shr() {\n    EvmTester::new()\n        .code(hex!(\"600560011c6000526001601ff3\"))\n        .revision(Revision::Constantinople)\n        .gas_used(24)\n        .status(StatusCode::Success)\n        .output_data([5 >> 1])\n        .check()\n}\n\n#[test]\nfn sar() {\n    EvmTester::new()\n        .code(hex!(\"600160000360021d60005260016000f3\"))\n        .revision(Revision::Constantinople)\n        .gas_used(30)\n        .status(StatusCode::Success)\n        .output_data([0xff])\n        .check() // MSB of (-1 >> 2) == -1\n}\n\n#[test]\nfn sar_01() {\n    EvmTester::new()\n        .code(hex!(\"600060011d60005260016000f3\"))\n        .revision(Revision::Constantinople)\n        .gas_used(24)\n        .status(StatusCode::Success)\n        .output_data([0])\n        .check()\n}\n\n#[test]\nfn shift_overflow() {\n    for op in [OpCode::SHL, OpCode::SHR, OpCode::SAR] {\n        EvmTester::new()\n            .code(\n                Bytecode::new()\n                    .pushv(0)\n                    .opcode(OpCode::NOT)\n                    .pushv(0x100)\n                    .opcode(op)\n                    .ret_top(),\n            )\n            .revision(Revision::Constantinople)\n            .inspect_output(move |output| {\n                assert_eq!(\n                    output.iter().copied().map(u64::from).sum::<u64>(),\n                    if op == OpCode::SAR { 32 * 0xff } else { 0 }\n                );\n            })\n            .check()\n    }\n}\n\n#[test]\nfn undefined_instruction_analysis_overflow() {\n    let undefined_opcode = OpCode(0x0c);\n    EvmTester::new()\n        .code(Bytecode::new().opcode(undefined_opcode))\n        .status(StatusCode::UndefinedInstruction)\n        .check()\n}\n\n#[test]\nfn abort() {\n    for r in Revision::iter() {\n        EvmTester::new()\n            .code(hex!(\"fe\"))\n            .revision(r)\n            .status(StatusCode::InvalidInstruction)\n            .check()\n    }\n}\n\n#[test]\nfn staticmode() {\n    for op in [\n        OpCode::SSTORE,\n        OpCode::LOG0,\n        OpCode::LOG1,\n        OpCode::LOG2,\n        OpCode::LOG3,\n        OpCode::LOG4,\n        OpCode::CALL,\n        OpCode::CREATE,\n        OpCode::CREATE2,\n        OpCode::SELFDESTRUCT,\n    ] {\n        let mut code_prefix = Bytecode::new().pushv(1);\n        for _ in 0..6 {\n            code_prefix = code_prefix.opcode(OpCode::DUP1);\n        }\n\n        EvmTester::new()\n            .code(code_prefix.opcode(op))\n            .revision(Revision::Constantinople)\n            .set_static(true)\n            .status(StatusCode::StaticModeViolation)\n            .gas_left(0)\n            .check()\n    }\n}\n\n#[test]\nfn memory_big_allocation() {\n    const SIZE: usize = 256 * 1024 + 1;\n    EvmTester::new()\n        .code(Bytecode::new().ret(0, SIZE))\n        .status(StatusCode::Success)\n        .output_data([0; SIZE])\n        .check()\n}\n\n#[test]\nfn memory_grow_mstore8() {\n    let code = Bytecode::new()\n        .pushv(0)\n        .opcode(OpCode::CALLDATALOAD)\n        .pushv(0)\n        .opcode(OpCode::JUMPDEST)\n        .opcode(OpCode::DUP1)\n        .opcode(OpCode::DUP1)\n        .opcode(OpCode::MSTORE8)\n        .pushv(1)\n        .opcode(OpCode::ADD)\n        .opcode(OpCode::DUP1)\n        .opcode(OpCode::DUP3)\n        .opcode(OpCode::EQ)\n        .opcode(OpCode::ISZERO)\n        .pushv(5)\n        .opcode(OpCode::JUMPI)\n        .opcode(OpCode::MSIZE)\n        .pushv(0)\n        .opcode(OpCode::RETURN);\n\n    const SIZE: usize = 4 * 1024 + 256 + 1;\n    let input = hex!(\"0000000000000000000000000000000000000000000000000000000000001101\").to_vec();\n\n    EvmTester::new()\n        .code(code)\n        .input(input)\n        .status(StatusCode::Success)\n        .inspect_output(|output| {\n            assert_eq!(output.len(), ((SIZE + 31) / 32) * 32);\n            for i in 0..SIZE {\n                assert_eq!(output[i] as usize, i % 256);\n            }\n\n            for i in SIZE..output.len() {\n                assert_eq!(output[i], 0);\n            }\n        })\n        .check()\n}\n\n#[test]\nfn mstore8_memory_cost() {\n    for (gas, status) in [(12, StatusCode::Success), (11, StatusCode::OutOfGas)] {\n        EvmTester::new()\n            .code(Bytecode::new().pushv(0).mstore8(0))\n            .gas(gas)\n            .status(status)\n            .check()\n    }\n}\n\n#[test]\nfn keccak256_memory_cost() {\n    for (gas, status) in [(45, StatusCode::Success), (44, StatusCode::OutOfGas)] {\n        EvmTester::new()\n            .code(Bytecode::new().pushv(1).pushv(0).opcode(OpCode::KECCAK256))\n            .gas(gas)\n            .status(status)\n            .check()\n    }\n}\n\n#[test]\nfn calldatacopy_memory_cost() {\n    for (gas, status) in [(18, StatusCode::Success), (17, StatusCode::OutOfGas)] {\n        EvmTester::new()\n            .code(\n                Bytecode::new()\n                    .pushv(1)\n                    .pushv(0)\n                    .pushv(0)\n                    .opcode(OpCode::CALLDATACOPY),\n            )\n            .gas(gas)\n            .status(status)\n            .check()\n    }\n}\n\nconst MAX_CODE_SIZE: usize = 0x6000;\n\n#[test]\nfn max_code_size_push1() {\n    let mut code = Bytecode::new();\n    for _ in 0..MAX_CODE_SIZE / 2 {\n        code = code.pushv(1);\n    }\n    let code = code.build();\n    assert_eq!(code.len(), MAX_CODE_SIZE);\n\n    EvmTester::new()\n        .code(code.clone())\n        .status(StatusCode::StackOverflow)\n        .check();\n    EvmTester::new()\n        .code(code[..code.len() - 1].to_vec())\n        .status(StatusCode::StackOverflow)\n        .check();\n}\n\n#[test]\nfn reverse_16_stack_items() {\n    // This test puts values 1, 2, ... , 16 on the stack and then reverse them with SWAP opcodes.\n    // This uses all variants of SWAP instruction.\n\n    let n = 16;\n    let mut code = Bytecode::new();\n    for i in 1..=n {\n        code = code.pushv(i);\n    }\n    code = code.pushv(0); // Temporary stack item.\n    code = code\n        .opcode(OpCode::SWAP16)\n        .opcode(OpCode::SWAP1)\n        .opcode(OpCode::SWAP16); // Swap 1 and 16.\n    code = code\n        .opcode(OpCode::SWAP15)\n        .opcode(OpCode::SWAP2)\n        .opcode(OpCode::SWAP15); // Swap 2 and 15.\n    code = code\n        .opcode(OpCode::SWAP14)\n        .opcode(OpCode::SWAP3)\n        .opcode(OpCode::SWAP14);\n    code = code\n        .opcode(OpCode::SWAP13)\n        .opcode(OpCode::SWAP4)\n        .opcode(OpCode::SWAP13);\n    code = code\n        .opcode(OpCode::SWAP12)\n        .opcode(OpCode::SWAP5)\n        .opcode(OpCode::SWAP12);\n    code = code\n        .opcode(OpCode::SWAP11)\n        .opcode(OpCode::SWAP6)\n        .opcode(OpCode::SWAP11);\n    code = code\n        .opcode(OpCode::SWAP10)\n        .opcode(OpCode::SWAP7)\n        .opcode(OpCode::SWAP10);\n    code = code\n        .opcode(OpCode::SWAP9)\n        .opcode(OpCode::SWAP8)\n        .opcode(OpCode::SWAP9);\n    code = code.opcode(OpCode::POP);\n    for i in 0..n {\n        code = code.mstore8(i);\n    }\n    code = code.ret(0, n);\n\n    EvmTester::new()\n        .code(code)\n        .status(StatusCode::Success)\n        .output_data(hex!(\"0102030405060708090a0b0c0d0e0f10\"))\n        .check()\n}\n\n#[test]\nfn memory_access() {\n    struct MemoryAccessOpcode {\n        opcode: OpCode,\n        memory_index_arg: i8,\n        memory_size_arg: i8,\n    }\n\n    impl From<(OpCode, i8, i8)> for MemoryAccessOpcode {\n        fn from((opcode, memory_index_arg, memory_size_arg): (OpCode, i8, i8)) -> Self {\n            Self {\n                opcode,\n                memory_index_arg,\n                memory_size_arg,\n            }\n        }\n    }\n\n    struct MemoryAccessParams {\n        index: u64,\n        size: u64,\n    }\n\n    impl From<(u64, u64)> for MemoryAccessParams {\n        fn from((index, size): (u64, u64)) -> Self {\n            Self { index, size }\n        }\n    }\n\n    let memory_access_opcodes: Vec<MemoryAccessOpcode> = vec![\n        (OpCode::KECCAK256, 0, 1),\n        (OpCode::CALLDATACOPY, 0, 2),\n        (OpCode::CODECOPY, 0, 2),\n        (OpCode::MLOAD, 0, -1),\n        (OpCode::MSTORE, 0, -1),\n        (OpCode::MSTORE8, 0, -1),\n        (OpCode::EXTCODECOPY, 1, 3),\n        (OpCode::RETURNDATACOPY, 0, 2),\n        (OpCode::LOG0, 0, 1),\n        (OpCode::LOG1, 0, 1),\n        (OpCode::LOG2, 0, 1),\n        (OpCode::LOG3, 0, 1),\n        (OpCode::LOG4, 0, 1),\n        (OpCode::RETURN, 0, 1),\n        (OpCode::REVERT, 0, 1),\n        (OpCode::CALL, 3, 4),\n        (OpCode::CALL, 5, 6),\n        (OpCode::CALLCODE, 3, 4),\n        (OpCode::CALLCODE, 5, 6),\n        (OpCode::DELEGATECALL, 2, 3),\n        (OpCode::DELEGATECALL, 4, 5),\n        (OpCode::STATICCALL, 2, 3),\n        (OpCode::STATICCALL, 4, 5),\n        (OpCode::CREATE, 1, 2),\n        (OpCode::CREATE2, 1, 2),\n    ]\n    .into_iter()\n    .map(MemoryAccessOpcode::from)\n    .collect();\n\n    let memory_access_test_cases: Vec<MemoryAccessParams> = vec![\n        (0, 0x100000000),\n        (0x80000000, 0x80000000),\n        (0x100000000, 0),\n        (0x100000000, 1),\n        (0x100000000, 0x100000000),\n    ]\n    .into_iter()\n    .map(MemoryAccessParams::from)\n    .collect();\n\n    let metrics = &*evmodin::instructions::PROPERTIES;\n\n    for p in memory_access_test_cases {\n        let push_size = format!(\"64{:0>10x}\", p.size);\n        let push_index = format!(\"64{:0>10x}\", p.index);\n\n        for t in &memory_access_opcodes {\n            let num_args = metrics[t.opcode.to_usize()].unwrap().stack_height_required as i8;\n            let mut h = max(num_args, t.memory_size_arg + 1);\n            let mut code = Bytecode::new();\n\n            if t.memory_size_arg >= 0 {\n                h -= 1;\n                while h != t.memory_size_arg {\n                    code = code.pushv(0);\n                    h -= 1;\n                }\n\n                code = code.append(hex::decode(&push_size).unwrap());\n            } else if p.index == 0 || p.size == 0 {\n                continue; // Skip opcodes not having SIZE argument.\n            }\n\n            h -= 1;\n            while h != t.memory_index_arg {\n                code = code.pushv(0);\n                h -= 1;\n            }\n\n            code = code.append(hex::decode(&push_index).unwrap());\n\n            while h != 0 {\n                code = code.pushv(0);\n                h -= 1;\n            }\n\n            code = code.opcode(t.opcode);\n\n            let gas = 8796294610952;\n\n            println!(\n                \"offset = {:#02x} size = {:#02x} opcode {}\",\n                p.index, p.size, t.opcode\n            );\n\n            let tester = EvmTester::new()\n                .code(code)\n                .gas(gas)\n                .revision(Revision::Constantinople);\n\n            if p.size == 0 {\n                // It is allowed to request 0 size memory at very big offset.\n                assert_ne!(\n                    tester\n                        .status(if t.opcode == OpCode::REVERT {\n                            StatusCode::Revert\n                        } else {\n                            StatusCode::Success\n                        })\n                        .check_and_get_result()\n                        .gas_left,\n                    0\n                );\n            } else {\n                if t.opcode == OpCode::RETURNDATACOPY {\n                    // In case of RETURNDATACOPY the \"invalid memory access\" might also be returned.\n                    tester.status_one_of([StatusCode::OutOfGas, StatusCode::InvalidMemoryAccess])\n                } else {\n                    tester.status(StatusCode::OutOfGas)\n                }\n                .gas_left(0)\n                .check();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/other.rs",
    "content": "use evmodin::{opcode::*, util::*, *};\n\n#[test]\nfn loop_full_of_jumpdests() {\n    // The code is a simple loop with a counter taken from the input or a constant (325) if the\n    // input is zero. The loop body contains of only JUMPDESTs, as much as the code size limit\n    // allows.\n\n    // The `mul(325, iszero(dup1(calldataload(0)))) + OP_OR` is equivalent of\n    // `((x == 0) * 325) | x`\n    // what is\n    // `x == 0 ? 325 : x`.\n\n    // The `not_(0)` is -1 so we can do `loop_counter + (-1)` to decrease the loop counter.\n\n    let code = Bytecode::new()\n        .pushv(15)\n        .pushv(0)\n        .opcode(OpCode::NOT)\n        .pushv(0)\n        .opcode(OpCode::CALLDATALOAD)\n        .opcode(OpCode::DUP1)\n        .opcode(OpCode::ISZERO)\n        .pushv(325)\n        .opcode(OpCode::MUL)\n        + OpCode::OR\n        + (MAX_CODE_SIZE - 20) * OpCode::JUMPDEST\n        + OpCode::DUP2\n        + OpCode::ADD\n        + OpCode::DUP1\n        + OpCode::DUP4\n        + OpCode::JUMPI;\n\n    assert_eq!(code.clone().build().len(), MAX_CODE_SIZE);\n\n    EvmTester::new()\n        .code(code)\n        .status(StatusCode::Success)\n        .gas_used(7987882)\n        .check()\n}\n\n#[test]\nfn jumpdest_with_high_offset() {\n    for offset in [3, 16383, 16384, 32767, 32768, 65535, 65536] {\n        let mut code = Bytecode::new().pushv(offset).opcode(OpCode::JUMP).build();\n        code.resize(offset, OpCode::INVALID.to_u8());\n        code.push(OpCode::JUMPDEST.to_u8());\n        EvmTester::new()\n            .code(code)\n            .status(StatusCode::Success)\n            .check()\n    }\n}\n"
  },
  {
    "path": "tests/state.rs",
    "content": "use ethereum_types::*;\nuse evmodin::{\n    opcode::*,\n    util::{mocked_host::*, *},\n    *,\n};\nuse hex_literal::hex;\n\n#[test]\nfn code() {\n    // CODESIZE 2 0 CODECOPY RETURN(0,9)\n    let code = hex!(\"38600260003960096000f3\");\n    EvmTester::new()\n        .code(code)\n        .gas_used(23)\n        .output_data(&code[2..11])\n        .check()\n}\n\n#[test]\nfn codecopy_combinations() {\n    // The CODECOPY arguments are provided in calldata: first byte is index, second byte is size.\n    // The whole copied code is returned.\n    let code = Bytecode::new()\n        .pushv(0)\n        .opcode(OpCode::CALLDATALOAD)\n        .pushv(1)\n        .opcode(OpCode::BYTE)\n        .opcode(OpCode::DUP1)\n        .pushv(0)\n        .opcode(OpCode::CALLDATALOAD)\n        .pushv(0)\n        .opcode(OpCode::BYTE)\n        .pushv(0)\n        .opcode(OpCode::CODECOPY)\n        .pushv(0)\n        .opcode(OpCode::RETURN)\n        .build();\n\n    assert_eq!(code.len(), 0x13);\n\n    for (input, output) in [\n        (hex!(\"0013\"), code.clone()),\n        (hex!(\"0012\"), code[..0x12].to_vec()),\n        (hex!(\"0014\"), code.iter().copied().chain([0]).collect()),\n        (hex!(\"1300\"), vec![]),\n        (hex!(\"1400\"), vec![]),\n        (hex!(\"1200\"), vec![]),\n        (hex!(\"1301\"), hex!(\"00\").to_vec()),\n        (hex!(\"1401\"), hex!(\"00\").to_vec()),\n        (hex!(\"1201\"), code[0x12..0x12 + 1].to_vec()),\n    ] {\n        EvmTester::new()\n            .code(code.clone())\n            .input(input.to_vec())\n            .output_data(output)\n            .check()\n    }\n}\n\n#[test]\nfn storage() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .sstore(0xee, 0xff)\n                .sload(0xee)\n                .mstore8(0)\n                .ret(0, 1),\n        )\n        .gas(100000)\n        .status(StatusCode::Success)\n        .gas_left(99776 - 20000)\n        .output_data(hex!(\"ff\"))\n        .check()\n}\n\n#[test]\nfn sstore_pop_stack() {\n    EvmTester::new()\n        .code(hex!(\"60008060015560005360016000f3\"))\n        .gas(100000)\n        .status(StatusCode::Success)\n        .output_data(hex!(\"00\"))\n        .check()\n}\n\n#[test]\nfn sload_cost_pre_tangerine_whistle() {\n    EvmTester::new()\n        .code(hex!(\"60008054\"))\n        .revision(Revision::Homestead)\n        .apply_host_fn(|host, message| {\n            host.accounts.entry(message.recipient).or_default();\n        })\n        .gas(56)\n        .status(StatusCode::Success)\n        .gas_left(0)\n        .inspect_host(|host, message| {\n            assert_eq!(host.accounts[&message.recipient].storage.len(), 0);\n        })\n        .check()\n}\n\n#[test]\nfn sstore_out_of_block_gas() {\n    for (gas, status) in [\n        // Barely enough gas to execute successfully.\n        (20011, StatusCode::Success),\n        // Out of block gas - 1 too low.\n        (20010, StatusCode::OutOfGas),\n        // Out of block gas - 2 too low.\n        (20009, StatusCode::OutOfGas),\n        // SSTORE instructions out of gas.\n        (20008, StatusCode::OutOfGas),\n    ] {\n        EvmTester::new()\n            .code(Bytecode::new().pushv(0).sstore(0, 1).opcode(OpCode::POP))\n            .gas(gas)\n            .status(status)\n            .check()\n    }\n}\n\n#[test]\nfn sstore_cost() {\n    for revision in [\n        Revision::Byzantium,\n        Revision::Constantinople,\n        Revision::Petersburg,\n        Revision::Istanbul,\n    ] {\n        let v1 = 1.into();\n\n        fn get_storage<K: Into<U256>>(host: &mut MockedHost, key: K) -> &mut StorageValue {\n            host.accounts\n                .entry(Address::zero())\n                .or_default()\n                .storage\n                .entry(key.into())\n                .or_default()\n        }\n\n        let t = EvmTester::new().revision(revision);\n\n        // Added:\n        t.clone()\n            .code(Bytecode::new().sstore(1, 1))\n            .gas_used(20006)\n            .status(StatusCode::Success)\n            .check();\n\n        // Deleted:\n        t.clone()\n            .code(Bytecode::new().sstore(1, 0))\n            .apply_host_fn(move |host, _| {\n                get_storage(host, v1).value = v1;\n            })\n            .gas_used(5006)\n            .status(StatusCode::Success)\n            .check();\n\n        // Modified:\n        t.clone()\n            .code(Bytecode::new().sstore(1, 2))\n            .apply_host_fn(move |host, _| {\n                get_storage(host, v1).value = v1;\n            })\n            .gas_used(5006)\n            .status(StatusCode::Success)\n            .check();\n\n        // Unchanged:\n        t.clone()\n            .code(Bytecode::new().sstore(1, 1))\n            .apply_host_fn(move |host, _| {\n                get_storage(host, v1).value = v1;\n            })\n            .gas_used(match revision {\n                Revision::Istanbul => 806,\n                Revision::Constantinople => 206,\n                _ => 5006,\n            })\n            .status(StatusCode::Success)\n            .check();\n\n        // Added & unchanged:\n        t.clone()\n            .code(Bytecode::new().sstore(1, 1).sstore(1, 1))\n            .gas_used(match revision {\n                Revision::Istanbul => 20812,\n                Revision::Constantinople => 20212,\n                _ => 25012,\n            })\n            .status(StatusCode::Success)\n            .check();\n\n        // Modified again:\n        t.clone()\n            .code(Bytecode::new().sstore(1, 2))\n            .apply_host_fn(move |host, _| {\n                let s = get_storage(host, v1);\n                s.dirty = true;\n                s.value = v1;\n            })\n            .status(StatusCode::Success)\n            .gas_used(match revision {\n                Revision::Istanbul => 806,\n                Revision::Constantinople => 206,\n                _ => 5006,\n            })\n            .check();\n\n        // Added & modified again:\n        t.clone()\n            .code(Bytecode::new().sstore(1, 1).sstore(1, 2))\n            .status(StatusCode::Success)\n            .gas_used(match revision {\n                Revision::Istanbul => 20812,\n                Revision::Constantinople => 20212,\n                _ => 25012,\n            })\n            .check();\n\n        // Modified & modified again:\n        t.clone()\n            .code(Bytecode::new().sstore(1, 2).sstore(1, 3))\n            .apply_host_fn(move |host, _| {\n                get_storage(host, v1).value = v1;\n            })\n            .status(StatusCode::Success)\n            .gas_used(match revision {\n                Revision::Istanbul => 5812,\n                Revision::Constantinople => 5212,\n                _ => 10012,\n            })\n            .check();\n\n        // Modified & modified again back to original:t.clone()\n        t.clone()\n            .code(Bytecode::new().sstore(1, 2).sstore(1, 1))\n            .apply_host_fn(move |host, _| {\n                get_storage(host, v1).value = v1;\n            })\n            .status(StatusCode::Success)\n            .gas_used(match revision {\n                Revision::Istanbul => 5812,\n                Revision::Constantinople => 5212,\n                _ => 10012,\n            })\n            .check();\n    }\n}\n\n#[test]\nfn sstore_below_stipend() {\n    let code = Bytecode::new().sstore(0, 0);\n\n    let t = EvmTester::new().code(code);\n\n    for (revision, status) in [\n        (Revision::Homestead, StatusCode::OutOfGas),\n        (Revision::Constantinople, StatusCode::Success),\n        (Revision::Istanbul, StatusCode::OutOfGas),\n    ] {\n        t.clone()\n            .revision(revision)\n            .gas(2306)\n            .status(status)\n            .check()\n    }\n\n    t.revision(Revision::Constantinople)\n        .gas(2307)\n        .status(StatusCode::Success)\n        .check()\n}\n\n#[test]\nfn tx_context() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .opcode(OpCode::TIMESTAMP)\n                .opcode(OpCode::COINBASE)\n                .opcode(OpCode::OR)\n                .opcode(OpCode::GASPRICE)\n                .opcode(OpCode::OR)\n                .opcode(OpCode::NUMBER)\n                .opcode(OpCode::OR)\n                .opcode(OpCode::DIFFICULTY)\n                .opcode(OpCode::OR)\n                .opcode(OpCode::GASLIMIT)\n                .opcode(OpCode::OR)\n                .opcode(OpCode::ORIGIN)\n                .opcode(OpCode::OR)\n                .opcode(OpCode::CHAINID)\n                .opcode(OpCode::OR)\n                .ret_top(),\n        )\n        .revision(Revision::Istanbul)\n        .apply_host_fn(|host, _| {\n            host.tx_context.block_timestamp = 0xdd;\n            host.tx_context.block_number = 0x1100;\n            host.tx_context.block_gas_limit = 0x990000;\n            host.tx_context.chain_id =\n                hex!(\"00000000000000000000000000000000000000000000000000000000aa000000\").into();\n            host.tx_context.block_coinbase.0[1] = 0xcc;\n            host.tx_context.tx_origin.0[2] = 0x55;\n            host.tx_context.block_difficulty =\n                hex!(\"00dd000000000000000000000000000000000000000000000000000000000000\").into();\n            host.tx_context.tx_gas_price =\n                hex!(\"0000660000000000000000000000000000000000000000000000000000000000\").into();\n        })\n        .status(StatusCode::Success)\n        .gas_used(52)\n        .inspect_output(|output_data| {\n            assert_eq!(output_data.len(), 32);\n            assert_eq!(output_data[31], 0xdd);\n            assert_eq!(output_data[30], 0x11);\n            assert_eq!(output_data[29], 0x99);\n            assert_eq!(output_data[28], 0xaa);\n            assert_eq!(output_data[14], 0x55);\n            assert_eq!(output_data[13], 0xcc);\n            assert_eq!(output_data[2], 0x66);\n            assert_eq!(output_data[1], 0xdd);\n        })\n        .check()\n}\n\n#[test]\nfn balance() {\n    EvmTester::new()\n        .apply_host_fn(|host, msg| {\n            host.accounts.entry(msg.recipient).or_default().balance = 0x0504030201_u64.into()\n        })\n        .code(\n            Bytecode::new()\n                .opcode(OpCode::ADDRESS)\n                .opcode(OpCode::BALANCE)\n                .mstore(0)\n                .ret(32 - 6, 6),\n        )\n        .gas_used(417)\n        .status(StatusCode::Success)\n        .output_data(hex!(\"000504030201\"))\n        .check()\n}\n\n#[test]\nfn account_info_homestead() {\n    let t = EvmTester::new()\n        .revision(Revision::Homestead)\n        .apply_host_fn(|host, msg| {\n            let acc = host.accounts.entry(msg.recipient).or_default();\n            acc.balance = 1.into();\n            acc.code = [1].to_vec().into();\n        });\n\n    t.clone()\n        .code(\n            Bytecode::new()\n                .opcode(OpCode::ADDRESS)\n                .opcode(OpCode::BALANCE)\n                .ret_top(),\n        )\n        .status(StatusCode::Success)\n        .gas_used(37)\n        .output_value(1)\n        .check();\n\n    t.clone()\n        .code(\n            Bytecode::new()\n                .opcode(OpCode::ADDRESS)\n                .opcode(OpCode::EXTCODESIZE)\n                .ret_top(),\n        )\n        .status(StatusCode::Success)\n        .gas_used(37)\n        .output_value(1)\n        .check();\n\n    t.code(\n        Bytecode::new()\n            .pushv(1)\n            .pushv(0)\n            .pushv(0)\n            .opcode(OpCode::ADDRESS)\n            .opcode(OpCode::EXTCODECOPY)\n            .ret(0, 1),\n    )\n    .status(StatusCode::Success)\n    .gas_used(43)\n    .output_data([1])\n    .check()\n}\n\n#[test]\nfn selfbalance() {\n    let t = EvmTester::new()\n        .apply_host_fn(|host, msg| {\n            host.accounts.entry(msg.recipient).or_default().balance = 0x0504030201_u64.into();\n        })\n        // NOTE: adding push here to balance out the stack pre-Istanbul (needed to get undefined\n        // instruction as a result)\n        .code(\n            Bytecode::new()\n                .pushv(1)\n                .opcode(OpCode::SELFBALANCE)\n                .mstore(0)\n                .ret(32 - 6, 6),\n        );\n\n    t.clone()\n        .revision(Revision::Constantinople)\n        .status(StatusCode::UndefinedInstruction)\n        .check();\n\n    t.revision(Revision::Istanbul)\n        .status(StatusCode::Success)\n        .gas_used(23)\n        .output_data(hex!(\"000504030201\"))\n        .check()\n}\n\n#[test]\nfn log() {\n    for op in [\n        OpCode::LOG0,\n        OpCode::LOG1,\n        OpCode::LOG2,\n        OpCode::LOG3,\n        OpCode::LOG4,\n    ] {\n        let n = op.to_usize() - OpCode::LOG0.to_usize();\n        EvmTester::new()\n            .code(\n                Bytecode::new()\n                    .pushv(1)\n                    .pushv(2)\n                    .pushv(3)\n                    .pushv(4)\n                    .mstore8_value(2, 0x77)\n                    .pushv(2)\n                    .pushv(2)\n                    .opcode(op),\n            )\n            .status(StatusCode::Success)\n            .gas_used((421 + n * 375) as i64)\n            .inspect_host(move |host, _| {\n                let r = host.recorded.lock();\n\n                assert_eq!(r.logs.len(), 1);\n                let last_log = r.logs.last().unwrap();\n                assert_eq!(&*last_log.data, &hex!(\"7700\") as &[u8]);\n                assert_eq!(last_log.topics.len(), n);\n                for i in 0..n {\n                    assert_eq!(last_log.topics[i], (4 - i).into());\n                }\n            })\n            .check()\n    }\n}\n\n#[test]\nfn log0_empty() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::LOG0),\n        )\n        .inspect_host(|host, _| {\n            let r = host.recorded.lock();\n            assert_eq!(r.logs.len(), 1);\n            let last_log = r.logs.last().unwrap();\n            assert_eq!(last_log.topics.len(), 0);\n            assert_eq!(last_log.data.len(), 0);\n        })\n        .check()\n}\n\n#[test]\nfn log_data_cost() {\n    for op in [\n        OpCode::LOG0,\n        OpCode::LOG1,\n        OpCode::LOG2,\n        OpCode::LOG3,\n        OpCode::LOG4,\n    ] {\n        let num_topics = op.to_u8() - OpCode::LOG0.to_u8();\n        let mut code = Bytecode::new().pushv(0);\n        for _ in 0..4 {\n            code = code.opcode(OpCode::DUP1);\n        }\n        code = code.pushv(1).pushv(0).opcode(op);\n\n        let cost = 407 + num_topics as usize * 375;\n\n        EvmTester::new()\n            .code(code)\n            .gas_used(cost as i64)\n            .status(StatusCode::Success)\n            .inspect_host(|host, _| {\n                assert_eq!(host.recorded.lock().logs.len(), 1);\n            })\n            .check()\n    }\n}\n\n#[test]\nfn selfdestruct() {\n    EvmTester::new()\n        .code(hex!(\"6009ff\"))\n        .revision(Revision::Spurious)\n        .status(StatusCode::Success)\n        .gas_used(5003)\n        .inspect_host(|host, _| {\n            assert_eq!(host.recorded.lock().selfdestructs.len(), 1);\n            assert_eq!(\n                host.recorded\n                    .lock()\n                    .selfdestructs\n                    .last()\n                    .unwrap()\n                    .beneficiary[19],\n                9\n            );\n        })\n        .check();\n\n    EvmTester::new()\n        .code(hex!(\"6007ff\"))\n        .revision(Revision::Homestead)\n        .status(StatusCode::Success)\n        .gas_used(3)\n        .inspect_host(|host, _| {\n            assert_eq!(host.recorded.lock().selfdestructs.len(), 1);\n            assert_eq!(\n                host.recorded\n                    .lock()\n                    .selfdestructs\n                    .last()\n                    .unwrap()\n                    .beneficiary[19],\n                7\n            );\n        })\n        .check();\n\n    EvmTester::new()\n        .code(hex!(\"6008ff\"))\n        .revision(Revision::Tangerine)\n        .status(StatusCode::Success)\n        .gas_used(30003)\n        .inspect_host(|host, _| {\n            assert_eq!(host.recorded.lock().selfdestructs.len(), 1);\n            assert_eq!(\n                host.recorded\n                    .lock()\n                    .selfdestructs\n                    .last()\n                    .unwrap()\n                    .beneficiary[19],\n                8\n            );\n        })\n        .check();\n}\n\n#[test]\nfn selfdestruct_with_balance() {\n    let beneficiary = Address::zero();\n    let code = Bytecode::new()\n        .pushb(beneficiary.0)\n        .opcode(OpCode::SELFDESTRUCT);\n\n    let mut t = EvmTester::new()\n        .code(code)\n        .destination(hex!(\"000000000000000000000000000000000000005e\"))\n        .apply_host_fn(|host, msg| {\n            host.accounts.entry(msg.recipient).or_default().balance = 0.into();\n        });\n\n    t.clone()\n        .revision(Revision::Homestead)\n        .status(StatusCode::Success)\n        .gas_used(3)\n        .inspect_host(|host, msg| {\n            let r = host.recorded.lock();\n\n            assert_eq!(r.account_accesses, [msg.recipient]); // Selfdestruct.\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Tangerine)\n        .status(StatusCode::Success)\n        .gas_used(30003)\n        .inspect_host(move |host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    // Exists?\n                    beneficiary,\n                    // Selfdestruct.\n                    msg.recipient\n                ]\n            );\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Tangerine)\n        .gas(30002)\n        .status(StatusCode::OutOfGas)\n        .inspect_host(move |host, _| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    // Exists?\n                    beneficiary\n                ]\n            );\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Spurious)\n        .status(StatusCode::Success)\n        .gas_used(5003)\n        .inspect_host(move |host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    // Balance.\n                    msg.recipient,\n                    // Selfdestruct.\n                    msg.recipient,\n                ]\n            )\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Spurious)\n        .gas(5002)\n        .status(StatusCode::OutOfGas)\n        .inspect_host(|host, _| {\n            assert_eq!(host.recorded.lock().account_accesses, []);\n        })\n        .check();\n\n    t = t.apply_host_fn(move |host, msg| {\n        host.accounts.entry(msg.recipient).or_default().balance = 1.into();\n    });\n\n    t.clone()\n        .revision(Revision::Homestead)\n        .gas_used(3)\n        .status(StatusCode::Success)\n        .inspect_host(|host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    // Selfdestruct.\n                    msg.recipient\n                ]\n            );\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Tangerine)\n        .gas_used(30003)\n        .status(StatusCode::Success)\n        .inspect_host(move |host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    // Exists?\n                    beneficiary,\n                    // Selfdestruct.\n                    msg.recipient\n                ]\n            );\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Tangerine)\n        .gas(30002)\n        .status(StatusCode::OutOfGas)\n        .inspect_host(move |host, _| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    // Exists?\n                    beneficiary,\n                ]\n            );\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Spurious)\n        .gas_used(30003)\n        .status(StatusCode::Success)\n        .inspect_host(move |host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    // Balance\n                    msg.recipient,\n                    // Exists?\n                    beneficiary,\n                    // Selfdestruct.\n                    msg.recipient\n                ]\n            );\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Spurious)\n        .gas(30002)\n        .status(StatusCode::OutOfGas)\n        .inspect_host(move |host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    // Balance\n                    msg.recipient,\n                    // Exists?\n                    beneficiary,\n                ]\n            );\n        })\n        .check();\n\n    t = t.apply_host_fn(move |host, msg| {\n        host.accounts.entry(beneficiary).or_default(); // Beneficiary exists.\n        host.accounts.get_mut(&msg.recipient).unwrap().balance = 0.into();\n    });\n\n    t.clone()\n        .revision(Revision::Homestead)\n        .gas_used(3)\n        .status(StatusCode::Success)\n        .inspect_host(|host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    // Selfdestruct.\n                    msg.recipient,\n                ]\n            );\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Tangerine)\n        .gas_used(5003)\n        .status(StatusCode::Success)\n        .inspect_host(move |host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    // Exists?\n                    beneficiary,\n                    // Selfdestruct.\n                    msg.recipient,\n                ]\n            );\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Tangerine)\n        .gas(5002)\n        .status(StatusCode::OutOfGas)\n        .inspect_host(move |host, _| {\n            assert_eq!(host.recorded.lock().account_accesses, []);\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Spurious)\n        .gas(5003)\n        .status(StatusCode::Success)\n        .inspect_host(move |host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    // Balance.\n                    msg.recipient,\n                    // Selfdestruct.\n                    msg.recipient,\n                ]\n            );\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Spurious)\n        .gas(5002)\n        .status(StatusCode::OutOfGas)\n        .inspect_host(|host, _| {\n            assert_eq!(host.recorded.lock().account_accesses, []);\n        })\n        .check();\n\n    t = t.apply_host_fn(|host, msg| {\n        host.accounts.entry(msg.recipient).or_default().balance = 1.into();\n    });\n\n    t.clone()\n        .revision(Revision::Homestead)\n        .gas_used(3)\n        .status(StatusCode::Success)\n        .inspect_host(|host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    // Selfdestruct\n                    msg.recipient\n                ]\n            );\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Tangerine)\n        .gas_used(5003)\n        .status(StatusCode::Success)\n        .inspect_host(move |host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    // Exists?\n                    beneficiary,\n                    // Selfdestruct\n                    msg.recipient\n                ]\n            );\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Tangerine)\n        .gas(5002)\n        .status(StatusCode::OutOfGas)\n        .inspect_host(|host, _| {\n            assert_eq!(host.recorded.lock().account_accesses, []);\n        })\n        .check();\n\n    t.clone()\n        .revision(Revision::Spurious)\n        .gas_used(5003)\n        .status(StatusCode::Success)\n        .inspect_host(move |host, msg| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [\n                    // Balance\n                    msg.recipient,\n                    // Exists?\n                    beneficiary,\n                    // Selfdestruct\n                    msg.recipient\n                ]\n            );\n        })\n        .check();\n\n    t.revision(Revision::Spurious)\n        .gas(5002)\n        .status(StatusCode::OutOfGas)\n        .inspect_host(|host, _| {\n            assert_eq!(host.recorded.lock().account_accesses, []);\n        })\n        .check();\n}\n\n#[test]\nfn blockhash() {\n    let t = EvmTester::new()\n        .code(hex!(\"60004060005260206000f3\"))\n        .status(StatusCode::Success)\n        .gas_used(38)\n        .apply_host_fn(|host, _| {\n            let mut v = H256(host.block_hash.into());\n            v.0[13] = 0x13;\n            host.block_hash = <[u8; 32]>::from(v).into();\n        });\n\n    t.clone()\n        .apply_host_fn(|host, _| {\n            host.tx_context.block_number = 0;\n        })\n        .inspect_output(|output| {\n            assert_eq!(output.len(), 32);\n            assert_eq!(output[13], 0);\n        })\n        .inspect_host(|host, _| {\n            assert_eq!(host.recorded.lock().blockhashes, [] as [u64; 0]);\n        })\n        .check();\n\n    t.clone()\n        .apply_host_fn(|host, _| {\n            host.tx_context.block_number = 257;\n        })\n        .inspect_output(|output| {\n            assert_eq!(output.len(), 32);\n            assert_eq!(output[13], 0);\n        })\n        .inspect_host(|host, _| {\n            assert_eq!(host.recorded.lock().blockhashes, [] as [u64; 0]);\n        })\n        .check();\n\n    t.apply_host_fn(|host, _| {\n        host.tx_context.block_number = 256;\n    })\n    .inspect_output(|output| {\n        assert_eq!(output.len(), 32);\n        assert_eq!(output[13], 0x13);\n    })\n    .inspect_host(|host, _| {\n        assert_eq!(host.recorded.lock().blockhashes, [0]);\n    })\n    .check();\n}\n\n#[test]\nfn extcode() {\n    let addr = hex!(\"fffffffffffffffffffffffffffffffffffffffe\").into();\n\n    EvmTester::new()\n        .apply_host_fn(move |host, _| {\n            host.accounts.entry(addr).or_default().code = (&hex!(\"0a0b0c0d\") as &[u8]).into();\n        })\n        .code(\n            Bytecode::new()\n                .append(hex!(\"6002600003803b60019003\")) // S = EXTCODESIZE(-2) - 1\n                .append(hex!(\"90600080913c\")) // EXTCODECOPY(-2, 0, 0, S)\n                .append(hex!(\"60046000f3\")), // RETURN(0, 4)\n        )\n        .gas_used(1445)\n        .status(StatusCode::Success)\n        .inspect(move |host, _, output| {\n            assert_eq!(output.len(), 4);\n            assert_eq!(output[..3], host.accounts[&addr].code[..3]);\n            assert_eq!(output[3], 0);\n            assert_eq!(host.recorded.lock().account_accesses.len(), 2);\n            assert_eq!(host.recorded.lock().account_accesses[0].0[19], 0xfe);\n            assert_eq!(host.recorded.lock().account_accesses[1].0[19], 0xfe);\n        })\n        .check()\n}\n\n#[test]\nfn extcodesize() {\n    EvmTester::new()\n        .apply_host_fn(|host, _| {\n            host.accounts\n                .entry(hex!(\"0000000000000000000000000000000000000002\").into())\n                .or_default()\n                .code = (&[0_u8] as &[u8]).into();\n        })\n        .code(\n            Bytecode::new()\n                .pushv(2)\n                .opcode(OpCode::EXTCODESIZE)\n                .ret_top(),\n        )\n        .output_value(1)\n        .check()\n}\n\n#[test]\nfn extcodecopy_big_index() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(1)\n                .opcode(OpCode::DUP1)\n                .pushv(u64::from(u32::MAX) + 1)\n                .pushv(0)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::EXTCODECOPY)\n                .pushv(0)\n                .opcode(OpCode::RETURN),\n        )\n        .output_data(hex!(\"00\"))\n        .check()\n}\n\n#[test]\nfn extcodehash() {\n    let t = EvmTester::new()\n        .apply_host_fn(|host, _| {\n            host.accounts.entry(Address::zero()).or_default().code_hash =\n                hex!(\"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\").into();\n        })\n        .code(hex!(\"60003f60005260206000f3\"));\n\n    t.clone()\n        .revision(Revision::Byzantium)\n        .status(StatusCode::UndefinedInstruction)\n        .check();\n\n    t.revision(Revision::Constantinople)\n        .status(StatusCode::Success)\n        .gas_used(418)\n        .inspect(|host, _, output| {\n            assert_eq!(\n                output,\n                <[u8; 32]>::from(host.accounts[&Address::zero()].code_hash)\n            );\n        })\n        .check()\n}\n\n#[test]\nfn codecopy_empty() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::CODECOPY)\n                .opcode(OpCode::MSIZE)\n                .ret_top(),\n        )\n        .status(StatusCode::Success)\n        .output_value(0)\n        .check()\n}\n\n#[test]\nfn extcodecopy_empty() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(0)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::EXTCODECOPY)\n                .opcode(OpCode::MSIZE)\n                .ret_top(),\n        )\n        .status(StatusCode::Success)\n        .output_value(0)\n        .check()\n}\n\n#[test]\nfn codecopy_memory_cost() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(1)\n                .pushv(0)\n                .pushv(0)\n                .opcode(OpCode::CODECOPY),\n        )\n        .status(StatusCode::Success)\n        .gas_used(18)\n        .check()\n}\n\n#[test]\nfn extcodecopy_memory_cost() {\n    EvmTester::new()\n        .code(\n            Bytecode::new()\n                .pushv(1)\n                .pushv(0)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::DUP1)\n                .opcode(OpCode::EXTCODECOPY),\n        )\n        .gas_used(718)\n        .check()\n}\n\n#[test]\nfn extcodecopy_nonzero_index() {\n    let index = 15;\n    let code = Bytecode::new()\n        .pushv(2)\n        .pushv(index)\n        .pushv(0)\n        .pushv(0xa)\n        .opcode(OpCode::EXTCODECOPY)\n        .ret(0, 2)\n        .build();\n    assert_eq!(code.len() + 1, index);\n\n    EvmTester::new()\n        .apply_host_fn(move |host, _| {\n            let mut code = std::iter::repeat(0).take(16).collect::<Vec<u8>>();\n            code[index] = 0xc0;\n            host.accounts\n                .entry(hex!(\"000000000000000000000000000000000000000a\").into())\n                .or_default()\n                .code = code.into();\n        })\n        .code(code)\n        .status(StatusCode::Success)\n        .output_data(hex!(\"c000\"))\n        .inspect_host(|host, _| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [hex!(\"000000000000000000000000000000000000000a\").into()]\n            );\n        })\n        .check()\n}\n\n#[test]\nfn extcodecopy_fill_tail() {\n    EvmTester::new()\n        .apply_host_fn(|host, _| {\n            let mut addr = Address::zero();\n            addr.0[19] = 0xa;\n            host.accounts.entry(addr).or_default().code = (&hex!(\"ff\") as &[u8]).into();\n        })\n        .code(\n            Bytecode::new()\n                .pushv(2)\n                .pushv(0)\n                .pushv(0)\n                .pushv(0xa)\n                .opcode(OpCode::EXTCODECOPY)\n                .ret(0, 2),\n        )\n        .status(StatusCode::Success)\n        .output_data(hex!(\"ff00\"))\n        .inspect_host(|host, _| {\n            assert_eq!(\n                host.recorded.lock().account_accesses,\n                [hex!(\"000000000000000000000000000000000000000a\").into()]\n            );\n        })\n        .check()\n}\n\n#[test]\nfn extcodecopy_buffer_overflow() {\n    let code = Bytecode::new()\n        .opcode(OpCode::NUMBER)\n        .opcode(OpCode::TIMESTAMP)\n        .opcode(OpCode::CALLDATASIZE)\n        .opcode(OpCode::ADDRESS)\n        .opcode(OpCode::EXTCODECOPY)\n        .opcode(OpCode::NUMBER)\n        .opcode(OpCode::CALLDATASIZE)\n        .opcode(OpCode::RETURN)\n        .build();\n\n    let t = EvmTester::new().apply_host_fn({\n        let code = code.clone();\n        move |host, msg| {\n            host.accounts.entry(msg.recipient).or_default().code = code.clone().into();\n        }\n    });\n\n    let s = code.len();\n    let values = [0, 1, s - 1, s, s + 1, 5000];\n    for offset in values {\n        for size in values {\n            t.clone()\n                .apply_host_fn(move |host, _| {\n                    host.tx_context.block_timestamp = offset as u64;\n                    host.tx_context.block_number = size as u64;\n                })\n                .code(code.clone())\n                .status(StatusCode::Success)\n                .inspect_output(move |output| {\n                    assert_eq!(output.len(), size);\n                })\n                .check();\n        }\n    }\n}\n"
  }
]