From e35d7415a91e36c9e3ca3a8b7edf421a02124876 Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 8 Nov 2023 14:50:27 +0100 Subject: [PATCH 01/52] rename main Evm structs, introduce wip external type --- Cargo.lock | 303 +----- Cargo.toml | 9 +- crates/revm/CHANGELOG.md | 7 + crates/revm/benches/bench.rs | 2 +- crates/revm/src/evm.rs | 924 ++++++++++++++++--- crates/revm/src/evm_context.rs | 29 +- crates/revm/src/evm_factory.rs | 185 ++++ crates/revm/src/evm_impl.rs | 842 ----------------- crates/revm/src/frame.rs | 18 + crates/revm/src/handler.rs | 68 +- crates/revm/src/handler/mainnet.rs | 68 +- crates/revm/src/handler/mainnet/call_loop.rs | 91 ++ crates/revm/src/handler/mainnet/main.rs | 67 ++ crates/revm/src/inspector.rs | 30 +- crates/revm/src/inspector/customprinter.rs | 2 +- crates/revm/src/inspector/gas.rs | 278 +++--- crates/revm/src/inspector/instruction.rs | 20 +- crates/revm/src/inspector/noop.rs | 2 +- crates/revm/src/lib.rs | 10 +- documentation/src/crates/revm/evm_impl.md | 2 +- documentation/src/crates/revm/host_trait.md | 2 +- examples/fork_ref_transact.rs | 4 +- 22 files changed, 1435 insertions(+), 1528 deletions(-) create mode 100644 crates/revm/src/evm_factory.rs delete mode 100644 crates/revm/src/evm_impl.rs create mode 100644 crates/revm/src/handler/mainnet/call_loop.rs create mode 100644 crates/revm/src/handler/mainnet/main.rs diff --git a/Cargo.lock b/Cargo.lock index a8fab9cf82..64c97aee32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,15 +101,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anstyle" version = "1.0.1" @@ -289,17 +280,6 @@ dependencies = [ "rustc_version 0.4.0", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "aurora-engine-modexp" version = "1.0.0" @@ -575,21 +555,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - [[package]] name = "clap" version = "4.3.21" @@ -615,19 +580,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" -[[package]] -name = "console" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "unicode-width", - "windows-sys 0.45.0", -] - [[package]] name = "const-hex" version = "1.6.2" @@ -670,7 +622,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.3.21", + "clap", "criterion-plot", "is-terminal", "itertools", @@ -885,12 +837,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - [[package]] name = "encoding_rs" version = "0.8.32" @@ -944,7 +890,7 @@ checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1336,12 +1282,6 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" -[[package]] -name = "hash-db" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" - [[package]] name = "hashbrown" version = "0.12.3" @@ -1368,30 +1308,12 @@ dependencies = [ "fxhash", ] -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.2" @@ -1562,19 +1484,6 @@ dependencies = [ "hashbrown 0.14.2", ] -[[package]] -name = "indicatif" -version = "0.17.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" -dependencies = [ - "console", - "instant", - "number_prefix", - "portable-atomic", - "unicode-width", -] - [[package]] name = "instant" version = "0.1.12" @@ -1596,9 +1505,9 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi", "rustix", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1767,7 +1676,7 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1869,7 +1778,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi", "libc", ] @@ -1894,12 +1803,6 @@ dependencies = [ "syn 2.0.28", ] -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - [[package]] name = "object" version = "0.31.1" @@ -2061,15 +1964,6 @@ dependencies = [ "spki", ] -[[package]] -name = "plain_hasher" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e19e6491bdde87c2c43d70f4c194bc8a758f2eb732df00f61e43f7362e3b4cc" -dependencies = [ - "crunchy", -] - [[package]] name = "plotters" version = "0.3.5" @@ -2098,12 +1992,6 @@ dependencies = [ "plotters-backend", ] -[[package]] -name = "portable-atomic" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f32154ba0af3a075eefa1eda8bb414ee928f62303a54ea85b8d6638ff1a6ee9e" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2436,24 +2324,6 @@ dependencies = [ "revm", ] -[[package]] -name = "revme" -version = "0.2.0" -dependencies = [ - "alloy-rlp", - "hash-db", - "hashbrown 0.14.2", - "indicatif", - "plain_hasher", - "revm", - "serde", - "serde_json", - "structopt", - "thiserror", - "triehash", - "walkdir", -] - [[package]] name = "rfc6979" version = "0.4.0" @@ -2587,7 +2457,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2931,7 +2801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2956,36 +2826,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap 2.34.0", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "strum" version = "0.25.0" @@ -3001,7 +2841,7 @@ version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "rustversion", @@ -3065,16 +2905,7 @@ dependencies = [ "fastrand", "redox_syscall", "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", + "windows-sys", ] [[package]] @@ -3182,7 +3013,7 @@ dependencies = [ "pin-project-lite", "socket2 0.5.3", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -3300,16 +3131,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "triehash" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1631b201eb031b563d2e85ca18ec8092508e262a3196ce9bd10a67ec87b9f5c" -dependencies = [ - "hash-db", - "rlp", -] - [[package]] name = "try-lock" version = "0.2.4" @@ -3387,18 +3208,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - [[package]] name = "unicode-xid" version = "0.2.4" @@ -3434,12 +3243,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" @@ -3613,37 +3416,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.2", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets", ] [[package]] @@ -3652,93 +3431,51 @@ version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1eeca1c172a285ee6c2c84c341ccea837e7c01b12fbb2d0fe3c9e550ce49ec8" dependencies = [ - "windows_aarch64_gnullvm 0.48.2", - "windows_aarch64_msvc 0.48.2", - "windows_i686_gnu 0.48.2", - "windows_i686_msvc 0.48.2", - "windows_x86_64_gnu 0.48.2", - "windows_x86_64_gnullvm 0.48.2", - "windows_x86_64_msvc 0.48.2", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b10d0c968ba7f6166195e13d593af609ec2e3d24f916f081690695cf5eaffb2f" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "571d8d4e62f26d4932099a9efe89660e8bd5087775a2ab5cdd8b747b811f1058" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2229ad223e178db5fbbc8bd8d3835e51e566b8474bfca58d2e6150c48bb723cd" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "600956e2d840c194eedfc5d18f8242bc2e17c7775b6684488af3a9fff6fe3287" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea99ff3f8b49fb7a8e0d305e5aec485bd068c2ba691b6e277d29eaeac945868a" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1a05a1ece9a7a0d5a7ccf30ba2c33e3a61a30e042ffd247567d1de1d94120d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.2" @@ -3761,7 +3498,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index fcb04eb722..20678f00a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,13 @@ [workspace] +members = [ + #"bins/revme", + "bins/revm-test", + "crates/revm", + "crates/primitives", + "crates/interpreter", + "crates/precompile", +] resolver = "2" -members = ["bins/*", "crates/*"] default-members = ["crates/revm"] [profile.release] diff --git a/crates/revm/CHANGELOG.md b/crates/revm/CHANGELOG.md index 60208fea7c..92c8d59e2f 100644 --- a/crates/revm/CHANGELOG.md +++ b/crates/revm/CHANGELOG.md @@ -1,3 +1,10 @@ +# v3.6.0 + +Big renaming long overdue: +* EVMImpl to Evm, +* EVM to EvmFactory +* EVMData to EvmContext + # v3.5.0 date 02.10.2023 diff --git a/crates/revm/benches/bench.rs b/crates/revm/benches/bench.rs index 988c2e9b4f..931636cac2 100644 --- a/crates/revm/benches/bench.rs +++ b/crates/revm/benches/bench.rs @@ -11,7 +11,7 @@ use revm::{ use revm_interpreter::{opcode::make_instruction_table, SharedMemory, EMPTY_SHARED_MEMORY}; use std::time::Duration; -type Evm = revm::EVM; +type Evm = revm::EvmFactory; fn analysis(c: &mut Criterion) { let mut evm = revm::new(); diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index be73045315..514b853e33 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,181 +1,847 @@ use crate::{ - db::{Database, DatabaseCommit, DatabaseRef}, - evm_impl::{new_evm, Transact}, - primitives::{db::WrapDatabaseRef, EVMError, EVMResult, Env, ExecutionResult, ResultAndState}, - Inspector, + db::Database, + handler::Handler, + inspector_instruction, + interpreter::{ + gas::initial_tx_gas, + opcode::{make_boxed_instruction_table, make_instruction_table, InstructionTables}, + CallContext, CallInputs, CallScheme, CreateInputs, Host, Interpreter, InterpreterAction, + InterpreterResult, SelfDestructResult, SharedMemory, Transfer, + }, + journaled_state::JournaledState, + precompile::Precompiles, + primitives::{ + specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, + Output, Spec, SpecId::*, TransactTo, B256, U256, + }, + CallStackFrame, EvmContext, FrameOrResult, Inspector, }; +use alloc::{boxed::Box, sync::Arc, vec::Vec}; +use auto_impl::auto_impl; +use core::{fmt, marker::PhantomData, ops::Range}; -/// Struct that takes Database and enabled transact to update state directly to database. -/// additionally it allows user to set all environment parameters. -/// -/// Parameters that can be set are divided between Config, Block and Transaction(tx) -/// -/// For transacting on EVM you can call transact_commit that will automatically apply changes to db. -/// -/// You can do a lot with rust and traits. For Database abstractions that we need you can implement, -/// Database, DatabaseRef or Database+DatabaseCommit and they enable functionality depending on what kind of -/// handling of struct you want. -/// * Database trait has mutable self in its functions. It is usefully if on get calls you want to modify -/// your cache or update some statistics. They enable `transact` and `inspect` functions -/// * DatabaseRef takes reference on object, this is useful if you only have reference on state and don't -/// want to update anything on it. It enabled `transact_ref` and `inspect_ref` functions -/// * Database+DatabaseCommit allow directly committing changes of transaction. it enabled `transact_commit` -/// and `inspect_commit` -/// -/// /// # Example -/// -/// ``` -/// # use revm::EVM; // Assuming this struct is in 'your_crate_name' -/// # struct SomeDatabase; // Mocking a database type for the purpose of this example -/// # struct Env; // Assuming the type Env is defined somewhere -/// -/// let evm: EVM = EVM::new(); -/// assert!(evm.db.is_none()); -/// ``` -/// -#[derive(Clone, Debug)] -pub struct EVM { - pub env: Env, - pub db: Option, -} +#[cfg(feature = "optimism")] +use crate::optimism; + +/// EVM call stack limit. +pub const CALL_STACK_LIMIT: u64 = 1024; -pub fn new() -> EVM { - EVM::new() +pub struct Evm<'a, SPEC: Spec, EXT, DB: Database> { + pub context: EvmContext<'a, EXT, DB>, + pub inspector: Option<&'a mut dyn Inspector>, + pub instruction_table: InstructionTables<'a, Self>, + pub handler: Handler, + _phantomdata: PhantomData, } -impl Default for EVM { - fn default() -> Self { - Self::new() +impl fmt::Debug for Evm<'_, SPEC, EXT, DB> +where + SPEC: Spec, + EXT: fmt::Debug, + DB: Database + fmt::Debug, + DB::Error: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Evm") + .field("data", &self.context) + .finish_non_exhaustive() } } -impl EVM { - /// Execute transaction and apply result to database - pub fn transact_commit(&mut self) -> Result> { - let ResultAndState { result, state } = self.transact()?; - self.db.as_mut().unwrap().commit(state); - Ok(result) +#[cfg(feature = "optimism")] +impl<'a, SPEC: Spec, DB: Database> Evm<'a, SPEC, DB> { + /// If the transaction is not a deposit transaction, subtract the L1 data fee from the + /// caller's balance directly after minting the requested amount of ETH. + fn remove_l1_cost( + is_deposit: bool, + tx_caller: Address, + l1_cost: U256, + db: &mut DB, + journal: &mut JournaledState, + ) -> Result<(), EVMError> { + if is_deposit { + return Ok(()); + } + let acc = journal + .load_account(tx_caller, db) + .map_err(EVMError::Database)? + .0; + if l1_cost.gt(&acc.info.balance) { + let u64_cost = if U256::from(u64::MAX).lt(&l1_cost) { + u64::MAX + } else { + l1_cost.as_limbs()[0] + }; + return Err(EVMError::Transaction( + InvalidTransaction::LackOfFundForMaxFee { + fee: u64_cost, + balance: acc.info.balance, + }, + )); + } + acc.info.balance = acc.info.balance.saturating_sub(l1_cost); + Ok(()) } - /// Inspect transaction and commit changes to database. - pub fn inspect_commit>( - &mut self, - inspector: INSP, - ) -> Result> { - let ResultAndState { result, state } = self.inspect(inspector)?; - self.db.as_mut().unwrap().commit(state); - Ok(result) + /// If the transaction is a deposit with a `mint` value, add the mint value + /// in wei to the caller's balance. This should be persisted to the database + /// prior to the rest of execution. + fn commit_mint_value( + tx_caller: Address, + tx_mint: Option, + db: &mut DB, + journal: &mut JournaledState, + ) -> Result<(), EVMError> { + if let Some(mint) = tx_mint { + journal + .load_account(tx_caller, db) + .map_err(EVMError::Database)? + .0 + .info + .balance += U256::from(mint); + journal.checkpoint(); + } + Ok(()) } } -impl EVM { - /// Do checks that could make transaction fail before call/create - pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { - if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, None).preverify_transaction() +impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { + pub fn new_with_spec( + db: &'a mut DB, + env: &'a mut Env, + external: EXT, + inspector: Option<&'a mut dyn Inspector>, + precompiles: Precompiles, + ) -> Self { + let journaled_state = JournaledState::new( + SPEC::SPEC_ID, + precompiles + .addresses() + .into_iter() + .cloned() + .collect::>(), + ); + // If T is present it should be a generic T that modifies handler. + let instruction_table = if inspector.is_some() { + let instruction_table = make_boxed_instruction_table::( + make_instruction_table::(), + inspector_instruction, + ); + InstructionTables::Boxed(Arc::new(instruction_table)) } else { - panic!("Database needs to be set"); + InstructionTables::Plain(Arc::new(make_instruction_table::())) + }; + #[cfg(feature = "optimism")] + let mut handler = if env.cfg.optimism { + Handler::optimism::() + } else { + Handler::mainnet::() + }; + #[cfg(not(feature = "optimism"))] + let mut handler = Handler::mainnet::(); + + if env.cfg.is_beneficiary_reward_disabled() { + // do nothing + handler.reward_beneficiary = |_, _| Ok(()); + } + + Self { + context: EvmContext { + env, + journaled_state, + db, + error: None, + precompiles, + external, + #[cfg(feature = "optimism")] + l1_block_info: None, + }, + inspector, + instruction_table, + handler, + _phantomdata: PhantomData {}, } } - /// Skip preverification steps and execute transaction without writing to DB, return change - /// state. - pub fn transact_preverified(&mut self) -> EVMResult { - if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, None).transact_preverified() - } else { - panic!("Database needs to be set"); + #[inline] + pub fn run( + &mut self, + instruction_table: &[FN; 256], + first_frame: Box, + ) -> InterpreterResult + where + FN: Fn(&mut Interpreter, &mut Self), + { + let mut call_stack: Vec> = Vec::with_capacity(1025); + call_stack.push(first_frame); + + #[cfg(feature = "memory_limit")] + let mut shared_memory = + SharedMemory::new_with_memory_limit(self.context.env.cfg.memory_limit); + #[cfg(not(feature = "memory_limit"))] + let mut shared_memory = SharedMemory::new(); + + shared_memory.new_context(); + + let mut stack_frame = call_stack.first_mut().unwrap(); + + loop { + // run interpreter + let action = stack_frame + .interpreter + .run(shared_memory, instruction_table, self); + // take shared memory back. + shared_memory = stack_frame.interpreter.take_memory(); + + let new_frame = match action { + InterpreterAction::SubCall { + inputs, + return_memory_offset, + } => self.handle_sub_call( + inputs, + stack_frame, + return_memory_offset, + &mut shared_memory, + ), + InterpreterAction::Create { inputs } => self.handle_sub_create(inputs, stack_frame), + InterpreterAction::Return { result } => { + // free memory context. + shared_memory.free_context(); + + let child = call_stack.pop().unwrap(); + let parent = call_stack.last_mut(); + + if let Some(result) = + self.handle_frame_return(child, parent, &mut shared_memory, result) + { + return result; + } + stack_frame = call_stack.last_mut().unwrap(); + continue; + } + }; + if let Some(new_frame) = new_frame { + shared_memory.new_context(); + call_stack.push(new_frame); + } + stack_frame = call_stack.last_mut().unwrap(); } } - /// Execute transaction without writing to DB, return change state. - pub fn transact(&mut self) -> EVMResult { - if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, None).transact() + fn handle_frame_return( + &mut self, + mut child_stack_frame: Box, + parent_stack_frame: Option<&mut Box>, + shared_memory: &mut SharedMemory, + mut result: InterpreterResult, + ) -> Option { + if let Some(inspector) = self.inspector.as_mut() { + result = if child_stack_frame.is_create { + let (result, address) = inspector.create_end( + &mut self.context, + result, + child_stack_frame.created_address, + ); + child_stack_frame.created_address = address; + result + } else { + inspector.call_end(&mut self.context, result) + }; + } + + // break from loop if this is last CallStackFrame. + let Some(parent_stack_frame) = parent_stack_frame else { + let result = if child_stack_frame.is_create { + self.context + .create_return::(result, child_stack_frame) + .0 + } else { + self.context.call_return(result, child_stack_frame) + }; + + return Some(result); + }; + + if child_stack_frame.is_create { + let (result, address) = self + .context + .create_return::(result, child_stack_frame); + parent_stack_frame + .interpreter + .insert_create_output(result, Some(address)) } else { - panic!("Database needs to be set"); + let subcall_memory_return_offset = + child_stack_frame.subcall_return_memory_range.clone(); + let result = self.context.call_return(result, child_stack_frame); + + parent_stack_frame.interpreter.insert_call_output( + shared_memory, + result, + subcall_memory_return_offset, + ) } + None } - /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect>(&mut self, mut inspector: INSP) -> EVMResult { - if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, Some(&mut inspector)).transact() - } else { - panic!("Database needs to be set"); + /// Handle Action for new sub create call, return None if there is no need + /// to add new stack frame. + #[inline] + fn handle_sub_create( + &mut self, + mut inputs: Box, + curent_stack_frame: &mut CallStackFrame, + ) -> Option> { + // Call inspector if it is some. + if let Some(inspector) = self.inspector.as_mut() { + if let Some((result, address)) = inspector.create(&mut self.context, &mut inputs) { + curent_stack_frame + .interpreter + .insert_create_output(result, address); + return None; + } + } + + match self.context.make_create_frame::(&inputs) { + FrameOrResult::Frame(new_frame) => Some(new_frame), + FrameOrResult::Result(mut result) => { + let mut address = None; + if let Some(inspector) = self.inspector.as_mut() { + let ret = inspector.create_end( + &mut self.context, + result, + curent_stack_frame.created_address, + ); + result = ret.0; + address = ret.1; + } + // insert result of the failed creation of create CallStackFrame. + curent_stack_frame + .interpreter + .insert_create_output(result, address); + None + } } } -} -impl<'a, DB: DatabaseRef> EVM { - /// Do checks that could make transaction fail before call/create - pub fn preverify_transaction_ref(&self) -> Result<(), EVMError> { - if let Some(db) = self.db.as_ref() { - new_evm::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None) - .preverify_transaction() - } else { - panic!("Database needs to be set"); + /// Handles action for new sub call, return None if there is no need to add + /// new stack frame. + #[inline] + fn handle_sub_call( + &mut self, + mut inputs: Box, + curent_stake_frame: &mut CallStackFrame, + return_memory_offset: Range, + shared_memory: &mut SharedMemory, + ) -> Option> { + // Call inspector if it is some. + if let Some(inspector) = self.inspector.as_mut() { + if let Some((result, range)) = inspector.call(&mut self.context, &mut inputs) { + curent_stake_frame + .interpreter + .insert_call_output(shared_memory, result, range); + return None; + } + } + match self + .context + .make_call_frame(&inputs, return_memory_offset.clone()) + { + FrameOrResult::Frame(new_frame) => Some(new_frame), + FrameOrResult::Result(mut result) => { + //println!("Result returned right away: {:#?}", result); + if let Some(inspector) = self.inspector.as_mut() { + result = inspector.call_end(&mut self.context, result); + } + curent_stake_frame.interpreter.insert_call_output( + shared_memory, + result, + return_memory_offset, + ); + None + } } } - /// Skip preverification steps and execute transaction - /// without writing to DB, return change state. - pub fn transact_preverified_ref(&self) -> EVMResult { - if let Some(db) = self.db.as_ref() { - new_evm::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None) - .transact_preverified() - } else { - panic!("Database needs to be set"); + /// Pre verify transaction. + pub fn preverify_transaction_inner(&mut self) -> Result<(), EVMError> { + let env = self.env(); + + // Important: validate block before tx. + env.validate_block_env::()?; + env.validate_tx::()?; + + let initial_gas_spend = initial_tx_gas::( + &env.tx.data, + env.tx.transact_to.is_create(), + &env.tx.access_list, + ); + + // Additional check to see if limit is big enough to cover initial gas. + if initial_gas_spend > env.tx.gas_limit { + return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into()); } + + // load acc + let tx_caller = env.tx.caller; + let (caller_account, _) = self + .context + .journaled_state + .load_account(tx_caller, self.context.db) + .map_err(EVMError::Database)?; + + self.context + .env + .validate_tx_against_state(caller_account) + .map_err(Into::into) } - /// Execute transaction without writing to DB, return change state. - pub fn transact_ref(&self) -> EVMResult { - if let Some(db) = self.db.as_ref() { - new_evm::<_>(&mut self.env.clone(), &mut WrapDatabaseRef(db), None).transact() + /// Transact preverified transaction. + pub fn transact_preverified_inner(&mut self) -> EVMResult { + let env = &self.context.env; + let tx_caller = env.tx.caller; + let tx_value = env.tx.value; + let tx_data = env.tx.data.clone(); + let tx_gas_limit = env.tx.gas_limit; + + // the L1-cost fee is only computed for Optimism non-deposit transactions. + #[cfg(feature = "optimism")] + let tx_l1_cost = if env.cfg.optimism && env.tx.optimism.source_hash.is_none() { + let l1_block_info = + optimism::L1BlockInfo::try_fetch(self.context.db).map_err(EVMError::Database)?; + + let Some(enveloped_tx) = &env.tx.optimism.enveloped_tx else { + panic!("[OPTIMISM] Failed to load enveloped transaction."); + }; + let tx_l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx); + + // storage l1 block info for later use. + self.context.l1_block_info = Some(l1_block_info); + + tx_l1_cost } else { - panic!("Database needs to be set"); + U256::ZERO + }; + + let initial_gas_spend = initial_tx_gas::( + &tx_data, + env.tx.transact_to.is_create(), + &env.tx.access_list, + ); + + // load coinbase + // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm + if SPEC::enabled(SHANGHAI) { + self.context + .journaled_state + .initial_account_load(self.context.env.block.coinbase, &[], self.context.db) + .map_err(EVMError::Database)?; + } + + self.context.load_access_list()?; + + // load acc + let journal = &mut self.context.journaled_state; + + #[cfg(feature = "optimism")] + if self.context.env.cfg.optimism { + Evm::::commit_mint_value( + tx_caller, + self.context.env.tx.optimism.mint, + self.context.db, + journal, + )?; + + let is_deposit = self.context.env.tx.optimism.source_hash.is_some(); + Evm::::remove_l1_cost( + is_deposit, + tx_caller, + tx_l1_cost, + self.context.db, + journal, + )?; } + + let (caller_account, _) = journal + .load_account(tx_caller, self.context.db) + .map_err(EVMError::Database)?; + + // Subtract gas costs from the caller's account. + // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. + let mut gas_cost = + U256::from(tx_gas_limit).saturating_mul(self.context.env.effective_gas_price()); + + // EIP-4844 + if SPEC::enabled(CANCUN) { + let data_fee = self.context.env.calc_data_fee().expect("already checked"); + gas_cost = gas_cost.saturating_add(data_fee); + } + + caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); + + // touch account so we know it is changed. + caller_account.mark_touch(); + + let transact_gas_limit = tx_gas_limit - initial_gas_spend; + + // call inner handling of call/create + let first_stack_frame = match self.context.env.tx.transact_to { + TransactTo::Call(address) => { + // Nonce is already checked + caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); + + self.context.make_call_frame( + &CallInputs { + contract: address, + transfer: Transfer { + source: tx_caller, + target: address, + value: tx_value, + }, + input: tx_data, + gas_limit: transact_gas_limit, + context: CallContext { + caller: tx_caller, + address, + code_address: address, + apparent_value: tx_value, + scheme: CallScheme::Call, + }, + is_static: false, + }, + 0..0, + ) + } + TransactTo::Create(scheme) => self.context.make_create_frame::(&CreateInputs { + caller: tx_caller, + scheme, + value: tx_value, + init_code: tx_data, + gas_limit: transact_gas_limit, + }), + }; + // Some only if it is create. + let mut created_address = None; + + // start main loop if CallStackFrame is created correctly + let interpreter_result = match first_stack_frame { + FrameOrResult::Frame(first_stack_frame) => { + created_address = first_stack_frame.created_address; + let table = self.instruction_table.clone(); + match table { + InstructionTables::Plain(table) => self.run(&table, first_stack_frame), + InstructionTables::Boxed(table) => self.run(&table, first_stack_frame), + } + } + FrameOrResult::Result(interpreter_result) => interpreter_result, + }; + + let handler = &self.handler; + let data = &mut self.context; + + // handle output of call/create calls. + let mut gas = + handler.call_return(data.env, interpreter_result.result, interpreter_result.gas); + + // set refund. Refund amount depends on hardfork. + gas.set_refund(handler.calculate_gas_refund(data.env, &gas) as i64); + + // Reimburse the caller + handler.reimburse_caller(data, &gas)?; + + // Reward beneficiary + handler.reward_beneficiary(data, &gas)?; + + // output of execution + let output = match data.env.tx.transact_to { + TransactTo::Call(_) => Output::Call(interpreter_result.output), + TransactTo::Create(_) => Output::Create(interpreter_result.output, created_address), + }; + + // main return + handler.main_return(data, interpreter_result.result, output, &gas) } +} - /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect_ref>>( - &'a self, - mut inspector: I, - ) -> EVMResult { - if let Some(db) = self.db.as_ref() { - new_evm( - &mut self.env.clone(), - &mut WrapDatabaseRef(db), - Some(&mut inspector), - ) - .transact() - } else { - panic!("Database needs to be set"); +/// EVM transaction interface. +#[auto_impl(&mut, Box)] +pub trait Transact { + /// Run checks that could make transaction fail before call/create. + fn preverify_transaction(&mut self) -> Result<(), EVMError>; + + /// Skip pre-verification steps and execute the transaction. + fn transact_preverified(&mut self) -> EVMResult; + + /// Execute transaction by running pre-verification steps and then transaction itself. + fn transact(&mut self) -> EVMResult; +} + +impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Transact for Evm<'a, SPEC, EXT, DB> { + #[inline] + fn preverify_transaction(&mut self) -> Result<(), EVMError> { + self.preverify_transaction_inner() + } + + #[inline] + fn transact_preverified(&mut self) -> EVMResult { + let output = self.transact_preverified_inner(); + self.handler.end(&mut self.context, output) + } + + #[inline] + fn transact(&mut self) -> EVMResult { + let output = self + .preverify_transaction_inner() + .and_then(|()| self.transact_preverified_inner()); + self.handler.end(&mut self.context, output) + } +} + +impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Host for Evm<'a, SPEC, EXT, DB> { + fn env(&mut self) -> &mut Env { + self.context.env() + } + + fn block_hash(&mut self, number: U256) -> Option { + self.context.block_hash(number) + } + + fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { + self.context.load_account(address) + } + + fn balance(&mut self, address: Address) -> Option<(U256, bool)> { + self.context.balance(address) + } + + fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { + self.context.code(address) + } + + /// Get code hash of address. + fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { + self.context.code_hash(address) + } + + fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { + self.context.sload(address, index) + } + + fn sstore( + &mut self, + address: Address, + index: U256, + value: U256, + ) -> Option<(U256, U256, U256, bool)> { + self.context.sstore(address, index, value) + } + + fn tload(&mut self, address: Address, index: U256) -> U256 { + self.context.tload(address, index) + } + + fn tstore(&mut self, address: Address, index: U256, value: U256) { + self.context.tstore(address, index, value) + } + + fn log(&mut self, address: Address, topics: Vec, data: Bytes) { + if let Some(inspector) = self.inspector.as_mut() { + inspector.log(&mut self.context, &address, &topics, &data); } + let log = Log { + address, + topics, + data, + }; + self.context.journaled_state.log(log); + } + + fn selfdestruct(&mut self, address: Address, target: Address) -> Option { + if let Some(inspector) = self.inspector.as_mut() { + let acc = self.context.journaled_state.state.get(&address).unwrap(); + inspector.selfdestruct(address, target, acc.info.balance); + } + self.context + .journaled_state + .selfdestruct(address, target, self.context.db) + .map_err(|e| self.context.error = Some(e)) + .ok() } } -impl EVM { - /// Creates a new [EVM] instance with the default environment, - pub fn new() -> Self { - Self::with_env(Default::default()) +/// Creates new EVM instance with erased types. +pub fn new_evm<'a, EXT, DB: Database>( + env: &'a mut Env, + db: &'a mut DB, + external: EXT, + insp: Option<&'a mut dyn Inspector>, +) -> Box + 'a> { + macro_rules! create_evm { + ($spec:ident) => { + Box::new(Evm::<'a, $spec, EXT, DB>::new_with_spec( + db, + env, + external, + insp, + Precompiles::new(revm_precompile::SpecId::from_spec_id($spec::SPEC_ID)).clone(), + )) + }; + } + + use specification::*; + match env.cfg.spec_id { + SpecId::FRONTIER | SpecId::FRONTIER_THAWING => create_evm!(FrontierSpec), + SpecId::HOMESTEAD | SpecId::DAO_FORK => create_evm!(HomesteadSpec), + SpecId::TANGERINE => create_evm!(TangerineSpec), + SpecId::SPURIOUS_DRAGON => create_evm!(SpuriousDragonSpec), + SpecId::BYZANTIUM => create_evm!(ByzantiumSpec), + SpecId::PETERSBURG | SpecId::CONSTANTINOPLE => create_evm!(PetersburgSpec), + SpecId::ISTANBUL | SpecId::MUIR_GLACIER => create_evm!(IstanbulSpec), + SpecId::BERLIN => create_evm!(BerlinSpec), + SpecId::LONDON | SpecId::ARROW_GLACIER | SpecId::GRAY_GLACIER => { + create_evm!(LondonSpec) + } + SpecId::MERGE => create_evm!(MergeSpec), + SpecId::SHANGHAI => create_evm!(ShanghaiSpec), + SpecId::CANCUN => create_evm!(CancunSpec), + SpecId::LATEST => create_evm!(LatestSpec), + #[cfg(feature = "optimism")] + SpecId::BEDROCK => create_evm!(BedrockSpec), + #[cfg(feature = "optimism")] + SpecId::REGOLITH => create_evm!(RegolithSpec), } +} + +#[cfg(feature = "optimism")] +#[cfg(test)] +mod tests { + use super::*; + + use crate::db::InMemoryDB; + use crate::primitives::{specification::BedrockSpec, state::AccountInfo, SpecId}; - /// Creates a new [EVM] instance with the given environment. - pub fn with_env(env: Env) -> Self { - Self { env, db: None } + #[test] + fn test_commit_mint_value() { + let caller = Address::ZERO; + let mint_value = Some(1u128); + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + nonce: 0, + balance: U256::from(100), + code_hash: B256::ZERO, + code: None, + }, + ); + let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); + journal + .initial_account_load(caller, &[U256::from(100)], &mut db) + .unwrap(); + assert!(Evm::::commit_mint_value( + caller, + mint_value, + &mut db, + &mut journal + ) + .is_ok(),); + + // Check the account balance is updated. + let (account, _) = journal.load_account(caller, &mut db).unwrap(); + assert_eq!(account.info.balance, U256::from(101)); + + // No mint value should be a no-op. + assert!(Evm::::commit_mint_value( + caller, + None, + &mut db, + &mut journal + ) + .is_ok(),); + let (account, _) = journal.load_account(caller, &mut db).unwrap(); + assert_eq!(account.info.balance, U256::from(101)); } - pub fn database(&mut self, db: DB) { - self.db = Some(db); + #[test] + fn test_remove_l1_cost_non_deposit() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); + let slots = &[U256::from(100)]; + journal + .initial_account_load(caller, slots, &mut db) + .unwrap(); + assert!(Evm::::remove_l1_cost( + true, + caller, + U256::ZERO, + &mut db, + &mut journal + ) + .is_ok(),); } - pub fn db(&mut self) -> Option<&mut DB> { - self.db.as_mut() + #[test] + fn test_remove_l1_cost() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + nonce: 0, + balance: U256::from(100), + code_hash: B256::ZERO, + code: None, + }, + ); + let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); + journal + .initial_account_load(caller, &[U256::from(100)], &mut db) + .unwrap(); + assert!(Evm::::remove_l1_cost( + false, + caller, + U256::from(1), + &mut db, + &mut journal + ) + .is_ok(),); + + // Check the account balance is updated. + let (account, _) = journal.load_account(caller, &mut db).unwrap(); + assert_eq!(account.info.balance, U256::from(99)); } - pub fn take_db(&mut self) -> DB { - core::mem::take(&mut self.db).unwrap() + #[test] + fn test_remove_l1_cost_lack_of_funds() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + nonce: 0, + balance: U256::from(100), + code_hash: B256::ZERO, + code: None, + }, + ); + let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); + journal + .initial_account_load(caller, &[U256::from(100)], &mut db) + .unwrap(); + assert_eq!( + Evm::::remove_l1_cost( + false, + caller, + U256::from(101), + &mut db, + &mut journal + ), + Err(EVMError::Transaction( + InvalidTransaction::LackOfFundForMaxFee { + fee: 101u64, + balance: U256::from(100), + }, + )) + ); } } diff --git a/crates/revm/src/evm_context.rs b/crates/revm/src/evm_context.rs index d0b47be655..e1b0bacf42 100644 --- a/crates/revm/src/evm_context.rs +++ b/crates/revm/src/evm_context.rs @@ -10,14 +10,14 @@ use crate::{ keccak256, Address, AnalysisKind, Bytecode, Bytes, EVMError, Env, Spec, SpecId::*, B256, U256, }, - CallStackFrame, CALL_STACK_LIMIT, + CallStackFrame, FrameOrResult, CALL_STACK_LIMIT, }; use alloc::boxed::Box; use core::ops::Range; /// EVM Data contains all the data that EVM needs to execute. #[derive(Debug)] -pub struct EvmContext<'a, DB: Database> { +pub struct EvmContext<'a, EXT, DB: Database> { /// EVM Environment contains all the information about config, block and transaction that /// evm needs. pub env: &'a mut Env, @@ -29,12 +29,14 @@ pub struct EvmContext<'a, DB: Database> { pub error: Option, /// Precompiles that are available for evm. pub precompiles: Precompiles, + /// External generic code. + pub external: EXT, /// Used as temporary value holder to store L1 block info. #[cfg(feature = "optimism")] pub l1_block_info: Option, } -impl<'a, DB: Database> EvmContext<'a, DB> { +impl<'a, EXT, DB: Database> EvmContext<'a, EXT, DB> { /// Load access list for berlin hard fork. /// /// Loading of accounts/storages is needed to make them warm. @@ -135,15 +137,12 @@ impl<'a, DB: Database> EvmContext<'a, DB> { } /// Make create frame. - pub fn make_create_frame( - &mut self, - inputs: &CreateInputs, - ) -> Result, InterpreterResult> { + pub fn make_create_frame(&mut self, inputs: &CreateInputs) -> FrameOrResult { // Prepare crate. let gas = Gas::new(inputs.gas_limit); let return_error = |e| { - Err(InterpreterResult { + FrameOrResult::Result(InterpreterResult { result: e, gas, output: Bytes::new(), @@ -210,13 +209,13 @@ impl<'a, DB: Database> EvmContext<'a, DB> { inputs.value, )); - Ok(Box::new(CallStackFrame { + FrameOrResult::new_frame(CallStackFrame { is_create: true, checkpoint, created_address: Some(created_address), subcall_return_memory_range: 0..0, interpreter: Interpreter::new(contract, gas.limit(), false), - })) + }) } /// Make call frame @@ -224,11 +223,11 @@ impl<'a, DB: Database> EvmContext<'a, DB> { &mut self, inputs: &CallInputs, return_memory_offset: Range, - ) -> Result, InterpreterResult> { + ) -> FrameOrResult { let gas = Gas::new(inputs.gas_limit); let return_result = |instruction_result: InstructionResult| { - Err(InterpreterResult { + FrameOrResult::Result(InterpreterResult { result: instruction_result, gas, output: Bytes::new(), @@ -279,7 +278,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { } else { self.journaled_state.checkpoint_revert(checkpoint); } - Err(result) + FrameOrResult::Result(result) } else if !bytecode.is_empty() { let contract = Box::new(Contract::new_with_context( inputs.input.clone(), @@ -288,13 +287,13 @@ impl<'a, DB: Database> EvmContext<'a, DB> { &inputs.context, )); // Create interpreter and execute subcall and push new CallStackFrame. - Ok(Box::new(CallStackFrame { + FrameOrResult::new_frame(CallStackFrame { is_create: false, checkpoint, created_address: None, subcall_return_memory_range: return_memory_offset, interpreter: Interpreter::new(contract, gas.limit(), inputs.is_static), - })) + }) } else { self.journaled_state.checkpoint_commit(); return_result(InstructionResult::Stop) diff --git a/crates/revm/src/evm_factory.rs b/crates/revm/src/evm_factory.rs new file mode 100644 index 0000000000..37685f7ba1 --- /dev/null +++ b/crates/revm/src/evm_factory.rs @@ -0,0 +1,185 @@ +use crate::{ + db::{Database, DatabaseCommit, DatabaseRef}, + evm::{new_evm, Transact}, + primitives::{db::WrapDatabaseRef, EVMError, EVMResult, Env, ExecutionResult, ResultAndState}, + Inspector, +}; + +/// Struct that takes Database and enabled transact to update state directly to database. +/// additionally it allows user to set all environment parameters. +/// +/// Parameters that can be set are divided between Config, Block and Transaction(tx) +/// +/// For transacting on EVM you can call transact_commit that will automatically apply changes to db. +/// +/// You can do a lot with rust and traits. For Database abstractions that we need you can implement, +/// Database, DatabaseRef or Database+DatabaseCommit and they enable functionality depending on what kind of +/// handling of struct you want. +/// * Database trait has mutable self in its functions. It is usefully if on get calls you want to modify +/// your cache or update some statistics. They enable `transact` and `inspect` functions +/// * DatabaseRef takes reference on object, this is useful if you only have reference on state and don't +/// want to update anything on it. It enabled `transact_ref` and `inspect_ref` functions +/// * Database+DatabaseCommit allow directly committing changes of transaction. it enabled `transact_commit` +/// and `inspect_commit` +/// +/// /// # Example +/// +/// ``` +/// # use revm::EVM; // Assuming this struct is in 'your_crate_name' +/// # struct SomeDatabase; // Mocking a database type for the purpose of this example +/// # struct Env; // Assuming the type Env is defined somewhere +/// +/// let evm: EVM = EVM::new(); +/// assert!(evm.db.is_none()); +/// ``` +/// +#[derive(Clone, Debug)] +pub struct EvmFactory { + pub env: Env, + pub db: Option, +} + +pub fn new() -> EvmFactory { + EvmFactory::new() +} + +impl Default for EvmFactory { + fn default() -> Self { + Self::new() + } +} + +impl EvmFactory { + /// Execute transaction and apply result to database + pub fn transact_commit(&mut self) -> Result> { + let ResultAndState { result, state } = self.transact()?; + self.db.as_mut().unwrap().commit(state); + Ok(result) + } + + /// Inspect transaction and commit changes to database. + pub fn inspect_commit>( + &mut self, + inspector: INSP, + ) -> Result> { + let ResultAndState { result, state } = self.inspect(inspector)?; + self.db.as_mut().unwrap().commit(state); + Ok(result) + } +} + +impl EvmFactory { + /// Do checks that could make transaction fail before call/create + pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { + if let Some(db) = self.db.as_mut() { + new_evm::<_, DB>(&mut self.env, db, (), None).preverify_transaction() + } else { + panic!("Database needs to be set"); + } + } + + /// Skip preverification steps and execute transaction without writing to DB, return change + /// state. + pub fn transact_preverified(&mut self) -> EVMResult { + if let Some(db) = self.db.as_mut() { + new_evm::<(), DB>(&mut self.env, db, (), None).transact_preverified() + } else { + panic!("Database needs to be set"); + } + } + + /// Execute transaction without writing to DB, return change state. + pub fn transact(&mut self) -> EVMResult { + if let Some(db) = self.db.as_mut() { + new_evm::<_, DB>(&mut self.env, db, (), None).transact() + } else { + panic!("Database needs to be set"); + } + } + + /// Execute transaction with given inspector, without wring to DB. Return change state. + pub fn inspect>( + &mut self, + mut inspector: INSP, + ) -> EVMResult { + if let Some(db) = self.db.as_mut() { + new_evm::<_, DB>(&mut self.env, db, (), Some(&mut inspector)).transact() + } else { + panic!("Database needs to be set"); + } + } +} + +impl<'a, DB: DatabaseRef> EvmFactory { + /// Do checks that could make transaction fail before call/create + pub fn preverify_transaction_ref(&self) -> Result<(), EVMError> { + if let Some(db) = self.db.as_ref() { + new_evm::<_, _>(&mut self.env.clone(), &mut WrapDatabaseRef(db), (), None) + .preverify_transaction() + } else { + panic!("Database needs to be set"); + } + } + + /// Skip preverification steps and execute transaction + /// without writing to DB, return change state. + pub fn transact_preverified_ref(&self) -> EVMResult { + if let Some(db) = self.db.as_ref() { + new_evm::<_, _>(&mut self.env.clone(), &mut WrapDatabaseRef(db), (), None) + .transact_preverified() + } else { + panic!("Database needs to be set"); + } + } + + /// Execute transaction without writing to DB, return change state. + pub fn transact_ref(&self) -> EVMResult { + if let Some(db) = self.db.as_ref() { + new_evm::<_, _>(&mut self.env.clone(), &mut WrapDatabaseRef(db), (), None).transact() + } else { + panic!("Database needs to be set"); + } + } + + /// Execute transaction with given inspector, without wring to DB. Return change state. + pub fn inspect_ref>>( + &'a self, + mut inspector: I, + ) -> EVMResult { + if let Some(db) = self.db.as_ref() { + new_evm( + &mut self.env.clone(), + &mut WrapDatabaseRef(db), + (), + Some(&mut inspector), + ) + .transact() + } else { + panic!("Database needs to be set"); + } + } +} + +impl EvmFactory { + /// Creates a new [EVM] instance with the default environment, + pub fn new() -> Self { + Self::with_env(Default::default()) + } + + /// Creates a new [EVM] instance with the given environment. + pub fn with_env(env: Env) -> Self { + Self { env, db: None } + } + + pub fn database(&mut self, db: DB) { + self.db = Some(db); + } + + pub fn db(&mut self) -> Option<&mut DB> { + self.db.as_mut() + } + + pub fn take_db(&mut self) -> DB { + core::mem::take(&mut self.db).unwrap() + } +} diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs deleted file mode 100644 index 4e95b4f5ea..0000000000 --- a/crates/revm/src/evm_impl.rs +++ /dev/null @@ -1,842 +0,0 @@ -use crate::{ - db::Database, - handler::Handler, - inspector_instruction, - interpreter::{ - gas::initial_tx_gas, - opcode::{make_boxed_instruction_table, make_instruction_table, InstructionTables}, - CallContext, CallInputs, CallScheme, CreateInputs, Host, Interpreter, InterpreterAction, - InterpreterResult, SelfDestructResult, SharedMemory, Transfer, - }, - journaled_state::JournaledState, - precompile::Precompiles, - primitives::{ - specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, - Output, Spec, SpecId::*, TransactTo, B256, U256, - }, - CallStackFrame, EvmContext, Inspector, -}; -use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use auto_impl::auto_impl; -use core::{fmt, marker::PhantomData, ops::Range}; - -#[cfg(feature = "optimism")] -use crate::optimism; - -/// EVM call stack limit. -pub const CALL_STACK_LIMIT: u64 = 1024; - -pub struct EVMImpl<'a, SPEC: Spec, DB: Database> { - pub context: EvmContext<'a, DB>, - pub inspector: Option<&'a mut dyn Inspector>, - pub instruction_table: InstructionTables<'a, Self>, - pub handler: Handler, - _phantomdata: PhantomData, -} - -impl fmt::Debug for EVMImpl<'_, SPEC, DB> -where - SPEC: Spec, - DB: Database + fmt::Debug, - DB::Error: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("EVMImpl") - .field("data", &self.context) - .finish_non_exhaustive() - } -} - -#[cfg(feature = "optimism")] -impl<'a, SPEC: Spec, DB: Database> EVMImpl<'a, SPEC, DB> { - /// If the transaction is not a deposit transaction, subtract the L1 data fee from the - /// caller's balance directly after minting the requested amount of ETH. - fn remove_l1_cost( - is_deposit: bool, - tx_caller: Address, - l1_cost: U256, - db: &mut DB, - journal: &mut JournaledState, - ) -> Result<(), EVMError> { - if is_deposit { - return Ok(()); - } - let acc = journal - .load_account(tx_caller, db) - .map_err(EVMError::Database)? - .0; - if l1_cost.gt(&acc.info.balance) { - let u64_cost = if U256::from(u64::MAX).lt(&l1_cost) { - u64::MAX - } else { - l1_cost.as_limbs()[0] - }; - return Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: u64_cost, - balance: acc.info.balance, - }, - )); - } - acc.info.balance = acc.info.balance.saturating_sub(l1_cost); - Ok(()) - } - - /// If the transaction is a deposit with a `mint` value, add the mint value - /// in wei to the caller's balance. This should be persisted to the database - /// prior to the rest of execution. - fn commit_mint_value( - tx_caller: Address, - tx_mint: Option, - db: &mut DB, - journal: &mut JournaledState, - ) -> Result<(), EVMError> { - if let Some(mint) = tx_mint { - journal - .load_account(tx_caller, db) - .map_err(EVMError::Database)? - .0 - .info - .balance += U256::from(mint); - journal.checkpoint(); - } - Ok(()) - } -} - -impl<'a, SPEC: Spec + 'static, DB: Database> EVMImpl<'a, SPEC, DB> { - pub fn new_with_spec( - db: &'a mut DB, - env: &'a mut Env, - inspector: Option<&'a mut dyn Inspector>, - precompiles: Precompiles, - ) -> Self { - let journaled_state = JournaledState::new( - SPEC::SPEC_ID, - precompiles - .addresses() - .into_iter() - .cloned() - .collect::>(), - ); - // If T is present it should be a generic T that modifies handler. - let instruction_table = if inspector.is_some() { - let instruction_table = make_boxed_instruction_table::( - make_instruction_table::(), - inspector_instruction, - ); - InstructionTables::Boxed(Arc::new(instruction_table)) - } else { - InstructionTables::Plain(Arc::new(make_instruction_table::())) - }; - #[cfg(feature = "optimism")] - let mut handler = if env.cfg.optimism { - Handler::optimism::() - } else { - Handler::mainnet::() - }; - #[cfg(not(feature = "optimism"))] - let mut handler = Handler::mainnet::(); - - if env.cfg.is_beneficiary_reward_disabled() { - // do nothing - handler.reward_beneficiary = |_, _| Ok(()); - } - - Self { - context: EvmContext { - env, - journaled_state, - db, - error: None, - precompiles, - #[cfg(feature = "optimism")] - l1_block_info: None, - }, - inspector, - instruction_table, - handler, - _phantomdata: PhantomData {}, - } - } - - #[inline] - pub fn run( - &mut self, - instruction_table: &[FN; 256], - first_frame: Box, - ) -> InterpreterResult - where - FN: Fn(&mut Interpreter, &mut Self), - { - let mut call_stack: Vec> = Vec::with_capacity(1025); - call_stack.push(first_frame); - - #[cfg(feature = "memory_limit")] - let mut shared_memory = - SharedMemory::new_with_memory_limit(self.context.env.cfg.memory_limit); - #[cfg(not(feature = "memory_limit"))] - let mut shared_memory = SharedMemory::new(); - - shared_memory.new_context(); - - let mut stack_frame = call_stack.first_mut().unwrap(); - - loop { - // run interpreter - let action = stack_frame - .interpreter - .run(shared_memory, instruction_table, self); - // take shared memory back. - shared_memory = stack_frame.interpreter.take_memory(); - - let new_frame = match action { - InterpreterAction::SubCall { - inputs, - return_memory_offset, - } => self.handle_sub_call( - inputs, - stack_frame, - return_memory_offset, - &mut shared_memory, - ), - InterpreterAction::Create { inputs } => self.handle_sub_create(inputs, stack_frame), - InterpreterAction::Return { result } => { - // free memory context. - shared_memory.free_context(); - - let child = call_stack.pop().unwrap(); - let parent = call_stack.last_mut(); - - if let Some(result) = - self.handle_frame_return(child, parent, &mut shared_memory, result) - { - return result; - } - stack_frame = call_stack.last_mut().unwrap(); - continue; - } - }; - if let Some(new_frame) = new_frame { - shared_memory.new_context(); - call_stack.push(new_frame); - } - stack_frame = call_stack.last_mut().unwrap(); - } - } - - fn handle_frame_return( - &mut self, - mut child_stack_frame: Box, - parent_stack_frame: Option<&mut Box>, - shared_memory: &mut SharedMemory, - mut result: InterpreterResult, - ) -> Option { - if let Some(inspector) = self.inspector.as_mut() { - result = if child_stack_frame.is_create { - let (result, address) = inspector.create_end( - &mut self.context, - result, - child_stack_frame.created_address, - ); - child_stack_frame.created_address = address; - result - } else { - inspector.call_end(&mut self.context, result) - }; - } - - // break from loop if this is last CallStackFrame. - let Some(parent_stack_frame) = parent_stack_frame else { - let result = if child_stack_frame.is_create { - self.context - .create_return::(result, child_stack_frame) - .0 - } else { - self.context.call_return(result, child_stack_frame) - }; - - return Some(result); - }; - - if child_stack_frame.is_create { - let (result, address) = self - .context - .create_return::(result, child_stack_frame); - parent_stack_frame - .interpreter - .insert_create_output(result, Some(address)) - } else { - let subcall_memory_return_offset = - child_stack_frame.subcall_return_memory_range.clone(); - let result = self.context.call_return(result, child_stack_frame); - - parent_stack_frame.interpreter.insert_call_output( - shared_memory, - result, - subcall_memory_return_offset, - ) - } - None - } - - /// Handle Action for new sub create call, return None if there is no need - /// to add new stack frame. - #[inline] - fn handle_sub_create( - &mut self, - mut inputs: Box, - curent_stack_frame: &mut CallStackFrame, - ) -> Option> { - // Call inspector if it is some. - if let Some(inspector) = self.inspector.as_mut() { - if let Some((result, address)) = inspector.create(&mut self.context, &mut inputs) { - curent_stack_frame - .interpreter - .insert_create_output(result, address); - return None; - } - } - - match self.context.make_create_frame::(&inputs) { - Ok(new_frame) => Some(new_frame), - Err(mut result) => { - let mut address = None; - if let Some(inspector) = self.inspector.as_mut() { - let ret = inspector.create_end( - &mut self.context, - result, - curent_stack_frame.created_address, - ); - result = ret.0; - address = ret.1; - } - // insert result of the failed creation of create CallStackFrame. - curent_stack_frame - .interpreter - .insert_create_output(result, address); - None - } - } - } - - /// Handles action for new sub call, return None if there is no need to add - /// new stack frame. - #[inline] - fn handle_sub_call( - &mut self, - mut inputs: Box, - curent_stake_frame: &mut CallStackFrame, - return_memory_offset: Range, - shared_memory: &mut SharedMemory, - ) -> Option> { - // Call inspector if it is some. - if let Some(inspector) = self.inspector.as_mut() { - if let Some((result, range)) = inspector.call(&mut self.context, &mut inputs) { - curent_stake_frame - .interpreter - .insert_call_output(shared_memory, result, range); - return None; - } - } - match self - .context - .make_call_frame(&inputs, return_memory_offset.clone()) - { - Ok(new_frame) => Some(new_frame), - Err(mut result) => { - //println!("Result returned right away: {:#?}", result); - if let Some(inspector) = self.inspector.as_mut() { - result = inspector.call_end(&mut self.context, result); - } - curent_stake_frame.interpreter.insert_call_output( - shared_memory, - result, - return_memory_offset, - ); - None - } - } - } - - /// Pre verify transaction. - pub fn preverify_transaction_inner(&mut self) -> Result<(), EVMError> { - let env = self.env(); - - // Important: validate block before tx. - env.validate_block_env::()?; - env.validate_tx::()?; - - let initial_gas_spend = initial_tx_gas::( - &env.tx.data, - env.tx.transact_to.is_create(), - &env.tx.access_list, - ); - - // Additional check to see if limit is big enough to cover initial gas. - if initial_gas_spend > env.tx.gas_limit { - return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into()); - } - - // load acc - let tx_caller = env.tx.caller; - let (caller_account, _) = self - .context - .journaled_state - .load_account(tx_caller, self.context.db) - .map_err(EVMError::Database)?; - - self.context - .env - .validate_tx_against_state(caller_account) - .map_err(Into::into) - } - - /// Transact preverified transaction. - pub fn transact_preverified_inner(&mut self) -> EVMResult { - let env = &self.context.env; - let tx_caller = env.tx.caller; - let tx_value = env.tx.value; - let tx_data = env.tx.data.clone(); - let tx_gas_limit = env.tx.gas_limit; - - // the L1-cost fee is only computed for Optimism non-deposit transactions. - #[cfg(feature = "optimism")] - let tx_l1_cost = if env.cfg.optimism && env.tx.optimism.source_hash.is_none() { - let l1_block_info = - optimism::L1BlockInfo::try_fetch(self.context.db).map_err(EVMError::Database)?; - - let Some(enveloped_tx) = &env.tx.optimism.enveloped_tx else { - panic!("[OPTIMISM] Failed to load enveloped transaction."); - }; - let tx_l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx); - - // storage l1 block info for later use. - self.context.l1_block_info = Some(l1_block_info); - - tx_l1_cost - } else { - U256::ZERO - }; - - let initial_gas_spend = initial_tx_gas::( - &tx_data, - env.tx.transact_to.is_create(), - &env.tx.access_list, - ); - - // load coinbase - // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm - if SPEC::enabled(SHANGHAI) { - self.context - .journaled_state - .initial_account_load(self.context.env.block.coinbase, &[], self.context.db) - .map_err(EVMError::Database)?; - } - - self.context.load_access_list()?; - - // load acc - let journal = &mut self.context.journaled_state; - - #[cfg(feature = "optimism")] - if self.context.env.cfg.optimism { - EVMImpl::::commit_mint_value( - tx_caller, - self.context.env.tx.optimism.mint, - self.context.db, - journal, - )?; - - let is_deposit = self.context.env.tx.optimism.source_hash.is_some(); - EVMImpl::::remove_l1_cost( - is_deposit, - tx_caller, - tx_l1_cost, - self.context.db, - journal, - )?; - } - - let (caller_account, _) = journal - .load_account(tx_caller, self.context.db) - .map_err(EVMError::Database)?; - - // Subtract gas costs from the caller's account. - // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. - let mut gas_cost = - U256::from(tx_gas_limit).saturating_mul(self.context.env.effective_gas_price()); - - // EIP-4844 - if SPEC::enabled(CANCUN) { - let data_fee = self.context.env.calc_data_fee().expect("already checked"); - gas_cost = gas_cost.saturating_add(data_fee); - } - - caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); - - // touch account so we know it is changed. - caller_account.mark_touch(); - - let transact_gas_limit = tx_gas_limit - initial_gas_spend; - - // call inner handling of call/create - let first_stack_frame = match self.context.env.tx.transact_to { - TransactTo::Call(address) => { - // Nonce is already checked - caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); - - self.context.make_call_frame( - &CallInputs { - contract: address, - transfer: Transfer { - source: tx_caller, - target: address, - value: tx_value, - }, - input: tx_data, - gas_limit: transact_gas_limit, - context: CallContext { - caller: tx_caller, - address, - code_address: address, - apparent_value: tx_value, - scheme: CallScheme::Call, - }, - is_static: false, - }, - 0..0, - ) - } - TransactTo::Create(scheme) => self.context.make_create_frame::(&CreateInputs { - caller: tx_caller, - scheme, - value: tx_value, - init_code: tx_data, - gas_limit: transact_gas_limit, - }), - }; - // Some only if it is create. - let mut created_address = None; - - // start main loop if CallStackFrame is created correctly - let interpreter_result = match first_stack_frame { - Ok(first_stack_frame) => { - created_address = first_stack_frame.created_address; - let table = self.instruction_table.clone(); - match table { - InstructionTables::Plain(table) => self.run(&table, first_stack_frame), - InstructionTables::Boxed(table) => self.run(&table, first_stack_frame), - } - } - Err(interpreter_result) => interpreter_result, - }; - - let handler = &self.handler; - let data = &mut self.context; - - // handle output of call/create calls. - let mut gas = - handler.call_return(data.env, interpreter_result.result, interpreter_result.gas); - - // set refund. Refund amount depends on hardfork. - gas.set_refund(handler.calculate_gas_refund(data.env, &gas) as i64); - - // Reimburse the caller - handler.reimburse_caller(data, &gas)?; - - // Reward beneficiary - handler.reward_beneficiary(data, &gas)?; - - // output of execution - let output = match data.env.tx.transact_to { - TransactTo::Call(_) => Output::Call(interpreter_result.output), - TransactTo::Create(_) => Output::Create(interpreter_result.output, created_address), - }; - - // main return - handler.main_return(data, interpreter_result.result, output, &gas) - } -} - -/// EVM transaction interface. -#[auto_impl(&mut, Box)] -pub trait Transact { - /// Run checks that could make transaction fail before call/create. - fn preverify_transaction(&mut self) -> Result<(), EVMError>; - - /// Skip pre-verification steps and execute the transaction. - fn transact_preverified(&mut self) -> EVMResult; - - /// Execute transaction by running pre-verification steps and then transaction itself. - fn transact(&mut self) -> EVMResult; -} - -impl<'a, SPEC: Spec + 'static, DB: Database> Transact for EVMImpl<'a, SPEC, DB> { - #[inline] - fn preverify_transaction(&mut self) -> Result<(), EVMError> { - self.preverify_transaction_inner() - } - - #[inline] - fn transact_preverified(&mut self) -> EVMResult { - let output = self.transact_preverified_inner(); - self.handler.end(&mut self.context, output) - } - - #[inline] - fn transact(&mut self) -> EVMResult { - let output = self - .preverify_transaction_inner() - .and_then(|()| self.transact_preverified_inner()); - self.handler.end(&mut self.context, output) - } -} - -impl<'a, SPEC: Spec + 'static, DB: Database> Host for EVMImpl<'a, SPEC, DB> { - fn env(&mut self) -> &mut Env { - self.context.env() - } - - fn block_hash(&mut self, number: U256) -> Option { - self.context.block_hash(number) - } - - fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { - self.context.load_account(address) - } - - fn balance(&mut self, address: Address) -> Option<(U256, bool)> { - self.context.balance(address) - } - - fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { - self.context.code(address) - } - - /// Get code hash of address. - fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { - self.context.code_hash(address) - } - - fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { - self.context.sload(address, index) - } - - fn sstore( - &mut self, - address: Address, - index: U256, - value: U256, - ) -> Option<(U256, U256, U256, bool)> { - self.context.sstore(address, index, value) - } - - fn tload(&mut self, address: Address, index: U256) -> U256 { - self.context.tload(address, index) - } - - fn tstore(&mut self, address: Address, index: U256, value: U256) { - self.context.tstore(address, index, value) - } - - fn log(&mut self, address: Address, topics: Vec, data: Bytes) { - if let Some(inspector) = self.inspector.as_mut() { - inspector.log(&mut self.context, &address, &topics, &data); - } - let log = Log { - address, - topics, - data, - }; - self.context.journaled_state.log(log); - } - - fn selfdestruct(&mut self, address: Address, target: Address) -> Option { - if let Some(inspector) = self.inspector.as_mut() { - let acc = self.context.journaled_state.state.get(&address).unwrap(); - inspector.selfdestruct(address, target, acc.info.balance); - } - self.context - .journaled_state - .selfdestruct(address, target, self.context.db) - .map_err(|e| self.context.error = Some(e)) - .ok() - } -} - -/// Creates new EVM instance with erased types. -pub fn new_evm<'a, DB: Database>( - env: &'a mut Env, - db: &'a mut DB, - insp: Option<&'a mut dyn Inspector>, -) -> Box + 'a> { - macro_rules! create_evm { - ($spec:ident) => { - Box::new(EVMImpl::<'a, $spec, DB>::new_with_spec( - db, - env, - insp, - Precompiles::new(revm_precompile::SpecId::from_spec_id($spec::SPEC_ID)).clone(), - )) - }; - } - - use specification::*; - match env.cfg.spec_id { - SpecId::FRONTIER | SpecId::FRONTIER_THAWING => create_evm!(FrontierSpec), - SpecId::HOMESTEAD | SpecId::DAO_FORK => create_evm!(HomesteadSpec), - SpecId::TANGERINE => create_evm!(TangerineSpec), - SpecId::SPURIOUS_DRAGON => create_evm!(SpuriousDragonSpec), - SpecId::BYZANTIUM => create_evm!(ByzantiumSpec), - SpecId::PETERSBURG | SpecId::CONSTANTINOPLE => create_evm!(PetersburgSpec), - SpecId::ISTANBUL | SpecId::MUIR_GLACIER => create_evm!(IstanbulSpec), - SpecId::BERLIN => create_evm!(BerlinSpec), - SpecId::LONDON | SpecId::ARROW_GLACIER | SpecId::GRAY_GLACIER => { - create_evm!(LondonSpec) - } - SpecId::MERGE => create_evm!(MergeSpec), - SpecId::SHANGHAI => create_evm!(ShanghaiSpec), - SpecId::CANCUN => create_evm!(CancunSpec), - SpecId::LATEST => create_evm!(LatestSpec), - #[cfg(feature = "optimism")] - SpecId::BEDROCK => create_evm!(BedrockSpec), - #[cfg(feature = "optimism")] - SpecId::REGOLITH => create_evm!(RegolithSpec), - } -} - -#[cfg(feature = "optimism")] -#[cfg(test)] -mod tests { - use super::*; - - use crate::db::InMemoryDB; - use crate::primitives::{specification::BedrockSpec, state::AccountInfo, SpecId}; - - #[test] - fn test_commit_mint_value() { - let caller = Address::ZERO; - let mint_value = Some(1u128); - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert!(EVMImpl::::commit_mint_value( - caller, - mint_value, - &mut db, - &mut journal - ) - .is_ok(),); - - // Check the account balance is updated. - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(101)); - - // No mint value should be a no-op. - assert!(EVMImpl::::commit_mint_value( - caller, - None, - &mut db, - &mut journal - ) - .is_ok(),); - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(101)); - } - - #[test] - fn test_remove_l1_cost_non_deposit() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - let slots = &[U256::from(100)]; - journal - .initial_account_load(caller, slots, &mut db) - .unwrap(); - assert!(EVMImpl::::remove_l1_cost( - true, - caller, - U256::ZERO, - &mut db, - &mut journal - ) - .is_ok(),); - } - - #[test] - fn test_remove_l1_cost() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert!(EVMImpl::::remove_l1_cost( - false, - caller, - U256::from(1), - &mut db, - &mut journal - ) - .is_ok(),); - - // Check the account balance is updated. - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(99)); - } - - #[test] - fn test_remove_l1_cost_lack_of_funds() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert_eq!( - EVMImpl::::remove_l1_cost( - false, - caller, - U256::from(101), - &mut db, - &mut journal - ), - Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: 101u64, - balance: U256::from(100), - }, - )) - ); - } -} diff --git a/crates/revm/src/frame.rs b/crates/revm/src/frame.rs index 251cd974de..b504047273 100644 --- a/crates/revm/src/frame.rs +++ b/crates/revm/src/frame.rs @@ -1,3 +1,5 @@ +use revm_interpreter::InterpreterResult; + use crate::JournalCheckpoint; use crate::{interpreter::Interpreter, primitives::Address}; use core::ops::Range; @@ -17,3 +19,19 @@ pub struct CallStackFrame { /// Interpreter pub interpreter: Interpreter, } + + +/// Contains either a frame or a result. +pub enum FrameOrResult { + /// Boxed stack frame + Frame(Box), + /// Interpreter result + Result(InterpreterResult), +} + +impl FrameOrResult { + /// Returns new frame. + pub fn new_frame(frame: CallStackFrame) -> Self { + Self::Frame(Box::new(frame)) + } +} \ No newline at end of file diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index eef328bd41..453dc5d69a 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -2,28 +2,32 @@ pub mod mainnet; #[cfg(feature = "optimism")] pub mod optimism; +use core::ops::Range; + +use revm_interpreter::{CallInputs, CreateInputs, SharedMemory}; + use crate::{ interpreter::{Gas, InstructionResult}, primitives::{db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec}, - EvmContext, + CallStackFrame, Evm, EvmContext, }; /// Handle call return and return final gas value. type CallReturnHandle = fn(&Env, InstructionResult, Gas) -> Gas; /// Reimburse the caller with ethereum it didn't spent. -type ReimburseCallerHandle = - fn(&mut EvmContext<'_, DB>, &Gas) -> EVMResultGeneric<(), ::Error>; +type ReimburseCallerHandle = + fn(&mut EvmContext<'_, EXT, DB>, &Gas) -> EVMResultGeneric<(), ::Error>; /// Reward beneficiary with transaction rewards. -type RewardBeneficiaryHandle = ReimburseCallerHandle; +type RewardBeneficiaryHandle = ReimburseCallerHandle; /// Calculate gas refund for transaction. type CalculateGasRefundHandle = fn(&Env, &Gas) -> u64; /// Main return handle, takes state from journal and transforms internal result to external. -type MainReturnHandle = fn( - &mut EvmContext<'_, DB>, +type MainReturnHandle = fn( + &mut EvmContext<'_, EXT, DB>, InstructionResult, Output, &Gas, @@ -33,41 +37,59 @@ type MainReturnHandle = fn( /// This will be called after all the other handlers. /// /// It is useful for catching errors and returning them in a different way. -type EndHandle = fn( - &mut EvmContext<'_, DB>, +type EndHandle = fn( + &mut EvmContext<'_, EXT, DB>, evm_output: Result::Error>>, ) -> Result::Error>>; +// Sub call +// type SubCall = fn( +// evm: &mut Evm<'_, SPEC, DB>, +// inputs: Box, +// curent_stake_frame: &mut CallStackFrame, +// shared_memory: &mut SharedMemory, +// return_memory_offset: Range, +// ) -> Option>; + +// /// sub create call +// type SubCreateCall = fn( +// evm: &mut Evm<'_, SPEC, DB>, +// curent_stack_frame: &mut CallStackFrame, +// inputs: Box, +// ) -> Option>; + /// Handler acts as a proxy and allow to define different behavior for different /// sections of the code. This allows nice integration of different chains or /// to disable some mainnet behavior. -pub struct Handler { +pub struct Handler { // Uses env, call result and returned gas from the call to determine the gas // that is returned from transaction execution.. pub call_return: CallReturnHandle, /// Reimburse the caller with ethereum it didn't spent. - pub reimburse_caller: ReimburseCallerHandle, + pub reimburse_caller: ReimburseCallerHandle, /// Reward the beneficiary with caller fee. - pub reward_beneficiary: RewardBeneficiaryHandle, + pub reward_beneficiary: RewardBeneficiaryHandle, /// Calculate gas refund for transaction. /// Some chains have it disabled. pub calculate_gas_refund: CalculateGasRefundHandle, /// Main return handle, returns the output of the transact. - pub main_return: MainReturnHandle, + pub main_return: MainReturnHandle, /// End handle. - pub end: EndHandle, + pub end: EndHandle, + // Called on sub call. + //pub sub_call: SubCall, } -impl Handler { +impl Handler { /// Handler for the mainnet pub fn mainnet() -> Self { Self { call_return: mainnet::handle_call_return::, calculate_gas_refund: mainnet::calculate_gas_refund::, - reimburse_caller: mainnet::handle_reimburse_caller::, - reward_beneficiary: mainnet::reward_beneficiary::, - main_return: mainnet::main_return::, - end: mainnet::end_handle::, + reimburse_caller: mainnet::handle_reimburse_caller::, + reward_beneficiary: mainnet::reward_beneficiary::, + main_return: mainnet::main::main_return::, + end: mainnet::main::end_handle::, } } @@ -76,10 +98,10 @@ impl Handler { pub fn optimism() -> Self { Self { call_return: optimism::handle_call_return::, + calculate_gas_refund: optimism::calculate_gas_refund::, // we reinburse caller the same was as in mainnet. // Refund is calculated differently then mainnet. reimburse_caller: mainnet::handle_reimburse_caller::, - calculate_gas_refund: optimism::calculate_gas_refund::, reward_beneficiary: optimism::reward_beneficiary::, // In case of halt of deposit transaction return Error. main_return: optimism::main_return::, @@ -95,7 +117,7 @@ impl Handler { /// Reimburse the caller with gas that were not spend. pub fn reimburse_caller( &self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext<'_, EXT, DB>, gas: &Gas, ) -> Result<(), EVMError> { (self.reimburse_caller)(context, gas) @@ -109,7 +131,7 @@ impl Handler { /// Reward beneficiary pub fn reward_beneficiary( &self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext<'_, EXT, DB>, gas: &Gas, ) -> Result<(), EVMError> { (self.reward_beneficiary)(context, gas) @@ -118,7 +140,7 @@ impl Handler { /// Main return. pub fn main_return( &self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext<'_, EXT, DB>, call_result: InstructionResult, output: Output, gas: &Gas, @@ -129,7 +151,7 @@ impl Handler { /// End handler. pub fn end( &self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext<'_, EXT, DB>, end_output: Result>, ) -> Result> { (self.end)(context, end_output) diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index c39293391f..120570f22c 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -1,5 +1,8 @@ //! Mainnet related handlers. +pub mod call_loop; +pub mod main; + use crate::{ interpreter::{return_ok, return_revert, Gas, InstructionResult, SuccessOrHalt}, primitives::{ @@ -35,8 +38,8 @@ pub fn handle_call_return( } #[inline] -pub fn handle_reimburse_caller( - context: &mut EvmContext<'_, DB>, +pub fn handle_reimburse_caller( + context: &mut EvmContext<'_, EXT, DB>, gas: &Gas, ) -> Result<(), EVMError> { let caller = context.env.tx.caller; @@ -58,8 +61,8 @@ pub fn handle_reimburse_caller( /// Reward beneficiary with gas fee. #[inline] -pub fn reward_beneficiary( - context: &mut EvmContext<'_, DB>, +pub fn reward_beneficiary( + context: &mut EvmContext<'_, EXT, DB>, gas: &Gas, ) -> Result<(), EVMError> { let beneficiary = context.env.block.coinbase; @@ -104,63 +107,6 @@ pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { } } -//pub fn main_first_call - -/// Main return handle, returns the output of the transaction. -#[inline] -pub fn main_return( - context: &mut EvmContext<'_, DB>, - call_result: InstructionResult, - output: Output, - gas: &Gas, -) -> Result> { - // used gas with refund calculated. - let gas_refunded = gas.refunded() as u64; - let final_gas_used = gas.spend() - gas_refunded; - - // reset journal and return present state. - let (state, logs) = context.journaled_state.finalize(); - - let result = match call_result.into() { - SuccessOrHalt::Success(reason) => ExecutionResult::Success { - reason, - gas_used: final_gas_used, - gas_refunded, - logs, - output, - }, - SuccessOrHalt::Revert => ExecutionResult::Revert { - gas_used: final_gas_used, - output: match output { - Output::Call(return_value) => return_value, - Output::Create(return_value, _) => return_value, - }, - }, - SuccessOrHalt::Halt(reason) => ExecutionResult::Halt { - reason, - gas_used: final_gas_used, - }, - SuccessOrHalt::FatalExternalError => { - return Err(EVMError::Database(context.error.take().unwrap())); - } - // Only two internal return flags. - SuccessOrHalt::InternalContinue | SuccessOrHalt::InternalCallOrCreate => { - panic!("Internal return flags should remain internal {call_result:?}") - } - }; - - Ok(ResultAndState { result, state }) -} - -/// Mainnet end handle does not change the output. -#[inline] -pub fn end_handle( - _context: &mut EvmContext<'_, DB>, - evm_output: Result>, -) -> Result> { - evm_output -} - #[cfg(test)] mod tests { use revm_interpreter::primitives::CancunSpec; diff --git a/crates/revm/src/handler/mainnet/call_loop.rs b/crates/revm/src/handler/mainnet/call_loop.rs new file mode 100644 index 0000000000..9d6eaf2d05 --- /dev/null +++ b/crates/revm/src/handler/mainnet/call_loop.rs @@ -0,0 +1,91 @@ +use revm_interpreter::SharedMemory; + +use crate::{ + interpreter::{CallInputs, CreateInputs}, + primitives::Spec, + CallStackFrame, Database, Evm, EvmContext, FrameOrResult, +}; +use core::ops::Range; + +/// +pub fn initial_call_create() -> FrameOrResult { + unimplemented!() +} + +// Handles action for new sub call, return None if there is no need to add +// new stack frame. +// #[inline] +// pub fn sub_call( +// evm: &mut Evm<'_, SPEC, DB>, +// mut inputs: Box, +// curent_stake_frame: &mut CallStackFrame, +// shared_memory: &mut SharedMemory, +// return_memory_offset: Range, +// ) -> Option> { +// // Call inspector if it is some. +// if let Some(inspector) = evm.inspector.as_mut() { +// if let Some((result, range)) = inspector.call(&mut evm.context, &mut inputs) { +// curent_stake_frame +// .interpreter +// .insert_call_output(shared_memory, result, range); +// return None; +// } +// } +// match evm +// .context +// .make_call_frame(&inputs, return_memory_offset.clone()) +// { +// FrameOrResult::Frame(new_frame) => Some(new_frame), +// FrameOrResult::Result(mut result) => { +// //println!("Result returned right away: {:#?}", result); +// if let Some(inspector) = evm.inspector.as_mut() { +// result = inspector.call_end(&mut evm.context, result); +// } +// curent_stake_frame.interpreter.insert_call_output( +// shared_memory, +// result, +// return_memory_offset, +// ); +// None +// } +// } +// } + +// /// Handle Action for new sub create call, return None if there is no need +// /// to add new stack frame. +// pub fn sub_create( +// evm: &mut Evm<'_, SPEC, DB>, +// curent_stack_frame: &mut CallStackFrame, +// mut inputs: Box, +// ) -> Option> { +// // Call inspector if it is some. +// if let Some(inspector) = evm.inspector.as_mut() { +// if let Some((result, address)) = inspector.create(&mut evm.context, &mut inputs) { +// curent_stack_frame +// .interpreter +// .insert_create_output(result, address); +// return None; +// } +// } + +// match evm.context.make_create_frame::(&inputs) { +// FrameOrResult::Frame(new_frame) => Some(new_frame), +// FrameOrResult::Result(mut result) => { +// let mut address = None; +// if let Some(inspector) = evm.inspector.as_mut() { +// let ret = inspector.create_end( +// &mut evm.context, +// result, +// curent_stack_frame.created_address, +// ); +// result = ret.0; +// address = ret.1; +// } +// // insert result of the failed creation of create CallStackFrame. +// curent_stack_frame +// .interpreter +// .insert_create_output(result, address); +// None +// } +// } +// } diff --git a/crates/revm/src/handler/mainnet/main.rs b/crates/revm/src/handler/mainnet/main.rs new file mode 100644 index 0000000000..e52c735bc2 --- /dev/null +++ b/crates/revm/src/handler/mainnet/main.rs @@ -0,0 +1,67 @@ +//! Handles related to the main function of the EVM. +//! +//! They handle initial setup of the EVM, call loop and the final return of the EVM + +use crate::{ + interpreter::{return_ok, return_revert, Gas, InstructionResult, SuccessOrHalt}, + primitives::{ + db::Database, EVMError, Env, ExecutionResult, Output, ResultAndState, Spec, SpecId::LONDON, + U256, + }, + EvmContext, +}; + +/// Main return handle, returns the output of the transaction. +#[inline] +pub fn main_return( + context: &mut EvmContext<'_, EXT, DB>, + call_result: InstructionResult, + output: Output, + gas: &Gas, +) -> Result> { + // used gas with refund calculated. + let gas_refunded = gas.refunded() as u64; + let final_gas_used = gas.spend() - gas_refunded; + + // reset journal and return present state. + let (state, logs) = context.journaled_state.finalize(); + + let result = match call_result.into() { + SuccessOrHalt::Success(reason) => ExecutionResult::Success { + reason, + gas_used: final_gas_used, + gas_refunded, + logs, + output, + }, + SuccessOrHalt::Revert => ExecutionResult::Revert { + gas_used: final_gas_used, + output: match output { + Output::Call(return_value) => return_value, + Output::Create(return_value, _) => return_value, + }, + }, + SuccessOrHalt::Halt(reason) => ExecutionResult::Halt { + reason, + gas_used: final_gas_used, + }, + SuccessOrHalt::FatalExternalError => { + return Err(EVMError::Database(context.error.take().unwrap())); + } + // Only two internal return flags. + SuccessOrHalt::InternalContinue | SuccessOrHalt::InternalCallOrCreate => { + panic!("Internal return flags should remain internal {call_result:?}") + } + }; + + Ok(ResultAndState { result, state }) +} + +/// Mainnet end handle does not change the output. +#[inline] +pub fn end_handle( + _context: &mut EvmContext<'_, EXT, DB>, + evm_output: Result>, +) -> Result> { + evm_output +} diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index 0eb953d7c7..5007dce23c 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -8,9 +8,9 @@ use crate::{ use auto_impl::auto_impl; #[cfg(feature = "std")] -mod customprinter; +//mod customprinter; #[cfg(all(feature = "std", feature = "serde"))] -mod eip3155; +//mod eip3155; mod gas; mod instruction; mod noop; @@ -20,22 +20,26 @@ use revm_interpreter::InterpreterResult; /// [Inspector] implementations. pub mod inspectors { #[cfg(feature = "std")] - pub use super::customprinter::CustomPrintTracer; + //pub use super::customprinter::CustomPrintTracer; #[cfg(all(feature = "std", feature = "serde"))] - pub use super::eip3155::TracerEip3155; + //pub use super::eip3155::TracerEip3155; pub use super::gas::GasInspector; pub use super::noop::NoOpInspector; } /// EVM [Interpreter] callbacks. #[auto_impl(&mut, Box)] -pub trait Inspector { +pub trait Inspector { /// Called before the interpreter is initialized. /// /// If `interp.instruction_result` is set to anything other than [InstructionResult::Continue] then the execution of the interpreter /// is skipped. #[inline] - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn initialize_interp( + &mut self, + interp: &mut Interpreter, + context: &mut EvmContext<'_, EXT, DB>, + ) { let _ = interp; let _ = context; } @@ -49,7 +53,7 @@ pub trait Inspector { /// /// To get the current opcode, use `interp.current_opcode()`. #[inline] - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, EXT, DB>) { let _ = interp; let _ = context; } @@ -58,7 +62,7 @@ pub trait Inspector { #[inline] fn log( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext<'_, EXT, DB>, address: &Address, topics: &[B256], data: &Bytes, @@ -74,7 +78,7 @@ pub trait Inspector { /// Setting `interp.instruction_result` to anything other than [InstructionResult::Continue] alters the execution /// of the interpreter. #[inline] - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, EXT, DB>) { let _ = interp; let _ = context; } @@ -85,7 +89,7 @@ pub trait Inspector { #[inline] fn call( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext<'_, EXT, DB>, inputs: &mut CallInputs, ) -> Option<(InterpreterResult, Range)> { let _ = context; @@ -100,7 +104,7 @@ pub trait Inspector { #[inline] fn call_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext<'_, EXT, DB>, result: InterpreterResult, ) -> InterpreterResult { let _ = context; @@ -113,7 +117,7 @@ pub trait Inspector { #[inline] fn create( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext<'_, EXT, DB>, inputs: &mut CreateInputs, ) -> Option<(InterpreterResult, Option
)> { let _ = context; @@ -128,7 +132,7 @@ pub trait Inspector { #[inline] fn create_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext<'_, EXT, DB>, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs index 9ea86d5f9b..6e7010540b 100644 --- a/crates/revm/src/inspector/customprinter.rs +++ b/crates/revm/src/inspector/customprinter.rs @@ -19,7 +19,7 @@ pub struct CustomPrintTracer { } impl Inspector for CustomPrintTracer { - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, Self, DB>) { self.gas_inspector.initialize_interp(interp, context); } diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 965d87e009..42df1e4018 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -24,11 +24,11 @@ impl GasInspector { } } -impl Inspector for GasInspector { +impl Inspector for GasInspector { fn initialize_interp( &mut self, interp: &mut crate::interpreter::Interpreter, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext<'_, EXT, DB>, ) { self.gas_remaining = interp.gas.limit(); } @@ -36,7 +36,7 @@ impl Inspector for GasInspector { fn step_end( &mut self, interp: &mut crate::interpreter::Interpreter, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext<'_, EXT, DB>, ) { let last_gas = core::mem::replace(&mut self.gas_remaining, interp.gas.remaining()); self.last_gas_cost = last_gas.saturating_sub(self.last_gas_cost); @@ -44,7 +44,7 @@ impl Inspector for GasInspector { fn call_end( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext<'_, EXT, DB>, mut result: InterpreterResult, ) -> InterpreterResult { if result.result.is_error() { @@ -56,7 +56,7 @@ impl Inspector for GasInspector { fn create_end( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext<'_, EXT, DB>, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { @@ -64,137 +64,137 @@ impl Inspector for GasInspector { } } -#[cfg(test)] -mod tests { - use core::ops::Range; - - use crate::{ - inspectors::GasInspector, - interpreter::{CallInputs, CreateInputs, Interpreter, InterpreterResult}, - primitives::{Address, Bytes, B256}, - Database, EvmContext, Inspector, - }; - - #[derive(Default, Debug)] - struct StackInspector { - pc: usize, - gas_inspector: GasInspector, - gas_remaining_steps: Vec<(usize, u64)>, - } - - impl Inspector for StackInspector { - fn initialize_interp( - &mut self, - interp: &mut Interpreter, - context: &mut EvmContext<'_, DB>, - ) { - self.gas_inspector.initialize_interp(interp, context); - } - - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { - self.pc = interp.program_counter(); - self.gas_inspector.step(interp, context); - } - - fn log( - &mut self, - context: &mut EvmContext<'_, DB>, - address: &Address, - topics: &[B256], - data: &Bytes, - ) { - self.gas_inspector.log(context, address, topics, data); - } - - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { - self.gas_inspector.step_end(interp, context); - self.gas_remaining_steps - .push((self.pc, self.gas_inspector.gas_remaining())); - } - - fn call( - &mut self, - context: &mut EvmContext<'_, DB>, - call: &mut CallInputs, - ) -> Option<(InterpreterResult, Range)> { - self.gas_inspector.call(context, call) - } - - fn call_end( - &mut self, - context: &mut EvmContext<'_, DB>, - result: InterpreterResult, - ) -> InterpreterResult { - self.gas_inspector.call_end(context, result) - } - - fn create( - &mut self, - context: &mut EvmContext<'_, DB>, - call: &mut CreateInputs, - ) -> Option<(InterpreterResult, Option
)> { - self.gas_inspector.create(context, call); - None - } - - fn create_end( - &mut self, - context: &mut EvmContext<'_, DB>, - result: InterpreterResult, - address: Option
, - ) -> (InterpreterResult, Option
) { - self.gas_inspector.create_end(context, result, address) - } - } - - #[test] - #[cfg(not(feature = "optimism"))] - fn test_gas_inspector() { - use crate::db::BenchmarkDB; - use crate::interpreter::opcode; - use crate::primitives::{address, Bytecode, Bytes, TransactTo}; - - let contract_data: Bytes = Bytes::from(vec![ - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0xb, - opcode::JUMPI, - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0x1, - opcode::JUMPDEST, - opcode::STOP, - ]); - let bytecode = Bytecode::new_raw(contract_data); - - let mut evm = crate::new(); - evm.database(BenchmarkDB::new_bytecode(bytecode.clone())); - evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); - evm.env.tx.transact_to = - TransactTo::Call(address!("0000000000000000000000000000000000000000")); - evm.env.tx.gas_limit = 21100; - - let mut inspector = StackInspector::default(); - evm.inspect(&mut inspector).unwrap(); - - // starting from 100gas - let steps = vec![ - // push1 -3 - (0, 97), - // push1 -3 - (2, 94), - // jumpi -10 - (4, 84), - // jumpdest 1 - (11, 83), - // stop 0 - (12, 83), - ]; - - assert_eq!(inspector.gas_remaining_steps, steps); - } -} +// #[cfg(test)] +// mod tests { +// use core::ops::Range; + +// use crate::{ +// inspectors::GasInspector, +// interpreter::{CallInputs, CreateInputs, Interpreter, InterpreterResult}, +// primitives::{Address, Bytes, B256}, +// Database, EvmContext, Inspector, +// }; + +// #[derive(Default, Debug)] +// struct StackInspector { +// pc: usize, +// gas_inspector: GasInspector, +// gas_remaining_steps: Vec<(usize, u64)>, +// } + +// impl Inspector for StackInspector { +// fn initialize_interp( +// &mut self, +// interp: &mut Interpreter, +// context: &mut EvmContext<'_, DB>, +// ) { +// self.gas_inspector.initialize_interp(interp, context); +// } + +// fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { +// self.pc = interp.program_counter(); +// self.gas_inspector.step(interp, context); +// } + +// fn log( +// &mut self, +// context: &mut EvmContext<'_, DB>, +// address: &Address, +// topics: &[B256], +// data: &Bytes, +// ) { +// self.gas_inspector.log(context, address, topics, data); +// } + +// fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { +// self.gas_inspector.step_end(interp, context); +// self.gas_remaining_steps +// .push((self.pc, self.gas_inspector.gas_remaining())); +// } + +// fn call( +// &mut self, +// context: &mut EvmContext<'_, DB>, +// call: &mut CallInputs, +// ) -> Option<(InterpreterResult, Range)> { +// self.gas_inspector.call(context, call) +// } + +// fn call_end( +// &mut self, +// context: &mut EvmContext<'_, DB>, +// result: InterpreterResult, +// ) -> InterpreterResult { +// self.gas_inspector.call_end(context, result) +// } + +// fn create( +// &mut self, +// context: &mut EvmContext<'_, DB>, +// call: &mut CreateInputs, +// ) -> Option<(InterpreterResult, Option
)> { +// self.gas_inspector.create(context, call); +// None +// } + +// fn create_end( +// &mut self, +// context: &mut EvmContext<'_, DB>, +// result: InterpreterResult, +// address: Option
, +// ) -> (InterpreterResult, Option
) { +// self.gas_inspector.create_end(context, result, address) +// } +// } + +// #[test] +// #[cfg(not(feature = "optimism"))] +// fn test_gas_inspector() { +// use crate::db::BenchmarkDB; +// use crate::interpreter::opcode; +// use crate::primitives::{address, Bytecode, Bytes, TransactTo}; + +// let contract_data: Bytes = Bytes::from(vec![ +// opcode::PUSH1, +// 0x1, +// opcode::PUSH1, +// 0xb, +// opcode::JUMPI, +// opcode::PUSH1, +// 0x1, +// opcode::PUSH1, +// 0x1, +// opcode::PUSH1, +// 0x1, +// opcode::JUMPDEST, +// opcode::STOP, +// ]); +// let bytecode = Bytecode::new_raw(contract_data); + +// let mut evm = crate::new(); +// evm.database(BenchmarkDB::new_bytecode(bytecode.clone())); +// evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); +// evm.env.tx.transact_to = +// TransactTo::Call(address!("0000000000000000000000000000000000000000")); +// evm.env.tx.gas_limit = 21100; + +// let mut inspector = StackInspector::default(); +// evm.inspect(&mut inspector).unwrap(); + +// // starting from 100gas +// let steps = vec![ +// // push1 -3 +// (0, 97), +// // push1 -3 +// (2, 94), +// // jumpi -10 +// (4, 84), +// // jumpdest 1 +// (11, 83), +// // stop 0 +// (12, 83), +// ]; + +// assert_eq!(inspector.gas_remaining_steps, steps); +// } +// } diff --git a/crates/revm/src/inspector/instruction.rs b/crates/revm/src/inspector/instruction.rs index b78be6ccea..495f54156a 100644 --- a/crates/revm/src/inspector/instruction.rs +++ b/crates/revm/src/inspector/instruction.rs @@ -1,4 +1,4 @@ -use crate::EVMImpl; +use crate::Evm; use alloc::boxed::Box; use revm_interpreter::{ opcode::{BoxedInstruction, Instruction}, @@ -7,11 +7,11 @@ use revm_interpreter::{ }; /// Outer closure that calls Inspector for every instruction. -pub fn inspector_instruction<'a, SPEC: Spec + 'static, DB: Database>( - instruction: Instruction>, -) -> BoxedInstruction<'a, EVMImpl<'a, SPEC, DB>> { +pub fn inspector_instruction<'a, SPEC: Spec + 'static, EXT, DB: Database>( + instruction: Instruction>, +) -> BoxedInstruction<'a, Evm<'a, SPEC, EXT, DB>> { Box::new( - move |interpreter: &mut Interpreter, host: &mut EVMImpl<'a, SPEC, DB>| { + move |interpreter: &mut Interpreter, host: &mut Evm<'a, SPEC, EXT, DB>| { if let Some(inspector) = host.inspector.as_mut() { // SAFETY: as the PC was already incremented we need to subtract 1 to preserve the // old Inspector behavior. @@ -39,15 +39,15 @@ pub fn inspector_instruction<'a, SPEC: Spec + 'static, DB: Database>( #[cfg(test)] mod tests { use super::*; - use crate::{db::EmptyDB, interpreter::opcode::*, primitives::BerlinSpec, EVMImpl}; + use crate::{db::EmptyDB, interpreter::opcode::*, primitives::BerlinSpec, Evm}; #[test] fn test_make_boxed_instruction_table() { // test that this pattern builds. - let inst: InstructionTable> = - make_instruction_table::, BerlinSpec>(); - let _test: BoxedInstructionTable<'_, EVMImpl<'_, BerlinSpec, _>> = - make_boxed_instruction_table::<'_, EVMImpl<'_, BerlinSpec, EmptyDB>, BerlinSpec, _>( + let inst: InstructionTable> = + make_instruction_table::, BerlinSpec>(); + let _test: BoxedInstructionTable<'_, Evm<'_, BerlinSpec, _, _>> = + make_boxed_instruction_table::<'_, Evm<'_, BerlinSpec, _, EmptyDB>, BerlinSpec, _>( inst, inspector_instruction, ); diff --git a/crates/revm/src/inspector/noop.rs b/crates/revm/src/inspector/noop.rs index 05d7292fc4..166208352d 100644 --- a/crates/revm/src/inspector/noop.rs +++ b/crates/revm/src/inspector/noop.rs @@ -4,4 +4,4 @@ use crate::{Database, Inspector}; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct NoOpInspector; -impl Inspector for NoOpInspector {} +impl Inspector for NoOpInspector {} diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 93f7c63086..afcd1518f4 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -11,9 +11,9 @@ compile_error!("`with-serde` feature has been renamed to `serde`."); extern crate alloc; pub mod db; -mod evm; +mod evm_factory; mod evm_context; -mod evm_impl; +mod evm; mod frame; pub mod handler; mod inspector; @@ -28,10 +28,10 @@ pub use db::{ CacheState, DBBox, State, StateBuilder, StateDBBox, TransitionAccount, TransitionState, }; pub use db::{Database, DatabaseCommit, DatabaseRef, InMemoryDB}; -pub use evm::{new, EVM}; +pub use evm_factory::{new, EvmFactory}; pub use evm_context::EvmContext; -pub use evm_impl::{new_evm, EVMImpl, Transact, CALL_STACK_LIMIT}; -pub use frame::CallStackFrame; +pub use evm::{new_evm, Evm, Transact, CALL_STACK_LIMIT}; +pub use frame::{CallStackFrame,FrameOrResult}; pub use journaled_state::{JournalCheckpoint, JournalEntry, JournaledState}; // reexport `revm_precompiles` diff --git a/documentation/src/crates/revm/evm_impl.md b/documentation/src/crates/revm/evm_impl.md index afb0a0ee99..6d5a0074e6 100644 --- a/documentation/src/crates/revm/evm_impl.md +++ b/documentation/src/crates/revm/evm_impl.md @@ -1,6 +1,6 @@ # EVM Implementation -This module implements the Ethereum Virtual Machine (EVM), a stack-based virtual machine that executes Ethereum smart contracts. The following methods are exposed through the `EVMImpl` struct. +This module implements the Ethereum Virtual Machine (EVM), a stack-based virtual machine that executes Ethereum smart contracts. The following methods are exposed through the `Evm` struct. ## Methods diff --git a/documentation/src/crates/revm/host_trait.md b/documentation/src/crates/revm/host_trait.md index 4abf291f5d..4ddb06ec24 100644 --- a/documentation/src/crates/revm/host_trait.md +++ b/documentation/src/crates/revm/host_trait.md @@ -2,7 +2,7 @@ The `Host` trait provides an interface that allows the EVM to interact with the external world. It contains methods to access environmental information, manipulate account balances, and interact with contract code and storage. -The [`EVMImpl`](./evm_impl.md) struct implements this `Host` trait. +The [`Evm`](./evm_impl.md) struct implements this `Host` trait. - `step` & `step_end` diff --git a/examples/fork_ref_transact.rs b/examples/fork_ref_transact.rs index 8abcacfd5c..0c5723cc5e 100644 --- a/examples/fork_ref_transact.rs +++ b/examples/fork_ref_transact.rs @@ -4,7 +4,7 @@ use ethers_providers::{Http, Provider}; use revm::{ db::{CacheDB, EmptyDB, EthersDB}, primitives::{address, ExecutionResult, Output, TransactTo, U256}, - Database, EVM, + Database, EvmFactory, }; use std::sync::Arc; @@ -65,7 +65,7 @@ async fn main() -> anyhow::Result<()> { .unwrap(); // initialise an empty (default) EVM - let mut evm = EVM::new(); + let mut evm = EvmFactory::new(); // insert pre-built database from above evm.database(cache_db); From fc03f9b7d0a3590a4dad0f3d6aac7340a7460175 Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 8 Nov 2023 15:36:07 +0100 Subject: [PATCH 02/52] tests --- crates/revm/src/evm_context.rs | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/crates/revm/src/evm_context.rs b/crates/revm/src/evm_context.rs index e1b0bacf42..abbee0f871 100644 --- a/crates/revm/src/evm_context.rs +++ b/crates/revm/src/evm_context.rs @@ -36,6 +36,56 @@ pub struct EvmContext<'a, EXT, DB: Database> { pub l1_block_info: Option, } +pub struct Temp { + pub external: EXT, + pub handle: Box, +} + +impl Temp { + pub fn call(&mut self) { + let handle = &mut self.handle; + let ext = &mut self.external; + handle(ext); + } +} + +#[derive(Default)] +pub struct External { + pub var: bool, +} + +impl External { + pub fn handle(&self) -> Box { + Box::new(call) + } +} + +impl ExtT for External { + fn change(&mut self) { + self.var = true; + } +} + +pub trait ExtT { + fn change(&mut self); +} + +fn call(temp: &mut impl ExtT) { + temp.change() +} + +pub fn test() { + let ext = External::default(); + let handle = ext.handle(); + let mut temp = Temp { + external: ext, + handle: handle, + }; + + temp.call(); + assert!(temp.external.var); +} + impl<'a, EXT, DB: Database> EvmContext<'a, EXT, DB> { /// Load access list for berlin hard fork. /// From 90a81f0cd0f01b02552d395042d9c27bb712eef5 Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 16 Nov 2023 09:25:29 +0100 Subject: [PATCH 03/52] Split evm and external context --- .../revm/src/{evm_context.rs => context.rs} | 14 +- crates/revm/src/evm.rs | 217 ++++++++++++------ crates/revm/src/evm_factory.rs | 52 +++-- crates/revm/src/handler.rs | 16 +- crates/revm/src/handler/mainnet.rs | 22 +- crates/revm/src/handler/mainnet/main.rs | 10 +- crates/revm/src/inspector.rs | 18 +- crates/revm/src/inspector/gas.rs | 8 +- crates/revm/src/inspector/instruction.rs | 6 +- crates/revm/src/inspector/noop.rs | 2 +- crates/revm/src/lib.rs | 4 +- 11 files changed, 230 insertions(+), 139 deletions(-) rename crates/revm/src/{evm_context.rs => context.rs} (98%) diff --git a/crates/revm/src/evm_context.rs b/crates/revm/src/context.rs similarity index 98% rename from crates/revm/src/evm_context.rs rename to crates/revm/src/context.rs index abbee0f871..0920195dab 100644 --- a/crates/revm/src/evm_context.rs +++ b/crates/revm/src/context.rs @@ -15,9 +15,17 @@ use crate::{ use alloc::boxed::Box; use core::ops::Range; +/// Main Context structure that contains both EvmContext and external contexts. +pub struct Context<'a, EXT, DB: Database> { + /// Evm Context. + pub evm: EvmContext<'a, DB>, + /// External generic code. + pub external: EXT, +} + /// EVM Data contains all the data that EVM needs to execute. #[derive(Debug)] -pub struct EvmContext<'a, EXT, DB: Database> { +pub struct EvmContext<'a, DB: Database> { /// EVM Environment contains all the information about config, block and transaction that /// evm needs. pub env: &'a mut Env, @@ -29,8 +37,6 @@ pub struct EvmContext<'a, EXT, DB: Database> { pub error: Option, /// Precompiles that are available for evm. pub precompiles: Precompiles, - /// External generic code. - pub external: EXT, /// Used as temporary value holder to store L1 block info. #[cfg(feature = "optimism")] pub l1_block_info: Option, @@ -86,7 +92,7 @@ pub fn test() { assert!(temp.external.var); } -impl<'a, EXT, DB: Database> EvmContext<'a, EXT, DB> { +impl<'a, DB: Database> EvmContext<'a, DB> { /// Load access list for berlin hard fork. /// /// Loading of accounts/storages is needed to make them warm. diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 514b853e33..777ccd089e 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -14,7 +14,7 @@ use crate::{ specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, Output, Spec, SpecId::*, TransactTo, B256, U256, }, - CallStackFrame, EvmContext, FrameOrResult, Inspector, + CallStackFrame, Context, EvmContext, FrameOrResult, Inspector, }; use alloc::{boxed::Box, sync::Arc, vec::Vec}; use auto_impl::auto_impl; @@ -26,9 +26,41 @@ use crate::optimism; /// EVM call stack limit. pub const CALL_STACK_LIMIT: u64 = 1024; +/// Register external handles. +pub trait RegisterHandler { + /// Register external handler. + fn register_handler(&self, handler: Handler) -> Handler + where + Self: Sized, + { + handler + } +} + +/// Default registered handler that produces default mainnet handler. +pub struct MainnetHandle; + +impl RegisterHandler for MainnetHandle {} + +pub struct ExternalData { + pub flagg: bool, +} + +impl RegisterHandler for ExternalData { + fn register_handler(&self, mut handler: Handler) -> Handler { + let t = handler.reimburse_caller.clone(); + handler.reimburse_caller = |data, gas| { + println!("Reimburse caller: {:#?} {:#?}", data.external.flagg, gas); + Ok(()) + //t(data, gas) + }; + handler + } +} + pub struct Evm<'a, SPEC: Spec, EXT, DB: Database> { - pub context: EvmContext<'a, EXT, DB>, - pub inspector: Option<&'a mut dyn Inspector>, + pub context: Context<'a, EXT, DB>, + pub inspector: Option<&'a mut dyn Inspector>, pub instruction_table: InstructionTables<'a, Self>, pub handler: Handler, _phantomdata: PhantomData, @@ -43,7 +75,7 @@ where { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Evm") - .field("data", &self.context) + .field("data", &self.context.evm) .finish_non_exhaustive() } } @@ -105,12 +137,12 @@ impl<'a, SPEC: Spec, DB: Database> Evm<'a, SPEC, DB> { } } -impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { +impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler+'a, DB: Database> Evm<'a, SPEC, EXT, DB> { pub fn new_with_spec( db: &'a mut DB, env: &'a mut Env, external: EXT, - inspector: Option<&'a mut dyn Inspector>, + inspector: Option<&'a mut dyn Inspector>, precompiles: Precompiles, ) -> Self { let journaled_state = JournaledState::new( @@ -131,6 +163,9 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { } else { InstructionTables::Plain(Arc::new(make_instruction_table::())) }; + + let mut handler = external.register_handler(Handler::mainnet::()); + /* TODO support #[cfg(feature = "optimism")] let mut handler = if env.cfg.optimism { Handler::optimism::() @@ -139,6 +174,7 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { }; #[cfg(not(feature = "optimism"))] let mut handler = Handler::mainnet::(); + */ if env.cfg.is_beneficiary_reward_disabled() { // do nothing @@ -146,15 +182,17 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { } Self { - context: EvmContext { - env, - journaled_state, - db, - error: None, - precompiles, + context: Context { + evm: EvmContext { + env, + journaled_state, + db, + error: None, + precompiles, + #[cfg(feature = "optimism")] + l1_block_info: None, + }, external, - #[cfg(feature = "optimism")] - l1_block_info: None, }, inspector, instruction_table, @@ -177,7 +215,7 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { #[cfg(feature = "memory_limit")] let mut shared_memory = - SharedMemory::new_with_memory_limit(self.context.env.cfg.memory_limit); + SharedMemory::new_with_memory_limit(self.context.evm.env.cfg.memory_limit); #[cfg(not(feature = "memory_limit"))] let mut shared_memory = SharedMemory::new(); @@ -238,14 +276,14 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { if let Some(inspector) = self.inspector.as_mut() { result = if child_stack_frame.is_create { let (result, address) = inspector.create_end( - &mut self.context, + &mut self.context.evm, result, child_stack_frame.created_address, ); child_stack_frame.created_address = address; result } else { - inspector.call_end(&mut self.context, result) + inspector.call_end(&mut self.context.evm, result) }; } @@ -253,10 +291,11 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { let Some(parent_stack_frame) = parent_stack_frame else { let result = if child_stack_frame.is_create { self.context + .evm .create_return::(result, child_stack_frame) .0 } else { - self.context.call_return(result, child_stack_frame) + self.context.evm.call_return(result, child_stack_frame) }; return Some(result); @@ -265,6 +304,7 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { if child_stack_frame.is_create { let (result, address) = self .context + .evm .create_return::(result, child_stack_frame); parent_stack_frame .interpreter @@ -272,7 +312,7 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { } else { let subcall_memory_return_offset = child_stack_frame.subcall_return_memory_range.clone(); - let result = self.context.call_return(result, child_stack_frame); + let result = self.context.evm.call_return(result, child_stack_frame); parent_stack_frame.interpreter.insert_call_output( shared_memory, @@ -293,7 +333,7 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { ) -> Option> { // Call inspector if it is some. if let Some(inspector) = self.inspector.as_mut() { - if let Some((result, address)) = inspector.create(&mut self.context, &mut inputs) { + if let Some((result, address)) = inspector.create(&mut self.context.evm, &mut inputs) { curent_stack_frame .interpreter .insert_create_output(result, address); @@ -301,13 +341,13 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { } } - match self.context.make_create_frame::(&inputs) { + match self.context.evm.make_create_frame::(&inputs) { FrameOrResult::Frame(new_frame) => Some(new_frame), FrameOrResult::Result(mut result) => { let mut address = None; if let Some(inspector) = self.inspector.as_mut() { let ret = inspector.create_end( - &mut self.context, + &mut self.context.evm, result, curent_stack_frame.created_address, ); @@ -335,7 +375,7 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { ) -> Option> { // Call inspector if it is some. if let Some(inspector) = self.inspector.as_mut() { - if let Some((result, range)) = inspector.call(&mut self.context, &mut inputs) { + if let Some((result, range)) = inspector.call(&mut self.context.evm, &mut inputs) { curent_stake_frame .interpreter .insert_call_output(shared_memory, result, range); @@ -344,13 +384,14 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { } match self .context + .evm .make_call_frame(&inputs, return_memory_offset.clone()) { FrameOrResult::Frame(new_frame) => Some(new_frame), FrameOrResult::Result(mut result) => { //println!("Result returned right away: {:#?}", result); if let Some(inspector) = self.inspector.as_mut() { - result = inspector.call_end(&mut self.context, result); + result = inspector.call_end(&mut self.context.evm, result); } curent_stake_frame.interpreter.insert_call_output( shared_memory, @@ -385,11 +426,13 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { let tx_caller = env.tx.caller; let (caller_account, _) = self .context + .evm .journaled_state - .load_account(tx_caller, self.context.db) + .load_account(tx_caller, self.context.evm.db) .map_err(EVMError::Database)?; self.context + .evm .env .validate_tx_against_state(caller_account) .map_err(Into::into) @@ -397,7 +440,7 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { /// Transact preverified transaction. pub fn transact_preverified_inner(&mut self) -> EVMResult { - let env = &self.context.env; + let env = &self.context.evm.env; let tx_caller = env.tx.caller; let tx_value = env.tx.value; let tx_data = env.tx.data.clone(); @@ -406,8 +449,8 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { // the L1-cost fee is only computed for Optimism non-deposit transactions. #[cfg(feature = "optimism")] let tx_l1_cost = if env.cfg.optimism && env.tx.optimism.source_hash.is_none() { - let l1_block_info = - optimism::L1BlockInfo::try_fetch(self.context.db).map_err(EVMError::Database)?; + let l1_block_info = optimism::L1BlockInfo::try_fetch(self.context.evm.db) + .map_err(EVMError::Database)?; let Some(enveloped_tx) = &env.tx.optimism.enveloped_tx else { panic!("[OPTIMISM] Failed to load enveloped transaction."); @@ -415,7 +458,7 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { let tx_l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx); // storage l1 block info for later use. - self.context.l1_block_info = Some(l1_block_info); + self.context.evm.l1_block_info = Some(l1_block_info); tx_l1_cost } else { @@ -432,47 +475,57 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm if SPEC::enabled(SHANGHAI) { self.context + .evm .journaled_state - .initial_account_load(self.context.env.block.coinbase, &[], self.context.db) + .initial_account_load( + self.context.evm.env.block.coinbase, + &[], + self.context.evm.db, + ) .map_err(EVMError::Database)?; } - self.context.load_access_list()?; + self.context.evm.load_access_list()?; // load acc - let journal = &mut self.context.journaled_state; + let journal = &mut self.context.evm.journaled_state; #[cfg(feature = "optimism")] - if self.context.env.cfg.optimism { + if self.context.evm.env.cfg.optimism { Evm::::commit_mint_value( tx_caller, - self.context.env.tx.optimism.mint, - self.context.db, + self.context.evm.env.tx.optimism.mint, + self.context.evm.db, journal, )?; - let is_deposit = self.context.env.tx.optimism.source_hash.is_some(); + let is_deposit = self.context.evm.env.tx.optimism.source_hash.is_some(); Evm::::remove_l1_cost( is_deposit, tx_caller, tx_l1_cost, - self.context.db, + self.context.evm.db, journal, )?; } let (caller_account, _) = journal - .load_account(tx_caller, self.context.db) + .load_account(tx_caller, self.context.evm.db) .map_err(EVMError::Database)?; // Subtract gas costs from the caller's account. // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. let mut gas_cost = - U256::from(tx_gas_limit).saturating_mul(self.context.env.effective_gas_price()); + U256::from(tx_gas_limit).saturating_mul(self.context.evm.env.effective_gas_price()); // EIP-4844 if SPEC::enabled(CANCUN) { - let data_fee = self.context.env.calc_data_fee().expect("already checked"); + let data_fee = self + .context + .evm + .env + .calc_data_fee() + .expect("already checked"); gas_cost = gas_cost.saturating_add(data_fee); } @@ -484,12 +537,12 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { let transact_gas_limit = tx_gas_limit - initial_gas_spend; // call inner handling of call/create - let first_stack_frame = match self.context.env.tx.transact_to { + let first_stack_frame = match self.context.evm.env.tx.transact_to { TransactTo::Call(address) => { // Nonce is already checked caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); - self.context.make_call_frame( + self.context.evm.make_call_frame( &CallInputs { contract: address, transfer: Transfer { @@ -511,13 +564,15 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { 0..0, ) } - TransactTo::Create(scheme) => self.context.make_create_frame::(&CreateInputs { - caller: tx_caller, - scheme, - value: tx_value, - init_code: tx_data, - gas_limit: transact_gas_limit, - }), + TransactTo::Create(scheme) => { + self.context.evm.make_create_frame::(&CreateInputs { + caller: tx_caller, + scheme, + value: tx_value, + init_code: tx_data, + gas_limit: transact_gas_limit, + }) + } }; // Some only if it is create. let mut created_address = None; @@ -536,29 +591,32 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> { }; let handler = &self.handler; - let data = &mut self.context; + let context = &mut self.context; // handle output of call/create calls. - let mut gas = - handler.call_return(data.env, interpreter_result.result, interpreter_result.gas); + let mut gas = handler.call_return( + context.evm.env, + interpreter_result.result, + interpreter_result.gas, + ); // set refund. Refund amount depends on hardfork. - gas.set_refund(handler.calculate_gas_refund(data.env, &gas) as i64); + gas.set_refund(handler.calculate_gas_refund(context.evm.env, &gas) as i64); // Reimburse the caller - handler.reimburse_caller(data, &gas)?; + handler.reimburse_caller(context, &gas)?; // Reward beneficiary - handler.reward_beneficiary(data, &gas)?; + handler.reward_beneficiary(context, &gas)?; // output of execution - let output = match data.env.tx.transact_to { + let output = match context.evm.env.tx.transact_to { TransactTo::Call(_) => Output::Call(interpreter_result.output), TransactTo::Create(_) => Output::Create(interpreter_result.output, created_address), }; // main return - handler.main_return(data, interpreter_result.result, output, &gas) + handler.main_return(context, interpreter_result.result, output, &gas) } } @@ -575,7 +633,9 @@ pub trait Transact { fn transact(&mut self) -> EVMResult; } -impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Transact for Evm<'a, SPEC, EXT, DB> { +impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler+'a, DB: Database> Transact + for Evm<'a, SPEC, EXT, DB> +{ #[inline] fn preverify_transaction(&mut self) -> Result<(), EVMError> { self.preverify_transaction_inner() @@ -598,32 +658,32 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Transact for Evm<'a impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Host for Evm<'a, SPEC, EXT, DB> { fn env(&mut self) -> &mut Env { - self.context.env() + self.context.evm.env() } fn block_hash(&mut self, number: U256) -> Option { - self.context.block_hash(number) + self.context.evm.block_hash(number) } fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { - self.context.load_account(address) + self.context.evm.load_account(address) } fn balance(&mut self, address: Address) -> Option<(U256, bool)> { - self.context.balance(address) + self.context.evm.balance(address) } fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { - self.context.code(address) + self.context.evm.code(address) } /// Get code hash of address. fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { - self.context.code_hash(address) + self.context.evm.code_hash(address) } fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { - self.context.sload(address, index) + self.context.evm.sload(address, index) } fn sstore( @@ -632,48 +692,55 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Host for Evm<'a, SPEC, EXT, DB index: U256, value: U256, ) -> Option<(U256, U256, U256, bool)> { - self.context.sstore(address, index, value) + self.context.evm.sstore(address, index, value) } fn tload(&mut self, address: Address, index: U256) -> U256 { - self.context.tload(address, index) + self.context.evm.tload(address, index) } fn tstore(&mut self, address: Address, index: U256, value: U256) { - self.context.tstore(address, index, value) + self.context.evm.tstore(address, index, value) } fn log(&mut self, address: Address, topics: Vec, data: Bytes) { if let Some(inspector) = self.inspector.as_mut() { - inspector.log(&mut self.context, &address, &topics, &data); + inspector.log(&mut self.context.evm, &address, &topics, &data); } let log = Log { address, topics, data, }; - self.context.journaled_state.log(log); + self.context.evm.journaled_state.log(log); } fn selfdestruct(&mut self, address: Address, target: Address) -> Option { if let Some(inspector) = self.inspector.as_mut() { - let acc = self.context.journaled_state.state.get(&address).unwrap(); + let acc = self + .context + .evm + .journaled_state + .state + .get(&address) + .unwrap(); inspector.selfdestruct(address, target, acc.info.balance); } self.context + .evm .journaled_state - .selfdestruct(address, target, self.context.db) - .map_err(|e| self.context.error = Some(e)) + .selfdestruct(address, target, self.context.evm.db) + .map_err(|e| self.context.evm.error = Some(e)) .ok() } } /// Creates new EVM instance with erased types. -pub fn new_evm<'a, EXT, DB: Database>( +pub fn new_evm<'a, EXT: RegisterHandler+'a, DB: Database>( env: &'a mut Env, db: &'a mut DB, external: EXT, - insp: Option<&'a mut dyn Inspector>, + insp: Option<&'a mut dyn Inspector>, ) -> Box + 'a> { macro_rules! create_evm { ($spec:ident) => { diff --git a/crates/revm/src/evm_factory.rs b/crates/revm/src/evm_factory.rs index 37685f7ba1..c000df6220 100644 --- a/crates/revm/src/evm_factory.rs +++ b/crates/revm/src/evm_factory.rs @@ -1,6 +1,6 @@ use crate::{ db::{Database, DatabaseCommit, DatabaseRef}, - evm::{new_evm, Transact}, + evm::{new_evm, MainnetHandle, Transact}, primitives::{db::WrapDatabaseRef, EVMError, EVMResult, Env, ExecutionResult, ResultAndState}, Inspector, }; @@ -58,7 +58,7 @@ impl EvmFactory { } /// Inspect transaction and commit changes to database. - pub fn inspect_commit>( + pub fn inspect_commit>( &mut self, inspector: INSP, ) -> Result> { @@ -72,7 +72,8 @@ impl EvmFactory { /// Do checks that could make transaction fail before call/create pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { if let Some(db) = self.db.as_mut() { - new_evm::<_, DB>(&mut self.env, db, (), None).preverify_transaction() + new_evm::(&mut self.env, db, MainnetHandle, None) + .preverify_transaction() } else { panic!("Database needs to be set"); } @@ -82,7 +83,8 @@ impl EvmFactory { /// state. pub fn transact_preverified(&mut self) -> EVMResult { if let Some(db) = self.db.as_mut() { - new_evm::<(), DB>(&mut self.env, db, (), None).transact_preverified() + new_evm::(&mut self.env, db, MainnetHandle, None) + .transact_preverified() } else { panic!("Database needs to be set"); } @@ -91,19 +93,17 @@ impl EvmFactory { /// Execute transaction without writing to DB, return change state. pub fn transact(&mut self) -> EVMResult { if let Some(db) = self.db.as_mut() { - new_evm::<_, DB>(&mut self.env, db, (), None).transact() + new_evm::(&mut self.env, db, MainnetHandle, None).transact() } else { panic!("Database needs to be set"); } } /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect>( - &mut self, - mut inspector: INSP, - ) -> EVMResult { + pub fn inspect>(&mut self, mut inspector: INSP) -> EVMResult { if let Some(db) = self.db.as_mut() { - new_evm::<_, DB>(&mut self.env, db, (), Some(&mut inspector)).transact() + new_evm::(&mut self.env, db, MainnetHandle, Some(&mut inspector)) + .transact() } else { panic!("Database needs to be set"); } @@ -114,8 +114,13 @@ impl<'a, DB: DatabaseRef> EvmFactory { /// Do checks that could make transaction fail before call/create pub fn preverify_transaction_ref(&self) -> Result<(), EVMError> { if let Some(db) = self.db.as_ref() { - new_evm::<_, _>(&mut self.env.clone(), &mut WrapDatabaseRef(db), (), None) - .preverify_transaction() + new_evm::( + &mut self.env.clone(), + &mut WrapDatabaseRef(db), + MainnetHandle, + None, + ) + .preverify_transaction() } else { panic!("Database needs to be set"); } @@ -125,8 +130,13 @@ impl<'a, DB: DatabaseRef> EvmFactory { /// without writing to DB, return change state. pub fn transact_preverified_ref(&self) -> EVMResult { if let Some(db) = self.db.as_ref() { - new_evm::<_, _>(&mut self.env.clone(), &mut WrapDatabaseRef(db), (), None) - .transact_preverified() + new_evm::( + &mut self.env.clone(), + &mut WrapDatabaseRef(db), + MainnetHandle, + None, + ) + .transact_preverified() } else { panic!("Database needs to be set"); } @@ -135,22 +145,28 @@ impl<'a, DB: DatabaseRef> EvmFactory { /// Execute transaction without writing to DB, return change state. pub fn transact_ref(&self) -> EVMResult { if let Some(db) = self.db.as_ref() { - new_evm::<_, _>(&mut self.env.clone(), &mut WrapDatabaseRef(db), (), None).transact() + new_evm::( + &mut self.env.clone(), + &mut WrapDatabaseRef(db), + MainnetHandle, + None, + ) + .transact() } else { panic!("Database needs to be set"); } } /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect_ref>>( + pub fn inspect_ref>>( &'a self, mut inspector: I, ) -> EVMResult { if let Some(db) = self.db.as_ref() { - new_evm( + new_evm::( &mut self.env.clone(), &mut WrapDatabaseRef(db), - (), + MainnetHandle, Some(&mut inspector), ) .transact() diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 453dc5d69a..222e10fc98 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -9,7 +9,7 @@ use revm_interpreter::{CallInputs, CreateInputs, SharedMemory}; use crate::{ interpreter::{Gas, InstructionResult}, primitives::{db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec}, - CallStackFrame, Evm, EvmContext, + CallStackFrame, Context, Evm, }; /// Handle call return and return final gas value. @@ -17,7 +17,7 @@ type CallReturnHandle = fn(&Env, InstructionResult, Gas) -> Gas; /// Reimburse the caller with ethereum it didn't spent. type ReimburseCallerHandle = - fn(&mut EvmContext<'_, EXT, DB>, &Gas) -> EVMResultGeneric<(), ::Error>; + fn(&mut Context<'_, EXT, DB>, &Gas) -> EVMResultGeneric<(), ::Error>; /// Reward beneficiary with transaction rewards. type RewardBeneficiaryHandle = ReimburseCallerHandle; @@ -27,7 +27,7 @@ type CalculateGasRefundHandle = fn(&Env, &Gas) -> u64; /// Main return handle, takes state from journal and transforms internal result to external. type MainReturnHandle = fn( - &mut EvmContext<'_, EXT, DB>, + &mut Context<'_, EXT, DB>, InstructionResult, Output, &Gas, @@ -38,7 +38,7 @@ type MainReturnHandle = fn( /// /// It is useful for catching errors and returning them in a different way. type EndHandle = fn( - &mut EvmContext<'_, EXT, DB>, + &mut Context<'_, EXT, DB>, evm_output: Result::Error>>, ) -> Result::Error>>; @@ -117,7 +117,7 @@ impl Handler { /// Reimburse the caller with gas that were not spend. pub fn reimburse_caller( &self, - context: &mut EvmContext<'_, EXT, DB>, + context: &mut Context<'_, EXT, DB>, gas: &Gas, ) -> Result<(), EVMError> { (self.reimburse_caller)(context, gas) @@ -131,7 +131,7 @@ impl Handler { /// Reward beneficiary pub fn reward_beneficiary( &self, - context: &mut EvmContext<'_, EXT, DB>, + context: &mut Context<'_, EXT, DB>, gas: &Gas, ) -> Result<(), EVMError> { (self.reward_beneficiary)(context, gas) @@ -140,7 +140,7 @@ impl Handler { /// Main return. pub fn main_return( &self, - context: &mut EvmContext<'_, EXT, DB>, + context: &mut Context<'_, EXT, DB>, call_result: InstructionResult, output: Output, gas: &Gas, @@ -151,7 +151,7 @@ impl Handler { /// End handler. pub fn end( &self, - context: &mut EvmContext<'_, EXT, DB>, + context: &mut Context<'_, EXT, DB>, end_output: Result>, ) -> Result> { (self.end)(context, end_output) diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index 120570f22c..3e6c447ed0 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -9,7 +9,7 @@ use crate::{ db::Database, EVMError, Env, ExecutionResult, Output, ResultAndState, Spec, SpecId::LONDON, U256, }, - EvmContext, + Context, EvmContext, }; /// Handle output of the transaction @@ -39,16 +39,17 @@ pub fn handle_call_return( #[inline] pub fn handle_reimburse_caller( - context: &mut EvmContext<'_, EXT, DB>, + context: &mut Context<'_, EXT, DB>, gas: &Gas, ) -> Result<(), EVMError> { - let caller = context.env.tx.caller; - let effective_gas_price = context.env.effective_gas_price(); + let caller = context.evm.env.tx.caller; + let effective_gas_price = context.evm.env.effective_gas_price(); // return balance of not spend gas. let (caller_account, _) = context + .evm .journaled_state - .load_account(caller, context.db) + .load_account(caller, context.evm.db) .map_err(EVMError::Database)?; caller_account.info.balance = caller_account @@ -62,23 +63,24 @@ pub fn handle_reimburse_caller( /// Reward beneficiary with gas fee. #[inline] pub fn reward_beneficiary( - context: &mut EvmContext<'_, EXT, DB>, + context: &mut Context<'_, EXT, DB>, gas: &Gas, ) -> Result<(), EVMError> { - let beneficiary = context.env.block.coinbase; - let effective_gas_price = context.env.effective_gas_price(); + let beneficiary = context.evm.env.block.coinbase; + let effective_gas_price = context.evm.env.effective_gas_price(); // transfer fee to coinbase/beneficiary. // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded. let coinbase_gas_price = if SPEC::enabled(LONDON) { - effective_gas_price.saturating_sub(context.env.block.basefee) + effective_gas_price.saturating_sub(context.evm.env.block.basefee) } else { effective_gas_price }; let (coinbase_account, _) = context + .evm .journaled_state - .load_account(beneficiary, context.db) + .load_account(beneficiary, context.evm.db) .map_err(EVMError::Database)?; coinbase_account.mark_touch(); diff --git a/crates/revm/src/handler/mainnet/main.rs b/crates/revm/src/handler/mainnet/main.rs index e52c735bc2..4f99d0bd9a 100644 --- a/crates/revm/src/handler/mainnet/main.rs +++ b/crates/revm/src/handler/mainnet/main.rs @@ -8,13 +8,13 @@ use crate::{ db::Database, EVMError, Env, ExecutionResult, Output, ResultAndState, Spec, SpecId::LONDON, U256, }, - EvmContext, + Context, EvmContext, }; /// Main return handle, returns the output of the transaction. #[inline] pub fn main_return( - context: &mut EvmContext<'_, EXT, DB>, + context: &mut Context<'_, EXT, DB>, call_result: InstructionResult, output: Output, gas: &Gas, @@ -24,7 +24,7 @@ pub fn main_return( let final_gas_used = gas.spend() - gas_refunded; // reset journal and return present state. - let (state, logs) = context.journaled_state.finalize(); + let (state, logs) = context.evm.journaled_state.finalize(); let result = match call_result.into() { SuccessOrHalt::Success(reason) => ExecutionResult::Success { @@ -46,7 +46,7 @@ pub fn main_return( gas_used: final_gas_used, }, SuccessOrHalt::FatalExternalError => { - return Err(EVMError::Database(context.error.take().unwrap())); + return Err(EVMError::Database(context.evm.error.take().unwrap())); } // Only two internal return flags. SuccessOrHalt::InternalContinue | SuccessOrHalt::InternalCallOrCreate => { @@ -60,7 +60,7 @@ pub fn main_return( /// Mainnet end handle does not change the output. #[inline] pub fn end_handle( - _context: &mut EvmContext<'_, EXT, DB>, + _context: &mut Context<'_, EXT, DB>, evm_output: Result>, ) -> Result> { evm_output diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index 5007dce23c..ea08ccb1ae 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -29,7 +29,7 @@ pub mod inspectors { /// EVM [Interpreter] callbacks. #[auto_impl(&mut, Box)] -pub trait Inspector { +pub trait Inspector { /// Called before the interpreter is initialized. /// /// If `interp.instruction_result` is set to anything other than [InstructionResult::Continue] then the execution of the interpreter @@ -38,7 +38,7 @@ pub trait Inspector { fn initialize_interp( &mut self, interp: &mut Interpreter, - context: &mut EvmContext<'_, EXT, DB>, + context: &mut EvmContext<'_, DB>, ) { let _ = interp; let _ = context; @@ -53,7 +53,7 @@ pub trait Inspector { /// /// To get the current opcode, use `interp.current_opcode()`. #[inline] - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, EXT, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { let _ = interp; let _ = context; } @@ -62,7 +62,7 @@ pub trait Inspector { #[inline] fn log( &mut self, - context: &mut EvmContext<'_, EXT, DB>, + context: &mut EvmContext<'_, DB>, address: &Address, topics: &[B256], data: &Bytes, @@ -78,7 +78,7 @@ pub trait Inspector { /// Setting `interp.instruction_result` to anything other than [InstructionResult::Continue] alters the execution /// of the interpreter. #[inline] - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, EXT, DB>) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { let _ = interp; let _ = context; } @@ -89,7 +89,7 @@ pub trait Inspector { #[inline] fn call( &mut self, - context: &mut EvmContext<'_, EXT, DB>, + context: &mut EvmContext<'_, DB>, inputs: &mut CallInputs, ) -> Option<(InterpreterResult, Range)> { let _ = context; @@ -104,7 +104,7 @@ pub trait Inspector { #[inline] fn call_end( &mut self, - context: &mut EvmContext<'_, EXT, DB>, + context: &mut EvmContext<'_, DB>, result: InterpreterResult, ) -> InterpreterResult { let _ = context; @@ -117,7 +117,7 @@ pub trait Inspector { #[inline] fn create( &mut self, - context: &mut EvmContext<'_, EXT, DB>, + context: &mut EvmContext<'_, DB>, inputs: &mut CreateInputs, ) -> Option<(InterpreterResult, Option
)> { let _ = context; @@ -132,7 +132,7 @@ pub trait Inspector { #[inline] fn create_end( &mut self, - context: &mut EvmContext<'_, EXT, DB>, + context: &mut EvmContext<'_, DB>, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 42df1e4018..1344ce573e 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -28,7 +28,7 @@ impl Inspector for GasInspector { fn initialize_interp( &mut self, interp: &mut crate::interpreter::Interpreter, - _context: &mut EvmContext<'_, EXT, DB>, + _context: &mut EvmContext<'_, DB>, ) { self.gas_remaining = interp.gas.limit(); } @@ -36,7 +36,7 @@ impl Inspector for GasInspector { fn step_end( &mut self, interp: &mut crate::interpreter::Interpreter, - _context: &mut EvmContext<'_, EXT, DB>, + _context: &mut EvmContext<'_, DB>, ) { let last_gas = core::mem::replace(&mut self.gas_remaining, interp.gas.remaining()); self.last_gas_cost = last_gas.saturating_sub(self.last_gas_cost); @@ -44,7 +44,7 @@ impl Inspector for GasInspector { fn call_end( &mut self, - _context: &mut EvmContext<'_, EXT, DB>, + _context: &mut EvmContext<'_, DB>, mut result: InterpreterResult, ) -> InterpreterResult { if result.result.is_error() { @@ -56,7 +56,7 @@ impl Inspector for GasInspector { fn create_end( &mut self, - _context: &mut EvmContext<'_, EXT, DB>, + _context: &mut EvmContext<'_, DB>, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { diff --git a/crates/revm/src/inspector/instruction.rs b/crates/revm/src/inspector/instruction.rs index 495f54156a..7a14bad873 100644 --- a/crates/revm/src/inspector/instruction.rs +++ b/crates/revm/src/inspector/instruction.rs @@ -7,7 +7,7 @@ use revm_interpreter::{ }; /// Outer closure that calls Inspector for every instruction. -pub fn inspector_instruction<'a, SPEC: Spec + 'static, EXT, DB: Database>( +pub fn inspector_instruction<'a, SPEC: Spec + 'static, EXT: 'a, DB: Database>( instruction: Instruction>, ) -> BoxedInstruction<'a, Evm<'a, SPEC, EXT, DB>> { Box::new( @@ -17,7 +17,7 @@ pub fn inspector_instruction<'a, SPEC: Spec + 'static, EXT, DB: Database>( // old Inspector behavior. interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.sub(1) }; - inspector.step(interpreter, &mut host.context); + inspector.step(interpreter, &mut host.context.evm); if interpreter.instruction_result != InstructionResult::Continue { return; } @@ -30,7 +30,7 @@ pub fn inspector_instruction<'a, SPEC: Spec + 'static, EXT, DB: Database>( // step ends if let Some(inspector) = host.inspector.as_mut() { - inspector.step_end(interpreter, &mut host.context); + inspector.step_end(interpreter, &mut host.context.evm); } }, ) diff --git a/crates/revm/src/inspector/noop.rs b/crates/revm/src/inspector/noop.rs index 166208352d..05d7292fc4 100644 --- a/crates/revm/src/inspector/noop.rs +++ b/crates/revm/src/inspector/noop.rs @@ -4,4 +4,4 @@ use crate::{Database, Inspector}; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct NoOpInspector; -impl Inspector for NoOpInspector {} +impl Inspector for NoOpInspector {} diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index afcd1518f4..0d4f5604b8 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -12,7 +12,7 @@ extern crate alloc; pub mod db; mod evm_factory; -mod evm_context; +mod context; mod evm; mod frame; pub mod handler; @@ -29,7 +29,7 @@ pub use db::{ }; pub use db::{Database, DatabaseCommit, DatabaseRef, InMemoryDB}; pub use evm_factory::{new, EvmFactory}; -pub use evm_context::EvmContext; +pub use context::{EvmContext,Context}; pub use evm::{new_evm, Evm, Transact, CALL_STACK_LIMIT}; pub use frame::{CallStackFrame,FrameOrResult}; pub use journaled_state::{JournalCheckpoint, JournalEntry, JournaledState}; From fda7f8f38c19bde07a0d9af89b267ae702f50550 Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 16 Nov 2023 16:35:16 +0100 Subject: [PATCH 04/52] continue previous commit --- crates/revm/src/evm.rs | 48 ++++-------------- crates/revm/src/evm_factory.rs | 46 ++++++++++++----- crates/revm/src/handler.rs | 74 +++++++++++++++------------ crates/revm/src/handler/register.rs | 77 +++++++++++++++++++++++++++++ 4 files changed, 163 insertions(+), 82 deletions(-) create mode 100644 crates/revm/src/handler/register.rs diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 777ccd089e..e2bdc46c7d 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,6 +1,7 @@ use crate::{ db::Database, handler::Handler, + handler::RegisterHandler, inspector_instruction, interpreter::{ gas::initial_tx_gas, @@ -26,43 +27,11 @@ use crate::optimism; /// EVM call stack limit. pub const CALL_STACK_LIMIT: u64 = 1024; -/// Register external handles. -pub trait RegisterHandler { - /// Register external handler. - fn register_handler(&self, handler: Handler) -> Handler - where - Self: Sized, - { - handler - } -} - -/// Default registered handler that produces default mainnet handler. -pub struct MainnetHandle; - -impl RegisterHandler for MainnetHandle {} - -pub struct ExternalData { - pub flagg: bool, -} - -impl RegisterHandler for ExternalData { - fn register_handler(&self, mut handler: Handler) -> Handler { - let t = handler.reimburse_caller.clone(); - handler.reimburse_caller = |data, gas| { - println!("Reimburse caller: {:#?} {:#?}", data.external.flagg, gas); - Ok(()) - //t(data, gas) - }; - handler - } -} - pub struct Evm<'a, SPEC: Spec, EXT, DB: Database> { pub context: Context<'a, EXT, DB>, pub inspector: Option<&'a mut dyn Inspector>, pub instruction_table: InstructionTables<'a, Self>, - pub handler: Handler, + pub handler: Handler<'a, EXT, DB>, _phantomdata: PhantomData, } @@ -137,7 +106,10 @@ impl<'a, SPEC: Spec, DB: Database> Evm<'a, SPEC, DB> { } } -impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler+'a, DB: Database> Evm<'a, SPEC, EXT, DB> { +impl<'a, SPEC: Spec + 'static, EXT: 'a, DB: Database> Evm<'a, SPEC, EXT, DB> +where + EXT: RegisterHandler, +{ pub fn new_with_spec( db: &'a mut DB, env: &'a mut Env, @@ -164,7 +136,7 @@ impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler+'a, DB: Database> Evm<'a, SP InstructionTables::Plain(Arc::new(make_instruction_table::())) }; - let mut handler = external.register_handler(Handler::mainnet::()); + let mut handler = external.register_handler::(Handler::mainnet::()); /* TODO support #[cfg(feature = "optimism")] let mut handler = if env.cfg.optimism { @@ -178,7 +150,7 @@ impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler+'a, DB: Database> Evm<'a, SP if env.cfg.is_beneficiary_reward_disabled() { // do nothing - handler.reward_beneficiary = |_, _| Ok(()); + handler.reward_beneficiary = Arc::new(|_, _| Ok(())); } Self { @@ -633,7 +605,7 @@ pub trait Transact { fn transact(&mut self) -> EVMResult; } -impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler+'a, DB: Database> Transact +impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler+'a, DB: Database> Transact for Evm<'a, SPEC, EXT, DB> { #[inline] @@ -736,7 +708,7 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Host for Evm<'a, SPEC, EXT, DB } /// Creates new EVM instance with erased types. -pub fn new_evm<'a, EXT: RegisterHandler+'a, DB: Database>( +pub fn new_evm<'a, EXT: RegisterHandler+'a, DB: Database>( env: &'a mut Env, db: &'a mut DB, external: EXT, diff --git a/crates/revm/src/evm_factory.rs b/crates/revm/src/evm_factory.rs index c000df6220..9d2f5d3535 100644 --- a/crates/revm/src/evm_factory.rs +++ b/crates/revm/src/evm_factory.rs @@ -1,6 +1,7 @@ use crate::{ db::{Database, DatabaseCommit, DatabaseRef}, - evm::{new_evm, MainnetHandle, Transact}, + evm::{new_evm, Transact}, + handler::MainnetHandle, primitives::{db::WrapDatabaseRef, EVMError, EVMResult, Env, ExecutionResult, ResultAndState}, Inspector, }; @@ -72,8 +73,13 @@ impl EvmFactory { /// Do checks that could make transaction fail before call/create pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, MainnetHandle, None) - .preverify_transaction() + new_evm::( + &mut self.env, + db, + MainnetHandle::default(), + None, + ) + .preverify_transaction() } else { panic!("Database needs to be set"); } @@ -83,8 +89,13 @@ impl EvmFactory { /// state. pub fn transact_preverified(&mut self) -> EVMResult { if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, MainnetHandle, None) - .transact_preverified() + new_evm::( + &mut self.env, + db, + MainnetHandle::default(), + None, + ) + .transact_preverified() } else { panic!("Database needs to be set"); } @@ -93,7 +104,13 @@ impl EvmFactory { /// Execute transaction without writing to DB, return change state. pub fn transact(&mut self) -> EVMResult { if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, MainnetHandle, None).transact() + new_evm::( + &mut self.env, + db, + MainnetHandle::default(), + None, + ) + .transact() } else { panic!("Database needs to be set"); } @@ -102,8 +119,13 @@ impl EvmFactory { /// Execute transaction with given inspector, without wring to DB. Return change state. pub fn inspect>(&mut self, mut inspector: INSP) -> EVMResult { if let Some(db) = self.db.as_mut() { - new_evm::(&mut self.env, db, MainnetHandle, Some(&mut inspector)) - .transact() + new_evm::( + &mut self.env, + db, + MainnetHandle::default(), + Some(&mut inspector), + ) + .transact() } else { panic!("Database needs to be set"); } @@ -117,7 +139,7 @@ impl<'a, DB: DatabaseRef> EvmFactory { new_evm::( &mut self.env.clone(), &mut WrapDatabaseRef(db), - MainnetHandle, + MainnetHandle::default(), None, ) .preverify_transaction() @@ -133,7 +155,7 @@ impl<'a, DB: DatabaseRef> EvmFactory { new_evm::( &mut self.env.clone(), &mut WrapDatabaseRef(db), - MainnetHandle, + MainnetHandle::default(), None, ) .transact_preverified() @@ -148,7 +170,7 @@ impl<'a, DB: DatabaseRef> EvmFactory { new_evm::( &mut self.env.clone(), &mut WrapDatabaseRef(db), - MainnetHandle, + MainnetHandle::default(), None, ) .transact() @@ -166,7 +188,7 @@ impl<'a, DB: DatabaseRef> EvmFactory { new_evm::( &mut self.env.clone(), &mut WrapDatabaseRef(db), - MainnetHandle, + MainnetHandle::default(), Some(&mut inspector), ) .transact() diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 222e10fc98..ede1f32248 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -2,45 +2,55 @@ pub mod mainnet; #[cfg(feature = "optimism")] pub mod optimism; -use core::ops::Range; +mod register; -use revm_interpreter::{CallInputs, CreateInputs, SharedMemory}; +pub use register::{ExternalData, MainnetHandle, RegisterHandler}; use crate::{ interpreter::{Gas, InstructionResult}, primitives::{db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec}, CallStackFrame, Context, Evm, }; +use alloc::sync::Arc; +use core::ops::Range; +use revm_interpreter::{CallInputs, CreateInputs, SharedMemory}; /// Handle call return and return final gas value. -type CallReturnHandle = fn(&Env, InstructionResult, Gas) -> Gas; +type CallReturnHandle<'a> = Arc Gas + 'a>; /// Reimburse the caller with ethereum it didn't spent. -type ReimburseCallerHandle = - fn(&mut Context<'_, EXT, DB>, &Gas) -> EVMResultGeneric<(), ::Error>; +type ReimburseCallerHandle<'a, EXT, DB> = Arc< + dyn Fn(&mut Context<'_, EXT, DB>, &Gas) -> EVMResultGeneric<(), ::Error> + 'a, +>; /// Reward beneficiary with transaction rewards. -type RewardBeneficiaryHandle = ReimburseCallerHandle; +type RewardBeneficiaryHandle<'a, EXT, DB> = ReimburseCallerHandle<'a, EXT, DB>; /// Calculate gas refund for transaction. -type CalculateGasRefundHandle = fn(&Env, &Gas) -> u64; +type CalculateGasRefundHandle<'a> = Arc u64 + 'a>; /// Main return handle, takes state from journal and transforms internal result to external. -type MainReturnHandle = fn( - &mut Context<'_, EXT, DB>, - InstructionResult, - Output, - &Gas, -) -> Result::Error>>; +type MainReturnHandle<'a, EXT, DB> = Arc< + dyn Fn( + &mut Context<'_, EXT, DB>, + InstructionResult, + Output, + &Gas, + ) -> Result::Error>> + + 'a, +>; /// End handle, takes result and state and returns final result. /// This will be called after all the other handlers. /// /// It is useful for catching errors and returning them in a different way. -type EndHandle = fn( - &mut Context<'_, EXT, DB>, - evm_output: Result::Error>>, -) -> Result::Error>>; +type EndHandle<'a, EXT, DB> = Arc< + dyn Fn( + &mut Context<'_, EXT, DB>, + Result::Error>>, + ) -> Result::Error>> + + 'a, +>; // Sub call // type SubCall = fn( @@ -61,35 +71,35 @@ type EndHandle = fn( /// Handler acts as a proxy and allow to define different behavior for different /// sections of the code. This allows nice integration of different chains or /// to disable some mainnet behavior. -pub struct Handler { +pub struct Handler<'a, EXT, DB: Database> { // Uses env, call result and returned gas from the call to determine the gas // that is returned from transaction execution.. - pub call_return: CallReturnHandle, + pub call_return: CallReturnHandle<'a>, /// Reimburse the caller with ethereum it didn't spent. - pub reimburse_caller: ReimburseCallerHandle, + pub reimburse_caller: ReimburseCallerHandle<'a, EXT, DB>, /// Reward the beneficiary with caller fee. - pub reward_beneficiary: RewardBeneficiaryHandle, + pub reward_beneficiary: RewardBeneficiaryHandle<'a, EXT, DB>, /// Calculate gas refund for transaction. /// Some chains have it disabled. - pub calculate_gas_refund: CalculateGasRefundHandle, + pub calculate_gas_refund: CalculateGasRefundHandle<'a>, /// Main return handle, returns the output of the transact. - pub main_return: MainReturnHandle, + pub main_return: MainReturnHandle<'a, EXT, DB>, /// End handle. - pub end: EndHandle, + pub end: EndHandle<'a, EXT, DB>, // Called on sub call. //pub sub_call: SubCall, } -impl Handler { +impl<'a, EXT: 'a, DB: Database + 'a> Handler<'a, EXT, DB> { /// Handler for the mainnet - pub fn mainnet() -> Self { + pub fn mainnet() -> Self { Self { - call_return: mainnet::handle_call_return::, - calculate_gas_refund: mainnet::calculate_gas_refund::, - reimburse_caller: mainnet::handle_reimburse_caller::, - reward_beneficiary: mainnet::reward_beneficiary::, - main_return: mainnet::main::main_return::, - end: mainnet::main::end_handle::, + call_return: Arc::new(mainnet::handle_call_return::), + calculate_gas_refund: Arc::new(mainnet::calculate_gas_refund::), + reimburse_caller: Arc::new(mainnet::handle_reimburse_caller::), + reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), + main_return: Arc::new(mainnet::main::main_return::), + end: Arc::new(mainnet::main::end_handle::), } } diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs new file mode 100644 index 0000000000..5442b0b1f8 --- /dev/null +++ b/crates/revm/src/handler/register.rs @@ -0,0 +1,77 @@ +use crate::{ + db::Database, + handler::Handler, + inspector_instruction, + interpreter::{ + gas::initial_tx_gas, + opcode::{make_boxed_instruction_table, make_instruction_table, InstructionTables}, + CallContext, CallInputs, CallScheme, CreateInputs, Host, Interpreter, InterpreterAction, + InterpreterResult, SelfDestructResult, SharedMemory, Transfer, + }, + journaled_state::JournaledState, + precompile::Precompiles, + primitives::{ + specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, + Output, Spec, SpecId::*, TransactTo, B256, U256, + }, + CallStackFrame, Context, EvmContext, FrameOrResult, Inspector, +}; +use alloc::{boxed::Box, sync::Arc, vec::Vec}; +use auto_impl::auto_impl; +use core::{fmt, marker::PhantomData, ops::Range}; + +/// Register external handles. +pub trait RegisterHandler { + /// Register external handler. + fn register_handler<'a, SPEC: Spec>( + &self, + handler: Handler<'a, Self, DB>, + ) -> Handler<'a, Self, DB> + where + Self: Sized, + { + handler + } +} + +/// Default registered handler that produces default mainnet handler. +#[derive(Default)] +pub struct MainnetHandle {} + +impl RegisterHandler for MainnetHandle {} + +pub struct InspectorHandle<'a, DB: Database, INS: Inspector> { + pub inspector: &'a mut INS, + pub _phantomdata: PhantomData<&'a DB>, +} + +impl<'a, DB: Database, INS: Inspector> RegisterHandler for InspectorHandle<'a, DB, INS> { + fn register_handler<'b, SPEC: Spec>( + &self, + handler: Handler<'b, Self, DB>, + ) -> Handler<'b, Self, DB> + where + Self: Sized, + { + handler + } +} + +pub struct ExternalData { + pub flagg: bool, + pub phantom: PhantomData, +} + +impl RegisterHandler for ExternalData { + fn register_handler<'a, SPEC: Spec>( + &self, + mut handler: Handler<'a, Self, DB>, + ) -> Handler<'a, Self, DB> { + let t = handler.reimburse_caller.clone(); + handler.reimburse_caller = Arc::new(|data, gas| { + println!("Reimburse caller: {:#?} {:#?}", data.external.flagg, gas); + crate::handler::mainnet::handle_reimburse_caller::(data, gas) + }); + handler + } +} From 99ec6734557d175d1cd3512ee8f8d9c2cb6b7921 Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 21 Nov 2023 15:36:02 +0100 Subject: [PATCH 05/52] wip inspector handle register --- crates/revm/src/evm.rs | 12 +--- crates/revm/src/handler.rs | 43 ++++++++++++- crates/revm/src/handler/mainnet.rs | 1 + crates/revm/src/handler/mainnet/frames.rs | 76 +++++++++++++++++++++++ crates/revm/src/handler/register.rs | 14 +++-- 5 files changed, 130 insertions(+), 16 deletions(-) create mode 100644 crates/revm/src/handler/mainnet/frames.rs diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index e2bdc46c7d..e11e24c027 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -136,18 +136,10 @@ where InstructionTables::Plain(Arc::new(make_instruction_table::())) }; + // temporary here. Factory should create handler and register external handles. let mut handler = external.register_handler::(Handler::mainnet::()); - /* TODO support - #[cfg(feature = "optimism")] - let mut handler = if env.cfg.optimism { - Handler::optimism::() - } else { - Handler::mainnet::() - }; - #[cfg(not(feature = "optimism"))] - let mut handler = Handler::mainnet::(); - */ + // temporary here. Factory should override this handle. if env.cfg.is_beneficiary_reward_disabled() { // do nothing handler.reward_beneficiary = Arc::new(|_, _| Ok(())); diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index ede1f32248..72549110b9 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -13,7 +13,7 @@ use crate::{ }; use alloc::sync::Arc; use core::ops::Range; -use revm_interpreter::{CallInputs, CreateInputs, SharedMemory}; +use revm_interpreter::{CallInputs, CreateInputs, InterpreterResult, SharedMemory}; /// Handle call return and return final gas value. type CallReturnHandle<'a> = Arc Gas + 'a>; @@ -40,6 +40,25 @@ type MainReturnHandle<'a, EXT, DB> = Arc< + 'a, >; +/// After subcall is finished, call this function to handle return result. +/// +/// Return Some if we want to halt execution. This can be done on any stack frame. +type FrameReturn<'a, EXT, DB> = Arc< + dyn Fn( + // context + &mut Context<'_, EXT, DB>, + // returned frame + Box, + // parent frame if it exist. + Option<&mut Box>, + // shared memory to insert output of the call. + &mut SharedMemory, + // output of frame execution. + InterpreterResult, + ) -> Option + + 'a, +>; + /// End handle, takes result and state and returns final result. /// This will be called after all the other handlers. /// @@ -88,6 +107,8 @@ pub struct Handler<'a, EXT, DB: Database> { pub end: EndHandle<'a, EXT, DB>, // Called on sub call. //pub sub_call: SubCall, + /// Frame return + pub frame_return: FrameReturn<'a, EXT, DB>, } impl<'a, EXT: 'a, DB: Database + 'a> Handler<'a, EXT, DB> { @@ -100,6 +121,7 @@ impl<'a, EXT: 'a, DB: Database + 'a> Handler<'a, EXT, DB> { reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), main_return: Arc::new(mainnet::main::main_return::), end: Arc::new(mainnet::main::end_handle::), + frame_return: Arc::new(mainnet::frames::handle_frame_return::), } } @@ -116,6 +138,7 @@ impl<'a, EXT: 'a, DB: Database + 'a> Handler<'a, EXT, DB> { // In case of halt of deposit transaction return Error. main_return: optimism::main_return::, end: optimism::end_handle::, + frame_return: Arc::new(mainnet::frames::handle_frame_return::), } } @@ -166,4 +189,22 @@ impl<'a, EXT: 'a, DB: Database + 'a> Handler<'a, EXT, DB> { ) -> Result> { (self.end)(context, end_output) } + + /// Frame return + pub fn frame_return( + &self, + context: &mut Context<'_, EXT, DB>, + child_stack_frame: Box, + parent_stack_frame: Option<&mut Box>, + shared_memory: &mut SharedMemory, + result: InterpreterResult, + ) -> Option { + (self.frame_return)( + context, + child_stack_frame, + parent_stack_frame, + shared_memory, + result, + ) + } } diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index 3e6c447ed0..debddc028d 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -2,6 +2,7 @@ pub mod call_loop; pub mod main; +pub mod frames; use crate::{ interpreter::{return_ok, return_revert, Gas, InstructionResult, SuccessOrHalt}, diff --git a/crates/revm/src/handler/mainnet/frames.rs b/crates/revm/src/handler/mainnet/frames.rs new file mode 100644 index 0000000000..71e4acfb36 --- /dev/null +++ b/crates/revm/src/handler/mainnet/frames.rs @@ -0,0 +1,76 @@ +use crate::{ + db::Database, + handler::Handler, + handler::RegisterHandler, + inspector_instruction, + interpreter::{ + gas::initial_tx_gas, + opcode::{make_boxed_instruction_table, make_instruction_table, InstructionTables}, + CallContext, CallInputs, CallScheme, CreateInputs, Host, Interpreter, InterpreterAction, + InterpreterResult, SelfDestructResult, SharedMemory, Transfer, + }, + journaled_state::JournaledState, + precompile::Precompiles, + primitives::{ + specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, + Output, Spec, SpecId::*, TransactTo, B256, U256, + }, + CallStackFrame, Context, EvmContext, FrameOrResult, Inspector, +}; +use alloc::{boxed::Box, sync::Arc, vec::Vec}; +use auto_impl::auto_impl; +use core::{fmt, marker::PhantomData, ops::Range}; + +pub fn handle_frame_return( + context: &mut Context<'_, EXT, DB>, + mut child_stack_frame: Box, + parent_stack_frame: Option<&mut Box>, + shared_memory: &mut SharedMemory, + mut result: InterpreterResult, +) -> Option { + // TODO + // if let Some(inspector) = self.inspector.as_mut() { + // result = if child_stack_frame.is_create { + // let (result, address) = inspector.create_end( + // &mut self.context.evm, + // result, + // child_stack_frame.created_address, + // ); + // child_stack_frame.created_address = address; + // result + // } else { + // inspector.call_end(&mut self.context.evm, result) + // }; + // } + + // break from loop if this is last CallStackFrame. + let Some(parent_stack_frame) = parent_stack_frame else { + let result = if child_stack_frame.is_create { + context + .evm + .create_return::(result, child_stack_frame) + .0 + } else { + context.evm.call_return(result, child_stack_frame) + }; + + return Some(result); + }; + + if child_stack_frame.is_create { + let (result, address) = context.evm.create_return::(result, child_stack_frame); + parent_stack_frame + .interpreter + .insert_create_output(result, Some(address)) + } else { + let subcall_memory_return_offset = child_stack_frame.subcall_return_memory_range.clone(); + let result = context.evm.call_return(result, child_stack_frame); + + parent_stack_frame.interpreter.insert_call_output( + shared_memory, + result, + subcall_memory_return_offset, + ) + } + None +} diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index 5442b0b1f8..84ebdbe653 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -29,6 +29,7 @@ pub trait RegisterHandler { ) -> Handler<'a, Self, DB> where Self: Sized, + DB: 'a, { handler } @@ -52,6 +53,7 @@ impl<'a, DB: Database, INS: Inspector> RegisterHandler for InspectorHand ) -> Handler<'b, Self, DB> where Self: Sized, + DB: 'b, { handler } @@ -66,11 +68,13 @@ impl RegisterHandler for ExternalData { fn register_handler<'a, SPEC: Spec>( &self, mut handler: Handler<'a, Self, DB>, - ) -> Handler<'a, Self, DB> { - let t = handler.reimburse_caller.clone(); - handler.reimburse_caller = Arc::new(|data, gas| { - println!("Reimburse caller: {:#?} {:#?}", data.external.flagg, gas); - crate::handler::mainnet::handle_reimburse_caller::(data, gas) + ) -> Handler<'a, Self, DB> + where + DB: 'a, + { + let old_handle = handler.reimburse_caller.clone(); + handler.reimburse_caller = Arc::new(move |data, gas| { + old_handle(data, gas) }); handler } From ee65aa5f4d73650ff69c7d51931fa2626805a8d0 Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 21 Nov 2023 18:57:18 +0100 Subject: [PATCH 06/52] add few more handlers for frame and host --- crates/revm/src/evm.rs | 191 +++------------------- crates/revm/src/handler.rs | 120 +++++++++++--- crates/revm/src/handler/mainnet.rs | 1 + crates/revm/src/handler/mainnet/frames.rs | 74 +++++++++ crates/revm/src/handler/mainnet/host.rs | 65 ++++++++ crates/revm/src/handler/register.rs | 33 +++- 6 files changed, 287 insertions(+), 197 deletions(-) create mode 100644 crates/revm/src/handler/mainnet/host.rs diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index e11e24c027..cc5236a26f 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -199,13 +199,17 @@ where InterpreterAction::SubCall { inputs, return_memory_offset, - } => self.handle_sub_call( + } => self.handler.frame_sub_call( + &mut self.context, inputs, stack_frame, - return_memory_offset, &mut shared_memory, + return_memory_offset, ), - InterpreterAction::Create { inputs } => self.handle_sub_create(inputs, stack_frame), + InterpreterAction::Create { inputs } => { + self.handler + .frame_sub_create(&mut self.context, stack_frame, inputs) + } InterpreterAction::Return { result } => { // free memory context. shared_memory.free_context(); @@ -213,9 +217,13 @@ where let child = call_stack.pop().unwrap(); let parent = call_stack.last_mut(); - if let Some(result) = - self.handle_frame_return(child, parent, &mut shared_memory, result) - { + if let Some(result) = self.handler.frame_return( + &mut self.context, + child, + parent, + &mut shared_memory, + result, + ) { return result; } stack_frame = call_stack.last_mut().unwrap(); @@ -230,143 +238,6 @@ where } } - fn handle_frame_return( - &mut self, - mut child_stack_frame: Box, - parent_stack_frame: Option<&mut Box>, - shared_memory: &mut SharedMemory, - mut result: InterpreterResult, - ) -> Option { - if let Some(inspector) = self.inspector.as_mut() { - result = if child_stack_frame.is_create { - let (result, address) = inspector.create_end( - &mut self.context.evm, - result, - child_stack_frame.created_address, - ); - child_stack_frame.created_address = address; - result - } else { - inspector.call_end(&mut self.context.evm, result) - }; - } - - // break from loop if this is last CallStackFrame. - let Some(parent_stack_frame) = parent_stack_frame else { - let result = if child_stack_frame.is_create { - self.context - .evm - .create_return::(result, child_stack_frame) - .0 - } else { - self.context.evm.call_return(result, child_stack_frame) - }; - - return Some(result); - }; - - if child_stack_frame.is_create { - let (result, address) = self - .context - .evm - .create_return::(result, child_stack_frame); - parent_stack_frame - .interpreter - .insert_create_output(result, Some(address)) - } else { - let subcall_memory_return_offset = - child_stack_frame.subcall_return_memory_range.clone(); - let result = self.context.evm.call_return(result, child_stack_frame); - - parent_stack_frame.interpreter.insert_call_output( - shared_memory, - result, - subcall_memory_return_offset, - ) - } - None - } - - /// Handle Action for new sub create call, return None if there is no need - /// to add new stack frame. - #[inline] - fn handle_sub_create( - &mut self, - mut inputs: Box, - curent_stack_frame: &mut CallStackFrame, - ) -> Option> { - // Call inspector if it is some. - if let Some(inspector) = self.inspector.as_mut() { - if let Some((result, address)) = inspector.create(&mut self.context.evm, &mut inputs) { - curent_stack_frame - .interpreter - .insert_create_output(result, address); - return None; - } - } - - match self.context.evm.make_create_frame::(&inputs) { - FrameOrResult::Frame(new_frame) => Some(new_frame), - FrameOrResult::Result(mut result) => { - let mut address = None; - if let Some(inspector) = self.inspector.as_mut() { - let ret = inspector.create_end( - &mut self.context.evm, - result, - curent_stack_frame.created_address, - ); - result = ret.0; - address = ret.1; - } - // insert result of the failed creation of create CallStackFrame. - curent_stack_frame - .interpreter - .insert_create_output(result, address); - None - } - } - } - - /// Handles action for new sub call, return None if there is no need to add - /// new stack frame. - #[inline] - fn handle_sub_call( - &mut self, - mut inputs: Box, - curent_stake_frame: &mut CallStackFrame, - return_memory_offset: Range, - shared_memory: &mut SharedMemory, - ) -> Option> { - // Call inspector if it is some. - if let Some(inspector) = self.inspector.as_mut() { - if let Some((result, range)) = inspector.call(&mut self.context.evm, &mut inputs) { - curent_stake_frame - .interpreter - .insert_call_output(shared_memory, result, range); - return None; - } - } - match self - .context - .evm - .make_call_frame(&inputs, return_memory_offset.clone()) - { - FrameOrResult::Frame(new_frame) => Some(new_frame), - FrameOrResult::Result(mut result) => { - //println!("Result returned right away: {:#?}", result); - if let Some(inspector) = self.inspector.as_mut() { - result = inspector.call_end(&mut self.context.evm, result); - } - curent_stake_frame.interpreter.insert_call_output( - shared_memory, - result, - return_memory_offset, - ); - None - } - } - } - /// Pre verify transaction. pub fn preverify_transaction_inner(&mut self) -> Result<(), EVMError> { let env = self.env(); @@ -597,7 +468,7 @@ pub trait Transact { fn transact(&mut self) -> EVMResult; } -impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler+'a, DB: Database> Transact +impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler + 'a, DB: Database> Transact for Evm<'a, SPEC, EXT, DB> { #[inline] @@ -641,7 +512,6 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Host for Evm<'a, SPEC, EXT, DB self.context.evm.code(address) } - /// Get code hash of address. fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { self.context.evm.code_hash(address) } @@ -668,39 +538,18 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Host for Evm<'a, SPEC, EXT, DB } fn log(&mut self, address: Address, topics: Vec, data: Bytes) { - if let Some(inspector) = self.inspector.as_mut() { - inspector.log(&mut self.context.evm, &address, &topics, &data); - } - let log = Log { - address, - topics, - data, - }; - self.context.evm.journaled_state.log(log); + self.handler + .host_log(&mut self.context, address, topics, data); } fn selfdestruct(&mut self, address: Address, target: Address) -> Option { - if let Some(inspector) = self.inspector.as_mut() { - let acc = self - .context - .evm - .journaled_state - .state - .get(&address) - .unwrap(); - inspector.selfdestruct(address, target, acc.info.balance); - } - self.context - .evm - .journaled_state - .selfdestruct(address, target, self.context.evm.db) - .map_err(|e| self.context.evm.error = Some(e)) - .ok() + self.handler + .host_selfdestruct(&mut self.context, address, target) } } /// Creates new EVM instance with erased types. -pub fn new_evm<'a, EXT: RegisterHandler+'a, DB: Database>( +pub fn new_evm<'a, EXT: RegisterHandler + 'a, DB: Database>( env: &'a mut Env, db: &'a mut DB, external: EXT, diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 72549110b9..bf4b746732 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -5,6 +5,7 @@ pub mod optimism; mod register; pub use register::{ExternalData, MainnetHandle, RegisterHandler}; +use revm_precompile::{Address, Bytes, B256}; use crate::{ interpreter::{Gas, InstructionResult}, @@ -13,7 +14,9 @@ use crate::{ }; use alloc::sync::Arc; use core::ops::Range; -use revm_interpreter::{CallInputs, CreateInputs, InterpreterResult, SharedMemory}; +use revm_interpreter::{ + CallInputs, CreateInputs, InterpreterResult, SelfDestructResult, SharedMemory, +}; /// Handle call return and return final gas value. type CallReturnHandle<'a> = Arc Gas + 'a>; @@ -43,7 +46,7 @@ type MainReturnHandle<'a, EXT, DB> = Arc< /// After subcall is finished, call this function to handle return result. /// /// Return Some if we want to halt execution. This can be done on any stack frame. -type FrameReturn<'a, EXT, DB> = Arc< +type FrameReturnHandle<'a, EXT, DB> = Arc< dyn Fn( // context &mut Context<'_, EXT, DB>, @@ -59,11 +62,22 @@ type FrameReturn<'a, EXT, DB> = Arc< + 'a, >; +/// Call to the host from Interpreter to save the log. +pub type HostLogHandle<'a, EXT, DB> = + Arc, Address, Vec, Bytes) + 'a>; + +/// Call to the host from Interpreter to selfdestruct account. +/// +/// After CANCUN hardfork original contract will stay the same but the value will +/// be transfered to the target. +pub type HostSelfdestruct<'a, EXT, DB> = + Arc, Address, Address) -> Option + 'a>; + /// End handle, takes result and state and returns final result. /// This will be called after all the other handlers. /// /// It is useful for catching errors and returning them in a different way. -type EndHandle<'a, EXT, DB> = Arc< +pub type EndHandle<'a, EXT, DB> = Arc< dyn Fn( &mut Context<'_, EXT, DB>, Result::Error>>, @@ -71,21 +85,27 @@ type EndHandle<'a, EXT, DB> = Arc< + 'a, >; -// Sub call -// type SubCall = fn( -// evm: &mut Evm<'_, SPEC, DB>, -// inputs: Box, -// curent_stake_frame: &mut CallStackFrame, -// shared_memory: &mut SharedMemory, -// return_memory_offset: Range, -// ) -> Option>; - -// /// sub create call -// type SubCreateCall = fn( -// evm: &mut Evm<'_, SPEC, DB>, -// curent_stack_frame: &mut CallStackFrame, -// inputs: Box, -// ) -> Option>; +/// Handle sub call. +type FrameSubCallHandle<'a, EXT, DB> = Arc< + dyn Fn( + &mut Context<'_, EXT, DB>, + Box, + &mut CallStackFrame, + &mut SharedMemory, + Range, + ) -> Option> + + 'a, +>; + +/// Handle sub create. +type FrameSubCreateHandle<'a, EXT, DB: Database> = Arc< + dyn Fn( + &mut Context<'_, EXT, DB>, + &mut CallStackFrame, + Box, + ) -> Option> + + 'a, +>; /// Handler acts as a proxy and allow to define different behavior for different /// sections of the code. This allows nice integration of different chains or @@ -108,7 +128,15 @@ pub struct Handler<'a, EXT, DB: Database> { // Called on sub call. //pub sub_call: SubCall, /// Frame return - pub frame_return: FrameReturn<'a, EXT, DB>, + pub frame_return: FrameReturnHandle<'a, EXT, DB>, + /// Frame sub call + pub frame_sub_call: FrameSubCallHandle<'a, EXT, DB>, + /// Frame sub crate + pub frame_sub_create: FrameSubCreateHandle<'a, EXT, DB>, + /// Host log handle. + pub host_log: HostLogHandle<'a, EXT, DB>, + /// Host selfdestruct handle. + pub host_selfdestruct: HostSelfdestruct<'a, EXT, DB>, } impl<'a, EXT: 'a, DB: Database + 'a> Handler<'a, EXT, DB> { @@ -122,6 +150,10 @@ impl<'a, EXT: 'a, DB: Database + 'a> Handler<'a, EXT, DB> { main_return: Arc::new(mainnet::main::main_return::), end: Arc::new(mainnet::main::end_handle::), frame_return: Arc::new(mainnet::frames::handle_frame_return::), + frame_sub_call: Arc::new(mainnet::frames::handle_frame_sub_call::), + frame_sub_create: Arc::new(mainnet::frames::handle_frame_sub_create::), + host_log: Arc::new(mainnet::host::handle_host_log::), + host_selfdestruct: Arc::new(mainnet::host::handle_selfdestruct::), } } @@ -139,6 +171,8 @@ impl<'a, EXT: 'a, DB: Database + 'a> Handler<'a, EXT, DB> { main_return: optimism::main_return::, end: optimism::end_handle::, frame_return: Arc::new(mainnet::frames::handle_frame_return::), + host_log: Arc::new(mainnet::host::handle_host_log::), + host_selfdestruct: Arc::new(mainnet::host::handle_selfdestruct::), } } @@ -190,6 +224,33 @@ impl<'a, EXT: 'a, DB: Database + 'a> Handler<'a, EXT, DB> { (self.end)(context, end_output) } + /// Call frame sub call handler. + pub fn frame_sub_call( + &self, + context: &mut Context<'_, EXT, DB>, + inputs: Box, + curent_stack_frame: &mut CallStackFrame, + shared_memory: &mut SharedMemory, + return_memory_offset: Range, + ) -> Option> { + (self.frame_sub_call)( + context, + inputs, + curent_stack_frame, + shared_memory, + return_memory_offset, + ) + } + + pub fn frame_sub_create( + &self, + context: &mut Context<'_, EXT, DB>, + curent_stack_frame: &mut CallStackFrame, + inputs: Box, + ) -> Option> { + (self.frame_sub_create)(context, curent_stack_frame, inputs) + } + /// Frame return pub fn frame_return( &self, @@ -207,4 +268,25 @@ impl<'a, EXT: 'a, DB: Database + 'a> Handler<'a, EXT, DB> { result, ) } + + /// Call host log handle. + pub fn host_log( + &self, + context: &mut Context<'_, EXT, DB>, + address: Address, + topics: Vec, + data: Bytes, + ) { + (self.host_log)(context, address, topics, data) + } + + /// Call host selfdestruct handle. + pub fn host_selfdestruct( + &self, + context: &mut Context<'_, EXT, DB>, + address: Address, + target: Address, + ) -> Option { + (self.host_selfdestruct)(context, address, target) + } } diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index debddc028d..3b29a86628 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -3,6 +3,7 @@ pub mod call_loop; pub mod main; pub mod frames; +pub mod host; use crate::{ interpreter::{return_ok, return_revert, Gas, InstructionResult, SuccessOrHalt}, diff --git a/crates/revm/src/handler/mainnet/frames.rs b/crates/revm/src/handler/mainnet/frames.rs index 71e4acfb36..75c808c7e0 100644 --- a/crates/revm/src/handler/mainnet/frames.rs +++ b/crates/revm/src/handler/mainnet/frames.rs @@ -74,3 +74,77 @@ pub fn handle_frame_return( } None } + +pub fn handle_frame_sub_call( + context: &mut Context<'_, EXT, DB>, + inputs: Box, + curent_stack_frame: &mut CallStackFrame, + shared_memory: &mut SharedMemory, + return_memory_offset: Range, +) -> Option> { + // TODO inspector handle + // if let Some(inspector) = self.inspector.as_mut() { + // if let Some((result, range)) = inspector.call(&mut self.context.evm, &mut inputs) { + // curent_stack_frame + // .interpreter + // .insert_call_output(shared_memory, result, range); + // return None; + // } + // } + match context + .evm + .make_call_frame(&inputs, return_memory_offset.clone()) + { + FrameOrResult::Frame(new_frame) => Some(new_frame), + FrameOrResult::Result(result) => { + // TODO handle inspector + // if let Some(inspector) = self.inspector.as_mut() { + // result = inspector.call_end(&mut self.context.evm, result); + // } + curent_stack_frame.interpreter.insert_call_output( + shared_memory, + result, + return_memory_offset, + ); + None + } + } +} + +pub fn handle_frame_sub_create( + context: &mut Context<'_, EXT, DB>, + curent_stack_frame: &mut CallStackFrame, + mut inputs: Box, +) -> Option> { + // TODO add inspector handle + // if let Some(inspector) = self.inspector.as_mut() { + // if let Some((result, address)) = inspector.create(&mut self.context.evm, &mut inputs) { + // curent_stack_frame + // .interpreter + // .insert_create_output(result, address); + // return None; + // } + // } + + match context.evm.make_create_frame::(&inputs) { + FrameOrResult::Frame(new_frame) => Some(new_frame), + FrameOrResult::Result(mut result) => { + let mut address = None; + // TODO add inspector handle + // if let Some(inspector) = self.inspector.as_mut() { + // let ret = inspector.create_end( + // &mut self.context.evm, + // result, + // curent_stack_frame.created_address, + // ); + // result = ret.0; + // address = ret.1; + // } + // insert result of the failed creation of create CallStackFrame. + curent_stack_frame + .interpreter + .insert_create_output(result, address); + None + } + } +} diff --git a/crates/revm/src/handler/mainnet/host.rs b/crates/revm/src/handler/mainnet/host.rs new file mode 100644 index 0000000000..722afbaf76 --- /dev/null +++ b/crates/revm/src/handler/mainnet/host.rs @@ -0,0 +1,65 @@ +use crate::{ + db::Database, + handler::Handler, + handler::RegisterHandler, + inspector_instruction, + interpreter::{ + gas::initial_tx_gas, + opcode::{make_boxed_instruction_table, make_instruction_table, InstructionTables}, + CallContext, CallInputs, CallScheme, CreateInputs, Host, Interpreter, InterpreterAction, + InterpreterResult, SelfDestructResult, SharedMemory, Transfer, + }, + journaled_state::JournaledState, + precompile::Precompiles, + primitives::{ + specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, + Output, Spec, SpecId::*, TransactTo, B256, U256, + }, + CallStackFrame, Context, EvmContext, FrameOrResult, Inspector, +}; +use alloc::{boxed::Box, sync::Arc, vec::Vec}; +use auto_impl::auto_impl; +use core::{fmt, marker::PhantomData, ops::Range}; + +pub fn handle_host_log( + context: &mut Context<'_, EXT, DB>, + address: Address, + topics: Vec, + data: Bytes, +) { + // TODO register inspector handle + // if let Some(inspector) = self.inspector.as_mut() { + // inspector.log(&mut self.context.evm, &address, &topics, &data); + // } + let log = Log { + address, + topics, + data, + }; + context.evm.journaled_state.log(log); +} + +pub fn handle_selfdestruct( + context: &mut Context<'_, EXT, DB>, + address: Address, + target: Address, +) -> Option { + // TODO register inspector handle + // if let Some(inspector) = self.inspector.as_mut() { + // let acc = self + // .context + // .evm + // .journaled_state + // .state + // .get(&address) + // .unwrap(); + // inspector.selfdestruct(address, target, acc.info.balance); + // } + + context + .evm + .journaled_state + .selfdestruct(address, target, &mut context.evm.db) + .map_err(|e| context.evm.error = Some(e)) + .ok() +} diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index 84ebdbe653..fed51ea300 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -28,7 +28,7 @@ pub trait RegisterHandler { handler: Handler<'a, Self, DB>, ) -> Handler<'a, Self, DB> where - Self: Sized, + Self: Sized+'a, DB: 'a, { handler @@ -43,18 +43,39 @@ impl RegisterHandler for MainnetHandle {} pub struct InspectorHandle<'a, DB: Database, INS: Inspector> { pub inspector: &'a mut INS, - pub _phantomdata: PhantomData<&'a DB>, + pub _phantomdata: PhantomData, } impl<'a, DB: Database, INS: Inspector> RegisterHandler for InspectorHandle<'a, DB, INS> { fn register_handler<'b, SPEC: Spec>( &self, - handler: Handler<'b, Self, DB>, + mut handler: Handler<'b, Self, DB>, ) -> Handler<'b, Self, DB> where - Self: Sized, + Self: Sized+'b, DB: 'b, { + + + + // return frame handle + let old_handle = handler.frame_return.clone(); + handler.frame_return = Arc::new( + move |context, mut child, parent, memory, mut result| -> Option { + let inspector = &mut context.external.inspector; + result = if child.is_create { + let (result, address) = + inspector.create_end(&mut context.evm, result, child.created_address); + child.created_address = address; + result + } else { + inspector.call_end(&mut context.evm, result) + }; + let output = old_handle(context, child, parent, memory, result); + output + }, + ); + handler } } @@ -73,9 +94,7 @@ impl RegisterHandler for ExternalData { DB: 'a, { let old_handle = handler.reimburse_caller.clone(); - handler.reimburse_caller = Arc::new(move |data, gas| { - old_handle(data, gas) - }); + handler.reimburse_caller = Arc::new(move |data, gas| old_handle(data, gas)); handler } } From 006a9c28b1430176d2089c120a6bf219577b20b0 Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 23 Nov 2023 02:07:18 +0100 Subject: [PATCH 07/52] Add instruction handle --- crates/interpreter/src/instructions/opcode.rs | 6 +- crates/revm/src/evm.rs | 37 ++++---- crates/revm/src/handler.rs | 31 ++++--- crates/revm/src/handler/mainnet.rs | 1 - crates/revm/src/handler/mainnet/call_loop.rs | 91 ------------------- crates/revm/src/handler/register.rs | 86 +++++++++++------- crates/revm/src/inspector/instruction.rs | 57 +++++++----- 7 files changed, 125 insertions(+), 184 deletions(-) delete mode 100644 crates/revm/src/handler/mainnet/call_loop.rs diff --git a/crates/interpreter/src/instructions/opcode.rs b/crates/interpreter/src/instructions/opcode.rs index 5e93c77d10..275dfa59ca 100644 --- a/crates/interpreter/src/instructions/opcode.rs +++ b/crates/interpreter/src/instructions/opcode.rs @@ -35,12 +35,12 @@ pub type BoxedInstructionTableArc<'a, H> = Arc>; /// Note that `Plain` variant gives us 10-20% faster Interpreter execution. /// /// Boxed variant can be used to wrap plain function pointer with closure. -pub enum InstructionTables<'a, H> { +pub enum InstructionTables<'a, H: Host> { Plain(InstructionTableArc), Boxed(BoxedInstructionTableArc<'a, H>), } -impl<'a, H> Clone for InstructionTables<'a, H> { +impl<'a, H: Host> Clone for InstructionTables<'a, H> { fn clone(&self) -> Self { match self { Self::Plain(table) => Self::Plain(table.clone()), @@ -95,7 +95,7 @@ pub fn make_boxed_instruction_table<'a, H, SPEC, FN>( outer: FN, ) -> BoxedInstructionTable<'a, H> where - H: Host + 'a, + H: Host, SPEC: Spec + 'static, FN: Fn(Instruction) -> BoxedInstruction<'a, H>, { diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index cc5236a26f..5fbebed013 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -27,11 +27,10 @@ use crate::optimism; /// EVM call stack limit. pub const CALL_STACK_LIMIT: u64 = 1024; -pub struct Evm<'a, SPEC: Spec, EXT, DB: Database> { +pub struct Evm<'a, SPEC: Spec + 'static, EXT, DB: Database> { pub context: Context<'a, EXT, DB>, - pub inspector: Option<&'a mut dyn Inspector>, pub instruction_table: InstructionTables<'a, Self>, - pub handler: Handler<'a, EXT, DB>, + pub handler: Handler<'a, Self, EXT, DB>, _phantomdata: PhantomData, } @@ -108,13 +107,12 @@ impl<'a, SPEC: Spec, DB: Database> Evm<'a, SPEC, DB> { impl<'a, SPEC: Spec + 'static, EXT: 'a, DB: Database> Evm<'a, SPEC, EXT, DB> where - EXT: RegisterHandler, + EXT: RegisterHandler<'a, DB> + 'a, { pub fn new_with_spec( db: &'a mut DB, env: &'a mut Env, external: EXT, - inspector: Option<&'a mut dyn Inspector>, precompiles: Precompiles, ) -> Self { let journaled_state = JournaledState::new( @@ -126,15 +124,18 @@ where .collect::>(), ); // If T is present it should be a generic T that modifies handler. - let instruction_table = if inspector.is_some() { - let instruction_table = make_boxed_instruction_table::( - make_instruction_table::(), - inspector_instruction, - ); - InstructionTables::Boxed(Arc::new(instruction_table)) - } else { - InstructionTables::Plain(Arc::new(make_instruction_table::())) - }; + // TODO move to inspector register. + // let instruction_table = if inspector.is_some() { + // let instruction_table = make_boxed_instruction_table::( + // make_instruction_table::(), + // inspector_instruction, + // ); + // InstructionTables::Boxed(Arc::new(instruction_table)) + // } else { + // InstructionTables::Plain(Arc::new(make_instruction_table::())) + // }; + let instruction_table = + InstructionTables::Plain(Arc::new(make_instruction_table::())); // temporary here. Factory should create handler and register external handles. let mut handler = external.register_handler::(Handler::mainnet::()); @@ -158,7 +159,6 @@ where }, external, }, - inspector, instruction_table, handler, _phantomdata: PhantomData {}, @@ -468,7 +468,7 @@ pub trait Transact { fn transact(&mut self) -> EVMResult; } -impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler + 'a, DB: Database> Transact +impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler<'a, DB> + 'a, DB: Database> Transact for Evm<'a, SPEC, EXT, DB> { #[inline] @@ -549,7 +549,7 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Host for Evm<'a, SPEC, EXT, DB } /// Creates new EVM instance with erased types. -pub fn new_evm<'a, EXT: RegisterHandler + 'a, DB: Database>( +pub fn new_evm<'a, EXT: RegisterHandler<'a, DB> + 'a, DB: Database>( env: &'a mut Env, db: &'a mut DB, external: EXT, @@ -561,7 +561,8 @@ pub fn new_evm<'a, EXT: RegisterHandler + 'a, DB: Database>( db, env, external, - insp, + // TODO inspector + //insp, Precompiles::new(revm_precompile::SpecId::from_spec_id($spec::SPEC_ID)).clone(), )) }; diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index bf4b746732..09781ec56d 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -1,10 +1,9 @@ pub mod mainnet; #[cfg(feature = "optimism")] pub mod optimism; +pub mod register; -mod register; - -pub use register::{ExternalData, MainnetHandle, RegisterHandler}; +pub use register::{InspectorHandle, MainnetHandle, RegisterHandler}; use revm_precompile::{Address, Bytes, B256}; use crate::{ @@ -15,11 +14,12 @@ use crate::{ use alloc::sync::Arc; use core::ops::Range; use revm_interpreter::{ - CallInputs, CreateInputs, InterpreterResult, SelfDestructResult, SharedMemory, + opcode::{make_instruction_table, InstructionTables}, + CallInputs, CreateInputs, Host, InterpreterResult, SelfDestructResult, SharedMemory, }; /// Handle call return and return final gas value. -type CallReturnHandle<'a> = Arc Gas + 'a>; +pub type CallReturnHandle<'a> = Arc Gas + 'a>; /// Reimburse the caller with ethereum it didn't spent. type ReimburseCallerHandle<'a, EXT, DB> = Arc< @@ -30,10 +30,10 @@ type ReimburseCallerHandle<'a, EXT, DB> = Arc< type RewardBeneficiaryHandle<'a, EXT, DB> = ReimburseCallerHandle<'a, EXT, DB>; /// Calculate gas refund for transaction. -type CalculateGasRefundHandle<'a> = Arc u64 + 'a>; +pub type CalculateGasRefundHandle<'a> = Arc u64 + 'a>; /// Main return handle, takes state from journal and transforms internal result to external. -type MainReturnHandle<'a, EXT, DB> = Arc< +pub type MainReturnHandle<'a, EXT, DB> = Arc< dyn Fn( &mut Context<'_, EXT, DB>, InstructionResult, @@ -46,7 +46,7 @@ type MainReturnHandle<'a, EXT, DB> = Arc< /// After subcall is finished, call this function to handle return result. /// /// Return Some if we want to halt execution. This can be done on any stack frame. -type FrameReturnHandle<'a, EXT, DB> = Arc< +pub type FrameReturnHandle<'a, EXT, DB> = Arc< dyn Fn( // context &mut Context<'_, EXT, DB>, @@ -86,7 +86,7 @@ pub type EndHandle<'a, EXT, DB> = Arc< >; /// Handle sub call. -type FrameSubCallHandle<'a, EXT, DB> = Arc< +pub type FrameSubCallHandle<'a, EXT, DB> = Arc< dyn Fn( &mut Context<'_, EXT, DB>, Box, @@ -98,7 +98,7 @@ type FrameSubCallHandle<'a, EXT, DB> = Arc< >; /// Handle sub create. -type FrameSubCreateHandle<'a, EXT, DB: Database> = Arc< +pub type FrameSubCreateHandle<'a, EXT, DB> = Arc< dyn Fn( &mut Context<'_, EXT, DB>, &mut CallStackFrame, @@ -110,7 +110,9 @@ type FrameSubCreateHandle<'a, EXT, DB: Database> = Arc< /// Handler acts as a proxy and allow to define different behavior for different /// sections of the code. This allows nice integration of different chains or /// to disable some mainnet behavior. -pub struct Handler<'a, EXT, DB: Database> { +pub struct Handler<'a, H: Host+'a, EXT, DB: Database> { + /// Instruction table type. + pub instruction_table: InstructionTables<'a, H>, // Uses env, call result and returned gas from the call to determine the gas // that is returned from transaction execution.. pub call_return: CallReturnHandle<'a>, @@ -125,8 +127,6 @@ pub struct Handler<'a, EXT, DB: Database> { pub main_return: MainReturnHandle<'a, EXT, DB>, /// End handle. pub end: EndHandle<'a, EXT, DB>, - // Called on sub call. - //pub sub_call: SubCall, /// Frame return pub frame_return: FrameReturnHandle<'a, EXT, DB>, /// Frame sub call @@ -139,7 +139,7 @@ pub struct Handler<'a, EXT, DB: Database> { pub host_selfdestruct: HostSelfdestruct<'a, EXT, DB>, } -impl<'a, EXT: 'a, DB: Database + 'a> Handler<'a, EXT, DB> { +impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// Handler for the mainnet pub fn mainnet() -> Self { Self { @@ -149,6 +149,9 @@ impl<'a, EXT: 'a, DB: Database + 'a> Handler<'a, EXT, DB> { reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), main_return: Arc::new(mainnet::main::main_return::), end: Arc::new(mainnet::main::end_handle::), + instruction_table: InstructionTables::Plain(Arc::new( + make_instruction_table::(), + )), frame_return: Arc::new(mainnet::frames::handle_frame_return::), frame_sub_call: Arc::new(mainnet::frames::handle_frame_sub_call::), frame_sub_create: Arc::new(mainnet::frames::handle_frame_sub_create::), diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index 3b29a86628..4f75d44e3a 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -1,6 +1,5 @@ //! Mainnet related handlers. -pub mod call_loop; pub mod main; pub mod frames; pub mod host; diff --git a/crates/revm/src/handler/mainnet/call_loop.rs b/crates/revm/src/handler/mainnet/call_loop.rs deleted file mode 100644 index 9d6eaf2d05..0000000000 --- a/crates/revm/src/handler/mainnet/call_loop.rs +++ /dev/null @@ -1,91 +0,0 @@ -use revm_interpreter::SharedMemory; - -use crate::{ - interpreter::{CallInputs, CreateInputs}, - primitives::Spec, - CallStackFrame, Database, Evm, EvmContext, FrameOrResult, -}; -use core::ops::Range; - -/// -pub fn initial_call_create() -> FrameOrResult { - unimplemented!() -} - -// Handles action for new sub call, return None if there is no need to add -// new stack frame. -// #[inline] -// pub fn sub_call( -// evm: &mut Evm<'_, SPEC, DB>, -// mut inputs: Box, -// curent_stake_frame: &mut CallStackFrame, -// shared_memory: &mut SharedMemory, -// return_memory_offset: Range, -// ) -> Option> { -// // Call inspector if it is some. -// if let Some(inspector) = evm.inspector.as_mut() { -// if let Some((result, range)) = inspector.call(&mut evm.context, &mut inputs) { -// curent_stake_frame -// .interpreter -// .insert_call_output(shared_memory, result, range); -// return None; -// } -// } -// match evm -// .context -// .make_call_frame(&inputs, return_memory_offset.clone()) -// { -// FrameOrResult::Frame(new_frame) => Some(new_frame), -// FrameOrResult::Result(mut result) => { -// //println!("Result returned right away: {:#?}", result); -// if let Some(inspector) = evm.inspector.as_mut() { -// result = inspector.call_end(&mut evm.context, result); -// } -// curent_stake_frame.interpreter.insert_call_output( -// shared_memory, -// result, -// return_memory_offset, -// ); -// None -// } -// } -// } - -// /// Handle Action for new sub create call, return None if there is no need -// /// to add new stack frame. -// pub fn sub_create( -// evm: &mut Evm<'_, SPEC, DB>, -// curent_stack_frame: &mut CallStackFrame, -// mut inputs: Box, -// ) -> Option> { -// // Call inspector if it is some. -// if let Some(inspector) = evm.inspector.as_mut() { -// if let Some((result, address)) = inspector.create(&mut evm.context, &mut inputs) { -// curent_stack_frame -// .interpreter -// .insert_create_output(result, address); -// return None; -// } -// } - -// match evm.context.make_create_frame::(&inputs) { -// FrameOrResult::Frame(new_frame) => Some(new_frame), -// FrameOrResult::Result(mut result) => { -// let mut address = None; -// if let Some(inspector) = evm.inspector.as_mut() { -// let ret = inspector.create_end( -// &mut evm.context, -// result, -// curent_stack_frame.created_address, -// ); -// result = ret.0; -// address = ret.1; -// } -// // insert result of the failed creation of create CallStackFrame. -// curent_stack_frame -// .interpreter -// .insert_create_output(result, address); -// None -// } -// } -// } diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index fed51ea300..dea22fdbb1 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -14,22 +14,21 @@ use crate::{ specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, Output, Spec, SpecId::*, TransactTo, B256, U256, }, - CallStackFrame, Context, EvmContext, FrameOrResult, Inspector, + CallStackFrame, Context, Evm, EvmContext, FrameOrResult, Inspector, }; use alloc::{boxed::Box, sync::Arc, vec::Vec}; use auto_impl::auto_impl; use core::{fmt, marker::PhantomData, ops::Range}; /// Register external handles. -pub trait RegisterHandler { - /// Register external handler. - fn register_handler<'a, SPEC: Spec>( +pub trait RegisterHandler<'a, DB: Database> { + fn register_handler( &self, - handler: Handler<'a, Self, DB>, - ) -> Handler<'a, Self, DB> + handler: Handler<'a, Evm<'a, SPEC, Self, DB>, Self, DB>, + ) -> Handler<'a, Evm<'a, SPEC, Self, DB>, Self, DB> where - Self: Sized+'a, DB: 'a, + Self: Sized, { handler } @@ -39,24 +38,47 @@ pub trait RegisterHandler { #[derive(Default)] pub struct MainnetHandle {} -impl RegisterHandler for MainnetHandle {} +impl<'a, DB: Database> RegisterHandler<'a, DB> for MainnetHandle {} pub struct InspectorHandle<'a, DB: Database, INS: Inspector> { pub inspector: &'a mut INS, pub _phantomdata: PhantomData, } -impl<'a, DB: Database, INS: Inspector> RegisterHandler for InspectorHandle<'a, DB, INS> { - fn register_handler<'b, SPEC: Spec>( +impl<'handler, DB: Database, INS: Inspector> RegisterHandler<'handler, DB> + for InspectorHandle<'handler, DB, INS> +{ + fn register_handler( &self, - mut handler: Handler<'b, Self, DB>, - ) -> Handler<'b, Self, DB> + mut handler: Handler<'handler, Evm<'handler, SPEC, Self, DB>, Self, DB>, + ) -> Handler<'handler, Evm<'handler, SPEC, Self, DB>, Self, DB> where - Self: Sized+'b, - DB: 'b, + Self: Sized, + DB: 'handler, { + // let instruction_table = make_boxed_instruction_table::< + // 'handler, + // Evm<'handler, SPEC, InspectorHandle<'handler, DB, INS>, DB>, + // SPEC, + // _, + // >( + // make_instruction_table::< + // Evm<'handler, SPEC, InspectorHandle<'handler, DB, INS>, DB>, + // SPEC, + // >(), + // inspector_instruction::, + // ); - + let flat_table = make_instruction_table::< + Evm<'handler, SPEC, InspectorHandle<'handler, DB, INS>, DB>, + SPEC, + >(); + + let table = core::array::from_fn(|i| inspector_instruction(flat_table[i])); + + let table = InstructionTables::Boxed(Arc::new(table)); + + handler.instruction_table = table; // return frame handle let old_handle = handler.frame_return.clone(); @@ -80,21 +102,21 @@ impl<'a, DB: Database, INS: Inspector> RegisterHandler for InspectorHand } } -pub struct ExternalData { - pub flagg: bool, - pub phantom: PhantomData, -} +// pub struct ExternalData { +// pub flagg: bool, +// pub phantom: PhantomData, +// } -impl RegisterHandler for ExternalData { - fn register_handler<'a, SPEC: Spec>( - &self, - mut handler: Handler<'a, Self, DB>, - ) -> Handler<'a, Self, DB> - where - DB: 'a, - { - let old_handle = handler.reimburse_caller.clone(); - handler.reimburse_caller = Arc::new(move |data, gas| old_handle(data, gas)); - handler - } -} +// impl RegisterHandler for ExternalData { +// fn register_handler<'a, SPEC: Spec>( +// &self, +// mut handler: Handler<'a, Evm<'a, SPEC, Self, DB>, Self, DB>, +// ) -> Handler<'a, Evm<'a, SPEC, Self, DB>, Self, DB> +// where +// DB: 'a, +// { +// let old_handle = handler.reimburse_caller.clone(); +// handler.reimburse_caller = Arc::new(move |data, gas| old_handle(data, gas)); +// handler +// } +// } diff --git a/crates/revm/src/inspector/instruction.rs b/crates/revm/src/inspector/instruction.rs index 7a14bad873..ab0415360c 100644 --- a/crates/revm/src/inspector/instruction.rs +++ b/crates/revm/src/inspector/instruction.rs @@ -1,4 +1,4 @@ -use crate::Evm; +use crate::{handler::InspectorHandle, Evm, Inspector}; use alloc::boxed::Box; use revm_interpreter::{ opcode::{BoxedInstruction, Instruction}, @@ -7,31 +7,34 @@ use revm_interpreter::{ }; /// Outer closure that calls Inspector for every instruction. -pub fn inspector_instruction<'a, SPEC: Spec + 'static, EXT: 'a, DB: Database>( - instruction: Instruction>, -) -> BoxedInstruction<'a, Evm<'a, SPEC, EXT, DB>> { +pub fn inspector_instruction<'a, SPEC: Spec + 'static, INSP: Inspector+'a, DB: Database>( + instruction: Instruction, DB>>, +) -> BoxedInstruction<'a, Evm<'a, SPEC, InspectorHandle<'a, DB, INSP>, DB>> { Box::new( - move |interpreter: &mut Interpreter, host: &mut Evm<'a, SPEC, EXT, DB>| { - if let Some(inspector) = host.inspector.as_mut() { - // SAFETY: as the PC was already incremented we need to subtract 1 to preserve the - // old Inspector behavior. - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.sub(1) }; + move |interpreter: &mut Interpreter, + host: &mut Evm<'a, SPEC, InspectorHandle<'a, DB, INSP>, DB>| { + // SAFETY: as the PC was already incremented we need to subtract 1 to preserve the + // old Inspector behavior. + interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.sub(1) }; - inspector.step(interpreter, &mut host.context.evm); - if interpreter.instruction_result != InstructionResult::Continue { - return; - } - - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.add(1) }; + host.context + .external + .inspector + .step(interpreter, &mut host.context.evm); + if interpreter.instruction_result != InstructionResult::Continue { + return; } + // return PC to old value + interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.add(1) }; + // execute instruction. instruction(interpreter, host); - // step ends - if let Some(inspector) = host.inspector.as_mut() { - inspector.step_end(interpreter, &mut host.context.evm); - } + host.context + .external + .inspector + .step_end(interpreter, &mut host.context.evm); }, ) } @@ -39,17 +42,21 @@ pub fn inspector_instruction<'a, SPEC: Spec + 'static, EXT: 'a, DB: Database>( #[cfg(test)] mod tests { use super::*; + use crate::inspectors::NoOpInspector; use crate::{db::EmptyDB, interpreter::opcode::*, primitives::BerlinSpec, Evm}; #[test] fn test_make_boxed_instruction_table() { // test that this pattern builds. - let inst: InstructionTable> = - make_instruction_table::, BerlinSpec>(); + let inst: InstructionTable< + Evm<'_, BerlinSpec, InspectorHandle<'_, EmptyDB, NoOpInspector>, EmptyDB>, + > = make_instruction_table::, BerlinSpec>(); let _test: BoxedInstructionTable<'_, Evm<'_, BerlinSpec, _, _>> = - make_boxed_instruction_table::<'_, Evm<'_, BerlinSpec, _, EmptyDB>, BerlinSpec, _>( - inst, - inspector_instruction, - ); + make_boxed_instruction_table::< + '_, + Evm<'_, BerlinSpec, InspectorHandle<'_, EmptyDB, NoOpInspector>, EmptyDB>, + BerlinSpec, + _, + >(inst, inspector_instruction); } } From 73f5ebd99366ebea5d8a5ce83c3962f1ca617661 Mon Sep 17 00:00:00 2001 From: rakita Date: Fri, 24 Nov 2023 10:37:05 +0100 Subject: [PATCH 08/52] add instruction handler registration and Inspector wrap --- crates/revm/src/context.rs | 50 ------- crates/revm/src/evm.rs | 45 ++---- crates/revm/src/evm_factory.rs | 37 +---- crates/revm/src/handler.rs | 6 +- crates/revm/src/handler/mainnet.rs | 11 +- crates/revm/src/handler/mainnet/frames.rs | 110 +++----------- crates/revm/src/handler/mainnet/host.rs | 41 +----- crates/revm/src/handler/mainnet/main.rs | 9 +- crates/revm/src/handler/register.rs | 170 ++++++++++++++++------ crates/revm/src/inspector/instruction.rs | 14 +- crates/revm/src/inspector/noop.rs | 8 +- 11 files changed, 210 insertions(+), 291 deletions(-) diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index 0920195dab..1bf572fc81 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -42,56 +42,6 @@ pub struct EvmContext<'a, DB: Database> { pub l1_block_info: Option, } -pub struct Temp { - pub external: EXT, - pub handle: Box, -} - -impl Temp { - pub fn call(&mut self) { - let handle = &mut self.handle; - let ext = &mut self.external; - handle(ext); - } -} - -#[derive(Default)] -pub struct External { - pub var: bool, -} - -impl External { - pub fn handle(&self) -> Box { - Box::new(call) - } -} - -impl ExtT for External { - fn change(&mut self) { - self.var = true; - } -} - -pub trait ExtT { - fn change(&mut self); -} - -fn call(temp: &mut impl ExtT) { - temp.change() -} - -pub fn test() { - let ext = External::default(); - let handle = ext.handle(); - let mut temp = Temp { - external: ext, - handle: handle, - }; - - temp.call(); - assert!(temp.external.var); -} - impl<'a, DB: Database> EvmContext<'a, DB> { /// Load access list for berlin hard fork. /// diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 5fbebed013..cd9670734f 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -2,24 +2,22 @@ use crate::{ db::Database, handler::Handler, handler::RegisterHandler, - inspector_instruction, interpreter::{ - gas::initial_tx_gas, - opcode::{make_boxed_instruction_table, make_instruction_table, InstructionTables}, - CallContext, CallInputs, CallScheme, CreateInputs, Host, Interpreter, InterpreterAction, - InterpreterResult, SelfDestructResult, SharedMemory, Transfer, + gas::initial_tx_gas, opcode::InstructionTables, CallContext, CallInputs, CallScheme, + CreateInputs, Host, Interpreter, InterpreterAction, InterpreterResult, SelfDestructResult, + SharedMemory, Transfer, }, journaled_state::JournaledState, precompile::Precompiles, primitives::{ - specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, + specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Output, Spec, SpecId::*, TransactTo, B256, U256, }, - CallStackFrame, Context, EvmContext, FrameOrResult, Inspector, + CallStackFrame, Context, EvmContext, FrameOrResult, }; use alloc::{boxed::Box, sync::Arc, vec::Vec}; use auto_impl::auto_impl; -use core::{fmt, marker::PhantomData, ops::Range}; +use core::{fmt, marker::PhantomData}; #[cfg(feature = "optimism")] use crate::optimism; @@ -28,9 +26,11 @@ use crate::optimism; pub const CALL_STACK_LIMIT: u64 = 1024; pub struct Evm<'a, SPEC: Spec + 'static, EXT, DB: Database> { + /// Context of execution, containing both EVM and external context. pub context: Context<'a, EXT, DB>, - pub instruction_table: InstructionTables<'a, Self>, + /// Handler of EVM that contains all the logic. pub handler: Handler<'a, Self, EXT, DB>, + /// Phantom data _phantomdata: PhantomData, } @@ -105,9 +105,9 @@ impl<'a, SPEC: Spec, DB: Database> Evm<'a, SPEC, DB> { } } -impl<'a, SPEC: Spec + 'static, EXT: 'a, DB: Database> Evm<'a, SPEC, EXT, DB> +impl<'a, SPEC: Spec, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> where - EXT: RegisterHandler<'a, DB> + 'a, + EXT: RegisterHandler<'a, DB, EXT>, { pub fn new_with_spec( db: &'a mut DB, @@ -123,19 +123,6 @@ where .cloned() .collect::>(), ); - // If T is present it should be a generic T that modifies handler. - // TODO move to inspector register. - // let instruction_table = if inspector.is_some() { - // let instruction_table = make_boxed_instruction_table::( - // make_instruction_table::(), - // inspector_instruction, - // ); - // InstructionTables::Boxed(Arc::new(instruction_table)) - // } else { - // InstructionTables::Plain(Arc::new(make_instruction_table::())) - // }; - let instruction_table = - InstructionTables::Plain(Arc::new(make_instruction_table::())); // temporary here. Factory should create handler and register external handles. let mut handler = external.register_handler::(Handler::mainnet::()); @@ -159,7 +146,6 @@ where }, external, }, - instruction_table, handler, _phantomdata: PhantomData {}, } @@ -416,7 +402,7 @@ where let interpreter_result = match first_stack_frame { FrameOrResult::Frame(first_stack_frame) => { created_address = first_stack_frame.created_address; - let table = self.instruction_table.clone(); + let table = self.handler.instruction_table.clone(); match table { InstructionTables::Plain(table) => self.run(&table, first_stack_frame), InstructionTables::Boxed(table) => self.run(&table, first_stack_frame), @@ -468,7 +454,7 @@ pub trait Transact { fn transact(&mut self) -> EVMResult; } -impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler<'a, DB> + 'a, DB: Database> Transact +impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler<'a, DB, EXT>, DB: Database> Transact for Evm<'a, SPEC, EXT, DB> { #[inline] @@ -549,11 +535,10 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Host for Evm<'a, SPEC, EXT, DB } /// Creates new EVM instance with erased types. -pub fn new_evm<'a, EXT: RegisterHandler<'a, DB> + 'a, DB: Database>( +pub fn new_evm<'a, EXT: RegisterHandler<'a, DB, EXT> + 'a, DB: Database>( env: &'a mut Env, db: &'a mut DB, external: EXT, - insp: Option<&'a mut dyn Inspector>, ) -> Box + 'a> { macro_rules! create_evm { ($spec:ident) => { @@ -561,8 +546,6 @@ pub fn new_evm<'a, EXT: RegisterHandler<'a, DB> + 'a, DB: Database>( db, env, external, - // TODO inspector - //insp, Precompiles::new(revm_precompile::SpecId::from_spec_id($spec::SPEC_ID)).clone(), )) }; diff --git a/crates/revm/src/evm_factory.rs b/crates/revm/src/evm_factory.rs index 9d2f5d3535..c6244814cb 100644 --- a/crates/revm/src/evm_factory.rs +++ b/crates/revm/src/evm_factory.rs @@ -73,13 +73,7 @@ impl EvmFactory { /// Do checks that could make transaction fail before call/create pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { if let Some(db) = self.db.as_mut() { - new_evm::( - &mut self.env, - db, - MainnetHandle::default(), - None, - ) - .preverify_transaction() + new_evm(&mut self.env, db, MainnetHandle::default()).preverify_transaction() } else { panic!("Database needs to be set"); } @@ -89,13 +83,7 @@ impl EvmFactory { /// state. pub fn transact_preverified(&mut self) -> EVMResult { if let Some(db) = self.db.as_mut() { - new_evm::( - &mut self.env, - db, - MainnetHandle::default(), - None, - ) - .transact_preverified() + new_evm(&mut self.env, db, MainnetHandle::default()).transact_preverified() } else { panic!("Database needs to be set"); } @@ -104,13 +92,7 @@ impl EvmFactory { /// Execute transaction without writing to DB, return change state. pub fn transact(&mut self) -> EVMResult { if let Some(db) = self.db.as_mut() { - new_evm::( - &mut self.env, - db, - MainnetHandle::default(), - None, - ) - .transact() + new_evm::<_, DB>(&mut self.env, db, MainnetHandle::default()).transact() } else { panic!("Database needs to be set"); } @@ -119,11 +101,11 @@ impl EvmFactory { /// Execute transaction with given inspector, without wring to DB. Return change state. pub fn inspect>(&mut self, mut inspector: INSP) -> EVMResult { if let Some(db) = self.db.as_mut() { - new_evm::( + new_evm( &mut self.env, db, MainnetHandle::default(), - Some(&mut inspector), + //Some(&mut inspector), ) .transact() } else { @@ -136,11 +118,10 @@ impl<'a, DB: DatabaseRef> EvmFactory { /// Do checks that could make transaction fail before call/create pub fn preverify_transaction_ref(&self) -> Result<(), EVMError> { if let Some(db) = self.db.as_ref() { - new_evm::( + new_evm( &mut self.env.clone(), &mut WrapDatabaseRef(db), MainnetHandle::default(), - None, ) .preverify_transaction() } else { @@ -156,7 +137,6 @@ impl<'a, DB: DatabaseRef> EvmFactory { &mut self.env.clone(), &mut WrapDatabaseRef(db), MainnetHandle::default(), - None, ) .transact_preverified() } else { @@ -171,7 +151,6 @@ impl<'a, DB: DatabaseRef> EvmFactory { &mut self.env.clone(), &mut WrapDatabaseRef(db), MainnetHandle::default(), - None, ) .transact() } else { @@ -185,11 +164,11 @@ impl<'a, DB: DatabaseRef> EvmFactory { mut inspector: I, ) -> EVMResult { if let Some(db) = self.db.as_ref() { - new_evm::( + new_evm( &mut self.env.clone(), &mut WrapDatabaseRef(db), MainnetHandle::default(), - Some(&mut inspector), + //Some(&mut inspector), ) .transact() } else { diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 09781ec56d..b73fecade9 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -9,7 +9,7 @@ use revm_precompile::{Address, Bytes, B256}; use crate::{ interpreter::{Gas, InstructionResult}, primitives::{db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec}, - CallStackFrame, Context, Evm, + CallStackFrame, Context, }; use alloc::sync::Arc; use core::ops::Range; @@ -70,7 +70,7 @@ pub type HostLogHandle<'a, EXT, DB> = /// /// After CANCUN hardfork original contract will stay the same but the value will /// be transfered to the target. -pub type HostSelfdestruct<'a, EXT, DB> = +pub type HostSelfdestructHandle<'a, EXT, DB> = Arc, Address, Address) -> Option + 'a>; /// End handle, takes result and state and returns final result. @@ -136,7 +136,7 @@ pub struct Handler<'a, H: Host+'a, EXT, DB: Database> { /// Host log handle. pub host_log: HostLogHandle<'a, EXT, DB>, /// Host selfdestruct handle. - pub host_selfdestruct: HostSelfdestruct<'a, EXT, DB>, + pub host_selfdestruct: HostSelfdestructHandle<'a, EXT, DB>, } impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index 4f75d44e3a..94ca0e4fbf 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -1,16 +1,13 @@ //! Mainnet related handlers. -pub mod main; pub mod frames; pub mod host; +pub mod main; use crate::{ - interpreter::{return_ok, return_revert, Gas, InstructionResult, SuccessOrHalt}, - primitives::{ - db::Database, EVMError, Env, ExecutionResult, Output, ResultAndState, Spec, SpecId::LONDON, - U256, - }, - Context, EvmContext, + interpreter::{return_ok, return_revert, Gas, InstructionResult}, + primitives::{db::Database, EVMError, Env, Spec, SpecId::LONDON, U256}, + Context, }; /// Handle output of the transaction diff --git a/crates/revm/src/handler/mainnet/frames.rs b/crates/revm/src/handler/mainnet/frames.rs index 75c808c7e0..e5e45c0a30 100644 --- a/crates/revm/src/handler/mainnet/frames.rs +++ b/crates/revm/src/handler/mainnet/frames.rs @@ -1,68 +1,38 @@ use crate::{ db::Database, - handler::Handler, - handler::RegisterHandler, - inspector_instruction, - interpreter::{ - gas::initial_tx_gas, - opcode::{make_boxed_instruction_table, make_instruction_table, InstructionTables}, - CallContext, CallInputs, CallScheme, CreateInputs, Host, Interpreter, InterpreterAction, - InterpreterResult, SelfDestructResult, SharedMemory, Transfer, - }, - journaled_state::JournaledState, - precompile::Precompiles, - primitives::{ - specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, - Output, Spec, SpecId::*, TransactTo, B256, U256, - }, - CallStackFrame, Context, EvmContext, FrameOrResult, Inspector, + interpreter::{CallInputs, CreateInputs, InterpreterResult, SharedMemory}, + primitives::Spec, + CallStackFrame, Context, FrameOrResult, }; -use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use auto_impl::auto_impl; -use core::{fmt, marker::PhantomData, ops::Range}; +use alloc::boxed::Box; +use core::ops::Range; +/// Handle frame return. pub fn handle_frame_return( context: &mut Context<'_, EXT, DB>, - mut child_stack_frame: Box, + child_stack_frame: Box, parent_stack_frame: Option<&mut Box>, shared_memory: &mut SharedMemory, - mut result: InterpreterResult, + result: InterpreterResult, ) -> Option { - // TODO - // if let Some(inspector) = self.inspector.as_mut() { - // result = if child_stack_frame.is_create { - // let (result, address) = inspector.create_end( - // &mut self.context.evm, - // result, - // child_stack_frame.created_address, - // ); - // child_stack_frame.created_address = address; - // result - // } else { - // inspector.call_end(&mut self.context.evm, result) - // }; - // } - // break from loop if this is last CallStackFrame. - let Some(parent_stack_frame) = parent_stack_frame else { - let result = if child_stack_frame.is_create { - context - .evm - .create_return::(result, child_stack_frame) - .0 - } else { - context.evm.call_return(result, child_stack_frame) - }; - - return Some(result); - }; - if child_stack_frame.is_create { + let Some(parent_stack_frame) = parent_stack_frame else { + return Some( + context + .evm + .create_return::(result, child_stack_frame) + .0, + ); + }; let (result, address) = context.evm.create_return::(result, child_stack_frame); parent_stack_frame .interpreter .insert_create_output(result, Some(address)) } else { + let Some(parent_stack_frame) = parent_stack_frame else { + return Some(context.evm.call_return(result, child_stack_frame)); + }; let subcall_memory_return_offset = child_stack_frame.subcall_return_memory_range.clone(); let result = context.evm.call_return(result, child_stack_frame); @@ -75,6 +45,7 @@ pub fn handle_frame_return( None } +/// Handle frame sub call. pub fn handle_frame_sub_call( context: &mut Context<'_, EXT, DB>, inputs: Box, @@ -82,25 +53,12 @@ pub fn handle_frame_sub_call( shared_memory: &mut SharedMemory, return_memory_offset: Range, ) -> Option> { - // TODO inspector handle - // if let Some(inspector) = self.inspector.as_mut() { - // if let Some((result, range)) = inspector.call(&mut self.context.evm, &mut inputs) { - // curent_stack_frame - // .interpreter - // .insert_call_output(shared_memory, result, range); - // return None; - // } - // } match context .evm .make_call_frame(&inputs, return_memory_offset.clone()) { FrameOrResult::Frame(new_frame) => Some(new_frame), FrameOrResult::Result(result) => { - // TODO handle inspector - // if let Some(inspector) = self.inspector.as_mut() { - // result = inspector.call_end(&mut self.context.evm, result); - // } curent_stack_frame.interpreter.insert_call_output( shared_memory, result, @@ -111,39 +69,19 @@ pub fn handle_frame_sub_call( } } +/// Handle frame sub create. pub fn handle_frame_sub_create( context: &mut Context<'_, EXT, DB>, curent_stack_frame: &mut CallStackFrame, - mut inputs: Box, + inputs: Box, ) -> Option> { - // TODO add inspector handle - // if let Some(inspector) = self.inspector.as_mut() { - // if let Some((result, address)) = inspector.create(&mut self.context.evm, &mut inputs) { - // curent_stack_frame - // .interpreter - // .insert_create_output(result, address); - // return None; - // } - // } - match context.evm.make_create_frame::(&inputs) { FrameOrResult::Frame(new_frame) => Some(new_frame), - FrameOrResult::Result(mut result) => { - let mut address = None; - // TODO add inspector handle - // if let Some(inspector) = self.inspector.as_mut() { - // let ret = inspector.create_end( - // &mut self.context.evm, - // result, - // curent_stack_frame.created_address, - // ); - // result = ret.0; - // address = ret.1; - // } + FrameOrResult::Result(result) => { // insert result of the failed creation of create CallStackFrame. curent_stack_frame .interpreter - .insert_create_output(result, address); + .insert_create_output(result, None); None } } diff --git a/crates/revm/src/handler/mainnet/host.rs b/crates/revm/src/handler/mainnet/host.rs index 722afbaf76..758cc02f77 100644 --- a/crates/revm/src/handler/mainnet/host.rs +++ b/crates/revm/src/handler/mainnet/host.rs @@ -1,36 +1,18 @@ use crate::{ db::Database, - handler::Handler, - handler::RegisterHandler, - inspector_instruction, - interpreter::{ - gas::initial_tx_gas, - opcode::{make_boxed_instruction_table, make_instruction_table, InstructionTables}, - CallContext, CallInputs, CallScheme, CreateInputs, Host, Interpreter, InterpreterAction, - InterpreterResult, SelfDestructResult, SharedMemory, Transfer, - }, - journaled_state::JournaledState, - precompile::Precompiles, - primitives::{ - specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, - Output, Spec, SpecId::*, TransactTo, B256, U256, - }, - CallStackFrame, Context, EvmContext, FrameOrResult, Inspector, + interpreter::SelfDestructResult, + primitives::{Address, Bytes, Log, Spec, B256}, + Context, }; -use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use auto_impl::auto_impl; -use core::{fmt, marker::PhantomData, ops::Range}; +use alloc::vec::Vec; +/// Handle host log call. pub fn handle_host_log( context: &mut Context<'_, EXT, DB>, address: Address, topics: Vec, data: Bytes, ) { - // TODO register inspector handle - // if let Some(inspector) = self.inspector.as_mut() { - // inspector.log(&mut self.context.evm, &address, &topics, &data); - // } let log = Log { address, topics, @@ -39,23 +21,12 @@ pub fn handle_host_log( context.evm.journaled_state.log(log); } +/// Handle host selfdestruct call. pub fn handle_selfdestruct( context: &mut Context<'_, EXT, DB>, address: Address, target: Address, ) -> Option { - // TODO register inspector handle - // if let Some(inspector) = self.inspector.as_mut() { - // let acc = self - // .context - // .evm - // .journaled_state - // .state - // .get(&address) - // .unwrap(); - // inspector.selfdestruct(address, target, acc.info.balance); - // } - context .evm .journaled_state diff --git a/crates/revm/src/handler/mainnet/main.rs b/crates/revm/src/handler/mainnet/main.rs index 4f99d0bd9a..a74f061aa9 100644 --- a/crates/revm/src/handler/mainnet/main.rs +++ b/crates/revm/src/handler/mainnet/main.rs @@ -3,12 +3,9 @@ //! They handle initial setup of the EVM, call loop and the final return of the EVM use crate::{ - interpreter::{return_ok, return_revert, Gas, InstructionResult, SuccessOrHalt}, - primitives::{ - db::Database, EVMError, Env, ExecutionResult, Output, ResultAndState, Spec, SpecId::LONDON, - U256, - }, - Context, EvmContext, + interpreter::{Gas, InstructionResult, SuccessOrHalt}, + primitives::{db::Database, EVMError, ExecutionResult, Output, ResultAndState}, + Context, }; /// Main return handle, returns the output of the transaction. diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index dea22fdbb1..d0aa5e9742 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -3,32 +3,24 @@ use crate::{ handler::Handler, inspector_instruction, interpreter::{ - gas::initial_tx_gas, - opcode::{make_boxed_instruction_table, make_instruction_table, InstructionTables}, - CallContext, CallInputs, CallScheme, CreateInputs, Host, Interpreter, InterpreterAction, - InterpreterResult, SelfDestructResult, SharedMemory, Transfer, + opcode::{make_instruction_table, InstructionTables}, + InterpreterResult, SelfDestructResult, }, - journaled_state::JournaledState, - precompile::Precompiles, - primitives::{ - specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, - Output, Spec, SpecId::*, TransactTo, B256, U256, - }, - CallStackFrame, Context, Evm, EvmContext, FrameOrResult, Inspector, + primitives::Spec, + CallStackFrame, Evm, FrameOrResult, Inspector, }; -use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use auto_impl::auto_impl; -use core::{fmt, marker::PhantomData, ops::Range}; +use alloc::sync::Arc; +use core::marker::PhantomData; /// Register external handles. -pub trait RegisterHandler<'a, DB: Database> { +pub trait RegisterHandler<'a, DB: Database, EXT> { fn register_handler( &self, - handler: Handler<'a, Evm<'a, SPEC, Self, DB>, Self, DB>, - ) -> Handler<'a, Evm<'a, SPEC, Self, DB>, Self, DB> + handler: Handler<'a, Evm<'a, SPEC, EXT, DB>, EXT, DB>, + ) -> Handler<'a, Evm<'a, SPEC, EXT, DB>, EXT, DB> where DB: 'a, - Self: Sized, + EXT: Sized, { handler } @@ -38,14 +30,42 @@ pub trait RegisterHandler<'a, DB: Database> { #[derive(Default)] pub struct MainnetHandle {} -impl<'a, DB: Database> RegisterHandler<'a, DB> for MainnetHandle {} +impl<'a, DB: Database> RegisterHandler<'a, DB, Self> for MainnetHandle {} -pub struct InspectorHandle<'a, DB: Database, INS: Inspector> { - pub inspector: &'a mut INS, - pub _phantomdata: PhantomData, +pub struct OptimismHandle {} + +impl<'a, DB: Database> RegisterHandler<'a, DB, ()> for OptimismHandle { + fn register_handler( + &self, + handler: Handler<'a, Evm<'a, SPEC, (), DB>, (), DB>, + ) -> Handler<'a, Evm<'a, SPEC, (), DB>, (), DB> + where + DB: 'a, + (): Sized, + { + Handler::mainnet::() + } +} + +pub struct InspectorHandle<'a, DB: Database, GI: GetInspector<'a, DB>> { + pub inspector: GI, + pub _phantomdata: PhantomData<&'a DB>, } -impl<'handler, DB: Database, INS: Inspector> RegisterHandler<'handler, DB> +impl<'a, DB: Database, GI: GetInspector<'a, DB>> InspectorHandle<'a, DB, GI> { + fn new(inspector: GI) -> Self { + Self { + inspector, + _phantomdata: PhantomData, + } + } +} + +pub trait GetInspector<'a, DB: Database> { + fn get(&mut self) -> &mut dyn Inspector; +} + +impl<'handler, DB: Database, INS: GetInspector<'handler, DB>> RegisterHandler<'handler, DB, Self> for InspectorHandle<'handler, DB, INS> { fn register_handler( @@ -56,35 +76,81 @@ impl<'handler, DB: Database, INS: Inspector> RegisterHandler<'handler, DB> Self: Sized, DB: 'handler, { - // let instruction_table = make_boxed_instruction_table::< - // 'handler, - // Evm<'handler, SPEC, InspectorHandle<'handler, DB, INS>, DB>, - // SPEC, - // _, - // >( - // make_instruction_table::< - // Evm<'handler, SPEC, InspectorHandle<'handler, DB, INS>, DB>, - // SPEC, - // >(), - // inspector_instruction::, - // ); - - let flat_table = make_instruction_table::< + // flag instruction table that is going to be wrapped. + let flat_instruction_table = make_instruction_table::< Evm<'handler, SPEC, InspectorHandle<'handler, DB, INS>, DB>, SPEC, >(); - let table = core::array::from_fn(|i| inspector_instruction(flat_table[i])); + // wrap instruction table with inspector handle. + handler.instruction_table = InstructionTables::Boxed(Arc::new(core::array::from_fn(|i| { + inspector_instruction(flat_instruction_table[i]) + }))); - let table = InstructionTables::Boxed(Arc::new(table)); + // handle sub create + handler.frame_sub_create = Arc::new( + move |context, frame, mut inputs| -> Option> { + if let Some((result, address)) = context + .external + .inspector + .get() + .create(&mut context.evm, &mut inputs) + { + frame.interpreter.insert_create_output(result, address); + return None; + } + + match context.evm.make_create_frame::(&inputs) { + FrameOrResult::Frame(new_frame) => Some(new_frame), + FrameOrResult::Result(result) => { + let (result, address) = context.external.inspector.get().create_end( + &mut context.evm, + result, + frame.created_address, + ); + // insert result of the failed creation of create CallStackFrame. + frame.interpreter.insert_create_output(result, address); + None + } + } + }, + ); - handler.instruction_table = table; + // handle sub call + handler.frame_sub_call = Arc::new( + move |context, mut inputs, frame, memory, return_memory_offset| -> Option> { + // inspector handle + let inspector = &mut context.external.inspector.get(); + if let Some((result, range)) = inspector.call(&mut context.evm, &mut inputs) { + frame.interpreter.insert_call_output(memory, result, range); + return None; + } + match context + .evm + .make_call_frame(&inputs, return_memory_offset.clone()) + { + FrameOrResult::Frame(new_frame) => Some(new_frame), + FrameOrResult::Result(result) => { + // inspector handle + let result = context + .external + .inspector + .get() + .call_end(&mut context.evm, result); + frame + .interpreter + .insert_call_output(memory, result, return_memory_offset); + None + } + } + }, + ); // return frame handle let old_handle = handler.frame_return.clone(); handler.frame_return = Arc::new( move |context, mut child, parent, memory, mut result| -> Option { - let inspector = &mut context.external.inspector; + let inspector = &mut context.external.inspector.get(); result = if child.is_create { let (result, address) = inspector.create_end(&mut context.evm, result, child.created_address); @@ -98,6 +164,28 @@ impl<'handler, DB: Database, INS: Inspector> RegisterHandler<'handler, DB> }, ); + // handle log + let old_handle = handler.host_log.clone(); + handler.host_log = Arc::new(move |context, address, topics, data| { + context + .external + .inspector + .get() + .log(&mut context.evm, &address, &topics, &data); + old_handle(context, address, topics, data) + }); + + // selfdestruct handle + let old_handle = handler.host_selfdestruct.clone(); + handler.host_selfdestruct = Arc::new( + move |context, address, target| -> Option { + let inspector = &mut context.external.inspector.get(); + let acc = context.evm.journaled_state.state.get(&address).unwrap(); + inspector.selfdestruct(address, target, acc.info.balance); + old_handle(context, address, target) + }, + ); + handler } } diff --git a/crates/revm/src/inspector/instruction.rs b/crates/revm/src/inspector/instruction.rs index ab0415360c..a88869fa09 100644 --- a/crates/revm/src/inspector/instruction.rs +++ b/crates/revm/src/inspector/instruction.rs @@ -1,4 +1,7 @@ -use crate::{handler::InspectorHandle, Evm, Inspector}; +use crate::{ + handler::{register::GetInspector, InspectorHandle}, + Evm, Inspector, +}; use alloc::boxed::Box; use revm_interpreter::{ opcode::{BoxedInstruction, Instruction}, @@ -7,7 +10,12 @@ use revm_interpreter::{ }; /// Outer closure that calls Inspector for every instruction. -pub fn inspector_instruction<'a, SPEC: Spec + 'static, INSP: Inspector+'a, DB: Database>( +pub fn inspector_instruction< + 'a, + SPEC: Spec + 'static, + INSP: GetInspector<'a, DB> + 'a, + DB: Database, +>( instruction: Instruction, DB>>, ) -> BoxedInstruction<'a, Evm<'a, SPEC, InspectorHandle<'a, DB, INSP>, DB>> { Box::new( @@ -20,6 +28,7 @@ pub fn inspector_instruction<'a, SPEC: Spec + 'static, INSP: Inspector+'a, D host.context .external .inspector + .get() .step(interpreter, &mut host.context.evm); if interpreter.instruction_result != InstructionResult::Continue { return; @@ -34,6 +43,7 @@ pub fn inspector_instruction<'a, SPEC: Spec + 'static, INSP: Inspector+'a, D host.context .external .inspector + .get() .step_end(interpreter, &mut host.context.evm); }, ) diff --git a/crates/revm/src/inspector/noop.rs b/crates/revm/src/inspector/noop.rs index 05d7292fc4..412278be74 100644 --- a/crates/revm/src/inspector/noop.rs +++ b/crates/revm/src/inspector/noop.rs @@ -1,7 +1,13 @@ -use crate::{Database, Inspector}; +use crate::{handler::register::GetInspector, Database, Inspector}; /// Dummy [Inspector], helpful as standalone replacement. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct NoOpInspector; impl Inspector for NoOpInspector {} + +impl GetInspector<'_, DB> for NoOpInspector { + fn get(&mut self) -> &mut dyn Inspector { + self + } +} \ No newline at end of file From 42dfcf4315134686670b77bfd76f434f176fc207 Mon Sep 17 00:00:00 2001 From: rakita Date: Sat, 25 Nov 2023 01:48:15 +0100 Subject: [PATCH 09/52] Rm Spec generic, more handlers, start factory --- crates/primitives/src/specification.rs | 4 + crates/revm/src/context.rs | 42 +-- crates/revm/src/evm.rs | 132 +++++---- crates/revm/src/evm_builder.rs | 41 +++ crates/revm/src/evm_factory.rs | 192 +++++++----- crates/revm/src/handler.rs | 90 +++--- crates/revm/src/handler/mainnet.rs | 10 +- crates/revm/src/handler/mainnet/frames.rs | 8 +- crates/revm/src/handler/mainnet/host.rs | 4 +- crates/revm/src/handler/mainnet/main.rs | 4 +- .../revm/src/handler/mainnet/preexecution.rs | 24 ++ crates/revm/src/handler/optimism.rs | 28 +- crates/revm/src/handler/register.rs | 65 ++-- crates/revm/src/inspector.rs | 28 +- crates/revm/src/inspector/customprinter.rs | 14 +- crates/revm/src/inspector/eip3155.rs | 14 +- crates/revm/src/inspector/gas.rs | 278 +++++++++--------- crates/revm/src/inspector/instruction.rs | 35 +-- crates/revm/src/inspector/noop.rs | 8 +- crates/revm/src/journaled_state.rs | 5 +- crates/revm/src/lib.rs | 12 +- 21 files changed, 577 insertions(+), 461 deletions(-) create mode 100644 crates/revm/src/evm_builder.rs create mode 100644 crates/revm/src/handler/mainnet/preexecution.rs diff --git a/crates/primitives/src/specification.rs b/crates/primitives/src/specification.rs index 5c5b6e1a14..0d80a20a07 100644 --- a/crates/primitives/src/specification.rs +++ b/crates/primitives/src/specification.rs @@ -40,6 +40,10 @@ impl SpecId { Self::n(spec_id) } + pub fn is_enabled_in(&self, other: Self) -> bool { + Self::enabled(*self, other) + } + #[inline] pub const fn enabled(our: SpecId, other: SpecId) -> bool { #[cfg(feature = "optimism")] diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index 1bf572fc81..5efb1170be 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -7,8 +7,8 @@ use crate::{ journaled_state::JournaledState, precompile::{Precompile, Precompiles}, primitives::{ - keccak256, Address, AnalysisKind, Bytecode, Bytes, EVMError, Env, Spec, SpecId::*, B256, - U256, + keccak256, Address, AnalysisKind, Bytecode, Bytes, EVMError, Env, Spec, SpecId, SpecId::*, + B256, U256, }, CallStackFrame, FrameOrResult, CALL_STACK_LIMIT, }; @@ -16,23 +16,23 @@ use alloc::boxed::Box; use core::ops::Range; /// Main Context structure that contains both EvmContext and external contexts. -pub struct Context<'a, EXT, DB: Database> { +pub struct Context { /// Evm Context. - pub evm: EvmContext<'a, DB>, + pub evm: EvmContext, /// External generic code. pub external: EXT, } /// EVM Data contains all the data that EVM needs to execute. #[derive(Debug)] -pub struct EvmContext<'a, DB: Database> { +pub struct EvmContext { /// EVM Environment contains all the information about config, block and transaction that /// evm needs. - pub env: &'a mut Env, + pub env: Box, /// EVM State with journaling support. pub journaled_state: JournaledState, /// Database to load data from. - pub db: &'a mut DB, + pub db: DB, /// Error that happened during execution. pub error: Option, /// Precompiles that are available for evm. @@ -42,7 +42,7 @@ pub struct EvmContext<'a, DB: Database> { pub l1_block_info: Option, } -impl<'a, DB: Database> EvmContext<'a, DB> { +impl<'a, DB: Database> EvmContext { /// Load access list for berlin hard fork. /// /// Loading of accounts/storages is needed to make them warm. @@ -50,7 +50,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { pub fn load_access_list(&mut self) -> Result<(), EVMError> { for (address, slots) in self.env.tx.access_list.iter() { self.journaled_state - .initial_account_load(*address, slots, self.db) + .initial_account_load(*address, slots, &mut self.db) .map_err(EVMError::Database)?; } Ok(()) @@ -58,7 +58,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { /// Return environment. pub fn env(&mut self) -> &mut Env { - self.env + &mut self.env } /// Fetch block hash from database. @@ -72,7 +72,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { /// Load account and return flags (is_cold, exists) pub fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { self.journaled_state - .load_account_exist(address, self.db) + .load_account_exist(address, &mut self.db) .map_err(|e| self.error = Some(e)) .ok() } @@ -90,7 +90,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { pub fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { let (acc, is_cold) = self .journaled_state - .load_code(address, self.db) + .load_code(address, &mut self.db) .map_err(|e| self.error = Some(e)) .ok()?; Some((acc.info.code.clone().unwrap(), is_cold)) @@ -114,7 +114,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { pub fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { // account is always warm. reference on that statement https://eips.ethereum.org/EIPS/eip-2929 see `Note 2:` self.journaled_state - .sload(address, index, self.db) + .sload(address, index, &mut self.db) .map_err(|e| self.error = Some(e)) .ok() } @@ -127,7 +127,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { value: U256, ) -> Option<(U256, U256, U256, bool)> { self.journaled_state - .sstore(address, index, value, self.db) + .sstore(address, index, value, &mut self.db) .map_err(|e| self.error = Some(e)) .ok() } @@ -143,7 +143,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { } /// Make create frame. - pub fn make_create_frame(&mut self, inputs: &CreateInputs) -> FrameOrResult { + pub fn make_create_frame(&mut self, spec_id: SpecId, inputs: &CreateInputs) -> FrameOrResult { // Prepare crate. let gas = Gas::new(inputs.gas_limit); @@ -185,7 +185,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { // Load account so it needs to be marked as warm for access list. if self .journaled_state - .load_account(created_address, self.db) + .load_account(created_address, &mut self.db) .map_err(|e| self.error = Some(e)) .is_err() { @@ -193,10 +193,11 @@ impl<'a, DB: Database> EvmContext<'a, DB> { } // create account, transfer funds and make the journal checkpoint. - let checkpoint = match self.journaled_state.create_account_checkpoint::( + let checkpoint = match self.journaled_state.create_account_checkpoint( inputs.caller, created_address, inputs.value, + spec_id, ) { Ok(checkpoint) => checkpoint, Err(e) => { @@ -245,7 +246,10 @@ impl<'a, DB: Database> EvmContext<'a, DB> { return return_result(InstructionResult::CallTooDeep); } - let account = match self.journaled_state.load_code(inputs.contract, self.db) { + let account = match self + .journaled_state + .load_code(inputs.contract, &mut self.db) + { Ok((account, _)) => account, Err(e) => { self.error = Some(e); @@ -269,7 +273,7 @@ impl<'a, DB: Database> EvmContext<'a, DB> { &inputs.transfer.source, &inputs.transfer.target, inputs.transfer.value, - self.db, + &mut self.db, ) { //println!("transfer error"); self.journaled_state.checkpoint_revert(checkpoint); diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index cd9670734f..56d804569e 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -18,6 +18,7 @@ use crate::{ use alloc::{boxed::Box, sync::Arc, vec::Vec}; use auto_impl::auto_impl; use core::{fmt, marker::PhantomData}; +use specification::SpecId; #[cfg(feature = "optimism")] use crate::optimism; @@ -25,18 +26,17 @@ use crate::optimism; /// EVM call stack limit. pub const CALL_STACK_LIMIT: u64 = 1024; -pub struct Evm<'a, SPEC: Spec + 'static, EXT, DB: Database> { +pub struct Evm<'a, EXT, DB: Database> { /// Context of execution, containing both EVM and external context. - pub context: Context<'a, EXT, DB>, + pub context: Context, /// Handler of EVM that contains all the logic. pub handler: Handler<'a, Self, EXT, DB>, - /// Phantom data - _phantomdata: PhantomData, + /// Specification id. + pub spec_id: SpecId, } -impl fmt::Debug for Evm<'_, SPEC, EXT, DB> +impl fmt::Debug for Evm<'_, EXT, DB> where - SPEC: Spec, EXT: fmt::Debug, DB: Database + fmt::Debug, DB::Error: fmt::Debug, @@ -105,18 +105,30 @@ impl<'a, SPEC: Spec, DB: Database> Evm<'a, SPEC, DB> { } } -impl<'a, SPEC: Spec, EXT, DB: Database> Evm<'a, SPEC, EXT, DB> +impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> where EXT: RegisterHandler<'a, DB, EXT>, { + pub fn new(context: Context, handler: Handler<'a, Self, EXT, DB>) -> Evm<'a, EXT, DB> { + let spec_id = handler.spec_id; + Evm { + context, + handler, + spec_id, + } + } + + /// TODO add spec_id as variable. pub fn new_with_spec( - db: &'a mut DB, - env: &'a mut Env, + db: DB, + env: Box, external: EXT, + handler: Handler<'a, Self, EXT, DB>, precompiles: Precompiles, ) -> Self { + let spec_id = handler.spec_id; let journaled_state = JournaledState::new( - SPEC::SPEC_ID, + spec_id, precompiles .addresses() .into_iter() @@ -125,13 +137,13 @@ where ); // temporary here. Factory should create handler and register external handles. - let mut handler = external.register_handler::(Handler::mainnet::()); + //let mut handler = external.register_handler::(Handler::mainnet::()); // temporary here. Factory should override this handle. - if env.cfg.is_beneficiary_reward_disabled() { - // do nothing - handler.reward_beneficiary = Arc::new(|_, _| Ok(())); - } + //if env.cfg.is_beneficiary_reward_disabled() { + // // do nothing + // handler.reward_beneficiary = Arc::new(|_, _| Ok(())); + //} Self { context: Context { @@ -146,8 +158,8 @@ where }, external, }, + spec_id, handler, - _phantomdata: PhantomData {}, } } @@ -226,17 +238,10 @@ where /// Pre verify transaction. pub fn preverify_transaction_inner(&mut self) -> Result<(), EVMError> { - let env = self.env(); + self.handler.validate_env(&self.context.evm.env)?; + let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env); - // Important: validate block before tx. - env.validate_block_env::()?; - env.validate_tx::()?; - - let initial_gas_spend = initial_tx_gas::( - &env.tx.data, - env.tx.transact_to.is_create(), - &env.tx.access_list, - ); + let env = self.env(); // Additional check to see if limit is big enough to cover initial gas. if initial_gas_spend > env.tx.gas_limit { @@ -249,7 +254,7 @@ where .context .evm .journaled_state - .load_account(tx_caller, self.context.evm.db) + .load_account(tx_caller, &mut self.context.evm.db) .map_err(EVMError::Database)?; self.context @@ -276,6 +281,7 @@ where let Some(enveloped_tx) = &env.tx.optimism.enveloped_tx else { panic!("[OPTIMISM] Failed to load enveloped transaction."); }; + // TODO specs let tx_l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx); // storage l1 block info for later use. @@ -286,22 +292,18 @@ where U256::ZERO }; - let initial_gas_spend = initial_tx_gas::( - &tx_data, - env.tx.transact_to.is_create(), - &env.tx.access_list, - ); + let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env); // load coinbase // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm - if SPEC::enabled(SHANGHAI) { + if self.spec_id.is_enabled_in(SHANGHAI) { self.context .evm .journaled_state .initial_account_load( self.context.evm.env.block.coinbase, &[], - self.context.evm.db, + &mut self.context.evm.db, ) .map_err(EVMError::Database)?; } @@ -313,6 +315,7 @@ where #[cfg(feature = "optimism")] if self.context.evm.env.cfg.optimism { + // TODO spec Evm::::commit_mint_value( tx_caller, self.context.evm.env.tx.optimism.mint, @@ -321,6 +324,7 @@ where )?; let is_deposit = self.context.evm.env.tx.optimism.source_hash.is_some(); + // TODO spec Evm::::remove_l1_cost( is_deposit, tx_caller, @@ -331,7 +335,7 @@ where } let (caller_account, _) = journal - .load_account(tx_caller, self.context.evm.db) + .load_account(tx_caller, &mut self.context.evm.db) .map_err(EVMError::Database)?; // Subtract gas costs from the caller's account. @@ -340,7 +344,7 @@ where U256::from(tx_gas_limit).saturating_mul(self.context.evm.env.effective_gas_price()); // EIP-4844 - if SPEC::enabled(CANCUN) { + if self.spec_id.is_enabled_in(CANCUN) { let data_fee = self .context .evm @@ -385,15 +389,16 @@ where 0..0, ) } - TransactTo::Create(scheme) => { - self.context.evm.make_create_frame::(&CreateInputs { + TransactTo::Create(scheme) => self.context.evm.make_create_frame( + self.spec_id, + &CreateInputs { caller: tx_caller, scheme, value: tx_value, init_code: tx_data, gas_limit: transact_gas_limit, - }) - } + }, + ), }; // Some only if it is create. let mut created_address = None; @@ -416,13 +421,13 @@ where // handle output of call/create calls. let mut gas = handler.call_return( - context.evm.env, + &mut context.evm.env, interpreter_result.result, interpreter_result.gas, ); // set refund. Refund amount depends on hardfork. - gas.set_refund(handler.calculate_gas_refund(context.evm.env, &gas) as i64); + gas.set_refund(handler.calculate_gas_refund(&mut context.evm.env, &gas) as i64); // Reimburse the caller handler.reimburse_caller(context, &gas)?; @@ -442,21 +447,23 @@ where } /// EVM transaction interface. -#[auto_impl(&mut, Box)] -pub trait Transact { +pub trait Transact { /// Run checks that could make transaction fail before call/create. - fn preverify_transaction(&mut self) -> Result<(), EVMError>; + fn preverify_transaction(&mut self) -> Result<(), EVMError>; /// Skip pre-verification steps and execute the transaction. - fn transact_preverified(&mut self) -> EVMResult; + fn transact_preverified(&mut self) -> EVMResult; /// Execute transaction by running pre-verification steps and then transaction itself. - fn transact(&mut self) -> EVMResult; + fn transact(&mut self) -> EVMResult; + + /// Into database and extern type. + fn into_db_ext(self) -> (DB, Box) + where + Self: Sized; } -impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler<'a, DB, EXT>, DB: Database> Transact - for Evm<'a, SPEC, EXT, DB> -{ +impl<'a, EXT: RegisterHandler<'a, DB, EXT>, DB: Database> Transact for Evm<'a, EXT, DB> { #[inline] fn preverify_transaction(&mut self) -> Result<(), EVMError> { self.preverify_transaction_inner() @@ -475,9 +482,14 @@ impl<'a, SPEC: Spec + 'static, EXT: RegisterHandler<'a, DB, EXT>, DB: Database> .and_then(|()| self.transact_preverified_inner()); self.handler.end(&mut self.context, output) } + + #[inline] + fn into_db_ext(self) -> (DB, Box) { + (self.context.evm.db, self.context.evm.env) + } } -impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Host for Evm<'a, SPEC, EXT, DB> { +impl<'a, EXT, DB: Database> Host for Evm<'a, EXT, DB> { fn env(&mut self) -> &mut Env { self.context.evm.env() } @@ -535,20 +547,22 @@ impl<'a, SPEC: Spec + 'static, EXT, DB: Database> Host for Evm<'a, SPEC, EXT, DB } /// Creates new EVM instance with erased types. -pub fn new_evm<'a, EXT: RegisterHandler<'a, DB, EXT> + 'a, DB: Database>( - env: &'a mut Env, - db: &'a mut DB, +pub fn new_evm<'a, EXT: RegisterHandler<'a, DB, EXT> + 'a, DB: Database + 'a>( + env: Box, + db: DB, external: EXT, -) -> Box + 'a> { +) -> Evm<'a, EXT, DB> { macro_rules! create_evm { - ($spec:ident) => { - Box::new(Evm::<'a, $spec, EXT, DB>::new_with_spec( + ($spec:ident) => {{ + let handler = external.register_handler::<$spec>(Handler::mainnet::<$spec>()); + Evm::new_with_spec( db, env, external, + handler, Precompiles::new(revm_precompile::SpecId::from_spec_id($spec::SPEC_ID)).clone(), - )) - }; + ) + }}; } use specification::*; diff --git a/crates/revm/src/evm_builder.rs b/crates/revm/src/evm_builder.rs new file mode 100644 index 0000000000..8ff7296162 --- /dev/null +++ b/crates/revm/src/evm_builder.rs @@ -0,0 +1,41 @@ +use core::marker::PhantomData; + +use revm_interpreter::primitives::SpecId; + +use crate::{ + db::{Database, EmptyDB}, + handler::{MainnetHandle, RegisterHandler}, + primitives::Spec, + Evm, EvmContext, Handler, +}; + +// /// Evm Builder +// pub struct EvmBuilder<'a, SPEC: Spec + 'static, EXT, DB: Database + 'a> { +// database: DB, +// evm: EvmContext, +// handler: Handler<'a, Evm<'a, SPEC, EXT, DB>, EXT, DB>, +// spec_id: SpecId, +// } + +// impl<'a, EXT: RegisterHandler<'a, DB, EXT>, DB: Database + 'a> EvmBuilder<'a, SPEC, EXT, DB> { +// pub fn new() -> EvmBuilder<'a, MainnetHandle, EmptyDB> { +// EvmBuilder { +// database: (), +// //evm: EvmContext::default(), +// //external: MainnetHandle::default(), +// spec_id: SpecId::LATEST, +// } +// } + +// pub fn db>( +// self, +// db: ODB, +// ) -> EvmBuilder<'a, OEXT, ODB> { +// EvmBuilder { +// database: db, +// //evm: self.evm, +// //external: MainnetHandle::default(), +// spec_id: self.spec_id, +// } +// } +// } diff --git a/crates/revm/src/evm_factory.rs b/crates/revm/src/evm_factory.rs index c6244814cb..32e9189719 100644 --- a/crates/revm/src/evm_factory.rs +++ b/crates/revm/src/evm_factory.rs @@ -1,9 +1,9 @@ use crate::{ db::{Database, DatabaseCommit, DatabaseRef}, evm::{new_evm, Transact}, - handler::MainnetHandle, + handler::{InspectorHandle, MainnetHandle, RegisterHandler}, primitives::{db::WrapDatabaseRef, EVMError, EVMResult, Env, ExecutionResult, ResultAndState}, - Inspector, + Evm, Inspector, }; /// Struct that takes Database and enabled transact to update state directly to database. @@ -36,7 +36,7 @@ use crate::{ /// #[derive(Clone, Debug)] pub struct EvmFactory { - pub env: Env, + pub env: Box, pub db: Option, } @@ -70,110 +70,152 @@ impl EvmFactory { } impl EvmFactory { + pub fn execute_evm< + 'a, + OUT, + EXT: RegisterHandler<'a, DB, EXT> + 'a, + FN: Fn(&mut Evm<'a, EXT, DB>) -> OUT, + >( + &mut self, + external: EXT, + exec: FN, + ) -> OUT + where + DB: 'a, + { + let Some(db) = self.db.take() else { + panic!("Database needs to be set"); + }; + let env = core::mem::take(&mut self.env); + let mut evm = new_evm::(env, db, external); + let res = exec(&mut evm); + + let (db, env) = evm.into_db_ext(); + self.env = env; + self.db = Some(db); + + res + } /// Do checks that could make transaction fail before call/create pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { - if let Some(db) = self.db.as_mut() { - new_evm(&mut self.env, db, MainnetHandle::default()).preverify_transaction() - } else { - panic!("Database needs to be set"); - } + self.execute_evm(MainnetHandle::default(), |evm| evm.preverify_transaction()) } /// Skip preverification steps and execute transaction without writing to DB, return change /// state. pub fn transact_preverified(&mut self) -> EVMResult { - if let Some(db) = self.db.as_mut() { - new_evm(&mut self.env, db, MainnetHandle::default()).transact_preverified() - } else { - panic!("Database needs to be set"); - } + self.execute_evm(MainnetHandle::default(), |evm| evm.transact_preverified()) } /// Execute transaction without writing to DB, return change state. pub fn transact(&mut self) -> EVMResult { - if let Some(db) = self.db.as_mut() { - new_evm::<_, DB>(&mut self.env, db, MainnetHandle::default()).transact() - } else { - panic!("Database needs to be set"); - } + self.execute_evm(MainnetHandle::default(), |evm| evm.transact()) } /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect>(&mut self, mut inspector: INSP) -> EVMResult { - if let Some(db) = self.db.as_mut() { - new_evm( - &mut self.env, - db, - MainnetHandle::default(), - //Some(&mut inspector), - ) - .transact() - } else { - panic!("Database needs to be set"); - } + pub fn inspect>(&mut self, inspector: INSP) -> EVMResult { + let insp = InspectorHandle::new(inspector); + self.execute_evm(insp, |evm| evm.transact()) } } -impl<'a, DB: DatabaseRef> EvmFactory { +impl EvmFactory { + pub fn execute_evm_ref< + 'a, + OUT:'a, + EXT: RegisterHandler<'a, WrapDatabaseRef<&'a DB>, EXT> + 'a, + FN: Fn(&mut Evm<'a, EXT, WrapDatabaseRef<&'a DB>>) -> OUT, + >( + &'a self, + external: EXT, + exec: FN, + ) -> OUT + where + DB: 'a, + { + //unimplemented!(); + let Some(db) = self.db.as_ref() else { + panic!("Database needs to be set"); + }; + let env = self.env.clone(); + let mut evm = new_evm::>(env, WrapDatabaseRef(db), external); + let res = exec(&mut evm); + + let (db, env) = evm.into_db_ext(); + //self.env = env; + + res + } /// Do checks that could make transaction fail before call/create pub fn preverify_transaction_ref(&self) -> Result<(), EVMError> { - if let Some(db) = self.db.as_ref() { - new_evm( - &mut self.env.clone(), - &mut WrapDatabaseRef(db), - MainnetHandle::default(), - ) - .preverify_transaction() - } else { - panic!("Database needs to be set"); - } + // if let Some(db) = self.db.as_ref() { + // new_evm( + // &mut self.env.clone(), + // &mut WrapDatabaseRef(db), + // MainnetHandle::default(), + // ) + // .preverify_transaction() + // } else { + // panic!("Database needs to be set"); + // } + + self.execute_evm_ref(MainnetHandle::default(), |mut evm| { + evm.preverify_transaction() + }) } /// Skip preverification steps and execute transaction /// without writing to DB, return change state. pub fn transact_preverified_ref(&self) -> EVMResult { - if let Some(db) = self.db.as_ref() { - new_evm::( - &mut self.env.clone(), - &mut WrapDatabaseRef(db), - MainnetHandle::default(), - ) - .transact_preverified() - } else { - panic!("Database needs to be set"); - } + // if let Some(db) = self.db.as_ref() { + // new_evm::( + // &mut self.env.clone(), + // &mut WrapDatabaseRef(db), + // MainnetHandle::default(), + // ) + // .transact_preverified() + // } else { + // panic!("Database needs to be set"); + // } + + self.execute_evm_ref(MainnetHandle::default(), |mut evm| { + evm.transact_preverified() + }) } /// Execute transaction without writing to DB, return change state. pub fn transact_ref(&self) -> EVMResult { - if let Some(db) = self.db.as_ref() { - new_evm::( - &mut self.env.clone(), - &mut WrapDatabaseRef(db), - MainnetHandle::default(), - ) - .transact() - } else { - panic!("Database needs to be set"); - } + // if let Some(db) = self.db.as_ref() { + // new_evm::( + // &mut self.env.clone(), + // &mut WrapDatabaseRef(db), + // MainnetHandle::default(), + // ) + // .transact() + // } else { + // panic!("Database needs to be set"); + // } + self.execute_evm_ref(MainnetHandle::default(), |mut evm| evm.transact()) } /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect_ref>>( + pub fn inspect_ref<'a, I: Inspector> + 'a>( &'a self, - mut inspector: I, + inspector: I, ) -> EVMResult { - if let Some(db) = self.db.as_ref() { - new_evm( - &mut self.env.clone(), - &mut WrapDatabaseRef(db), - MainnetHandle::default(), - //Some(&mut inspector), - ) - .transact() - } else { - panic!("Database needs to be set"); - } + // if let Some(db) = self.db.as_ref() { + // new_evm( + // &mut self.env.clone(), + // &mut WrapDatabaseRef(db), + // InspectorHandle::new(inspector), + // ) + // .transact() + // } else { + // panic!("Database needs to be set"); + // } + + let insp = InspectorHandle::new(inspector); + self.execute_evm_ref(insp, |mut evm| evm.transact()) } } @@ -184,7 +226,7 @@ impl EvmFactory { } /// Creates a new [EVM] instance with the given environment. - pub fn with_env(env: Env) -> Self { + pub fn with_env(env: Box) -> Self { Self { env, db: None } } diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index b73fecade9..04fa6dcfad 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -8,7 +8,9 @@ use revm_precompile::{Address, Bytes, B256}; use crate::{ interpreter::{Gas, InstructionResult}, - primitives::{db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec}, + primitives::{ + db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec, SpecId, + }, CallStackFrame, Context, }; use alloc::sync::Arc; @@ -22,9 +24,8 @@ use revm_interpreter::{ pub type CallReturnHandle<'a> = Arc Gas + 'a>; /// Reimburse the caller with ethereum it didn't spent. -type ReimburseCallerHandle<'a, EXT, DB> = Arc< - dyn Fn(&mut Context<'_, EXT, DB>, &Gas) -> EVMResultGeneric<(), ::Error> + 'a, ->; +type ReimburseCallerHandle<'a, EXT, DB> = + Arc, &Gas) -> EVMResultGeneric<(), ::Error> + 'a>; /// Reward beneficiary with transaction rewards. type RewardBeneficiaryHandle<'a, EXT, DB> = ReimburseCallerHandle<'a, EXT, DB>; @@ -35,7 +36,7 @@ pub type CalculateGasRefundHandle<'a> = Arc u64 + 'a>; /// Main return handle, takes state from journal and transforms internal result to external. pub type MainReturnHandle<'a, EXT, DB> = Arc< dyn Fn( - &mut Context<'_, EXT, DB>, + &mut Context, InstructionResult, Output, &Gas, @@ -49,7 +50,7 @@ pub type MainReturnHandle<'a, EXT, DB> = Arc< pub type FrameReturnHandle<'a, EXT, DB> = Arc< dyn Fn( // context - &mut Context<'_, EXT, DB>, + &mut Context, // returned frame Box, // parent frame if it exist. @@ -64,14 +65,14 @@ pub type FrameReturnHandle<'a, EXT, DB> = Arc< /// Call to the host from Interpreter to save the log. pub type HostLogHandle<'a, EXT, DB> = - Arc, Address, Vec, Bytes) + 'a>; + Arc, Address, Vec, Bytes) + 'a>; /// Call to the host from Interpreter to selfdestruct account. /// /// After CANCUN hardfork original contract will stay the same but the value will /// be transfered to the target. pub type HostSelfdestructHandle<'a, EXT, DB> = - Arc, Address, Address) -> Option + 'a>; + Arc, Address, Address) -> Option + 'a>; /// End handle, takes result and state and returns final result. /// This will be called after all the other handlers. @@ -79,7 +80,7 @@ pub type HostSelfdestructHandle<'a, EXT, DB> = /// It is useful for catching errors and returning them in a different way. pub type EndHandle<'a, EXT, DB> = Arc< dyn Fn( - &mut Context<'_, EXT, DB>, + &mut Context, Result::Error>>, ) -> Result::Error>> + 'a, @@ -88,7 +89,7 @@ pub type EndHandle<'a, EXT, DB> = Arc< /// Handle sub call. pub type FrameSubCallHandle<'a, EXT, DB> = Arc< dyn Fn( - &mut Context<'_, EXT, DB>, + &mut Context, Box, &mut CallStackFrame, &mut SharedMemory, @@ -100,17 +101,30 @@ pub type FrameSubCallHandle<'a, EXT, DB> = Arc< /// Handle sub create. pub type FrameSubCreateHandle<'a, EXT, DB> = Arc< dyn Fn( - &mut Context<'_, EXT, DB>, + &mut Context, &mut CallStackFrame, Box, ) -> Option> + 'a, >; +/// Handle that validates env. +pub type ValidateEnvHandle<'a, DB> = + Arc Result<(), EVMError<::Error>> + 'a>; + +/// Initial gas calculation handle +pub type InitialTxGasHandle<'a> = Arc u64 + 'a>; + /// Handler acts as a proxy and allow to define different behavior for different /// sections of the code. This allows nice integration of different chains or /// to disable some mainnet behavior. -pub struct Handler<'a, H: Host+'a, EXT, DB: Database> { +pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { + /// Specification ID. + pub spec_id: SpecId, + /// Initial tx gas. + pub initial_tx_gas: InitialTxGasHandle<'a>, + /// Validate Env + pub validate_env: ValidateEnvHandle<'a, DB>, /// Instruction table type. pub instruction_table: InstructionTables<'a, H>, // Uses env, call result and returned gas from the call to determine the gas @@ -141,8 +155,11 @@ pub struct Handler<'a, H: Host+'a, EXT, DB: Database> { impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// Handler for the mainnet - pub fn mainnet() -> Self { + pub fn mainnet() -> Self { Self { + spec_id: SPEC::SPEC_ID, + initial_tx_gas: Arc::new(mainnet::preexecution::initial_tx_gas::), + validate_env: Arc::new(mainnet::preexecution::validate_env::), call_return: Arc::new(mainnet::handle_call_return::), calculate_gas_refund: Arc::new(mainnet::calculate_gas_refund::), reimburse_caller: Arc::new(mainnet::handle_reimburse_caller::), @@ -160,25 +177,6 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { } } - /// Handler for the optimism - #[cfg(feature = "optimism")] - pub fn optimism() -> Self { - Self { - call_return: optimism::handle_call_return::, - calculate_gas_refund: optimism::calculate_gas_refund::, - // we reinburse caller the same was as in mainnet. - // Refund is calculated differently then mainnet. - reimburse_caller: mainnet::handle_reimburse_caller::, - reward_beneficiary: optimism::reward_beneficiary::, - // In case of halt of deposit transaction return Error. - main_return: optimism::main_return::, - end: optimism::end_handle::, - frame_return: Arc::new(mainnet::frames::handle_frame_return::), - host_log: Arc::new(mainnet::host::handle_host_log::), - host_selfdestruct: Arc::new(mainnet::host::handle_selfdestruct::), - } - } - /// Handle call return, depending on instruction result gas will be reimbursed or not. pub fn call_return(&self, env: &Env, call_result: InstructionResult, returned_gas: Gas) -> Gas { (self.call_return)(env, call_result, returned_gas) @@ -187,7 +185,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// Reimburse the caller with gas that were not spend. pub fn reimburse_caller( &self, - context: &mut Context<'_, EXT, DB>, + context: &mut Context, gas: &Gas, ) -> Result<(), EVMError> { (self.reimburse_caller)(context, gas) @@ -201,7 +199,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// Reward beneficiary pub fn reward_beneficiary( &self, - context: &mut Context<'_, EXT, DB>, + context: &mut Context, gas: &Gas, ) -> Result<(), EVMError> { (self.reward_beneficiary)(context, gas) @@ -210,7 +208,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// Main return. pub fn main_return( &self, - context: &mut Context<'_, EXT, DB>, + context: &mut Context, call_result: InstructionResult, output: Output, gas: &Gas, @@ -221,7 +219,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// End handler. pub fn end( &self, - context: &mut Context<'_, EXT, DB>, + context: &mut Context, end_output: Result>, ) -> Result> { (self.end)(context, end_output) @@ -230,7 +228,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// Call frame sub call handler. pub fn frame_sub_call( &self, - context: &mut Context<'_, EXT, DB>, + context: &mut Context, inputs: Box, curent_stack_frame: &mut CallStackFrame, shared_memory: &mut SharedMemory, @@ -247,7 +245,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { pub fn frame_sub_create( &self, - context: &mut Context<'_, EXT, DB>, + context: &mut Context, curent_stack_frame: &mut CallStackFrame, inputs: Box, ) -> Option> { @@ -257,7 +255,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// Frame return pub fn frame_return( &self, - context: &mut Context<'_, EXT, DB>, + context: &mut Context, child_stack_frame: Box, parent_stack_frame: Option<&mut Box>, shared_memory: &mut SharedMemory, @@ -275,7 +273,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// Call host log handle. pub fn host_log( &self, - context: &mut Context<'_, EXT, DB>, + context: &mut Context, address: Address, topics: Vec, data: Bytes, @@ -286,10 +284,20 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// Call host selfdestruct handle. pub fn host_selfdestruct( &self, - context: &mut Context<'_, EXT, DB>, + context: &mut Context, address: Address, target: Address, ) -> Option { (self.host_selfdestruct)(context, address, target) } + + /// Validate env. + pub fn validate_env(&self, env: &Env) -> Result<(), EVMError> { + (self.validate_env)(env) + } + + /// Initial gas + pub fn initial_tx_gas(&self, env: &Env) -> u64 { + (self.initial_tx_gas)(env) + } } diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index 94ca0e4fbf..427776250d 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -1,5 +1,7 @@ //! Mainnet related handlers. + +pub mod preexecution; pub mod frames; pub mod host; pub mod main; @@ -37,7 +39,7 @@ pub fn handle_call_return( #[inline] pub fn handle_reimburse_caller( - context: &mut Context<'_, EXT, DB>, + context: &mut Context, gas: &Gas, ) -> Result<(), EVMError> { let caller = context.evm.env.tx.caller; @@ -47,7 +49,7 @@ pub fn handle_reimburse_caller( let (caller_account, _) = context .evm .journaled_state - .load_account(caller, context.evm.db) + .load_account(caller, &mut context.evm.db) .map_err(EVMError::Database)?; caller_account.info.balance = caller_account @@ -61,7 +63,7 @@ pub fn handle_reimburse_caller( /// Reward beneficiary with gas fee. #[inline] pub fn reward_beneficiary( - context: &mut Context<'_, EXT, DB>, + context: &mut Context, gas: &Gas, ) -> Result<(), EVMError> { let beneficiary = context.evm.env.block.coinbase; @@ -78,7 +80,7 @@ pub fn reward_beneficiary( let (coinbase_account, _) = context .evm .journaled_state - .load_account(beneficiary, context.evm.db) + .load_account(beneficiary, &mut context.evm.db) .map_err(EVMError::Database)?; coinbase_account.mark_touch(); diff --git a/crates/revm/src/handler/mainnet/frames.rs b/crates/revm/src/handler/mainnet/frames.rs index e5e45c0a30..3e8a08df1d 100644 --- a/crates/revm/src/handler/mainnet/frames.rs +++ b/crates/revm/src/handler/mainnet/frames.rs @@ -9,7 +9,7 @@ use core::ops::Range; /// Handle frame return. pub fn handle_frame_return( - context: &mut Context<'_, EXT, DB>, + context: &mut Context, child_stack_frame: Box, parent_stack_frame: Option<&mut Box>, shared_memory: &mut SharedMemory, @@ -47,7 +47,7 @@ pub fn handle_frame_return( /// Handle frame sub call. pub fn handle_frame_sub_call( - context: &mut Context<'_, EXT, DB>, + context: &mut Context, inputs: Box, curent_stack_frame: &mut CallStackFrame, shared_memory: &mut SharedMemory, @@ -71,11 +71,11 @@ pub fn handle_frame_sub_call( /// Handle frame sub create. pub fn handle_frame_sub_create( - context: &mut Context<'_, EXT, DB>, + context: &mut Context, curent_stack_frame: &mut CallStackFrame, inputs: Box, ) -> Option> { - match context.evm.make_create_frame::(&inputs) { + match context.evm.make_create_frame(SPEC::SPEC_ID, &inputs) { FrameOrResult::Frame(new_frame) => Some(new_frame), FrameOrResult::Result(result) => { // insert result of the failed creation of create CallStackFrame. diff --git a/crates/revm/src/handler/mainnet/host.rs b/crates/revm/src/handler/mainnet/host.rs index 758cc02f77..684135f161 100644 --- a/crates/revm/src/handler/mainnet/host.rs +++ b/crates/revm/src/handler/mainnet/host.rs @@ -8,7 +8,7 @@ use alloc::vec::Vec; /// Handle host log call. pub fn handle_host_log( - context: &mut Context<'_, EXT, DB>, + context: &mut Context, address: Address, topics: Vec, data: Bytes, @@ -23,7 +23,7 @@ pub fn handle_host_log( /// Handle host selfdestruct call. pub fn handle_selfdestruct( - context: &mut Context<'_, EXT, DB>, + context: &mut Context, address: Address, target: Address, ) -> Option { diff --git a/crates/revm/src/handler/mainnet/main.rs b/crates/revm/src/handler/mainnet/main.rs index a74f061aa9..c3f4d49100 100644 --- a/crates/revm/src/handler/mainnet/main.rs +++ b/crates/revm/src/handler/mainnet/main.rs @@ -11,7 +11,7 @@ use crate::{ /// Main return handle, returns the output of the transaction. #[inline] pub fn main_return( - context: &mut Context<'_, EXT, DB>, + context: &mut Context, call_result: InstructionResult, output: Output, gas: &Gas, @@ -57,7 +57,7 @@ pub fn main_return( /// Mainnet end handle does not change the output. #[inline] pub fn end_handle( - _context: &mut Context<'_, EXT, DB>, + _context: &mut Context, evm_output: Result>, ) -> Result> { evm_output diff --git a/crates/revm/src/handler/mainnet/preexecution.rs b/crates/revm/src/handler/mainnet/preexecution.rs new file mode 100644 index 0000000000..c5fad18533 --- /dev/null +++ b/crates/revm/src/handler/mainnet/preexecution.rs @@ -0,0 +1,24 @@ +use revm_interpreter::gas; + +use crate::{ + interpreter::{Gas, InstructionResult, SuccessOrHalt}, + primitives::{db::Database, EVMError, ExecutionResult, Output, ResultAndState}, + primitives::{Env, Spec}, + Context, +}; + +/// Validate environment for the mainnet. +pub fn validate_env(env: &Env) -> Result<(), EVMError> { + // Important: validate block before tx. + env.validate_block_env::()?; + env.validate_tx::()?; + Ok(()) +} + +pub fn initial_tx_gas(env: &Env) -> u64 { + let input = &env.tx.data; + let is_create = env.tx.transact_to.is_create(); + let access_list = &env.tx.access_list; + + gas::initial_tx_gas::(input, is_create, access_list) +} diff --git a/crates/revm/src/handler/optimism.rs b/crates/revm/src/handler/optimism.rs index b90dc9bad0..47dfde8f2e 100644 --- a/crates/revm/src/handler/optimism.rs +++ b/crates/revm/src/handler/optimism.rs @@ -12,6 +12,28 @@ use crate::{ }; use core::ops::Mul; +pub struct OptimismHandle {} + +impl<'a, DB: Database> RegisterHandler<'a, DB, ()> for OptimismHandle { + fn register_handler( + &self, + mut handler: Handler<'a, Evm<'a, SPEC, (), DB>, (), DB>, + ) -> Handler<'a, Evm<'a, SPEC, (), DB>, (), DB> + where + DB: 'a, + (): Sized, + { + handler.call_return = Arc::new(optimism::handle_call_return::); + handler.calculate_gas_refund = Arc::new(optimism::calculate_gas_refund::); + // we reinburse caller the same was as in mainnet. + // Refund is calculated differently then mainnet. + handler.reward_beneficiary = Arc::new(reward_beneficiary::); + // In case of halt of deposit transaction return Error. + handler.main_return = Arc::new(optimism::main_return::); + handler.end = Arc::new(optimism::end_handle::); + } +} + /// Handle output of the transaction #[inline] pub fn handle_call_return( @@ -92,7 +114,7 @@ pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { /// Reward beneficiary with gas fee. #[inline] pub fn reward_beneficiary( - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, gas: &Gas, ) -> Result<(), EVMError> { let is_deposit = context.env.cfg.optimism && context.env.tx.optimism.source_hash.is_some(); @@ -146,7 +168,7 @@ pub fn reward_beneficiary( /// Main return handle, returns the output of the transaction. #[inline] pub fn main_return( - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, call_result: InstructionResult, output: Output, gas: &Gas, @@ -171,7 +193,7 @@ pub fn main_return( /// Deposit transaction can't be reverted and is always successful. #[inline] pub fn end_handle( - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, evm_output: Result>, ) -> Result> { evm_output.or_else(|err| { diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index d0aa5e9742..a2bd220c08 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -16,8 +16,8 @@ use core::marker::PhantomData; pub trait RegisterHandler<'a, DB: Database, EXT> { fn register_handler( &self, - handler: Handler<'a, Evm<'a, SPEC, EXT, DB>, EXT, DB>, - ) -> Handler<'a, Evm<'a, SPEC, EXT, DB>, EXT, DB> + handler: Handler<'a, Evm<'a, EXT, DB>, EXT, DB>, + ) -> Handler<'a, Evm<'a, EXT, DB>, EXT, DB> where DB: 'a, EXT: Sized, @@ -32,28 +32,13 @@ pub struct MainnetHandle {} impl<'a, DB: Database> RegisterHandler<'a, DB, Self> for MainnetHandle {} -pub struct OptimismHandle {} - -impl<'a, DB: Database> RegisterHandler<'a, DB, ()> for OptimismHandle { - fn register_handler( - &self, - handler: Handler<'a, Evm<'a, SPEC, (), DB>, (), DB>, - ) -> Handler<'a, Evm<'a, SPEC, (), DB>, (), DB> - where - DB: 'a, - (): Sized, - { - Handler::mainnet::() - } -} - pub struct InspectorHandle<'a, DB: Database, GI: GetInspector<'a, DB>> { pub inspector: GI, pub _phantomdata: PhantomData<&'a DB>, } impl<'a, DB: Database, GI: GetInspector<'a, DB>> InspectorHandle<'a, DB, GI> { - fn new(inspector: GI) -> Self { + pub fn new(inspector: GI) -> Self { Self { inspector, _phantomdata: PhantomData, @@ -61,6 +46,12 @@ impl<'a, DB: Database, GI: GetInspector<'a, DB>> InspectorHandle<'a, DB, GI> { } } +impl<'a, DB: Database, INSP: Inspector> GetInspector<'a, DB> for INSP { + fn get(&mut self) -> &mut dyn Inspector { + self + } +} + pub trait GetInspector<'a, DB: Database> { fn get(&mut self) -> &mut dyn Inspector; } @@ -70,19 +61,17 @@ impl<'handler, DB: Database, INS: GetInspector<'handler, DB>> RegisterHandler<'h { fn register_handler( &self, - mut handler: Handler<'handler, Evm<'handler, SPEC, Self, DB>, Self, DB>, - ) -> Handler<'handler, Evm<'handler, SPEC, Self, DB>, Self, DB> + mut handler: Handler<'handler, Evm<'handler, Self, DB>, Self, DB>, + ) -> Handler<'handler, Evm<'handler, Self, DB>, Self, DB> where Self: Sized, DB: 'handler, { - // flag instruction table that is going to be wrapped. - let flat_instruction_table = make_instruction_table::< - Evm<'handler, SPEC, InspectorHandle<'handler, DB, INS>, DB>, - SPEC, - >(); + // Every instruction inside flat table that is going to be wrapped by inspector calls. + let flat_instruction_table = + make_instruction_table::, DB>, SPEC>(); - // wrap instruction table with inspector handle. + // wrap instruction table with inspector handles. handler.instruction_table = InstructionTables::Boxed(Arc::new(core::array::from_fn(|i| { inspector_instruction(flat_instruction_table[i]) }))); @@ -100,7 +89,7 @@ impl<'handler, DB: Database, INS: GetInspector<'handler, DB>> RegisterHandler<'h return None; } - match context.evm.make_create_frame::(&inputs) { + match context.evm.make_create_frame(SPEC::SPEC_ID, &inputs) { FrameOrResult::Frame(new_frame) => Some(new_frame), FrameOrResult::Result(result) => { let (result, address) = context.external.inspector.get().create_end( @@ -159,8 +148,7 @@ impl<'handler, DB: Database, INS: GetInspector<'handler, DB>> RegisterHandler<'h } else { inspector.call_end(&mut context.evm, result) }; - let output = old_handle(context, child, parent, memory, result); - output + old_handle(context, child, parent, memory, result) }, ); @@ -189,22 +177,3 @@ impl<'handler, DB: Database, INS: GetInspector<'handler, DB>> RegisterHandler<'h handler } } - -// pub struct ExternalData { -// pub flagg: bool, -// pub phantom: PhantomData, -// } - -// impl RegisterHandler for ExternalData { -// fn register_handler<'a, SPEC: Spec>( -// &self, -// mut handler: Handler<'a, Evm<'a, SPEC, Self, DB>, Self, DB>, -// ) -> Handler<'a, Evm<'a, SPEC, Self, DB>, Self, DB> -// where -// DB: 'a, -// { -// let old_handle = handler.reimburse_caller.clone(); -// handler.reimburse_caller = Arc::new(move |data, gas| old_handle(data, gas)); -// handler -// } -// } diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index ea08ccb1ae..556e3cbad7 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -8,9 +8,9 @@ use crate::{ use auto_impl::auto_impl; #[cfg(feature = "std")] -//mod customprinter; +mod customprinter; #[cfg(all(feature = "std", feature = "serde"))] -//mod eip3155; +mod eip3155; mod gas; mod instruction; mod noop; @@ -20,9 +20,9 @@ use revm_interpreter::InterpreterResult; /// [Inspector] implementations. pub mod inspectors { #[cfg(feature = "std")] - //pub use super::customprinter::CustomPrintTracer; + pub use super::customprinter::CustomPrintTracer; #[cfg(all(feature = "std", feature = "serde"))] - //pub use super::eip3155::TracerEip3155; + pub use super::eip3155::TracerEip3155; pub use super::gas::GasInspector; pub use super::noop::NoOpInspector; } @@ -35,11 +35,7 @@ pub trait Inspector { /// If `interp.instruction_result` is set to anything other than [InstructionResult::Continue] then the execution of the interpreter /// is skipped. #[inline] - fn initialize_interp( - &mut self, - interp: &mut Interpreter, - context: &mut EvmContext<'_, DB>, - ) { + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let _ = interp; let _ = context; } @@ -53,7 +49,7 @@ pub trait Inspector { /// /// To get the current opcode, use `interp.current_opcode()`. #[inline] - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let _ = interp; let _ = context; } @@ -62,7 +58,7 @@ pub trait Inspector { #[inline] fn log( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, address: &Address, topics: &[B256], data: &Bytes, @@ -78,7 +74,7 @@ pub trait Inspector { /// Setting `interp.instruction_result` to anything other than [InstructionResult::Continue] alters the execution /// of the interpreter. #[inline] - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let _ = interp; let _ = context; } @@ -89,7 +85,7 @@ pub trait Inspector { #[inline] fn call( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, inputs: &mut CallInputs, ) -> Option<(InterpreterResult, Range)> { let _ = context; @@ -104,7 +100,7 @@ pub trait Inspector { #[inline] fn call_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, ) -> InterpreterResult { let _ = context; @@ -117,7 +113,7 @@ pub trait Inspector { #[inline] fn create( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, inputs: &mut CreateInputs, ) -> Option<(InterpreterResult, Option
)> { let _ = context; @@ -132,7 +128,7 @@ pub trait Inspector { #[inline] fn create_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs index 6e7010540b..50e555e733 100644 --- a/crates/revm/src/inspector/customprinter.rs +++ b/crates/revm/src/inspector/customprinter.rs @@ -19,13 +19,13 @@ pub struct CustomPrintTracer { } impl Inspector for CustomPrintTracer { - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, Self, DB>) { + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.initialize_interp(interp, context); } // get opcode by calling `interp.contract.opcode(interp.program_counter())`. // all other information can be obtained from interp. - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { let opcode = interp.current_opcode(); let opcode_str = opcode::OPCODE_JUMPMAP[opcode as usize]; @@ -50,13 +50,13 @@ impl Inspector for CustomPrintTracer { self.gas_inspector.step(interp, context); } - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.step_end(interp, context); } fn call_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, ) -> InterpreterResult { self.gas_inspector.call_end(context, result) @@ -64,7 +64,7 @@ impl Inspector for CustomPrintTracer { fn create_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { @@ -73,7 +73,7 @@ impl Inspector for CustomPrintTracer { fn call( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, inputs: &mut CallInputs, ) -> Option<(InterpreterResult, Range)> { println!( @@ -89,7 +89,7 @@ impl Inspector for CustomPrintTracer { fn create( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, inputs: &mut CreateInputs, ) -> Option<(InterpreterResult, Option
)> { println!( diff --git a/crates/revm/src/inspector/eip3155.rs b/crates/revm/src/inspector/eip3155.rs index c0803282dc..91091b7bca 100644 --- a/crates/revm/src/inspector/eip3155.rs +++ b/crates/revm/src/inspector/eip3155.rs @@ -49,13 +49,13 @@ impl TracerEip3155 { } impl Inspector for TracerEip3155 { - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.initialize_interp(interp, context); } // get opcode by calling `interp.contract.opcode(interp.program_counter())`. // all other information can be obtained from interp. - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.step(interp, context); self.stack = interp.stack.clone(); self.pc = interp.program_counter(); @@ -64,7 +64,7 @@ impl Inspector for TracerEip3155 { self.gas = interp.gas.remaining(); } - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.step_end(interp, context); if self.skip { self.skip = false; @@ -76,7 +76,7 @@ impl Inspector for TracerEip3155 { fn call( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, _inputs: &mut CallInputs, ) -> Option<(InterpreterResult, Range)> { None @@ -84,7 +84,7 @@ impl Inspector for TracerEip3155 { fn call_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, ) -> InterpreterResult { let result = self.gas_inspector.call_end(context, result); @@ -105,7 +105,7 @@ impl Inspector for TracerEip3155 { fn create( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, _inputs: &mut CreateInputs, ) -> Option<(InterpreterResult, Option
)> { None @@ -113,7 +113,7 @@ impl Inspector for TracerEip3155 { fn create_end( &mut self, - context: &mut EvmContext<'_, DB>, + context: &mut EvmContext, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 1344ce573e..5c21c21583 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -24,11 +24,11 @@ impl GasInspector { } } -impl Inspector for GasInspector { +impl Inspector for GasInspector { fn initialize_interp( &mut self, interp: &mut crate::interpreter::Interpreter, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, ) { self.gas_remaining = interp.gas.limit(); } @@ -36,7 +36,7 @@ impl Inspector for GasInspector { fn step_end( &mut self, interp: &mut crate::interpreter::Interpreter, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, ) { let last_gas = core::mem::replace(&mut self.gas_remaining, interp.gas.remaining()); self.last_gas_cost = last_gas.saturating_sub(self.last_gas_cost); @@ -44,7 +44,7 @@ impl Inspector for GasInspector { fn call_end( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, mut result: InterpreterResult, ) -> InterpreterResult { if result.result.is_error() { @@ -56,7 +56,7 @@ impl Inspector for GasInspector { fn create_end( &mut self, - _context: &mut EvmContext<'_, DB>, + _context: &mut EvmContext, result: InterpreterResult, address: Option
, ) -> (InterpreterResult, Option
) { @@ -64,137 +64,137 @@ impl Inspector for GasInspector { } } -// #[cfg(test)] -// mod tests { -// use core::ops::Range; - -// use crate::{ -// inspectors::GasInspector, -// interpreter::{CallInputs, CreateInputs, Interpreter, InterpreterResult}, -// primitives::{Address, Bytes, B256}, -// Database, EvmContext, Inspector, -// }; - -// #[derive(Default, Debug)] -// struct StackInspector { -// pc: usize, -// gas_inspector: GasInspector, -// gas_remaining_steps: Vec<(usize, u64)>, -// } - -// impl Inspector for StackInspector { -// fn initialize_interp( -// &mut self, -// interp: &mut Interpreter, -// context: &mut EvmContext<'_, DB>, -// ) { -// self.gas_inspector.initialize_interp(interp, context); -// } - -// fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { -// self.pc = interp.program_counter(); -// self.gas_inspector.step(interp, context); -// } - -// fn log( -// &mut self, -// context: &mut EvmContext<'_, DB>, -// address: &Address, -// topics: &[B256], -// data: &Bytes, -// ) { -// self.gas_inspector.log(context, address, topics, data); -// } - -// fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<'_, DB>) { -// self.gas_inspector.step_end(interp, context); -// self.gas_remaining_steps -// .push((self.pc, self.gas_inspector.gas_remaining())); -// } - -// fn call( -// &mut self, -// context: &mut EvmContext<'_, DB>, -// call: &mut CallInputs, -// ) -> Option<(InterpreterResult, Range)> { -// self.gas_inspector.call(context, call) -// } - -// fn call_end( -// &mut self, -// context: &mut EvmContext<'_, DB>, -// result: InterpreterResult, -// ) -> InterpreterResult { -// self.gas_inspector.call_end(context, result) -// } - -// fn create( -// &mut self, -// context: &mut EvmContext<'_, DB>, -// call: &mut CreateInputs, -// ) -> Option<(InterpreterResult, Option
)> { -// self.gas_inspector.create(context, call); -// None -// } - -// fn create_end( -// &mut self, -// context: &mut EvmContext<'_, DB>, -// result: InterpreterResult, -// address: Option
, -// ) -> (InterpreterResult, Option
) { -// self.gas_inspector.create_end(context, result, address) -// } -// } - -// #[test] -// #[cfg(not(feature = "optimism"))] -// fn test_gas_inspector() { -// use crate::db::BenchmarkDB; -// use crate::interpreter::opcode; -// use crate::primitives::{address, Bytecode, Bytes, TransactTo}; - -// let contract_data: Bytes = Bytes::from(vec![ -// opcode::PUSH1, -// 0x1, -// opcode::PUSH1, -// 0xb, -// opcode::JUMPI, -// opcode::PUSH1, -// 0x1, -// opcode::PUSH1, -// 0x1, -// opcode::PUSH1, -// 0x1, -// opcode::JUMPDEST, -// opcode::STOP, -// ]); -// let bytecode = Bytecode::new_raw(contract_data); - -// let mut evm = crate::new(); -// evm.database(BenchmarkDB::new_bytecode(bytecode.clone())); -// evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); -// evm.env.tx.transact_to = -// TransactTo::Call(address!("0000000000000000000000000000000000000000")); -// evm.env.tx.gas_limit = 21100; - -// let mut inspector = StackInspector::default(); -// evm.inspect(&mut inspector).unwrap(); - -// // starting from 100gas -// let steps = vec![ -// // push1 -3 -// (0, 97), -// // push1 -3 -// (2, 94), -// // jumpi -10 -// (4, 84), -// // jumpdest 1 -// (11, 83), -// // stop 0 -// (12, 83), -// ]; - -// assert_eq!(inspector.gas_remaining_steps, steps); -// } -// } +#[cfg(test)] +mod tests { + use core::ops::Range; + + use crate::{ + inspectors::GasInspector, + interpreter::{CallInputs, CreateInputs, Interpreter, InterpreterResult}, + primitives::{Address, Bytes, B256}, + Database, EvmContext, Inspector, + }; + + #[derive(Default, Debug)] + struct StackInspector { + pc: usize, + gas_inspector: GasInspector, + gas_remaining_steps: Vec<(usize, u64)>, + } + + impl Inspector for StackInspector { + fn initialize_interp( + &mut self, + interp: &mut Interpreter, + context: &mut EvmContext, + ) { + self.gas_inspector.initialize_interp(interp, context); + } + + fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + self.pc = interp.program_counter(); + self.gas_inspector.step(interp, context); + } + + fn log( + &mut self, + context: &mut EvmContext, + address: &Address, + topics: &[B256], + data: &Bytes, + ) { + self.gas_inspector.log(context, address, topics, data); + } + + fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + self.gas_inspector.step_end(interp, context); + self.gas_remaining_steps + .push((self.pc, self.gas_inspector.gas_remaining())); + } + + fn call( + &mut self, + context: &mut EvmContext, + call: &mut CallInputs, + ) -> Option<(InterpreterResult, Range)> { + self.gas_inspector.call(context, call) + } + + fn call_end( + &mut self, + context: &mut EvmContext, + result: InterpreterResult, + ) -> InterpreterResult { + self.gas_inspector.call_end(context, result) + } + + fn create( + &mut self, + context: &mut EvmContext, + call: &mut CreateInputs, + ) -> Option<(InterpreterResult, Option
)> { + self.gas_inspector.create(context, call); + None + } + + fn create_end( + &mut self, + context: &mut EvmContext, + result: InterpreterResult, + address: Option
, + ) -> (InterpreterResult, Option
) { + self.gas_inspector.create_end(context, result, address) + } + } + + #[test] + #[cfg(not(feature = "optimism"))] + fn test_gas_inspector() { + use crate::db::BenchmarkDB; + use crate::interpreter::opcode; + use crate::primitives::{address, Bytecode, Bytes, TransactTo}; + + let contract_data: Bytes = Bytes::from(vec![ + opcode::PUSH1, + 0x1, + opcode::PUSH1, + 0xb, + opcode::JUMPI, + opcode::PUSH1, + 0x1, + opcode::PUSH1, + 0x1, + opcode::PUSH1, + 0x1, + opcode::JUMPDEST, + opcode::STOP, + ]); + let bytecode = Bytecode::new_raw(contract_data); + + let mut evm = crate::new(); + evm.database(BenchmarkDB::new_bytecode(bytecode.clone())); + evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); + evm.env.tx.transact_to = + TransactTo::Call(address!("0000000000000000000000000000000000000000")); + evm.env.tx.gas_limit = 21100; + + let mut inspector = StackInspector::default(); + evm.inspect(&mut inspector).unwrap(); + + // starting from 100gas + let steps = vec![ + // push1 -3 + (0, 97), + // push1 -3 + (2, 94), + // jumpi -10 + (4, 84), + // jumpdest 1 + (11, 83), + // stop 0 + (12, 83), + ]; + + assert_eq!(inspector.gas_remaining_steps, steps); + } +} diff --git a/crates/revm/src/inspector/instruction.rs b/crates/revm/src/inspector/instruction.rs index a88869fa09..94a06b4f57 100644 --- a/crates/revm/src/inspector/instruction.rs +++ b/crates/revm/src/inspector/instruction.rs @@ -1,26 +1,21 @@ use crate::{ handler::{register::GetInspector, InspectorHandle}, - Evm, Inspector, + Evm, }; use alloc::boxed::Box; use revm_interpreter::{ opcode::{BoxedInstruction, Instruction}, - primitives::{db::Database, Spec}, + primitives::db::Database, InstructionResult, Interpreter, }; /// Outer closure that calls Inspector for every instruction. -pub fn inspector_instruction< - 'a, - SPEC: Spec + 'static, - INSP: GetInspector<'a, DB> + 'a, - DB: Database, ->( - instruction: Instruction, DB>>, -) -> BoxedInstruction<'a, Evm<'a, SPEC, InspectorHandle<'a, DB, INSP>, DB>> { +pub fn inspector_instruction<'a, INSP: GetInspector<'a, DB> + 'a, DB: Database>( + instruction: Instruction, DB>>, +) -> BoxedInstruction<'a, Evm<'a, InspectorHandle<'a, DB, INSP>, DB>> { Box::new( move |interpreter: &mut Interpreter, - host: &mut Evm<'a, SPEC, InspectorHandle<'a, DB, INSP>, DB>| { + host: &mut Evm<'a, InspectorHandle<'a, DB, INSP>, DB>| { // SAFETY: as the PC was already incremented we need to subtract 1 to preserve the // old Inspector behavior. interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.sub(1) }; @@ -58,15 +53,13 @@ mod tests { #[test] fn test_make_boxed_instruction_table() { // test that this pattern builds. - let inst: InstructionTable< - Evm<'_, BerlinSpec, InspectorHandle<'_, EmptyDB, NoOpInspector>, EmptyDB>, - > = make_instruction_table::, BerlinSpec>(); - let _test: BoxedInstructionTable<'_, Evm<'_, BerlinSpec, _, _>> = - make_boxed_instruction_table::< - '_, - Evm<'_, BerlinSpec, InspectorHandle<'_, EmptyDB, NoOpInspector>, EmptyDB>, - BerlinSpec, - _, - >(inst, inspector_instruction); + let inst: InstructionTable, EmptyDB>> = + make_instruction_table::, BerlinSpec>(); + let _test: BoxedInstructionTable<'_, Evm<'_, _, _>> = make_boxed_instruction_table::< + '_, + Evm<'_, InspectorHandle<'_, EmptyDB, NoOpInspector>, EmptyDB>, + BerlinSpec, + _, + >(inst, inspector_instruction); } } diff --git a/crates/revm/src/inspector/noop.rs b/crates/revm/src/inspector/noop.rs index 412278be74..05d7292fc4 100644 --- a/crates/revm/src/inspector/noop.rs +++ b/crates/revm/src/inspector/noop.rs @@ -1,13 +1,7 @@ -use crate::{handler::register::GetInspector, Database, Inspector}; +use crate::{Database, Inspector}; /// Dummy [Inspector], helpful as standalone replacement. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct NoOpInspector; impl Inspector for NoOpInspector {} - -impl GetInspector<'_, DB> for NoOpInspector { - fn get(&mut self) -> &mut dyn Inspector { - self - } -} \ No newline at end of file diff --git a/crates/revm/src/journaled_state.rs b/crates/revm/src/journaled_state.rs index f4c2db0c91..900107201b 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/revm/src/journaled_state.rs @@ -207,11 +207,12 @@ impl JournaledState { /// Panics if the caller is not loaded inside of the EVM state. /// This is should have been done inside `create_inner`. #[inline] - pub fn create_account_checkpoint( + pub fn create_account_checkpoint( &mut self, caller: Address, address: Address, balance: U256, + spec_id: SpecId, ) -> Result { // Enter subroutine let checkpoint = self.checkpoint(); @@ -260,7 +261,7 @@ impl JournaledState { account.info.balance = new_balance; // EIP-161: State trie clearing (invariant-preserving alternative) - if SPEC::enabled(SPURIOUS_DRAGON) { + if spec_id.is_enabled_in(SPURIOUS_DRAGON) { // nonce is going to be reset to zero in AccountCreated journal entry. account.info.nonce = 1; } diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 0d4f5604b8..97626f0a1b 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -10,10 +10,11 @@ compile_error!("`with-serde` feature has been renamed to `serde`."); #[macro_use] extern crate alloc; -pub mod db; -mod evm_factory; mod context; +pub mod db; mod evm; +mod evm_builder; +mod evm_factory; mod frame; pub mod handler; mod inspector; @@ -23,15 +24,16 @@ mod journaled_state; pub mod optimism; pub type DummyStateDB = InMemoryDB; +pub use context::{Context, EvmContext}; #[cfg(feature = "std")] pub use db::{ CacheState, DBBox, State, StateBuilder, StateDBBox, TransitionAccount, TransitionState, }; pub use db::{Database, DatabaseCommit, DatabaseRef, InMemoryDB}; -pub use evm_factory::{new, EvmFactory}; -pub use context::{EvmContext,Context}; pub use evm::{new_evm, Evm, Transact, CALL_STACK_LIMIT}; -pub use frame::{CallStackFrame,FrameOrResult}; +//pub use evm_builder::EvmBuilder; +pub use evm_factory::{new, EvmFactory}; +pub use frame::{CallStackFrame, FrameOrResult}; pub use journaled_state::{JournalCheckpoint, JournalEntry, JournaledState}; // reexport `revm_precompiles` From 62526061483743299d49f1eb93cb56d4eebd6a4a Mon Sep 17 00:00:00 2001 From: rakita Date: Mon, 27 Nov 2023 12:48:13 +0100 Subject: [PATCH 10/52] move towards the builder, allow EVM modify --- crates/revm/benches/bench.rs | 2 +- crates/revm/src/context.rs | 16 +- crates/revm/src/evm.rs | 109 ++++--------- crates/revm/src/evm_builder.rs | 148 +++++++++++++----- crates/revm/src/evm_factory.rs | 60 +------ crates/revm/src/frame.rs | 3 +- crates/revm/src/handler/mainnet.rs | 3 +- .../revm/src/handler/mainnet/preexecution.rs | 4 +- crates/revm/src/handler/optimism.rs | 20 +-- crates/revm/src/inspector/gas.rs | 6 +- crates/revm/src/journaled_state.rs | 4 +- crates/revm/src/optimism.rs | 83 ++++++++-- 12 files changed, 248 insertions(+), 210 deletions(-) diff --git a/crates/revm/benches/bench.rs b/crates/revm/benches/bench.rs index 931636cac2..d85fe24d4c 100644 --- a/crates/revm/benches/bench.rs +++ b/crates/revm/benches/bench.rs @@ -94,7 +94,7 @@ fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { ..Default::default() }; let mut shared_memory = SharedMemory::new(); - let mut host = DummyHost::new(evm.env.clone()); + let mut host = DummyHost::new(*evm.env.clone()); let instruction_table = make_instruction_table::(); b.iter(move || { // replace memory with empty memory to use it inside interpreter. diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index 5efb1170be..a8c603bc73 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -19,11 +19,11 @@ use core::ops::Range; pub struct Context { /// Evm Context. pub evm: EvmContext, - /// External generic code. + /// External contexts. pub external: EXT, } -/// EVM Data contains all the data that EVM needs to execute. +/// EVM contexts contains data that EVM needs for execution. #[derive(Debug)] pub struct EvmContext { /// EVM Environment contains all the information about config, block and transaction that @@ -43,6 +43,18 @@ pub struct EvmContext { } impl<'a, DB: Database> EvmContext { + pub fn new(db: DB) -> Self { + Self { + env: Box::default(), + journaled_state: JournaledState::new(SpecId::LATEST, vec![]), + db, + error: None, + precompiles: Precompiles::default(), + #[cfg(feature = "optimism")] + l1_block_info: None, + } + } + /// Load access list for berlin hard fork. /// /// Loading of accounts/storages is needed to make them warm. diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 56d804569e..4ad53c2728 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,24 +1,25 @@ use crate::{ - db::Database, + db::{Database, EmptyDB}, + evm_builder::EvmBuilder, handler::Handler, - handler::RegisterHandler, + handler::{MainnetHandle, RegisterHandler}, interpreter::{ - gas::initial_tx_gas, opcode::InstructionTables, CallContext, CallInputs, CallScheme, - CreateInputs, Host, Interpreter, InterpreterAction, InterpreterResult, SelfDestructResult, - SharedMemory, Transfer, + opcode::InstructionTables, CallContext, CallInputs, CallScheme, CreateInputs, Host, + Interpreter, InterpreterAction, InterpreterResult, SelfDestructResult, SharedMemory, + Transfer, }, journaled_state::JournaledState, precompile::Precompiles, primitives::{ - specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, - Output, Spec, SpecId::*, TransactTo, B256, U256, + specification::{self, SpecId}, + Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Output, + SpecId::*, + TransactTo, B256, U256, }, CallStackFrame, Context, EvmContext, FrameOrResult, }; -use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use auto_impl::auto_impl; -use core::{fmt, marker::PhantomData}; -use specification::SpecId; +use alloc::{boxed::Box, vec::Vec}; +use core::fmt; #[cfg(feature = "optimism")] use crate::optimism; @@ -48,63 +49,6 @@ where } } -#[cfg(feature = "optimism")] -impl<'a, SPEC: Spec, DB: Database> Evm<'a, SPEC, DB> { - /// If the transaction is not a deposit transaction, subtract the L1 data fee from the - /// caller's balance directly after minting the requested amount of ETH. - fn remove_l1_cost( - is_deposit: bool, - tx_caller: Address, - l1_cost: U256, - db: &mut DB, - journal: &mut JournaledState, - ) -> Result<(), EVMError> { - if is_deposit { - return Ok(()); - } - let acc = journal - .load_account(tx_caller, db) - .map_err(EVMError::Database)? - .0; - if l1_cost.gt(&acc.info.balance) { - let u64_cost = if U256::from(u64::MAX).lt(&l1_cost) { - u64::MAX - } else { - l1_cost.as_limbs()[0] - }; - return Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: u64_cost, - balance: acc.info.balance, - }, - )); - } - acc.info.balance = acc.info.balance.saturating_sub(l1_cost); - Ok(()) - } - - /// If the transaction is a deposit with a `mint` value, add the mint value - /// in wei to the caller's balance. This should be persisted to the database - /// prior to the rest of execution. - fn commit_mint_value( - tx_caller: Address, - tx_mint: Option, - db: &mut DB, - journal: &mut JournaledState, - ) -> Result<(), EVMError> { - if let Some(mint) = tx_mint { - journal - .load_account(tx_caller, db) - .map_err(EVMError::Database)? - .0 - .info - .balance += U256::from(mint); - journal.checkpoint(); - } - Ok(()) - } -} - impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> where EXT: RegisterHandler<'a, DB, EXT>, @@ -118,6 +62,17 @@ where } } + /// Returns evm builder. + pub fn builder() -> EvmBuilder<'a, MainnetHandle, EmptyDB> { + EvmBuilder::default() + } + + /// Allow for evm setting to be modified by feeding current evm + /// to the builder for modifications. + pub fn modify(self) -> EvmBuilder<'a, EXT, DB> { + EvmBuilder::new(self) + } + /// TODO add spec_id as variable. pub fn new_with_spec( db: DB, @@ -136,15 +91,6 @@ where .collect::>(), ); - // temporary here. Factory should create handler and register external handles. - //let mut handler = external.register_handler::(Handler::mainnet::()); - - // temporary here. Factory should override this handle. - //if env.cfg.is_beneficiary_reward_disabled() { - // // do nothing - // handler.reward_beneficiary = Arc::new(|_, _| Ok(())); - //} - Self { context: Context { evm: EvmContext { @@ -163,6 +109,7 @@ where } } + /// Runs main call loop. #[inline] pub fn run( &mut self, @@ -282,7 +229,7 @@ where panic!("[OPTIMISM] Failed to load enveloped transaction."); }; // TODO specs - let tx_l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx); + let tx_l1_cost = l1_block_info.calculate_tx_l1_cost(enveloped_tx, self.spec_id); // storage l1 block info for later use. self.context.evm.l1_block_info = Some(l1_block_info); @@ -315,8 +262,7 @@ where #[cfg(feature = "optimism")] if self.context.evm.env.cfg.optimism { - // TODO spec - Evm::::commit_mint_value( + crate::optimism::commit_mint_value( tx_caller, self.context.evm.env.tx.optimism.mint, self.context.evm.db, @@ -324,8 +270,7 @@ where )?; let is_deposit = self.context.evm.env.tx.optimism.source_hash.is_some(); - // TODO spec - Evm::::remove_l1_cost( + crate::optimism::remove_l1_cost( is_deposit, tx_caller, tx_l1_cost, diff --git a/crates/revm/src/evm_builder.rs b/crates/revm/src/evm_builder.rs index 8ff7296162..d80b03d78a 100644 --- a/crates/revm/src/evm_builder.rs +++ b/crates/revm/src/evm_builder.rs @@ -1,41 +1,119 @@ -use core::marker::PhantomData; - use revm_interpreter::primitives::SpecId; use crate::{ - db::{Database, EmptyDB}, + db::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}, handler::{MainnetHandle, RegisterHandler}, - primitives::Spec, - Evm, EvmContext, Handler, + primitives::LatestSpec, + Context, Evm, EvmContext, Handler, }; -// /// Evm Builder -// pub struct EvmBuilder<'a, SPEC: Spec + 'static, EXT, DB: Database + 'a> { -// database: DB, -// evm: EvmContext, -// handler: Handler<'a, Evm<'a, SPEC, EXT, DB>, EXT, DB>, -// spec_id: SpecId, -// } - -// impl<'a, EXT: RegisterHandler<'a, DB, EXT>, DB: Database + 'a> EvmBuilder<'a, SPEC, EXT, DB> { -// pub fn new() -> EvmBuilder<'a, MainnetHandle, EmptyDB> { -// EvmBuilder { -// database: (), -// //evm: EvmContext::default(), -// //external: MainnetHandle::default(), -// spec_id: SpecId::LATEST, -// } -// } - -// pub fn db>( -// self, -// db: ODB, -// ) -> EvmBuilder<'a, OEXT, ODB> { -// EvmBuilder { -// database: db, -// //evm: self.evm, -// //external: MainnetHandle::default(), -// spec_id: self.spec_id, -// } -// } -// } +/// Evm Builder +pub struct EvmBuilder<'a, EXT, DB: Database> { + evm: EvmContext, + external: EXT, + handler: Handler<'a, Evm<'a, EXT, DB>, EXT, DB>, + spec_id: SpecId, +} + +impl<'a> Default for EvmBuilder<'a, MainnetHandle, EmptyDB> { + fn default() -> Self { + Self { + evm: EvmContext::new(EmptyDB::default()), + external: MainnetHandle::default(), + handler: Handler::mainnet::(), + spec_id: SpecId::LATEST, + } + } +} + +impl<'a, EXT, DB: Database> EvmBuilder<'a, EXT, DB> { + pub fn new(evm: Evm<'a, EXT, DB>) -> Self { + Self { + evm: evm.context.evm, + external: evm.context.external, + handler: evm.handler, + spec_id: evm.spec_id, + } + } + + /// Build Evm. + pub fn build(self) -> Evm<'a, EXT, DB> { + Evm { + context: Context { + evm: self.evm, + external: self.external, + }, + handler: self.handler, + spec_id: self.spec_id, + } + } + + /// Sets specification Id , that will mark the version of EVM. + /// It represent the hard fork of ethereum. + /// + /// # Note + /// + /// When changed it will reset the handler to default mainnet. + pub fn with_spec_id(mut self, spec_id: SpecId) -> Self { + self.spec_id = spec_id; + // TODO add match for other spec + self.handler = Handler::mainnet::(); + self + } + + /// Sets the [`Database`] that will be used by [`Evm`]. + /// + /// # Note + /// + /// When changed it will reset the handler to default mainnet. + pub fn with_db(self, db: ODB) -> EvmBuilder<'a, EXT, ODB> { + EvmBuilder { + evm: EvmContext::new(db), + external: self.external, + handler: Handler::mainnet::(), + spec_id: self.spec_id, + } + } + /// Sets the [`DatabaseRef`] that will be used by [`Evm`]. + /// + /// # Note + /// + /// When changed it will reset the handler to default mainnet. + pub fn with_ref_db( + self, + db: RDB, + ) -> EvmBuilder<'a, EXT, WrapDatabaseRef> { + EvmBuilder { + evm: EvmContext::new(WrapDatabaseRef(db)), + external: self.external, + handler: Handler::mainnet::(), + spec_id: self.spec_id, + } + } + + /// Sets the external data that can be used by Handler inside EVM. + /// + /// # Note + /// + /// When changed it will reset the handler to default mainnet. + pub fn with_external(self, external: OEXT) -> EvmBuilder<'a, OEXT, DB> { + EvmBuilder { + evm: self.evm, + external: external, + handler: Handler::mainnet::(), + spec_id: self.spec_id, + } + } + + pub fn register_handler>( + self, + handler: H, + ) -> EvmBuilder<'a, EXT, DB> { + EvmBuilder { + evm: self.evm, + external: self.external, + handler: handler.register_handler::(self.handler), + spec_id: self.spec_id, + } + } +} diff --git a/crates/revm/src/evm_factory.rs b/crates/revm/src/evm_factory.rs index 32e9189719..dd5f0cedbf 100644 --- a/crates/revm/src/evm_factory.rs +++ b/crates/revm/src/evm_factory.rs @@ -122,7 +122,7 @@ impl EvmFactory { impl EvmFactory { pub fn execute_evm_ref< 'a, - OUT:'a, + OUT: 'a, EXT: RegisterHandler<'a, WrapDatabaseRef<&'a DB>, EXT> + 'a, FN: Fn(&mut Evm<'a, EXT, WrapDatabaseRef<&'a DB>>) -> OUT, >( @@ -141,61 +141,22 @@ impl EvmFactory { let mut evm = new_evm::>(env, WrapDatabaseRef(db), external); let res = exec(&mut evm); - let (db, env) = evm.into_db_ext(); - //self.env = env; - res } /// Do checks that could make transaction fail before call/create pub fn preverify_transaction_ref(&self) -> Result<(), EVMError> { - // if let Some(db) = self.db.as_ref() { - // new_evm( - // &mut self.env.clone(), - // &mut WrapDatabaseRef(db), - // MainnetHandle::default(), - // ) - // .preverify_transaction() - // } else { - // panic!("Database needs to be set"); - // } - - self.execute_evm_ref(MainnetHandle::default(), |mut evm| { - evm.preverify_transaction() - }) + self.execute_evm_ref(MainnetHandle::default(), |evm| evm.preverify_transaction()) } /// Skip preverification steps and execute transaction /// without writing to DB, return change state. pub fn transact_preverified_ref(&self) -> EVMResult { - // if let Some(db) = self.db.as_ref() { - // new_evm::( - // &mut self.env.clone(), - // &mut WrapDatabaseRef(db), - // MainnetHandle::default(), - // ) - // .transact_preverified() - // } else { - // panic!("Database needs to be set"); - // } - - self.execute_evm_ref(MainnetHandle::default(), |mut evm| { - evm.transact_preverified() - }) + self.execute_evm_ref(MainnetHandle::default(), |evm| evm.transact_preverified()) } /// Execute transaction without writing to DB, return change state. pub fn transact_ref(&self) -> EVMResult { - // if let Some(db) = self.db.as_ref() { - // new_evm::( - // &mut self.env.clone(), - // &mut WrapDatabaseRef(db), - // MainnetHandle::default(), - // ) - // .transact() - // } else { - // panic!("Database needs to be set"); - // } - self.execute_evm_ref(MainnetHandle::default(), |mut evm| evm.transact()) + self.execute_evm_ref(MainnetHandle::default(), |evm| evm.transact()) } /// Execute transaction with given inspector, without wring to DB. Return change state. @@ -203,19 +164,8 @@ impl EvmFactory { &'a self, inspector: I, ) -> EVMResult { - // if let Some(db) = self.db.as_ref() { - // new_evm( - // &mut self.env.clone(), - // &mut WrapDatabaseRef(db), - // InspectorHandle::new(inspector), - // ) - // .transact() - // } else { - // panic!("Database needs to be set"); - // } - let insp = InspectorHandle::new(inspector); - self.execute_evm_ref(insp, |mut evm| evm.transact()) + self.execute_evm_ref(insp, |evm| evm.transact()) } } diff --git a/crates/revm/src/frame.rs b/crates/revm/src/frame.rs index b504047273..1c950548d9 100644 --- a/crates/revm/src/frame.rs +++ b/crates/revm/src/frame.rs @@ -20,7 +20,6 @@ pub struct CallStackFrame { pub interpreter: Interpreter, } - /// Contains either a frame or a result. pub enum FrameOrResult { /// Boxed stack frame @@ -34,4 +33,4 @@ impl FrameOrResult { pub fn new_frame(frame: CallStackFrame) -> Self { Self::Frame(Box::new(frame)) } -} \ No newline at end of file +} diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index 427776250d..e90e22c389 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -1,10 +1,9 @@ //! Mainnet related handlers. - -pub mod preexecution; pub mod frames; pub mod host; pub mod main; +pub mod preexecution; use crate::{ interpreter::{return_ok, return_revert, Gas, InstructionResult}, diff --git a/crates/revm/src/handler/mainnet/preexecution.rs b/crates/revm/src/handler/mainnet/preexecution.rs index c5fad18533..35d61e64b3 100644 --- a/crates/revm/src/handler/mainnet/preexecution.rs +++ b/crates/revm/src/handler/mainnet/preexecution.rs @@ -1,10 +1,8 @@ use revm_interpreter::gas; use crate::{ - interpreter::{Gas, InstructionResult, SuccessOrHalt}, - primitives::{db::Database, EVMError, ExecutionResult, Output, ResultAndState}, + primitives::{db::Database, EVMError}, primitives::{Env, Spec}, - Context, }; /// Validate environment for the mainnet. diff --git a/crates/revm/src/handler/optimism.rs b/crates/revm/src/handler/optimism.rs index 47dfde8f2e..7ab96bea13 100644 --- a/crates/revm/src/handler/optimism.rs +++ b/crates/revm/src/handler/optimism.rs @@ -2,35 +2,37 @@ use super::mainnet; use crate::{ + handler::RegisterHandler, interpreter::{return_ok, return_revert, Gas, InstructionResult}, optimism, primitives::{ db::Database, Account, EVMError, Env, ExecutionResult, Halt, HashMap, InvalidTransaction, Output, ResultAndState, Spec, SpecId::REGOLITH, U256, }, - EvmContext, + Evm, EvmContext, Handler, }; +use alloc::sync::Arc; use core::ops::Mul; pub struct OptimismHandle {} -impl<'a, DB: Database> RegisterHandler<'a, DB, ()> for OptimismHandle { +impl<'a, DB: Database> RegisterHandler<'a, DB, OptimismHandle> for OptimismHandle { fn register_handler( &self, - mut handler: Handler<'a, Evm<'a, SPEC, (), DB>, (), DB>, - ) -> Handler<'a, Evm<'a, SPEC, (), DB>, (), DB> + mut handler: Handler<'a, Evm<'a, OptimismHandle, DB>, (), DB>, + ) -> Handler<'a, Evm<'a, (), DB>, (), DB> where DB: 'a, (): Sized, { - handler.call_return = Arc::new(optimism::handle_call_return::); - handler.calculate_gas_refund = Arc::new(optimism::calculate_gas_refund::); + handler.call_return = Arc::new(handle_call_return::); + handler.calculate_gas_refund = Arc::new(calculate_gas_refund::); // we reinburse caller the same was as in mainnet. // Refund is calculated differently then mainnet. handler.reward_beneficiary = Arc::new(reward_beneficiary::); // In case of halt of deposit transaction return Error. - handler.main_return = Arc::new(optimism::main_return::); - handler.end = Arc::new(optimism::end_handle::); + handler.main_return = Arc::new(main_return::); + handler.end = Arc::new(end_handle::); } } @@ -173,7 +175,7 @@ pub fn main_return( output: Output, gas: &Gas, ) -> Result> { - let result = mainnet::main_return::(context, call_result, output, gas)?; + let result = mainnet::main::main_return::(context, call_result, output, gas)?; if result.result.is_halt() { // Post-regolith, if the transaction is a deposit transaction and it haults, diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 5c21c21583..3b4e7c0b50 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -83,11 +83,7 @@ mod tests { } impl Inspector for StackInspector { - fn initialize_interp( - &mut self, - interp: &mut Interpreter, - context: &mut EvmContext, - ) { + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.initialize_interp(interp, context); } diff --git a/crates/revm/src/journaled_state.rs b/crates/revm/src/journaled_state.rs index 900107201b..764cbb8150 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/revm/src/journaled_state.rs @@ -1,7 +1,7 @@ use crate::interpreter::{InstructionResult, SelfDestructResult}; use crate::primitives::{ - db::Database, hash_map::Entry, Account, Address, Bytecode, HashMap, Log, Spec, SpecId::*, - State, StorageSlot, TransientStorage, KECCAK_EMPTY, PRECOMPILE3, U256, + db::Database, hash_map::Entry, Account, Address, Bytecode, HashMap, Log, SpecId::*, State, + StorageSlot, TransientStorage, KECCAK_EMPTY, PRECOMPILE3, U256, }; use alloc::vec::Vec; use core::mem; diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index e6d1f093c8..9c2a3fad41 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -1,6 +1,11 @@ //! Optimism-specific constants, types, and helpers. -use crate::primitives::{address, db::Database, Address, Bytes, Spec, SpecId, U256}; +use crate::{ + primitives::{ + address, db::Database, Address, Bytes, EVMError, InvalidTransaction, Spec, SpecId, U256, + }, + JournaledState, +}; use core::ops::Mul; const ZERO_BYTE_COST: u64 = 4; @@ -19,6 +24,60 @@ pub const BASE_FEE_RECIPIENT: Address = address!("420000000000000000000000000000 /// The address of the L1Block contract. pub const L1_BLOCK_CONTRACT: Address = address!("4200000000000000000000000000000000000015"); +/// If the transaction is a deposit with a `mint` value, add the mint value +/// in wei to the caller's balance. This should be persisted to the database +/// prior to the rest of execution. +pub(crate) fn commit_mint_value( + tx_caller: Address, + tx_mint: Option, + db: &mut DB, + journal: &mut JournaledState, +) -> Result<(), EVMError> { + if let Some(mint) = tx_mint { + journal + .load_account(tx_caller, db) + .map_err(EVMError::Database)? + .0 + .info + .balance += U256::from(mint); + journal.checkpoint(); + } + Ok(()) +} + +/// If the transaction is not a deposit transaction, subtract the L1 data fee from the +/// caller's balance directly after minting the requested amount of ETH. +pub(crate) fn remove_l1_cost( + is_deposit: bool, + tx_caller: Address, + l1_cost: U256, + db: &mut DB, + journal: &mut JournaledState, +) -> Result<(), EVMError> { + if is_deposit { + return Ok(()); + } + let acc = journal + .load_account(tx_caller, db) + .map_err(EVMError::Database)? + .0; + if l1_cost.gt(&acc.info.balance) { + let u64_cost = if U256::from(u64::MAX).lt(&l1_cost) { + u64::MAX + } else { + l1_cost.as_limbs()[0] + }; + return Err(EVMError::Transaction( + InvalidTransaction::LackOfFundForMaxFee { + fee: u64_cost, + balance: acc.info.balance, + }, + )); + } + acc.info.balance = acc.info.balance.saturating_sub(l1_cost); + Ok(()) +} + /// L1 block info /// /// We can extract L1 epoch data from each L2 block, by looking at the `setL1BlockValues` @@ -59,7 +118,7 @@ impl L1BlockInfo { /// /// Prior to regolith, an extra 68 non-zero bytes were included in the rollup data costs to /// account for the empty signature. - pub fn data_gas(&self, input: &Bytes) -> U256 { + pub fn data_gas(&self, input: &Bytes, spec_id: SpecId) -> U256 { let mut rollup_data_gas_cost = U256::from(input.iter().fold(0, |acc, byte| { acc + if *byte == 0x00 { ZERO_BYTE_COST @@ -69,7 +128,7 @@ impl L1BlockInfo { })); // Prior to regolith, an extra 68 non zero bytes were included in the rollup data costs. - if !SPEC::enabled(SpecId::REGOLITH) { + if !spec_id.is_enabled_in(SpecId::REGOLITH) { rollup_data_gas_cost += U256::from(NON_ZERO_BYTE_COST).mul(U256::from(68)); } @@ -77,13 +136,13 @@ impl L1BlockInfo { } /// Calculate the gas cost of a transaction based on L1 block data posted on L2 - pub fn calculate_tx_l1_cost(&self, input: &Bytes) -> U256 { + pub fn calculate_tx_l1_cost(&self, input: &Bytes, spec_id: SpecId) -> U256 { // If the input is not a deposit transaction, the default value is zero. if input.is_empty() || input.first() == Some(&0x7F) { return U256::ZERO; } - let rollup_data_gas_cost = self.data_gas::(input); + let rollup_data_gas_cost = self.data_gas(input, spec_id); rollup_data_gas_cost .saturating_add(self.l1_fee_overhead) .saturating_mul(self.l1_base_fee) @@ -112,12 +171,12 @@ mod tests { // gas cost = 3 non-zero bytes * NON_ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 // gas cost = 3 * 16 + 68 * 16 = 1136 let input = bytes!("FACADE"); - let bedrock_data_gas = l1_block_info.data_gas::(&input); + let bedrock_data_gas = l1_block_info.data_gas(&input, SpecId::BEDROCK); assert_eq!(bedrock_data_gas, U256::from(1136)); // Regolith has no added 68 non zero bytes // gas cost = 3 * 16 = 48 - let regolith_data_gas = l1_block_info.data_gas::(&input); + let regolith_data_gas = l1_block_info.data_gas(&input, SpecId::REGOLITH); assert_eq!(regolith_data_gas, U256::from(48)); } @@ -136,12 +195,12 @@ mod tests { // gas cost = 3 non-zero * NON_ZERO_BYTE_COST + 2 * ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 // gas cost = 3 * 16 + 2 * 4 + 68 * 16 = 1144 let input = bytes!("FA00CA00DE"); - let bedrock_data_gas = l1_block_info.data_gas::(&input); + let bedrock_data_gas = l1_block_info.data_gas(&input, SpecId::BEDROCK); assert_eq!(bedrock_data_gas, U256::from(1144)); // Regolith has no added 68 non zero bytes // gas cost = 3 * 16 + 2 * 4 = 56 - let regolith_data_gas = l1_block_info.data_gas::(&input); + let regolith_data_gas = l1_block_info.data_gas(&input, SpecId::REGOLITH); assert_eq!(regolith_data_gas, U256::from(56)); } @@ -154,17 +213,17 @@ mod tests { }; let input = bytes!("FACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost::(&input); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); assert_eq!(gas_cost, U256::from(1048)); // Zero rollup data gas cost should result in zero let input = bytes!(""); - let gas_cost = l1_block_info.calculate_tx_l1_cost::(&input); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); assert_eq!(gas_cost, U256::ZERO); // Deposit transactions with the EIP-2718 type of 0x7F should result in zero let input = bytes!("7FFACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost::(&input); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); assert_eq!(gas_cost, U256::ZERO); } } From a8eba5413ee04cf739909c8301ada3b571336829 Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 29 Nov 2023 00:12:07 +0100 Subject: [PATCH 11/52] wip on EvmBuilder and modify functionality --- Cargo.lock | 16 -- Cargo.toml | 2 +- crates/precompile/src/lib.rs | 50 ++--- crates/primitives/src/env.rs | 12 ++ crates/revm/benches/bench.rs | 211 +++++++++++---------- crates/revm/src/context.rs | 29 ++- crates/revm/src/db.rs | 2 + crates/revm/src/evm.rs | 40 ++-- crates/revm/src/evm_builder.rs | 43 ++++- crates/revm/src/evm_factory.rs | 7 +- crates/revm/src/handler.rs | 16 +- crates/revm/src/handler/register.rs | 1 + crates/revm/src/inspector/customprinter.rs | 52 ++--- crates/revm/src/inspector/gas.rs | 29 ++- crates/revm/src/journaled_state.rs | 14 +- crates/revm/src/lib.rs | 6 +- 16 files changed, 296 insertions(+), 234 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 64c97aee32..3b808114b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1641,12 +1641,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "microbench" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4c44e40aee4e6fd2f4257bb91e5948ce79285aeb949129448889cf2fbf6da0b" - [[package]] name = "mime" version = "0.3.17" @@ -2314,16 +2308,6 @@ dependencies = [ "serde", ] -[[package]] -name = "revm-test" -version = "0.1.0" -dependencies = [ - "bytes", - "hex", - "microbench", - "revm", -] - [[package]] name = "rfc6979" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 20678f00a8..39ee555436 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] members = [ #"bins/revme", - "bins/revm-test", + #"bins/revm-test", "crates/revm", "crates/primitives", "crates/interpreter", diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index 80b80f06a9..cee3b03fad 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -19,7 +19,7 @@ mod modexp; mod secp256k1; pub mod utilities; -use alloc::{boxed::Box, collections::BTreeMap, vec::Vec}; +use alloc::boxed::Box; use core::{fmt, hash::Hash}; use once_cell::race::OnceBox; #[doc(hidden)] @@ -56,9 +56,10 @@ impl PrecompileOutput { } } } -#[derive(Clone, Debug)] +#[derive(Clone, Default, Debug)] pub struct Precompiles { - pub inner: Vec, + /// Precompiles. + pub inner: HashMap, } impl Precompiles { @@ -66,22 +67,7 @@ impl Precompiles { /// /// Other precompiles with overwrite existing precompiles. pub fn extend(&mut self, other: impl IntoIterator) { - self.inner = self - .inner - .iter() - .cloned() - .chain(other) - .map(|i| (i.0, i.1.clone())) - .collect::>() - .into_iter() - .map(|(k, v)| PrecompileWithAddress(k, v)) - .collect::>(); - } -} - -impl Default for Precompiles { - fn default() -> Self { - Self::new(SpecId::LATEST).clone() //berlin + self.inner.extend(other.into_iter().map(Into::into)); } } @@ -143,14 +129,17 @@ impl Precompiles { pub fn homestead() -> &'static Self { static INSTANCE: OnceBox = OnceBox::new(); INSTANCE.get_or_init(|| { - let mut inner = vec![ - secp256k1::ECRECOVER, - hash::SHA256, - hash::RIPEMD160, - identity::FUN, - ]; - inner.sort_unstable_by_key(|i| i.0); - Box::new(Self { inner }) + let mut precompiles = Precompiles::default(); + precompiles.extend( + [ + secp256k1::ECRECOVER, + hash::SHA256, + hash::RIPEMD160, + identity::FUN, + ] + .into_iter(), + ); + Box::new(precompiles) }) } @@ -245,7 +234,7 @@ impl Precompiles { /// Returns an iterator over the precompiles addresses. #[inline] pub fn addresses(&self) -> impl IntoIterator { - self.inner.iter().map(|i| &i.0) + self.inner.keys() } /// Is the given address a precompile. @@ -258,10 +247,7 @@ impl Precompiles { #[inline] pub fn get(&self, address: &Address) -> Option { //return None; - self.inner - .binary_search_by_key(address, |i| i.0) - .ok() - .map(|i| self.inner[i].1.clone()) + self.inner.get(address).cloned() } /// Is the precompiles list empty. diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index a6b3917d95..a04b80470e 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -471,6 +471,12 @@ impl BlockEnv { .as_ref() .map(|a| a.excess_blob_gas) } + + /// Clears environment and resets fields to default values. + #[inline] + pub fn clear(&mut self) { + *self = Self::default(); + } } impl Default for BlockEnv { @@ -556,6 +562,12 @@ impl TxEnv { pub fn get_total_blob_gas(&self) -> u64 { GAS_PER_BLOB * self.blob_hashes.len() as u64 } + + /// Clears environment and resets fields to default values. + #[inline] + pub fn clear(&mut self) { + *self = Self::default(); + } } impl Default for TxEnv { diff --git a/crates/revm/benches/bench.rs b/crates/revm/benches/bench.rs index d85fe24d4c..8fa8e0e643 100644 --- a/crates/revm/benches/bench.rs +++ b/crates/revm/benches/bench.rs @@ -7,120 +7,121 @@ use revm::{ primitives::{ address, bytes, hex, BerlinSpec, Bytecode, BytecodeState, Bytes, TransactTo, U256, }, + EvmBuilder, }; use revm_interpreter::{opcode::make_instruction_table, SharedMemory, EMPTY_SHARED_MEMORY}; use std::time::Duration; -type Evm = revm::EvmFactory; - -fn analysis(c: &mut Criterion) { - let mut evm = revm::new(); - - evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); - evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); - // evm.env.tx.data = bytes!("30627b7c"); - evm.env.tx.data = bytes!("8035F0CE"); - - let contract_data: Bytes = hex::decode(ANALYSIS).unwrap().into(); - - let mut g = c.benchmark_group("analysis"); - g.noise_threshold(0.03) - .warm_up_time(Duration::from_secs(3)) - .measurement_time(Duration::from_secs(10)) - .sample_size(10); - - let raw = Bytecode::new_raw(contract_data.clone()); - evm.database(BenchmarkDB::new_bytecode(raw)); - bench_transact(&mut g, &mut evm); - - let checked = Bytecode::new_raw(contract_data.clone()).to_checked(); - evm.database(BenchmarkDB::new_bytecode(checked)); - bench_transact(&mut g, &mut evm); - - let analysed = to_analysed(Bytecode::new_raw(contract_data)); - evm.database(BenchmarkDB::new_bytecode(analysed)); - bench_transact(&mut g, &mut evm); - - g.finish(); -} - -fn snailtracer(c: &mut Criterion) { - let mut evm = revm::new(); - evm.database(BenchmarkDB::new_bytecode(bytecode(SNAILTRACER))); - - evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); - evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); - evm.env.tx.data = bytes!("30627b7c"); - - let mut g = c.benchmark_group("snailtracer"); - g.noise_threshold(0.03) - .warm_up_time(Duration::from_secs(3)) - .measurement_time(Duration::from_secs(10)) - .sample_size(10); - bench_transact(&mut g, &mut evm); - bench_eval(&mut g, &mut evm); - g.finish(); -} - -fn transfer(c: &mut Criterion) { - let mut evm = revm::new(); - evm.database(BenchmarkDB::new_bytecode(Bytecode::new())); - - evm.env.tx.caller = address!("0000000000000000000000000000000000000001"); - evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); - evm.env.tx.value = U256::from(10); - - let mut g = c.benchmark_group("transfer"); - g.noise_threshold(0.03).warm_up_time(Duration::from_secs(1)); - bench_transact(&mut g, &mut evm); - g.finish(); -} - -fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { - let state = match evm.db.as_mut().unwrap().0.state { - BytecodeState::Raw => "raw", - BytecodeState::Checked { .. } => "checked", - BytecodeState::Analysed { .. } => "analysed", - }; - let id = format!("transact/{state}"); - g.bench_function(id, |b| b.iter(|| evm.transact().unwrap())); -} - -fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { - g.bench_function("eval", |b| { - let contract = Contract { - input: evm.env.tx.data.clone(), - bytecode: BytecodeLocked::try_from(evm.db.as_ref().unwrap().0.clone()).unwrap(), - ..Default::default() - }; - let mut shared_memory = SharedMemory::new(); - let mut host = DummyHost::new(*evm.env.clone()); - let instruction_table = make_instruction_table::(); - b.iter(move || { - // replace memory with empty memory to use it inside interpreter. - // Later return memory back. - let temp = core::mem::replace(&mut shared_memory, EMPTY_SHARED_MEMORY); - let mut interpreter = Interpreter::new(Box::new(contract.clone()), u64::MAX, false); - let res = interpreter.run(temp, &instruction_table, &mut host); - shared_memory = interpreter.take_memory(); - host.clear(); - res - }) - }); -} +// fn analysis(c: &mut Criterion) { +// let mut evm = revm::new(); + +// evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); +// evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); +// // evm.env.tx.data = bytes!("30627b7c"); +// evm.env.tx.data = bytes!("8035F0CE"); + +// let contract_data: Bytes = hex::decode(ANALYSIS).unwrap().into(); + +// let mut g = c.benchmark_group("analysis"); +// g.noise_threshold(0.03) +// .warm_up_time(Duration::from_secs(3)) +// .measurement_time(Duration::from_secs(10)) +// .sample_size(10); + +// let raw = Bytecode::new_raw(contract_data.clone()); +// evm.database(BenchmarkDB::new_bytecode(raw)); +// bench_transact(&mut g, &mut evm); + +// let checked = Bytecode::new_raw(contract_data.clone()).to_checked(); +// evm.database(BenchmarkDB::new_bytecode(checked)); +// bench_transact(&mut g, &mut evm); + +// let analysed = to_analysed(Bytecode::new_raw(contract_data)); +// evm.database(BenchmarkDB::new_bytecode(analysed)); +// bench_transact(&mut g, &mut evm); + +// g.finish(); +// } + +// fn snailtracer(c: &mut Criterion) { +// let mut evm = revm::new(); +// evm.database(BenchmarkDB::new_bytecode(bytecode(SNAILTRACER))); + +// evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); +// evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); +// evm.env.tx.data = bytes!("30627b7c"); + +// let mut g = c.benchmark_group("snailtracer"); +// g.noise_threshold(0.03) +// .warm_up_time(Duration::from_secs(3)) +// .measurement_time(Duration::from_secs(10)) +// .sample_size(10); +// bench_transact(&mut g, &mut evm); +// bench_eval(&mut g, &mut evm); +// g.finish(); +// } + +// fn transfer(c: &mut Criterion) { +// let mut evm = revm::new(); +// evm.database(BenchmarkDB::new_bytecode(Bytecode::new())); + +// evm.env.tx.caller = address!("0000000000000000000000000000000000000001"); +// evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); +// evm.env.tx.value = U256::from(10); + +// let mut g = c.benchmark_group("transfer"); +// g.noise_threshold(0.03).warm_up_time(Duration::from_secs(1)); +// bench_transact(&mut g, &mut evm); +// g.finish(); +// } + +// fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { +// let state = match evm.db.as_mut().unwrap().0.state { +// BytecodeState::Raw => "raw", +// BytecodeState::Checked { .. } => "checked", +// BytecodeState::Analysed { .. } => "analysed", +// }; +// let id = format!("transact/{state}"); +// g.bench_function(id, |b| b.iter(|| evm.transact().unwrap())); +// } + +// fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { +// g.bench_function("eval", |b| { +// let contract = Contract { +// input: evm.env.tx.data.clone(), +// bytecode: BytecodeLocked::try_from(evm.db.as_ref().unwrap().0.clone()).unwrap(), +// ..Default::default() +// }; +// let mut shared_memory = SharedMemory::new(); +// let mut host = DummyHost::new(*evm.env.clone()); +// let instruction_table = make_instruction_table::(); +// b.iter(move || { +// // replace memory with empty memory to use it inside interpreter. +// // Later return memory back. +// let temp = core::mem::replace(&mut shared_memory, EMPTY_SHARED_MEMORY); +// let mut interpreter = Interpreter::new(Box::new(contract.clone()), u64::MAX, false); +// let res = interpreter.run(temp, &instruction_table, &mut host); +// shared_memory = interpreter.take_memory(); +// host.clear(); +// res +// }) +// }); +// } fn bytecode(s: &str) -> Bytecode { to_analysed(Bytecode::new_raw(hex::decode(s).unwrap().into())) } -#[rustfmt::skip] -criterion_group!( - benches, - analysis, - snailtracer, - transfer, -); -criterion_main!(benches); +// #[rustfmt::skip] +// criterion_group!( +// benches, +// analysis, +// snailtracer, +// transfer, +// ); +// criterion_main!(benches); + +fn main() {} const ANALYSIS: &str = "6060604052341561000f57600080fd5b604051610dd1380380610dd18339810160405280805190602001909190805182019190602001805190602001909190805182019190505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100a79291906100e3565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100d99291906100e3565b5050505050610188565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012457805160ff1916838001178555610152565b82800160010185558215610152579182015b82811115610151578251825591602001919060010190610136565b5b50905061015f9190610163565b5090565b61018591905b80821115610181576000816000905550600101610169565b5090565b90565b610c3a806101976000396000f3006060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014257806318160ddd1461019c57806323b872dd146101c557806327e235e31461023e578063313ce5671461028b5780635c658165146102ba57806370a082311461032657806395d89b4114610373578063a9059cbb14610401578063dd62ed3e1461045b575b600080fd5b34156100bf57600080fd5b6100c76104c7565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101075780820151818401526020810190506100ec565b50505050905090810190601f1680156101345780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014d57600080fd5b610182600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610565565b604051808215151515815260200191505060405180910390f35b34156101a757600080fd5b6101af610657565b6040518082815260200191505060405180910390f35b34156101d057600080fd5b610224600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061065d565b604051808215151515815260200191505060405180910390f35b341561024957600080fd5b610275600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108f7565b6040518082815260200191505060405180910390f35b341561029657600080fd5b61029e61090f565b604051808260ff1660ff16815260200191505060405180910390f35b34156102c557600080fd5b610310600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610922565b6040518082815260200191505060405180910390f35b341561033157600080fd5b61035d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610947565b6040518082815260200191505060405180910390f35b341561037e57600080fd5b610386610990565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103c65780820151818401526020810190506103ab565b50505050905090810190601f1680156103f35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561040c57600080fd5b610441600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610a2e565b604051808215151515815260200191505060405180910390f35b341561046657600080fd5b6104b1600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b87565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561055d5780601f106105325761010080835404028352916020019161055d565b820191906000526020600020905b81548152906001019060200180831161054057829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015801561072e5750828110155b151561073957600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108865782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a265780601f106109fb57610100808354040283529160200191610a26565b820191906000526020600020905b815481529060010190602001808311610a0957829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a7e57600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820df254047bc8f2904ad3e966b6db116d703bebd40efadadb5e738c836ffc8f58a0029"; diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index a8c603bc73..b7ca54de1b 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -7,8 +7,8 @@ use crate::{ journaled_state::JournaledState, precompile::{Precompile, Precompiles}, primitives::{ - keccak256, Address, AnalysisKind, Bytecode, Bytes, EVMError, Env, Spec, SpecId, SpecId::*, - B256, U256, + keccak256, Address, AnalysisKind, Bytecode, Bytes, EVMError, Env, HashSet, Spec, SpecId, + SpecId::*, B256, U256, }, CallStackFrame, FrameOrResult, CALL_STACK_LIMIT, }; @@ -46,7 +46,7 @@ impl<'a, DB: Database> EvmContext { pub fn new(db: DB) -> Self { Self { env: Box::default(), - journaled_state: JournaledState::new(SpecId::LATEST, vec![]), + journaled_state: JournaledState::new(SpecId::LATEST, HashSet::new()), db, error: None, precompiles: Precompiles::default(), @@ -55,6 +55,29 @@ impl<'a, DB: Database> EvmContext { } } + /// New context with database and environment. + pub fn new_with_env(db: DB, env: Box) -> Self { + Self { + env, + journaled_state: JournaledState::new(SpecId::LATEST, HashSet::new()), + db, + error: None, + precompiles: Precompiles::default(), + #[cfg(feature = "optimism")] + l1_block_info: None, + } + } + + /// Sets precompiles + pub fn set_precompiles(&mut self, precompiles: Precompiles) { + self.journaled_state.precompile_addresses = precompiles + .addresses() + .into_iter() + .cloned() + .collect::>(); + self.precompiles = precompiles; + } + /// Load access list for berlin hard fork. /// /// Loading of accounts/storages is needed to make them warm. diff --git a/crates/revm/src/db.rs b/crates/revm/src/db.rs index 9d6494701e..d65f214907 100644 --- a/crates/revm/src/db.rs +++ b/crates/revm/src/db.rs @@ -21,3 +21,5 @@ pub use states::{ compile_error!( "`web3db` feature is deprecated, drop-in replacement can be found with feature `ethersdb`" ); + +pub type DummyStateDB = InMemoryDB; diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 4ad53c2728..f341fd3b5f 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -12,7 +12,7 @@ use crate::{ precompile::Precompiles, primitives::{ specification::{self, SpecId}, - Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Output, + Address, Bytecode, Bytes, EVMError, EVMResult, Env, HashSet, InvalidTransaction, Output, SpecId::*, TransactTo, B256, U256, }, @@ -49,6 +49,13 @@ where } } +impl<'a> Evm<'a, MainnetHandle, EmptyDB> { + /// Returns evm builder. + pub fn builder() -> EvmBuilder<'a, MainnetHandle, EmptyDB> { + EvmBuilder::default() + } +} + impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> where EXT: RegisterHandler<'a, DB, EXT>, @@ -62,17 +69,26 @@ where } } - /// Returns evm builder. - pub fn builder() -> EvmBuilder<'a, MainnetHandle, EmptyDB> { - EvmBuilder::default() - } - /// Allow for evm setting to be modified by feeding current evm /// to the builder for modifications. pub fn modify(self) -> EvmBuilder<'a, EXT, DB> { EvmBuilder::new(self) } + /// Modify spec id, this will create new EVM that matches this spec id. + pub fn modify_spec_id(self, spec_id: SpecId) -> Self { + if self.spec_id == spec_id { + return self; + } + self.modify().with_spec_id(spec_id).build() + } + + /// Returns internal database and external struct. + #[inline] + pub fn into_context(self) -> Context { + self.context + } + /// TODO add spec_id as variable. pub fn new_with_spec( db: DB, @@ -88,7 +104,7 @@ where .addresses() .into_iter() .cloned() - .collect::>(), + .collect::>(), ); Self { @@ -401,11 +417,6 @@ pub trait Transact { /// Execute transaction by running pre-verification steps and then transaction itself. fn transact(&mut self) -> EVMResult; - - /// Into database and extern type. - fn into_db_ext(self) -> (DB, Box) - where - Self: Sized; } impl<'a, EXT: RegisterHandler<'a, DB, EXT>, DB: Database> Transact for Evm<'a, EXT, DB> { @@ -427,11 +438,6 @@ impl<'a, EXT: RegisterHandler<'a, DB, EXT>, DB: Database> Transact for .and_then(|()| self.transact_preverified_inner()); self.handler.end(&mut self.context, output) } - - #[inline] - fn into_db_ext(self) -> (DB, Box) { - (self.context.evm.db, self.context.evm.env) - } } impl<'a, EXT, DB: Database> Host for Evm<'a, EXT, DB> { diff --git a/crates/revm/src/evm_builder.rs b/crates/revm/src/evm_builder.rs index d80b03d78a..af9c58395d 100644 --- a/crates/revm/src/evm_builder.rs +++ b/crates/revm/src/evm_builder.rs @@ -1,13 +1,16 @@ -use revm_interpreter::primitives::SpecId; +//! Evm Builder. use crate::{ db::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}, handler::{MainnetHandle, RegisterHandler}, - primitives::LatestSpec, + primitives::{BlockEnv, CfgEnv, Env, TxEnv}, + primitives::{LatestSpec, SpecId}, Context, Evm, EvmContext, Handler, }; -/// Evm Builder +/// Evm Builder allows building or modifying EVM. +/// Note that some of the methods that changes underlying structures +/// will reset the registered handler to default mainnet. pub struct EvmBuilder<'a, EXT, DB: Database> { evm: EvmContext, external: EXT, @@ -61,6 +64,30 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, EXT, DB> { self } + /// Modify Environment of EVM. + pub fn modify_env(mut self, f: impl FnOnce(&mut Env)) -> Self { + f(&mut self.evm.env); + self + } + + /// Modify Transaction Environment of EVM. + pub fn modify_tx_env(mut self, f: impl FnOnce(&mut TxEnv)) -> Self { + f(&mut self.evm.env.tx); + self + } + + /// Modify Block Environment of EVM. + pub fn modify_block_env(mut self, f: impl FnOnce(&mut BlockEnv)) -> Self { + f(&mut self.evm.env.block); + self + } + + /// Modify Config Environment of EVM. + pub fn modify_cfg_env(mut self, f: impl FnOnce(&mut CfgEnv)) -> Self { + f(&mut self.evm.env.cfg); + self + } + /// Sets the [`Database`] that will be used by [`Evm`]. /// /// # Note @@ -96,15 +123,21 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, EXT, DB> { /// # Note /// /// When changed it will reset the handler to default mainnet. - pub fn with_external(self, external: OEXT) -> EvmBuilder<'a, OEXT, DB> { + pub fn with_external>( + self, + external: OEXT, + ) -> EvmBuilder<'a, OEXT, DB> { + let handler = external.register_handler::(Handler::mainnet::()); EvmBuilder { evm: self.evm, external: external, - handler: Handler::mainnet::(), + handler, spec_id: self.spec_id, } } + /// Register Handler that modifies the behavior of EVM. + /// Check [`Handler`] for more information. pub fn register_handler>( self, handler: H, diff --git a/crates/revm/src/evm_factory.rs b/crates/revm/src/evm_factory.rs index dd5f0cedbf..b8303a1709 100644 --- a/crates/revm/src/evm_factory.rs +++ b/crates/revm/src/evm_factory.rs @@ -3,7 +3,7 @@ use crate::{ evm::{new_evm, Transact}, handler::{InspectorHandle, MainnetHandle, RegisterHandler}, primitives::{db::WrapDatabaseRef, EVMError, EVMResult, Env, ExecutionResult, ResultAndState}, - Evm, Inspector, + Context, Evm, EvmContext, Inspector, }; /// Struct that takes Database and enabled transact to update state directly to database. @@ -90,7 +90,10 @@ impl EvmFactory { let mut evm = new_evm::(env, db, external); let res = exec(&mut evm); - let (db, env) = evm.into_db_ext(); + let Context { + evm: EvmContext { db, env, .. }, + .. + } = evm.into_context(); self.env = env; self.db = Some(db); diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 04fa6dcfad..6227d3e000 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -1,13 +1,21 @@ +//! Handler contains all the logic that is specific to the Evm. +//! It is used to define different behavior depending on the chain (Optimism,Mainnet) or +//! hardfork (Berlin, London, ..). + pub mod mainnet; #[cfg(feature = "optimism")] pub mod optimism; pub mod register; pub use register::{InspectorHandle, MainnetHandle, RegisterHandler}; -use revm_precompile::{Address, Bytes, B256}; use crate::{ - interpreter::{Gas, InstructionResult}, + interpreter::{ + opcode::{make_instruction_table, InstructionTables}, + CallInputs, CreateInputs, Gas, Host, InstructionResult, InterpreterResult, + SelfDestructResult, SharedMemory, + }, + precompile::{Address, Bytes, B256}, primitives::{ db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec, SpecId, }, @@ -15,10 +23,6 @@ use crate::{ }; use alloc::sync::Arc; use core::ops::Range; -use revm_interpreter::{ - opcode::{make_instruction_table, InstructionTables}, - CallInputs, CreateInputs, Host, InterpreterResult, SelfDestructResult, SharedMemory, -}; /// Handle call return and return final gas value. pub type CallReturnHandle<'a> = Arc Gas + 'a>; diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index a2bd220c08..18a689810a 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -67,6 +67,7 @@ impl<'handler, DB: Database, INS: GetInspector<'handler, DB>> RegisterHandler<'h Self: Sized, DB: 'handler, { + println!("Register handle"); // Every instruction inside flat table that is going to be wrapped by inspector calls. let flat_instruction_table = make_instruction_table::, DB>, SPEC>(); diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs index 50e555e733..ceabcd45b7 100644 --- a/crates/revm/src/inspector/customprinter.rs +++ b/crates/revm/src/inspector/customprinter.rs @@ -110,30 +110,30 @@ impl Inspector for CustomPrintTracer { #[cfg(test)] mod test { - #[test] - #[cfg(not(feature = "optimism"))] - fn gas_calculation_underflow() { - use crate::primitives::{address, bytes}; - - // https://github.com/bluealloy/revm/issues/277 - // checks this use case - let mut evm = crate::new(); - let mut database = crate::InMemoryDB::default(); - let code = bytes!("5b597fb075978b6c412c64d169d56d839a8fe01b3f4607ed603b2c78917ce8be1430fe6101e8527ffe64706ecad72a2f5c97a95e006e279dc57081902029ce96af7edae5de116fec610208527f9fc1ef09d4dd80683858ae3ea18869fe789ddc365d8d9d800e26c9872bac5e5b6102285260276102485360d461024953601661024a53600e61024b53607d61024c53600961024d53600b61024e5360b761024f5360596102505360796102515360a061025253607261025353603a6102545360fb61025553601261025653602861025753600761025853606f61025953601761025a53606161025b53606061025c5360a661025d53602b61025e53608961025f53607a61026053606461026153608c6102625360806102635360d56102645360826102655360ae61026653607f6101e8610146610220677a814b184591c555735fdcca53617f4d2b9134b29090c87d01058e27e962047654f259595947443b1b816b65cdb6277f4b59c10a36f4e7b8658f5a5e6f5561"); - - let acc_info = crate::primitives::AccountInfo { - balance: "0x100c5d668240db8e00".parse().unwrap(), - code_hash: crate::primitives::keccak256(&code), - code: Some(crate::primitives::Bytecode::new_raw(code.clone())), - nonce: 1, - }; - let callee = address!("5fdcca53617f4d2b9134b29090c87d01058e27e9"); - database.insert_account_info(callee, acc_info); - evm.database(database); - evm.env.tx.caller = address!("5fdcca53617f4d2b9134b29090c87d01058e27e0"); - evm.env.tx.transact_to = crate::primitives::TransactTo::Call(callee); - evm.env.tx.data = crate::primitives::Bytes::new(); - evm.env.tx.value = crate::primitives::U256::ZERO; - let _ = evm.inspect_commit(super::CustomPrintTracer::default()); - } + // #[test] + // #[cfg(not(feature = "optimism"))] + // fn gas_calculation_underflow() { + // use crate::primitives::{address, bytes}; + + // // https://github.com/bluealloy/revm/issues/277 + // // checks this use case + // let mut evm = crate::new(); + // let mut database = crate::InMemoryDB::default(); + // let code = bytes!("5b597fb075978b6c412c64d169d56d839a8fe01b3f4607ed603b2c78917ce8be1430fe6101e8527ffe64706ecad72a2f5c97a95e006e279dc57081902029ce96af7edae5de116fec610208527f9fc1ef09d4dd80683858ae3ea18869fe789ddc365d8d9d800e26c9872bac5e5b6102285260276102485360d461024953601661024a53600e61024b53607d61024c53600961024d53600b61024e5360b761024f5360596102505360796102515360a061025253607261025353603a6102545360fb61025553601261025653602861025753600761025853606f61025953601761025a53606161025b53606061025c5360a661025d53602b61025e53608961025f53607a61026053606461026153608c6102625360806102635360d56102645360826102655360ae61026653607f6101e8610146610220677a814b184591c555735fdcca53617f4d2b9134b29090c87d01058e27e962047654f259595947443b1b816b65cdb6277f4b59c10a36f4e7b8658f5a5e6f5561"); + + // let acc_info = crate::primitives::AccountInfo { + // balance: "0x100c5d668240db8e00".parse().unwrap(), + // code_hash: crate::primitives::keccak256(&code), + // code: Some(crate::primitives::Bytecode::new_raw(code.clone())), + // nonce: 1, + // }; + // let callee = address!("5fdcca53617f4d2b9134b29090c87d01058e27e9"); + // database.insert_account_info(callee, acc_info); + // evm.database(database); + // evm.env.tx.caller = address!("5fdcca53617f4d2b9134b29090c87d01058e27e0"); + // evm.env.tx.transact_to = crate::primitives::TransactTo::Call(callee); + // evm.env.tx.data = crate::primitives::Bytes::new(); + // evm.env.tx.value = crate::primitives::U256::ZERO; + // let _ = evm.inspect_commit(super::CustomPrintTracer::default()); + // } } diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 3b4e7c0b50..71d0b88a86 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -147,8 +147,10 @@ mod tests { #[cfg(not(feature = "optimism"))] fn test_gas_inspector() { use crate::db::BenchmarkDB; + use crate::handler::InspectorHandle; use crate::interpreter::opcode; use crate::primitives::{address, Bytecode, Bytes, TransactTo}; + use crate::{Evm, Transact}; let contract_data: Bytes = Bytes::from(vec![ opcode::PUSH1, @@ -167,15 +169,24 @@ mod tests { ]); let bytecode = Bytecode::new_raw(contract_data); - let mut evm = crate::new(); - evm.database(BenchmarkDB::new_bytecode(bytecode.clone())); - evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); - evm.env.tx.transact_to = - TransactTo::Call(address!("0000000000000000000000000000000000000000")); - evm.env.tx.gas_limit = 21100; - - let mut inspector = StackInspector::default(); - evm.inspect(&mut inspector).unwrap(); + let mut evm = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) + .with_external(InspectorHandle::::new( + StackInspector::default(), + )) + .modify_tx_env(|tx| { + tx.clear(); + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = + TransactTo::Call(address!("0000000000000000000000000000000000000000")); + tx.gas_limit = 21100; + }) + .build(); + + // run evm. + evm.transact().unwrap(); + + let inspector = evm.into_context().external.inspector; // starting from 100gas let steps = vec![ diff --git a/crates/revm/src/journaled_state.rs b/crates/revm/src/journaled_state.rs index 764cbb8150..a95bdf9ccc 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/revm/src/journaled_state.rs @@ -1,7 +1,7 @@ use crate::interpreter::{InstructionResult, SelfDestructResult}; use crate::primitives::{ - db::Database, hash_map::Entry, Account, Address, Bytecode, HashMap, Log, SpecId::*, State, - StorageSlot, TransientStorage, KECCAK_EMPTY, PRECOMPILE3, U256, + db::Database, hash_map::Entry, Account, Address, Bytecode, HashMap, HashSet, Log, SpecId::*, + State, StorageSlot, TransientStorage, KECCAK_EMPTY, PRECOMPILE3, U256, }; use alloc::vec::Vec; use core::mem; @@ -29,9 +29,7 @@ pub struct JournaledState { /// Precompiles addresses are used to check if loaded address /// should be considered cold or hot loaded. It is cloned from /// EvmContext to be directly accessed from JournaledState. - /// - /// Note that addresses are sorted. - pub precompile_addresses: Vec
, + pub precompile_addresses: HashSet
, } impl JournaledState { @@ -45,7 +43,7 @@ impl JournaledState { /// # Note /// /// Precompile addresses should be sorted. - pub fn new(spec: SpecId, precompile_addresses: Vec
) -> JournaledState { + pub fn new(spec: SpecId, precompile_addresses: HashSet
) -> JournaledState { Self { state: HashMap::new(), transient_storage: TransientStorage::default(), @@ -227,7 +225,7 @@ impl JournaledState { // Account is not precompile. if account.info.code_hash != KECCAK_EMPTY || account.info.nonce != 0 - || self.precompile_addresses.binary_search(&address).is_ok() + || self.precompile_addresses.contains(&address) { self.checkpoint_revert(checkpoint); return Err(InstructionResult::CreateCollision); @@ -540,7 +538,7 @@ impl JournaledState { .push(JournalEntry::AccountLoaded { address }); // precompiles are warm loaded so we need to take that into account - let is_cold = self.precompile_addresses.binary_search(&address).is_err(); + let is_cold = !self.precompile_addresses.contains(&address); (vac.insert(account), is_cold) } diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 97626f0a1b..0a5afa6e2a 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -23,16 +23,14 @@ mod journaled_state; #[cfg(feature = "optimism")] pub mod optimism; -pub type DummyStateDB = InMemoryDB; pub use context::{Context, EvmContext}; #[cfg(feature = "std")] pub use db::{ CacheState, DBBox, State, StateBuilder, StateDBBox, TransitionAccount, TransitionState, }; -pub use db::{Database, DatabaseCommit, DatabaseRef, InMemoryDB}; +pub use db::{Database, DatabaseCommit, DatabaseRef, DummyStateDB, InMemoryDB}; pub use evm::{new_evm, Evm, Transact, CALL_STACK_LIMIT}; -//pub use evm_builder::EvmBuilder; -pub use evm_factory::{new, EvmFactory}; +pub use evm_builder::EvmBuilder; pub use frame::{CallStackFrame, FrameOrResult}; pub use journaled_state::{JournalCheckpoint, JournalEntry, JournaledState}; From ed188d7d08b4d12db483f74d3907c21baf16239a Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 29 Nov 2023 17:42:26 +0100 Subject: [PATCH 12/52] EvmBuilder with stages wip --- Cargo.lock | 1 + crates/revm/Cargo.toml | 1 + crates/revm/src/evm.rs | 45 +++--- crates/revm/src/evm_builder.rs | 89 ++++++++-- crates/revm/src/evm_factory.rs | 197 ----------------------- crates/revm/src/handler.rs | 7 +- crates/revm/src/lib.rs | 29 ++-- documentation/src/crates/revm.md | 4 +- documentation/src/crates/revm/builder.md | 6 + 9 files changed, 123 insertions(+), 256 deletions(-) delete mode 100644 crates/revm/src/evm_factory.rs create mode 100644 documentation/src/crates/revm/builder.md diff --git a/Cargo.lock b/Cargo.lock index 3b808114b4..13f34f7ae9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2260,6 +2260,7 @@ dependencies = [ "ethers-core", "ethers-providers", "futures", + "once_cell", "revm-interpreter", "revm-precompile", "serde", diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index f73148c630..b9b01197e2 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -15,6 +15,7 @@ revm-precompile = { path = "../precompile", version = "2.2.0", default-features #misc auto_impl = { version = "1.1", default-features = false } +once_cell = { version = "1.18", default-features = false } # Optional serde = { version = "1.0", features = ["derive", "rc"], optional = true } diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index f341fd3b5f..349f9cf524 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,6 +1,8 @@ +#[cfg(feature = "optimism")] +use crate::optimism; use crate::{ db::{Database, EmptyDB}, - evm_builder::EvmBuilder, + evm_builder::{BuilderStage, EvmBuilder, SettingExternal, SettingDb}, handler::Handler, handler::{MainnetHandle, RegisterHandler}, interpreter::{ @@ -21,19 +23,17 @@ use crate::{ use alloc::{boxed::Box, vec::Vec}; use core::fmt; -#[cfg(feature = "optimism")] -use crate::optimism; - /// EVM call stack limit. pub const CALL_STACK_LIMIT: u64 = 1024; +/// EVM instance containing both internal EVM context and external context (if specified) +/// and the handler that dictates the logic of EVM (or hardfork specification). pub struct Evm<'a, EXT, DB: Database> { /// Context of execution, containing both EVM and external context. pub context: Context, - /// Handler of EVM that contains all the logic. + /// Handler of EVM that contains all the logic. Handler contains specification id + /// and it different depending on the specified fork. pub handler: Handler<'a, Self, EXT, DB>, - /// Specification id. - pub spec_id: SpecId, } impl fmt::Debug for Evm<'_, EXT, DB> @@ -51,7 +51,7 @@ where impl<'a> Evm<'a, MainnetHandle, EmptyDB> { /// Returns evm builder. - pub fn builder() -> EvmBuilder<'a, MainnetHandle, EmptyDB> { + pub fn builder() -> EvmBuilder<'a, SettingDb, MainnetHandle, EmptyDB> { EvmBuilder::default() } } @@ -60,24 +60,27 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> where EXT: RegisterHandler<'a, DB, EXT>, { + /// Create new EVM. pub fn new(context: Context, handler: Handler<'a, Self, EXT, DB>) -> Evm<'a, EXT, DB> { - let spec_id = handler.spec_id; - Evm { - context, - handler, - spec_id, - } + Evm { context, handler } + } + + /// Returns specification (hardfork) that the EVM is instanced with. + /// + /// SpecId depends on the handler. + pub fn spec_id(&self) -> SpecId { + self.handler.spec_id } /// Allow for evm setting to be modified by feeding current evm /// to the builder for modifications. - pub fn modify(self) -> EvmBuilder<'a, EXT, DB> { + pub fn modify(self) -> EvmBuilder<'a, SettingExternal, EXT, DB> { EvmBuilder::new(self) } /// Modify spec id, this will create new EVM that matches this spec id. pub fn modify_spec_id(self, spec_id: SpecId) -> Self { - if self.spec_id == spec_id { + if self.spec_id() == spec_id { return self; } self.modify().with_spec_id(spec_id).build() @@ -97,9 +100,8 @@ where handler: Handler<'a, Self, EXT, DB>, precompiles: Precompiles, ) -> Self { - let spec_id = handler.spec_id; let journaled_state = JournaledState::new( - spec_id, + handler.spec_id, precompiles .addresses() .into_iter() @@ -120,7 +122,6 @@ where }, external, }, - spec_id, handler, } } @@ -259,7 +260,7 @@ where // load coinbase // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm - if self.spec_id.is_enabled_in(SHANGHAI) { + if self.spec_id().is_enabled_in(SHANGHAI) { self.context .evm .journaled_state @@ -305,7 +306,7 @@ where U256::from(tx_gas_limit).saturating_mul(self.context.evm.env.effective_gas_price()); // EIP-4844 - if self.spec_id.is_enabled_in(CANCUN) { + if self.handler.spec_id.is_enabled_in(CANCUN) { let data_fee = self .context .evm @@ -351,7 +352,7 @@ where ) } TransactTo::Create(scheme) => self.context.evm.make_create_frame( - self.spec_id, + self.spec_id(), &CreateInputs { caller: tx_caller, scheme, diff --git a/crates/revm/src/evm_builder.rs b/crates/revm/src/evm_builder.rs index af9c58395d..ec8f8c5e84 100644 --- a/crates/revm/src/evm_builder.rs +++ b/crates/revm/src/evm_builder.rs @@ -1,9 +1,11 @@ //! Evm Builder. +use core::marker::PhantomData; + use crate::{ db::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}, handler::{MainnetHandle, RegisterHandler}, - primitives::{BlockEnv, CfgEnv, Env, TxEnv}, + primitives::{BlockEnv, CfgEnv, Env, Spec, TxEnv}, primitives::{LatestSpec, SpecId}, Context, Evm, EvmContext, Handler, }; @@ -11,31 +13,40 @@ use crate::{ /// Evm Builder allows building or modifying EVM. /// Note that some of the methods that changes underlying structures /// will reset the registered handler to default mainnet. -pub struct EvmBuilder<'a, EXT, DB: Database> { +pub struct EvmBuilder<'a, STAGE: BuilderStage, EXT: RegisterHandler<'a, DB, EXT>, DB: Database> { evm: EvmContext, external: EXT, handler: Handler<'a, Evm<'a, EXT, DB>, EXT, DB>, - spec_id: SpecId, + phantom: PhantomData, } -impl<'a> Default for EvmBuilder<'a, MainnetHandle, EmptyDB> { +pub trait BuilderStage {} + +pub struct SettingDb; +impl BuilderStage for SettingDb {} + +pub struct SettingExternal; +impl BuilderStage for SettingExternal {} + +impl<'a> Default for EvmBuilder<'a, SettingDb, MainnetHandle, EmptyDB> { fn default() -> Self { Self { evm: EvmContext::new(EmptyDB::default()), external: MainnetHandle::default(), handler: Handler::mainnet::(), - spec_id: SpecId::LATEST, + phantom: PhantomData, } } } -impl<'a, EXT, DB: Database> EvmBuilder<'a, EXT, DB> { +impl<'a, EXT: RegisterHandler<'a, DB, EXT>, DB: Database> EvmBuilder<'a, SettingExternal, EXT, DB> {} +/* +impl<'a, EXT: RegisterHandler<'a,DB,EXT>, DB: Database> EvmBuilder<'a, EXT, DB> { pub fn new(evm: Evm<'a, EXT, DB>) -> Self { Self { evm: evm.context.evm, external: evm.context.external, handler: evm.handler, - spec_id: evm.spec_id, } } @@ -47,7 +58,43 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, EXT, DB> { external: self.external, }, handler: self.handler, - spec_id: self.spec_id, + } + } + + /// Creates the Handler with Generic Spec. + fn create_handle_generic( + &self, + ) -> Handler<'a, Evm<'a, EXT, DB>, EXT, DB> { + self.external.register_handle(Handler::mainnet::()) + } + + /// Creates the Handler with variable SpecId, inside it will call function with Generic Spec. + fn create_handler(&self, spec_id: SpecId) -> Handler<'a, Evm<'a, EXT, DB>, EXT, DB> { + use crate::primitives::specification::*; + match spec_id { + SpecId::FRONTIER | SpecId::FRONTIER_THAWING => { + self.create_handle_generic::() + } + SpecId::HOMESTEAD | SpecId::DAO_FORK => self.create_handle_generic::(), + SpecId::TANGERINE => self.create_handle_generic::(), + SpecId::SPURIOUS_DRAGON => self.create_handle_generic::(), + SpecId::BYZANTIUM => self.create_handle_generic::(), + SpecId::PETERSBURG | SpecId::CONSTANTINOPLE => { + self.create_handle_generic::() + } + SpecId::ISTANBUL | SpecId::MUIR_GLACIER => self.create_handle_generic::(), + SpecId::BERLIN => self.create_handle_generic::(), + SpecId::LONDON | SpecId::ARROW_GLACIER | SpecId::GRAY_GLACIER => { + self.create_handle_generic::() + } + SpecId::MERGE => self.create_handle_generic::(), + SpecId::SHANGHAI => self.create_handle_generic::(), + SpecId::CANCUN => self.create_handle_generic::(), + SpecId::LATEST => self.create_handle_generic::(), + #[cfg(feature = "optimism")] + SpecId::BEDROCK => self.create_handle_generic::(), + #[cfg(feature = "optimism")] + SpecId::REGOLITH => self.create_handle_generic::(), } } @@ -58,9 +105,8 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, EXT, DB> { /// /// When changed it will reset the handler to default mainnet. pub fn with_spec_id(mut self, spec_id: SpecId) -> Self { - self.spec_id = spec_id; // TODO add match for other spec - self.handler = Handler::mainnet::(); + self.handler = self.create_handler(spec_id); self } @@ -98,7 +144,6 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, EXT, DB> { evm: EvmContext::new(db), external: self.external, handler: Handler::mainnet::(), - spec_id: self.spec_id, } } /// Sets the [`DatabaseRef`] that will be used by [`Evm`]. @@ -110,12 +155,15 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, EXT, DB> { self, db: RDB, ) -> EvmBuilder<'a, EXT, WrapDatabaseRef> { - EvmBuilder { + let present_spec_id = self.handler.spec_id; + + let mut builder = EvmBuilder { evm: EvmContext::new(WrapDatabaseRef(db)), external: self.external, handler: Handler::mainnet::(), - spec_id: self.spec_id, - } + }; + builder.handler = builder.create_handler(present_spec_id); + builder } /// Sets the external data that can be used by Handler inside EVM. @@ -132,8 +180,17 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, EXT, DB> { evm: self.evm, external: external, handler, - spec_id: self.spec_id, } + + let present_spec_id = self.handler.spec_id; + + let mut builder = EvmBuilder { + evm: EvmContext::new(WrapDatabaseRef(db)), + external: self.external, + handler: Handler::mainnet::(), + }; + builder.handler = builder.create_handler(present_spec_id); + builder } /// Register Handler that modifies the behavior of EVM. @@ -146,7 +203,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, EXT, DB> { evm: self.evm, external: self.external, handler: handler.register_handler::(self.handler), - spec_id: self.spec_id, } } } +*/ diff --git a/crates/revm/src/evm_factory.rs b/crates/revm/src/evm_factory.rs deleted file mode 100644 index b8303a1709..0000000000 --- a/crates/revm/src/evm_factory.rs +++ /dev/null @@ -1,197 +0,0 @@ -use crate::{ - db::{Database, DatabaseCommit, DatabaseRef}, - evm::{new_evm, Transact}, - handler::{InspectorHandle, MainnetHandle, RegisterHandler}, - primitives::{db::WrapDatabaseRef, EVMError, EVMResult, Env, ExecutionResult, ResultAndState}, - Context, Evm, EvmContext, Inspector, -}; - -/// Struct that takes Database and enabled transact to update state directly to database. -/// additionally it allows user to set all environment parameters. -/// -/// Parameters that can be set are divided between Config, Block and Transaction(tx) -/// -/// For transacting on EVM you can call transact_commit that will automatically apply changes to db. -/// -/// You can do a lot with rust and traits. For Database abstractions that we need you can implement, -/// Database, DatabaseRef or Database+DatabaseCommit and they enable functionality depending on what kind of -/// handling of struct you want. -/// * Database trait has mutable self in its functions. It is usefully if on get calls you want to modify -/// your cache or update some statistics. They enable `transact` and `inspect` functions -/// * DatabaseRef takes reference on object, this is useful if you only have reference on state and don't -/// want to update anything on it. It enabled `transact_ref` and `inspect_ref` functions -/// * Database+DatabaseCommit allow directly committing changes of transaction. it enabled `transact_commit` -/// and `inspect_commit` -/// -/// /// # Example -/// -/// ``` -/// # use revm::EVM; // Assuming this struct is in 'your_crate_name' -/// # struct SomeDatabase; // Mocking a database type for the purpose of this example -/// # struct Env; // Assuming the type Env is defined somewhere -/// -/// let evm: EVM = EVM::new(); -/// assert!(evm.db.is_none()); -/// ``` -/// -#[derive(Clone, Debug)] -pub struct EvmFactory { - pub env: Box, - pub db: Option, -} - -pub fn new() -> EvmFactory { - EvmFactory::new() -} - -impl Default for EvmFactory { - fn default() -> Self { - Self::new() - } -} - -impl EvmFactory { - /// Execute transaction and apply result to database - pub fn transact_commit(&mut self) -> Result> { - let ResultAndState { result, state } = self.transact()?; - self.db.as_mut().unwrap().commit(state); - Ok(result) - } - - /// Inspect transaction and commit changes to database. - pub fn inspect_commit>( - &mut self, - inspector: INSP, - ) -> Result> { - let ResultAndState { result, state } = self.inspect(inspector)?; - self.db.as_mut().unwrap().commit(state); - Ok(result) - } -} - -impl EvmFactory { - pub fn execute_evm< - 'a, - OUT, - EXT: RegisterHandler<'a, DB, EXT> + 'a, - FN: Fn(&mut Evm<'a, EXT, DB>) -> OUT, - >( - &mut self, - external: EXT, - exec: FN, - ) -> OUT - where - DB: 'a, - { - let Some(db) = self.db.take() else { - panic!("Database needs to be set"); - }; - let env = core::mem::take(&mut self.env); - let mut evm = new_evm::(env, db, external); - let res = exec(&mut evm); - - let Context { - evm: EvmContext { db, env, .. }, - .. - } = evm.into_context(); - self.env = env; - self.db = Some(db); - - res - } - /// Do checks that could make transaction fail before call/create - pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { - self.execute_evm(MainnetHandle::default(), |evm| evm.preverify_transaction()) - } - - /// Skip preverification steps and execute transaction without writing to DB, return change - /// state. - pub fn transact_preverified(&mut self) -> EVMResult { - self.execute_evm(MainnetHandle::default(), |evm| evm.transact_preverified()) - } - - /// Execute transaction without writing to DB, return change state. - pub fn transact(&mut self) -> EVMResult { - self.execute_evm(MainnetHandle::default(), |evm| evm.transact()) - } - - /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect>(&mut self, inspector: INSP) -> EVMResult { - let insp = InspectorHandle::new(inspector); - self.execute_evm(insp, |evm| evm.transact()) - } -} - -impl EvmFactory { - pub fn execute_evm_ref< - 'a, - OUT: 'a, - EXT: RegisterHandler<'a, WrapDatabaseRef<&'a DB>, EXT> + 'a, - FN: Fn(&mut Evm<'a, EXT, WrapDatabaseRef<&'a DB>>) -> OUT, - >( - &'a self, - external: EXT, - exec: FN, - ) -> OUT - where - DB: 'a, - { - //unimplemented!(); - let Some(db) = self.db.as_ref() else { - panic!("Database needs to be set"); - }; - let env = self.env.clone(); - let mut evm = new_evm::>(env, WrapDatabaseRef(db), external); - let res = exec(&mut evm); - - res - } - /// Do checks that could make transaction fail before call/create - pub fn preverify_transaction_ref(&self) -> Result<(), EVMError> { - self.execute_evm_ref(MainnetHandle::default(), |evm| evm.preverify_transaction()) - } - - /// Skip preverification steps and execute transaction - /// without writing to DB, return change state. - pub fn transact_preverified_ref(&self) -> EVMResult { - self.execute_evm_ref(MainnetHandle::default(), |evm| evm.transact_preverified()) - } - - /// Execute transaction without writing to DB, return change state. - pub fn transact_ref(&self) -> EVMResult { - self.execute_evm_ref(MainnetHandle::default(), |evm| evm.transact()) - } - - /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect_ref<'a, I: Inspector> + 'a>( - &'a self, - inspector: I, - ) -> EVMResult { - let insp = InspectorHandle::new(inspector); - self.execute_evm_ref(insp, |evm| evm.transact()) - } -} - -impl EvmFactory { - /// Creates a new [EVM] instance with the default environment, - pub fn new() -> Self { - Self::with_env(Default::default()) - } - - /// Creates a new [EVM] instance with the given environment. - pub fn with_env(env: Box) -> Self { - Self { env, db: None } - } - - pub fn database(&mut self, db: DB) { - self.db = Some(db); - } - - pub fn db(&mut self) -> Option<&mut DB> { - self.db.as_mut() - } - - pub fn take_db(&mut self) -> DB { - core::mem::take(&mut self.db).unwrap() - } -} diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 6227d3e000..0042f9f5ce 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -2,13 +2,16 @@ //! It is used to define different behavior depending on the chain (Optimism,Mainnet) or //! hardfork (Berlin, London, ..). +// Modules. pub mod mainnet; #[cfg(feature = "optimism")] pub mod optimism; pub mod register; +// Exports. pub use register::{InspectorHandle, MainnetHandle, RegisterHandler}; +// Includes. use crate::{ interpreter::{ opcode::{make_instruction_table, InstructionTables}, @@ -17,12 +20,14 @@ use crate::{ }, precompile::{Address, Bytes, B256}, primitives::{ - db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec, SpecId, + db::Database, specification::*, EVMError, EVMResultGeneric, Env, Output, ResultAndState, + Spec, SpecId, }, CallStackFrame, Context, }; use alloc::sync::Arc; use core::ops::Range; +use once_cell::race::OnceBox; /// Handle call return and return final gas value. pub type CallReturnHandle<'a> = Arc Gas + 'a>; diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 0a5afa6e2a..e4905774f4 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -10,19 +10,21 @@ compile_error!("`with-serde` feature has been renamed to `serde`."); #[macro_use] extern crate alloc; +// Define modules. + mod context; pub mod db; mod evm; mod evm_builder; -mod evm_factory; mod frame; pub mod handler; mod inspector; mod journaled_state; - #[cfg(feature = "optimism")] pub mod optimism; +// Export items. + pub use context::{Context, EvmContext}; #[cfg(feature = "std")] pub use db::{ @@ -32,26 +34,17 @@ pub use db::{Database, DatabaseCommit, DatabaseRef, DummyStateDB, InMemoryDB}; pub use evm::{new_evm, Evm, Transact, CALL_STACK_LIMIT}; pub use evm_builder::EvmBuilder; pub use frame::{CallStackFrame, FrameOrResult}; +pub use handler::Handler; +pub use inspector::{inspector_instruction, inspectors, Inspector}; pub use journaled_state::{JournalCheckpoint, JournalEntry, JournaledState}; +#[cfg(feature = "optimism")] +pub use optimism::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; -// reexport `revm_precompiles` -#[doc(inline)] -pub use revm_precompile as precompile; +// Reexport libraries -// reexport `revm_interpreter` #[doc(inline)] pub use revm_interpreter as interpreter; - -// reexport `revm_primitives` #[doc(inline)] pub use revm_interpreter::primitives; - -// reexport inspector implementations -pub use inspector::inspectors; -pub use inspector::{inspector_instruction, Inspector}; - -// export Optimism types, helpers, and constants -#[cfg(feature = "optimism")] -pub use optimism::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; - -pub use handler::Handler; +#[doc(inline)] +pub use revm_precompile as precompile; diff --git a/documentation/src/crates/revm.md b/documentation/src/crates/revm.md index adee43a5ea..9240096d7a 100644 --- a/documentation/src/crates/revm.md +++ b/documentation/src/crates/revm.md @@ -1,10 +1,10 @@ # Rust Ethereum Virtual Machine (revm) -The `crate` is focused on the implementation of Ethereum Virtual Machine (EVM) including database handling, state journaling, and an inspection system for observing and logging the execution of EVM. This crate pulls together everything described prior to deliver the rust evm. +The `crate` is focused on the implementation of Ethereum Virtual Machine (EVM) including call loop and host implementation, database handling, state journaling, logic handlers and an inspection system for observing and logging the execution of EVM. This crate pulls together everything described prior to deliver the rust evm. Modules: -- `db`: This module includes structures and functions for database interaction. +- `db`: This module includes structures and functions for database interaction. It is a glue between EVM and database. It transforms or aggregates the EVM changes. - `evm`: This module contains a Struct that takes Database and enabled transact to update state directly to database. Additionally it allows user to set all environment parameters. - `evm_impl`: This module includes more specific implementations related to the EVM regarding state transitions. - `inspector`: This module introduces the `Inspector` trait and its implementations for observing the EVM execution. diff --git a/documentation/src/crates/revm/builder.md b/documentation/src/crates/revm/builder.md new file mode 100644 index 0000000000..66a00020b4 --- /dev/null +++ b/documentation/src/crates/revm/builder.md @@ -0,0 +1,6 @@ + +# Evm Builder + +Is a helper function that allows easier setting of database external and logic structures. + +There is a dependency between Database, External and Spec types so setting Database will reset External and Handle field while setting External field would reset Handler. Note that Database will never be reset. \ No newline at end of file From 64a1bd6d87f849e6bcf3cb079fcd70dddf41d7aa Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 29 Nov 2023 19:43:57 +0100 Subject: [PATCH 13/52] Add wip stages for builer --- crates/revm/src/evm.rs | 6 ++++-- crates/revm/src/evm_builder.rs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index d78744a44a..3f1364f5dc 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -74,7 +74,7 @@ where /// Allow for evm setting to be modified by feeding current evm /// to the builder for modifications. - pub fn modify(self) -> EvmBuilder<'a, SettingExternal, EXT, EXT, DB> { + pub fn modify(self) -> EvmBuilder<'a, SettingExternal, EXT, MainnetHandle, DB> { EvmBuilder::new(self) } @@ -83,7 +83,9 @@ where if self.spec_id() == spec_id { return self; } - self.modify().with_spec_id(spec_id).build() + //self.modify().with_spec_id(spec_id).build() + // TODO + self } /// Returns internal database and external struct. diff --git a/crates/revm/src/evm_builder.rs b/crates/revm/src/evm_builder.rs index 5506406ce2..34fb3ed265 100644 --- a/crates/revm/src/evm_builder.rs +++ b/crates/revm/src/evm_builder.rs @@ -72,10 +72,10 @@ impl<'a, EXT, HREG: RegisterHandler<'a, DB, EXT>, DB: Database> /// # Note /// /// When changed it will reset the handler to default mainnet. - pub fn with_ref_db, EXT>>( + pub fn with_ref_db( self, db: ODB, - ) -> EvmBuilder<'a, SettingExternal, EXT, OHREG, WrapDatabaseRef> { + ) -> EvmBuilder<'a, SettingExternal, EXT, MainnetHandle, WrapDatabaseRef> { let present_spec_id = self.handler.spec_id; let mut builder = EvmBuilder { From 45d72098ba4b1803dc88d020ff6c42f0bd26be85 Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 30 Nov 2023 20:09:35 +0100 Subject: [PATCH 14/52] make handle register simple function, add raw instruction table, split external data from registers --- crates/revm/src/evm.rs | 21 +- crates/revm/src/evm_builder.rs | 41 +-- crates/revm/src/handler.rs | 6 +- crates/revm/src/handler/register.rs | 302 +++++++++++------------ crates/revm/src/inspector/gas.rs | 2 + crates/revm/src/inspector/instruction.rs | 37 +-- crates/revm/src/inspector/noop.rs | 8 +- 7 files changed, 194 insertions(+), 223 deletions(-) diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 3f1364f5dc..52bc7e8972 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -4,7 +4,6 @@ use crate::{ db::{Database, EmptyDB}, evm_builder::{BuilderStage, EvmBuilder, SettingDb, SettingExternal}, handler::Handler, - handler::{MainnetHandle, RegisterHandler}, interpreter::{ opcode::InstructionTables, CallContext, CallInputs, CallScheme, CreateInputs, Host, Interpreter, InterpreterAction, InterpreterResult, SelfDestructResult, SharedMemory, @@ -21,7 +20,7 @@ use crate::{ CallStackFrame, Context, EvmContext, FrameOrResult, }; use alloc::{boxed::Box, vec::Vec}; -use core::fmt; +use core::{fmt, ops::Deref}; /// EVM call stack limit. pub const CALL_STACK_LIMIT: u64 = 1024; @@ -49,17 +48,14 @@ where } } -impl<'a> Evm<'a, MainnetHandle, EmptyDB> { +impl<'a> Evm<'a, (), EmptyDB> { /// Returns evm builder. - pub fn builder() -> EvmBuilder<'a, SettingDb, (), MainnetHandle, EmptyDB> { + pub fn builder() -> EvmBuilder<'a, SettingDb, (), EmptyDB> { EvmBuilder::default() } } -impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> -where - EXT: RegisterHandler<'a, DB, EXT>, -{ +impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { /// Create new EVM. pub fn new(context: Context, handler: Handler<'a, Self, EXT, DB>) -> Evm<'a, EXT, DB> { Evm { context, handler } @@ -74,7 +70,7 @@ where /// Allow for evm setting to be modified by feeding current evm /// to the builder for modifications. - pub fn modify(self) -> EvmBuilder<'a, SettingExternal, EXT, MainnetHandle, DB> { + pub fn modify(self) -> EvmBuilder<'a, SettingExternal, EXT, DB> { EvmBuilder::new(self) } @@ -422,7 +418,7 @@ pub trait Transact { fn transact(&mut self) -> EVMResult; } -impl<'a, EXT: RegisterHandler<'a, DB, EXT>, DB: Database> Transact for Evm<'a, EXT, DB> { +impl<'a, EXT, DB: Database> Transact for Evm<'a, EXT, DB> { #[inline] fn preverify_transaction(&mut self) -> Result<(), EVMError> { self.preverify_transaction_inner() @@ -501,19 +497,18 @@ impl<'a, EXT, DB: Database> Host for Evm<'a, EXT, DB> { } /// Creates new EVM instance with erased types. -pub fn new_evm<'a, EXT: RegisterHandler<'a, DB, EXT> + 'a, DB: Database + 'a>( +pub fn new_evm<'a, EXT, DB: Database + 'a>( env: Box, db: DB, external: EXT, ) -> Evm<'a, EXT, DB> { macro_rules! create_evm { ($spec:ident) => {{ - let handler = external.register_handler::<$spec>(Handler::mainnet::<$spec>()); Evm::new_with_spec( db, env, external, - handler, + Handler::mainnet::<$spec>(), Precompiles::new(revm_precompile::SpecId::from_spec_id($spec::SPEC_ID)).clone(), ) }}; diff --git a/crates/revm/src/evm_builder.rs b/crates/revm/src/evm_builder.rs index 34fb3ed265..082060ad26 100644 --- a/crates/revm/src/evm_builder.rs +++ b/crates/revm/src/evm_builder.rs @@ -4,7 +4,7 @@ use core::marker::PhantomData; use crate::{ db::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}, - handler::{MainnetHandle, RegisterHandler}, + handler::HandleRegister, primitives::{BlockEnv, CfgEnv, Env, Spec, TxEnv}, primitives::{LatestSpec, SpecId}, Context, Evm, EvmContext, Handler, @@ -13,17 +13,11 @@ use crate::{ /// Evm Builder allows building or modifying EVM. /// Note that some of the methods that changes underlying structures /// will reset the registered handler to default mainnet. -pub struct EvmBuilder< - 'a, - Stage: BuilderStage, - EXT, - HREG: RegisterHandler<'a, DB, EXT>, - DB: Database, -> { +pub struct EvmBuilder<'a, Stage: BuilderStage, EXT, DB: Database> { evm: EvmContext, external: EXT, - handle_registrator: HREG, handler: Handler<'a, Evm<'a, EXT, DB>, EXT, DB>, + handle_registers: Vec>, phantom: PhantomData, } @@ -35,35 +29,30 @@ impl BuilderStage for SettingDb {} pub struct SettingExternal; impl BuilderStage for SettingExternal {} -impl<'a> Default for EvmBuilder<'a, SettingDb, (), MainnetHandle, EmptyDB> { +impl<'a> Default for EvmBuilder<'a, SettingDb, (), EmptyDB> { fn default() -> Self { Self { evm: EvmContext::new(EmptyDB::default()), external: (), - handle_registrator: MainnetHandle::default(), handler: Handler::mainnet::(), + handle_registers: Vec::new(), phantom: PhantomData, } } } -impl<'a, EXT, HREG: RegisterHandler<'a, DB, EXT>, DB: Database> - EvmBuilder<'a, SettingDb, EXT, HREG, DB> -{ +impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDb, EXT, DB> { /// Sets the [`Database`] that will be used by [`Evm`]. /// /// # Note /// /// When changed it will reset the handler to default mainnet. - pub fn with_db( - self, - db: ODB, - ) -> EvmBuilder<'a, SettingExternal, EXT, MainnetHandle, ODB> { + pub fn with_db(self, db: ODB) -> EvmBuilder<'a, SettingExternal, EXT, ODB> { EvmBuilder { evm: EvmContext::new(db), external: self.external, - handle_registrator: MainnetHandle::default(), handler: Handler::mainnet::(), + handle_registers: Vec::new(), phantom: PhantomData, } } @@ -75,37 +64,33 @@ impl<'a, EXT, HREG: RegisterHandler<'a, DB, EXT>, DB: Database> pub fn with_ref_db( self, db: ODB, - ) -> EvmBuilder<'a, SettingExternal, EXT, MainnetHandle, WrapDatabaseRef> { + ) -> EvmBuilder<'a, SettingExternal, EXT, WrapDatabaseRef> { let present_spec_id = self.handler.spec_id; let mut builder = EvmBuilder { evm: EvmContext::new(WrapDatabaseRef(db)), external: self.external, - handle_registrator: MainnetHandle::default(), handler: Handler::mainnet::(), + handle_registers: Vec::new(), phantom: PhantomData, }; - //builder.handler = builder.create_handler(present_spec_id); builder } } -impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternal, EXT, MainnetHandle, DB> { +impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternal, EXT, DB> { pub fn new(evm: Evm<'a, EXT, DB>) -> Self { Self { evm: evm.context.evm, external: evm.context.external, - handle_registrator: MainnetHandle::default(), handler: evm.handler, + handle_registers: Vec::new(), phantom: PhantomData, } } } -impl<'a, EXT, HREG: RegisterHandler<'a, DB, EXT>, DB: Database> - EvmBuilder<'a, SettingExternal, EXT, HREG, DB> -{ -} +impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternal, EXT, DB> {} /* impl<'a, EXT: RegisterHandler<'a,DB,EXT>, DB: Database> EvmBuilder<'a, EXT, DB> { pub fn new(evm: Evm<'a, EXT, DB>) -> Self { diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 0042f9f5ce..f8ef85cae8 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -9,7 +9,7 @@ pub mod optimism; pub mod register; // Exports. -pub use register::{InspectorHandle, MainnetHandle, RegisterHandler}; +pub use register::{inspector_handle_register, HandleRegister}; // Includes. use crate::{ @@ -130,12 +130,12 @@ pub type InitialTxGasHandle<'a> = Arc u64 + 'a>; pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { /// Specification ID. pub spec_id: SpecId, + /// Instruction table type. + pub instruction_table: InstructionTables<'a, H>, /// Initial tx gas. pub initial_tx_gas: InitialTxGasHandle<'a>, /// Validate Env pub validate_env: ValidateEnvHandle<'a, DB>, - /// Instruction table type. - pub instruction_table: InstructionTables<'a, H>, // Uses env, call result and returned gas from the call to determine the gas // that is returned from transaction execution.. pub call_return: CallReturnHandle<'a>, diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index ca5fcb4a81..8efada5409 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -2,179 +2,161 @@ use crate::{ db::Database, handler::Handler, inspector_instruction, - interpreter::{ - opcode::{make_instruction_table, InstructionTables}, - InterpreterResult, SelfDestructResult, - }, - primitives::Spec, + interpreter::{InterpreterResult, SelfDestructResult}, CallStackFrame, Evm, FrameOrResult, Inspector, }; use alloc::sync::Arc; -use core::marker::PhantomData; +use revm_interpreter::opcode::{BoxedInstructionTable, InstructionTable}; -/// Register external handles. -pub trait RegisterHandler<'a, DB: Database, EXT> { - fn register_handler( - &self, - handler: Handler<'a, Evm<'a, EXT, DB>, EXT, DB>, - ) -> Handler<'a, Evm<'a, EXT, DB>, EXT, DB> - where - DB: 'a, - EXT: Sized, - { - handler - } +pub trait GetInspector<'a, DB: Database> { + fn get_inspector(&mut self) -> &mut dyn Inspector; } -/// Default registered handler that produces default mainnet handler. -#[derive(Default)] -pub struct MainnetHandle {} - -impl<'a, EXT, DB: Database> RegisterHandler<'a, DB, EXT> for MainnetHandle {} +/// Wants +/// List of function that would modify the handler +/// Functions need to be Spec aware. Generic over Spec. +/// They dont need to be tied to one structure, so they need to be generic over trait. +/// +/// Problems: +/// Trait Remove it +/// +pub type EvmHandler<'a, EXT, DB> = Handler<'a, Evm<'a, EXT, DB>, EXT, DB>; -pub struct InspectorHandle<'a, DB: Database, GI: GetInspector<'a, DB>> { - pub inspector: GI, - pub _phantomdata: PhantomData<&'a DB>, +#[derive(Default)] +pub enum RawInstructionTable<'a, EXT, DB: Database> { + #[default] + Default, + PlainRaw(InstructionTable>), + BoxedRaw(BoxedInstructionTable<'a, Evm<'a, EXT, DB>>), } -impl<'a, DB: Database, GI: GetInspector<'a, DB>> InspectorHandle<'a, DB, GI> { - pub fn new(inspector: GI) -> Self { - Self { - inspector, - _phantomdata: PhantomData, +// Note that +pub type HandleRegister<'a, EXT, DB> = + Arc, &mut RawInstructionTable<'a, EXT, DB>)>; + +pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( + handler: &'a mut EvmHandler<'a, EXT, DB>, + instruction_table: &'a mut RawInstructionTable<'a, EXT, DB>, +) { + let spec_id = handler.spec_id; + let taken_table = core::mem::take(instruction_table); + // Every instruction inside flat table that is going to be wrapped by inspector calls. + match taken_table { + RawInstructionTable::PlainRaw(table) => { + *instruction_table = RawInstructionTable::BoxedRaw( + table + .into_iter() + .map(|i| inspector_instruction(i)) + .collect::>() + .try_into() + .unwrap_or_else(|_| unreachable!()), + ); } + RawInstructionTable::BoxedRaw(table) => { + *instruction_table = RawInstructionTable::BoxedRaw( + table + .into_iter() + .map(|i| inspector_instruction(i)) + .collect::>() + .try_into() + .unwrap_or_else(|_| unreachable!()), + ); + } + _ => unreachable!(), } -} - -impl<'a, DB: Database, INSP: Inspector> GetInspector<'a, DB> for INSP { - fn get(&mut self) -> &mut dyn Inspector { - self - } -} - -pub trait GetInspector<'a, DB: Database> { - fn get(&mut self) -> &mut dyn Inspector; -} -impl<'handler, DB: Database, INS: GetInspector<'handler, DB>> RegisterHandler<'handler, DB, Self> - for InspectorHandle<'handler, DB, INS> -{ - fn register_handler( - &self, - mut handler: Handler<'handler, Evm<'handler, Self, DB>, Self, DB>, - ) -> Handler<'handler, Evm<'handler, Self, DB>, Self, DB> - where - Self: Sized, - DB: 'handler, - { - println!("Register handle"); - // Every instruction inside flat table that is going to be wrapped by inspector calls. - let flat_instruction_table = - make_instruction_table::, DB>, SPEC>(); - - // wrap instruction table with inspector handles. - handler.instruction_table = InstructionTables::Boxed(Arc::new(core::array::from_fn(|i| { - inspector_instruction(flat_instruction_table[i]) - }))); - - // handle sub create - handler.frame_sub_create = Arc::new( - move |context, frame, mut inputs| -> Option> { - if let Some((result, address)) = context - .external - .inspector - .get() - .create(&mut context.evm, &mut inputs) - { + // handle sub create + handler.frame_sub_create = Arc::new( + move |context, frame, mut inputs| -> Option> { + if let Some((result, address)) = context + .external + .get_inspector() + .create(&mut context.evm, &mut inputs) + { + frame.interpreter.insert_create_output(result, address); + return None; + } + + match context.evm.make_create_frame(spec_id, &inputs) { + FrameOrResult::Frame(new_frame) => Some(new_frame), + FrameOrResult::Result(result) => { + let (result, address) = context.external.get_inspector().create_end( + &mut context.evm, + result, + frame.created_address, + ); + // insert result of the failed creation of create CallStackFrame. frame.interpreter.insert_create_output(result, address); - return None; - } - - match context.evm.make_create_frame(SPEC::SPEC_ID, &inputs) { - FrameOrResult::Frame(new_frame) => Some(new_frame), - FrameOrResult::Result(result) => { - let (result, address) = context.external.inspector.get().create_end( - &mut context.evm, - result, - frame.created_address, - ); - // insert result of the failed creation of create CallStackFrame. - frame.interpreter.insert_create_output(result, address); - None - } - } - }, - ); - - // handle sub call - handler.frame_sub_call = Arc::new( - move |context, mut inputs, frame, memory, return_memory_offset| -> Option> { - // inspector handle - let inspector = &mut context.external.inspector.get(); - if let Some((result, range)) = inspector.call(&mut context.evm, &mut inputs) { - frame.interpreter.insert_call_output(memory, result, range); - return None; + None } - match context - .evm - .make_call_frame(&inputs, return_memory_offset.clone()) - { - FrameOrResult::Frame(new_frame) => Some(new_frame), - FrameOrResult::Result(result) => { - // inspector handle - let result = context - .external - .inspector - .get() - .call_end(&mut context.evm, result); - frame - .interpreter - .insert_call_output(memory, result, return_memory_offset); - None - } + } + }, + ); + + // handle sub call + handler.frame_sub_call = Arc::new( + move |context, mut inputs, frame, memory, return_memory_offset| -> Option> { + // inspector handle + let inspector = &mut context.external.get_inspector(); + if let Some((result, range)) = inspector.call(&mut context.evm, &mut inputs) { + frame.interpreter.insert_call_output(memory, result, range); + return None; + } + match context + .evm + .make_call_frame(&inputs, return_memory_offset.clone()) + { + FrameOrResult::Frame(new_frame) => Some(new_frame), + FrameOrResult::Result(result) => { + // inspector handle + let result = context + .external + .get_inspector() + .call_end(&mut context.evm, result); + frame + .interpreter + .insert_call_output(memory, result, return_memory_offset); + None } - }, - ); - - // return frame handle - let old_handle = handler.frame_return.clone(); - handler.frame_return = Arc::new( - move |context, mut child, parent, memory, mut result| -> Option { - let inspector = &mut context.external.inspector.get(); - result = if child.is_create { - let (result, address) = - inspector.create_end(&mut context.evm, result, child.created_address); - child.created_address = address; - result - } else { - inspector.call_end(&mut context.evm, result) - }; - old_handle(context, child, parent, memory, result) - }, - ); - - // handle log - let old_handle = handler.host_log.clone(); - handler.host_log = Arc::new(move |context, address, topics, data| { - context - .external - .inspector - .get() - .log(&mut context.evm, &address, &topics, &data); - old_handle(context, address, topics, data) - }); - - // selfdestruct handle - let old_handle = handler.host_selfdestruct.clone(); - handler.host_selfdestruct = Arc::new( - move |context, address, target| -> Option { - let inspector = &mut context.external.inspector.get(); - let acc = context.evm.journaled_state.state.get(&address).unwrap(); - inspector.selfdestruct(address, target, acc.info.balance); - old_handle(context, address, target) - }, - ); - - handler - } + } + }, + ); + + // return frame handle + let old_handle = handler.frame_return.clone(); + handler.frame_return = Arc::new( + move |context, mut child, parent, memory, mut result| -> Option { + let inspector = &mut context.external.get_inspector(); + result = if child.is_create { + let (result, address) = + inspector.create_end(&mut context.evm, result, child.created_address); + child.created_address = address; + result + } else { + inspector.call_end(&mut context.evm, result) + }; + old_handle(context, child, parent, memory, result) + }, + ); + + // handle log + let old_handle = handler.host_log.clone(); + handler.host_log = Arc::new(move |context, address, topics, data| { + context + .external + .get_inspector() + .log(&mut context.evm, &address, &topics, &data); + old_handle(context, address, topics, data) + }); + + // selfdestruct handle + let old_handle = handler.host_selfdestruct.clone(); + handler.host_selfdestruct = Arc::new( + move |context, address, target| -> Option { + let inspector = &mut context.external.get_inspector(); + let acc = context.evm.journaled_state.state.get(&address).unwrap(); + inspector.selfdestruct(address, target, acc.info.balance); + old_handle(context, address, target) + }, + ); } diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 3b72fff23b..8dea58fd6b 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -69,6 +69,7 @@ mod tests { use core::ops::Range; use crate::{ + handler::register::inspector_handle_register, inspectors::GasInspector, interpreter::{CallInputs, CreateInputs, Interpreter, InterpreterResult}, primitives::{Address, Bytes, B256}, @@ -150,6 +151,7 @@ mod tests { db::BenchmarkDB, interpreter::opcode, primitives::{address, Bytecode, Bytes, TransactTo}, + Evm, }; let contract_data: Bytes = Bytes::from(vec![ diff --git a/crates/revm/src/inspector/instruction.rs b/crates/revm/src/inspector/instruction.rs index 94a06b4f57..4a899f6e90 100644 --- a/crates/revm/src/inspector/instruction.rs +++ b/crates/revm/src/inspector/instruction.rs @@ -1,8 +1,8 @@ use crate::{ - handler::{register::GetInspector, InspectorHandle}, + handler::{inspector_handle_register, register::GetInspector}, Evm, }; -use alloc::boxed::Box; +use alloc::sync::Arc; use revm_interpreter::{ opcode::{BoxedInstruction, Instruction}, primitives::db::Database, @@ -10,20 +10,23 @@ use revm_interpreter::{ }; /// Outer closure that calls Inspector for every instruction. -pub fn inspector_instruction<'a, INSP: GetInspector<'a, DB> + 'a, DB: Database>( - instruction: Instruction, DB>>, -) -> BoxedInstruction<'a, Evm<'a, InspectorHandle<'a, DB, INSP>, DB>> { +pub fn inspector_instruction< + 'a, + INSP: GetInspector<'a, DB>, + DB: Database, + Instruction: Fn(&mut Interpreter, &mut Evm<'a, INSP, DB>) + 'a, +>( + instruction: Instruction, +) -> BoxedInstruction<'a, Evm<'a, INSP, DB>> { Box::new( - move |interpreter: &mut Interpreter, - host: &mut Evm<'a, InspectorHandle<'a, DB, INSP>, DB>| { + move |interpreter: &mut Interpreter, host: &mut Evm<'a, INSP, DB>| { // SAFETY: as the PC was already incremented we need to subtract 1 to preserve the // old Inspector behavior. interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.sub(1) }; host.context .external - .inspector - .get() + .get_inspector() .step(interpreter, &mut host.context.evm); if interpreter.instruction_result != InstructionResult::Continue { return; @@ -37,8 +40,7 @@ pub fn inspector_instruction<'a, INSP: GetInspector<'a, DB> + 'a, DB: Database>( host.context .external - .inspector - .get() + .get_inspector() .step_end(interpreter, &mut host.context.evm); }, ) @@ -53,13 +55,12 @@ mod tests { #[test] fn test_make_boxed_instruction_table() { // test that this pattern builds. - let inst: InstructionTable, EmptyDB>> = + let inst: InstructionTable> = make_instruction_table::, BerlinSpec>(); - let _test: BoxedInstructionTable<'_, Evm<'_, _, _>> = make_boxed_instruction_table::< - '_, - Evm<'_, InspectorHandle<'_, EmptyDB, NoOpInspector>, EmptyDB>, - BerlinSpec, - _, - >(inst, inspector_instruction); + let _test: BoxedInstructionTable<'_, Evm<'_, _, _>> = + make_boxed_instruction_table::<'_, Evm<'_, NoOpInspector, EmptyDB>, BerlinSpec, _>( + inst, + inspector_instruction, + ); } } diff --git a/crates/revm/src/inspector/noop.rs b/crates/revm/src/inspector/noop.rs index 05d7292fc4..e436df9092 100644 --- a/crates/revm/src/inspector/noop.rs +++ b/crates/revm/src/inspector/noop.rs @@ -1,7 +1,13 @@ -use crate::{Database, Inspector}; +use crate::{handler::register::GetInspector, Database, Inspector}; /// Dummy [Inspector], helpful as standalone replacement. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct NoOpInspector; impl Inspector for NoOpInspector {} + +impl<'a, DB: Database> GetInspector<'a, DB> for NoOpInspector { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } +} From 5ca95801327f9b2bb070bd01eaae7922f54ed51f Mon Sep 17 00:00:00 2001 From: rakita Date: Fri, 1 Dec 2023 01:09:40 +0100 Subject: [PATCH 15/52] wip on simple builder functions and handler registry --- crates/revm/src/evm.rs | 8 +- crates/revm/src/evm_builder.rs | 127 ++++++++++++++-------------- crates/revm/src/handler/register.rs | 19 ++++- crates/revm/src/inspector/gas.rs | 22 +++-- 4 files changed, 101 insertions(+), 75 deletions(-) diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 52bc7e8972..12b9ebcf89 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -2,7 +2,7 @@ use crate::optimism; use crate::{ db::{Database, EmptyDB}, - evm_builder::{BuilderStage, EvmBuilder, SettingDb, SettingExternal}, + evm_builder::{EvmBuilder, SettingDbStage, SettingHandlerStage}, handler::Handler, interpreter::{ opcode::InstructionTables, CallContext, CallInputs, CallScheme, CreateInputs, Host, @@ -20,7 +20,7 @@ use crate::{ CallStackFrame, Context, EvmContext, FrameOrResult, }; use alloc::{boxed::Box, vec::Vec}; -use core::{fmt, ops::Deref}; +use core::fmt; /// EVM call stack limit. pub const CALL_STACK_LIMIT: u64 = 1024; @@ -50,7 +50,7 @@ where impl<'a> Evm<'a, (), EmptyDB> { /// Returns evm builder. - pub fn builder() -> EvmBuilder<'a, SettingDb, (), EmptyDB> { + pub fn builder() -> EvmBuilder<'a, SettingDbStage, (), EmptyDB> { EvmBuilder::default() } } @@ -70,7 +70,7 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { /// Allow for evm setting to be modified by feeding current evm /// to the builder for modifications. - pub fn modify(self) -> EvmBuilder<'a, SettingExternal, EXT, DB> { + pub fn modify(self) -> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { EvmBuilder::new(self) } diff --git a/crates/revm/src/evm_builder.rs b/crates/revm/src/evm_builder.rs index 082060ad26..00ea186ae6 100644 --- a/crates/revm/src/evm_builder.rs +++ b/crates/revm/src/evm_builder.rs @@ -2,9 +2,14 @@ use core::marker::PhantomData; +use revm_interpreter::opcode::make_instruction_table; + use crate::{ db::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}, - handler::HandleRegister, + handler::{ + register::{self, RawInstructionTable}, + HandleRegister, + }, primitives::{BlockEnv, CfgEnv, Env, Spec, TxEnv}, primitives::{LatestSpec, SpecId}, Context, Evm, EvmContext, Handler, @@ -23,13 +28,16 @@ pub struct EvmBuilder<'a, Stage: BuilderStage, EXT, DB: Database> { pub trait BuilderStage {} -pub struct SettingDb; -impl BuilderStage for SettingDb {} +pub struct SettingDbStage; +impl BuilderStage for SettingDbStage {} + +pub struct SettingExternalStage; +impl BuilderStage for SettingExternalStage {} -pub struct SettingExternal; -impl BuilderStage for SettingExternal {} +pub struct SettingHandlerStage; +impl BuilderStage for SettingHandlerStage {} -impl<'a> Default for EvmBuilder<'a, SettingDb, (), EmptyDB> { +impl<'a> Default for EvmBuilder<'a, SettingDbStage, (), EmptyDB> { fn default() -> Self { Self { evm: EvmContext::new(EmptyDB::default()), @@ -41,13 +49,13 @@ impl<'a> Default for EvmBuilder<'a, SettingDb, (), EmptyDB> { } } -impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDb, EXT, DB> { +impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { /// Sets the [`Database`] that will be used by [`Evm`]. /// /// # Note /// /// When changed it will reset the handler to default mainnet. - pub fn with_db(self, db: ODB) -> EvmBuilder<'a, SettingExternal, EXT, ODB> { + pub fn with_db(self, db: ODB) -> EvmBuilder<'a, SettingExternalStage, EXT, ODB> { EvmBuilder { evm: EvmContext::new(db), external: self.external, @@ -64,7 +72,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDb, EXT, DB> { pub fn with_ref_db( self, db: ODB, - ) -> EvmBuilder<'a, SettingExternal, EXT, WrapDatabaseRef> { + ) -> EvmBuilder<'a, SettingExternalStage, EXT, WrapDatabaseRef> { let present_spec_id = self.handler.spec_id; let mut builder = EvmBuilder { @@ -78,30 +86,47 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDb, EXT, DB> { } } -impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternal, EXT, DB> { - pub fn new(evm: Evm<'a, EXT, DB>) -> Self { - Self { - evm: evm.context.evm, - external: evm.context.external, - handler: evm.handler, +impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternalStage, EXT, DB> { + pub fn with_empty_external(self) -> EvmBuilder<'a, SettingExternalStage, (), DB> { + EvmBuilder { + evm: self.evm, + external: (), + handler: Handler::mainnet::(), + handle_registers: Vec::new(), + phantom: PhantomData, + } + } + + pub fn with_external( + self, + external: OEXT, + ) -> EvmBuilder<'a, SettingHandlerStage, OEXT, DB> { + EvmBuilder { + evm: self.evm, + external: external, + handler: Handler::mainnet::(), handle_registers: Vec::new(), phantom: PhantomData, } } } -impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternal, EXT, DB> {} -/* -impl<'a, EXT: RegisterHandler<'a,DB,EXT>, DB: Database> EvmBuilder<'a, EXT, DB> { +impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { + /// Creates new build from EVM, evm is consumed and all field are moved to Builder. + /// + /// Builder is in SettingHandlerStage and both database and external are set. pub fn new(evm: Evm<'a, EXT, DB>) -> Self { Self { evm: evm.context.evm, external: evm.context.external, handler: evm.handler, + // TODO move registers from EVM + handle_registers: Vec::new(), + phantom: PhantomData, } } - /// Build Evm. + /// Consumes the Builder and build the Build Evm. pub fn build(self) -> Evm<'a, EXT, DB> { Evm { context: Context { @@ -116,7 +141,15 @@ impl<'a, EXT: RegisterHandler<'a,DB,EXT>, DB: Database> EvmBuilder<'a, EXT, DB> fn create_handle_generic( &self, ) -> Handler<'a, Evm<'a, EXT, DB>, EXT, DB> { - self.external.register_handle(Handler::mainnet::()) + let mut handler = Handler::mainnet::(); + let raw_table = + RawInstructionTable::PlainRaw(make_instruction_table::, SPEC>()); + // apply all registers to default handeler and raw mainnet instruction table. + for register in self.handle_registers.iter() { + register(&mut handler, &mut RawInstructionTable::Default); + } + handler.instruction_table = raw_table.into_arc(); + handler } /// Creates the Handler with variable SpecId, inside it will call function with Generic Spec. @@ -154,13 +187,24 @@ impl<'a, EXT: RegisterHandler<'a,DB,EXT>, DB: Database> EvmBuilder<'a, EXT, DB> /// /// # Note /// - /// When changed it will reset the handler to default mainnet. + /// When changed it will reapply all handle registers. pub fn with_spec_id(mut self, spec_id: SpecId) -> Self { // TODO add match for other spec self.handler = self.create_handler(spec_id); self } + /// Register Handler that modifies the behavior of EVM. + /// Check [`Handler`] for more information. + pub fn push_handler(mut self, handle_register: register::Register<'a, EXT, DB>) -> Self { + //self.handle_registers.push(handle_register); + self + } +} + +// Accessed always. + +impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> { /// Modify Environment of EVM. pub fn modify_env(mut self, f: impl FnOnce(&mut Env)) -> Self { f(&mut self.evm.env); @@ -184,45 +228,4 @@ impl<'a, EXT: RegisterHandler<'a,DB,EXT>, DB: Database> EvmBuilder<'a, EXT, DB> f(&mut self.evm.env.cfg); self } - - /// Sets the external data that can be used by Handler inside EVM. - /// - /// # Note - /// - /// When changed it will reset the handler to default mainnet. - pub fn with_external>( - self, - external: OEXT, - ) -> EvmBuilder<'a, OEXT, DB> { - let handler = external.register_handler::(Handler::mainnet::()); - EvmBuilder { - evm: self.evm, - external: external, - handler, - } - - let present_spec_id = self.handler.spec_id; - - let mut builder = EvmBuilder { - evm: EvmContext::new(WrapDatabaseRef(db)), - external: self.external, - handler: Handler::mainnet::(), - }; - builder.handler = builder.create_handler(present_spec_id); - builder - } - - /// Register Handler that modifies the behavior of EVM. - /// Check [`Handler`] for more information. - pub fn register_handler>( - self, - handler: H, - ) -> EvmBuilder<'a, EXT, DB> { - EvmBuilder { - evm: self.evm, - external: self.external, - handler: handler.register_handler::(self.handler), - } - } } -*/ diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index 8efada5409..f002fd187f 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -6,7 +6,7 @@ use crate::{ CallStackFrame, Evm, FrameOrResult, Inspector, }; use alloc::sync::Arc; -use revm_interpreter::opcode::{BoxedInstructionTable, InstructionTable}; +use revm_interpreter::opcode::{BoxedInstructionTable, InstructionTable, InstructionTables}; pub trait GetInspector<'a, DB: Database> { fn get_inspector(&mut self) -> &mut dyn Inspector; @@ -30,9 +30,24 @@ pub enum RawInstructionTable<'a, EXT, DB: Database> { BoxedRaw(BoxedInstructionTable<'a, Evm<'a, EXT, DB>>), } +impl<'a, EXT, DB: Database> RawInstructionTable<'a, EXT, DB> { + pub fn into_arc(self) -> InstructionTables<'a, Evm<'a, EXT, DB>> { + match self { + Self::Default => unimplemented!("Default instruction table is not supported"), + Self::PlainRaw(table) => InstructionTables::Plain(Arc::new(table)), + Self::BoxedRaw(table) => InstructionTables::Boxed(Arc::new(table)), + } + } +} + // Note that pub type HandleRegister<'a, EXT, DB> = - Arc, &mut RawInstructionTable<'a, EXT, DB>)>; + Box, &mut RawInstructionTable<'a, EXT, DB>)>; + +pub enum Register<'a, EXT, DB: Database> { + Plain(fn(&'a mut EvmHandler<'a, EXT, DB>, &'a mut RawInstructionTable<'a, EXT, DB>)), + Box(Box, &'a mut RawInstructionTable<'a, EXT, DB>)>), +} pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( handler: &'a mut EvmHandler<'a, EXT, DB>, diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 8dea58fd6b..08e4687b58 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -69,7 +69,7 @@ mod tests { use core::ops::Range; use crate::{ - handler::register::inspector_handle_register, + handler::register::{inspector_handle_register, GetInspector}, inspectors::GasInspector, interpreter::{CallInputs, CreateInputs, Interpreter, InterpreterResult}, primitives::{Address, Bytes, B256}, @@ -83,6 +83,12 @@ mod tests { gas_remaining_steps: Vec<(usize, u64)>, } + impl GetInspector<'_, DB> for StackInspector { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } + } + impl Inspector for StackInspector { fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.initialize_interp(interp, context); @@ -147,11 +153,13 @@ mod tests { #[test] #[cfg(not(feature = "optimism"))] fn test_gas_inspector() { + use alloc::sync::Arc; + use crate::{ db::BenchmarkDB, interpreter::opcode, primitives::{address, Bytecode, Bytes, TransactTo}, - Evm, + Evm, Transact, handler::register::Register, }; let contract_data: Bytes = Bytes::from(vec![ @@ -171,11 +179,9 @@ mod tests { ]); let bytecode = Bytecode::new_raw(contract_data); - let mut evm = Evm::builder() + let mut evm: Evm<'_, StackInspector, BenchmarkDB> = Evm::builder() .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) - .with_external(InspectorHandle::::new( - StackInspector::default(), - )) + .with_external(StackInspector::default()) .modify_tx_env(|tx| { tx.clear(); tx.caller = address!("1000000000000000000000000000000000000000"); @@ -183,12 +189,14 @@ mod tests { TransactTo::Call(address!("0000000000000000000000000000000000000000")); tx.gas_limit = 21100; }) + .push_handler(Register::Plain(inspector_handle_register)) + .push_handler(Register::Box(Box::new(inspector_handle_register))) .build(); // run evm. evm.transact().unwrap(); - let inspector = evm.into_context().external.inspector; + let inspector = evm.into_context().external; // starting from 100gas let steps = vec![ From aef8257ee07299aef87ce3eff9822a69a1f1f513 Mon Sep 17 00:00:00 2001 From: rakita Date: Fri, 1 Dec 2023 17:32:44 +0100 Subject: [PATCH 16/52] Examples and cleanup --- crates/interpreter/src/instructions/opcode.rs | 19 +- crates/primitives/src/env.rs | 14 +- .../revm/src/{evm_builder.rs => builder.rs} | 149 +++- crates/revm/src/evm.rs | 94 +- crates/revm/src/evm_impl.rs | 832 ------------------ crates/revm/src/handler.rs | 8 +- crates/revm/src/handler/register.rs | 73 +- crates/revm/src/lib.rs | 6 +- documentation/src/crates/revm/builder.md | 11 +- 9 files changed, 210 insertions(+), 996 deletions(-) rename crates/revm/src/{evm_builder.rs => builder.rs} (64%) delete mode 100644 crates/revm/src/evm_impl.rs diff --git a/crates/interpreter/src/instructions/opcode.rs b/crates/interpreter/src/instructions/opcode.rs index 5eac58ef56..a39352a62a 100644 --- a/crates/interpreter/src/instructions/opcode.rs +++ b/crates/interpreter/src/instructions/opcode.rs @@ -18,7 +18,7 @@ pub type Instruction = fn(&mut Interpreter, &mut H); pub type InstructionTable = [Instruction; 256]; /// Arc over plain instruction table -pub type InstructionTableArc = Arc>; +//pub type InstructionTableArc = Arc>; /// EVM opcode function signature. pub type BoxedInstruction<'a, H> = Box; @@ -26,8 +26,8 @@ pub type BoxedInstruction<'a, H> = Box; /// A table of instructions. pub type BoxedInstructionTable<'a, H> = [BoxedInstruction<'a, H>; 256]; -/// Arc over instruction table -pub type BoxedInstructionTableArc<'a, H> = Arc>; +// /// Arc over instruction table +// pub type BoxedInstructionTableArc<'a, H> = Arc>; /// Instruction set that contains plain instruction table that contains simple `fn` function pointer. /// and Boxed `Fn` variant that contains `Box` function pointer that can be used with closured. @@ -36,17 +36,8 @@ pub type BoxedInstructionTableArc<'a, H> = Arc>; /// /// Boxed variant can be used to wrap plain function pointer with closure. pub enum InstructionTables<'a, H: Host> { - Plain(InstructionTableArc), - Boxed(BoxedInstructionTableArc<'a, H>), -} - -impl<'a, H: Host> Clone for InstructionTables<'a, H> { - fn clone(&self) -> Self { - match self { - Self::Plain(table) => Self::Plain(table.clone()), - Self::Boxed(table) => Self::Boxed(table.clone()), - } - } + Plain(InstructionTable), + Boxed(BoxedInstructionTable<'a, H>), } macro_rules! opcodes { diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index ac899a0155..986ac060eb 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -19,6 +19,12 @@ pub struct Env { } impl Env { + /// Resets environment to default values. + #[inline] + pub fn clear(&mut self) { + *self = Self::default(); + } + /// Calculates the effective gas price of the transaction. #[inline] pub fn effective_gas_price(&self) -> U256 { @@ -176,7 +182,7 @@ impl Env { /// Validate transaction against state. #[inline] - pub fn validate_tx_against_state( + pub fn validate_tx_against_state( &self, account: &mut Account, ) -> Result<(), InvalidTransaction> { @@ -213,7 +219,7 @@ impl Env { .and_then(|gas_cost| gas_cost.checked_add(self.tx.value)) .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; - if SpecId::enabled(self.cfg.spec_id, SpecId::CANCUN) { + if SPEC::enabled(SpecId::CANCUN) { let data_fee = self.calc_data_fee().expect("already checked"); balance_check = balance_check .checked_add(U256::from(data_fee)) @@ -243,8 +249,9 @@ impl Env { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[non_exhaustive] pub struct CfgEnv { + /// Chain ID of the EVM, it will be compared to the transaction's Chain ID. + /// Chain ID is introduced EIP-155 pub chain_id: u64, - pub spec_id: SpecId, /// KZG Settings for point evaluation precompile. By default, this is loaded from the ethereum mainnet trusted setup. #[cfg(feature = "c-kzg")] #[cfg_attr(feature = "serde", serde(skip))] @@ -377,7 +384,6 @@ impl Default for CfgEnv { fn default() -> Self { Self { chain_id: 1, - spec_id: SpecId::LATEST, perf_analyse_created_bytecodes: AnalysisKind::default(), limit_contract_code_size: None, #[cfg(feature = "c-kzg")] diff --git a/crates/revm/src/evm_builder.rs b/crates/revm/src/builder.rs similarity index 64% rename from crates/revm/src/evm_builder.rs rename to crates/revm/src/builder.rs index 00ea186ae6..a1f36b8e18 100644 --- a/crates/revm/src/evm_builder.rs +++ b/crates/revm/src/builder.rs @@ -1,19 +1,13 @@ //! Evm Builder. -use core::marker::PhantomData; - -use revm_interpreter::opcode::make_instruction_table; - use crate::{ db::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}, - handler::{ - register::{self, RawInstructionTable}, - HandleRegister, - }, - primitives::{BlockEnv, CfgEnv, Env, Spec, TxEnv}, - primitives::{LatestSpec, SpecId}, + handler::{register, HandleRegister}, + interpreter::opcode::make_instruction_table, + primitives::{BlockEnv, CfgEnv, Env, LatestSpec, Spec, SpecId, TxEnv}, Context, Evm, EvmContext, Handler, }; +use core::marker::PhantomData; /// Evm Builder allows building or modifying EVM. /// Note that some of the methods that changes underlying structures @@ -22,7 +16,7 @@ pub struct EvmBuilder<'a, Stage: BuilderStage, EXT, DB: Database> { evm: EvmContext, external: EXT, handler: Handler<'a, Evm<'a, EXT, DB>, EXT, DB>, - handle_registers: Vec>, + handle_registers: Vec>, phantom: PhantomData, } @@ -50,6 +44,20 @@ impl<'a> Default for EvmBuilder<'a, SettingDbStage, (), EmptyDB> { } impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { + /// Sets the [`EmptyDB`] as the [`Database`] that will be used by [`Evm`]. + /// + /// # Note + /// + /// When changed it will reset the handler to the mainnet. + pub fn with_empty_db(self) -> EvmBuilder<'a, SettingExternalStage, EXT, EmptyDB> { + EvmBuilder { + evm: EvmContext::new(EmptyDB::default()), + external: self.external, + handler: Handler::mainnet::(), + handle_registers: Vec::new(), + phantom: PhantomData, + } + } /// Sets the [`Database`] that will be used by [`Evm`]. /// /// # Note @@ -73,21 +81,29 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { self, db: ODB, ) -> EvmBuilder<'a, SettingExternalStage, EXT, WrapDatabaseRef> { - let present_spec_id = self.handler.spec_id; - - let mut builder = EvmBuilder { + EvmBuilder { evm: EvmContext::new(WrapDatabaseRef(db)), external: self.external, handler: Handler::mainnet::(), handle_registers: Vec::new(), phantom: PhantomData, - }; - builder + } + } + + /// Build the [`Evm`] with [`EmptyDB`], [`LatestSpec`] and mainnet handler. + pub fn build(self) -> Evm<'a, EXT, DB> { + Evm { + context: Context { + evm: self.evm, + external: self.external, + }, + handler: self.handler, + } } } impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternalStage, EXT, DB> { - pub fn with_empty_external(self) -> EvmBuilder<'a, SettingExternalStage, (), DB> { + pub fn with_empty_external(self) -> EvmBuilder<'a, SettingHandlerStage, (), DB> { EvmBuilder { evm: self.evm, external: (), @@ -109,6 +125,17 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternalStage, EXT, DB> { phantom: PhantomData, } } + + /// Consumes the Builder and build the Build Evm with default mainnet handler. + pub fn build(self) -> Evm<'a, EXT, DB> { + Evm { + context: Context { + evm: self.evm, + external: self.external, + }, + handler: self.handler, + } + } } impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { @@ -142,13 +169,13 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { &self, ) -> Handler<'a, Evm<'a, EXT, DB>, EXT, DB> { let mut handler = Handler::mainnet::(); - let raw_table = - RawInstructionTable::PlainRaw(make_instruction_table::, SPEC>()); // apply all registers to default handeler and raw mainnet instruction table. for register in self.handle_registers.iter() { - register(&mut handler, &mut RawInstructionTable::Default); + register(&mut handler); + if handler.instruction_table.is_none() { + panic!("Handler must have instruction table") + } } - handler.instruction_table = raw_table.into_arc(); handler } @@ -194,9 +221,18 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { self } + pub fn push_handler(mut self, handle_register: register::HandleRegister<'a, EXT, DB>) -> Self { + self.handle_registers + .push(register::HandleRegisters::Plain(handle_register)); + self + } + /// Register Handler that modifies the behavior of EVM. /// Check [`Handler`] for more information. - pub fn push_handler(mut self, handle_register: register::Register<'a, EXT, DB>) -> Self { + pub fn push_handler_box( + mut self, + handle_register: register::HandleRegisterBox<'a, EXT, DB>, + ) -> Self { //self.handle_registers.push(handle_register); self } @@ -228,4 +264,73 @@ impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> f(&mut self.evm.env.cfg); self } + + /// Clear Environment of EVM. + pub fn clear_env(mut self) -> Self { + self.evm.env.clear(); + self + } + + /// Clear Transaction environment of EVM. + pub fn clear_tx_env(mut self) -> Self { + self.evm.env.tx.clear(); + self + } + /// Clear Block environment of EVM. + pub fn clear_block_env(mut self) -> Self { + self.evm.env.block.clear(); + self + } +} + +#[cfg(test)] +mod test { + use super::SpecId; + use crate::{ + db::EmptyDB, handler::register::inspector_handle_register, inspectors::NoOpInspector, Evm, + }; + + #[test] + fn simple_build() { + // build without external with latest spec + Evm::builder().build(); + // build with empty db + Evm::builder().with_empty_db().build(); + // build with_db + Evm::builder().with_db(EmptyDB::default()).build(); + // build with empty external + Evm::builder().with_empty_db().with_empty_external().build(); + // build with some external + Evm::builder().with_empty_db().with_external(()).build(); + // build with spec + Evm::builder() + .with_empty_db() + .with_empty_external() + .with_spec_id(SpecId::HOMESTEAD) + .build(); + + // with with Env change in multiple places + Evm::builder() + .with_empty_db() + .modify_tx_env(|tx| tx.gas_limit = 10) + .with_empty_external() + .build(); + Evm::builder().modify_tx_env(|tx| tx.gas_limit = 10).build(); + Evm::builder() + .with_empty_db() + .modify_tx_env(|tx| tx.gas_limit = 10) + .build(); + Evm::builder() + .with_empty_db() + .with_empty_external() + .modify_tx_env(|tx| tx.gas_limit = 10) + .build(); + + // with inspector handle + Evm::builder() + .with_empty_db() + .with_external(NoOpInspector::default()) + .push_handler(inspector_handle_register) + .build(); + } } diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 12b9ebcf89..f47c0416db 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -2,7 +2,7 @@ use crate::optimism; use crate::{ db::{Database, EmptyDB}, - evm_builder::{EvmBuilder, SettingDbStage, SettingHandlerStage}, + builder::{EvmBuilder, SettingDbStage, SettingHandlerStage}, handler::Handler, interpreter::{ opcode::InstructionTables, CallContext, CallInputs, CallScheme, CreateInputs, Host, @@ -198,8 +198,8 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { } } - /// Pre verify transaction. - pub fn preverify_transaction_inner(&mut self) -> Result<(), EVMError> { + /// Pre verify transaction. Returns initial gas spend + pub fn preverify_transaction_inner(&mut self) -> Result> { self.handler.validate_env(&self.context.evm.env)?; let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env); @@ -223,11 +223,13 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { .evm .env .validate_tx_against_state(caller_account) - .map_err(Into::into) + .map_err(Into::into)?; + + Ok(initial_gas_spend) } /// Transact preverified transaction. - pub fn transact_preverified_inner(&mut self) -> EVMResult { + pub fn transact_preverified_inner(&mut self, initial_gas_spend: u64) -> EVMResult { let env = &self.context.evm.env; let tx_caller = env.tx.caller; let tx_value = env.tx.value; @@ -254,8 +256,6 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { U256::ZERO }; - let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env); - // load coinbase // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm if self.spec_id().is_enabled_in(SHANGHAI) { @@ -367,11 +367,22 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { let interpreter_result = match first_stack_frame { FrameOrResult::Frame(first_stack_frame) => { created_address = first_stack_frame.created_address; - let table = self.handler.instruction_table.clone(); - match table { + // take instruction talbe + let table = self + .handler + .instruction_table + .take() + .expect("Instruction table should be present"); + + // run main loop + let output = match &table { InstructionTables::Plain(table) => self.run(&table, first_stack_frame), InstructionTables::Boxed(table) => self.run(&table, first_stack_frame), - } + }; + // return instruction table + self.handler.instruction_table = Some(table); + + output } FrameOrResult::Result(interpreter_result) => interpreter_result, }; @@ -406,27 +417,16 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { } } -/// EVM transaction interface. -pub trait Transact { - /// Run checks that could make transaction fail before call/create. - fn preverify_transaction(&mut self) -> Result<(), EVMError>; - - /// Skip pre-verification steps and execute the transaction. - fn transact_preverified(&mut self) -> EVMResult; - - /// Execute transaction by running pre-verification steps and then transaction itself. - fn transact(&mut self) -> EVMResult; -} - -impl<'a, EXT, DB: Database> Transact for Evm<'a, EXT, DB> { +impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { #[inline] fn preverify_transaction(&mut self) -> Result<(), EVMError> { - self.preverify_transaction_inner() + self.preverify_transaction_inner().map(|_| ()) } #[inline] fn transact_preverified(&mut self) -> EVMResult { - let output = self.transact_preverified_inner(); + let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env); + let output = self.transact_preverified_inner(initial_gas_spend); self.handler.end(&mut self.context, output) } @@ -434,7 +434,7 @@ impl<'a, EXT, DB: Database> Transact for Evm<'a, EXT, DB> { fn transact(&mut self) -> EVMResult { let output = self .preverify_transaction_inner() - .and_then(|()| self.transact_preverified_inner()); + .and_then(|initial_gas_spend| self.transact_preverified_inner(initial_gas_spend)); self.handler.end(&mut self.context, output) } } @@ -496,48 +496,6 @@ impl<'a, EXT, DB: Database> Host for Evm<'a, EXT, DB> { } } -/// Creates new EVM instance with erased types. -pub fn new_evm<'a, EXT, DB: Database + 'a>( - env: Box, - db: DB, - external: EXT, -) -> Evm<'a, EXT, DB> { - macro_rules! create_evm { - ($spec:ident) => {{ - Evm::new_with_spec( - db, - env, - external, - Handler::mainnet::<$spec>(), - Precompiles::new(revm_precompile::SpecId::from_spec_id($spec::SPEC_ID)).clone(), - ) - }}; - } - - use specification::*; - match env.cfg.spec_id { - SpecId::FRONTIER | SpecId::FRONTIER_THAWING => create_evm!(FrontierSpec), - SpecId::HOMESTEAD | SpecId::DAO_FORK => create_evm!(HomesteadSpec), - SpecId::TANGERINE => create_evm!(TangerineSpec), - SpecId::SPURIOUS_DRAGON => create_evm!(SpuriousDragonSpec), - SpecId::BYZANTIUM => create_evm!(ByzantiumSpec), - SpecId::PETERSBURG | SpecId::CONSTANTINOPLE => create_evm!(PetersburgSpec), - SpecId::ISTANBUL | SpecId::MUIR_GLACIER => create_evm!(IstanbulSpec), - SpecId::BERLIN => create_evm!(BerlinSpec), - SpecId::LONDON | SpecId::ARROW_GLACIER | SpecId::GRAY_GLACIER => { - create_evm!(LondonSpec) - } - SpecId::MERGE => create_evm!(MergeSpec), - SpecId::SHANGHAI => create_evm!(ShanghaiSpec), - SpecId::CANCUN => create_evm!(CancunSpec), - SpecId::LATEST => create_evm!(LatestSpec), - #[cfg(feature = "optimism")] - SpecId::BEDROCK => create_evm!(BedrockSpec), - #[cfg(feature = "optimism")] - SpecId::REGOLITH => create_evm!(RegolithSpec), - } -} - #[cfg(feature = "optimism")] #[cfg(test)] mod tests { diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs deleted file mode 100644 index 0b7a4a2c66..0000000000 --- a/crates/revm/src/evm_impl.rs +++ /dev/null @@ -1,832 +0,0 @@ -use crate::{ - db::Database, - handler::Handler, - inspector_instruction, - interpreter::{ - gas::initial_tx_gas, - opcode::{make_boxed_instruction_table, make_instruction_table, InstructionTables}, - CallContext, CallInputs, CallScheme, CreateInputs, Host, Interpreter, InterpreterAction, - InterpreterResult, SelfDestructResult, SharedMemory, Transfer, - }, - journaled_state::JournaledState, - precompile::Precompiles, - primitives::{ - specification, Address, Bytecode, Bytes, EVMError, EVMResult, Env, InvalidTransaction, Log, - Output, Spec, SpecId::*, TransactTo, B256, U256, - }, - CallStackFrame, EvmContext, Inspector, -}; -use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use auto_impl::auto_impl; -use core::{fmt, marker::PhantomData, ops::Range}; - -#[cfg(feature = "optimism")] -use crate::optimism; - -/// EVM call stack limit. -pub const CALL_STACK_LIMIT: u64 = 1024; - -pub struct EVMImpl<'a, SPEC: Spec, DB: Database> { - pub context: EvmContext<'a, DB>, - pub inspector: Option<&'a mut dyn Inspector>, - pub instruction_table: InstructionTables<'a, Self>, - pub handler: Handler, - _phantomdata: PhantomData, -} - -impl fmt::Debug for EVMImpl<'_, SPEC, DB> -where - SPEC: Spec, - DB: Database + fmt::Debug, - DB::Error: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("EVMImpl") - .field("data", &self.context) - .finish_non_exhaustive() - } -} - -#[cfg(feature = "optimism")] -impl<'a, SPEC: Spec, DB: Database> EVMImpl<'a, SPEC, DB> { - /// If the transaction is not a deposit transaction, subtract the L1 data fee from the - /// caller's balance directly after minting the requested amount of ETH. - fn remove_l1_cost( - is_deposit: bool, - tx_caller: Address, - l1_cost: U256, - db: &mut DB, - journal: &mut JournaledState, - ) -> Result<(), EVMError> { - if is_deposit { - return Ok(()); - } - let acc = journal - .load_account(tx_caller, db) - .map_err(EVMError::Database)? - .0; - if l1_cost.gt(&acc.info.balance) { - return Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(l1_cost), - balance: Box::new(acc.info.balance), - }, - )); - } - acc.info.balance = acc.info.balance.saturating_sub(l1_cost); - Ok(()) - } - - /// If the transaction is a deposit with a `mint` value, add the mint value - /// in wei to the caller's balance. This should be persisted to the database - /// prior to the rest of execution. - fn commit_mint_value( - tx_caller: Address, - tx_mint: Option, - db: &mut DB, - journal: &mut JournaledState, - ) -> Result<(), EVMError> { - if let Some(mint) = tx_mint { - journal - .load_account(tx_caller, db) - .map_err(EVMError::Database)? - .0 - .info - .balance += U256::from(mint); - journal.checkpoint(); - } - Ok(()) - } -} - -impl<'a, SPEC: Spec + 'static, DB: Database> EVMImpl<'a, SPEC, DB> { - pub fn new_with_spec( - db: &'a mut DB, - env: &'a mut Env, - inspector: Option<&'a mut dyn Inspector>, - precompiles: Precompiles, - ) -> Self { - let journaled_state = - JournaledState::new(SPEC::SPEC_ID, precompiles.addresses().copied().collect()); - // If T is present it should be a generic T that modifies handler. - let instruction_table = if inspector.is_some() { - let instruction_table = make_boxed_instruction_table::( - make_instruction_table::(), - inspector_instruction, - ); - InstructionTables::Boxed(Arc::new(instruction_table)) - } else { - InstructionTables::Plain(Arc::new(make_instruction_table::())) - }; - #[cfg(feature = "optimism")] - let mut handler = if env.cfg.optimism { - Handler::optimism::() - } else { - Handler::mainnet::() - }; - #[cfg(not(feature = "optimism"))] - let mut handler = Handler::mainnet::(); - - if env.cfg.is_beneficiary_reward_disabled() { - // do nothing - handler.reward_beneficiary = |_, _| Ok(()); - } - - Self { - context: EvmContext { - env, - journaled_state, - db, - error: None, - precompiles, - #[cfg(feature = "optimism")] - l1_block_info: None, - }, - inspector, - instruction_table, - handler, - _phantomdata: PhantomData {}, - } - } - - #[inline] - pub fn run( - &mut self, - instruction_table: &[FN; 256], - first_frame: Box, - ) -> InterpreterResult - where - FN: Fn(&mut Interpreter, &mut Self), - { - let mut call_stack: Vec> = Vec::with_capacity(1025); - call_stack.push(first_frame); - - #[cfg(feature = "memory_limit")] - let mut shared_memory = - SharedMemory::new_with_memory_limit(self.context.env.cfg.memory_limit); - #[cfg(not(feature = "memory_limit"))] - let mut shared_memory = SharedMemory::new(); - - shared_memory.new_context(); - - let mut stack_frame = call_stack.first_mut().unwrap(); - - loop { - // run interpreter - let action = stack_frame - .interpreter - .run(shared_memory, instruction_table, self); - // take shared memory back. - shared_memory = stack_frame.interpreter.take_memory(); - - let new_frame = match action { - InterpreterAction::SubCall { - inputs, - return_memory_offset, - } => self.handle_sub_call( - inputs, - stack_frame, - return_memory_offset, - &mut shared_memory, - ), - InterpreterAction::Create { inputs } => self.handle_sub_create(inputs, stack_frame), - InterpreterAction::Return { result } => { - // free memory context. - shared_memory.free_context(); - - let child = call_stack.pop().unwrap(); - let parent = call_stack.last_mut(); - - if let Some(result) = - self.handle_frame_return(child, parent, &mut shared_memory, result) - { - return result; - } - stack_frame = call_stack.last_mut().unwrap(); - continue; - } - }; - if let Some(new_frame) = new_frame { - shared_memory.new_context(); - call_stack.push(new_frame); - } - stack_frame = call_stack.last_mut().unwrap(); - } - } - - fn handle_frame_return( - &mut self, - mut child_stack_frame: Box, - parent_stack_frame: Option<&mut Box>, - shared_memory: &mut SharedMemory, - mut result: InterpreterResult, - ) -> Option { - if let Some(inspector) = self.inspector.as_mut() { - result = if child_stack_frame.is_create { - let (result, address) = inspector.create_end( - &mut self.context, - result, - child_stack_frame.created_address, - ); - child_stack_frame.created_address = address; - result - } else { - inspector.call_end(&mut self.context, result) - }; - } - - // break from loop if this is last CallStackFrame. - let Some(parent_stack_frame) = parent_stack_frame else { - let result = if child_stack_frame.is_create { - self.context - .create_return::(result, child_stack_frame) - .0 - } else { - self.context.call_return(result, child_stack_frame) - }; - - return Some(result); - }; - - if child_stack_frame.is_create { - let (result, address) = self - .context - .create_return::(result, child_stack_frame); - parent_stack_frame - .interpreter - .insert_create_output(result, Some(address)) - } else { - let subcall_memory_return_offset = - child_stack_frame.subcall_return_memory_range.clone(); - let result = self.context.call_return(result, child_stack_frame); - - parent_stack_frame.interpreter.insert_call_output( - shared_memory, - result, - subcall_memory_return_offset, - ) - } - None - } - - /// Handle Action for new sub create call, return None if there is no need - /// to add new stack frame. - #[inline] - fn handle_sub_create( - &mut self, - mut inputs: Box, - curent_stack_frame: &mut CallStackFrame, - ) -> Option> { - // Call inspector if it is some. - if let Some(inspector) = self.inspector.as_mut() { - if let Some((result, address)) = inspector.create(&mut self.context, &mut inputs) { - curent_stack_frame - .interpreter - .insert_create_output(result, address); - return None; - } - } - - match self.context.make_create_frame::(&inputs) { - Ok(new_frame) => Some(new_frame), - Err(mut result) => { - let mut address = None; - if let Some(inspector) = self.inspector.as_mut() { - let ret = inspector.create_end( - &mut self.context, - result, - curent_stack_frame.created_address, - ); - result = ret.0; - address = ret.1; - } - // insert result of the failed creation of create CallStackFrame. - curent_stack_frame - .interpreter - .insert_create_output(result, address); - None - } - } - } - - /// Handles action for new sub call, return None if there is no need to add - /// new stack frame. - #[inline] - fn handle_sub_call( - &mut self, - mut inputs: Box, - curent_stake_frame: &mut CallStackFrame, - return_memory_offset: Range, - shared_memory: &mut SharedMemory, - ) -> Option> { - // Call inspector if it is some. - if let Some(inspector) = self.inspector.as_mut() { - if let Some((result, range)) = inspector.call(&mut self.context, &mut inputs) { - curent_stake_frame - .interpreter - .insert_call_output(shared_memory, result, range); - return None; - } - } - match self - .context - .make_call_frame(&inputs, return_memory_offset.clone()) - { - Ok(new_frame) => Some(new_frame), - Err(mut result) => { - if let Some(inspector) = &mut self.inspector { - result = inspector.call_end(&mut self.context, result); - } - curent_stake_frame.interpreter.insert_call_output( - shared_memory, - result, - return_memory_offset, - ); - None - } - } - } - - /// Pre verify transaction. - pub fn preverify_transaction_inner(&mut self) -> Result<(), EVMError> { - let env = self.env(); - - // Important: validate block before tx. - env.validate_block_env::()?; - env.validate_tx::()?; - - let initial_gas_spend = initial_tx_gas::( - &env.tx.data, - env.tx.transact_to.is_create(), - &env.tx.access_list, - ); - - // Additional check to see if limit is big enough to cover initial gas. - if initial_gas_spend > env.tx.gas_limit { - return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into()); - } - - // load acc - let tx_caller = env.tx.caller; - let (caller_account, _) = self - .context - .journaled_state - .load_account(tx_caller, self.context.db) - .map_err(EVMError::Database)?; - - self.context - .env - .validate_tx_against_state(caller_account) - .map_err(Into::into) - } - - /// Transact preverified transaction. - pub fn transact_preverified_inner(&mut self) -> EVMResult { - let env = &self.context.env; - let tx_caller = env.tx.caller; - let tx_value = env.tx.value; - let tx_data = env.tx.data.clone(); - let tx_gas_limit = env.tx.gas_limit; - - // the L1-cost fee is only computed for Optimism non-deposit transactions. - #[cfg(feature = "optimism")] - let tx_l1_cost = if env.cfg.optimism && env.tx.optimism.source_hash.is_none() { - let l1_block_info = - optimism::L1BlockInfo::try_fetch(self.context.db).map_err(EVMError::Database)?; - - let Some(enveloped_tx) = &env.tx.optimism.enveloped_tx else { - panic!("[OPTIMISM] Failed to load enveloped transaction."); - }; - let tx_l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx); - - // storage l1 block info for later use. - self.context.l1_block_info = Some(l1_block_info); - - tx_l1_cost - } else { - U256::ZERO - }; - - let initial_gas_spend = initial_tx_gas::( - &tx_data, - env.tx.transact_to.is_create(), - &env.tx.access_list, - ); - - // load coinbase - // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm - if SPEC::enabled(SHANGHAI) { - self.context - .journaled_state - .initial_account_load(self.context.env.block.coinbase, &[], self.context.db) - .map_err(EVMError::Database)?; - } - - self.context.load_access_list()?; - - // load acc - let journal = &mut self.context.journaled_state; - - #[cfg(feature = "optimism")] - if self.context.env.cfg.optimism { - EVMImpl::::commit_mint_value( - tx_caller, - self.context.env.tx.optimism.mint, - self.context.db, - journal, - )?; - - let is_deposit = self.context.env.tx.optimism.source_hash.is_some(); - EVMImpl::::remove_l1_cost( - is_deposit, - tx_caller, - tx_l1_cost, - self.context.db, - journal, - )?; - } - - let (caller_account, _) = journal - .load_account(tx_caller, self.context.db) - .map_err(EVMError::Database)?; - - // Subtract gas costs from the caller's account. - // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. - let mut gas_cost = - U256::from(tx_gas_limit).saturating_mul(self.context.env.effective_gas_price()); - - // EIP-4844 - if SPEC::enabled(CANCUN) { - let data_fee = self.context.env.calc_data_fee().expect("already checked"); - gas_cost = gas_cost.saturating_add(data_fee); - } - - caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); - - // touch account so we know it is changed. - caller_account.mark_touch(); - - let transact_gas_limit = tx_gas_limit - initial_gas_spend; - - // call inner handling of call/create - let first_stack_frame = match self.context.env.tx.transact_to { - TransactTo::Call(address) => { - // Nonce is already checked - caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); - - self.context.make_call_frame( - &CallInputs { - contract: address, - transfer: Transfer { - source: tx_caller, - target: address, - value: tx_value, - }, - input: tx_data, - gas_limit: transact_gas_limit, - context: CallContext { - caller: tx_caller, - address, - code_address: address, - apparent_value: tx_value, - scheme: CallScheme::Call, - }, - is_static: false, - }, - 0..0, - ) - } - TransactTo::Create(scheme) => self.context.make_create_frame::(&CreateInputs { - caller: tx_caller, - scheme, - value: tx_value, - init_code: tx_data, - gas_limit: transact_gas_limit, - }), - }; - // Some only if it is create. - let mut created_address = None; - - // start main loop if CallStackFrame is created correctly - let interpreter_result = match first_stack_frame { - Ok(first_stack_frame) => { - created_address = first_stack_frame.created_address; - let table = self.instruction_table.clone(); - match table { - InstructionTables::Plain(table) => self.run(&table, first_stack_frame), - InstructionTables::Boxed(table) => self.run(&table, first_stack_frame), - } - } - Err(interpreter_result) => interpreter_result, - }; - - let handler = &self.handler; - let data = &mut self.context; - - // handle output of call/create calls. - let mut gas = - handler.call_return(data.env, interpreter_result.result, interpreter_result.gas); - - // set refund. Refund amount depends on hardfork. - gas.set_refund(handler.calculate_gas_refund(data.env, &gas) as i64); - - // Reimburse the caller - handler.reimburse_caller(data, &gas)?; - - // Reward beneficiary - handler.reward_beneficiary(data, &gas)?; - - // output of execution - let output = match data.env.tx.transact_to { - TransactTo::Call(_) => Output::Call(interpreter_result.output), - TransactTo::Create(_) => Output::Create(interpreter_result.output, created_address), - }; - - // main return - handler.main_return(data, interpreter_result.result, output, &gas) - } -} - -/// EVM transaction interface. -#[auto_impl(&mut, Box)] -pub trait Transact { - /// Run checks that could make transaction fail before call/create. - fn preverify_transaction(&mut self) -> Result<(), EVMError>; - - /// Skip pre-verification steps and execute the transaction. - fn transact_preverified(&mut self) -> EVMResult; - - /// Execute transaction by running pre-verification steps and then transaction itself. - fn transact(&mut self) -> EVMResult; -} - -impl<'a, SPEC: Spec + 'static, DB: Database> Transact for EVMImpl<'a, SPEC, DB> { - #[inline] - fn preverify_transaction(&mut self) -> Result<(), EVMError> { - self.preverify_transaction_inner() - } - - #[inline] - fn transact_preverified(&mut self) -> EVMResult { - let output = self.transact_preverified_inner(); - self.handler.end(&mut self.context, output) - } - - #[inline] - fn transact(&mut self) -> EVMResult { - let output = self - .preverify_transaction_inner() - .and_then(|()| self.transact_preverified_inner()); - self.handler.end(&mut self.context, output) - } -} - -impl<'a, SPEC: Spec + 'static, DB: Database> Host for EVMImpl<'a, SPEC, DB> { - fn env(&mut self) -> &mut Env { - self.context.env() - } - - fn block_hash(&mut self, number: U256) -> Option { - self.context.block_hash(number) - } - - fn load_account(&mut self, address: Address) -> Option<(bool, bool)> { - self.context.load_account(address) - } - - fn balance(&mut self, address: Address) -> Option<(U256, bool)> { - self.context.balance(address) - } - - fn code(&mut self, address: Address) -> Option<(Bytecode, bool)> { - self.context.code(address) - } - - /// Get code hash of address. - fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { - self.context.code_hash(address) - } - - fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { - self.context.sload(address, index) - } - - fn sstore( - &mut self, - address: Address, - index: U256, - value: U256, - ) -> Option<(U256, U256, U256, bool)> { - self.context.sstore(address, index, value) - } - - fn tload(&mut self, address: Address, index: U256) -> U256 { - self.context.tload(address, index) - } - - fn tstore(&mut self, address: Address, index: U256, value: U256) { - self.context.tstore(address, index, value) - } - - fn log(&mut self, address: Address, topics: Vec, data: Bytes) { - if let Some(inspector) = self.inspector.as_mut() { - inspector.log(&mut self.context, &address, &topics, &data); - } - let log = Log { - address, - topics, - data, - }; - self.context.journaled_state.log(log); - } - - fn selfdestruct(&mut self, address: Address, target: Address) -> Option { - if let Some(inspector) = self.inspector.as_mut() { - let acc = self.context.journaled_state.state.get(&address).unwrap(); - inspector.selfdestruct(address, target, acc.info.balance); - } - self.context - .journaled_state - .selfdestruct(address, target, self.context.db) - .map_err(|e| self.context.error = Some(e)) - .ok() - } -} - -/// Creates new EVM instance with erased types. -pub fn new_evm<'a, DB: Database>( - env: &'a mut Env, - db: &'a mut DB, - insp: Option<&'a mut dyn Inspector>, -) -> Box + 'a> { - macro_rules! create_evm { - ($spec:ident) => { - Box::new(EVMImpl::<'a, $spec, DB>::new_with_spec( - db, - env, - insp, - Precompiles::new(revm_precompile::SpecId::from_spec_id($spec::SPEC_ID)).clone(), - )) - }; - } - - use specification::*; - match env.cfg.spec_id { - SpecId::FRONTIER | SpecId::FRONTIER_THAWING => create_evm!(FrontierSpec), - SpecId::HOMESTEAD | SpecId::DAO_FORK => create_evm!(HomesteadSpec), - SpecId::TANGERINE => create_evm!(TangerineSpec), - SpecId::SPURIOUS_DRAGON => create_evm!(SpuriousDragonSpec), - SpecId::BYZANTIUM => create_evm!(ByzantiumSpec), - SpecId::PETERSBURG | SpecId::CONSTANTINOPLE => create_evm!(PetersburgSpec), - SpecId::ISTANBUL | SpecId::MUIR_GLACIER => create_evm!(IstanbulSpec), - SpecId::BERLIN => create_evm!(BerlinSpec), - SpecId::LONDON | SpecId::ARROW_GLACIER | SpecId::GRAY_GLACIER => { - create_evm!(LondonSpec) - } - SpecId::MERGE => create_evm!(MergeSpec), - SpecId::SHANGHAI => create_evm!(ShanghaiSpec), - SpecId::CANCUN => create_evm!(CancunSpec), - SpecId::LATEST => create_evm!(LatestSpec), - #[cfg(feature = "optimism")] - SpecId::BEDROCK => create_evm!(BedrockSpec), - #[cfg(feature = "optimism")] - SpecId::REGOLITH => create_evm!(RegolithSpec), - #[cfg(feature = "optimism")] - SpecId::CANYON => create_evm!(CanyonSpec), - } -} - -#[cfg(feature = "optimism")] -#[cfg(test)] -mod tests { - use super::*; - - use crate::db::InMemoryDB; - use crate::primitives::{specification::BedrockSpec, state::AccountInfo, SpecId}; - - #[test] - fn test_commit_mint_value() { - let caller = Address::ZERO; - let mint_value = Some(1u128); - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert!(EVMImpl::::commit_mint_value( - caller, - mint_value, - &mut db, - &mut journal - ) - .is_ok(),); - - // Check the account balance is updated. - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(101)); - - // No mint value should be a no-op. - assert!(EVMImpl::::commit_mint_value( - caller, - None, - &mut db, - &mut journal - ) - .is_ok(),); - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(101)); - } - - #[test] - fn test_remove_l1_cost_non_deposit() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - let slots = &[U256::from(100)]; - journal - .initial_account_load(caller, slots, &mut db) - .unwrap(); - assert!(EVMImpl::::remove_l1_cost( - true, - caller, - U256::ZERO, - &mut db, - &mut journal - ) - .is_ok(),); - } - - #[test] - fn test_remove_l1_cost() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert!(EVMImpl::::remove_l1_cost( - false, - caller, - U256::from(1), - &mut db, - &mut journal - ) - .is_ok(),); - - // Check the account balance is updated. - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(99)); - } - - #[test] - fn test_remove_l1_cost_lack_of_funds() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert_eq!( - EVMImpl::::remove_l1_cost( - false, - caller, - U256::from(101), - &mut db, - &mut journal - ), - Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(U256::from(101)), - balance: Box::new(U256::from(100)), - }, - )) - ); - } -} diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index f8ef85cae8..d7fd76c968 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -131,11 +131,13 @@ pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { /// Specification ID. pub spec_id: SpecId, /// Instruction table type. - pub instruction_table: InstructionTables<'a, H>, + pub instruction_table: Option>, /// Initial tx gas. pub initial_tx_gas: InitialTxGasHandle<'a>, /// Validate Env pub validate_env: ValidateEnvHandle<'a, DB>, + /// Validate Transaction against the state. + // Uses env, call result and returned gas from the call to determine the gas // that is returned from transaction execution.. pub call_return: CallReturnHandle<'a>, @@ -167,6 +169,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { pub fn mainnet() -> Self { Self { spec_id: SPEC::SPEC_ID, + instruction_table: Some(InstructionTables::Plain(make_instruction_table::())), initial_tx_gas: Arc::new(mainnet::preexecution::initial_tx_gas::), validate_env: Arc::new(mainnet::preexecution::validate_env::), call_return: Arc::new(mainnet::handle_call_return::), @@ -175,9 +178,6 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), main_return: Arc::new(mainnet::main::main_return::), end: Arc::new(mainnet::main::end_handle::), - instruction_table: InstructionTables::Plain(Arc::new( - make_instruction_table::(), - )), frame_return: Arc::new(mainnet::frames::handle_frame_return::), frame_sub_call: Arc::new(mainnet::frames::handle_frame_sub_call::), frame_sub_create: Arc::new(mainnet::frames::handle_frame_sub_create::), diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index f002fd187f..0fd5d730f7 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -22,63 +22,42 @@ pub trait GetInspector<'a, DB: Database> { /// pub type EvmHandler<'a, EXT, DB> = Handler<'a, Evm<'a, EXT, DB>, EXT, DB>; -#[derive(Default)] -pub enum RawInstructionTable<'a, EXT, DB: Database> { - #[default] - Default, - PlainRaw(InstructionTable>), - BoxedRaw(BoxedInstructionTable<'a, Evm<'a, EXT, DB>>), -} +pub type EvmInstructionTables<'a, EXT, DB> = InstructionTables<'a, Evm<'a, EXT, DB>>; -impl<'a, EXT, DB: Database> RawInstructionTable<'a, EXT, DB> { - pub fn into_arc(self) -> InstructionTables<'a, Evm<'a, EXT, DB>> { - match self { - Self::Default => unimplemented!("Default instruction table is not supported"), - Self::PlainRaw(table) => InstructionTables::Plain(Arc::new(table)), - Self::BoxedRaw(table) => InstructionTables::Boxed(Arc::new(table)), - } - } -} +// Handle register +pub type HandleRegister<'a, EXT, DB> = fn(&mut EvmHandler<'a, EXT, DB>); -// Note that -pub type HandleRegister<'a, EXT, DB> = - Box, &mut RawInstructionTable<'a, EXT, DB>)>; +// Boxed handle register +pub type HandleRegisterBox<'a, EXT, DB> = Box)>; -pub enum Register<'a, EXT, DB: Database> { - Plain(fn(&'a mut EvmHandler<'a, EXT, DB>, &'a mut RawInstructionTable<'a, EXT, DB>)), - Box(Box, &'a mut RawInstructionTable<'a, EXT, DB>)>), +pub enum HandleRegisters<'a, EXT, DB: Database> { + Plain(HandleRegister<'a, EXT, DB>), + Box(HandleRegisterBox<'a, EXT, DB>), } pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( handler: &'a mut EvmHandler<'a, EXT, DB>, - instruction_table: &'a mut RawInstructionTable<'a, EXT, DB>, ) { let spec_id = handler.spec_id; - let taken_table = core::mem::take(instruction_table); // Every instruction inside flat table that is going to be wrapped by inspector calls. - match taken_table { - RawInstructionTable::PlainRaw(table) => { - *instruction_table = RawInstructionTable::BoxedRaw( - table - .into_iter() - .map(|i| inspector_instruction(i)) - .collect::>() - .try_into() - .unwrap_or_else(|_| unreachable!()), - ); - } - RawInstructionTable::BoxedRaw(table) => { - *instruction_table = RawInstructionTable::BoxedRaw( - table - .into_iter() - .map(|i| inspector_instruction(i)) - .collect::>() - .try_into() - .unwrap_or_else(|_| unreachable!()), - ); - } - _ => unreachable!(), - } + let table = handler + .instruction_table + .take() + .expect("Handler must have instruction table"); + let table = match table { + EvmInstructionTables::Plain(table) => table + .into_iter() + .map(|i| inspector_instruction(i)) + .collect::>(), + EvmInstructionTables::Boxed(table) => table + .into_iter() + .map(|i| inspector_instruction(i)) + .collect::>(), + }; + + handler.instruction_table = Some(EvmInstructionTables::Boxed( + table.try_into().unwrap_or_else(|_| unreachable!()), + )); // handle sub create handler.frame_sub_create = Arc::new( diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 30995a1024..a2b7f3c00d 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -15,7 +15,7 @@ extern crate alloc; mod context; pub mod db; mod evm; -mod evm_builder; +mod builder; mod frame; pub mod handler; mod inspector; @@ -31,8 +31,8 @@ pub use db::{ CacheState, DBBox, State, StateBuilder, StateDBBox, TransitionAccount, TransitionState, }; pub use db::{Database, DatabaseCommit, DatabaseRef, DummyStateDB, InMemoryDB}; -pub use evm::{new_evm, Evm, Transact, CALL_STACK_LIMIT}; -pub use evm_builder::EvmBuilder; +pub use evm::{Evm, CALL_STACK_LIMIT}; +pub use builder::EvmBuilder; pub use frame::{CallStackFrame, FrameOrResult}; pub use handler::Handler; pub use inspector::{inspector_instruction, inspectors, Inspector}; diff --git a/documentation/src/crates/revm/builder.md b/documentation/src/crates/revm/builder.md index 66a00020b4..a8a38ade84 100644 --- a/documentation/src/crates/revm/builder.md +++ b/documentation/src/crates/revm/builder.md @@ -1,6 +1,13 @@ # Evm Builder -Is a helper function that allows easier setting of database external and logic structures. +Is a helper that allows easier setting of database, external and logic structures. -There is a dependency between Database, External and Spec types so setting Database will reset External and Handle field while setting External field would reset Handler. Note that Database will never be reset. \ No newline at end of file +It ties dependency between Database, External and Spec and allows setting custom logic. As there is a dependency between them setting Database will reset External and Handle field while setting External field would reset Handler. Note that Database will never be reset. + +Simple example of using `EvmBuilder` is + +``` + +Evm::build().with_empty_db().with_empty_external() +``` \ No newline at end of file From bebc79d251841bf799e22b00524c566dd4aded69 Mon Sep 17 00:00:00 2001 From: rakita Date: Fri, 1 Dec 2023 17:33:59 +0100 Subject: [PATCH 17/52] fix lifetime and fmt --- crates/revm/src/evm.rs | 2 +- crates/revm/src/handler.rs | 1 - crates/revm/src/handler/register.rs | 2 +- crates/revm/src/inspector/gas.rs | 3 ++- crates/revm/src/lib.rs | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index f47c0416db..898647f818 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,8 +1,8 @@ #[cfg(feature = "optimism")] use crate::optimism; use crate::{ - db::{Database, EmptyDB}, builder::{EvmBuilder, SettingDbStage, SettingHandlerStage}, + db::{Database, EmptyDB}, handler::Handler, interpreter::{ opcode::InstructionTables, CallContext, CallInputs, CallScheme, CreateInputs, Host, diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index d7fd76c968..307ab678e5 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -137,7 +137,6 @@ pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { /// Validate Env pub validate_env: ValidateEnvHandle<'a, DB>, /// Validate Transaction against the state. - // Uses env, call result and returned gas from the call to determine the gas // that is returned from transaction execution.. pub call_return: CallReturnHandle<'a>, diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index 0fd5d730f7..3118f89011 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -36,7 +36,7 @@ pub enum HandleRegisters<'a, EXT, DB: Database> { } pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( - handler: &'a mut EvmHandler<'a, EXT, DB>, + handler: &mut EvmHandler<'a, EXT, DB>, ) { let spec_id = handler.spec_id; // Every instruction inside flat table that is going to be wrapped by inspector calls. diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 08e4687b58..40f460b7eb 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -157,9 +157,10 @@ mod tests { use crate::{ db::BenchmarkDB, + handler::register::Register, interpreter::opcode, primitives::{address, Bytecode, Bytes, TransactTo}, - Evm, Transact, handler::register::Register, + Evm, Transact, }; let contract_data: Bytes = Bytes::from(vec![ diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index a2b7f3c00d..80028451b6 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -12,10 +12,10 @@ extern crate alloc; // Define modules. +mod builder; mod context; pub mod db; mod evm; -mod builder; mod frame; pub mod handler; mod inspector; @@ -25,6 +25,7 @@ pub mod optimism; // Export items. +pub use builder::EvmBuilder; pub use context::{Context, EvmContext}; #[cfg(feature = "std")] pub use db::{ @@ -32,7 +33,6 @@ pub use db::{ }; pub use db::{Database, DatabaseCommit, DatabaseRef, DummyStateDB, InMemoryDB}; pub use evm::{Evm, CALL_STACK_LIMIT}; -pub use builder::EvmBuilder; pub use frame::{CallStackFrame, FrameOrResult}; pub use handler::Handler; pub use inspector::{inspector_instruction, inspectors, Inspector}; From 580ddf8db1f3ac8beea0006eda9d906957f6830d Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 5 Dec 2023 17:49:43 +0100 Subject: [PATCH 18/52] Add more handlers, deduct caller, validate tx agains state --- crates/revm/src/builder.rs | 2 +- crates/revm/src/evm.rs | 148 ++++++------------ crates/revm/src/handler.rs | 47 ++++-- crates/revm/src/handler/mainnet.rs | 44 +++++- .../revm/src/handler/mainnet/preexecution.rs | 34 +++- crates/revm/src/handler/optimism.rs | 8 + crates/revm/src/handler/register.rs | 9 ++ crates/revm/src/inspector/gas.rs | 9 +- crates/revm/src/optimism.rs | 1 - 9 files changed, 177 insertions(+), 125 deletions(-) diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index a1f36b8e18..af3939025f 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -171,7 +171,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { let mut handler = Handler::mainnet::(); // apply all registers to default handeler and raw mainnet instruction table. for register in self.handle_registers.iter() { - register(&mut handler); + register.register(&mut handler); if handler.instruction_table.is_none() { panic!("Handler must have instruction table") } diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 898647f818..89a6d8fcfb 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -12,10 +12,8 @@ use crate::{ journaled_state::JournaledState, precompile::Precompiles, primitives::{ - specification::{self, SpecId}, - Address, Bytecode, Bytes, EVMError, EVMResult, Env, HashSet, InvalidTransaction, Output, - SpecId::*, - TransactTo, B256, U256, + specification::SpecId, Address, Bytecode, Bytes, EVMError, EVMResult, Env, HashSet, Output, + SpecId::*, TransactTo, B256, U256, }, CallStackFrame, Context, EvmContext, FrameOrResult, }; @@ -198,44 +196,9 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { } } - /// Pre verify transaction. Returns initial gas spend - pub fn preverify_transaction_inner(&mut self) -> Result> { - self.handler.validate_env(&self.context.evm.env)?; - let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env); - - let env = self.env(); - - // Additional check to see if limit is big enough to cover initial gas. - if initial_gas_spend > env.tx.gas_limit { - return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into()); - } - - // load acc - let tx_caller = env.tx.caller; - let (caller_account, _) = self - .context - .evm - .journaled_state - .load_account(tx_caller, &mut self.context.evm.db) - .map_err(EVMError::Database)?; - - self.context - .evm - .env - .validate_tx_against_state(caller_account) - .map_err(Into::into)?; - - Ok(initial_gas_spend) - } - /// Transact preverified transaction. pub fn transact_preverified_inner(&mut self, initial_gas_spend: u64) -> EVMResult { - let env = &self.context.evm.env; - let tx_caller = env.tx.caller; - let tx_value = env.tx.value; - let tx_data = env.tx.data.clone(); - let tx_gas_limit = env.tx.gas_limit; - + let tx_caller = self.context.evm.env.tx.caller; // the L1-cost fee is only computed for Optimism non-deposit transactions. #[cfg(feature = "optimism")] let tx_l1_cost = if env.cfg.optimism && env.tx.optimism.source_hash.is_none() { @@ -272,11 +235,11 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { self.context.evm.load_access_list()?; - // load acc - let journal = &mut self.context.evm.journaled_state; - #[cfg(feature = "optimism")] if self.context.evm.env.cfg.optimism { + // load acc + let journal = &mut self.context.evm.journaled_state; + crate::optimism::commit_mint_value( tx_caller, self.context.evm.env.tx.optimism.mint, @@ -294,68 +257,42 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { )?; } - let (caller_account, _) = journal - .load_account(tx_caller, &mut self.context.evm.db) - .map_err(EVMError::Database)?; + // deduce caller balance with its limit. + self.handler.deduct_caller(&mut self.context)?; - // Subtract gas costs from the caller's account. - // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. - let mut gas_cost = - U256::from(tx_gas_limit).saturating_mul(self.context.evm.env.effective_gas_price()); - - // EIP-4844 - if self.handler.spec_id.is_enabled_in(CANCUN) { - let data_fee = self - .context - .evm - .env - .calc_data_fee() - .expect("already checked"); - gas_cost = gas_cost.saturating_add(data_fee); - } - - caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); - - // touch account so we know it is changed. - caller_account.mark_touch(); - - let transact_gas_limit = tx_gas_limit - initial_gas_spend; + // gas limit used in calls. + let transact_gas_limit = self.context.evm.env.tx.gas_limit - initial_gas_spend; // call inner handling of call/create let first_stack_frame = match self.context.evm.env.tx.transact_to { - TransactTo::Call(address) => { - // Nonce is already checked - caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); - - self.context.evm.make_call_frame( - &CallInputs { - contract: address, - transfer: Transfer { - source: tx_caller, - target: address, - value: tx_value, - }, - input: tx_data, - gas_limit: transact_gas_limit, - context: CallContext { - caller: tx_caller, - address, - code_address: address, - apparent_value: tx_value, - scheme: CallScheme::Call, - }, - is_static: false, + TransactTo::Call(address) => self.context.evm.make_call_frame( + &CallInputs { + contract: address, + transfer: Transfer { + source: tx_caller, + target: address, + value: self.context.evm.env.tx.value, }, - 0..0, - ) - } + input: self.context.evm.env.tx.data.clone(), + gas_limit: transact_gas_limit, + context: CallContext { + caller: tx_caller, + address, + code_address: address, + apparent_value: self.context.evm.env.tx.value, + scheme: CallScheme::Call, + }, + is_static: false, + }, + 0..0, + ), TransactTo::Create(scheme) => self.context.evm.make_create_frame( self.spec_id(), &CreateInputs { caller: tx_caller, scheme, - value: tx_value, - init_code: tx_data, + value: self.context.evm.env.tx.value, + init_code: self.context.evm.env.tx.data.clone(), gas_limit: transact_gas_limit, }, ), @@ -419,22 +356,27 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { #[inline] - fn preverify_transaction(&mut self) -> Result<(), EVMError> { - self.preverify_transaction_inner().map(|_| ()) + pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { + self.handler.validate_env(&self.context.evm.env)?; + self.handler.initial_tx_gas(&self.context.evm.env)?; + self.handler.validate_tx_against_state(&mut self.context)?; + Ok(()) } #[inline] - fn transact_preverified(&mut self) -> EVMResult { - let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env); + pub fn transact_preverified(&mut self) -> EVMResult { + let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env)?; let output = self.transact_preverified_inner(initial_gas_spend); self.handler.end(&mut self.context, output) } #[inline] - fn transact(&mut self) -> EVMResult { - let output = self - .preverify_transaction_inner() - .and_then(|initial_gas_spend| self.transact_preverified_inner(initial_gas_spend)); + pub fn transact(&mut self) -> EVMResult { + self.handler.validate_env(&self.context.evm.env)?; + let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env)?; + self.handler.validate_tx_against_state(&mut self.context)?; + + let output = self.transact_preverified_inner(initial_gas_spend); self.handler.end(&mut self.context, output) } } diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 307ab678e5..d25616c9cb 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -20,18 +20,20 @@ use crate::{ }, precompile::{Address, Bytes, B256}, primitives::{ - db::Database, specification::*, EVMError, EVMResultGeneric, Env, Output, ResultAndState, - Spec, SpecId, + db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec, SpecId, }, CallStackFrame, Context, }; use alloc::sync::Arc; use core::ops::Range; -use once_cell::race::OnceBox; /// Handle call return and return final gas value. pub type CallReturnHandle<'a> = Arc Gas + 'a>; +/// Deduct the caller to its limit. +type DeductCallerHandle<'a, EXT, DB> = + Arc) -> EVMResultGeneric<(), ::Error> + 'a>; + /// Reimburse the caller with ethereum it didn't spent. type ReimburseCallerHandle<'a, EXT, DB> = Arc, &Gas) -> EVMResultGeneric<(), ::Error> + 'a>; @@ -121,8 +123,14 @@ pub type FrameSubCreateHandle<'a, EXT, DB> = Arc< pub type ValidateEnvHandle<'a, DB> = Arc Result<(), EVMError<::Error>> + 'a>; +/// Handle that validates transaction environment against the state. +/// Second parametar is initial gas. +pub type ValidateTxEnvAgainstState<'a, EXT, DB> = + Arc) -> Result<(), EVMError<::Error>> + 'a>; + /// Initial gas calculation handle -pub type InitialTxGasHandle<'a> = Arc u64 + 'a>; +pub type InitialTxGasHandle<'a, DB> = + Arc Result::Error>> + 'a>; /// Handler acts as a proxy and allow to define different behavior for different /// sections of the code. This allows nice integration of different chains or @@ -133,13 +141,17 @@ pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { /// Instruction table type. pub instruction_table: Option>, /// Initial tx gas. - pub initial_tx_gas: InitialTxGasHandle<'a>, + pub initial_tx_gas: InitialTxGasHandle<'a, DB>, + /// Validate transactions agains state data. + pub validate_tx_against_state: ValidateTxEnvAgainstState<'a, EXT, DB>, /// Validate Env pub validate_env: ValidateEnvHandle<'a, DB>, /// Validate Transaction against the state. - // Uses env, call result and returned gas from the call to determine the gas - // that is returned from transaction execution.. + /// Uses env, call result and returned gas from the call to determine the gas + /// that is returned from transaction execution.. pub call_return: CallReturnHandle<'a>, + /// Deduct max value from the caller. + pub deduct_caller: DeductCallerHandle<'a, EXT, DB>, /// Reimburse the caller with ethereum it didn't spent. pub reimburse_caller: ReimburseCallerHandle<'a, EXT, DB>, /// Reward the beneficiary with caller fee. @@ -169,10 +181,14 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { Self { spec_id: SPEC::SPEC_ID, instruction_table: Some(InstructionTables::Plain(make_instruction_table::())), - initial_tx_gas: Arc::new(mainnet::preexecution::initial_tx_gas::), + initial_tx_gas: Arc::new(mainnet::preexecution::initial_tx_gas::), validate_env: Arc::new(mainnet::preexecution::validate_env::), + validate_tx_against_state: Arc::new( + mainnet::preexecution::validate_tx_against_state::, + ), call_return: Arc::new(mainnet::handle_call_return::), calculate_gas_refund: Arc::new(mainnet::calculate_gas_refund::), + deduct_caller: Arc::new(mainnet::deduct_caller::), reimburse_caller: Arc::new(mainnet::handle_reimburse_caller::), reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), main_return: Arc::new(mainnet::main::main_return::), @@ -199,6 +215,11 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { (self.reimburse_caller)(context, gas) } + /// Deduct caller to its limit. + pub fn deduct_caller(&self, context: &mut Context) -> Result<(), EVMError> { + (self.deduct_caller)(context) + } + /// Calculate gas refund for transaction. Some chains have it disabled. pub fn calculate_gas_refund(&self, env: &Env, gas: &Gas) -> u64 { (self.calculate_gas_refund)(env, gas) @@ -305,7 +326,15 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { } /// Initial gas - pub fn initial_tx_gas(&self, env: &Env) -> u64 { + pub fn initial_tx_gas(&self, env: &Env) -> Result> { (self.initial_tx_gas)(env) } + + /// Validate ttansaction against the state. + pub fn validate_tx_against_state( + &self, + context: &mut Context, + ) -> Result<(), EVMError> { + (self.validate_tx_against_state)(context) + } } diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index e90e22c389..cf7122b6d9 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -7,7 +7,12 @@ pub mod preexecution; use crate::{ interpreter::{return_ok, return_revert, Gas, InstructionResult}, - primitives::{db::Database, EVMError, Env, Spec, SpecId::LONDON, U256}, + primitives::{ + db::Database, + EVMError, Env, Spec, + SpecId::{CANCUN, LONDON}, + TransactTo, U256, + }, Context, }; @@ -36,6 +41,43 @@ pub fn handle_call_return( gas } +#[inline] +pub fn deduct_caller( + context: &mut Context, +) -> Result<(), EVMError> { + // load caller's account. + let (caller_account, _) = context + .evm + .journaled_state + .load_account(context.evm.env.tx.caller, &mut context.evm.db) + .map_err(EVMError::Database)?; + + // Subtract gas costs from the caller's account. + // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. + let mut gas_cost = U256::from(context.evm.env.tx.gas_limit) + .saturating_mul(context.evm.env.effective_gas_price()); + + // EIP-4844 + if SPEC::enabled(CANCUN) { + let data_fee = context.evm.env.calc_data_fee().expect("already checked"); + gas_cost = gas_cost.saturating_add(data_fee); + } + + // set new caller account balance. + caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); + + // touch account so we know it is changed. + caller_account.mark_touch(); + + // bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`. + if matches!(context.evm.env.tx.transact_to, TransactTo::Call(_)) { + // Nonce is already checked + caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); + } + + Ok(()) +} + #[inline] pub fn handle_reimburse_caller( context: &mut Context, diff --git a/crates/revm/src/handler/mainnet/preexecution.rs b/crates/revm/src/handler/mainnet/preexecution.rs index 35d61e64b3..31c9326f92 100644 --- a/crates/revm/src/handler/mainnet/preexecution.rs +++ b/crates/revm/src/handler/mainnet/preexecution.rs @@ -1,8 +1,8 @@ use revm_interpreter::gas; use crate::{ - primitives::{db::Database, EVMError}, - primitives::{Env, Spec}, + primitives::{db::Database, EVMError, Env, InvalidTransaction, Spec}, + Context, EvmContext, }; /// Validate environment for the mainnet. @@ -13,10 +13,36 @@ pub fn validate_env(env: &Env) -> Result<(), EVMError< Ok(()) } -pub fn initial_tx_gas(env: &Env) -> u64 { +pub fn validate_tx_against_state( + context: &mut Context, +) -> Result<(), EVMError> { + // load acc + let tx_caller = context.evm.env.tx.caller; + let (caller_account, _) = context + .evm + .journaled_state + .load_account(tx_caller, &mut context.evm.db) + .map_err(EVMError::Database)?; + + context + .evm + .env + .validate_tx_against_state::(caller_account) + .map_err(EVMError::Transaction)?; + + Ok(()) +} + +pub fn initial_tx_gas(env: &Env) -> Result> { let input = &env.tx.data; let is_create = env.tx.transact_to.is_create(); let access_list = &env.tx.access_list; - gas::initial_tx_gas::(input, is_create, access_list) + let initial_gas_spend = gas::initial_tx_gas::(input, is_create, access_list); + + // Additional check to see if limit is big enough to cover initial gas. + if initial_gas_spend > env.tx.gas_limit { + return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into()); + } + Ok(initial_gas_spend) } diff --git a/crates/revm/src/handler/optimism.rs b/crates/revm/src/handler/optimism.rs index 8408848452..1d604490e2 100644 --- a/crates/revm/src/handler/optimism.rs +++ b/crates/revm/src/handler/optimism.rs @@ -113,6 +113,14 @@ pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { } } +/// Deduct max balance from caller +#[inline] +pub fn deduct_caller( + context: &mut Context, +) -> Result<(), EVMError> { + mainnet::deduct_caller::(context) +} + /// Reward beneficiary with gas fee. #[inline] pub fn reward_beneficiary( diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index 3118f89011..a2209cb648 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -35,6 +35,15 @@ pub enum HandleRegisters<'a, EXT, DB: Database> { Box(HandleRegisterBox<'a, EXT, DB>), } +impl<'a, EXT, DB: Database> HandleRegisters<'a, EXT, DB> { + pub fn register(&self, handler: &mut EvmHandler<'a, EXT, DB>) { + match self { + HandleRegisters::Plain(f) => f(handler), + HandleRegisters::Box(f) => f(handler), + } + } +} + pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( handler: &mut EvmHandler<'a, EXT, DB>, ) { diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 40f460b7eb..370979d397 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -153,14 +153,11 @@ mod tests { #[test] #[cfg(not(feature = "optimism"))] fn test_gas_inspector() { - use alloc::sync::Arc; - use crate::{ db::BenchmarkDB, - handler::register::Register, interpreter::opcode, primitives::{address, Bytecode, Bytes, TransactTo}, - Evm, Transact, + Evm, }; let contract_data: Bytes = Bytes::from(vec![ @@ -190,8 +187,8 @@ mod tests { TransactTo::Call(address!("0000000000000000000000000000000000000000")); tx.gas_limit = 21100; }) - .push_handler(Register::Plain(inspector_handle_register)) - .push_handler(Register::Box(Box::new(inspector_handle_register))) + .push_handler(inspector_handle_register) + .push_handler_box(Box::new(inspector_handle_register)) .build(); // run evm. diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index 9c2a3fad41..e8332707dc 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -40,7 +40,6 @@ pub(crate) fn commit_mint_value( .0 .info .balance += U256::from(mint); - journal.checkpoint(); } Ok(()) } From 6352f904d453588e67737d324690e3625011f6db Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 6 Dec 2023 17:27:37 +0100 Subject: [PATCH 19/52] All handlers counted, started on docs, some cleanup --- Cargo.lock | 1 - crates/interpreter/src/gas.rs | 11 + crates/interpreter/src/gas/calc.rs | 2 +- crates/interpreter/src/instructions/opcode.rs | 1 - crates/revm/Cargo.toml | 4 +- crates/revm/src/builder.rs | 21 +- crates/revm/src/evm.rs | 314 ++++++------------ crates/revm/src/handler.rs | 64 ++-- crates/revm/src/handler/mainnet.rs | 117 ++++--- crates/revm/src/handler/mainnet/frames.rs | 48 ++- .../revm/src/handler/mainnet/preexecution.rs | 8 +- crates/revm/src/handler/optimism.rs | 55 ++- crates/revm/src/handler/register.rs | 12 +- crates/revm/src/inspector/gas.rs | 3 +- crates/revm/src/inspector/instruction.rs | 10 +- crates/revm/src/optimism.rs | 53 --- documentation/src/crates/interpreter/host.md | 5 +- documentation/src/crates/revm/builder.md | 17 +- documentation/src/crates/revm/evm.md | 51 ++- documentation/src/crates/revm/evm_impl.md | 35 -- documentation/src/crates/revm/evm_old.md | 17 + documentation/src/crates/revm/handler.md | 0 documentation/src/crates/revm/host_trait.md | 57 ---- documentation/src/crates/revm/state.md | 3 + 24 files changed, 431 insertions(+), 478 deletions(-) delete mode 100644 documentation/src/crates/revm/evm_impl.md create mode 100644 documentation/src/crates/revm/evm_old.md create mode 100644 documentation/src/crates/revm/handler.md delete mode 100644 documentation/src/crates/revm/host_trait.md create mode 100644 documentation/src/crates/revm/state.md diff --git a/Cargo.lock b/Cargo.lock index 984b56f105..a3d6669556 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2252,7 +2252,6 @@ dependencies = [ "ethers-core", "ethers-providers", "futures", - "once_cell", "revm-interpreter", "revm-precompile", "serde", diff --git a/crates/interpreter/src/gas.rs b/crates/interpreter/src/gas.rs index 2d9ebd6f05..3f1b6933b4 100644 --- a/crates/interpreter/src/gas.rs +++ b/crates/interpreter/src/gas.rs @@ -5,6 +5,7 @@ mod constants; pub use calc::*; pub use constants::*; +use revm_primitives::{Spec, SpecId::LONDON}; /// Represents the state of gas during execution. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] @@ -80,6 +81,16 @@ impl Gas { self.refunded += refund; } + /// Set a refund value for final refund. + /// + /// Max refund value is limited to Nth part (depending of fork) of gas spend. + /// + /// Related to EIP-3529: Reduction in refunds + pub fn set_final_refund(&mut self) { + let max_refund_quotient = if SPEC::enabled(LONDON) { 5 } else { 2 }; + self.refunded = (self.refunded() as u64).min(self.spend() / max_refund_quotient) as i64; + } + /// Set a refund value pub fn set_refund(&mut self, refund: i64) { self.refunded = refund; diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index 98d26948ab..1c5d0acec2 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -339,7 +339,7 @@ pub fn memory_gas(a: usize) -> u64 { /// Initial gas that is deducted for transaction to be included. /// Initial gas contains initial stipend gas, gas for access list and input data. -pub fn initial_tx_gas( +pub fn validate_initial_tx_gas( input: &[u8], is_create: bool, access_list: &[(Address, Vec)], diff --git a/crates/interpreter/src/instructions/opcode.rs b/crates/interpreter/src/instructions/opcode.rs index a39352a62a..0644a4bdd2 100644 --- a/crates/interpreter/src/instructions/opcode.rs +++ b/crates/interpreter/src/instructions/opcode.rs @@ -7,7 +7,6 @@ use crate::{ Host, Interpreter, }; use alloc::boxed::Box; -use alloc::sync::Arc; use core::fmt; /// EVM opcode function signature. diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index dac3cef692..52dfe8cc15 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -10,12 +10,12 @@ version = "3.5.0" readme = "../../README.md" [dependencies] +# revm revm-interpreter = { path = "../interpreter", version = "1.3.0", default-features = false } revm-precompile = { path = "../precompile", version = "2.2.0", default-features = false } -#misc +# misc auto_impl = { version = "1.1", default-features = false } -once_cell = { version = "1.18", default-features = false } # Optional serde = { version = "1.0", features = ["derive", "rc"], optional = true } diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index af3939025f..c50ae0382d 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -2,8 +2,7 @@ use crate::{ db::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}, - handler::{register, HandleRegister}, - interpreter::opcode::make_instruction_table, + handler::register, primitives::{BlockEnv, CfgEnv, Env, LatestSpec, Spec, SpecId, TxEnv}, Context, Evm, EvmContext, Handler, }; @@ -221,7 +220,10 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { self } - pub fn push_handler(mut self, handle_register: register::HandleRegister<'a, EXT, DB>) -> Self { + pub fn append_handler( + mut self, + handle_register: register::HandleRegister<'a, EXT, DB>, + ) -> Self { self.handle_registers .push(register::HandleRegisters::Plain(handle_register)); self @@ -229,11 +231,12 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { /// Register Handler that modifies the behavior of EVM. /// Check [`Handler`] for more information. - pub fn push_handler_box( + pub fn append_handler_box( mut self, handle_register: register::HandleRegisterBox<'a, EXT, DB>, ) -> Self { - //self.handle_registers.push(handle_register); + self.handle_registers + .push(register::HandleRegisters::Box(handle_register)); self } } @@ -266,18 +269,18 @@ impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> } /// Clear Environment of EVM. - pub fn clear_env(mut self) -> Self { + pub fn with_clear_env(mut self) -> Self { self.evm.env.clear(); self } /// Clear Transaction environment of EVM. - pub fn clear_tx_env(mut self) -> Self { + pub fn with_clear_tx_env(mut self) -> Self { self.evm.env.tx.clear(); self } /// Clear Block environment of EVM. - pub fn clear_block_env(mut self) -> Self { + pub fn with_clear_block_env(mut self) -> Self { self.evm.env.block.clear(); self } @@ -330,7 +333,7 @@ mod test { Evm::builder() .with_empty_db() .with_external(NoOpInspector::default()) - .push_handler(inspector_handle_register) + .append_handler(inspector_handle_register) .build(); } } diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 89a6d8fcfb..02e7c346f9 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -5,17 +5,14 @@ use crate::{ db::{Database, EmptyDB}, handler::Handler, interpreter::{ - opcode::InstructionTables, CallContext, CallInputs, CallScheme, CreateInputs, Host, - Interpreter, InterpreterAction, InterpreterResult, SelfDestructResult, SharedMemory, - Transfer, + opcode::InstructionTables, Host, Interpreter, InterpreterAction, InterpreterResult, + SelfDestructResult, SharedMemory, }, - journaled_state::JournaledState, - precompile::Precompiles, primitives::{ - specification::SpecId, Address, Bytecode, Bytes, EVMError, EVMResult, Env, HashSet, Output, - SpecId::*, TransactTo, B256, U256, + specification::SpecId, Address, Bytecode, Bytes, EVMError, EVMResult, Env, Output, + TransactTo, B256, U256, }, - CallStackFrame, Context, EvmContext, FrameOrResult, + CallStackFrame, Context, FrameOrResult, }; use alloc::{boxed::Box, vec::Vec}; use core::fmt; @@ -23,8 +20,10 @@ use core::fmt; /// EVM call stack limit. pub const CALL_STACK_LIMIT: u64 = 1024; -/// EVM instance containing both internal EVM context and external context (if specified) +/// EVM instance containing both internal EVM context and external context /// and the handler that dictates the logic of EVM (or hardfork specification). +/// +/// pub struct Evm<'a, EXT, DB: Database> { /// Context of execution, containing both EVM and external context. pub context: Context, @@ -41,7 +40,7 @@ where { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Evm") - .field("data", &self.context.evm) + .field("evm context", &self.context.evm) .finish_non_exhaustive() } } @@ -66,6 +65,40 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { self.handler.spec_id } + /// Pre verify transaction by checking Environment, initial gas spend and if caller + /// has enough balance to pay for the gas. + #[inline] + pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { + self.handler.validate_env(&self.context.evm.env)?; + self.handler + .validate_initial_tx_gas(&self.context.evm.env)?; + self.handler.validate_tx_against_state(&mut self.context)?; + Ok(()) + } + + /// Transact pre-verified transaction, this function will not validate the transaction. + #[inline] + pub fn transact_preverified(&mut self) -> EVMResult { + let initial_gas_spend = self + .handler + .validate_initial_tx_gas(&self.context.evm.env)?; + let output = self.transact_preverified_inner(initial_gas_spend); + self.handler.end(&mut self.context, output) + } + + /// Transact transaction, this function will validate the transaction. + #[inline] + pub fn transact(&mut self) -> EVMResult { + self.handler.validate_env(&self.context.evm.env)?; + let initial_gas_spend = self + .handler + .validate_initial_tx_gas(&self.context.evm.env)?; + self.handler.validate_tx_against_state(&mut self.context)?; + + let output = self.transact_preverified_inner(initial_gas_spend); + self.handler.end(&mut self.context, output) + } + /// Allow for evm setting to be modified by feeding current evm /// to the builder for modifications. pub fn modify(self) -> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { @@ -88,43 +121,51 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { self.context } - /// TODO add spec_id as variable. - pub fn new_with_spec( - db: DB, - env: Box, - external: EXT, - handler: Handler<'a, Self, EXT, DB>, - precompiles: Precompiles, - ) -> Self { - let journaled_state = JournaledState::new( - handler.spec_id, - precompiles - .addresses() - .into_iter() - .cloned() - .collect::>(), - ); + /// Start the main loop. + pub fn start_the_loop( + &mut self, + first_stack_frame: FrameOrResult, + ) -> (InterpreterResult, Output) { + // Some only if it is create. + let mut created_address = None; - Self { - context: Context { - evm: EvmContext { - env, - journaled_state, - db, - error: None, - precompiles, - #[cfg(feature = "optimism")] - l1_block_info: None, - }, - external, - }, - handler, - } + // start main loop if CallStackFrame is created correctly + let result = match first_stack_frame { + FrameOrResult::Frame(first_stack_frame) => { + created_address = first_stack_frame.created_address; + // take instruction talbe + let table = self + .handler + .instruction_table + .take() + .expect("Instruction table should be present"); + + // run main loop + let output = match &table { + InstructionTables::Plain(table) => self.run_the_loop(&table, first_stack_frame), + InstructionTables::Boxed(table) => self.run_the_loop(&table, first_stack_frame), + }; + + // return instruction table + self.handler.instruction_table = Some(table); + + output + } + FrameOrResult::Result(interpreter_result) => interpreter_result, + }; + + // output of execution + let main_output = match self.context.evm.env.tx.transact_to { + TransactTo::Call(_) => Output::Call(result.output.clone()), + TransactTo::Create(_) => Output::Create(result.output.clone(), created_address), + }; + + (result, main_output) } /// Runs main call loop. #[inline] - pub fn run( + pub fn run_the_loop( &mut self, instruction_table: &[FN; 256], first_frame: Box, @@ -196,188 +237,33 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { } } - /// Transact preverified transaction. - pub fn transact_preverified_inner(&mut self, initial_gas_spend: u64) -> EVMResult { - let tx_caller = self.context.evm.env.tx.caller; - // the L1-cost fee is only computed for Optimism non-deposit transactions. - #[cfg(feature = "optimism")] - let tx_l1_cost = if env.cfg.optimism && env.tx.optimism.source_hash.is_none() { - let l1_block_info = optimism::L1BlockInfo::try_fetch(self.context.evm.db) - .map_err(EVMError::Database)?; - - let Some(enveloped_tx) = &env.tx.optimism.enveloped_tx else { - panic!("[OPTIMISM] Failed to load enveloped transaction."); - }; - // TODO specs - let tx_l1_cost = l1_block_info.calculate_tx_l1_cost(enveloped_tx, self.spec_id); - - // storage l1 block info for later use. - self.context.evm.l1_block_info = Some(l1_block_info); - - tx_l1_cost - } else { - U256::ZERO - }; - - // load coinbase - // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm - if self.spec_id().is_enabled_in(SHANGHAI) { - self.context - .evm - .journaled_state - .initial_account_load( - self.context.evm.env.block.coinbase, - &[], - &mut self.context.evm.db, - ) - .map_err(EVMError::Database)?; - } - - self.context.evm.load_access_list()?; - - #[cfg(feature = "optimism")] - if self.context.evm.env.cfg.optimism { - // load acc - let journal = &mut self.context.evm.journaled_state; - - crate::optimism::commit_mint_value( - tx_caller, - self.context.evm.env.tx.optimism.mint, - self.context.evm.db, - journal, - )?; - - let is_deposit = self.context.evm.env.tx.optimism.source_hash.is_some(); - crate::optimism::remove_l1_cost( - is_deposit, - tx_caller, - tx_l1_cost, - self.context.evm.db, - journal, - )?; - } + /// Transact pre-verified transaction. + fn transact_preverified_inner(&mut self, initial_gas_spend: u64) -> EVMResult { + let hndl = &mut self.handler; + let ctx = &mut self.context; + // load access list and beneficiary if needed. + hndl.main_load(ctx)?; // deduce caller balance with its limit. - self.handler.deduct_caller(&mut self.context)?; - + hndl.deduct_caller(ctx)?; // gas limit used in calls. - let transact_gas_limit = self.context.evm.env.tx.gas_limit - initial_gas_spend; - - // call inner handling of call/create - let first_stack_frame = match self.context.evm.env.tx.transact_to { - TransactTo::Call(address) => self.context.evm.make_call_frame( - &CallInputs { - contract: address, - transfer: Transfer { - source: tx_caller, - target: address, - value: self.context.evm.env.tx.value, - }, - input: self.context.evm.env.tx.data.clone(), - gas_limit: transact_gas_limit, - context: CallContext { - caller: tx_caller, - address, - code_address: address, - apparent_value: self.context.evm.env.tx.value, - scheme: CallScheme::Call, - }, - is_static: false, - }, - 0..0, - ), - TransactTo::Create(scheme) => self.context.evm.make_create_frame( - self.spec_id(), - &CreateInputs { - caller: tx_caller, - scheme, - value: self.context.evm.env.tx.value, - init_code: self.context.evm.env.tx.data.clone(), - gas_limit: transact_gas_limit, - }, - ), - }; - // Some only if it is create. - let mut created_address = None; + let first_frame = + hndl.create_first_frame(ctx, ctx.evm.env.tx.gas_limit - initial_gas_spend); - // start main loop if CallStackFrame is created correctly - let interpreter_result = match first_stack_frame { - FrameOrResult::Frame(first_stack_frame) => { - created_address = first_stack_frame.created_address; - // take instruction talbe - let table = self - .handler - .instruction_table - .take() - .expect("Instruction table should be present"); + // Starts the main running loop. + let (result, main_output) = self.start_the_loop(first_frame); - // run main loop - let output = match &table { - InstructionTables::Plain(table) => self.run(&table, first_stack_frame), - InstructionTables::Boxed(table) => self.run(&table, first_stack_frame), - }; - // return instruction table - self.handler.instruction_table = Some(table); - - output - } - FrameOrResult::Result(interpreter_result) => interpreter_result, - }; - - let handler = &self.handler; - let context = &mut self.context; + let hndl = &mut self.handler; + let ctx = &mut self.context; // handle output of call/create calls. - let mut gas = handler.call_return( - &mut context.evm.env, - interpreter_result.result, - interpreter_result.gas, - ); - - // set refund. Refund amount depends on hardfork. - gas.set_refund(handler.calculate_gas_refund(&mut context.evm.env, &gas) as i64); - + let gas = hndl.call_return(&ctx.evm.env, result.result, result.gas); // Reimburse the caller - handler.reimburse_caller(context, &gas)?; - + hndl.reimburse_caller(ctx, &gas)?; // Reward beneficiary - handler.reward_beneficiary(context, &gas)?; - - // output of execution - let output = match context.evm.env.tx.transact_to { - TransactTo::Call(_) => Output::Call(interpreter_result.output), - TransactTo::Create(_) => Output::Create(interpreter_result.output, created_address), - }; - + hndl.reward_beneficiary(ctx, &gas)?; // main return - handler.main_return(context, interpreter_result.result, output, &gas) - } -} - -impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { - #[inline] - pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { - self.handler.validate_env(&self.context.evm.env)?; - self.handler.initial_tx_gas(&self.context.evm.env)?; - self.handler.validate_tx_against_state(&mut self.context)?; - Ok(()) - } - - #[inline] - pub fn transact_preverified(&mut self) -> EVMResult { - let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env)?; - let output = self.transact_preverified_inner(initial_gas_spend); - self.handler.end(&mut self.context, output) - } - - #[inline] - pub fn transact(&mut self) -> EVMResult { - self.handler.validate_env(&self.context.evm.env)?; - let initial_gas_spend = self.handler.initial_tx_gas(&self.context.evm.env)?; - self.handler.validate_tx_against_state(&mut self.context)?; - - let output = self.transact_preverified_inner(initial_gas_spend); - self.handler.end(&mut self.context, output) + hndl.main_return(ctx, result.result, main_output, &gas) } } diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index d25616c9cb..3e2b54f2e9 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -22,7 +22,7 @@ use crate::{ primitives::{ db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec, SpecId, }, - CallStackFrame, Context, + CallStackFrame, Context, FrameOrResult, }; use alloc::sync::Arc; use core::ops::Range; @@ -30,19 +30,22 @@ use core::ops::Range; /// Handle call return and return final gas value. pub type CallReturnHandle<'a> = Arc Gas + 'a>; +/// Load access list account, precompiles and beneficiary. +/// There is not need to load Caller as it is assumed that +/// it will be loaded in DeductCallerHandle. +pub type MainLoadHandle<'a, EXT, DB> = + Arc) -> Result<(), EVMError<::Error>> + 'a>; + /// Deduct the caller to its limit. -type DeductCallerHandle<'a, EXT, DB> = +pub type DeductCallerHandle<'a, EXT, DB> = Arc) -> EVMResultGeneric<(), ::Error> + 'a>; /// Reimburse the caller with ethereum it didn't spent. -type ReimburseCallerHandle<'a, EXT, DB> = +pub type ReimburseCallerHandle<'a, EXT, DB> = Arc, &Gas) -> EVMResultGeneric<(), ::Error> + 'a>; /// Reward beneficiary with transaction rewards. -type RewardBeneficiaryHandle<'a, EXT, DB> = ReimburseCallerHandle<'a, EXT, DB>; - -/// Calculate gas refund for transaction. -pub type CalculateGasRefundHandle<'a> = Arc u64 + 'a>; +pub type RewardBeneficiaryHandle<'a, EXT, DB> = ReimburseCallerHandle<'a, EXT, DB>; /// Main return handle, takes state from journal and transforms internal result to external. pub type MainReturnHandle<'a, EXT, DB> = Arc< @@ -74,6 +77,10 @@ pub type FrameReturnHandle<'a, EXT, DB> = Arc< + 'a, >; +/// Create first frame. +pub type CreateFirstFrame<'a, EXT, DB> = + Arc, u64) -> FrameOrResult + 'a>; + /// Call to the host from Interpreter to save the log. pub type HostLogHandle<'a, EXT, DB> = Arc, Address, Vec, Bytes) + 'a>; @@ -129,7 +136,7 @@ pub type ValidateTxEnvAgainstState<'a, EXT, DB> = Arc) -> Result<(), EVMError<::Error>> + 'a>; /// Initial gas calculation handle -pub type InitialTxGasHandle<'a, DB> = +pub type ValidateInitialTxGasHandle<'a, DB> = Arc Result::Error>> + 'a>; /// Handler acts as a proxy and allow to define different behavior for different @@ -141,8 +148,8 @@ pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { /// Instruction table type. pub instruction_table: Option>, /// Initial tx gas. - pub initial_tx_gas: InitialTxGasHandle<'a, DB>, - /// Validate transactions agains state data. + pub validate_initial_tx_gas: ValidateInitialTxGasHandle<'a, DB>, + /// Validate transactions against state data. pub validate_tx_against_state: ValidateTxEnvAgainstState<'a, EXT, DB>, /// Validate Env pub validate_env: ValidateEnvHandle<'a, DB>, @@ -150,19 +157,20 @@ pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { /// Uses env, call result and returned gas from the call to determine the gas /// that is returned from transaction execution.. pub call_return: CallReturnHandle<'a>, + /// Main load handle + pub main_load_handle: MainLoadHandle<'a, EXT, DB>, /// Deduct max value from the caller. pub deduct_caller: DeductCallerHandle<'a, EXT, DB>, /// Reimburse the caller with ethereum it didn't spent. pub reimburse_caller: ReimburseCallerHandle<'a, EXT, DB>, /// Reward the beneficiary with caller fee. pub reward_beneficiary: RewardBeneficiaryHandle<'a, EXT, DB>, - /// Calculate gas refund for transaction. - /// Some chains have it disabled. - pub calculate_gas_refund: CalculateGasRefundHandle<'a>, /// Main return handle, returns the output of the transact. pub main_return: MainReturnHandle<'a, EXT, DB>, /// End handle. pub end: EndHandle<'a, EXT, DB>, + /// Create Main frame + pub create_first_frame: CreateFirstFrame<'a, EXT, DB>, /// Frame return pub frame_return: FrameReturnHandle<'a, EXT, DB>, /// Frame sub call @@ -181,17 +189,20 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { Self { spec_id: SPEC::SPEC_ID, instruction_table: Some(InstructionTables::Plain(make_instruction_table::())), - initial_tx_gas: Arc::new(mainnet::preexecution::initial_tx_gas::), + validate_initial_tx_gas: Arc::new( + mainnet::preexecution::validate_initial_tx_gas::, + ), validate_env: Arc::new(mainnet::preexecution::validate_env::), validate_tx_against_state: Arc::new( mainnet::preexecution::validate_tx_against_state::, ), call_return: Arc::new(mainnet::handle_call_return::), - calculate_gas_refund: Arc::new(mainnet::calculate_gas_refund::), + main_load_handle: Arc::new(mainnet::main_load::), deduct_caller: Arc::new(mainnet::deduct_caller::), reimburse_caller: Arc::new(mainnet::handle_reimburse_caller::), reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), main_return: Arc::new(mainnet::main::main_return::), + create_first_frame: Arc::new(mainnet::frames::create_first_frame::), end: Arc::new(mainnet::main::end_handle::), frame_return: Arc::new(mainnet::frames::handle_frame_return::), frame_sub_call: Arc::new(mainnet::frames::handle_frame_sub_call::), @@ -220,11 +231,6 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { (self.deduct_caller)(context) } - /// Calculate gas refund for transaction. Some chains have it disabled. - pub fn calculate_gas_refund(&self, env: &Env, gas: &Gas) -> u64 { - (self.calculate_gas_refund)(env, gas) - } - /// Reward beneficiary pub fn reward_beneficiary( &self, @@ -326,8 +332,8 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { } /// Initial gas - pub fn initial_tx_gas(&self, env: &Env) -> Result> { - (self.initial_tx_gas)(env) + pub fn validate_initial_tx_gas(&self, env: &Env) -> Result> { + (self.validate_initial_tx_gas)(env) } /// Validate ttansaction against the state. @@ -337,4 +343,18 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { ) -> Result<(), EVMError> { (self.validate_tx_against_state)(context) } + + /// Create first call frame. + pub fn create_first_frame( + &self, + context: &mut Context, + gas_limit: u64, + ) -> FrameOrResult { + (self.create_first_frame)(context, gas_limit) + } + + /// Main load + pub fn main_load(&self, context: &mut Context) -> Result<(), EVMError> { + (self.main_load_handle)(context) + } } diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index cf7122b6d9..21f278f1f5 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -9,24 +9,22 @@ use crate::{ interpreter::{return_ok, return_revert, Gas, InstructionResult}, primitives::{ db::Database, - EVMError, Env, Spec, - SpecId::{CANCUN, LONDON}, + Account, EVMError, Env, Spec, + SpecId::{CANCUN, LONDON, SHANGHAI}, TransactTo, U256, }, Context, }; -/// Handle output of the transaction -#[inline] -pub fn handle_call_return( +pub fn handle_call_return_with_refund_flag( env: &Env, call_result: InstructionResult, returned_gas: Gas, + refund_enabled: bool, ) -> Gas { - let tx_gas_limit = env.tx.gas_limit; // Spend the gas limit. Gas is reimbursed when the tx returns successfully. - let mut gas = Gas::new(tx_gas_limit); - gas.record_cost(tx_gas_limit); + let mut gas = Gas::new(env.tx.gas_limit); + gas.record_cost(env.tx.gas_limit); match call_result { return_ok!() => { @@ -38,43 +36,97 @@ pub fn handle_call_return( } _ => {} } + // Calculate gas refund for transaction. + // If config is set to disable gas refund, it will return 0. + // If spec is set to london, it will decrease the maximum refund amount to 5th part of + // gas spend. (Before london it was 2th part of gas spend) + if refund_enabled { + // EIP-3529: Reduction in refunds + gas.set_final_refund::() + }; + gas } +/// Handle output of the transaction #[inline] -pub fn deduct_caller( - context: &mut Context, -) -> Result<(), EVMError> { - // load caller's account. - let (caller_account, _) = context - .evm - .journaled_state - .load_account(context.evm.env.tx.caller, &mut context.evm.db) - .map_err(EVMError::Database)?; +pub fn handle_call_return( + env: &Env, + call_result: InstructionResult, + returned_gas: Gas, +) -> Gas { + handle_call_return_with_refund_flag::(env, call_result, returned_gas, true) +} +pub(crate) fn deduct_caller_inner(caller_account: &mut Account, env: &Env) { // Subtract gas costs from the caller's account. // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. - let mut gas_cost = U256::from(context.evm.env.tx.gas_limit) - .saturating_mul(context.evm.env.effective_gas_price()); + let mut gas_cost = U256::from(env.tx.gas_limit).saturating_mul(env.effective_gas_price()); // EIP-4844 if SPEC::enabled(CANCUN) { - let data_fee = context.evm.env.calc_data_fee().expect("already checked"); + let data_fee = env.calc_data_fee().expect("already checked"); gas_cost = gas_cost.saturating_add(data_fee); } // set new caller account balance. caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); - // touch account so we know it is changed. - caller_account.mark_touch(); - // bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`. - if matches!(context.evm.env.tx.transact_to, TransactTo::Call(_)) { + if matches!(env.tx.transact_to, TransactTo::Call(_)) { // Nonce is already checked caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); } + // touch account so we know it is changed. + caller_account.mark_touch(); +} + +/// Main load handle +#[inline] +pub fn main_load( + context: &mut Context, +) -> Result<(), EVMError> { + // the L1-cost fee is only computed for Optimism non-deposit transactions. + #[cfg(feature = "optimism")] + if env.cfg.optimism && env.tx.optimism.source_hash.is_none() { + let l1_block_info = + optimism::L1BlockInfo::try_fetch(self.context.evm.db).map_err(EVMError::Database)?; + + // storage l1 block info for later use. + self.context.evm.l1_block_info = Some(l1_block_info); + + tx_l1_cost + } + + // load coinbase + // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm + if SPEC::enabled(SHANGHAI) { + context + .evm + .journaled_state + .initial_account_load(context.evm.env.block.coinbase, &[], &mut context.evm.db) + .map_err(EVMError::Database)?; + } + + context.evm.load_access_list()?; + Ok(()) +} + +#[inline] +pub fn deduct_caller( + context: &mut Context, +) -> Result<(), EVMError> { + // load caller's account. + let (caller_account, _) = context + .evm + .journaled_state + .load_account(context.evm.env.tx.caller, &mut context.evm.db) + .map_err(EVMError::Database)?; + + // deduct gas cost from caller's account. + deduct_caller_inner::(caller_account, &context.evm.env); + Ok(()) } @@ -133,23 +185,6 @@ pub fn reward_beneficiary( Ok(()) } -/// Calculate gas refund for transaction. -/// -/// If config is set to disable gas refund, it will return 0. -/// -/// If spec is set to london, it will decrease the maximum refund amount to 5th part of -/// gas spend. (Before london it was 2th part of gas spend) -#[inline] -pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { - if env.cfg.is_gas_refund_disabled() { - 0 - } else { - // EIP-3529: Reduction in refunds - let max_refund_quotient = if SPEC::enabled(LONDON) { 5 } else { 2 }; - (gas.refunded() as u64).min(gas.spend() / max_refund_quotient) - } -} - #[cfg(test)] mod tests { use revm_interpreter::primitives::CancunSpec; diff --git a/crates/revm/src/handler/mainnet/frames.rs b/crates/revm/src/handler/mainnet/frames.rs index 3e8a08df1d..4a41a89bc7 100644 --- a/crates/revm/src/handler/mainnet/frames.rs +++ b/crates/revm/src/handler/mainnet/frames.rs @@ -1,11 +1,55 @@ use crate::{ db::Database, - interpreter::{CallInputs, CreateInputs, InterpreterResult, SharedMemory}, - primitives::Spec, + interpreter::{ + CallContext, CallInputs, CreateInputs, InterpreterResult, SharedMemory, Transfer, + }, + primitives::{Spec, TransactTo}, CallStackFrame, Context, FrameOrResult, }; use alloc::boxed::Box; use core::ops::Range; +use revm_interpreter::CallScheme; + +/// Creates first fmrae +pub fn create_first_frame( + context: &mut Context, + gas_limit: u64, +) -> FrameOrResult { + // call inner handling of call/create + match context.evm.env.tx.transact_to { + TransactTo::Call(address) => context.evm.make_call_frame( + &CallInputs { + contract: address, + transfer: Transfer { + source: context.evm.env.tx.caller, + target: address, + value: context.evm.env.tx.value, + }, + input: context.evm.env.tx.data.clone(), + gas_limit, + context: CallContext { + caller: context.evm.env.tx.caller, + address, + code_address: address, + apparent_value: context.evm.env.tx.value, + scheme: CallScheme::Call, + }, + is_static: false, + }, + 0..0, + ), + TransactTo::Create(scheme) => context.evm.make_create_frame( + SPEC::SPEC_ID, + &CreateInputs { + caller: context.evm.env.tx.caller, + scheme, + value: context.evm.env.tx.value, + init_code: context.evm.env.tx.data.clone(), + gas_limit, + }, + ), + } +} /// Handle frame return. pub fn handle_frame_return( diff --git a/crates/revm/src/handler/mainnet/preexecution.rs b/crates/revm/src/handler/mainnet/preexecution.rs index 31c9326f92..6ac70d9b7e 100644 --- a/crates/revm/src/handler/mainnet/preexecution.rs +++ b/crates/revm/src/handler/mainnet/preexecution.rs @@ -2,7 +2,7 @@ use revm_interpreter::gas; use crate::{ primitives::{db::Database, EVMError, Env, InvalidTransaction, Spec}, - Context, EvmContext, + Context, }; /// Validate environment for the mainnet. @@ -33,12 +33,14 @@ pub fn validate_tx_against_state( Ok(()) } -pub fn initial_tx_gas(env: &Env) -> Result> { +pub fn validate_initial_tx_gas( + env: &Env, +) -> Result> { let input = &env.tx.data; let is_create = env.tx.transact_to.is_create(); let access_list = &env.tx.access_list; - let initial_gas_spend = gas::initial_tx_gas::(input, is_create, access_list); + let initial_gas_spend = gas::validate_initial_tx_gas::(input, is_create, access_list); // Additional check to see if limit is big enough to cover initial gas. if initial_gas_spend > env.tx.gas_limit { diff --git a/crates/revm/src/handler/optimism.rs b/crates/revm/src/handler/optimism.rs index 1d604490e2..cc4a082f4f 100644 --- a/crates/revm/src/handler/optimism.rs +++ b/crates/revm/src/handler/optimism.rs @@ -97,6 +97,11 @@ pub fn handle_call_return( } _ => {} } + // Prior to Regolith, deposit transactions did not receive gas refunds. + if !is_deposit && SPEC::enabled(REGOLITH) { + gas.set_final_refund::() + } + gas } @@ -118,7 +123,55 @@ pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { pub fn deduct_caller( context: &mut Context, ) -> Result<(), EVMError> { - mainnet::deduct_caller::(context) + // load caller's account. + let (caller_account, _) = context + .evm + .journaled_state + .load_account(context.evm.env.tx.caller, &mut context.evm.db) + .map_err(EVMError::Database)?; + + // If the transaction is a deposit with a `mint` value, add the mint value + // in wei to the caller's balance. This should be persisted to the database + // prior to the rest of execution. + if let Some(mint) = context.evm.env.tx.optimism.mint { + caller_account.info.balance += U256::from(mint); + } + + // We deduct caller max balance after minting and before deducing the + // l1 cost, max values is already checked in pre_validate but l1 cost wasn't. + mainnet::deduct_caller::(context)?; + + // If the transaction is not a deposit transaction, subtract the L1 data fee from the + // caller's balance directly after minting the requested amount of ETH. + if env.tx.optimism.source_hash.is_none() { + // get envelope + let Some(enveloped_tx) = &context.env.tx.optimism.enveloped_tx else { + panic!("[OPTIMISM] Failed to load enveloped transaction."); + }; + + // TODO specs + let tx_l1_cost = self + .context + .evm + .l1_block_info + .expect("L1BlockInfo should be loaded") + .calculate_tx_l1_cost(enveloped_tx, self.spec_id); + + if tx_l1_cost.gt(&acc.info.balance) { + let u64_cost = if U256::from(u64::MAX).lt(&l1_cost) { + u64::MAX + } else { + tx_l1_cost.as_limbs()[0] + }; + return Err(EVMError::Transaction( + InvalidTransaction::LackOfFundForMaxFee { + fee: u64_cost, + balance: acc.info.balance, + }, + )); + } + acc.info.balance = acc.info.balance.saturating_sub(l1_cost); + } } /// Reward beneficiary with gas fee. diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index a2209cb648..2424384b82 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -2,24 +2,16 @@ use crate::{ db::Database, handler::Handler, inspector_instruction, - interpreter::{InterpreterResult, SelfDestructResult}, + interpreter::{opcode::InstructionTables, InterpreterResult, SelfDestructResult}, CallStackFrame, Evm, FrameOrResult, Inspector, }; use alloc::sync::Arc; -use revm_interpreter::opcode::{BoxedInstructionTable, InstructionTable, InstructionTables}; pub trait GetInspector<'a, DB: Database> { fn get_inspector(&mut self) -> &mut dyn Inspector; } -/// Wants -/// List of function that would modify the handler -/// Functions need to be Spec aware. Generic over Spec. -/// They dont need to be tied to one structure, so they need to be generic over trait. -/// -/// Problems: -/// Trait Remove it -/// +/// EVM Handler pub type EvmHandler<'a, EXT, DB> = Handler<'a, Evm<'a, EXT, DB>, EXT, DB>; pub type EvmInstructionTables<'a, EXT, DB> = InstructionTables<'a, Evm<'a, EXT, DB>>; diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 370979d397..5feb28d844 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -187,8 +187,7 @@ mod tests { TransactTo::Call(address!("0000000000000000000000000000000000000000")); tx.gas_limit = 21100; }) - .push_handler(inspector_handle_register) - .push_handler_box(Box::new(inspector_handle_register)) + .append_handler(inspector_handle_register) .build(); // run evm. diff --git a/crates/revm/src/inspector/instruction.rs b/crates/revm/src/inspector/instruction.rs index 4a899f6e90..0db373e470 100644 --- a/crates/revm/src/inspector/instruction.rs +++ b/crates/revm/src/inspector/instruction.rs @@ -1,12 +1,6 @@ -use crate::{ - handler::{inspector_handle_register, register::GetInspector}, - Evm, -}; -use alloc::sync::Arc; +use crate::{handler::register::GetInspector, Evm}; use revm_interpreter::{ - opcode::{BoxedInstruction, Instruction}, - primitives::db::Database, - InstructionResult, Interpreter, + opcode::BoxedInstruction, primitives::db::Database, InstructionResult, Interpreter, }; /// Outer closure that calls Inspector for every instruction. diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index e8332707dc..29c1c7d264 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -24,59 +24,6 @@ pub const BASE_FEE_RECIPIENT: Address = address!("420000000000000000000000000000 /// The address of the L1Block contract. pub const L1_BLOCK_CONTRACT: Address = address!("4200000000000000000000000000000000000015"); -/// If the transaction is a deposit with a `mint` value, add the mint value -/// in wei to the caller's balance. This should be persisted to the database -/// prior to the rest of execution. -pub(crate) fn commit_mint_value( - tx_caller: Address, - tx_mint: Option, - db: &mut DB, - journal: &mut JournaledState, -) -> Result<(), EVMError> { - if let Some(mint) = tx_mint { - journal - .load_account(tx_caller, db) - .map_err(EVMError::Database)? - .0 - .info - .balance += U256::from(mint); - } - Ok(()) -} - -/// If the transaction is not a deposit transaction, subtract the L1 data fee from the -/// caller's balance directly after minting the requested amount of ETH. -pub(crate) fn remove_l1_cost( - is_deposit: bool, - tx_caller: Address, - l1_cost: U256, - db: &mut DB, - journal: &mut JournaledState, -) -> Result<(), EVMError> { - if is_deposit { - return Ok(()); - } - let acc = journal - .load_account(tx_caller, db) - .map_err(EVMError::Database)? - .0; - if l1_cost.gt(&acc.info.balance) { - let u64_cost = if U256::from(u64::MAX).lt(&l1_cost) { - u64::MAX - } else { - l1_cost.as_limbs()[0] - }; - return Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: u64_cost, - balance: acc.info.balance, - }, - )); - } - acc.info.balance = acc.info.balance.saturating_sub(l1_cost); - Ok(()) -} - /// L1 block info /// /// We can extract L1 epoch data from each L2 block, by looking at the `setL1BlockValues` diff --git a/documentation/src/crates/interpreter/host.md b/documentation/src/crates/interpreter/host.md index a3a7f0ab39..90752f3a7d 100644 --- a/documentation/src/crates/interpreter/host.md +++ b/documentation/src/crates/interpreter/host.md @@ -2,11 +2,10 @@ The `host.rs` module in this Rust EVM implementation defines a crucial trait `Host`. The `Host` trait outlines an interface for the interaction of the EVM interpreter with its environment (or "host"), encompassing essential operations such as account and storage access, creating logs, and invoking transactions. +The [`Evm`](./../revm/evm.md) struct implements this `Host` trait. ## Trait Methods -- `step` & `step_end`: These methods manage the execution of EVM opcodes. The `step` method is invoked before executing an opcode, while `step_end` is invoked after. These methods can modify the EVM state or halt execution based on certain conditions. - - `env`: This method provides access to the EVM environment, including information about the current block and transaction. - `load_account`: Retrieves information about a given Ethereum account. @@ -21,7 +20,5 @@ The `host.rs` module in this Rust EVM implementation defines a crucial trait `Ho - `selfdestruct`: Marks an Ethereum account to be self-destructed, transferring its funds to a target account. -- `create` & `call`: These methods handle the creation of new smart contracts and the invocation of smart contract functions, respectively. - The `Host` trait provides a standard interface that any host environment for the EVM must implement. This abstraction allows the EVM code to interact with the state of the Ethereum network in a generic way, thereby enhancing modularity and interoperability. Different implementations of the `Host` trait can be used to simulate different environments for testing or for connecting to different Ethereum-like networks. \ No newline at end of file diff --git a/documentation/src/crates/revm/builder.md b/documentation/src/crates/revm/builder.md index a8a38ade84..a74ebc6a36 100644 --- a/documentation/src/crates/revm/builder.md +++ b/documentation/src/crates/revm/builder.md @@ -1,9 +1,22 @@ # Evm Builder -Is a helper that allows easier setting of database, external and logic structures. +It creates the EVM and applies different handler, and allows setting external context and custom logic. + +EVM inside revm consist of the few parts `Context` and `Handler`. + +Context represent the state that is needed for execution and handler contains list of functions that act as a logic. + +`Context` is additionally split between `EvmContext` and `External` context. `External` context is fully generic without any trait restrains and its purpose is to allow custom handlers to have access to internal state (For example external contexts can be a Inspector). While `EvmContext` is internal and contains `Database`, Environment, JournaledState and Precompiles. + +Handler is.. it is not generic but has a specification identification variable. It contains list of function that are wrapped around `Arc`. Functions (aka handles) are grouped by functionality on: +* preverification functions. Are related to the preverification of set Environment data. +* main function: Deducs caller balance, loads warm accounts/storages, and handler beneficiary rewards. +* Frame function: Handles call and creates and sub calls. +* Instruction functions: Is instruction table that executes opcodes. + +Builder ties dependencies between generic Database, External context and Spec and allows overriding handlers. As there is a dependency between them setting Database will reset External and Handle field while setting External field would reset Handler. Note that Database will never be reset. -It ties dependency between Database, External and Spec and allows setting custom logic. As there is a dependency between them setting Database will reset External and Handle field while setting External field would reset Handler. Note that Database will never be reset. Simple example of using `EvmBuilder` is diff --git a/documentation/src/crates/revm/evm.md b/documentation/src/crates/revm/evm.md index b314ead3c1..6f7bd53d9c 100644 --- a/documentation/src/crates/revm/evm.md +++ b/documentation/src/crates/revm/evm.md @@ -1,17 +1,48 @@ -# EVM Module Documentation +# EVM -This document provides the documentation for the `EVM` module. +`Evm` is the primary structure that implements the Ethereum Virtual Machine (EVM), a stack-based virtual machine that executes Ethereum smart contracts. -## The `EVM` +It is consisting of two main parts `Context` and `Handler`. `Context` represent the state that is needed for execution and `Handler` contains list of functions that act as a logic. -The primary struct in this module is `EVM`. The EVM struct is generic over a type DB. This means that when you use the EVM struct, you can specify the type that DB should represent. It adds flexibility to the struct, allowing it to store different types of databases or data structures in the db field depending on the use case. The `EVM` struct enables `transact` to update the state directly to the database. Additionally, it allows the user to set all environment parameters. +`Context` is additionally split between `EvmContext` and `External` context. `EvmContext` is internal and contains `Database`, `Environment`, `JournaledState` and `Precompiles`. And `External` context is fully generic without any trait restrains and its purpose is to allow custom handlers to have access to internal state (For example external contexts can be a Inspector), more on its usage can be seen in [`EvmBuilder`](./builder.md) -The parameters that can be set are divided between `Config`, `Block`, and `Transaction` (tx). For transacting on the EVM, you can call `transact_commit` that will automatically apply changes to the database. -## Database Abstractions +`Evm` implements the [`Host`](./host.md) trait, which defines an interface for the interaction of the EVM interpreter with its environment (or "host"), encompassing essential operations such as account and storage access, creating logs, and invoking transactions. -You can implement the traits Database, DatabaseRef or Database + DatabaseCommit depending on the desired handling of the struct. +revm in runtime runs two main loops. First loop is Interpreter loop that loops over bytecode opcodes and executes instruction. Second loop is call loop that everything starts with, it creates call frames, handles subcalls and its return outputs and call Interpreter loop to execute bytecode instructions. -- `Database`: Has mutable `self` in its functions. It's useful if you want to modify your cache or update some statistics on `get` calls. This trait enables `preverify_transaction`, `transact_preverified`, `transact` and `inspect` functions. -- `DatabaseRef`: Takes a reference on the object, this is useful if you only have a reference on the state and don't want to update anything on it. It enables `previerify_transaction`, `transact_preverified_ref`, `transact_ref` and `inspect_ref` functions. -- `Database + DatabaseCommit`: Allows directly committing changes of a transaction. It enables `transact_commit` and `inspect_commit` functions. + + + + +## Methods + +- `run_interpreter` + + This method is responsible for setting up and running the interpreter for a specific contract. + + - `contract`: A `Contract` instance that the interpreter will execute. + - `gas_limit`: A `u64` that determines the maximum amount of gas that the execution can consume. + - `is_static`: A boolean flag indicating if the execution is static. Static executions cannot modify the state. + + The method returns a tuple containing the result of the execution and the interpreter instance. The result is an `InstructionResult` enumeration value that indicates if the execution was successful or if an error occurred. + + This creates a contract with a specific bytecode and a gas price, then runs the interpreter on this contract with a specified gas limit. The is_static flag is set to false which means the execution can modify the state. The stack machine implements the following instructions: + +- `call_precompile` + + This method handles the execution of precompiled contracts. These are a special set of contracts that are part of the Ethereum protocol and implemented in native code for efficiency. + + - `gas`: A `Gas` instance representing the amount of gas available for execution. + - `contract`: The address of the precompiled contract in the form of a `B160` instance. + - `input_data`: The input data for the contract as a `Bytes` instance. + + The method returns a tuple containing the result of the contract execution, the remaining gas, and any output data as a `Bytes` instance. + +- `call_inner` + + This method performs a contract call within the EVM. + + - `inputs`: A mutable reference to a `CallInputs` instance, which contains all the necessary information for the contract call. + + The method returns a tuple containing the result of the call (as an `InstructionResult`), the remaining gas (as a `Gas` instance), and any output data from the call (as a `Bytes` instance). diff --git a/documentation/src/crates/revm/evm_impl.md b/documentation/src/crates/revm/evm_impl.md deleted file mode 100644 index 6d5a0074e6..0000000000 --- a/documentation/src/crates/revm/evm_impl.md +++ /dev/null @@ -1,35 +0,0 @@ -# EVM Implementation - -This module implements the Ethereum Virtual Machine (EVM), a stack-based virtual machine that executes Ethereum smart contracts. The following methods are exposed through the `Evm` struct. - -## Methods - -- `run_interpreter` - - This method is responsible for setting up and running the interpreter for a specific contract. - - - `contract`: A `Contract` instance that the interpreter will execute. - - `gas_limit`: A `u64` that determines the maximum amount of gas that the execution can consume. - - `is_static`: A boolean flag indicating if the execution is static. Static executions cannot modify the state. - - The method returns a tuple containing the result of the execution and the interpreter instance. The result is an `InstructionResult` enumeration value that indicates if the execution was successful or if an error occurred. - - This creates a contract with a specific bytecode and a gas price, then runs the interpreter on this contract with a specified gas limit. The is_static flag is set to false which means the execution can modify the state. The stack machine implements the following instructions: - -- `call_precompile` - - This method handles the execution of precompiled contracts. These are a special set of contracts that are part of the Ethereum protocol and implemented in native code for efficiency. - - - `gas`: A `Gas` instance representing the amount of gas available for execution. - - `contract`: The address of the precompiled contract in the form of a `B160` instance. - - `input_data`: The input data for the contract as a `Bytes` instance. - - The method returns a tuple containing the result of the contract execution, the remaining gas, and any output data as a `Bytes` instance. - -- `call_inner` - - This method performs a contract call within the EVM. - - - `inputs`: A mutable reference to a `CallInputs` instance, which contains all the necessary information for the contract call. - - The method returns a tuple containing the result of the call (as an `InstructionResult`), the remaining gas (as a `Gas` instance), and any output data from the call (as a `Bytes` instance). diff --git a/documentation/src/crates/revm/evm_old.md b/documentation/src/crates/revm/evm_old.md new file mode 100644 index 0000000000..b314ead3c1 --- /dev/null +++ b/documentation/src/crates/revm/evm_old.md @@ -0,0 +1,17 @@ +# EVM Module Documentation + +This document provides the documentation for the `EVM` module. + +## The `EVM` + +The primary struct in this module is `EVM`. The EVM struct is generic over a type DB. This means that when you use the EVM struct, you can specify the type that DB should represent. It adds flexibility to the struct, allowing it to store different types of databases or data structures in the db field depending on the use case. The `EVM` struct enables `transact` to update the state directly to the database. Additionally, it allows the user to set all environment parameters. + +The parameters that can be set are divided between `Config`, `Block`, and `Transaction` (tx). For transacting on the EVM, you can call `transact_commit` that will automatically apply changes to the database. + +## Database Abstractions + +You can implement the traits Database, DatabaseRef or Database + DatabaseCommit depending on the desired handling of the struct. + +- `Database`: Has mutable `self` in its functions. It's useful if you want to modify your cache or update some statistics on `get` calls. This trait enables `preverify_transaction`, `transact_preverified`, `transact` and `inspect` functions. +- `DatabaseRef`: Takes a reference on the object, this is useful if you only have a reference on the state and don't want to update anything on it. It enables `previerify_transaction`, `transact_preverified_ref`, `transact_ref` and `inspect_ref` functions. +- `Database + DatabaseCommit`: Allows directly committing changes of a transaction. It enables `transact_commit` and `inspect_commit` functions. diff --git a/documentation/src/crates/revm/handler.md b/documentation/src/crates/revm/handler.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/documentation/src/crates/revm/host_trait.md b/documentation/src/crates/revm/host_trait.md deleted file mode 100644 index 4ddb06ec24..0000000000 --- a/documentation/src/crates/revm/host_trait.md +++ /dev/null @@ -1,57 +0,0 @@ -# Host Implementation - -The `Host` trait provides an interface that allows the EVM to interact with the external world. It contains methods to access environmental information, manipulate account balances, and interact with contract code and storage. - -The [`Evm`](./evm_impl.md) struct implements this `Host` trait. - -- `step` & `step_end` - - These methods are used to control the interpreter's execution. They move the interpreter forward one step, allowing the user to inspect the state of the interpreter after each individual operation. These control the execution of the interpreter, allowing step-by-step execution and inspection. - -- `env` - - This method returns a mutable reference to the environment information that the EVM uses for its execution. The `Env` struct contains details about the current block, such as the timestamp, block number, difficulty, and gas limit. - -- `block_hash` - - This method retrieves the hash of a block given its number. It's typically used within smart contracts for actions like random number generation. - -- `load_account` - - This method loads the account associated with a given address and returns information about the account's existence and if it's a contract. - -- `balance` - - This method retrieves the balance of an Ethereum account given its address. It returns a tuple containing the balance and a boolean indicating whether the account was "cold" (accessed for the first time in the current transaction). - -- `code` - - This method retrieves the bytecode of a given address. It returns a tuple containing the bytecode and a boolean indicating whether the account was "cold". - -- `code_hash` - - This method retrieves the code_hash at a given address. It returns a tuple containing the hash and a boolean indicating whether the account was "cold". - -- `sload` & `sstore` - - These methods interact with the contract storage. The `sload` method retrieves a value from contract storage, while `sstore` sets a value in contract storage. - -- `tload` & `tstore` - - As defined in [EIP1153](https://eips.ethereum.org/EIPS/eip-1153), for transient storage reads and writes. - -- `log` - - This method is used to create log entries, which are a way for contracts to produce output that external observers (like dapps or the frontend of a blockchain explorer) can listen for and react to. - -- `selfdestruct` - - The selfdestruct method attempts to terminate the specified address, transferring its remaining balance to a given target address. If the `Inspector` is `Some`, the self-destruction event is observed or logged via an inspector. The method returns an Option, encapsulating the outcome of the operation: Some(SelfDestructResult) on success and None if an error occurs, with the error being stored internally for later reference. - -- `create` - - The create method initiates the creation of a contract with the provided CreateInputs. If the `Inspector` is `Some`, the creation process is observed or logged using an inspector, both at the start and end of the creation. The method returns a tuple consisting of the operation's result (InstructionResult), the optional address (Option) of the newly created contract, the amount of gas consumed (Gas), and the output data (Bytes). If the inspector intervenes and determines the instruction shouldn't continue, an early return occurs with the observed outcomes. - -- `call` - - The call method manages a contract invocation using the provided CallInputs. If the `Inspector` is `Some`, the call event is observed or logged via an inspector before execution. The method yields a tuple representing the outcome of the call: the result status (InstructionResult), the consumed gas (Gas), and the output data (Bytes). If the inspector suggests early termination, the method returns immediately with the observed results. Otherwise, the main call execution is processed, and the outcomes, either raw or observed, are returned accordingly. \ No newline at end of file diff --git a/documentation/src/crates/revm/state.md b/documentation/src/crates/revm/state.md new file mode 100644 index 0000000000..aa77850284 --- /dev/null +++ b/documentation/src/crates/revm/state.md @@ -0,0 +1,3 @@ +# State implementations + +State inheritis the `Database` trait and implements fetching of external state and storage. and various functionality on output of the EVM execution, most notable caching changes while execution multiple transactions. \ No newline at end of file From 900ff890f66a06daf821dd9f165c4f71a2753b56 Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 7 Dec 2023 18:17:45 +0100 Subject: [PATCH 20/52] renaming and docs --- crates/revm/src/handler.rs | 103 ++++++++++++++--------- crates/revm/src/handler/optimism.rs | 2 +- crates/revm/src/handler/register.rs | 8 +- documentation/src/SUMMARY.md | 5 +- documentation/src/crates/revm.md | 13 +-- documentation/src/crates/revm/builder.md | 6 +- documentation/src/crates/revm/evm.md | 76 ++++++++++++----- documentation/src/crates/revm/evm_old.md | 8 -- documentation/src/crates/revm/state.md | 10 ++- 9 files changed, 139 insertions(+), 92 deletions(-) diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 3e2b54f2e9..58ffeaeedc 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -139,20 +139,16 @@ pub type ValidateTxEnvAgainstState<'a, EXT, DB> = pub type ValidateInitialTxGasHandle<'a, DB> = Arc Result::Error>> + 'a>; -/// Handler acts as a proxy and allow to define different behavior for different -/// sections of the code. This allows nice integration of different chains or -/// to disable some mainnet behavior. -pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { - /// Specification ID. - pub spec_id: SpecId, - /// Instruction table type. - pub instruction_table: Option>, +pub struct ValidationHandles<'a, EXT, DB: Database> { /// Initial tx gas. pub validate_initial_tx_gas: ValidateInitialTxGasHandle<'a, DB>, /// Validate transactions against state data. pub validate_tx_against_state: ValidateTxEnvAgainstState<'a, EXT, DB>, /// Validate Env pub validate_env: ValidateEnvHandle<'a, DB>, +} + +pub struct MainHandles<'a, EXT, DB: Database> { /// Validate Transaction against the state. /// Uses env, call result and returned gas from the call to determine the gas /// that is returned from transaction execution.. @@ -169,6 +165,9 @@ pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { pub main_return: MainReturnHandle<'a, EXT, DB>, /// End handle. pub end: EndHandle<'a, EXT, DB>, +} + +pub struct FrameHandles<'a, EXT, DB: Database> { /// Create Main frame pub create_first_frame: CreateFirstFrame<'a, EXT, DB>, /// Frame return @@ -177,6 +176,22 @@ pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { pub frame_sub_call: FrameSubCallHandle<'a, EXT, DB>, /// Frame sub crate pub frame_sub_create: FrameSubCreateHandle<'a, EXT, DB>, +} + +/// Handler acts as a proxy and allow to define different behavior for different +/// sections of the code. This allows nice integration of different chains or +/// to disable some mainnet behavior. +pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { + /// Specification ID. + pub spec_id: SpecId, + /// Instruction table type. + pub instruction_table: Option>, + /// Validity handles. + pub validation: ValidationHandles<'a, EXT, DB>, + /// Main handles. + pub main: MainHandles<'a, EXT, DB>, + /// Frame handles. + pub frame: FrameHandles<'a, EXT, DB>, /// Host log handle. pub host_log: HostLogHandle<'a, EXT, DB>, /// Host selfdestruct handle. @@ -189,24 +204,32 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { Self { spec_id: SPEC::SPEC_ID, instruction_table: Some(InstructionTables::Plain(make_instruction_table::())), - validate_initial_tx_gas: Arc::new( - mainnet::preexecution::validate_initial_tx_gas::, - ), - validate_env: Arc::new(mainnet::preexecution::validate_env::), - validate_tx_against_state: Arc::new( - mainnet::preexecution::validate_tx_against_state::, - ), - call_return: Arc::new(mainnet::handle_call_return::), - main_load_handle: Arc::new(mainnet::main_load::), - deduct_caller: Arc::new(mainnet::deduct_caller::), - reimburse_caller: Arc::new(mainnet::handle_reimburse_caller::), - reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), - main_return: Arc::new(mainnet::main::main_return::), - create_first_frame: Arc::new(mainnet::frames::create_first_frame::), - end: Arc::new(mainnet::main::end_handle::), - frame_return: Arc::new(mainnet::frames::handle_frame_return::), - frame_sub_call: Arc::new(mainnet::frames::handle_frame_sub_call::), - frame_sub_create: Arc::new(mainnet::frames::handle_frame_sub_create::), + validation: ValidationHandles { + validate_initial_tx_gas: Arc::new( + mainnet::preexecution::validate_initial_tx_gas::, + ), + validate_env: Arc::new(mainnet::preexecution::validate_env::), + validate_tx_against_state: Arc::new( + mainnet::preexecution::validate_tx_against_state::, + ), + }, + main: MainHandles { + call_return: Arc::new(mainnet::handle_call_return::), + main_load_handle: Arc::new(mainnet::main_load::), + deduct_caller: Arc::new(mainnet::deduct_caller::), + reimburse_caller: Arc::new(mainnet::handle_reimburse_caller::), + reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), + main_return: Arc::new(mainnet::main::main_return::), + end: Arc::new(mainnet::main::end_handle::), + }, + frame: FrameHandles { + create_first_frame: Arc::new(mainnet::frames::create_first_frame::), + frame_return: Arc::new(mainnet::frames::handle_frame_return::), + frame_sub_call: Arc::new(mainnet::frames::handle_frame_sub_call::), + frame_sub_create: Arc::new( + mainnet::frames::handle_frame_sub_create::, + ), + }, host_log: Arc::new(mainnet::host::handle_host_log::), host_selfdestruct: Arc::new(mainnet::host::handle_selfdestruct::), } @@ -214,7 +237,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// Handle call return, depending on instruction result gas will be reimbursed or not. pub fn call_return(&self, env: &Env, call_result: InstructionResult, returned_gas: Gas) -> Gas { - (self.call_return)(env, call_result, returned_gas) + (self.main.call_return)(env, call_result, returned_gas) } /// Reimburse the caller with gas that were not spend. @@ -223,12 +246,12 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { context: &mut Context, gas: &Gas, ) -> Result<(), EVMError> { - (self.reimburse_caller)(context, gas) + (self.main.reimburse_caller)(context, gas) } /// Deduct caller to its limit. pub fn deduct_caller(&self, context: &mut Context) -> Result<(), EVMError> { - (self.deduct_caller)(context) + (self.main.deduct_caller)(context) } /// Reward beneficiary @@ -237,7 +260,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { context: &mut Context, gas: &Gas, ) -> Result<(), EVMError> { - (self.reward_beneficiary)(context, gas) + (self.main.reward_beneficiary)(context, gas) } /// Main return. @@ -248,7 +271,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { output: Output, gas: &Gas, ) -> Result> { - (self.main_return)(context, call_result, output, gas) + (self.main.main_return)(context, call_result, output, gas) } /// End handler. @@ -257,7 +280,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { context: &mut Context, end_output: Result>, ) -> Result> { - (self.end)(context, end_output) + (self.main.end)(context, end_output) } /// Call frame sub call handler. @@ -269,7 +292,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { shared_memory: &mut SharedMemory, return_memory_offset: Range, ) -> Option> { - (self.frame_sub_call)( + (self.frame.frame_sub_call)( context, inputs, curent_stack_frame, @@ -284,7 +307,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { curent_stack_frame: &mut CallStackFrame, inputs: Box, ) -> Option> { - (self.frame_sub_create)(context, curent_stack_frame, inputs) + (self.frame.frame_sub_create)(context, curent_stack_frame, inputs) } /// Frame return @@ -296,7 +319,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { shared_memory: &mut SharedMemory, result: InterpreterResult, ) -> Option { - (self.frame_return)( + (self.frame.frame_return)( context, child_stack_frame, parent_stack_frame, @@ -328,12 +351,12 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// Validate env. pub fn validate_env(&self, env: &Env) -> Result<(), EVMError> { - (self.validate_env)(env) + (self.validation.validate_env)(env) } /// Initial gas pub fn validate_initial_tx_gas(&self, env: &Env) -> Result> { - (self.validate_initial_tx_gas)(env) + (self.validation.validate_initial_tx_gas)(env) } /// Validate ttansaction against the state. @@ -341,7 +364,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { &self, context: &mut Context, ) -> Result<(), EVMError> { - (self.validate_tx_against_state)(context) + (self.validation.validate_tx_against_state)(context) } /// Create first call frame. @@ -350,11 +373,11 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { context: &mut Context, gas_limit: u64, ) -> FrameOrResult { - (self.create_first_frame)(context, gas_limit) + (self.frame.create_first_frame)(context, gas_limit) } /// Main load pub fn main_load(&self, context: &mut Context) -> Result<(), EVMError> { - (self.main_load_handle)(context) + (self.main.main_load_handle)(context) } } diff --git a/crates/revm/src/handler/optimism.rs b/crates/revm/src/handler/optimism.rs index cc4a082f4f..1af36f1499 100644 --- a/crates/revm/src/handler/optimism.rs +++ b/crates/revm/src/handler/optimism.rs @@ -239,7 +239,7 @@ pub fn main_return( let result = mainnet::main::main_return::(context, call_result, output, gas)?; if result.result.is_halt() { - // Post-regolith, if the transaction is a deposit transaction and it haults, + // Post-regolith, if the transaction is a deposit transaction and it halts, // we bubble up to the global return handler. The mint value will be persisted // and the caller nonce will be incremented there. let is_deposit = context.env.tx.optimism.source_hash.is_some(); diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index 2424384b82..0a8ebac5aa 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -61,7 +61,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( )); // handle sub create - handler.frame_sub_create = Arc::new( + handler.frame.frame_sub_create = Arc::new( move |context, frame, mut inputs| -> Option> { if let Some((result, address)) = context .external @@ -89,7 +89,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( ); // handle sub call - handler.frame_sub_call = Arc::new( + handler.frame.frame_sub_call = Arc::new( move |context, mut inputs, frame, memory, return_memory_offset| -> Option> { // inspector handle let inspector = &mut context.external.get_inspector(); @@ -118,8 +118,8 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( ); // return frame handle - let old_handle = handler.frame_return.clone(); - handler.frame_return = Arc::new( + let old_handle = handler.frame.frame_return.clone(); + handler.frame.frame_return = Arc::new( move |context, mut child, parent, memory, mut result| -> Option { let inspector = &mut context.external.get_inspector(); result = if child.is_create { diff --git a/documentation/src/SUMMARY.md b/documentation/src/SUMMARY.md index 9cc423b5b3..bfa087b036 100644 --- a/documentation/src/SUMMARY.md +++ b/documentation/src/SUMMARY.md @@ -3,9 +3,10 @@ - [Introduction](./introduction.md) - [Revm](./crates/revm.md) - [evm](./crates/revm/evm.md) - - [evm_impl](./crates/revm/evm_impl.md) - - [The Host Trait](./crates/revm/host_trait.md) + - [builder](./crates/revm/builder.md) + - [handler](./crates/revm/handler.md) - [inspector](./crates/revm/inspector.md) + - [state](./crates/revm/state.md) - [journaled_state](./crates/revm/journaled_state.md) - [Interpreter](./crates/interpreter.md) - [gas](./crates/interpreter/gas.md) diff --git a/documentation/src/crates/revm.md b/documentation/src/crates/revm.md index 9240096d7a..9d31ea00c0 100644 --- a/documentation/src/crates/revm.md +++ b/documentation/src/crates/revm.md @@ -1,19 +1,14 @@ # Rust Ethereum Virtual Machine (revm) -The `crate` is focused on the implementation of Ethereum Virtual Machine (EVM) including call loop and host implementation, database handling, state journaling, logic handlers and an inspection system for observing and logging the execution of EVM. This crate pulls together everything described prior to deliver the rust evm. +The `crate` is focused on the implementation of Ethereum Virtual Machine (EVM) including call loop and host implementation, database handling, state journaling and powerful logic handlers that can be overwritten. This crate pulls Primitives, Interpreter and Precompiles together to deliver the rust evm. Modules: - +- `evm`: This is main module that executed EVM calls. +- `builder`: This modules build the Evm, sets database, handlers and other parameters. Here is where we set handlers for specific fork or external state for inspection. - `db`: This module includes structures and functions for database interaction. It is a glue between EVM and database. It transforms or aggregates the EVM changes. -- `evm`: This module contains a Struct that takes Database and enabled transact to update state directly to database. Additionally it allows user to set all environment parameters. -- `evm_impl`: This module includes more specific implementations related to the EVM regarding state transitions. -- `inspector`: This module introduces the `Inspector` trait and its implementations for observing the EVM execution. +- `inspector`: This module introduces the `Inspector` trait and its implementations for observing the EVM execution. This was main way to inspect EVM execution before the Builder and Handlers were introduced. It is still enabled through the Builder. - `journaled_state`: This module manages the state of the EVM and implements a journaling system to handle changes and reverts. -External Crates: - -- `alloc`: The alloc crate is used to provide the ability to allocate memory on the heap. It's a part of Rust's standard library that can be used in environments without a full host OS. - Re-exported Crates: - `revm_precompile`: This crate is re-exported, providing the precompiled contracts used in the EVM implementation. diff --git a/documentation/src/crates/revm/builder.md b/documentation/src/crates/revm/builder.md index a74ebc6a36..d6ba47e66a 100644 --- a/documentation/src/crates/revm/builder.md +++ b/documentation/src/crates/revm/builder.md @@ -3,11 +3,7 @@ It creates the EVM and applies different handler, and allows setting external context and custom logic. -EVM inside revm consist of the few parts `Context` and `Handler`. - -Context represent the state that is needed for execution and handler contains list of functions that act as a logic. - -`Context` is additionally split between `EvmContext` and `External` context. `External` context is fully generic without any trait restrains and its purpose is to allow custom handlers to have access to internal state (For example external contexts can be a Inspector). While `EvmContext` is internal and contains `Database`, Environment, JournaledState and Precompiles. +`Evm` inside revm consist of the few parts `Context` and `Handler`. `Context` is additionally split between `EvmContext` and `External` context. Read here for more information [`Evm`](./evm.md) internals. Handler is.. it is not generic but has a specification identification variable. It contains list of function that are wrapped around `Arc`. Functions (aka handles) are grouped by functionality on: * preverification functions. Are related to the preverification of set Environment data. diff --git a/documentation/src/crates/revm/evm.md b/documentation/src/crates/revm/evm.md index 6f7bd53d9c..86da7c753b 100644 --- a/documentation/src/crates/revm/evm.md +++ b/documentation/src/crates/revm/evm.md @@ -2,47 +2,79 @@ `Evm` is the primary structure that implements the Ethereum Virtual Machine (EVM), a stack-based virtual machine that executes Ethereum smart contracts. -It is consisting of two main parts `Context` and `Handler`. `Context` represent the state that is needed for execution and `Handler` contains list of functions that act as a logic. +## Structure -`Context` is additionally split between `EvmContext` and `External` context. `EvmContext` is internal and contains `Database`, `Environment`, `JournaledState` and `Precompiles`. And `External` context is fully generic without any trait restrains and its purpose is to allow custom handlers to have access to internal state (For example external contexts can be a Inspector), more on its usage can be seen in [`EvmBuilder`](./builder.md) +It is consisting of two main parts the `Context` and the `Handler`. `Context` represent the state that is needed for execution and `Handler` contains list of functions that act as a logic. +`Context` is additionally split between `EvmContext` and `External` context. `EvmContext` is internal and contains `Database`, `Environment`, `JournaledState` and `Precompiles`. And `External` context is fully generic without any trait restrains and its purpose is to allow custom handlers to save state in runtime or allows hooks to be added (For example external contexts can be a Inspector), more on its usage can be seen in [`EvmBuilder`](./builder.md) -`Evm` implements the [`Host`](./host.md) trait, which defines an interface for the interaction of the EVM interpreter with its environment (or "host"), encompassing essential operations such as account and storage access, creating logs, and invoking transactions. +`Evm` implements the [`Host`](./../interpreter/host.md) trait, which defines an interface for the interaction of the EVM Interpreter with its environment (or "host"), encompassing essential operations such as account and storage access, creating logs, and invoking sub calls and selfdestruct. -revm in runtime runs two main loops. First loop is Interpreter loop that loops over bytecode opcodes and executes instruction. Second loop is call loop that everything starts with, it creates call frames, handles subcalls and its return outputs and call Interpreter loop to execute bytecode instructions. +Data structures of block and transaction can be found inside `Environment` +## Runtime +Runtime consist of two parts first is verification and second is execution. +Handlers logic that does verification are: +* validate_env + + That verifies if all data is set in `Environment` and if they are valid, for example if `gas_limit` is smaller than block `gas_limit`. -## Methods +* validate_initial_tx_gas + + It calculated initial gas needed for transaction to be executed and checks if it is less them the transaction gas_limit. Note that this does not touch the `Database` or state -- `run_interpreter` +* validate_tx_against_state + + It loads the caller account and checks those information. Among them the nonce, if there is enough balance to pay for max gas spent and balance transferred. - This method is responsible for setting up and running the interpreter for a specific contract. +Logic when running transaction consist of few stages that are implemented as a handle calls: +* main_load + + Loads access list and beneficiary from `Database`. Cold load is done here. - - `contract`: A `Contract` instance that the interpreter will execute. - - `gas_limit`: A `u64` that determines the maximum amount of gas that the execution can consume. - - `is_static`: A boolean flag indicating if the execution is static. Static executions cannot modify the state. +* deduct_caller: + + Deducts values from the caller to the maximum amount of gas that can be spent on the transaction. This loads the caller account from the `Database`. - The method returns a tuple containing the result of the execution and the interpreter instance. The result is an `InstructionResult` enumeration value that indicates if the execution was successful or if an error occurred. +* create_first_frame and start_the_loop + + These two handles main call loop that creates and handles stack of frames. It is responsible for handling subcalls and its return outputs and call Interpreter loop to execute bytecode instructions. - This creates a contract with a specific bytecode and a gas price, then runs the interpreter on this contract with a specified gas limit. The is_static flag is set to false which means the execution can modify the state. The stack machine implements the following instructions: +* call_return + + Handler that allows processing of the returned output from the call. It calculated refunded gas and final spent gas. -- `call_precompile` +* reimburse_caller + + Reimburse the caller with gas that was not spent during the execution of the transaction. + Or balance of gas that needs to be refunded. - This method handles the execution of precompiled contracts. These are a special set of contracts that are part of the Ethereum protocol and implemented in native code for efficiency. +* reward_beneficiary + + At the end of every transaction beneficiary needs to be rewarded with the fee. - - `gas`: A `Gas` instance representing the amount of gas available for execution. - - `contract`: The address of the precompiled contract in the form of a `B160` instance. - - `input_data`: The input data for the contract as a `Bytes` instance. +* main_return - The method returns a tuple containing the result of the contract execution, the remaining gas, and any output data as a `Bytes` instance. + It returns the changes state and the result of the execution. -- `call_inner` +`Evm` in runtime runs **two** loops.First loop is call loop that everything starts with, it creates call frames, handles subcalls and its return outputs and call Interpreter loop to execute bytecode instructions. Second loop is Interpreter loop that loops over bytecode opcodes and executes instruction. - This method performs a contract call within the EVM. +First loop, the call loop, implements stack of `Frames` and it is responsible for handling sub calls and its return outputs. At the start Evm creates first `Frame` that contains `Interpreter` and starts the loop. `Interpreter` returns the `InterpreterAction` and action can be a `Return` of a call this means this interpreter finished its run or `SubCall`/`SubCreate` that means that new `Frame` needs to be created and pushed to the stack. When `Interpreter` returns `Return` action `Frame` is popped from the stack and its return value is pushed to the parent `Frame` stack. When `Interpreter` returns `SubCall`/`SubCreate` action new `Frame` is created and pushed to the stack and the loop continues. When the stack is empty the loop finishes. - - `inputs`: A mutable reference to a `CallInputs` instance, which contains all the necessary information for the contract call. +Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction. It is called from the call loop and it is responsible for executing bytecode instructions. It is implemented in [`Interpreter`](../revm_interpreter/interpreter.md) crate. + + +# Functions + +`Evm` is build with a `EvmBuilder` that allows setting of `Database`, `External` context and `Handler`. Builder is created with `Evm::builder()` function. For more information on building check [`EvmBuilder`](./builder.md) documentation. + +After building `Evm` it can be used to execute transactions. There are three functions that can be used to execute transactions: +* preverify +* transaction preverified +* transact + +If we want to modify `Evm` for example change the specification we can use `.modify()` function that would give us the `EvmBuilder` back and we can set new specification and build new `Evm` from it, as setting new specification would need to reset the `Handler` functions. - The method returns a tuple containing the result of the call (as an `InstructionResult`), the remaining gas (as a `Gas` instance), and any output data from the call (as a `Bytes` instance). diff --git a/documentation/src/crates/revm/evm_old.md b/documentation/src/crates/revm/evm_old.md index b314ead3c1..0a0fad90b4 100644 --- a/documentation/src/crates/revm/evm_old.md +++ b/documentation/src/crates/revm/evm_old.md @@ -7,11 +7,3 @@ This document provides the documentation for the `EVM` module. The primary struct in this module is `EVM`. The EVM struct is generic over a type DB. This means that when you use the EVM struct, you can specify the type that DB should represent. It adds flexibility to the struct, allowing it to store different types of databases or data structures in the db field depending on the use case. The `EVM` struct enables `transact` to update the state directly to the database. Additionally, it allows the user to set all environment parameters. The parameters that can be set are divided between `Config`, `Block`, and `Transaction` (tx). For transacting on the EVM, you can call `transact_commit` that will automatically apply changes to the database. - -## Database Abstractions - -You can implement the traits Database, DatabaseRef or Database + DatabaseCommit depending on the desired handling of the struct. - -- `Database`: Has mutable `self` in its functions. It's useful if you want to modify your cache or update some statistics on `get` calls. This trait enables `preverify_transaction`, `transact_preverified`, `transact` and `inspect` functions. -- `DatabaseRef`: Takes a reference on the object, this is useful if you only have a reference on the state and don't want to update anything on it. It enables `previerify_transaction`, `transact_preverified_ref`, `transact_ref` and `inspect_ref` functions. -- `Database + DatabaseCommit`: Allows directly committing changes of a transaction. It enables `transact_commit` and `inspect_commit` functions. diff --git a/documentation/src/crates/revm/state.md b/documentation/src/crates/revm/state.md index aa77850284..0dc646b088 100644 --- a/documentation/src/crates/revm/state.md +++ b/documentation/src/crates/revm/state.md @@ -1,3 +1,11 @@ # State implementations -State inheritis the `Database` trait and implements fetching of external state and storage. and various functionality on output of the EVM execution, most notable caching changes while execution multiple transactions. \ No newline at end of file +State inheritis the `Database` trait and implements fetching of external state and storage. and various functionality on output of the EVM execution, most notable caching changes while execution multiple transactions. + +## Database Abstractions + +You can implement the traits Database, DatabaseRef or Database + DatabaseCommit depending on the desired handling of the struct. + +- `Database`: Has mutable `self` in its functions. It's useful if you want to modify your cache or update some statistics on `get` calls. This trait enables `preverify_transaction`, `transact_preverified`, `transact` and `inspect` functions. +- `DatabaseRef`: Takes a reference on the object, this is useful if you only have a reference on the state and don't want to update anything on it. It enables `previerify_transaction`, `transact_preverified_ref`, `transact_ref` and `inspect_ref` functions. +- `Database + DatabaseCommit`: Allows directly committing changes of a transaction. It enables `transact_commit` and `inspect_commit` functions. \ No newline at end of file From 55f8924358605ebdb055edf3127919f74ffd70f6 Mon Sep 17 00:00:00 2001 From: rakita Date: Fri, 8 Dec 2023 17:42:08 +0100 Subject: [PATCH 21/52] Support all Inspector functionality with Handler --- crates/revm/src/handler/register.rs | 129 +++++++++++++++++++--------- crates/revm/src/inspector.rs | 24 ++---- crates/revm/src/inspector/gas.rs | 12 +-- 3 files changed, 99 insertions(+), 66 deletions(-) diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index 0a8ebac5aa..8caeb57afd 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -2,10 +2,11 @@ use crate::{ db::Database, handler::Handler, inspector_instruction, - interpreter::{opcode::InstructionTables, InterpreterResult, SelfDestructResult}, - CallStackFrame, Evm, FrameOrResult, Inspector, + interpreter::{opcode::InstructionTables, InterpreterResult}, + CallStackFrame, Evm, FrameOrResult, Inspector, JournalEntry, }; use alloc::sync::Arc; +use revm_interpreter::{opcode, Interpreter}; pub trait GetInspector<'a, DB: Database> { fn get_inspector(&mut self) -> &mut dyn Inspector; @@ -36,6 +37,7 @@ impl<'a, EXT, DB: Database> HandleRegisters<'a, EXT, DB> { } } +/// Register Inspector handles that interact with Inspector instance. pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( handler: &mut EvmHandler<'a, EXT, DB>, ) { @@ -45,7 +47,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( .instruction_table .take() .expect("Handler must have instruction table"); - let table = match table { + let mut table = match table { EvmInstructionTables::Plain(table) => table .into_iter() .map(|i| inspector_instruction(i)) @@ -56,6 +58,75 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( .collect::>(), }; + // Register inspector Log instruction. + let mut inspect_log = |index: u8| { + table.get_mut(index as usize).map(|i| { + Box::new( + |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { + let old_log_len = host.context.evm.journaled_state.logs.len(); + i(interpreter, host); + // check if log was added. It is possible that revert happened + // cause of gas or stack underflow. + if host.context.evm.journaled_state.logs.len() == old_log_len + 1 { + // clone log. + // TODO decide if we should remove this and leave the comment + // that log can be found as journaled_state. + let last_log = host + .context + .evm + .journaled_state + .logs + .last() + .unwrap() + .clone(); + // call Inspector + host.context + .external + .get_inspector() + .log(&mut host.context.evm, &last_log); + } + }, + ) + }); + }; + + inspect_log(opcode::LOG0); + inspect_log(opcode::LOG1); + inspect_log(opcode::LOG2); + inspect_log(opcode::LOG3); + inspect_log(opcode::LOG4); + + // register selfdestruct function. + table.get_mut(opcode::SELFDESTRUCT as usize).map(|i| { + Box::new( + |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { + // execute selfdestruct + i(interpreter, host); + // check if selfdestruct was successful and if journal entry is made. + if let Some(JournalEntry::AccountDestroyed { + address, + target, + had_balance, + .. + }) = host + .context + .evm + .journaled_state + .journal + .last() + .unwrap() + .last() + { + host.context.external.get_inspector().selfdestruct( + *address, + *target, + *had_balance, + ); + } + }, + ) + }); + handler.instruction_table = Some(EvmInstructionTables::Boxed( table.try_into().unwrap_or_else(|_| unreachable!()), )); @@ -63,23 +134,20 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( // handle sub create handler.frame.frame_sub_create = Arc::new( move |context, frame, mut inputs| -> Option> { - if let Some((result, address)) = context - .external - .get_inspector() - .create(&mut context.evm, &mut inputs) - { + let inspector = context.external.get_inspector(); + if let Some((result, address)) = inspector.create(&mut context.evm, &mut inputs) { frame.interpreter.insert_create_output(result, address); return None; } match context.evm.make_create_frame(spec_id, &inputs) { - FrameOrResult::Frame(new_frame) => Some(new_frame), + FrameOrResult::Frame(mut new_frame) => { + inspector.initialize_interp(&mut new_frame.interpreter, &mut context.evm); + Some(new_frame) + } FrameOrResult::Result(result) => { - let (result, address) = context.external.get_inspector().create_end( - &mut context.evm, - result, - frame.created_address, - ); + let (result, address) = + inspector.create_end(&mut context.evm, result, frame.created_address); // insert result of the failed creation of create CallStackFrame. frame.interpreter.insert_create_output(result, address); None @@ -92,7 +160,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( handler.frame.frame_sub_call = Arc::new( move |context, mut inputs, frame, memory, return_memory_offset| -> Option> { // inspector handle - let inspector = &mut context.external.get_inspector(); + let inspector = context.external.get_inspector(); if let Some((result, range)) = inspector.call(&mut context.evm, &mut inputs) { frame.interpreter.insert_call_output(memory, result, range); return None; @@ -101,13 +169,13 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( .evm .make_call_frame(&inputs, return_memory_offset.clone()) { - FrameOrResult::Frame(new_frame) => Some(new_frame), + FrameOrResult::Frame(mut new_frame) => { + inspector.initialize_interp(&mut new_frame.interpreter, &mut context.evm); + Some(new_frame) + } FrameOrResult::Result(result) => { // inspector handle - let result = context - .external - .get_inspector() - .call_end(&mut context.evm, result); + let result = inspector.call_end(&mut context.evm, result); frame .interpreter .insert_call_output(memory, result, return_memory_offset); @@ -133,25 +201,4 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( old_handle(context, child, parent, memory, result) }, ); - - // handle log - let old_handle = handler.host_log.clone(); - handler.host_log = Arc::new(move |context, address, topics, data| { - context - .external - .get_inspector() - .log(&mut context.evm, &address, &topics, &data); - old_handle(context, address, topics, data) - }); - - // selfdestruct handle - let old_handle = handler.host_selfdestruct.clone(); - handler.host_selfdestruct = Arc::new( - move |context, address, target| -> Option { - let inspector = &mut context.external.get_inspector(); - let acc = context.evm.journaled_state.state.get(&address).unwrap(); - inspector.selfdestruct(address, target, acc.info.balance); - old_handle(context, address, target) - }, - ); } diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index 1622fccaa6..18fa54b44b 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -2,7 +2,7 @@ use core::ops::Range; use crate::{ interpreter::{CallInputs, CreateInputs, Interpreter}, - primitives::{db::Database, Address, Bytes, B256, U256}, + primitives::{db::Database, Address, Log, U256}, EvmContext, }; use auto_impl::auto_impl; @@ -54,21 +54,6 @@ pub trait Inspector { let _ = context; } - /// Called when a log is emitted. - #[inline] - fn log( - &mut self, - context: &mut EvmContext, - address: &Address, - topics: &[B256], - data: &Bytes, - ) { - let _ = context; - let _ = address; - let _ = topics; - let _ = data; - } - /// Called after `step` when the instruction has been executed. /// /// Setting `interp.instruction_result` to anything other than [crate::interpreter::InstructionResult::Continue] alters the execution @@ -79,6 +64,13 @@ pub trait Inspector { let _ = context; } + /// Called when a log is emitted. + #[inline] + fn log(&mut self, context: &mut EvmContext, log: &Log) { + let _ = context; + let _ = log; + } + /// Called whenever a call to a contract is about to start. /// /// InstructionResulting anything other than [crate::interpreter::InstructionResult::Continue] overrides the result of the call. diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 5feb28d844..2f98fe0a5b 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -72,7 +72,7 @@ mod tests { handler::register::{inspector_handle_register, GetInspector}, inspectors::GasInspector, interpreter::{CallInputs, CreateInputs, Interpreter, InterpreterResult}, - primitives::{Address, Bytes, B256}, + primitives::{Address, Log}, Database, EvmContext, Inspector, }; @@ -99,14 +99,8 @@ mod tests { self.gas_inspector.step(interp, context); } - fn log( - &mut self, - context: &mut EvmContext, - address: &Address, - topics: &[B256], - data: &Bytes, - ) { - self.gas_inspector.log(context, address, topics, data); + fn log(&mut self, context: &mut EvmContext, log: &Log) { + self.gas_inspector.log(context, log); } fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { From c930aebab993d9c9b90f03c010327a6b3da423a5 Mon Sep 17 00:00:00 2001 From: rakita Date: Sat, 9 Dec 2023 01:51:29 +0100 Subject: [PATCH 22/52] Handler restructured. Documentation added --- crates/revm/src/builder.rs | 124 +++--- crates/revm/src/evm.rs | 78 ++-- crates/revm/src/frame.rs | 1 - crates/revm/src/handler.rs | 404 ++++-------------- crates/revm/src/handler/handle_types.rs | 21 + crates/revm/src/handler/handle_types/frame.rs | 154 +++++++ crates/revm/src/handler/handle_types/main.rs | 128 ++++++ .../src/handler/handle_types/validation.rs | 60 +++ crates/revm/src/handler/mainnet.rs | 241 +---------- .../handler/mainnet/{frames.rs => frame.rs} | 97 ++++- crates/revm/src/handler/mainnet/host.rs | 36 -- crates/revm/src/handler/mainnet/main.rs | 137 +++++- .../{preexecution.rs => validation.rs} | 2 + crates/revm/src/handler/register.rs | 184 +------- crates/revm/src/inspector.rs | 6 +- crates/revm/src/inspector/gas.rs | 9 +- crates/revm/src/inspector/handler_register.rs | 234 ++++++++++ crates/revm/src/inspector/instruction.rs | 60 --- crates/revm/src/inspector/noop.rs | 4 +- crates/revm/src/lib.rs | 4 +- .../handler_register.rs} | 32 +- documentation/src/crates/revm/builder.md | 16 +- documentation/src/crates/revm/evm.md | 66 +-- documentation/src/crates/revm/evm_old.md | 9 - documentation/src/crates/revm/handler.md | 76 ++++ 25 files changed, 1138 insertions(+), 1045 deletions(-) create mode 100644 crates/revm/src/handler/handle_types.rs create mode 100644 crates/revm/src/handler/handle_types/frame.rs create mode 100644 crates/revm/src/handler/handle_types/main.rs create mode 100644 crates/revm/src/handler/handle_types/validation.rs rename crates/revm/src/handler/mainnet/{frames.rs => frame.rs} (59%) delete mode 100644 crates/revm/src/handler/mainnet/host.rs rename crates/revm/src/handler/mainnet/{preexecution.rs => validation.rs} (94%) create mode 100644 crates/revm/src/inspector/handler_register.rs delete mode 100644 crates/revm/src/inspector/instruction.rs rename crates/revm/src/{handler/optimism.rs => optimism/handler_register.rs} (93%) delete mode 100644 documentation/src/crates/revm/evm_old.md diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index c50ae0382d..8c30933bb2 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -3,7 +3,7 @@ use crate::{ db::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}, handler::register, - primitives::{BlockEnv, CfgEnv, Env, LatestSpec, Spec, SpecId, TxEnv}, + primitives::{BlockEnv, CfgEnv, Env, LatestSpec, SpecId, TxEnv}, Context, Evm, EvmContext, Handler, }; use core::marker::PhantomData; @@ -15,18 +15,23 @@ pub struct EvmBuilder<'a, Stage: BuilderStage, EXT, DB: Database> { evm: EvmContext, external: EXT, handler: Handler<'a, Evm<'a, EXT, DB>, EXT, DB>, - handle_registers: Vec>, phantom: PhantomData, } +/// Trait that unlocks builder stages. pub trait BuilderStage {} +/// First stage of the builder allows setting the database. pub struct SettingDbStage; impl BuilderStage for SettingDbStage {} +/// Second stage of the builder allows setting the external context. +/// Requires the database to be set. pub struct SettingExternalStage; impl BuilderStage for SettingExternalStage {} +/// Third stage of the builder allows setting the handler. +/// Requires the database and external context to be set. pub struct SettingHandlerStage; impl BuilderStage for SettingHandlerStage {} @@ -36,7 +41,6 @@ impl<'a> Default for EvmBuilder<'a, SettingDbStage, (), EmptyDB> { evm: EvmContext::new(EmptyDB::default()), external: (), handler: Handler::mainnet::(), - handle_registers: Vec::new(), phantom: PhantomData, } } @@ -53,7 +57,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { evm: EvmContext::new(EmptyDB::default()), external: self.external, handler: Handler::mainnet::(), - handle_registers: Vec::new(), + phantom: PhantomData, } } @@ -67,7 +71,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { evm: EvmContext::new(db), external: self.external, handler: Handler::mainnet::(), - handle_registers: Vec::new(), + phantom: PhantomData, } } @@ -84,7 +88,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { evm: EvmContext::new(WrapDatabaseRef(db)), external: self.external, handler: Handler::mainnet::(), - handle_registers: Vec::new(), + phantom: PhantomData, } } @@ -102,17 +106,18 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { } impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternalStage, EXT, DB> { - pub fn with_empty_external(self) -> EvmBuilder<'a, SettingHandlerStage, (), DB> { + /// Sets empty external context. + pub fn without_external_context(self) -> EvmBuilder<'a, SettingHandlerStage, (), DB> { EvmBuilder { evm: self.evm, external: (), handler: Handler::mainnet::(), - handle_registers: Vec::new(), phantom: PhantomData, } } - pub fn with_external( + /// Sets the external context that will be used by [`Evm`]. + pub fn with_external_context( self, external: OEXT, ) -> EvmBuilder<'a, SettingHandlerStage, OEXT, DB> { @@ -120,7 +125,6 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternalStage, EXT, DB> { evm: self.evm, external: external, handler: Handler::mainnet::(), - handle_registers: Vec::new(), phantom: PhantomData, } } @@ -146,8 +150,6 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { evm: evm.context.evm, external: evm.context.external, handler: evm.handler, - // TODO move registers from EVM - handle_registers: Vec::new(), phantom: PhantomData, } } @@ -163,51 +165,6 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { } } - /// Creates the Handler with Generic Spec. - fn create_handle_generic( - &self, - ) -> Handler<'a, Evm<'a, EXT, DB>, EXT, DB> { - let mut handler = Handler::mainnet::(); - // apply all registers to default handeler and raw mainnet instruction table. - for register in self.handle_registers.iter() { - register.register(&mut handler); - if handler.instruction_table.is_none() { - panic!("Handler must have instruction table") - } - } - handler - } - - /// Creates the Handler with variable SpecId, inside it will call function with Generic Spec. - fn create_handler(&self, spec_id: SpecId) -> Handler<'a, Evm<'a, EXT, DB>, EXT, DB> { - use crate::primitives::specification::*; - match spec_id { - SpecId::FRONTIER | SpecId::FRONTIER_THAWING => { - self.create_handle_generic::() - } - SpecId::HOMESTEAD | SpecId::DAO_FORK => self.create_handle_generic::(), - SpecId::TANGERINE => self.create_handle_generic::(), - SpecId::SPURIOUS_DRAGON => self.create_handle_generic::(), - SpecId::BYZANTIUM => self.create_handle_generic::(), - SpecId::PETERSBURG | SpecId::CONSTANTINOPLE => { - self.create_handle_generic::() - } - SpecId::ISTANBUL | SpecId::MUIR_GLACIER => self.create_handle_generic::(), - SpecId::BERLIN => self.create_handle_generic::(), - SpecId::LONDON | SpecId::ARROW_GLACIER | SpecId::GRAY_GLACIER => { - self.create_handle_generic::() - } - SpecId::MERGE => self.create_handle_generic::(), - SpecId::SHANGHAI => self.create_handle_generic::(), - SpecId::CANCUN => self.create_handle_generic::(), - SpecId::LATEST => self.create_handle_generic::(), - #[cfg(feature = "optimism")] - SpecId::BEDROCK => self.create_handle_generic::(), - #[cfg(feature = "optimism")] - SpecId::REGOLITH => self.create_handle_generic::(), - } - } - /// Sets specification Id , that will mark the version of EVM. /// It represent the hard fork of ethereum. /// @@ -215,28 +172,28 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { /// /// When changed it will reapply all handle registers. pub fn with_spec_id(mut self, spec_id: SpecId) -> Self { - // TODO add match for other spec - self.handler = self.create_handler(spec_id); + self.handler = self.handler.change_spec_id(spec_id); self } - pub fn append_handler( + /// Appends the handler register to the handler. + pub fn append_handler_register( mut self, handle_register: register::HandleRegister<'a, EXT, DB>, ) -> Self { - self.handle_registers - .push(register::HandleRegisters::Plain(handle_register)); + self.handler + .append_handle_register(register::HandleRegisters::Plain(handle_register)); self } /// Register Handler that modifies the behavior of EVM. /// Check [`Handler`] for more information. - pub fn append_handler_box( + pub fn append_handler_register_box( mut self, handle_register: register::HandleRegisterBox<'a, EXT, DB>, ) -> Self { - self.handle_registers - .push(register::HandleRegisters::Box(handle_register)); + self.handler + .append_handle_register(register::HandleRegisters::Box(handle_register)); self } } @@ -290,7 +247,7 @@ impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> mod test { use super::SpecId; use crate::{ - db::EmptyDB, handler::register::inspector_handle_register, inspectors::NoOpInspector, Evm, + db::EmptyDB, inspector::inspector_handle_register, inspectors::NoOpInspector, Evm, }; #[test] @@ -302,13 +259,19 @@ mod test { // build with_db Evm::builder().with_db(EmptyDB::default()).build(); // build with empty external - Evm::builder().with_empty_db().with_empty_external().build(); + Evm::builder() + .with_empty_db() + .without_external_context() + .build(); // build with some external - Evm::builder().with_empty_db().with_external(()).build(); + Evm::builder() + .with_empty_db() + .with_external_context(()) + .build(); // build with spec Evm::builder() .with_empty_db() - .with_empty_external() + .without_external_context() .with_spec_id(SpecId::HOMESTEAD) .build(); @@ -316,7 +279,7 @@ mod test { Evm::builder() .with_empty_db() .modify_tx_env(|tx| tx.gas_limit = 10) - .with_empty_external() + .without_external_context() .build(); Evm::builder().modify_tx_env(|tx| tx.gas_limit = 10).build(); Evm::builder() @@ -325,15 +288,30 @@ mod test { .build(); Evm::builder() .with_empty_db() - .with_empty_external() + .without_external_context() .modify_tx_env(|tx| tx.gas_limit = 10) .build(); // with inspector handle Evm::builder() .with_empty_db() - .with_external(NoOpInspector::default()) - .append_handler(inspector_handle_register) + .with_external_context(NoOpInspector::default()) + .append_handler_register(inspector_handle_register) + .build(); + } + + #[test] + fn build_modify_build() { + let evm = Evm::builder() + .with_empty_db() + .without_external_context() + .with_spec_id(SpecId::HOMESTEAD) + .build(); + + let evm = evm.modify().with_spec_id(SpecId::FRONTIER).build(); + let _ = evm + .modify() + .modify_tx_env(|tx| tx.chain_id = Some(2)) .build(); } } diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 02e7c346f9..8cd720373e 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -9,7 +9,7 @@ use crate::{ SelfDestructResult, SharedMemory, }, primitives::{ - specification::SpecId, Address, Bytecode, Bytes, EVMError, EVMResult, Env, Output, + specification::SpecId, Address, Bytecode, Bytes, EVMError, EVMResult, Env, Log, Output, TransactTo, B256, U256, }, CallStackFrame, Context, FrameOrResult, @@ -69,10 +69,13 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { /// has enough balance to pay for the gas. #[inline] pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { - self.handler.validate_env(&self.context.evm.env)?; + self.handler.validation().env(&self.context.evm.env)?; self.handler - .validate_initial_tx_gas(&self.context.evm.env)?; - self.handler.validate_tx_against_state(&mut self.context)?; + .validation() + .initial_tx_gas(&self.context.evm.env)?; + self.handler + .validation() + .tx_against_state(&mut self.context)?; Ok(()) } @@ -81,22 +84,26 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { pub fn transact_preverified(&mut self) -> EVMResult { let initial_gas_spend = self .handler - .validate_initial_tx_gas(&self.context.evm.env)?; + .validation() + .initial_tx_gas(&self.context.evm.env)?; let output = self.transact_preverified_inner(initial_gas_spend); - self.handler.end(&mut self.context, output) + self.handler.main().end(&mut self.context, output) } /// Transact transaction, this function will validate the transaction. #[inline] pub fn transact(&mut self) -> EVMResult { - self.handler.validate_env(&self.context.evm.env)?; + self.handler.validation().env(&self.context.evm.env)?; let initial_gas_spend = self .handler - .validate_initial_tx_gas(&self.context.evm.env)?; - self.handler.validate_tx_against_state(&mut self.context)?; + .validation() + .initial_tx_gas(&self.context.evm.env)?; + self.handler + .validation() + .tx_against_state(&mut self.context)?; let output = self.transact_preverified_inner(initial_gas_spend); - self.handler.end(&mut self.context, output) + self.handler.main().end(&mut self.context, output) } /// Allow for evm setting to be modified by feeding current evm @@ -110,9 +117,7 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { if self.spec_id() == spec_id { return self; } - //self.modify().with_spec_id(spec_id).build() - // TODO - self + self.modify().with_spec_id(spec_id).build() } /// Returns internal database and external struct. @@ -136,8 +141,7 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { // take instruction talbe let table = self .handler - .instruction_table - .take() + .take_instruction_table() .expect("Instruction table should be present"); // run main loop @@ -147,7 +151,7 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { }; // return instruction table - self.handler.instruction_table = Some(table); + self.handler.set_instruction_table(table); output } @@ -198,7 +202,7 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { InterpreterAction::SubCall { inputs, return_memory_offset, - } => self.handler.frame_sub_call( + } => self.handler.frame().sub_call( &mut self.context, inputs, stack_frame, @@ -207,7 +211,8 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { ), InterpreterAction::Create { inputs } => { self.handler - .frame_sub_create(&mut self.context, stack_frame, inputs) + .frame() + .sub_create(&mut self.context, stack_frame, inputs) } InterpreterAction::Return { result } => { // free memory context. @@ -216,7 +221,7 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { let child = call_stack.pop().unwrap(); let parent = call_stack.last_mut(); - if let Some(result) = self.handler.frame_return( + if let Some(result) = self.handler.frame().frame_return( &mut self.context, child, parent, @@ -243,12 +248,13 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { let ctx = &mut self.context; // load access list and beneficiary if needed. - hndl.main_load(ctx)?; + hndl.main().load(ctx)?; // deduce caller balance with its limit. - hndl.deduct_caller(ctx)?; + hndl.main().deduct_caller(ctx)?; // gas limit used in calls. - let first_frame = - hndl.create_first_frame(ctx, ctx.evm.env.tx.gas_limit - initial_gas_spend); + let first_frame = hndl + .frame() + .create_first_frame(ctx, ctx.evm.env.tx.gas_limit - initial_gas_spend); // Starts the main running loop. let (result, main_output) = self.start_the_loop(first_frame); @@ -257,13 +263,16 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { let ctx = &mut self.context; // handle output of call/create calls. - let gas = hndl.call_return(&ctx.evm.env, result.result, result.gas); + let gas = hndl + .frame() + .first_frame_return(&ctx.evm.env, result.result, result.gas); // Reimburse the caller - hndl.reimburse_caller(ctx, &gas)?; + hndl.main().reimburse_caller(ctx, &gas)?; // Reward beneficiary - hndl.reward_beneficiary(ctx, &gas)?; + hndl.main().reward_beneficiary(ctx, &gas)?; // main return - hndl.main_return(ctx, result.result, main_output, &gas) + hndl.main() + .main_return(ctx, result.result, main_output, &gas) } } @@ -314,13 +323,20 @@ impl<'a, EXT, DB: Database> Host for Evm<'a, EXT, DB> { } fn log(&mut self, address: Address, topics: Vec, data: Bytes) { - self.handler - .host_log(&mut self.context, address, topics, data); + self.context.evm.journaled_state.log(Log { + address, + topics, + data, + }); } fn selfdestruct(&mut self, address: Address, target: Address) -> Option { - self.handler - .host_selfdestruct(&mut self.context, address, target) + self.context + .evm + .journaled_state + .selfdestruct(address, target, &mut self.context.evm.db) + .map_err(|e| self.context.evm.error = Some(e)) + .ok() } } diff --git a/crates/revm/src/frame.rs b/crates/revm/src/frame.rs index 706be21895..e78d61cca5 100644 --- a/crates/revm/src/frame.rs +++ b/crates/revm/src/frame.rs @@ -9,7 +9,6 @@ use core::ops::Range; #[derive(Debug)] pub struct CallStackFrame { /// True if it is create false if it is call. - /// TODO make a enum for this. pub is_create: bool, /// Journal checkpoint pub checkpoint: JournalCheckpoint, diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 58ffeaeedc..a98abbbde3 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -3,180 +3,24 @@ //! hardfork (Berlin, London, ..). // Modules. +mod handle_types; pub mod mainnet; -#[cfg(feature = "optimism")] -pub mod optimism; pub mod register; // Exports. -pub use register::{inspector_handle_register, HandleRegister}; +pub use handle_types::*; // Includes. use crate::{ interpreter::{ opcode::{make_instruction_table, InstructionTables}, - CallInputs, CreateInputs, Gas, Host, InstructionResult, InterpreterResult, - SelfDestructResult, SharedMemory, + Host, }, - precompile::{Address, Bytes, B256}, - primitives::{ - db::Database, EVMError, EVMResultGeneric, Env, Output, ResultAndState, Spec, SpecId, - }, - CallStackFrame, Context, FrameOrResult, + primitives::{db::Database, Spec, SpecId}, }; -use alloc::sync::Arc; -use core::ops::Range; - -/// Handle call return and return final gas value. -pub type CallReturnHandle<'a> = Arc Gas + 'a>; - -/// Load access list account, precompiles and beneficiary. -/// There is not need to load Caller as it is assumed that -/// it will be loaded in DeductCallerHandle. -pub type MainLoadHandle<'a, EXT, DB> = - Arc) -> Result<(), EVMError<::Error>> + 'a>; - -/// Deduct the caller to its limit. -pub type DeductCallerHandle<'a, EXT, DB> = - Arc) -> EVMResultGeneric<(), ::Error> + 'a>; - -/// Reimburse the caller with ethereum it didn't spent. -pub type ReimburseCallerHandle<'a, EXT, DB> = - Arc, &Gas) -> EVMResultGeneric<(), ::Error> + 'a>; - -/// Reward beneficiary with transaction rewards. -pub type RewardBeneficiaryHandle<'a, EXT, DB> = ReimburseCallerHandle<'a, EXT, DB>; - -/// Main return handle, takes state from journal and transforms internal result to external. -pub type MainReturnHandle<'a, EXT, DB> = Arc< - dyn Fn( - &mut Context, - InstructionResult, - Output, - &Gas, - ) -> Result::Error>> - + 'a, ->; - -/// After subcall is finished, call this function to handle return result. -/// -/// Return Some if we want to halt execution. This can be done on any stack frame. -pub type FrameReturnHandle<'a, EXT, DB> = Arc< - dyn Fn( - // context - &mut Context, - // returned frame - Box, - // parent frame if it exist. - Option<&mut Box>, - // shared memory to insert output of the call. - &mut SharedMemory, - // output of frame execution. - InterpreterResult, - ) -> Option - + 'a, ->; - -/// Create first frame. -pub type CreateFirstFrame<'a, EXT, DB> = - Arc, u64) -> FrameOrResult + 'a>; - -/// Call to the host from Interpreter to save the log. -pub type HostLogHandle<'a, EXT, DB> = - Arc, Address, Vec, Bytes) + 'a>; - -/// Call to the host from Interpreter to selfdestruct account. -/// -/// After CANCUN hardfork original contract will stay the same but the value will -/// be transfered to the target. -pub type HostSelfdestructHandle<'a, EXT, DB> = - Arc, Address, Address) -> Option + 'a>; +use register::HandleRegisters; -/// End handle, takes result and state and returns final result. -/// This will be called after all the other handlers. -/// -/// It is useful for catching errors and returning them in a different way. -pub type EndHandle<'a, EXT, DB> = Arc< - dyn Fn( - &mut Context, - Result::Error>>, - ) -> Result::Error>> - + 'a, ->; - -/// Handle sub call. -pub type FrameSubCallHandle<'a, EXT, DB> = Arc< - dyn Fn( - &mut Context, - Box, - &mut CallStackFrame, - &mut SharedMemory, - Range, - ) -> Option> - + 'a, ->; - -/// Handle sub create. -pub type FrameSubCreateHandle<'a, EXT, DB> = Arc< - dyn Fn( - &mut Context, - &mut CallStackFrame, - Box, - ) -> Option> - + 'a, ->; - -/// Handle that validates env. -pub type ValidateEnvHandle<'a, DB> = - Arc Result<(), EVMError<::Error>> + 'a>; - -/// Handle that validates transaction environment against the state. -/// Second parametar is initial gas. -pub type ValidateTxEnvAgainstState<'a, EXT, DB> = - Arc) -> Result<(), EVMError<::Error>> + 'a>; - -/// Initial gas calculation handle -pub type ValidateInitialTxGasHandle<'a, DB> = - Arc Result::Error>> + 'a>; - -pub struct ValidationHandles<'a, EXT, DB: Database> { - /// Initial tx gas. - pub validate_initial_tx_gas: ValidateInitialTxGasHandle<'a, DB>, - /// Validate transactions against state data. - pub validate_tx_against_state: ValidateTxEnvAgainstState<'a, EXT, DB>, - /// Validate Env - pub validate_env: ValidateEnvHandle<'a, DB>, -} - -pub struct MainHandles<'a, EXT, DB: Database> { - /// Validate Transaction against the state. - /// Uses env, call result and returned gas from the call to determine the gas - /// that is returned from transaction execution.. - pub call_return: CallReturnHandle<'a>, - /// Main load handle - pub main_load_handle: MainLoadHandle<'a, EXT, DB>, - /// Deduct max value from the caller. - pub deduct_caller: DeductCallerHandle<'a, EXT, DB>, - /// Reimburse the caller with ethereum it didn't spent. - pub reimburse_caller: ReimburseCallerHandle<'a, EXT, DB>, - /// Reward the beneficiary with caller fee. - pub reward_beneficiary: RewardBeneficiaryHandle<'a, EXT, DB>, - /// Main return handle, returns the output of the transact. - pub main_return: MainReturnHandle<'a, EXT, DB>, - /// End handle. - pub end: EndHandle<'a, EXT, DB>, -} - -pub struct FrameHandles<'a, EXT, DB: Database> { - /// Create Main frame - pub create_first_frame: CreateFirstFrame<'a, EXT, DB>, - /// Frame return - pub frame_return: FrameReturnHandle<'a, EXT, DB>, - /// Frame sub call - pub frame_sub_call: FrameSubCallHandle<'a, EXT, DB>, - /// Frame sub crate - pub frame_sub_create: FrameSubCreateHandle<'a, EXT, DB>, -} +use self::register::EvmHandler; /// Handler acts as a proxy and allow to define different behavior for different /// sections of the code. This allows nice integration of different chains or @@ -186,16 +30,14 @@ pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { pub spec_id: SpecId, /// Instruction table type. pub instruction_table: Option>, + /// Registers that will be called on initialization. + pub registers: Vec>, /// Validity handles. - pub validation: ValidationHandles<'a, EXT, DB>, + pub validation: ValidationHandler<'a, EXT, DB>, /// Main handles. - pub main: MainHandles<'a, EXT, DB>, + pub main: MainHandler<'a, EXT, DB>, /// Frame handles. - pub frame: FrameHandles<'a, EXT, DB>, - /// Host log handle. - pub host_log: HostLogHandle<'a, EXT, DB>, - /// Host selfdestruct handle. - pub host_selfdestruct: HostSelfdestructHandle<'a, EXT, DB>, + pub frame: FrameHandler<'a, EXT, DB>, } impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { @@ -204,180 +46,92 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { Self { spec_id: SPEC::SPEC_ID, instruction_table: Some(InstructionTables::Plain(make_instruction_table::())), - validation: ValidationHandles { - validate_initial_tx_gas: Arc::new( - mainnet::preexecution::validate_initial_tx_gas::, - ), - validate_env: Arc::new(mainnet::preexecution::validate_env::), - validate_tx_against_state: Arc::new( - mainnet::preexecution::validate_tx_against_state::, - ), - }, - main: MainHandles { - call_return: Arc::new(mainnet::handle_call_return::), - main_load_handle: Arc::new(mainnet::main_load::), - deduct_caller: Arc::new(mainnet::deduct_caller::), - reimburse_caller: Arc::new(mainnet::handle_reimburse_caller::), - reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), - main_return: Arc::new(mainnet::main::main_return::), - end: Arc::new(mainnet::main::end_handle::), - }, - frame: FrameHandles { - create_first_frame: Arc::new(mainnet::frames::create_first_frame::), - frame_return: Arc::new(mainnet::frames::handle_frame_return::), - frame_sub_call: Arc::new(mainnet::frames::handle_frame_sub_call::), - frame_sub_create: Arc::new( - mainnet::frames::handle_frame_sub_create::, - ), - }, - host_log: Arc::new(mainnet::host::handle_host_log::), - host_selfdestruct: Arc::new(mainnet::host::handle_selfdestruct::), + registers: Vec::new(), + validation: ValidationHandler::new::(), + main: MainHandler::new::(), + frame: FrameHandler::new::(), } } - /// Handle call return, depending on instruction result gas will be reimbursed or not. - pub fn call_return(&self, env: &Env, call_result: InstructionResult, returned_gas: Gas) -> Gas { - (self.main.call_return)(env, call_result, returned_gas) - } - - /// Reimburse the caller with gas that were not spend. - pub fn reimburse_caller( - &self, - context: &mut Context, - gas: &Gas, - ) -> Result<(), EVMError> { - (self.main.reimburse_caller)(context, gas) - } - - /// Deduct caller to its limit. - pub fn deduct_caller(&self, context: &mut Context) -> Result<(), EVMError> { - (self.main.deduct_caller)(context) - } - - /// Reward beneficiary - pub fn reward_beneficiary( - &self, - context: &mut Context, - gas: &Gas, - ) -> Result<(), EVMError> { - (self.main.reward_beneficiary)(context, gas) - } - - /// Main return. - pub fn main_return( - &self, - context: &mut Context, - call_result: InstructionResult, - output: Output, - gas: &Gas, - ) -> Result> { - (self.main.main_return)(context, call_result, output, gas) - } - - /// End handler. - pub fn end( - &self, - context: &mut Context, - end_output: Result>, - ) -> Result> { - (self.main.end)(context, end_output) - } - - /// Call frame sub call handler. - pub fn frame_sub_call( - &self, - context: &mut Context, - inputs: Box, - curent_stack_frame: &mut CallStackFrame, - shared_memory: &mut SharedMemory, - return_memory_offset: Range, - ) -> Option> { - (self.frame.frame_sub_call)( - context, - inputs, - curent_stack_frame, - shared_memory, - return_memory_offset, - ) - } - - pub fn frame_sub_create( - &self, - context: &mut Context, - curent_stack_frame: &mut CallStackFrame, - inputs: Box, - ) -> Option> { - (self.frame.frame_sub_create)(context, curent_stack_frame, inputs) + /// Specification ID. + pub fn spec_id(&self) -> SpecId { + self.spec_id } - /// Frame return - pub fn frame_return( - &self, - context: &mut Context, - child_stack_frame: Box, - parent_stack_frame: Option<&mut Box>, - shared_memory: &mut SharedMemory, - result: InterpreterResult, - ) -> Option { - (self.frame.frame_return)( - context, - child_stack_frame, - parent_stack_frame, - shared_memory, - result, - ) + /// Take instruction table. + pub fn take_instruction_table(&mut self) -> Option> { + self.instruction_table.take() } - /// Call host log handle. - pub fn host_log( - &self, - context: &mut Context, - address: Address, - topics: Vec, - data: Bytes, - ) { - (self.host_log)(context, address, topics, data) + /// Set instruction table. + pub fn set_instruction_table(&mut self, table: InstructionTables<'a, H>) { + self.instruction_table = Some(table); } - /// Call host selfdestruct handle. - pub fn host_selfdestruct( - &self, - context: &mut Context, - address: Address, - target: Address, - ) -> Option { - (self.host_selfdestruct)(context, address, target) + /// Returns reference to main handler. + pub fn main(&self) -> &MainHandler<'a, EXT, DB> { + &self.main } - /// Validate env. - pub fn validate_env(&self, env: &Env) -> Result<(), EVMError> { - (self.validation.validate_env)(env) + /// Returns reference to frame handler. + pub fn frame(&self) -> &FrameHandler<'a, EXT, DB> { + &self.frame } - /// Initial gas - pub fn validate_initial_tx_gas(&self, env: &Env) -> Result> { - (self.validation.validate_initial_tx_gas)(env) + /// Returns reference to validation handler. + pub fn validation(&self) -> &ValidationHandler<'a, EXT, DB> { + &self.validation } +} - /// Validate ttansaction against the state. - pub fn validate_tx_against_state( - &self, - context: &mut Context, - ) -> Result<(), EVMError> { - (self.validation.validate_tx_against_state)(context) +impl<'a, EXT: 'a, DB: Database + 'a> EvmHandler<'a, EXT, DB> { + /// Append handle register. + pub fn append_handle_register(&mut self, register: HandleRegisters<'a, EXT, DB>) { + register.register(self); + self.registers.push(register); } - /// Create first call frame. - pub fn create_first_frame( - &self, - context: &mut Context, - gas_limit: u64, - ) -> FrameOrResult { - (self.frame.create_first_frame)(context, gas_limit) + /// Creates the Handler with Generic Spec. + pub fn create_handle_generic(&mut self) -> EvmHandler<'a, EXT, DB> { + let registers = core::mem::take(&mut self.registers); + let mut base_handler = Handler::mainnet::(); + // apply all registers to default handeler and raw mainnet instruction table. + for register in registers { + base_handler.append_handle_register(register) + } + base_handler } - /// Main load - pub fn main_load(&self, context: &mut Context) -> Result<(), EVMError> { - (self.main.main_load_handle)(context) + /// Creates the Handler with variable SpecId, inside it will call function with Generic Spec. + pub fn change_spec_id(mut self, spec_id: SpecId) -> EvmHandler<'a, EXT, DB> { + if self.spec_id == spec_id { + return self; + } + use crate::primitives::specification::*; + match spec_id { + SpecId::FRONTIER | SpecId::FRONTIER_THAWING => { + self.create_handle_generic::() + } + SpecId::HOMESTEAD | SpecId::DAO_FORK => self.create_handle_generic::(), + SpecId::TANGERINE => self.create_handle_generic::(), + SpecId::SPURIOUS_DRAGON => self.create_handle_generic::(), + SpecId::BYZANTIUM => self.create_handle_generic::(), + SpecId::PETERSBURG | SpecId::CONSTANTINOPLE => { + self.create_handle_generic::() + } + SpecId::ISTANBUL | SpecId::MUIR_GLACIER => self.create_handle_generic::(), + SpecId::BERLIN => self.create_handle_generic::(), + SpecId::LONDON | SpecId::ARROW_GLACIER | SpecId::GRAY_GLACIER => { + self.create_handle_generic::() + } + SpecId::MERGE => self.create_handle_generic::(), + SpecId::SHANGHAI => self.create_handle_generic::(), + SpecId::CANCUN => self.create_handle_generic::(), + SpecId::LATEST => self.create_handle_generic::(), + #[cfg(feature = "optimism")] + SpecId::BEDROCK => self.create_handle_generic::(), + #[cfg(feature = "optimism")] + SpecId::REGOLITH => self.create_handle_generic::(), + } } } diff --git a/crates/revm/src/handler/handle_types.rs b/crates/revm/src/handler/handle_types.rs new file mode 100644 index 0000000000..8c22e5b1a2 --- /dev/null +++ b/crates/revm/src/handler/handle_types.rs @@ -0,0 +1,21 @@ +// Modules + +pub mod frame; +pub mod main; +pub mod validation; + +// Exports + +pub use validation::{ + ValidateEnvHandle, ValidateInitialTxGasHandle, ValidateTxEnvAgainstState, ValidationHandler, +}; + +pub use main::{ + DeductCallerHandle, EndHandle, MainHandler, MainLoadHandle, MainReturnHandle, + ReimburseCallerHandle, RewardBeneficiaryHandle, +}; + +pub use frame::{ + CreateFirstFrameHandle, FrameHandler, FrameReturnHandle, FrameSubCallHandle, + FrameSubCreateHandle, +}; diff --git a/crates/revm/src/handler/handle_types/frame.rs b/crates/revm/src/handler/handle_types/frame.rs new file mode 100644 index 0000000000..b9819b2f79 --- /dev/null +++ b/crates/revm/src/handler/handle_types/frame.rs @@ -0,0 +1,154 @@ +use crate::{ + handler::mainnet, + interpreter::{ + CallInputs, CreateInputs, Gas, InstructionResult, InterpreterResult, SharedMemory, + }, + primitives::{db::Database, Env, Spec}, + CallStackFrame, Context, FrameOrResult, +}; +use alloc::sync::Arc; +use core::ops::Range; + +/// Creates the first frame. +pub type CreateFirstFrameHandle<'a, EXT, DB> = + Arc, u64) -> FrameOrResult + 'a>; + +/// Handles first frame return handle. +pub type FirstFrameReturnHandle<'a> = Arc Gas + 'a>; + +/// After subcall is finished, call this function to handle return result. +/// +/// Return Some if we want to halt execution. This can be done on any stack frame. +pub type FrameReturnHandle<'a, EXT, DB> = Arc< + dyn Fn( + // context + &mut Context, + // returned frame + Box, + // parent frame if it exist. + Option<&mut Box>, + // shared memory to insert output of the call. + &mut SharedMemory, + // output of frame execution. + InterpreterResult, + ) -> Option + + 'a, +>; + +/// Handle sub call. +pub type FrameSubCallHandle<'a, EXT, DB> = Arc< + dyn Fn( + &mut Context, + Box, + &mut CallStackFrame, + &mut SharedMemory, + Range, + ) -> Option> + + 'a, +>; + +/// Handle sub create. +pub type FrameSubCreateHandle<'a, EXT, DB> = Arc< + dyn Fn( + &mut Context, + &mut CallStackFrame, + Box, + ) -> Option> + + 'a, +>; + +/// Handles related to stack frames. +pub struct FrameHandler<'a, EXT, DB: Database> { + /// Create Main frame + pub create_first_frame: CreateFirstFrameHandle<'a, EXT, DB>, + /// Validate Transaction against the state. + /// Uses env, call result and returned gas from the call to determine the gas + /// that is returned from transaction execution.. + pub first_frame_return: FirstFrameReturnHandle<'a>, + /// Frame return + pub frame_return: FrameReturnHandle<'a, EXT, DB>, + /// Frame sub call + pub sub_call: FrameSubCallHandle<'a, EXT, DB>, + /// Frame sub crate + pub sub_create: FrameSubCreateHandle<'a, EXT, DB>, +} + +impl<'a, EXT: 'a, DB: Database + 'a> FrameHandler<'a, EXT, DB> { + /// Creates mainnet FrameHandler.. + pub fn new() -> Self { + Self { + first_frame_return: Arc::new(mainnet::main_frame_return::), + create_first_frame: Arc::new(mainnet::create_first_frame::), + frame_return: Arc::new(mainnet::handle_frame_return::), + sub_call: Arc::new(mainnet::handle_frame_sub_call::), + sub_create: Arc::new(mainnet::handle_frame_sub_create::), + } + } +} + +impl<'a, EXT, DB: Database> FrameHandler<'a, EXT, DB> { + /// Create first call frame. + pub fn create_first_frame( + &self, + context: &mut Context, + gas_limit: u64, + ) -> FrameOrResult { + (self.create_first_frame)(context, gas_limit) + } + + /// Handle call return, depending on instruction result gas will be reimbursed or not. + pub fn first_frame_return( + &self, + env: &Env, + call_result: InstructionResult, + returned_gas: Gas, + ) -> Gas { + (self.first_frame_return)(env, call_result, returned_gas) + } + + /// Call frame sub call handler. + pub fn sub_call( + &self, + context: &mut Context, + inputs: Box, + curent_stack_frame: &mut CallStackFrame, + shared_memory: &mut SharedMemory, + return_memory_offset: Range, + ) -> Option> { + (self.sub_call)( + context, + inputs, + curent_stack_frame, + shared_memory, + return_memory_offset, + ) + } + + /// Create sub frame + pub fn sub_create( + &self, + context: &mut Context, + curent_stack_frame: &mut CallStackFrame, + inputs: Box, + ) -> Option> { + (self.sub_create)(context, curent_stack_frame, inputs) + } + + /// Frame return + pub fn frame_return( + &self, + context: &mut Context, + child_stack_frame: Box, + parent_stack_frame: Option<&mut Box>, + shared_memory: &mut SharedMemory, + result: InterpreterResult, + ) -> Option { + (self.frame_return)( + context, + child_stack_frame, + parent_stack_frame, + shared_memory, + result, + ) + } +} diff --git a/crates/revm/src/handler/handle_types/main.rs b/crates/revm/src/handler/handle_types/main.rs new file mode 100644 index 0000000000..f14910f738 --- /dev/null +++ b/crates/revm/src/handler/handle_types/main.rs @@ -0,0 +1,128 @@ +// Includes. +use crate::{ + handler::mainnet, + interpreter::{Gas, InstructionResult}, + primitives::{db::Database, EVMError, EVMResultGeneric, Output, ResultAndState, Spec}, + Context, +}; +use alloc::sync::Arc; + +/// Load access list account, precompiles and beneficiary. +/// There is not need to load Caller as it is assumed that +/// it will be loaded in DeductCallerHandle. +pub type MainLoadHandle<'a, EXT, DB> = + Arc) -> Result<(), EVMError<::Error>> + 'a>; + +/// Deduct the caller to its limit. +pub type DeductCallerHandle<'a, EXT, DB> = + Arc) -> EVMResultGeneric<(), ::Error> + 'a>; + +/// Reimburse the caller with ethereum it didn't spent. +pub type ReimburseCallerHandle<'a, EXT, DB> = + Arc, &Gas) -> EVMResultGeneric<(), ::Error> + 'a>; + +/// Reward beneficiary with transaction rewards. +pub type RewardBeneficiaryHandle<'a, EXT, DB> = ReimburseCallerHandle<'a, EXT, DB>; + +/// Main return handle, takes state from journal and transforms internal result to external. +pub type MainReturnHandle<'a, EXT, DB> = Arc< + dyn Fn( + &mut Context, + InstructionResult, + Output, + &Gas, + ) -> Result::Error>> + + 'a, +>; + +/// End handle, takes result and state and returns final result. +/// This will be called after all the other handlers. +/// +/// It is useful for catching errors and returning them in a different way. +pub type EndHandle<'a, EXT, DB> = Arc< + dyn Fn( + &mut Context, + Result::Error>>, + ) -> Result::Error>> + + 'a, +>; + +/// Handles related to main function. +pub struct MainHandler<'a, EXT, DB: Database> { + /// Main load handle + pub load: MainLoadHandle<'a, EXT, DB>, + /// Deduct max value from the caller. + pub deduct_caller: DeductCallerHandle<'a, EXT, DB>, + /// Reimburse the caller with ethereum it didn't spent. + pub reimburse_caller: ReimburseCallerHandle<'a, EXT, DB>, + /// Reward the beneficiary with caller fee. + pub reward_beneficiary: RewardBeneficiaryHandle<'a, EXT, DB>, + /// Main return handle, returns the output of the transact. + pub main_return: MainReturnHandle<'a, EXT, DB>, + /// End handle. + pub end: EndHandle<'a, EXT, DB>, +} + +impl<'a, EXT: 'a, DB: Database + 'a> MainHandler<'a, EXT, DB> { + /// Creates mainnet MainHandles. + pub fn new() -> Self { + Self { + load: Arc::new(mainnet::main_load::), + deduct_caller: Arc::new(mainnet::main_deduct_caller::), + reimburse_caller: Arc::new(mainnet::main_reimburse_caller::), + reward_beneficiary: Arc::new(mainnet::main_reward_beneficiary::), + main_return: Arc::new(mainnet::main_return::), + end: Arc::new(mainnet::main_end::), + } + } +} + +impl<'a, EXT, DB: Database> MainHandler<'a, EXT, DB> { + /// Reimburse the caller with gas that were not spend. + pub fn reimburse_caller( + &self, + context: &mut Context, + gas: &Gas, + ) -> Result<(), EVMError> { + (self.reimburse_caller)(context, gas) + } + + /// Deduct caller to its limit. + pub fn deduct_caller(&self, context: &mut Context) -> Result<(), EVMError> { + (self.deduct_caller)(context) + } + + /// Reward beneficiary + pub fn reward_beneficiary( + &self, + context: &mut Context, + gas: &Gas, + ) -> Result<(), EVMError> { + (self.reward_beneficiary)(context, gas) + } + + /// Main return. + pub fn main_return( + &self, + context: &mut Context, + call_result: InstructionResult, + output: Output, + gas: &Gas, + ) -> Result> { + (self.main_return)(context, call_result, output, gas) + } + + /// End handler. + pub fn end( + &self, + context: &mut Context, + end_output: Result>, + ) -> Result> { + (self.end)(context, end_output) + } + + /// Main load + pub fn load(&self, context: &mut Context) -> Result<(), EVMError> { + (self.load)(context) + } +} diff --git a/crates/revm/src/handler/handle_types/validation.rs b/crates/revm/src/handler/handle_types/validation.rs new file mode 100644 index 0000000000..8516960d28 --- /dev/null +++ b/crates/revm/src/handler/handle_types/validation.rs @@ -0,0 +1,60 @@ +use crate::{ + handler::mainnet, + primitives::{db::Database, EVMError, Env, Spec}, + Context, +}; +use alloc::sync::Arc; + +/// Handle that validates env. +pub type ValidateEnvHandle<'a, DB> = + Arc Result<(), EVMError<::Error>> + 'a>; + +/// Handle that validates transaction environment against the state. +/// Second parametar is initial gas. +pub type ValidateTxEnvAgainstState<'a, EXT, DB> = + Arc) -> Result<(), EVMError<::Error>> + 'a>; + +/// Initial gas calculation handle +pub type ValidateInitialTxGasHandle<'a, DB> = + Arc Result::Error>> + 'a>; + +/// Handles related to validation. +pub struct ValidationHandler<'a, EXT, DB: Database> { + /// Validate and calculate initial transaction gas. + pub initial_tx_gas: ValidateInitialTxGasHandle<'a, DB>, + /// Validate transactions against state data. + pub tx_against_state: ValidateTxEnvAgainstState<'a, EXT, DB>, + /// Validate Env. + pub env: ValidateEnvHandle<'a, DB>, +} + +impl<'a, EXT: 'a, DB: Database + 'a> ValidationHandler<'a, EXT, DB> { + /// Create new ValidationHandles + pub fn new() -> Self { + Self { + initial_tx_gas: Arc::new(mainnet::validate_initial_tx_gas::), + env: Arc::new(mainnet::validate_env::), + tx_against_state: Arc::new(mainnet::validate_tx_against_state::), + } + } +} + +impl<'a, EXT, DB: Database> ValidationHandler<'a, EXT, DB> { + /// Validate env. + pub fn env(&self, env: &Env) -> Result<(), EVMError> { + (self.env)(env) + } + + /// Initial gas + pub fn initial_tx_gas(&self, env: &Env) -> Result> { + (self.initial_tx_gas)(env) + } + + /// Validate ttansaction against the state. + pub fn tx_against_state( + &self, + context: &mut Context, + ) -> Result<(), EVMError> { + (self.tx_against_state)(context) + } +} diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index 21f278f1f5..4be73cc9a1 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -1,234 +1,15 @@ //! Mainnet related handlers. -pub mod frames; -pub mod host; -pub mod main; -pub mod preexecution; +mod frame; +mod main; +mod validation; -use crate::{ - interpreter::{return_ok, return_revert, Gas, InstructionResult}, - primitives::{ - db::Database, - Account, EVMError, Env, Spec, - SpecId::{CANCUN, LONDON, SHANGHAI}, - TransactTo, U256, - }, - Context, +pub use frame::{ + create_first_frame, frame_return_with_refund_flag, handle_frame_return, handle_frame_sub_call, + handle_frame_sub_create, main_frame_return, }; - -pub fn handle_call_return_with_refund_flag( - env: &Env, - call_result: InstructionResult, - returned_gas: Gas, - refund_enabled: bool, -) -> Gas { - // Spend the gas limit. Gas is reimbursed when the tx returns successfully. - let mut gas = Gas::new(env.tx.gas_limit); - gas.record_cost(env.tx.gas_limit); - - match call_result { - return_ok!() => { - gas.erase_cost(returned_gas.remaining()); - gas.record_refund(returned_gas.refunded()); - } - return_revert!() => { - gas.erase_cost(returned_gas.remaining()); - } - _ => {} - } - // Calculate gas refund for transaction. - // If config is set to disable gas refund, it will return 0. - // If spec is set to london, it will decrease the maximum refund amount to 5th part of - // gas spend. (Before london it was 2th part of gas spend) - if refund_enabled { - // EIP-3529: Reduction in refunds - gas.set_final_refund::() - }; - - gas -} - -/// Handle output of the transaction -#[inline] -pub fn handle_call_return( - env: &Env, - call_result: InstructionResult, - returned_gas: Gas, -) -> Gas { - handle_call_return_with_refund_flag::(env, call_result, returned_gas, true) -} - -pub(crate) fn deduct_caller_inner(caller_account: &mut Account, env: &Env) { - // Subtract gas costs from the caller's account. - // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. - let mut gas_cost = U256::from(env.tx.gas_limit).saturating_mul(env.effective_gas_price()); - - // EIP-4844 - if SPEC::enabled(CANCUN) { - let data_fee = env.calc_data_fee().expect("already checked"); - gas_cost = gas_cost.saturating_add(data_fee); - } - - // set new caller account balance. - caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); - - // bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`. - if matches!(env.tx.transact_to, TransactTo::Call(_)) { - // Nonce is already checked - caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); - } - - // touch account so we know it is changed. - caller_account.mark_touch(); -} - -/// Main load handle -#[inline] -pub fn main_load( - context: &mut Context, -) -> Result<(), EVMError> { - // the L1-cost fee is only computed for Optimism non-deposit transactions. - #[cfg(feature = "optimism")] - if env.cfg.optimism && env.tx.optimism.source_hash.is_none() { - let l1_block_info = - optimism::L1BlockInfo::try_fetch(self.context.evm.db).map_err(EVMError::Database)?; - - // storage l1 block info for later use. - self.context.evm.l1_block_info = Some(l1_block_info); - - tx_l1_cost - } - - // load coinbase - // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm - if SPEC::enabled(SHANGHAI) { - context - .evm - .journaled_state - .initial_account_load(context.evm.env.block.coinbase, &[], &mut context.evm.db) - .map_err(EVMError::Database)?; - } - - context.evm.load_access_list()?; - Ok(()) -} - -#[inline] -pub fn deduct_caller( - context: &mut Context, -) -> Result<(), EVMError> { - // load caller's account. - let (caller_account, _) = context - .evm - .journaled_state - .load_account(context.evm.env.tx.caller, &mut context.evm.db) - .map_err(EVMError::Database)?; - - // deduct gas cost from caller's account. - deduct_caller_inner::(caller_account, &context.evm.env); - - Ok(()) -} - -#[inline] -pub fn handle_reimburse_caller( - context: &mut Context, - gas: &Gas, -) -> Result<(), EVMError> { - let caller = context.evm.env.tx.caller; - let effective_gas_price = context.evm.env.effective_gas_price(); - - // return balance of not spend gas. - let (caller_account, _) = context - .evm - .journaled_state - .load_account(caller, &mut context.evm.db) - .map_err(EVMError::Database)?; - - caller_account.info.balance = caller_account - .info - .balance - .saturating_add(effective_gas_price * U256::from(gas.remaining() + gas.refunded() as u64)); - - Ok(()) -} - -/// Reward beneficiary with gas fee. -#[inline] -pub fn reward_beneficiary( - context: &mut Context, - gas: &Gas, -) -> Result<(), EVMError> { - let beneficiary = context.evm.env.block.coinbase; - let effective_gas_price = context.evm.env.effective_gas_price(); - - // transfer fee to coinbase/beneficiary. - // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded. - let coinbase_gas_price = if SPEC::enabled(LONDON) { - effective_gas_price.saturating_sub(context.evm.env.block.basefee) - } else { - effective_gas_price - }; - - let (coinbase_account, _) = context - .evm - .journaled_state - .load_account(beneficiary, &mut context.evm.db) - .map_err(EVMError::Database)?; - - coinbase_account.mark_touch(); - coinbase_account.info.balance = coinbase_account - .info - .balance - .saturating_add(coinbase_gas_price * U256::from(gas.spend() - gas.refunded() as u64)); - - Ok(()) -} - -#[cfg(test)] -mod tests { - use revm_interpreter::primitives::CancunSpec; - - use super::*; - - #[test] - fn test_consume_gas() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let gas = handle_call_return::(&env, InstructionResult::Stop, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_consume_gas_with_refund() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let mut return_gas = Gas::new(90); - return_gas.record_refund(30); - - let gas = handle_call_return::(&env, InstructionResult::Stop, return_gas); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 30); - - let gas = handle_call_return::(&env, InstructionResult::Revert, return_gas); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_revert_gas() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let gas = handle_call_return::(&env, InstructionResult::Revert, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } -} +pub use main::{ + deduct_caller_inner, main_deduct_caller, main_end, main_load, main_reimburse_caller, + main_return, main_reward_beneficiary, +}; +pub use validation::{validate_env, validate_initial_tx_gas, validate_tx_against_state}; diff --git a/crates/revm/src/handler/mainnet/frames.rs b/crates/revm/src/handler/mainnet/frame.rs similarity index 59% rename from crates/revm/src/handler/mainnet/frames.rs rename to crates/revm/src/handler/mainnet/frame.rs index 4a41a89bc7..9233aef3b7 100644 --- a/crates/revm/src/handler/mainnet/frames.rs +++ b/crates/revm/src/handler/mainnet/frame.rs @@ -1,14 +1,14 @@ use crate::{ db::Database, interpreter::{ - CallContext, CallInputs, CreateInputs, InterpreterResult, SharedMemory, Transfer, + return_ok, return_revert, CallContext, CallInputs, CallScheme, CreateInputs, Gas, + InstructionResult, InterpreterResult, SharedMemory, Transfer, }, - primitives::{Spec, TransactTo}, + primitives::{Env, Spec, TransactTo}, CallStackFrame, Context, FrameOrResult, }; use alloc::boxed::Box; use core::ops::Range; -use revm_interpreter::CallScheme; /// Creates first fmrae pub fn create_first_frame( @@ -51,6 +51,49 @@ pub fn create_first_frame( } } +/// Helper function called inside [`main_call_return`] +pub fn frame_return_with_refund_flag( + env: &Env, + call_result: InstructionResult, + returned_gas: Gas, + refund_enabled: bool, +) -> Gas { + // Spend the gas limit. Gas is reimbursed when the tx returns successfully. + let mut gas = Gas::new(env.tx.gas_limit); + gas.record_cost(env.tx.gas_limit); + + match call_result { + return_ok!() => { + gas.erase_cost(returned_gas.remaining()); + gas.record_refund(returned_gas.refunded()); + } + return_revert!() => { + gas.erase_cost(returned_gas.remaining()); + } + _ => {} + } + // Calculate gas refund for transaction. + // If config is set to disable gas refund, it will return 0. + // If spec is set to london, it will decrease the maximum refund amount to 5th part of + // gas spend. (Before london it was 2th part of gas spend) + if refund_enabled { + // EIP-3529: Reduction in refunds + gas.set_final_refund::() + }; + + gas +} + +/// Handle output of the transaction +#[inline] +pub fn main_frame_return( + env: &Env, + call_result: InstructionResult, + returned_gas: Gas, +) -> Gas { + frame_return_with_refund_flag::(env, call_result, returned_gas, true) +} + /// Handle frame return. pub fn handle_frame_return( context: &mut Context, @@ -130,3 +173,51 @@ pub fn handle_frame_sub_create( } } } + +#[cfg(test)] +mod tests { + use revm_interpreter::primitives::CancunSpec; + + use super::*; + + #[test] + fn test_consume_gas() { + let mut env = Env::default(); + env.tx.gas_limit = 100; + + let gas = main_frame_return::(&env, InstructionResult::Stop, Gas::new(90)); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 0); + } + + #[test] + fn test_consume_gas_with_refund() { + let mut env = Env::default(); + env.tx.gas_limit = 100; + + let mut return_gas = Gas::new(90); + return_gas.record_refund(30); + + let gas = main_frame_return::(&env, InstructionResult::Stop, return_gas); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 30); + + let gas = main_frame_return::(&env, InstructionResult::Revert, return_gas); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 0); + } + + #[test] + fn test_revert_gas() { + let mut env = Env::default(); + env.tx.gas_limit = 100; + + let gas = main_frame_return::(&env, InstructionResult::Revert, Gas::new(90)); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 0); + } +} diff --git a/crates/revm/src/handler/mainnet/host.rs b/crates/revm/src/handler/mainnet/host.rs deleted file mode 100644 index 684135f161..0000000000 --- a/crates/revm/src/handler/mainnet/host.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::{ - db::Database, - interpreter::SelfDestructResult, - primitives::{Address, Bytes, Log, Spec, B256}, - Context, -}; -use alloc::vec::Vec; - -/// Handle host log call. -pub fn handle_host_log( - context: &mut Context, - address: Address, - topics: Vec, - data: Bytes, -) { - let log = Log { - address, - topics, - data, - }; - context.evm.journaled_state.log(log); -} - -/// Handle host selfdestruct call. -pub fn handle_selfdestruct( - context: &mut Context, - address: Address, - target: Address, -) -> Option { - context - .evm - .journaled_state - .selfdestruct(address, target, &mut context.evm.db) - .map_err(|e| context.evm.error = Some(e)) - .ok() -} diff --git a/crates/revm/src/handler/mainnet/main.rs b/crates/revm/src/handler/mainnet/main.rs index c3f4d49100..c90fe7a15f 100644 --- a/crates/revm/src/handler/mainnet/main.rs +++ b/crates/revm/src/handler/mainnet/main.rs @@ -4,10 +4,46 @@ use crate::{ interpreter::{Gas, InstructionResult, SuccessOrHalt}, - primitives::{db::Database, EVMError, ExecutionResult, Output, ResultAndState}, + primitives::{ + db::Database, + Account, EVMError, Env, ExecutionResult, Output, ResultAndState, Spec, + SpecId::{CANCUN, LONDON, SHANGHAI}, + TransactTo, U256, + }, Context, }; +/// Main load handle +#[inline] +pub fn main_load( + context: &mut Context, +) -> Result<(), EVMError> { + // the L1-cost fee is only computed for Optimism non-deposit transactions. + #[cfg(feature = "optimism")] + if env.cfg.optimism && env.tx.optimism.source_hash.is_none() { + let l1_block_info = + optimism::L1BlockInfo::try_fetch(self.context.evm.db).map_err(EVMError::Database)?; + + // storage l1 block info for later use. + self.context.evm.l1_block_info = Some(l1_block_info); + + tx_l1_cost + } + + // load coinbase + // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm + if SPEC::enabled(SHANGHAI) { + context + .evm + .journaled_state + .initial_account_load(context.evm.env.block.coinbase, &[], &mut context.evm.db) + .map_err(EVMError::Database)?; + } + + context.evm.load_access_list()?; + Ok(()) +} + /// Main return handle, returns the output of the transaction. #[inline] pub fn main_return( @@ -56,9 +92,106 @@ pub fn main_return( /// Mainnet end handle does not change the output. #[inline] -pub fn end_handle( +pub fn main_end( _context: &mut Context, evm_output: Result>, ) -> Result> { evm_output } + +/// Reward beneficiary with gas fee. +#[inline] +pub fn main_reward_beneficiary( + context: &mut Context, + gas: &Gas, +) -> Result<(), EVMError> { + let beneficiary = context.evm.env.block.coinbase; + let effective_gas_price = context.evm.env.effective_gas_price(); + + // transfer fee to coinbase/beneficiary. + // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded. + let coinbase_gas_price = if SPEC::enabled(LONDON) { + effective_gas_price.saturating_sub(context.evm.env.block.basefee) + } else { + effective_gas_price + }; + + let (coinbase_account, _) = context + .evm + .journaled_state + .load_account(beneficiary, &mut context.evm.db) + .map_err(EVMError::Database)?; + + coinbase_account.mark_touch(); + coinbase_account.info.balance = coinbase_account + .info + .balance + .saturating_add(coinbase_gas_price * U256::from(gas.spend() - gas.refunded() as u64)); + + Ok(()) +} + +/// Helper function that deducts the caller balance. +pub fn deduct_caller_inner(caller_account: &mut Account, env: &Env) { + // Subtract gas costs from the caller's account. + // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. + let mut gas_cost = U256::from(env.tx.gas_limit).saturating_mul(env.effective_gas_price()); + + // EIP-4844 + if SPEC::enabled(CANCUN) { + let data_fee = env.calc_data_fee().expect("already checked"); + gas_cost = gas_cost.saturating_add(data_fee); + } + + // set new caller account balance. + caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); + + // bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`. + if matches!(env.tx.transact_to, TransactTo::Call(_)) { + // Nonce is already checked + caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); + } + + // touch account so we know it is changed. + caller_account.mark_touch(); +} + +#[inline] +pub fn main_deduct_caller( + context: &mut Context, +) -> Result<(), EVMError> { + // load caller's account. + let (caller_account, _) = context + .evm + .journaled_state + .load_account(context.evm.env.tx.caller, &mut context.evm.db) + .map_err(EVMError::Database)?; + + // deduct gas cost from caller's account. + deduct_caller_inner::(caller_account, &context.evm.env); + + Ok(()) +} + +#[inline] +pub fn main_reimburse_caller( + context: &mut Context, + gas: &Gas, +) -> Result<(), EVMError> { + let caller = context.evm.env.tx.caller; + let effective_gas_price = context.evm.env.effective_gas_price(); + + // return balance of not spend gas. + let (caller_account, _) = context + .evm + .journaled_state + .load_account(caller, &mut context.evm.db) + .map_err(EVMError::Database)?; + + caller_account.info.balance = caller_account + .info + .balance + .saturating_add(effective_gas_price * U256::from(gas.remaining() + gas.refunded() as u64)); + + Ok(()) +} diff --git a/crates/revm/src/handler/mainnet/preexecution.rs b/crates/revm/src/handler/mainnet/validation.rs similarity index 94% rename from crates/revm/src/handler/mainnet/preexecution.rs rename to crates/revm/src/handler/mainnet/validation.rs index 6ac70d9b7e..aef1d032c5 100644 --- a/crates/revm/src/handler/mainnet/preexecution.rs +++ b/crates/revm/src/handler/mainnet/validation.rs @@ -13,6 +13,7 @@ pub fn validate_env(env: &Env) -> Result<(), EVMError< Ok(()) } +/// Validates transaction against the state. pub fn validate_tx_against_state( context: &mut Context, ) -> Result<(), EVMError> { @@ -33,6 +34,7 @@ pub fn validate_tx_against_state( Ok(()) } +/// Validate initial transaction gas. pub fn validate_initial_tx_gas( env: &Env, ) -> Result> { diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index 8caeb57afd..fbdfc1851a 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -1,20 +1,9 @@ -use crate::{ - db::Database, - handler::Handler, - inspector_instruction, - interpreter::{opcode::InstructionTables, InterpreterResult}, - CallStackFrame, Evm, FrameOrResult, Inspector, JournalEntry, -}; -use alloc::sync::Arc; -use revm_interpreter::{opcode, Interpreter}; - -pub trait GetInspector<'a, DB: Database> { - fn get_inspector(&mut self) -> &mut dyn Inspector; -} +use crate::{db::Database, handler::Handler, interpreter::opcode::InstructionTables, Evm}; /// EVM Handler pub type EvmHandler<'a, EXT, DB> = Handler<'a, Evm<'a, EXT, DB>, EXT, DB>; +/// EVM Instruction Tables pub type EvmInstructionTables<'a, EXT, DB> = InstructionTables<'a, Evm<'a, EXT, DB>>; // Handle register @@ -24,11 +13,14 @@ pub type HandleRegister<'a, EXT, DB> = fn(&mut EvmHandler<'a, EXT, DB>); pub type HandleRegisterBox<'a, EXT, DB> = Box)>; pub enum HandleRegisters<'a, EXT, DB: Database> { + /// Plain function register Plain(HandleRegister<'a, EXT, DB>), + /// Boxed function register. Box(HandleRegisterBox<'a, EXT, DB>), } impl<'a, EXT, DB: Database> HandleRegisters<'a, EXT, DB> { + /// Call register function to modify EvmHandler. pub fn register(&self, handler: &mut EvmHandler<'a, EXT, DB>) { match self { HandleRegisters::Plain(f) => f(handler), @@ -36,169 +28,3 @@ impl<'a, EXT, DB: Database> HandleRegisters<'a, EXT, DB> { } } } - -/// Register Inspector handles that interact with Inspector instance. -pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( - handler: &mut EvmHandler<'a, EXT, DB>, -) { - let spec_id = handler.spec_id; - // Every instruction inside flat table that is going to be wrapped by inspector calls. - let table = handler - .instruction_table - .take() - .expect("Handler must have instruction table"); - let mut table = match table { - EvmInstructionTables::Plain(table) => table - .into_iter() - .map(|i| inspector_instruction(i)) - .collect::>(), - EvmInstructionTables::Boxed(table) => table - .into_iter() - .map(|i| inspector_instruction(i)) - .collect::>(), - }; - - // Register inspector Log instruction. - let mut inspect_log = |index: u8| { - table.get_mut(index as usize).map(|i| { - Box::new( - |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { - let old_log_len = host.context.evm.journaled_state.logs.len(); - i(interpreter, host); - // check if log was added. It is possible that revert happened - // cause of gas or stack underflow. - if host.context.evm.journaled_state.logs.len() == old_log_len + 1 { - // clone log. - // TODO decide if we should remove this and leave the comment - // that log can be found as journaled_state. - let last_log = host - .context - .evm - .journaled_state - .logs - .last() - .unwrap() - .clone(); - // call Inspector - host.context - .external - .get_inspector() - .log(&mut host.context.evm, &last_log); - } - }, - ) - }); - }; - - inspect_log(opcode::LOG0); - inspect_log(opcode::LOG1); - inspect_log(opcode::LOG2); - inspect_log(opcode::LOG3); - inspect_log(opcode::LOG4); - - // register selfdestruct function. - table.get_mut(opcode::SELFDESTRUCT as usize).map(|i| { - Box::new( - |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { - // execute selfdestruct - i(interpreter, host); - // check if selfdestruct was successful and if journal entry is made. - if let Some(JournalEntry::AccountDestroyed { - address, - target, - had_balance, - .. - }) = host - .context - .evm - .journaled_state - .journal - .last() - .unwrap() - .last() - { - host.context.external.get_inspector().selfdestruct( - *address, - *target, - *had_balance, - ); - } - }, - ) - }); - - handler.instruction_table = Some(EvmInstructionTables::Boxed( - table.try_into().unwrap_or_else(|_| unreachable!()), - )); - - // handle sub create - handler.frame.frame_sub_create = Arc::new( - move |context, frame, mut inputs| -> Option> { - let inspector = context.external.get_inspector(); - if let Some((result, address)) = inspector.create(&mut context.evm, &mut inputs) { - frame.interpreter.insert_create_output(result, address); - return None; - } - - match context.evm.make_create_frame(spec_id, &inputs) { - FrameOrResult::Frame(mut new_frame) => { - inspector.initialize_interp(&mut new_frame.interpreter, &mut context.evm); - Some(new_frame) - } - FrameOrResult::Result(result) => { - let (result, address) = - inspector.create_end(&mut context.evm, result, frame.created_address); - // insert result of the failed creation of create CallStackFrame. - frame.interpreter.insert_create_output(result, address); - None - } - } - }, - ); - - // handle sub call - handler.frame.frame_sub_call = Arc::new( - move |context, mut inputs, frame, memory, return_memory_offset| -> Option> { - // inspector handle - let inspector = context.external.get_inspector(); - if let Some((result, range)) = inspector.call(&mut context.evm, &mut inputs) { - frame.interpreter.insert_call_output(memory, result, range); - return None; - } - match context - .evm - .make_call_frame(&inputs, return_memory_offset.clone()) - { - FrameOrResult::Frame(mut new_frame) => { - inspector.initialize_interp(&mut new_frame.interpreter, &mut context.evm); - Some(new_frame) - } - FrameOrResult::Result(result) => { - // inspector handle - let result = inspector.call_end(&mut context.evm, result); - frame - .interpreter - .insert_call_output(memory, result, return_memory_offset); - None - } - } - }, - ); - - // return frame handle - let old_handle = handler.frame.frame_return.clone(); - handler.frame.frame_return = Arc::new( - move |context, mut child, parent, memory, mut result| -> Option { - let inspector = &mut context.external.get_inspector(); - result = if child.is_create { - let (result, address) = - inspector.create_end(&mut context.evm, result, child.created_address); - child.created_address = address; - result - } else { - inspector.call_end(&mut context.evm, result) - }; - old_handle(context, child, parent, memory, result) - }, - ); -} diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index 18fa54b44b..60ce286ce6 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -12,10 +12,12 @@ mod customprinter; #[cfg(all(feature = "std", feature = "serde"))] mod eip3155; mod gas; -mod instruction; +mod handler_register; mod noop; -pub use instruction::inspector_instruction; +// Exports. + +pub use handler_register::{inspector_handle_register, inspector_instruction, GetInspector}; use revm_interpreter::InterpreterResult; /// [Inspector] implementations. pub mod inspectors { diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 2f98fe0a5b..8d8dc71eb3 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -66,15 +66,14 @@ impl Inspector for GasInspector { #[cfg(test)] mod tests { - use core::ops::Range; - use crate::{ - handler::register::{inspector_handle_register, GetInspector}, + inspector::{inspector_handle_register, GetInspector}, inspectors::GasInspector, interpreter::{CallInputs, CreateInputs, Interpreter, InterpreterResult}, primitives::{Address, Log}, Database, EvmContext, Inspector, }; + use core::ops::Range; #[derive(Default, Debug)] struct StackInspector { @@ -173,7 +172,7 @@ mod tests { let mut evm: Evm<'_, StackInspector, BenchmarkDB> = Evm::builder() .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) - .with_external(StackInspector::default()) + .with_external_context(StackInspector::default()) .modify_tx_env(|tx| { tx.clear(); tx.caller = address!("1000000000000000000000000000000000000000"); @@ -181,7 +180,7 @@ mod tests { TransactTo::Call(address!("0000000000000000000000000000000000000000")); tx.gas_limit = 21100; }) - .append_handler(inspector_handle_register) + .append_handler_register(inspector_handle_register) .build(); // run evm. diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs new file mode 100644 index 0000000000..a562e11162 --- /dev/null +++ b/crates/revm/src/inspector/handler_register.rs @@ -0,0 +1,234 @@ +use crate::{ + db::Database, + handler::register::{EvmHandler, EvmInstructionTables}, + interpreter::{opcode, Interpreter, InterpreterResult}, + CallStackFrame, Evm, FrameOrResult, Inspector, JournalEntry, +}; +use alloc::sync::Arc; +use revm_interpreter::{opcode::BoxedInstruction, InstructionResult}; + +pub trait GetInspector<'a, DB: Database> { + fn get_inspector(&mut self) -> &mut dyn Inspector; +} + +/// Register Inspector handles that interact with Inspector instance. +pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( + handler: &mut EvmHandler<'a, EXT, DB>, +) { + let spec_id = handler.spec_id; + // Every instruction inside flat table that is going to be wrapped by inspector calls. + let table = handler + .instruction_table + .take() + .expect("Handler must have instruction table"); + let mut table = match table { + EvmInstructionTables::Plain(table) => table + .into_iter() + .map(|i| inspector_instruction(i)) + .collect::>(), + EvmInstructionTables::Boxed(table) => table + .into_iter() + .map(|i| inspector_instruction(i)) + .collect::>(), + }; + + // Register inspector Log instruction. + let mut inspect_log = |index: u8| { + table.get_mut(index as usize).map(|i| { + Box::new( + |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { + let old_log_len = host.context.evm.journaled_state.logs.len(); + i(interpreter, host); + // check if log was added. It is possible that revert happened + // cause of gas or stack underflow. + if host.context.evm.journaled_state.logs.len() == old_log_len + 1 { + // clone log. + // TODO decide if we should remove this and leave the comment + // that log can be found as journaled_state. + let last_log = host + .context + .evm + .journaled_state + .logs + .last() + .unwrap() + .clone(); + // call Inspector + host.context + .external + .get_inspector() + .log(&mut host.context.evm, &last_log); + } + }, + ) + }); + }; + + inspect_log(opcode::LOG0); + inspect_log(opcode::LOG1); + inspect_log(opcode::LOG2); + inspect_log(opcode::LOG3); + inspect_log(opcode::LOG4); + + // register selfdestruct function. + table.get_mut(opcode::SELFDESTRUCT as usize).map(|i| { + Box::new( + |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { + // execute selfdestruct + i(interpreter, host); + // check if selfdestruct was successful and if journal entry is made. + if let Some(JournalEntry::AccountDestroyed { + address, + target, + had_balance, + .. + }) = host + .context + .evm + .journaled_state + .journal + .last() + .unwrap() + .last() + { + host.context.external.get_inspector().selfdestruct( + *address, + *target, + *had_balance, + ); + } + }, + ) + }); + + handler.instruction_table = Some(EvmInstructionTables::Boxed( + table.try_into().unwrap_or_else(|_| unreachable!()), + )); + + // handle sub create + handler.frame.sub_create = Arc::new( + move |context, frame, mut inputs| -> Option> { + let inspector = context.external.get_inspector(); + if let Some((result, address)) = inspector.create(&mut context.evm, &mut inputs) { + frame.interpreter.insert_create_output(result, address); + return None; + } + + match context.evm.make_create_frame(spec_id, &inputs) { + FrameOrResult::Frame(mut new_frame) => { + inspector.initialize_interp(&mut new_frame.interpreter, &mut context.evm); + Some(new_frame) + } + FrameOrResult::Result(result) => { + let (result, address) = + inspector.create_end(&mut context.evm, result, frame.created_address); + // insert result of the failed creation of create CallStackFrame. + frame.interpreter.insert_create_output(result, address); + None + } + } + }, + ); + + // handle sub call + handler.frame.sub_call = Arc::new( + move |context, mut inputs, frame, memory, return_memory_offset| -> Option> { + // inspector handle + let inspector = context.external.get_inspector(); + if let Some((result, range)) = inspector.call(&mut context.evm, &mut inputs) { + frame.interpreter.insert_call_output(memory, result, range); + return None; + } + match context + .evm + .make_call_frame(&inputs, return_memory_offset.clone()) + { + FrameOrResult::Frame(mut new_frame) => { + inspector.initialize_interp(&mut new_frame.interpreter, &mut context.evm); + Some(new_frame) + } + FrameOrResult::Result(result) => { + // inspector handle + let result = inspector.call_end(&mut context.evm, result); + frame + .interpreter + .insert_call_output(memory, result, return_memory_offset); + None + } + } + }, + ); + + // return frame handle + let old_handle = handler.frame.frame_return.clone(); + handler.frame.frame_return = Arc::new( + move |context, mut child, parent, memory, mut result| -> Option { + let inspector = &mut context.external.get_inspector(); + result = if child.is_create { + let (result, address) = + inspector.create_end(&mut context.evm, result, child.created_address); + child.created_address = address; + result + } else { + inspector.call_end(&mut context.evm, result) + }; + old_handle(context, child, parent, memory, result) + }, + ); +} + +/// Outer closure that calls Inspector for every instruction. +pub fn inspector_instruction< + 'a, + INSP: GetInspector<'a, DB>, + DB: Database, + Instruction: Fn(&mut Interpreter, &mut Evm<'a, INSP, DB>) + 'a, +>( + instruction: Instruction, +) -> BoxedInstruction<'a, Evm<'a, INSP, DB>> { + Box::new( + move |interpreter: &mut Interpreter, host: &mut Evm<'a, INSP, DB>| { + // SAFETY: as the PC was already incremented we need to subtract 1 to preserve the + // old Inspector behavior. + interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.sub(1) }; + + host.context + .external + .get_inspector() + .step(interpreter, &mut host.context.evm); + if interpreter.instruction_result != InstructionResult::Continue { + return; + } + + // return PC to old value + interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.add(1) }; + + // execute instruction. + instruction(interpreter, host); + + host.context + .external + .get_inspector() + .step_end(interpreter, &mut host.context.evm); + }, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::inspectors::NoOpInspector; + use crate::{db::EmptyDB, interpreter::opcode::*, primitives::BerlinSpec, Evm}; + + #[test] + fn test_make_boxed_instruction_table() { + // test that this pattern builds. + let inst: InstructionTable> = + make_instruction_table::, BerlinSpec>(); + let _test: BoxedInstructionTable<'_, Evm<'_, _, _>> = + make_boxed_instruction_table::<'_, Evm<'_, NoOpInspector, EmptyDB>, BerlinSpec, _>( + inst, + inspector_instruction, + ); + } +} diff --git a/crates/revm/src/inspector/instruction.rs b/crates/revm/src/inspector/instruction.rs deleted file mode 100644 index 0db373e470..0000000000 --- a/crates/revm/src/inspector/instruction.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::{handler::register::GetInspector, Evm}; -use revm_interpreter::{ - opcode::BoxedInstruction, primitives::db::Database, InstructionResult, Interpreter, -}; - -/// Outer closure that calls Inspector for every instruction. -pub fn inspector_instruction< - 'a, - INSP: GetInspector<'a, DB>, - DB: Database, - Instruction: Fn(&mut Interpreter, &mut Evm<'a, INSP, DB>) + 'a, ->( - instruction: Instruction, -) -> BoxedInstruction<'a, Evm<'a, INSP, DB>> { - Box::new( - move |interpreter: &mut Interpreter, host: &mut Evm<'a, INSP, DB>| { - // SAFETY: as the PC was already incremented we need to subtract 1 to preserve the - // old Inspector behavior. - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.sub(1) }; - - host.context - .external - .get_inspector() - .step(interpreter, &mut host.context.evm); - if interpreter.instruction_result != InstructionResult::Continue { - return; - } - - // return PC to old value - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.add(1) }; - - // execute instruction. - instruction(interpreter, host); - - host.context - .external - .get_inspector() - .step_end(interpreter, &mut host.context.evm); - }, - ) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::inspectors::NoOpInspector; - use crate::{db::EmptyDB, interpreter::opcode::*, primitives::BerlinSpec, Evm}; - - #[test] - fn test_make_boxed_instruction_table() { - // test that this pattern builds. - let inst: InstructionTable> = - make_instruction_table::, BerlinSpec>(); - let _test: BoxedInstructionTable<'_, Evm<'_, _, _>> = - make_boxed_instruction_table::<'_, Evm<'_, NoOpInspector, EmptyDB>, BerlinSpec, _>( - inst, - inspector_instruction, - ); - } -} diff --git a/crates/revm/src/inspector/noop.rs b/crates/revm/src/inspector/noop.rs index e436df9092..70d32b133b 100644 --- a/crates/revm/src/inspector/noop.rs +++ b/crates/revm/src/inspector/noop.rs @@ -1,5 +1,5 @@ -use crate::{handler::register::GetInspector, Database, Inspector}; - +use super::GetInspector; +use crate::{Database, Inspector}; /// Dummy [Inspector], helpful as standalone replacement. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct NoOpInspector; diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 80028451b6..bf5dfb3d9b 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -35,7 +35,9 @@ pub use db::{Database, DatabaseCommit, DatabaseRef, DummyStateDB, InMemoryDB}; pub use evm::{Evm, CALL_STACK_LIMIT}; pub use frame::{CallStackFrame, FrameOrResult}; pub use handler::Handler; -pub use inspector::{inspector_instruction, inspectors, Inspector}; +pub use inspector::{ + inspector_handle_register, inspector_instruction, inspectors, GetInspector, Inspector, +}; pub use journaled_state::{JournalCheckpoint, JournalEntry, JournaledState}; // export Optimism types, helpers, and constants #[cfg(feature = "optimism")] diff --git a/crates/revm/src/handler/optimism.rs b/crates/revm/src/optimism/handler_register.rs similarity index 93% rename from crates/revm/src/handler/optimism.rs rename to crates/revm/src/optimism/handler_register.rs index 1af36f1499..504e4e7a7a 100644 --- a/crates/revm/src/handler/optimism.rs +++ b/crates/revm/src/optimism/handler_register.rs @@ -1,8 +1,7 @@ //! Handler related to Optimism chain -use super::mainnet; use crate::{ - handler::RegisterHandler, + handler::{mainnet, RegisterHandler}, interpreter::{return_ok, return_revert, Gas, InstructionResult}, optimism, primitives::{ @@ -14,26 +13,15 @@ use crate::{ use alloc::sync::Arc; use core::ops::Mul; -pub struct OptimismHandle {} - -impl<'a, DB: Database> RegisterHandler<'a, DB, OptimismHandle> for OptimismHandle { - fn register_handler( - &self, - mut handler: Handler<'a, Evm<'a, OptimismHandle, DB>, (), DB>, - ) -> Handler<'a, Evm<'a, (), DB>, (), DB> - where - DB: 'a, - (): Sized, - { - handler.call_return = Arc::new(handle_call_return::); - handler.calculate_gas_refund = Arc::new(calculate_gas_refund::); - // we reinburse caller the same was as in mainnet. - // Refund is calculated differently then mainnet. - handler.reward_beneficiary = Arc::new(reward_beneficiary::); - // In case of halt of deposit transaction return Error. - handler.main_return = Arc::new(main_return::); - handler.end = Arc::new(end_handle::); - } +pub fn optimism_handle_register<'a, DB: Database, EXT>(handler: &mut EvmHandler<'a, EXT, DB>) { + handler.call_return = Arc::new(handle_call_return::); + handler.calculate_gas_refund = Arc::new(calculate_gas_refund::); + // we reinburse caller the same was as in mainnet. + // Refund is calculated differently then mainnet. + handler.reward_beneficiary = Arc::new(reward_beneficiary::); + // In case of halt of deposit transaction return Error. + handler.main_return = Arc::new(main_return::); + handler.end = Arc::new(end_handle::); } /// Handle output of the transaction diff --git a/documentation/src/crates/revm/builder.md b/documentation/src/crates/revm/builder.md index d6ba47e66a..74aef5a7ee 100644 --- a/documentation/src/crates/revm/builder.md +++ b/documentation/src/crates/revm/builder.md @@ -3,20 +3,16 @@ It creates the EVM and applies different handler, and allows setting external context and custom logic. -`Evm` inside revm consist of the few parts `Context` and `Handler`. `Context` is additionally split between `EvmContext` and `External` context. Read here for more information [`Evm`](./evm.md) internals. +`Evm` inside revm consist of the few parts `Context` and `Handler`. `Context` is additionally split between `EvmContext` and `External` context. Read here for more information on [`Evm`](./evm.md) internals. -Handler is.. it is not generic but has a specification identification variable. It contains list of function that are wrapped around `Arc`. Functions (aka handles) are grouped by functionality on: -* preverification functions. Are related to the preverification of set Environment data. -* main function: Deducs caller balance, loads warm accounts/storages, and handler beneficiary rewards. -* Frame function: Handles call and creates and sub calls. -* Instruction functions: Is instruction table that executes opcodes. - -Builder ties dependencies between generic Database, External context and Spec and allows overriding handlers. As there is a dependency between them setting Database will reset External and Handle field while setting External field would reset Handler. Note that Database will never be reset. +Builder ties dependencies between generic `Database`, `External` context and `Spec` and allows handle registers to be added. As there is a generic dependency between them setting `Database` will reset `External` context and `Handler` field while setting `External` field would reset just a `Handler` as it will become invalid. Note that Database will never be reset. Simple example of using `EvmBuilder` is ``` +Evm::build().with_empty_db().without_external_context() +``` + +`EvmBuilder` has three stages that will gradually unlock more functionality, first is setting the database, second is setting external context and third is setting/registering the handler registers. Environment can be modified in anytime with `modify_*`' functions that take a closure. -Evm::build().with_empty_db().with_empty_external() -``` \ No newline at end of file diff --git a/documentation/src/crates/revm/evm.md b/documentation/src/crates/revm/evm.md index 86da7c753b..49fc0f933d 100644 --- a/documentation/src/crates/revm/evm.md +++ b/documentation/src/crates/revm/evm.md @@ -2,79 +2,37 @@ `Evm` is the primary structure that implements the Ethereum Virtual Machine (EVM), a stack-based virtual machine that executes Ethereum smart contracts. -## Structure +## What is inside It is consisting of two main parts the `Context` and the `Handler`. `Context` represent the state that is needed for execution and `Handler` contains list of functions that act as a logic. -`Context` is additionally split between `EvmContext` and `External` context. `EvmContext` is internal and contains `Database`, `Environment`, `JournaledState` and `Precompiles`. And `External` context is fully generic without any trait restrains and its purpose is to allow custom handlers to save state in runtime or allows hooks to be added (For example external contexts can be a Inspector), more on its usage can be seen in [`EvmBuilder`](./builder.md) +`Context` is additionally split between `EvmContext` and `External` context. `EvmContext` is internal and contains `Database`, `Environment`, `JournaledState` and `Precompiles`. And `External` context is fully generic without any trait restrains and its purpose is to allow custom handlers to save state in runtime or allows hooks to be added (For example external contexts can be a Inspector), more on its usage can be seen in [`EvmBuilder`](./builder.md). `Evm` implements the [`Host`](./../interpreter/host.md) trait, which defines an interface for the interaction of the EVM Interpreter with its environment (or "host"), encompassing essential operations such as account and storage access, creating logs, and invoking sub calls and selfdestruct. -Data structures of block and transaction can be found inside `Environment` +Data structures of block and transaction can be found inside `Environment`. And more information on journaled state can be found in [`JournaledState`](../revm_journaled_state/journaled_state.md) documentation. ## Runtime -Runtime consist of two parts first is verification and second is execution. +Runtime consist of list of functions from `Handler` that are called in predefined order. They are grouped by functionality on `Verification`, `Main`, `Frame` and `Instruction` functions. Verification function are related to the preverification of set `Environment` data. Main function is the main logic of the transaction execution. Frame function handles call and creates and sub calls. And Instruction functions are instruction table that executes opcodes. -Handlers logic that does verification are: - -* validate_env - - That verifies if all data is set in `Environment` and if they are valid, for example if `gas_limit` is smaller than block `gas_limit`. - -* validate_initial_tx_gas - - It calculated initial gas needed for transaction to be executed and checks if it is less them the transaction gas_limit. Note that this does not touch the `Database` or state - -* validate_tx_against_state - - It loads the caller account and checks those information. Among them the nonce, if there is enough balance to pay for max gas spent and balance transferred. - -Logic when running transaction consist of few stages that are implemented as a handle calls: -* main_load - - Loads access list and beneficiary from `Database`. Cold load is done here. - -* deduct_caller: - - Deducts values from the caller to the maximum amount of gas that can be spent on the transaction. This loads the caller account from the `Database`. - -* create_first_frame and start_the_loop - - These two handles main call loop that creates and handles stack of frames. It is responsible for handling subcalls and its return outputs and call Interpreter loop to execute bytecode instructions. - -* call_return - - Handler that allows processing of the returned output from the call. It calculated refunded gas and final spent gas. - -* reimburse_caller - - Reimburse the caller with gas that was not spent during the execution of the transaction. - Or balance of gas that needs to be refunded. - -* reward_beneficiary - - At the end of every transaction beneficiary needs to be rewarded with the fee. - -* main_return - - It returns the changes state and the result of the execution. - -`Evm` in runtime runs **two** loops.First loop is call loop that everything starts with, it creates call frames, handles subcalls and its return outputs and call Interpreter loop to execute bytecode instructions. Second loop is Interpreter loop that loops over bytecode opcodes and executes instruction. +`Evm` execution runs **two** loops. First loop is call loop that everything starts with, it creates call frames, handles subcalls and its return outputs and call Interpreter loop to execute bytecode instructions it is handled by `FrameHandler`. Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction from `InstructionTable`. First loop, the call loop, implements stack of `Frames` and it is responsible for handling sub calls and its return outputs. At the start Evm creates first `Frame` that contains `Interpreter` and starts the loop. `Interpreter` returns the `InterpreterAction` and action can be a `Return` of a call this means this interpreter finished its run or `SubCall`/`SubCreate` that means that new `Frame` needs to be created and pushed to the stack. When `Interpreter` returns `Return` action `Frame` is popped from the stack and its return value is pushed to the parent `Frame` stack. When `Interpreter` returns `SubCall`/`SubCreate` action new `Frame` is created and pushed to the stack and the loop continues. When the stack is empty the loop finishes. Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction. It is called from the call loop and it is responsible for executing bytecode instructions. It is implemented in [`Interpreter`](../revm_interpreter/interpreter.md) crate. +To dive deeper into the `Evm` logic check [`Handler`](../handler.md) documentation. -# Functions +# Functionalities `Evm` is build with a `EvmBuilder` that allows setting of `Database`, `External` context and `Handler`. Builder is created with `Evm::builder()` function. For more information on building check [`EvmBuilder`](./builder.md) documentation. After building `Evm` it can be used to execute transactions. There are three functions that can be used to execute transactions: -* preverify -* transaction preverified -* transact +* preverify - that only preverifies transaction information. +* transact preverified - is next step after preverification that executes transaction. +* transact - it calls both preverifies and it executes transaction. -If we want to modify `Evm` for example change the specification we can use `.modify()` function that would give us the `EvmBuilder` back and we can set new specification and build new `Evm` from it, as setting new specification would need to reset the `Handler` functions. +If we want to modify `Evm` for example change the specification ID we can use `.modify()` function that would give us the `EvmBuilder` and we can set new specification that would build new `Evm` from it, as setting new specification would need to reset the `Handler` functions. +More on the builder can be found in [`EvmBuilder`](./builder.md) documentation. \ No newline at end of file diff --git a/documentation/src/crates/revm/evm_old.md b/documentation/src/crates/revm/evm_old.md deleted file mode 100644 index 0a0fad90b4..0000000000 --- a/documentation/src/crates/revm/evm_old.md +++ /dev/null @@ -1,9 +0,0 @@ -# EVM Module Documentation - -This document provides the documentation for the `EVM` module. - -## The `EVM` - -The primary struct in this module is `EVM`. The EVM struct is generic over a type DB. This means that when you use the EVM struct, you can specify the type that DB should represent. It adds flexibility to the struct, allowing it to store different types of databases or data structures in the db field depending on the use case. The `EVM` struct enables `transact` to update the state directly to the database. Additionally, it allows the user to set all environment parameters. - -The parameters that can be set are divided between `Config`, `Block`, and `Transaction` (tx). For transacting on the EVM, you can call `transact_commit` that will automatically apply changes to the database. diff --git a/documentation/src/crates/revm/handler.md b/documentation/src/crates/revm/handler.md index e69de29bb2..1c025969b6 100644 --- a/documentation/src/crates/revm/handler.md +++ b/documentation/src/crates/revm/handler.md @@ -0,0 +1,76 @@ +# Handler + +Is logic part of the Evm, it contains the Specification ID, and list of functions that do the logic and list of registers that can change the behaviour of the Handler. + +Functions can be grouped in four categories and are marked in that way in the code: +* Validation functions: `ValidateHandler` +* Main functions: `MainHandler` +* Frame functions: `FrameHandler` +* Internal functions: `InstractionTable` + +### ValidationHandler + +Consist of functions that are used to validate transaction and block data. They are called before the execution of the transaction and they are used to check if the data (`Environment`) is valid. They are called in the following order: + +* validate_env + + That verifies if all data is set in `Environment` and if they are valid, for example if `gas_limit` is smaller than block `gas_limit`. + +* validate_initial_tx_gas + + It calculated initial gas needed for transaction to be executed and checks if it is less them the transaction gas_limit. Note that this does not touch the `Database` or state + +* validate_tx_against_state + + It loads the caller account and checks those information. Among them the nonce, if there is enough balance to pay for max gas spent and balance transferred. + +### MainHandler + +Consist of functions that are used to execute transaction. They are called in the following order: + +Logic when running transaction consist of few stages that are implemented as a handle calls: +* main_load + + Loads access list and beneficiary from `Database`. Cold load is done here. + +* deduct_caller: + + Deducts values from the caller to the maximum amount of gas that can be spent on the transaction. This loads the caller account from the `Database`. + +* create_first_frame and start_the_loop + + These two handles main call loop that creates and handles stack of frames. It is responsible for handling subcalls and its return outputs and call Interpreter loop to execute bytecode instructions. + +* call_return + + Handler that allows processing of the returned output from the call. It calculated refunded gas and final spent gas. + +* reimburse_caller + + Reimburse the caller with gas that was not spent during the execution of the transaction. + Or balance of gas that needs to be refunded. + +* reward_beneficiary + + At the end of every transaction beneficiary needs to be rewarded with the fee. + +* main_return + + It returns the changes state and the result of the execution. + + +### FrameHandler + +Consist of the function that handles the call stack and the first loop. They are called in the following order: + +TODO + +### InstructionTable + +Is a list of 256 function pointers that are used to execute instructions. They have two types, first is simple function that is faster and second is BoxedInstraction that has a small performance penalty but allows to capture the data. Look at the Interpreter documentation for more information. + +### Handle Registers + +Simple function that is used to modify handler functions. Amazing thing about them is that they can be done over generic external type. For example this allows to have a register over trait that would allow to add hooks to the any type that implements the trait, that trait can be a GetInspector trait so anyone that implement it would be able to register inspector related functions. It is used inside the `EvmBuilder` to change behaviour of the default mainnet Handler. + +Order of the registers is important as they are called in the order they are registered. And it matters if register overrides the previous handle or just wraps it, overriding handle can disrupt the logic of previous registered handles. \ No newline at end of file From 5dc55bd1ae93630b6ba74431dec6a3b3640ed183 Mon Sep 17 00:00:00 2001 From: rakita Date: Sat, 9 Dec 2023 02:08:24 +0100 Subject: [PATCH 23/52] more docs on registers --- documentation/src/crates/revm/handler.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/documentation/src/crates/revm/handler.md b/documentation/src/crates/revm/handler.md index 1c025969b6..0fbb2ae738 100644 --- a/documentation/src/crates/revm/handler.md +++ b/documentation/src/crates/revm/handler.md @@ -8,6 +8,14 @@ Functions can be grouped in four categories and are marked in that way in the co * Frame functions: `FrameHandler` * Internal functions: `InstractionTable` +### Handle Registers + +Simple function that is used to modify handler functions. Amazing thing about them is that they can be done over generic external type. For example this allows to have a register over trait that would allow to add hooks to the any type that implements the trait, that trait can be a GetInspector trait so anyone that implement it would be able to register inspector related functions. It is used inside the `EvmBuilder` to change behaviour of the default mainnet Handler. + +Order of the registers is important as they are called in the order they are registered. And it matters if register overrides the previous handle or just wraps it, overriding handle can disrupt the logic of previous registered handles. + +Registers are very powerful as they allow modification of any part of the Evm and with additional of the `External` context it becomes a powerful combo. Simple example would be to register new precompiles for the Evm. + ### ValidationHandler Consist of functions that are used to validate transaction and block data. They are called before the execution of the transaction and they are used to check if the data (`Environment`) is valid. They are called in the following order: @@ -67,10 +75,4 @@ TODO ### InstructionTable -Is a list of 256 function pointers that are used to execute instructions. They have two types, first is simple function that is faster and second is BoxedInstraction that has a small performance penalty but allows to capture the data. Look at the Interpreter documentation for more information. - -### Handle Registers - -Simple function that is used to modify handler functions. Amazing thing about them is that they can be done over generic external type. For example this allows to have a register over trait that would allow to add hooks to the any type that implements the trait, that trait can be a GetInspector trait so anyone that implement it would be able to register inspector related functions. It is used inside the `EvmBuilder` to change behaviour of the default mainnet Handler. - -Order of the registers is important as they are called in the order they are registered. And it matters if register overrides the previous handle or just wraps it, overriding handle can disrupt the logic of previous registered handles. \ No newline at end of file +Is a list of 256 function pointers that are used to execute instructions. They have two types, first is simple function that is faster and second is BoxedInstraction that has a small performance penalty but allows to capture the data. Look at the Interpreter documentation for more information. \ No newline at end of file From 238ea1b2ef57b512a9460900c9c994f80bb53225 Mon Sep 17 00:00:00 2001 From: rakita Date: Fri, 15 Dec 2023 12:52:37 +0100 Subject: [PATCH 24/52] integrate builder, fmt, move optimism l1block --- Cargo.lock | 16 ++ Cargo.toml | 2 +- bins/revm-test/src/bin/analysis.rs | 40 ++-- bins/revm-test/src/bin/snailtracer.rs | 65 ++--- bins/revm-test/src/bin/transfer.rs | 42 ++-- crates/interpreter/src/interpreter.rs | 40 ++-- crates/precompile/src/lib.rs | 2 +- crates/revm/benches/bench.rs | 225 +++++++++--------- crates/revm/src/builder.rs | 58 +++-- crates/revm/src/context.rs | 11 + crates/revm/src/evm.rs | 35 +-- crates/revm/src/frame.rs | 1 + crates/revm/src/handler.rs | 6 +- crates/revm/src/handler/handle_types/frame.rs | 2 +- crates/revm/src/handler/register.rs | 1 + crates/revm/src/inspector/customprinter.rs | 73 +++--- crates/revm/src/inspector/handler_register.rs | 2 +- crates/revm/src/optimism.rs | 176 +------------- crates/revm/src/optimism/l1block.rs | 173 ++++++++++++++ 19 files changed, 514 insertions(+), 456 deletions(-) create mode 100644 crates/revm/src/optimism/l1block.rs diff --git a/Cargo.lock b/Cargo.lock index a3d6669556..3a40e0f541 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1624,6 +1624,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "microbench" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4c44e40aee4e6fd2f4257bb91e5948ce79285aeb949129448889cf2fbf6da0b" + [[package]] name = "mime" version = "0.3.17" @@ -2300,6 +2306,16 @@ dependencies = [ "serde", ] +[[package]] +name = "revm-test" +version = "0.1.0" +dependencies = [ + "bytes", + "hex", + "microbench", + "revm", +] + [[package]] name = "rfc6979" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 39ee555436..20678f00a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] members = [ #"bins/revme", - #"bins/revm-test", + "bins/revm-test", "crates/revm", "crates/primitives", "crates/interpreter", diff --git a/bins/revm-test/src/bin/analysis.rs b/bins/revm-test/src/bin/analysis.rs index a7a81af9ce..b29eba7965 100644 --- a/bins/revm-test/src/bin/analysis.rs +++ b/bins/revm-test/src/bin/analysis.rs @@ -3,33 +3,29 @@ use std::time::Instant; use revm::{ db::BenchmarkDB, interpreter::analysis::to_analysed, - primitives::{Bytecode, Bytes, TransactTo}, + primitives::{address, bytes, Bytecode, Bytes, TransactTo}, + Evm, }; extern crate alloc; fn main() { let contract_data : Bytes = hex::decode( "6060604052341561000f57600080fd5b604051610dd1380380610dd18339810160405280805190602001909190805182019190602001805190602001909190805182019190505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100a79291906100e3565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100d99291906100e3565b5050505050610188565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012457805160ff1916838001178555610152565b82800160010185558215610152579182015b82811115610151578251825591602001919060010190610136565b5b50905061015f9190610163565b5090565b61018591905b80821115610181576000816000905550600101610169565b5090565b90565b610c3a806101976000396000f3006060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014257806318160ddd1461019c57806323b872dd146101c557806327e235e31461023e578063313ce5671461028b5780635c658165146102ba57806370a082311461032657806395d89b4114610373578063a9059cbb14610401578063dd62ed3e1461045b575b600080fd5b34156100bf57600080fd5b6100c76104c7565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101075780820151818401526020810190506100ec565b50505050905090810190601f1680156101345780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014d57600080fd5b610182600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610565565b604051808215151515815260200191505060405180910390f35b34156101a757600080fd5b6101af610657565b6040518082815260200191505060405180910390f35b34156101d057600080fd5b610224600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061065d565b604051808215151515815260200191505060405180910390f35b341561024957600080fd5b610275600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108f7565b6040518082815260200191505060405180910390f35b341561029657600080fd5b61029e61090f565b604051808260ff1660ff16815260200191505060405180910390f35b34156102c557600080fd5b610310600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610922565b6040518082815260200191505060405180910390f35b341561033157600080fd5b61035d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610947565b6040518082815260200191505060405180910390f35b341561037e57600080fd5b610386610990565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103c65780820151818401526020810190506103ab565b50505050905090810190601f1680156103f35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561040c57600080fd5b610441600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610a2e565b604051808215151515815260200191505060405180910390f35b341561046657600080fd5b6104b1600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b87565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561055d5780601f106105325761010080835404028352916020019161055d565b820191906000526020600020905b81548152906001019060200180831161054057829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015801561072e5750828110155b151561073957600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108865782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a265780601f106109fb57610100808354040283529160200191610a26565b820191906000526020600020905b815481529060010190602001808311610a0957829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a7e57600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820df254047bc8f2904ad3e966b6db116d703bebd40efadadb5e738c836ffc8f58a0029" ).unwrap().into(); - // BenchmarkDB is dummy state that implements Database trait. - let mut evm = revm::new(); - - // execution globals block hash/gas_limit/coinbase/timestamp.. - evm.env.tx.caller = "0x1000000000000000000000000000000000000000" - .parse() - .unwrap(); - evm.env.tx.transact_to = TransactTo::Call( - "0x0000000000000000000000000000000000000000" - .parse() - .unwrap(), - ); - //evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); - evm.env.tx.data = Bytes::from(hex::decode("8035F0CE").unwrap()); - let bytecode_raw = Bytecode::new_raw(contract_data.clone()); let bytecode_checked = Bytecode::new_raw(contract_data.clone()).to_checked(); let bytecode_analysed = to_analysed(Bytecode::new_raw(contract_data)); - evm.database(BenchmarkDB::new_bytecode(bytecode_raw)); + // BenchmarkDB is dummy state that implements Database trait. + let mut evm = Evm::builder() + .modify_tx_env(|tx| { + // execution globals block hash/gas_limit/coinbase/timestamp.. + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); + //evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); + tx.data = bytes!("8035F0CE"); + }) + .with_db(BenchmarkDB::new_bytecode(bytecode_raw)) + .build(); // just to spead up processor. for _ in 0..10000 { @@ -42,7 +38,10 @@ fn main() { } println!("Raw elapsed time: {:?}", timer.elapsed()); - evm.database(BenchmarkDB::new_bytecode(bytecode_checked)); + let mut evm = evm + .modify() + .with_db(BenchmarkDB::new_bytecode(bytecode_checked)) + .build(); let timer = Instant::now(); for _ in 0..30000 { @@ -50,7 +49,10 @@ fn main() { } println!("Checked elapsed time: {:?}", timer.elapsed()); - evm.database(BenchmarkDB::new_bytecode(bytecode_analysed)); + let mut evm = evm + .modify() + .with_db(BenchmarkDB::new_bytecode(bytecode_analysed)) + .build(); let timer = Instant::now(); for _ in 0..30000 { diff --git a/bins/revm-test/src/bin/snailtracer.rs b/bins/revm-test/src/bin/snailtracer.rs index 19ed7df595..62a7b27659 100644 --- a/bins/revm-test/src/bin/snailtracer.rs +++ b/bins/revm-test/src/bin/snailtracer.rs @@ -1,55 +1,36 @@ +use std::time::Duration; + use revm::{ db::BenchmarkDB, interpreter::analysis::to_analysed, - primitives::{Bytecode, Bytes, TransactTo}, + primitives::{address, bytes, Bytecode, Bytes, TransactTo}, + Evm, }; -extern crate alloc; pub fn simple_example() { - let contract_data : Bytes = hex::decode("608060405234801561001057600080fd5b506004361061004c5760003560e01c806330627b7c1461005157806375ac892a14610085578063784f13661461011d578063c294360114610146575b600080fd5b610059610163565b604080516001600160f81b03199485168152928416602084015292168183015290519081900360600190f35b6100a86004803603604081101561009b57600080fd5b50803590602001356102d1565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100e25781810151838201526020016100ca565b50505050905090810190601f16801561010f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100596004803603606081101561013357600080fd5b508035906020810135906040013561055b565b6100a86004803603602081101561015c57600080fd5b5035610590565b6000806000610176610400610300610834565b60405180606001604052806001546000546207d5dc028161019357fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526102259161021c916102139161020e91612ef7565b612f64565b6207d5dc612feb565b620f424061301e565b8051600e556020810151600f55604001516010556102416142dd565b61025a816102556102006101806008613064565b613212565b90506102708161025561014561021c6008613064565b905061028481610255610258806008613064565b905061029a8161025561020a61020c6008613064565b90506102a781600461301e565b90506102b1613250565b8051602082015160409092015160f891821b9692821b9550901b92509050565b606060005b6000548112156104c95760006102ed828686613064565b90506002816000015160f81b90808054603f811680603e811461032a576002830184556001831661031c578192505b600160028404019350610342565b600084815260209081902060ff198516905560419094555b505050600190038154600116156103685790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146103c557600283018455600183166103b7578192505b6001600284040193506103dd565b600084815260209081902060ff198516905560419094555b505050600190038154600116156104035790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146104605760028301845560018316610452578192505b600160028404019350610478565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561049e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016102d6565b506002805460408051602060018416156101000260001901909316849004601f8101849004840282018401909252818152929183018282801561054d5780601f106105225761010080835404028352916020019161054d565b820191906000526020600020905b81548152906001019060200180831161053057829003601f168201915b505050505090505b92915050565b60008060008061056c878787613064565b8051602082015160409092015160f891821b9a92821b9950901b9650945050505050565b600154606090600019015b600081126107a35760005b6000548112156107995760006105bd828487613064565b90506002816000015160f81b90808054603f811680603e81146105fa57600283018455600183166105ec578192505b600160028404019350610612565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106385790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146106955760028301845560018316610687578192505b6001600284040193506106ad565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106d35790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146107305760028301845560018316610722578192505b600160028404019350610748565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561076e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016105a6565b506000190161059b565b506002805460408051602060018416156101000260001901909316849004601f810184900484028201840190925281815292918301828280156108275780601f106107fc57610100808354040283529160200191610827565b820191906000526020600020905b81548152906001019060200180831161080a57829003601f168201915b505050505090505b919050565b8160008190555080600181905550604051806080016040528060405180606001604052806302faf08081526020016303197500815260200163119e7f8081525081526020016108a460405180606001604052806000815260200161a673198152602001620f423f19815250612f64565b815260006020808301829052604092830182905283518051600355808201516004558301516005558381015180516006559081015160075582015160085582820151600955606092830151600a805460ff1916911515919091179055815192830190915260015490548291906207d5dc028161091c57fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526109979161021c916102139161020e91612ef7565b8051600e55602080820151600f55604091820151601055815160a08101835264174876e8008152825160608082018552641748862a40825263026e8f00828501526304dd1e008286015282840191825284518082018652600080825281860181905281870181905284870191825286518084018852620b71b081526203d09081880181905281890152928501928352608085018181526011805460018082018355919093528651600b9093027f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c688101938455955180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c69880155808901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6a8801558901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6b870155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6c870155808801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6d8701558801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6e860155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6f860155958601517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c7085015594909501517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c71830155517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c72909101805492949192909160ff1990911690836002811115610c1057fe5b0217905550505060116040518060a0016040528064174876e8008152602001604051806060016040528064174290493f19815260200163026e8f0081526020016304dd1e008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806203d09081526020016203d0908152602001620b71b0815250815260200160006002811115610cb657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610d5857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164174876e800815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b0815250815260200160006002811115610dfd57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610e9f57fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164173e54e97f1981525081526020016040518060600160405280600081526020016000815260200160008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160006002811115610f3f57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610fe157fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174876e80081526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b081525081526020016000600281111561108657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561112857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174399c9ff1981526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b08152508152602001600060028111156111ce57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561127057fe5b0217905550505060116040518060a0016040528062fbc5208152602001604051806060016040528063019bfcc0815260200162fbc52081526020016302cd29c0815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561131157fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156113b357fe5b0217905550505060116040518060a001604052806323c34600815260200160405180606001604052806302faf080815260200163289c455081526020016304dd1e008152508152602001604051806060016040528062b71b00815260200162b71b00815260200162b71b00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016000600281111561145657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156114f857fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561160c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156116fd57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561180e57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156118ff57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611a1357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611b0457fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611c1557fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611d0657fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611e1a57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611f0b57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a6081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561201c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561210d57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561222157fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561231257fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561242357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561251457fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561262857fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561271957fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561282d57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561291e57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612a3257fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612b2357fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612c3757fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612d2857fe5b0217905550505060005b601254811015612ef257600060128281548110612d4b57fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115612e6c57fe5b6002811115612e7757fe5b815250509050612eac61020e612e95836020015184600001516132cd565b612ea7846040015185600001516132cd565b612ef7565b60128381548110612eb957fe5b60009182526020918290208351600960139093029091019182015590820151600a820155604090910151600b9091015550600101612d32565b505050565b612eff6142dd565b604051806060016040528083602001518560400151028460400151866020015102038152602001836040015185600001510284600001518660400151020381526020018360000151856020015102846020015186600001510203815250905092915050565b612f6c6142dd565b604082015160208301518351600092612f9292918002918002919091019080020161330c565b90506040518060600160405280828560000151620f42400281612fb157fe5b058152602001828560200151620f42400281612fc957fe5b058152602001828560400151620f42400281612fe157fe5b0590529392505050565b612ff36142dd565b5060408051606081018252835183028152602080850151840290820152928101519091029082015290565b6130266142dd565b60405180606001604052808385600001518161303e57fe5b0581526020018385602001518161305157fe5b05815260200183856040015181612fe157fe5b61306c6142dd565b6000546013805463ffffffff1916918502860163ffffffff169190911790556130936142dd565b905060005b828112156131f157600061317261314c61021c613115600b60405180606001604052908160008201548152602001600182015481526020016002820154815250506207a1206000546207a1206130ec613343565b63ffffffff16816130f957fe5b0663ffffffff168d620f424002018161310e57fe5b0503612feb565b60408051606081018252600e548152600f5460208201526010549181019190915260015461025591906207a12090816130ec613343565b604080516060810182526006548152600754602082015260085491810191909152613212565b6040805160e081019091526003546080820190815260045460a083015260055460c083015291925060009181906131ae9061025586608c612feb565b81526020016131bc84612f64565b815260006020820181905260409091015290506131e5846102556131df8461336c565b8861301e565b93505050600101613098565b5061320861021c61320183613753565b60ff612feb565b90505b9392505050565b61321a6142dd565b50604080516060810182528251845101815260208084015181860151019082015291810151928101519092019181019190915290565b60008080556001819055613266906002906142fe565b60006003819055600481905560058190556006819055600781905560088190556009819055600a805460ff19169055600b819055600c819055600d819055600e819055600f81905560108190556132bf90601190614345565b6132cb60126000614366565b565b6132d56142dd565b5060408051606081018252825184510381526020808401518186015103908201528282015184830151039181019190915292915050565b80600260018201055b8181121561333d5780915060028182858161332c57fe5b05018161333557fe5b059050613315565b50919050565b6013805463ffffffff19811663ffffffff9182166341c64e6d0261303901821617918290551690565b6133746142dd565b600a826040015113156133a657604051806060016040528060008152602001600081526020016000815250905061082f565b60008060006133b48561379f565b91945092509050826133e857604051806060016040528060008152602001600081526020016000815250935050505061082f565b6133f0614387565b6133f86143c7565b6134006142dd565b6134086142dd565b600086600181111561341657fe5b1415613505576011858154811061342957fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff909116908111156134e157fe5b60028111156134ec57fe5b8152505093508360600151915083604001519050613653565b6012858154811061351257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff9091169081111561363357fe5b600281111561363e57fe5b8152505092508260a001519150826080015190505b6040820151600190811215613669575060408201515b808360200151131561367c575060208201515b808360400151131561368f575060408201515b60408a01805160010190819052600512156136f75780620f42406136b1613343565b63ffffffff16816136be57fe5b0663ffffffff1612156136e8576136e16136db84620f4240612feb565b8261301e565b92506136f7565b50965061082f95505050505050565b6136ff6142dd565b600088600181111561370d57fe5b14156137255761371e8b878b613a57565b9050613733565b6137308b868b613aec565b90505b6137448361025561021c8785613baa565b9b9a5050505050505050505050565b61375b6142dd565b60405180606001604052806137738460000151613be8565b81526020016137858460200151613be8565b81526020016137978460400151613be8565b905292915050565b60008080808080805b6011548110156138c2576000613890601183815481106137c457fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff9091169081111561387c57fe5b600281111561388757fe5b9052508a613c13565b90506000811380156138a957508415806138a957508481125b156138b957809450600093508192505b506001016137a8565b5060005b601254811015613a49576000613a17601283815481106138e257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115613a0357fe5b6002811115613a0e57fe5b9052508a613cbb565b9050600081138015613a305750841580613a3057508481125b15613a4057809450600193508192505b506001016138c6565b509196909550909350915050565b613a5f6142dd565b6000613a7a856000015161025561021c886020015187612feb565b90506000613a8f61020e8387602001516132cd565b9050600085608001516002811115613aa357fe5b1415613ae1576000613ab9828860200151613e0c565b12613acd57613aca81600019612feb565b90505b613ad8868383613e31565b9250505061320b565b613ad8868383613fc1565b613af46142dd565b6000613b0f856000015161025561021c886020015187612feb565b6060860151909150620a2c2a9015613b2757506216e3605b6000620f4240613b3f87606001518960200151613e0c565b81613b4657fe5b05905060008112613b55576000035b64e8d4a5100081800281038380020281900590036000811215613b8c57613b8188858960600151613fc1565b94505050505061320b565b613b9e88858960600151868686614039565b98975050505050505050565b613bb26142dd565b50604080516060810182528251845102815260208084015181860151029082015291810151928101519092029181019190915290565b600080821215613bfa5750600061082f565b620f4240821315613c0f5750620f424061082f565b5090565b600080613c28846020015184600001516132cd565b90506000620f4240613c3e838660200151613e0c565b81613c4557fe5b865191900591506000908002613c5b8480613e0c565b838402030190506000811215613c775760009350505050610555565b613c808161330c565b90506103e88183031315613c9957900391506105559050565b6103e88183011315613caf570191506105559050565b50600095945050505050565b600080613cd0846020015185600001516132cd565b90506000613ce6856040015186600001516132cd565b90506000613cf8856020015183612ef7565b90506000620f4240613d0a8584613e0c565b81613d1157fe5b0590506103e71981138015613d2757506103e881125b15613d39576000945050505050610555565b85518751600091613d49916132cd565b9050600082613d588386613e0c565b81613d5f57fe5b0590506000811280613d735750620f424081135b15613d875760009650505050505050610555565b6000613d938388612ef7565b9050600084613da68b6020015184613e0c565b81613dad57fe5b0590506000811280613dc35750620f4240818401135b15613dd957600098505050505050505050610555565b600085613de68985613e0c565b81613ded57fe5b0590506103e88112156137445760009950505050505050505050610555565b6040808201519083015160208084015190850151845186510291020191020192915050565b613e396142dd565b6000620f424080613e48613343565b63ffffffff1681613e5557fe5b0663ffffffff16625fdfb00281613e6857fe5b0590506000620f4240613e79613343565b63ffffffff1681613e8657fe5b0663ffffffff1690506000613e9a8261330c565b6103e8029050613ea86142dd565b620186a0613eb98760000151614216565b1315613ee657604051806060016040528060008152602001620f4240815260200160008152509050613f09565b6040518060600160405280620f4240815260200160008152602001600081525090505b613f1661020e8288612ef7565b90506000613f2761020e8884612ef7565b9050613f7f61020e613f64613f5285620f424088613f448c61422e565b0281613f4c57fe5b05612feb565b61025585620f424089613f448d61424e565b6102558a613f7689620f42400361330c565b6103e802612feb565b9150613fb460405180608001604052808a81526020018481526020018b6040015181526020018b60600151151581525061336c565b9998505050505050505050565b613fc96142dd565b6000613ffb61020e8660200151613ff686620f4240613fec898c60200151613e0c565b60020281613f4c57fe5b6132cd565b90506140306040518060800160405280868152602001838152602001876040015181526020018760600151151581525061336c565b95945050505050565b6140416142dd565b60608701516000199015614053575060015b600061408961020e61021c61406c8c602001518a612feb565b613ff68b6140798a61330c565b620f42408c8e0205018802612feb565b60608a0151909150620f42408601906140ba57620f42406140aa838a613e0c565b816140b157fe5b05620f42400390505b60408a0151619c406c0c9f2c9cd04674edea40000000620ea6008480028502850285020205019060021261415e5761412a61411f60405180608001604052808d81526020018681526020018e6040015181526020018e6060015115151581525061336c565b82620f424003612feb565b92506141448361025561413e8e8e8e613fc1565b84612feb565b925061415383620f424061301e565b94505050505061420c565b600281056203d09001620f4240614173613343565b63ffffffff168161418057fe5b0663ffffffff1612156141b2576141536141a461419e8d8d8d613fc1565b83612feb565b600283056203d0900161301e565b6142056141f76141ec60405180608001604052808e81526020018781526020018f6040015181526020018f6060015115151581525061336c565b83620f424003612feb565b60028305620b71b00361301e565b9450505050505b9695505050505050565b60008082131561422757508061082f565b5060000390565b60008061423a8361424e565b905061320b81820264e8d4a510000361330c565b60005b600082121561426757625fdfb082019150614251565b5b625fdfb0821261427f57625fdfb082039150614268565b6001828160025b818313156142d457818385028161429957fe5b0585019450620f4240808788860202816142af57fe5b05816142b757fe5b600095909503940592506001810181029190910290600201614286565b50505050919050565b60405180606001604052806000815260200160008152602001600081525090565b50805460018160011615610100020316600290046000825580601f106143245750614342565b601f0160209004906000526020600020908101906143429190614401565b50565b50805460008255600b02906000526020600020908101906143429190614416565b50805460008255601302906000526020600020908101906143429190614475565b6040518060a00160405280600081526020016143a16142dd565b81526020016143ae6142dd565b81526020016143bb6142dd565b81526020016000905290565b6040518060e001604052806143da6142dd565b81526020016143e76142dd565b81526020016143f46142dd565b81526020016143a16142dd565b5b80821115613c0f5760008155600101614402565b5b80821115613c0f57600080825560018201819055600282018190556003820181905560048201819055600582018190556006820181905560078201819055600882018190556009820155600a8101805460ff19169055600b01614417565b5b80821115613c0f576000808255600182018190556002820181905560038201819055600482018190556005820181905560068201819055600782018190556008820181905560098201819055600a8201819055600b8201819055600c8201819055600d8201819055600e8201819055600f820181905560108201819055601182015560128101805460ff1916905560130161447656fea2646970667358221220037024f5647853879c58fbcc61ac3616455f6f731cc6e84f91eb5a3b4e06c00464736f6c63430007060033").unwrap().into(); + let bytecode = to_analysed(Bytecode::new_raw(CONTRACT_DATA)); // BenchmarkDB is dummy state that implements Database trait. - let mut evm = revm::new(); - let bytecode = to_analysed(Bytecode::new_raw(contract_data)); - evm.database(BenchmarkDB::new_bytecode(bytecode.clone())); - - // execution globals block hash/gas_limit/coinbase/timestamp.. - evm.env.tx.caller = "0x1000000000000000000000000000000000000000" - .parse() - .unwrap(); - evm.env.tx.transact_to = TransactTo::Call( - "0x0000000000000000000000000000000000000000" - .parse() - .unwrap(), - ); - evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); + let mut evm = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) + .modify_tx_env(|tx| { + // execution globals block hash/gas_limit/coinbase/timestamp.. + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); + tx.data = bytes!("30627b7c"); + }) + .build(); // Microbenchmark - //let bench_options = microbench::Options::default().time(Duration::from_secs(2)); + let bench_options = microbench::Options::default().time(Duration::from_secs(2)); - //let env = evm.env.clone(); - //microbench::bench( - // &bench_options, - // "Snailtracer Host+Interpreter benchmark", - // || { - let _ = evm.transact().unwrap(); - // }, - //); - /* - // revm interpreter - let contract = Contract { - input: evm.env.tx.data, - bytecode: BytecodeLocked::try_from(bytecode).unwrap(), - ..Default::default() - }; - - let mut host = DummyHost::new(env); - microbench::bench(&bench_options, "Snailtracer Interpreter benchmark", || { - let mut interpreter = Interpreter::new(Box::new(contract.clone()), u64::MAX, false); - interpreter.run::<_, BerlinSpec>(&mut host); - host.clear() - }); - */ + microbench::bench( + &bench_options, + "Snailtracer Host+Interpreter benchmark", + || { + let _ = evm.transact(); + }, + ); } fn main() { @@ -57,3 +38,5 @@ fn main() { simple_example(); //println!("end!"); } + +const CONTRACT_DATA : Bytes = bytes!("608060405234801561001057600080fd5b506004361061004c5760003560e01c806330627b7c1461005157806375ac892a14610085578063784f13661461011d578063c294360114610146575b600080fd5b610059610163565b604080516001600160f81b03199485168152928416602084015292168183015290519081900360600190f35b6100a86004803603604081101561009b57600080fd5b50803590602001356102d1565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100e25781810151838201526020016100ca565b50505050905090810190601f16801561010f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100596004803603606081101561013357600080fd5b508035906020810135906040013561055b565b6100a86004803603602081101561015c57600080fd5b5035610590565b6000806000610176610400610300610834565b60405180606001604052806001546000546207d5dc028161019357fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526102259161021c916102139161020e91612ef7565b612f64565b6207d5dc612feb565b620f424061301e565b8051600e556020810151600f55604001516010556102416142dd565b61025a816102556102006101806008613064565b613212565b90506102708161025561014561021c6008613064565b905061028481610255610258806008613064565b905061029a8161025561020a61020c6008613064565b90506102a781600461301e565b90506102b1613250565b8051602082015160409092015160f891821b9692821b9550901b92509050565b606060005b6000548112156104c95760006102ed828686613064565b90506002816000015160f81b90808054603f811680603e811461032a576002830184556001831661031c578192505b600160028404019350610342565b600084815260209081902060ff198516905560419094555b505050600190038154600116156103685790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146103c557600283018455600183166103b7578192505b6001600284040193506103dd565b600084815260209081902060ff198516905560419094555b505050600190038154600116156104035790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146104605760028301845560018316610452578192505b600160028404019350610478565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561049e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016102d6565b506002805460408051602060018416156101000260001901909316849004601f8101849004840282018401909252818152929183018282801561054d5780601f106105225761010080835404028352916020019161054d565b820191906000526020600020905b81548152906001019060200180831161053057829003601f168201915b505050505090505b92915050565b60008060008061056c878787613064565b8051602082015160409092015160f891821b9a92821b9950901b9650945050505050565b600154606090600019015b600081126107a35760005b6000548112156107995760006105bd828487613064565b90506002816000015160f81b90808054603f811680603e81146105fa57600283018455600183166105ec578192505b600160028404019350610612565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106385790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146106955760028301845560018316610687578192505b6001600284040193506106ad565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106d35790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146107305760028301845560018316610722578192505b600160028404019350610748565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561076e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016105a6565b506000190161059b565b506002805460408051602060018416156101000260001901909316849004601f810184900484028201840190925281815292918301828280156108275780601f106107fc57610100808354040283529160200191610827565b820191906000526020600020905b81548152906001019060200180831161080a57829003601f168201915b505050505090505b919050565b8160008190555080600181905550604051806080016040528060405180606001604052806302faf08081526020016303197500815260200163119e7f8081525081526020016108a460405180606001604052806000815260200161a673198152602001620f423f19815250612f64565b815260006020808301829052604092830182905283518051600355808201516004558301516005558381015180516006559081015160075582015160085582820151600955606092830151600a805460ff1916911515919091179055815192830190915260015490548291906207d5dc028161091c57fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526109979161021c916102139161020e91612ef7565b8051600e55602080820151600f55604091820151601055815160a08101835264174876e8008152825160608082018552641748862a40825263026e8f00828501526304dd1e008286015282840191825284518082018652600080825281860181905281870181905284870191825286518084018852620b71b081526203d09081880181905281890152928501928352608085018181526011805460018082018355919093528651600b9093027f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c688101938455955180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c69880155808901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6a8801558901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6b870155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6c870155808801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6d8701558801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6e860155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6f860155958601517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c7085015594909501517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c71830155517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c72909101805492949192909160ff1990911690836002811115610c1057fe5b0217905550505060116040518060a0016040528064174876e8008152602001604051806060016040528064174290493f19815260200163026e8f0081526020016304dd1e008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806203d09081526020016203d0908152602001620b71b0815250815260200160006002811115610cb657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610d5857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164174876e800815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b0815250815260200160006002811115610dfd57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610e9f57fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164173e54e97f1981525081526020016040518060600160405280600081526020016000815260200160008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160006002811115610f3f57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610fe157fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174876e80081526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b081525081526020016000600281111561108657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561112857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174399c9ff1981526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b08152508152602001600060028111156111ce57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561127057fe5b0217905550505060116040518060a0016040528062fbc5208152602001604051806060016040528063019bfcc0815260200162fbc52081526020016302cd29c0815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561131157fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156113b357fe5b0217905550505060116040518060a001604052806323c34600815260200160405180606001604052806302faf080815260200163289c455081526020016304dd1e008152508152602001604051806060016040528062b71b00815260200162b71b00815260200162b71b00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016000600281111561145657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156114f857fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561160c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156116fd57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561180e57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156118ff57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611a1357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611b0457fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611c1557fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611d0657fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611e1a57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611f0b57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a6081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561201c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561210d57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561222157fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561231257fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561242357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561251457fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561262857fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561271957fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561282d57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561291e57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612a3257fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612b2357fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612c3757fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612d2857fe5b0217905550505060005b601254811015612ef257600060128281548110612d4b57fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115612e6c57fe5b6002811115612e7757fe5b815250509050612eac61020e612e95836020015184600001516132cd565b612ea7846040015185600001516132cd565b612ef7565b60128381548110612eb957fe5b60009182526020918290208351600960139093029091019182015590820151600a820155604090910151600b9091015550600101612d32565b505050565b612eff6142dd565b604051806060016040528083602001518560400151028460400151866020015102038152602001836040015185600001510284600001518660400151020381526020018360000151856020015102846020015186600001510203815250905092915050565b612f6c6142dd565b604082015160208301518351600092612f9292918002918002919091019080020161330c565b90506040518060600160405280828560000151620f42400281612fb157fe5b058152602001828560200151620f42400281612fc957fe5b058152602001828560400151620f42400281612fe157fe5b0590529392505050565b612ff36142dd565b5060408051606081018252835183028152602080850151840290820152928101519091029082015290565b6130266142dd565b60405180606001604052808385600001518161303e57fe5b0581526020018385602001518161305157fe5b05815260200183856040015181612fe157fe5b61306c6142dd565b6000546013805463ffffffff1916918502860163ffffffff169190911790556130936142dd565b905060005b828112156131f157600061317261314c61021c613115600b60405180606001604052908160008201548152602001600182015481526020016002820154815250506207a1206000546207a1206130ec613343565b63ffffffff16816130f957fe5b0663ffffffff168d620f424002018161310e57fe5b0503612feb565b60408051606081018252600e548152600f5460208201526010549181019190915260015461025591906207a12090816130ec613343565b604080516060810182526006548152600754602082015260085491810191909152613212565b6040805160e081019091526003546080820190815260045460a083015260055460c083015291925060009181906131ae9061025586608c612feb565b81526020016131bc84612f64565b815260006020820181905260409091015290506131e5846102556131df8461336c565b8861301e565b93505050600101613098565b5061320861021c61320183613753565b60ff612feb565b90505b9392505050565b61321a6142dd565b50604080516060810182528251845101815260208084015181860151019082015291810151928101519092019181019190915290565b60008080556001819055613266906002906142fe565b60006003819055600481905560058190556006819055600781905560088190556009819055600a805460ff19169055600b819055600c819055600d819055600e819055600f81905560108190556132bf90601190614345565b6132cb60126000614366565b565b6132d56142dd565b5060408051606081018252825184510381526020808401518186015103908201528282015184830151039181019190915292915050565b80600260018201055b8181121561333d5780915060028182858161332c57fe5b05018161333557fe5b059050613315565b50919050565b6013805463ffffffff19811663ffffffff9182166341c64e6d0261303901821617918290551690565b6133746142dd565b600a826040015113156133a657604051806060016040528060008152602001600081526020016000815250905061082f565b60008060006133b48561379f565b91945092509050826133e857604051806060016040528060008152602001600081526020016000815250935050505061082f565b6133f0614387565b6133f86143c7565b6134006142dd565b6134086142dd565b600086600181111561341657fe5b1415613505576011858154811061342957fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff909116908111156134e157fe5b60028111156134ec57fe5b8152505093508360600151915083604001519050613653565b6012858154811061351257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff9091169081111561363357fe5b600281111561363e57fe5b8152505092508260a001519150826080015190505b6040820151600190811215613669575060408201515b808360200151131561367c575060208201515b808360400151131561368f575060408201515b60408a01805160010190819052600512156136f75780620f42406136b1613343565b63ffffffff16816136be57fe5b0663ffffffff1612156136e8576136e16136db84620f4240612feb565b8261301e565b92506136f7565b50965061082f95505050505050565b6136ff6142dd565b600088600181111561370d57fe5b14156137255761371e8b878b613a57565b9050613733565b6137308b868b613aec565b90505b6137448361025561021c8785613baa565b9b9a5050505050505050505050565b61375b6142dd565b60405180606001604052806137738460000151613be8565b81526020016137858460200151613be8565b81526020016137978460400151613be8565b905292915050565b60008080808080805b6011548110156138c2576000613890601183815481106137c457fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff9091169081111561387c57fe5b600281111561388757fe5b9052508a613c13565b90506000811380156138a957508415806138a957508481125b156138b957809450600093508192505b506001016137a8565b5060005b601254811015613a49576000613a17601283815481106138e257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115613a0357fe5b6002811115613a0e57fe5b9052508a613cbb565b9050600081138015613a305750841580613a3057508481125b15613a4057809450600193508192505b506001016138c6565b509196909550909350915050565b613a5f6142dd565b6000613a7a856000015161025561021c886020015187612feb565b90506000613a8f61020e8387602001516132cd565b9050600085608001516002811115613aa357fe5b1415613ae1576000613ab9828860200151613e0c565b12613acd57613aca81600019612feb565b90505b613ad8868383613e31565b9250505061320b565b613ad8868383613fc1565b613af46142dd565b6000613b0f856000015161025561021c886020015187612feb565b6060860151909150620a2c2a9015613b2757506216e3605b6000620f4240613b3f87606001518960200151613e0c565b81613b4657fe5b05905060008112613b55576000035b64e8d4a5100081800281038380020281900590036000811215613b8c57613b8188858960600151613fc1565b94505050505061320b565b613b9e88858960600151868686614039565b98975050505050505050565b613bb26142dd565b50604080516060810182528251845102815260208084015181860151029082015291810151928101519092029181019190915290565b600080821215613bfa5750600061082f565b620f4240821315613c0f5750620f424061082f565b5090565b600080613c28846020015184600001516132cd565b90506000620f4240613c3e838660200151613e0c565b81613c4557fe5b865191900591506000908002613c5b8480613e0c565b838402030190506000811215613c775760009350505050610555565b613c808161330c565b90506103e88183031315613c9957900391506105559050565b6103e88183011315613caf570191506105559050565b50600095945050505050565b600080613cd0846020015185600001516132cd565b90506000613ce6856040015186600001516132cd565b90506000613cf8856020015183612ef7565b90506000620f4240613d0a8584613e0c565b81613d1157fe5b0590506103e71981138015613d2757506103e881125b15613d39576000945050505050610555565b85518751600091613d49916132cd565b9050600082613d588386613e0c565b81613d5f57fe5b0590506000811280613d735750620f424081135b15613d875760009650505050505050610555565b6000613d938388612ef7565b9050600084613da68b6020015184613e0c565b81613dad57fe5b0590506000811280613dc35750620f4240818401135b15613dd957600098505050505050505050610555565b600085613de68985613e0c565b81613ded57fe5b0590506103e88112156137445760009950505050505050505050610555565b6040808201519083015160208084015190850151845186510291020191020192915050565b613e396142dd565b6000620f424080613e48613343565b63ffffffff1681613e5557fe5b0663ffffffff16625fdfb00281613e6857fe5b0590506000620f4240613e79613343565b63ffffffff1681613e8657fe5b0663ffffffff1690506000613e9a8261330c565b6103e8029050613ea86142dd565b620186a0613eb98760000151614216565b1315613ee657604051806060016040528060008152602001620f4240815260200160008152509050613f09565b6040518060600160405280620f4240815260200160008152602001600081525090505b613f1661020e8288612ef7565b90506000613f2761020e8884612ef7565b9050613f7f61020e613f64613f5285620f424088613f448c61422e565b0281613f4c57fe5b05612feb565b61025585620f424089613f448d61424e565b6102558a613f7689620f42400361330c565b6103e802612feb565b9150613fb460405180608001604052808a81526020018481526020018b6040015181526020018b60600151151581525061336c565b9998505050505050505050565b613fc96142dd565b6000613ffb61020e8660200151613ff686620f4240613fec898c60200151613e0c565b60020281613f4c57fe5b6132cd565b90506140306040518060800160405280868152602001838152602001876040015181526020018760600151151581525061336c565b95945050505050565b6140416142dd565b60608701516000199015614053575060015b600061408961020e61021c61406c8c602001518a612feb565b613ff68b6140798a61330c565b620f42408c8e0205018802612feb565b60608a0151909150620f42408601906140ba57620f42406140aa838a613e0c565b816140b157fe5b05620f42400390505b60408a0151619c406c0c9f2c9cd04674edea40000000620ea6008480028502850285020205019060021261415e5761412a61411f60405180608001604052808d81526020018681526020018e6040015181526020018e6060015115151581525061336c565b82620f424003612feb565b92506141448361025561413e8e8e8e613fc1565b84612feb565b925061415383620f424061301e565b94505050505061420c565b600281056203d09001620f4240614173613343565b63ffffffff168161418057fe5b0663ffffffff1612156141b2576141536141a461419e8d8d8d613fc1565b83612feb565b600283056203d0900161301e565b6142056141f76141ec60405180608001604052808e81526020018781526020018f6040015181526020018f6060015115151581525061336c565b83620f424003612feb565b60028305620b71b00361301e565b9450505050505b9695505050505050565b60008082131561422757508061082f565b5060000390565b60008061423a8361424e565b905061320b81820264e8d4a510000361330c565b60005b600082121561426757625fdfb082019150614251565b5b625fdfb0821261427f57625fdfb082039150614268565b6001828160025b818313156142d457818385028161429957fe5b0585019450620f4240808788860202816142af57fe5b05816142b757fe5b600095909503940592506001810181029190910290600201614286565b50505050919050565b60405180606001604052806000815260200160008152602001600081525090565b50805460018160011615610100020316600290046000825580601f106143245750614342565b601f0160209004906000526020600020908101906143429190614401565b50565b50805460008255600b02906000526020600020908101906143429190614416565b50805460008255601302906000526020600020908101906143429190614475565b6040518060a00160405280600081526020016143a16142dd565b81526020016143ae6142dd565b81526020016143bb6142dd565b81526020016000905290565b6040518060e001604052806143da6142dd565b81526020016143e76142dd565b81526020016143f46142dd565b81526020016143a16142dd565b5b80821115613c0f5760008155600101614402565b5b80821115613c0f57600080825560018201819055600282018190556003820181905560048201819055600582018190556006820181905560078201819055600882018190556009820155600a8101805460ff19169055600b01614417565b5b80821115613c0f576000808255600182018190556002820181905560038201819055600482018190556005820181905560068201819055600782018190556008820181905560098201819055600a8201819055600b8201819055600c8201819055600d8201819055600e8201819055600f820181905560108201819055601182015560128101805460ff1916905560130161447656fea2646970667358221220037024f5647853879c58fbcc61ac3616455f6f731cc6e84f91eb5a3b4e06c00464736f6c63430007060033"); diff --git a/bins/revm-test/src/bin/transfer.rs b/bins/revm-test/src/bin/transfer.rs index ff74d442c4..2af94b040d 100644 --- a/bins/revm-test/src/bin/transfer.rs +++ b/bins/revm-test/src/bin/transfer.rs @@ -1,39 +1,33 @@ use revm::{ db::BenchmarkDB, primitives::{Bytecode, TransactTo, U256}, + Evm, }; -use std::time::{Duration, Instant}; +use std::time::Duration; extern crate alloc; fn main() { // BenchmarkDB is dummy state that implements Database trait. - let mut evm = revm::new(); - - // execution globals block hash/gas_limit/coinbase/timestamp.. - evm.env.tx.caller = "0x0000000000000000000000000000000000000001" - .parse() - .unwrap(); - evm.env.tx.value = U256::from(10); - evm.env.tx.transact_to = TransactTo::Call( - "0x0000000000000000000000000000000000000000" - .parse() - .unwrap(), - ); - //evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); - - evm.database(BenchmarkDB::new_bytecode(Bytecode::new())); + let mut evm = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(Bytecode::new())) + .modify_tx_env(|tx| { + // execution globals block hash/gas_limit/coinbase/timestamp.. + tx.caller = "0x0000000000000000000000000000000000000001" + .parse() + .unwrap(); + tx.value = U256::from(10); + tx.transact_to = TransactTo::Call( + "0x0000000000000000000000000000000000000000" + .parse() + .unwrap(), + ); + }) + .build(); // Microbenchmark - let bench_options = microbench::Options::default().time(Duration::from_secs(1)); + let bench_options = microbench::Options::default().time(Duration::from_secs(3)); microbench::bench(&bench_options, "Simple value transfer", || { let _ = evm.transact().unwrap(); }); - - let time = Instant::now(); - for _ in 0..10000 { - let _ = evm.transact().unwrap(); - } - let elapsed = time.elapsed(); - println!("10k runs in {:?}", elapsed.as_nanos() / 10_000); } diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 743e161e3b..1caed8a239 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -95,8 +95,7 @@ impl Interpreter { /// When sub create call returns we can insert output of that call into this interpreter. pub fn insert_create_output(&mut self, result: InterpreterResult, address: Option
) { - let interpreter = self; - interpreter.return_data_buffer = match result.result { + self.return_data_buffer = match result.result { // Save data to return data buffer if the create reverted return_revert!() => result.output, // Otherwise clear it @@ -105,19 +104,19 @@ impl Interpreter { match result.result { return_ok!() => { - push_b256!(interpreter, address.unwrap_or_default().into_word()); - interpreter.gas.erase_cost(result.gas.remaining()); - interpreter.gas.record_refund(result.gas.refunded()); + push_b256!(self, address.unwrap_or_default().into_word()); + self.gas.erase_cost(result.gas.remaining()); + self.gas.record_refund(result.gas.refunded()); } return_revert!() => { - push!(interpreter, U256::ZERO); - interpreter.gas.erase_cost(result.gas.remaining()); + push!(self, U256::ZERO); + self.gas.erase_cost(result.gas.remaining()); } InstructionResult::FatalExternalError => { - interpreter.instruction_result = InstructionResult::FatalExternalError; + self.instruction_result = InstructionResult::FatalExternalError; } _ => { - push!(interpreter, U256::ZERO); + push!(self, U256::ZERO); } } } @@ -135,28 +134,27 @@ impl Interpreter { let out_offset = memory_return_offset.start; let out_len = memory_return_offset.len(); - let interpreter = self; - interpreter.return_data_buffer = result.output; - let target_len = min(out_len, interpreter.return_data_buffer.len()); + self.return_data_buffer = result.output; + let target_len = min(out_len, self.return_data_buffer.len()); match result.result { return_ok!() => { // return unspend gas. - interpreter.gas.erase_cost(result.gas.remaining()); - interpreter.gas.record_refund(result.gas.refunded()); - shared_memory.set(out_offset, &interpreter.return_data_buffer[..target_len]); - push!(interpreter, U256::from(1)); + self.gas.erase_cost(result.gas.remaining()); + self.gas.record_refund(result.gas.refunded()); + shared_memory.set(out_offset, &self.return_data_buffer[..target_len]); + push!(self, U256::from(1)); } return_revert!() => { - interpreter.gas.erase_cost(result.gas.remaining()); - shared_memory.set(out_offset, &interpreter.return_data_buffer[..target_len]); - push!(interpreter, U256::ZERO); + self.gas.erase_cost(result.gas.remaining()); + shared_memory.set(out_offset, &self.return_data_buffer[..target_len]); + push!(self, U256::ZERO); } InstructionResult::FatalExternalError => { - interpreter.instruction_result = InstructionResult::FatalExternalError; + self.instruction_result = InstructionResult::FatalExternalError; } _ => { - push!(interpreter, U256::ZERO); + push!(self, U256::ZERO); } } } diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index a0499917e5..96d65d2ba7 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -19,7 +19,7 @@ mod modexp; mod secp256k1; pub mod utilities; -use alloc::boxed::Box; +use alloc::{boxed::Box, vec::Vec}; use core::{fmt, hash::Hash}; use once_cell::race::OnceBox; #[doc(hidden)] diff --git a/crates/revm/benches/bench.rs b/crates/revm/benches/bench.rs index 8fa8e0e643..20f84ff227 100644 --- a/crates/revm/benches/bench.rs +++ b/crates/revm/benches/bench.rs @@ -7,121 +7,134 @@ use revm::{ primitives::{ address, bytes, hex, BerlinSpec, Bytecode, BytecodeState, Bytes, TransactTo, U256, }, - EvmBuilder, + Evm, }; use revm_interpreter::{opcode::make_instruction_table, SharedMemory, EMPTY_SHARED_MEMORY}; use std::time::Duration; -// fn analysis(c: &mut Criterion) { -// let mut evm = revm::new(); - -// evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); -// evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); -// // evm.env.tx.data = bytes!("30627b7c"); -// evm.env.tx.data = bytes!("8035F0CE"); - -// let contract_data: Bytes = hex::decode(ANALYSIS).unwrap().into(); - -// let mut g = c.benchmark_group("analysis"); -// g.noise_threshold(0.03) -// .warm_up_time(Duration::from_secs(3)) -// .measurement_time(Duration::from_secs(10)) -// .sample_size(10); - -// let raw = Bytecode::new_raw(contract_data.clone()); -// evm.database(BenchmarkDB::new_bytecode(raw)); -// bench_transact(&mut g, &mut evm); - -// let checked = Bytecode::new_raw(contract_data.clone()).to_checked(); -// evm.database(BenchmarkDB::new_bytecode(checked)); -// bench_transact(&mut g, &mut evm); - -// let analysed = to_analysed(Bytecode::new_raw(contract_data)); -// evm.database(BenchmarkDB::new_bytecode(analysed)); -// bench_transact(&mut g, &mut evm); - -// g.finish(); -// } - -// fn snailtracer(c: &mut Criterion) { -// let mut evm = revm::new(); -// evm.database(BenchmarkDB::new_bytecode(bytecode(SNAILTRACER))); - -// evm.env.tx.caller = address!("1000000000000000000000000000000000000000"); -// evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); -// evm.env.tx.data = bytes!("30627b7c"); - -// let mut g = c.benchmark_group("snailtracer"); -// g.noise_threshold(0.03) -// .warm_up_time(Duration::from_secs(3)) -// .measurement_time(Duration::from_secs(10)) -// .sample_size(10); -// bench_transact(&mut g, &mut evm); -// bench_eval(&mut g, &mut evm); -// g.finish(); -// } - -// fn transfer(c: &mut Criterion) { -// let mut evm = revm::new(); -// evm.database(BenchmarkDB::new_bytecode(Bytecode::new())); - -// evm.env.tx.caller = address!("0000000000000000000000000000000000000001"); -// evm.env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); -// evm.env.tx.value = U256::from(10); - -// let mut g = c.benchmark_group("transfer"); -// g.noise_threshold(0.03).warm_up_time(Duration::from_secs(1)); -// bench_transact(&mut g, &mut evm); -// g.finish(); -// } - -// fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { -// let state = match evm.db.as_mut().unwrap().0.state { -// BytecodeState::Raw => "raw", -// BytecodeState::Checked { .. } => "checked", -// BytecodeState::Analysed { .. } => "analysed", -// }; -// let id = format!("transact/{state}"); -// g.bench_function(id, |b| b.iter(|| evm.transact().unwrap())); -// } - -// fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { -// g.bench_function("eval", |b| { -// let contract = Contract { -// input: evm.env.tx.data.clone(), -// bytecode: BytecodeLocked::try_from(evm.db.as_ref().unwrap().0.clone()).unwrap(), -// ..Default::default() -// }; -// let mut shared_memory = SharedMemory::new(); -// let mut host = DummyHost::new(*evm.env.clone()); -// let instruction_table = make_instruction_table::(); -// b.iter(move || { -// // replace memory with empty memory to use it inside interpreter. -// // Later return memory back. -// let temp = core::mem::replace(&mut shared_memory, EMPTY_SHARED_MEMORY); -// let mut interpreter = Interpreter::new(Box::new(contract.clone()), u64::MAX, false); -// let res = interpreter.run(temp, &instruction_table, &mut host); -// shared_memory = interpreter.take_memory(); -// host.clear(); -// res -// }) -// }); -// } +fn analysis(c: &mut Criterion) { + let evm = Evm::builder() + .modify_tx_env(|tx| { + tx.caller = address!("0000000000000000000000000000000000000002"); + tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); + // evm.env.tx.data = bytes!("30627b7c"); + tx.data = bytes!("8035F0CE"); + }) + .build(); + + let contract_data: Bytes = hex::decode(ANALYSIS).unwrap().into(); + + let mut g = c.benchmark_group("analysis"); + g.noise_threshold(0.03) + .warm_up_time(Duration::from_secs(3)) + .measurement_time(Duration::from_secs(10)) + .sample_size(10); + + let raw = Bytecode::new_raw(contract_data.clone()); + let mut evm = evm.modify().with_db(BenchmarkDB::new_bytecode(raw)).build(); + bench_transact(&mut g, &mut evm); + + let checked = Bytecode::new_raw(contract_data.clone()).to_checked(); + let mut evm = evm + .modify() + .with_db(BenchmarkDB::new_bytecode(checked)) + .build(); + bench_transact(&mut g, &mut evm); + + let analysed = to_analysed(Bytecode::new_raw(contract_data)); + let mut evm = evm + .modify() + .with_db(BenchmarkDB::new_bytecode(analysed)) + .build(); + bench_transact(&mut g, &mut evm); + + g.finish(); +} + +fn snailtracer(c: &mut Criterion) { + let mut evm = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(bytecode(SNAILTRACER))) + .modify_tx_env(|tx| { + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); + tx.data = bytes!("30627b7c"); + }) + .build(); + + let mut g = c.benchmark_group("snailtracer"); + g.noise_threshold(0.03) + .warm_up_time(Duration::from_secs(3)) + .measurement_time(Duration::from_secs(10)) + .sample_size(10); + bench_transact(&mut g, &mut evm); + bench_eval(&mut g, &mut evm); + g.finish(); +} + +fn transfer(c: &mut Criterion) { + let mut evm = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(Bytecode::new())) + .modify_tx_env(|tx| { + tx.caller = address!("0000000000000000000000000000000000000001"); + tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000000")); + tx.value = U256::from(10); + }) + .build(); + + let mut g = c.benchmark_group("transfer"); + g.noise_threshold(0.03).warm_up_time(Duration::from_secs(1)); + bench_transact(&mut g, &mut evm); + g.finish(); +} + +fn bench_transact<'a, EXT>( + g: &mut BenchmarkGroup<'_, WallTime>, + evm: &mut Evm<'a, EXT, BenchmarkDB>, +) { + let state = match evm.context.evm.db.0.state { + BytecodeState::Raw => "raw", + BytecodeState::Checked { .. } => "checked", + BytecodeState::Analysed { .. } => "analysed", + }; + let id = format!("transact/{state}"); + g.bench_function(id, |b| b.iter(|| evm.transact().unwrap())); +} + +fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm<'static, (), BenchmarkDB>) { + g.bench_function("eval", |b| { + let contract = Contract { + input: evm.context.evm.env.tx.data.clone(), + bytecode: BytecodeLocked::try_from(evm.context.evm.db.0.clone()).unwrap(), + ..Default::default() + }; + let mut shared_memory = SharedMemory::new(); + let mut host = DummyHost::new(*evm.context.evm.env.clone()); + let instruction_table = make_instruction_table::(); + b.iter(move || { + // replace memory with empty memory to use it inside interpreter. + // Later return memory back. + let temp = core::mem::replace(&mut shared_memory, EMPTY_SHARED_MEMORY); + let mut interpreter = Interpreter::new(Box::new(contract.clone()), u64::MAX, false); + let res = interpreter.run(temp, &instruction_table, &mut host); + shared_memory = interpreter.take_memory(); + host.clear(); + res + }) + }); +} fn bytecode(s: &str) -> Bytecode { to_analysed(Bytecode::new_raw(hex::decode(s).unwrap().into())) } -// #[rustfmt::skip] -// criterion_group!( -// benches, -// analysis, -// snailtracer, -// transfer, -// ); -// criterion_main!(benches); - -fn main() {} +#[rustfmt::skip] +criterion_group!( + benches, + analysis, + snailtracer, + transfer, +); +criterion_main!(benches); const ANALYSIS: &str = "6060604052341561000f57600080fd5b604051610dd1380380610dd18339810160405280805190602001909190805182019190602001805190602001909190805182019190505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100a79291906100e3565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100d99291906100e3565b5050505050610188565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012457805160ff1916838001178555610152565b82800160010185558215610152579182015b82811115610151578251825591602001919060010190610136565b5b50905061015f9190610163565b5090565b61018591905b80821115610181576000816000905550600101610169565b5090565b90565b610c3a806101976000396000f3006060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014257806318160ddd1461019c57806323b872dd146101c557806327e235e31461023e578063313ce5671461028b5780635c658165146102ba57806370a082311461032657806395d89b4114610373578063a9059cbb14610401578063dd62ed3e1461045b575b600080fd5b34156100bf57600080fd5b6100c76104c7565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101075780820151818401526020810190506100ec565b50505050905090810190601f1680156101345780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014d57600080fd5b610182600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610565565b604051808215151515815260200191505060405180910390f35b34156101a757600080fd5b6101af610657565b6040518082815260200191505060405180910390f35b34156101d057600080fd5b610224600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061065d565b604051808215151515815260200191505060405180910390f35b341561024957600080fd5b610275600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108f7565b6040518082815260200191505060405180910390f35b341561029657600080fd5b61029e61090f565b604051808260ff1660ff16815260200191505060405180910390f35b34156102c557600080fd5b610310600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610922565b6040518082815260200191505060405180910390f35b341561033157600080fd5b61035d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610947565b6040518082815260200191505060405180910390f35b341561037e57600080fd5b610386610990565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103c65780820151818401526020810190506103ab565b50505050905090810190601f1680156103f35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561040c57600080fd5b610441600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610a2e565b604051808215151515815260200191505060405180910390f35b341561046657600080fd5b6104b1600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b87565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561055d5780601f106105325761010080835404028352916020019161055d565b820191906000526020600020905b81548152906001019060200180831161054057829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015801561072e5750828110155b151561073957600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108865782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a265780601f106109fb57610100808354040283529160200191610a26565b820191906000526020600020905b815481529060010190602001808311610a0957829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a7e57600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820df254047bc8f2904ad3e966b6db116d703bebd40efadadb5e738c836ffc8f58a0029"; diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index 8c30933bb2..54a050cd21 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -10,7 +10,7 @@ use core::marker::PhantomData; /// Evm Builder allows building or modifying EVM. /// Note that some of the methods that changes underlying structures -/// will reset the registered handler to default mainnet. +/// will reset the registered handler to default mainnet. pub struct EvmBuilder<'a, Stage: BuilderStage, EXT, DB: Database> { evm: EvmContext, external: EXT, @@ -46,7 +46,7 @@ impl<'a> Default for EvmBuilder<'a, SettingDbStage, (), EmptyDB> { } } -impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { +impl<'a, BS: BuilderStage, EXT, DB: Database> EvmBuilder<'a, BS, EXT, DB> { /// Sets the [`EmptyDB`] as the [`Database`] that will be used by [`Evm`]. /// /// # Note @@ -54,7 +54,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { /// When changed it will reset the handler to the mainnet. pub fn with_empty_db(self) -> EvmBuilder<'a, SettingExternalStage, EXT, EmptyDB> { EvmBuilder { - evm: EvmContext::new(EmptyDB::default()), + evm: self.evm.with_db(EmptyDB::default()), external: self.external, handler: Handler::mainnet::(), @@ -68,7 +68,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { /// When changed it will reset the handler to default mainnet. pub fn with_db(self, db: ODB) -> EvmBuilder<'a, SettingExternalStage, EXT, ODB> { EvmBuilder { - evm: EvmContext::new(db), + evm: self.evm.with_db(db), external: self.external, handler: Handler::mainnet::(), @@ -85,7 +85,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { db: ODB, ) -> EvmBuilder<'a, SettingExternalStage, EXT, WrapDatabaseRef> { EvmBuilder { - evm: EvmContext::new(WrapDatabaseRef(db)), + evm: self.evm.with_db(WrapDatabaseRef(db)), external: self.external, handler: Handler::mainnet::(), @@ -129,16 +129,22 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternalStage, EXT, DB> { } } - /// Consumes the Builder and build the Build Evm with default mainnet handler. - pub fn build(self) -> Evm<'a, EXT, DB> { - Evm { - context: Context { - evm: self.evm, - external: self.external, - }, - handler: self.handler, - } + /// Modify Database of EVM. + pub fn modify_db(mut self, f: impl FnOnce(&mut DB)) -> Self { + f(&mut self.evm.db); + self } + + // Consumes the Builder and build the Build Evm with default mainnet handler. + // pub fn build(self) -> Evm<'a, EXT, DB> { + // Evm { + // context: Context { + // evm: self.evm, + // external: self.external, + // }, + // handler: self.handler, + // } + // } } impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { @@ -154,17 +160,23 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { } } - /// Consumes the Builder and build the Build Evm. - pub fn build(self) -> Evm<'a, EXT, DB> { - Evm { - context: Context { - evm: self.evm, - external: self.external, - }, - handler: self.handler, - } + /// Modify Database of EVM. + pub fn modify_db(mut self, f: impl FnOnce(&mut DB)) -> Self { + f(&mut self.evm.db); + self } + // Consumes the Builder and build the Build Evm. + // pub fn build(self) -> Evm<'a, EXT, DB> { + // Evm { + // context: Context { + // evm: self.evm, + // external: self.external, + // }, + // handler: self.handler, + // } + // } + /// Sets specification Id , that will mark the version of EVM. /// It represent the hard fork of ethereum. /// diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index b7ca54de1b..c54dfce175 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -43,6 +43,17 @@ pub struct EvmContext { } impl<'a, DB: Database> EvmContext { + pub fn with_db(self, db: ODB) -> EvmContext { + EvmContext { + env: self.env, + journaled_state: self.journaled_state, + db, + error: None, + precompiles: self.precompiles, + #[cfg(feature = "optimism")] + l1_block_info: self.l1_block_info, + } + } pub fn new(db: DB) -> Self { Self { env: Box::default(), diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 8cd720373e..25ce2bd7c8 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -22,8 +22,6 @@ pub const CALL_STACK_LIMIT: u64 = 1024; /// EVM instance containing both internal EVM context and external context /// and the handler that dictates the logic of EVM (or hardfork specification). -/// -/// pub struct Evm<'a, EXT, DB: Database> { /// Context of execution, containing both EVM and external context. pub context: Context, @@ -46,7 +44,7 @@ where } impl<'a> Evm<'a, (), EmptyDB> { - /// Returns evm builder. + /// Returns evm builder with empty database and empty external context. pub fn builder() -> EvmBuilder<'a, SettingDbStage, (), EmptyDB> { EvmBuilder::default() } @@ -58,6 +56,14 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { Evm { context, handler } } + /// Allow for evm setting to be modified by feeding current evm + /// into the builder for modifications. + pub fn modify(self) -> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { + EvmBuilder::new(self) + } +} + +impl Evm<'_, EXT, DB> { /// Returns specification (hardfork) that the EVM is instanced with. /// /// SpecId depends on the handler. @@ -79,7 +85,9 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { Ok(()) } - /// Transact pre-verified transaction, this function will not validate the transaction. + /// Transact pre-verified transaction + /// + /// This function will not validate the transaction. #[inline] pub fn transact_preverified(&mut self) -> EVMResult { let initial_gas_spend = self @@ -90,7 +98,9 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { self.handler.main().end(&mut self.context, output) } - /// Transact transaction, this function will validate the transaction. + /// Transact transaction + /// + /// This function will validate the transaction. #[inline] pub fn transact(&mut self) -> EVMResult { self.handler.validation().env(&self.context.evm.env)?; @@ -106,12 +116,6 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { self.handler.main().end(&mut self.context, output) } - /// Allow for evm setting to be modified by feeding current evm - /// to the builder for modifications. - pub fn modify(self) -> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { - EvmBuilder::new(self) - } - /// Modify spec id, this will create new EVM that matches this spec id. pub fn modify_spec_id(self, spec_id: SpecId) -> Self { if self.spec_id() == spec_id { @@ -131,7 +135,7 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { &mut self, first_stack_frame: FrameOrResult, ) -> (InterpreterResult, Output) { - // Some only if it is create. + // Created address will be something only if it is create. let mut created_address = None; // start main loop if CallStackFrame is created correctly @@ -150,7 +154,7 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { InstructionTables::Boxed(table) => self.run_the_loop(&table, first_stack_frame), }; - // return instruction table + // return back instruction table self.handler.set_instruction_table(table); output @@ -188,7 +192,8 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { shared_memory.new_context(); - let mut stack_frame = call_stack.first_mut().unwrap(); + // peek last stack frame. + let mut stack_frame = call_stack.last_mut().unwrap(); loop { // run interpreter @@ -276,7 +281,7 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { } } -impl<'a, EXT, DB: Database> Host for Evm<'a, EXT, DB> { +impl Host for Evm<'_, EXT, DB> { fn env(&mut self) -> &mut Env { self.context.evm.env() } diff --git a/crates/revm/src/frame.rs b/crates/revm/src/frame.rs index e78d61cca5..02989ca073 100644 --- a/crates/revm/src/frame.rs +++ b/crates/revm/src/frame.rs @@ -3,6 +3,7 @@ use crate::{ primitives::Address, JournalCheckpoint, }; +use alloc::boxed::Box; use core::ops::Range; /// Call CallStackFrame. diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index a98abbbde3..7ab2b1a7e3 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -18,9 +18,8 @@ use crate::{ }, primitives::{db::Database, Spec, SpecId}, }; -use register::HandleRegisters; - -use self::register::EvmHandler; +use alloc::vec::Vec; +use register::{EvmHandler, HandleRegisters}; /// Handler acts as a proxy and allow to define different behavior for different /// sections of the code. This allows nice integration of different chains or @@ -108,6 +107,7 @@ impl<'a, EXT: 'a, DB: Database + 'a> EvmHandler<'a, EXT, DB> { return self; } use crate::primitives::specification::*; + // We are transitioning from var to generic spec. match spec_id { SpecId::FRONTIER | SpecId::FRONTIER_THAWING => { self.create_handle_generic::() diff --git a/crates/revm/src/handler/handle_types/frame.rs b/crates/revm/src/handler/handle_types/frame.rs index b9819b2f79..0275e85251 100644 --- a/crates/revm/src/handler/handle_types/frame.rs +++ b/crates/revm/src/handler/handle_types/frame.rs @@ -6,7 +6,7 @@ use crate::{ primitives::{db::Database, Env, Spec}, CallStackFrame, Context, FrameOrResult, }; -use alloc::sync::Arc; +use alloc::{boxed::Box, sync::Arc}; use core::ops::Range; /// Creates the first frame. diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs index fbdfc1851a..2a5e9df89e 100644 --- a/crates/revm/src/handler/register.rs +++ b/crates/revm/src/handler/register.rs @@ -1,4 +1,5 @@ use crate::{db::Database, handler::Handler, interpreter::opcode::InstructionTables, Evm}; +use alloc::boxed::Box; /// EVM Handler pub type EvmHandler<'a, EXT, DB> = Handler<'a, Evm<'a, EXT, DB>, EXT, DB>; diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs index ceabcd45b7..170a6ffbad 100644 --- a/crates/revm/src/inspector/customprinter.rs +++ b/crates/revm/src/inspector/customprinter.rs @@ -7,7 +7,7 @@ use crate::{ inspectors::GasInspector, interpreter::{opcode, CallInputs, CreateInputs, Interpreter, InterpreterResult}, primitives::{Address, U256}, - Database, EvmContext, Inspector, + Database, EvmContext, GetInspector, Inspector, }; /// Custom print [Inspector], it has step level information of execution. @@ -18,6 +18,12 @@ pub struct CustomPrintTracer { gas_inspector: GasInspector, } +impl<'a, DB: Database> GetInspector<'a, DB> for CustomPrintTracer { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } +} + impl Inspector for CustomPrintTracer { fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { self.gas_inspector.initialize_interp(interp, context); @@ -109,31 +115,42 @@ impl Inspector for CustomPrintTracer { #[cfg(test)] mod test { - - // #[test] - // #[cfg(not(feature = "optimism"))] - // fn gas_calculation_underflow() { - // use crate::primitives::{address, bytes}; - - // // https://github.com/bluealloy/revm/issues/277 - // // checks this use case - // let mut evm = crate::new(); - // let mut database = crate::InMemoryDB::default(); - // let code = bytes!("5b597fb075978b6c412c64d169d56d839a8fe01b3f4607ed603b2c78917ce8be1430fe6101e8527ffe64706ecad72a2f5c97a95e006e279dc57081902029ce96af7edae5de116fec610208527f9fc1ef09d4dd80683858ae3ea18869fe789ddc365d8d9d800e26c9872bac5e5b6102285260276102485360d461024953601661024a53600e61024b53607d61024c53600961024d53600b61024e5360b761024f5360596102505360796102515360a061025253607261025353603a6102545360fb61025553601261025653602861025753600761025853606f61025953601761025a53606161025b53606061025c5360a661025d53602b61025e53608961025f53607a61026053606461026153608c6102625360806102635360d56102645360826102655360ae61026653607f6101e8610146610220677a814b184591c555735fdcca53617f4d2b9134b29090c87d01058e27e962047654f259595947443b1b816b65cdb6277f4b59c10a36f4e7b8658f5a5e6f5561"); - - // let acc_info = crate::primitives::AccountInfo { - // balance: "0x100c5d668240db8e00".parse().unwrap(), - // code_hash: crate::primitives::keccak256(&code), - // code: Some(crate::primitives::Bytecode::new_raw(code.clone())), - // nonce: 1, - // }; - // let callee = address!("5fdcca53617f4d2b9134b29090c87d01058e27e9"); - // database.insert_account_info(callee, acc_info); - // evm.database(database); - // evm.env.tx.caller = address!("5fdcca53617f4d2b9134b29090c87d01058e27e0"); - // evm.env.tx.transact_to = crate::primitives::TransactTo::Call(callee); - // evm.env.tx.data = crate::primitives::Bytes::new(); - // evm.env.tx.value = crate::primitives::U256::ZERO; - // let _ = evm.inspect_commit(super::CustomPrintTracer::default()); - // } + use crate::{ + inspector_handle_register, + inspectors::CustomPrintTracer, + primitives::{address, bytes, SpecId}, + Evm, InMemoryDB, + }; + + #[test] + fn gas_calculation_underflow() { + let callee = address!("5fdcca53617f4d2b9134b29090c87d01058e27e9"); + + // https://github.com/bluealloy/revm/issues/277 + // checks this use case + let mut evm = Evm::builder() + .with_db(InMemoryDB::default()) + .modify_db(|db| { + let code = bytes!("5b597fb075978b6c412c64d169d56d839a8fe01b3f4607ed603b2c78917ce8be1430fe6101e8527ffe64706ecad72a2f5c97a95e006e279dc57081902029ce96af7edae5de116fec610208527f9fc1ef09d4dd80683858ae3ea18869fe789ddc365d8d9d800e26c9872bac5e5b6102285260276102485360d461024953601661024a53600e61024b53607d61024c53600961024d53600b61024e5360b761024f5360596102505360796102515360a061025253607261025353603a6102545360fb61025553601261025653602861025753600761025853606f61025953601761025a53606161025b53606061025c5360a661025d53602b61025e53608961025f53607a61026053606461026153608c6102625360806102635360d56102645360826102655360ae61026653607f6101e8610146610220677a814b184591c555735fdcca53617f4d2b9134b29090c87d01058e27e962047654f259595947443b1b816b65cdb6277f4b59c10a36f4e7b8658f5a5e6f5561"); + let info = crate::primitives::AccountInfo { + balance: "0x100c5d668240db8e00".parse().unwrap(), + code_hash: crate::primitives::keccak256(&code), + code: Some(crate::primitives::Bytecode::new_raw(code.clone())), + nonce: 1, + }; + db.insert_account_info(callee, info); + }) + .modify_tx_env(|tx| { + tx.caller = address!("5fdcca53617f4d2b9134b29090c87d01058e27e0"); + tx.transact_to = crate::primitives::TransactTo::Call(callee); + tx.data = crate::primitives::Bytes::new(); + tx.value = crate::primitives::U256::ZERO; + }) + .with_external_context(CustomPrintTracer::default()) + .with_spec_id(SpecId::BERLIN) + .append_handler_register(inspector_handle_register) + .build(); + + evm.transact().expect("Transaction to work"); + } } diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index a562e11162..7e1946cb14 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -4,7 +4,7 @@ use crate::{ interpreter::{opcode, Interpreter, InterpreterResult}, CallStackFrame, Evm, FrameOrResult, Inspector, JournalEntry, }; -use alloc::sync::Arc; +use alloc::{boxed::Box, sync::Arc, vec::Vec}; use revm_interpreter::{opcode::BoxedInstruction, InstructionResult}; pub trait GetInspector<'a, DB: Database> { diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index 29c1c7d264..f0ce3348d9 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -1,175 +1,7 @@ //! Optimism-specific constants, types, and helpers. -use crate::{ - primitives::{ - address, db::Database, Address, Bytes, EVMError, InvalidTransaction, Spec, SpecId, U256, - }, - JournaledState, -}; -use core::ops::Mul; +mod handler_register; +mod l1block; -const ZERO_BYTE_COST: u64 = 4; -const NON_ZERO_BYTE_COST: u64 = 16; - -const L1_BASE_FEE_SLOT: U256 = U256::from_limbs([1u64, 0, 0, 0]); -const L1_OVERHEAD_SLOT: U256 = U256::from_limbs([5u64, 0, 0, 0]); -const L1_SCALAR_SLOT: U256 = U256::from_limbs([6u64, 0, 0, 0]); - -/// The address of L1 fee recipient. -pub const L1_FEE_RECIPIENT: Address = address!("420000000000000000000000000000000000001A"); - -/// The address of the base fee recipient. -pub const BASE_FEE_RECIPIENT: Address = address!("4200000000000000000000000000000000000019"); - -/// The address of the L1Block contract. -pub const L1_BLOCK_CONTRACT: Address = address!("4200000000000000000000000000000000000015"); - -/// L1 block info -/// -/// We can extract L1 epoch data from each L2 block, by looking at the `setL1BlockValues` -/// transaction data. This data is then used to calculate the L1 cost of a transaction. -/// -/// Here is the format of the `setL1BlockValues` transaction data: -/// -/// setL1BlockValues(uint64 _number, uint64 _timestamp, uint256 _basefee, bytes32 _hash, -/// uint64 _sequenceNumber, bytes32 _batcherHash, uint256 _l1FeeOverhead, uint256 _l1FeeScalar) -/// -/// For now, we only care about the fields necessary for L1 cost calculation. -#[derive(Clone, Debug)] -pub struct L1BlockInfo { - /// The base fee of the L1 origin block. - pub l1_base_fee: U256, - /// The current L1 fee overhead. - pub l1_fee_overhead: U256, - /// The current L1 fee scalar. - pub l1_fee_scalar: U256, -} - -impl L1BlockInfo { - /// Try to fetch the L1 block info from the database. - pub fn try_fetch(db: &mut DB) -> Result { - let l1_base_fee = db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?; - let l1_fee_overhead = db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?; - let l1_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?; - - Ok(L1BlockInfo { - l1_base_fee, - l1_fee_overhead, - l1_fee_scalar, - }) - } - - /// Calculate the data gas for posting the transaction on L1. Calldata costs 16 gas per non-zero - /// byte and 4 gas per zero byte. - /// - /// Prior to regolith, an extra 68 non-zero bytes were included in the rollup data costs to - /// account for the empty signature. - pub fn data_gas(&self, input: &Bytes, spec_id: SpecId) -> U256 { - let mut rollup_data_gas_cost = U256::from(input.iter().fold(0, |acc, byte| { - acc + if *byte == 0x00 { - ZERO_BYTE_COST - } else { - NON_ZERO_BYTE_COST - } - })); - - // Prior to regolith, an extra 68 non zero bytes were included in the rollup data costs. - if !spec_id.is_enabled_in(SpecId::REGOLITH) { - rollup_data_gas_cost += U256::from(NON_ZERO_BYTE_COST).mul(U256::from(68)); - } - - rollup_data_gas_cost - } - - /// Calculate the gas cost of a transaction based on L1 block data posted on L2 - pub fn calculate_tx_l1_cost(&self, input: &Bytes, spec_id: SpecId) -> U256 { - // If the input is not a deposit transaction, the default value is zero. - if input.is_empty() || input.first() == Some(&0x7F) { - return U256::ZERO; - } - - let rollup_data_gas_cost = self.data_gas(input, spec_id); - rollup_data_gas_cost - .saturating_add(self.l1_fee_overhead) - .saturating_mul(self.l1_base_fee) - .saturating_mul(self.l1_fee_scalar) - / U256::from(1_000_000) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::primitives::{bytes, specification::*}; - - #[test] - fn test_data_gas_non_zero_bytes() { - let l1_block_info = L1BlockInfo { - l1_base_fee: U256::from(1_000_000), - l1_fee_overhead: U256::from(1_000_000), - l1_fee_scalar: U256::from(1_000_000), - }; - - // 0xFACADE = 6 nibbles = 3 bytes - // 0xFACADE = 1111 1010 . 1100 1010 . 1101 1110 - - // Pre-regolith (ie bedrock) has an extra 68 non-zero bytes - // gas cost = 3 non-zero bytes * NON_ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 - // gas cost = 3 * 16 + 68 * 16 = 1136 - let input = bytes!("FACADE"); - let bedrock_data_gas = l1_block_info.data_gas(&input, SpecId::BEDROCK); - assert_eq!(bedrock_data_gas, U256::from(1136)); - - // Regolith has no added 68 non zero bytes - // gas cost = 3 * 16 = 48 - let regolith_data_gas = l1_block_info.data_gas(&input, SpecId::REGOLITH); - assert_eq!(regolith_data_gas, U256::from(48)); - } - - #[test] - fn test_data_gas_zero_bytes() { - let l1_block_info = L1BlockInfo { - l1_base_fee: U256::from(1_000_000), - l1_fee_overhead: U256::from(1_000_000), - l1_fee_scalar: U256::from(1_000_000), - }; - - // 0xFA00CA00DE = 10 nibbles = 5 bytes - // 0xFA00CA00DE = 1111 1010 . 0000 0000 . 1100 1010 . 0000 0000 . 1101 1110 - - // Pre-regolith (ie bedrock) has an extra 68 non-zero bytes - // gas cost = 3 non-zero * NON_ZERO_BYTE_COST + 2 * ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 - // gas cost = 3 * 16 + 2 * 4 + 68 * 16 = 1144 - let input = bytes!("FA00CA00DE"); - let bedrock_data_gas = l1_block_info.data_gas(&input, SpecId::BEDROCK); - assert_eq!(bedrock_data_gas, U256::from(1144)); - - // Regolith has no added 68 non zero bytes - // gas cost = 3 * 16 + 2 * 4 = 56 - let regolith_data_gas = l1_block_info.data_gas(&input, SpecId::REGOLITH); - assert_eq!(regolith_data_gas, U256::from(56)); - } - - #[test] - fn test_calculate_tx_l1_cost() { - let l1_block_info = L1BlockInfo { - l1_base_fee: U256::from(1_000), - l1_fee_overhead: U256::from(1_000), - l1_fee_scalar: U256::from(1_000), - }; - - let input = bytes!("FACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); - assert_eq!(gas_cost, U256::from(1048)); - - // Zero rollup data gas cost should result in zero - let input = bytes!(""); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); - assert_eq!(gas_cost, U256::ZERO); - - // Deposit transactions with the EIP-2718 type of 0x7F should result in zero - let input = bytes!("7FFACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); - assert_eq!(gas_cost, U256::ZERO); - } -} +pub use handler_register::optimism_handle_register; +pub use l1block::L1BlockInfo; diff --git a/crates/revm/src/optimism/l1block.rs b/crates/revm/src/optimism/l1block.rs new file mode 100644 index 0000000000..3a0bc5ab0a --- /dev/null +++ b/crates/revm/src/optimism/l1block.rs @@ -0,0 +1,173 @@ +use crate::{ + primitives::{ + address, db::Database, Address, Bytes, EVMError, InvalidTransaction, Spec, SpecId, U256, + }, + JournaledState, +}; +use core::ops::Mul; + +const ZERO_BYTE_COST: u64 = 4; +const NON_ZERO_BYTE_COST: u64 = 16; + +const L1_BASE_FEE_SLOT: U256 = U256::from_limbs([1u64, 0, 0, 0]); +const L1_OVERHEAD_SLOT: U256 = U256::from_limbs([5u64, 0, 0, 0]); +const L1_SCALAR_SLOT: U256 = U256::from_limbs([6u64, 0, 0, 0]); + +/// The address of L1 fee recipient. +pub const L1_FEE_RECIPIENT: Address = address!("420000000000000000000000000000000000001A"); + +/// The address of the base fee recipient. +pub const BASE_FEE_RECIPIENT: Address = address!("4200000000000000000000000000000000000019"); + +/// The address of the L1Block contract. +pub const L1_BLOCK_CONTRACT: Address = address!("4200000000000000000000000000000000000015"); + +/// L1 block info +/// +/// We can extract L1 epoch data from each L2 block, by looking at the `setL1BlockValues` +/// transaction data. This data is then used to calculate the L1 cost of a transaction. +/// +/// Here is the format of the `setL1BlockValues` transaction data: +/// +/// setL1BlockValues(uint64 _number, uint64 _timestamp, uint256 _basefee, bytes32 _hash, +/// uint64 _sequenceNumber, bytes32 _batcherHash, uint256 _l1FeeOverhead, uint256 _l1FeeScalar) +/// +/// For now, we only care about the fields necessary for L1 cost calculation. +#[derive(Clone, Debug)] +pub struct L1BlockInfo { + /// The base fee of the L1 origin block. + pub l1_base_fee: U256, + /// The current L1 fee overhead. + pub l1_fee_overhead: U256, + /// The current L1 fee scalar. + pub l1_fee_scalar: U256, +} + +impl L1BlockInfo { + /// Try to fetch the L1 block info from the database. + pub fn try_fetch(db: &mut DB) -> Result { + let l1_base_fee = db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?; + let l1_fee_overhead = db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?; + let l1_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?; + + Ok(L1BlockInfo { + l1_base_fee, + l1_fee_overhead, + l1_fee_scalar, + }) + } + + /// Calculate the data gas for posting the transaction on L1. Calldata costs 16 gas per non-zero + /// byte and 4 gas per zero byte. + /// + /// Prior to regolith, an extra 68 non-zero bytes were included in the rollup data costs to + /// account for the empty signature. + pub fn data_gas(&self, input: &Bytes, spec_id: SpecId) -> U256 { + let mut rollup_data_gas_cost = U256::from(input.iter().fold(0, |acc, byte| { + acc + if *byte == 0x00 { + ZERO_BYTE_COST + } else { + NON_ZERO_BYTE_COST + } + })); + + // Prior to regolith, an extra 68 non zero bytes were included in the rollup data costs. + if !spec_id.is_enabled_in(SpecId::REGOLITH) { + rollup_data_gas_cost += U256::from(NON_ZERO_BYTE_COST).mul(U256::from(68)); + } + + rollup_data_gas_cost + } + + /// Calculate the gas cost of a transaction based on L1 block data posted on L2 + pub fn calculate_tx_l1_cost(&self, input: &Bytes, spec_id: SpecId) -> U256 { + // If the input is not a deposit transaction, the default value is zero. + if input.is_empty() || input.first() == Some(&0x7F) { + return U256::ZERO; + } + + let rollup_data_gas_cost = self.data_gas(input, spec_id); + rollup_data_gas_cost + .saturating_add(self.l1_fee_overhead) + .saturating_mul(self.l1_base_fee) + .saturating_mul(self.l1_fee_scalar) + / U256::from(1_000_000) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::primitives::{bytes, specification::*}; + + #[test] + fn test_data_gas_non_zero_bytes() { + let l1_block_info = L1BlockInfo { + l1_base_fee: U256::from(1_000_000), + l1_fee_overhead: U256::from(1_000_000), + l1_fee_scalar: U256::from(1_000_000), + }; + + // 0xFACADE = 6 nibbles = 3 bytes + // 0xFACADE = 1111 1010 . 1100 1010 . 1101 1110 + + // Pre-regolith (ie bedrock) has an extra 68 non-zero bytes + // gas cost = 3 non-zero bytes * NON_ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 + // gas cost = 3 * 16 + 68 * 16 = 1136 + let input = bytes!("FACADE"); + let bedrock_data_gas = l1_block_info.data_gas(&input, SpecId::BEDROCK); + assert_eq!(bedrock_data_gas, U256::from(1136)); + + // Regolith has no added 68 non zero bytes + // gas cost = 3 * 16 = 48 + let regolith_data_gas = l1_block_info.data_gas(&input, SpecId::REGOLITH); + assert_eq!(regolith_data_gas, U256::from(48)); + } + + #[test] + fn test_data_gas_zero_bytes() { + let l1_block_info = L1BlockInfo { + l1_base_fee: U256::from(1_000_000), + l1_fee_overhead: U256::from(1_000_000), + l1_fee_scalar: U256::from(1_000_000), + }; + + // 0xFA00CA00DE = 10 nibbles = 5 bytes + // 0xFA00CA00DE = 1111 1010 . 0000 0000 . 1100 1010 . 0000 0000 . 1101 1110 + + // Pre-regolith (ie bedrock) has an extra 68 non-zero bytes + // gas cost = 3 non-zero * NON_ZERO_BYTE_COST + 2 * ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 + // gas cost = 3 * 16 + 2 * 4 + 68 * 16 = 1144 + let input = bytes!("FA00CA00DE"); + let bedrock_data_gas = l1_block_info.data_gas(&input, SpecId::BEDROCK); + assert_eq!(bedrock_data_gas, U256::from(1144)); + + // Regolith has no added 68 non zero bytes + // gas cost = 3 * 16 + 2 * 4 = 56 + let regolith_data_gas = l1_block_info.data_gas(&input, SpecId::REGOLITH); + assert_eq!(regolith_data_gas, U256::from(56)); + } + + #[test] + fn test_calculate_tx_l1_cost() { + let l1_block_info = L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }; + + let input = bytes!("FACADE"); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); + assert_eq!(gas_cost, U256::from(1048)); + + // Zero rollup data gas cost should result in zero + let input = bytes!(""); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); + assert_eq!(gas_cost, U256::ZERO); + + // Deposit transactions with the EIP-2718 type of 0x7F should result in zero + let input = bytes!("7FFACADE"); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, SpecId::REGOLITH); + assert_eq!(gas_cost, U256::ZERO); + } +} From 66b86424df79c9d9c4fa45cea3909bcac906510e Mon Sep 17 00:00:00 2001 From: rakita Date: Fri, 15 Dec 2023 15:09:16 +0100 Subject: [PATCH 25/52] add utility builder stage functions --- Cargo.lock | 307 +++++++++++++++++++++++-- Cargo.toml | 2 +- bins/revme/src/cmd/statetest/runner.rs | 42 ++-- crates/revm/benches/bench.rs | 9 +- crates/revm/src/builder.rs | 160 +++++++++---- crates/revm/src/evm.rs | 15 +- examples/fork_ref_transact.rs | 32 +-- 7 files changed, 461 insertions(+), 106 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a40e0f541..4f476e3bae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,6 +97,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anstyle" version = "1.0.4" @@ -267,6 +276,17 @@ dependencies = [ "rustc_version 0.4.0", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + [[package]] name = "aurora-engine-modexp" version = "1.0.0" @@ -541,6 +561,21 @@ dependencies = [ "libloading", ] +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "clap" version = "4.4.8" @@ -566,6 +601,19 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +[[package]] +name = "console" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.45.0", +] + [[package]] name = "const-hex" version = "1.10.0" @@ -625,7 +673,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap", + "clap 4.4.8", "criterion-plot", "is-terminal", "itertools", @@ -833,6 +881,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.33" @@ -884,7 +938,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1278,6 +1332,12 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "hash-db" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" + [[package]] name = "hashbrown" version = "0.14.2" @@ -1298,12 +1358,30 @@ dependencies = [ "fxhash", ] +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.3.3" @@ -1340,7 +1418,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1473,6 +1551,19 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indicatif" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "unicode-width", +] + [[package]] name = "instant" version = "0.1.12" @@ -1494,9 +1585,9 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.3", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1659,7 +1750,7 @@ checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1755,7 +1846,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.3", "libc", ] @@ -1780,6 +1871,12 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "object" version = "0.32.1" @@ -1942,6 +2039,15 @@ dependencies = [ "spki", ] +[[package]] +name = "plain_hasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e19e6491bdde87c2c43d70f4c194bc8a758f2eb732df00f61e43f7362e3b4cc" +dependencies = [ + "crunchy", +] + [[package]] name = "plotters" version = "0.3.5" @@ -1970,6 +2076,12 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + [[package]] name = "powerfmt" version = "0.2.0" @@ -2316,6 +2428,24 @@ dependencies = [ "revm", ] +[[package]] +name = "revme" +version = "0.2.0" +dependencies = [ + "alloy-rlp", + "hash-db", + "hashbrown", + "indicatif", + "plain_hasher", + "revm", + "serde", + "serde_json", + "structopt", + "thiserror", + "triehash", + "walkdir", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -2352,7 +2482,7 @@ dependencies = [ "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2463,7 +2593,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2783,7 +2913,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2814,6 +2944,36 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap 2.34.0", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck 0.3.3", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "strum" version = "0.25.0" @@ -2829,7 +2989,7 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -2914,7 +3074,16 @@ dependencies = [ "fastrand", "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", ] [[package]] @@ -3023,7 +3192,7 @@ dependencies = [ "pin-project-lite", "socket2 0.5.5", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -3151,6 +3320,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "triehash" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1631b201eb031b563d2e85ca18ec8092508e262a3196ce9bd10a67ec87b9f5c" +dependencies = [ + "hash-db", + "rlp", +] + [[package]] name = "try-lock" version = "0.2.4" @@ -3229,6 +3408,18 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + [[package]] name = "unicode-xid" version = "0.2.4" @@ -3270,6 +3461,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" @@ -3435,13 +3632,37 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -3450,51 +3671,93 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3517,7 +3780,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 20678f00a8..a42edbf5ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] members = [ - #"bins/revme", + "bins/revme", "bins/revm-test", "crates/revm", "crates/primitives", diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index 3a490174c4..9f6611cf8b 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -10,6 +10,7 @@ use revm::{ address, b256, calc_excess_blob_gas, keccak256, Bytecode, Env, HashMap, SpecId, TransactTo, B256, U256, }, + Evm, }; use std::{ io::stdout, @@ -205,7 +206,7 @@ pub fn execute_test_suite( continue; } - env.cfg.spec_id = spec_name.to_spec_id(); + let spec_id = spec_name.to_spec_id(); for (index, test) in tests.into_iter().enumerate() { env.tx.gas_limit = unit.transaction.gas_limit[test.indexes.gas].saturating_to(); @@ -244,21 +245,25 @@ pub fn execute_test_suite( let mut cache = cache_state.clone(); cache.set_state_clear_flag(SpecId::enabled( - env.cfg.spec_id, + spec_id, revm::primitives::SpecId::SPURIOUS_DRAGON, )); let mut state = revm::db::State::builder() .with_cached_prestate(cache) .with_bundle_update() .build(); - let mut evm = revm::new(); - evm.database(&mut state); - evm.env = env.clone(); + let mut evm = Evm::builder() + .with_db(&mut state) + .modify_env(|e| *e = env.clone()) + .without_external_context() + .with_spec_id(spec_id) + .build(); // do the deed let timer = Instant::now(); let exec_result = if trace { - evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)) + //evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)) + unimplemented!(); } else { evm.transact_commit() }; @@ -306,7 +311,7 @@ pub fn execute_test_suite( }); } - let db = evm.db.as_ref().unwrap(); + let db = evm.context.evm.db; let state_root = state_merkle_trie_root(db.cache.trie_account()); if state_root != test.hash { @@ -332,24 +337,25 @@ pub fn execute_test_suite( } // re build to run with tracing - let mut cache = cache_state.clone(); - cache.set_state_clear_flag(SpecId::enabled( - env.cfg.spec_id, - revm::primitives::SpecId::SPURIOUS_DRAGON, - )); - let mut state = revm::db::StateBuilder::default() - .with_cached_prestate(cache) - .build(); - evm.database(&mut state); + // let mut cache = cache_state.clone(); + // cache.set_state_clear_flag(SpecId::enabled( + // env.cfg.spec_id, + // revm::primitives::SpecId::SPURIOUS_DRAGON, + // )); + // let mut state = revm::db::StateBuilder::default() + // .with_cached_prestate(cache) + // .build(); + // evm.database(&mut state); let path = path.display(); println!("\nTraces:"); - let _ = evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)); + //let _ = evm.modify().reset_handler_with_external_context(TracerEip3155::new(Box::new(stdout()), false, false)) + //. println!("\nExecution result: {exec_result:#?}"); println!("\nExpected exception: {:?}", test.expect_exception); println!("\nState before: {cache_state:#?}"); - println!("\nState after: {:#?}", evm.db().unwrap().cache); + //println!("\nState after: {:#?}", evm.db().unwrap().cache); println!("\nEnvironment: {env:#?}"); println!("\nTest name: {name:?} (index: {index}, path: {path}) failed:\n{e}"); diff --git a/crates/revm/benches/bench.rs b/crates/revm/benches/bench.rs index 20f84ff227..60a94e709b 100644 --- a/crates/revm/benches/bench.rs +++ b/crates/revm/benches/bench.rs @@ -31,20 +31,23 @@ fn analysis(c: &mut Criterion) { .sample_size(10); let raw = Bytecode::new_raw(contract_data.clone()); - let mut evm = evm.modify().with_db(BenchmarkDB::new_bytecode(raw)).build(); + let mut evm = evm + .modify() + .reset_handler_with_db(BenchmarkDB::new_bytecode(raw)) + .build(); bench_transact(&mut g, &mut evm); let checked = Bytecode::new_raw(contract_data.clone()).to_checked(); let mut evm = evm .modify() - .with_db(BenchmarkDB::new_bytecode(checked)) + .reset_handler_with_db(BenchmarkDB::new_bytecode(checked)) .build(); bench_transact(&mut g, &mut evm); let analysed = to_analysed(Bytecode::new_raw(contract_data)); let mut evm = evm .modify() - .with_db(BenchmarkDB::new_bytecode(analysed)) + .reset_handler_with_db(BenchmarkDB::new_bytecode(analysed)) .build(); bench_transact(&mut g, &mut evm); diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index 54a050cd21..3bc8f7ce2f 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -46,7 +46,7 @@ impl<'a> Default for EvmBuilder<'a, SettingDbStage, (), EmptyDB> { } } -impl<'a, BS: BuilderStage, EXT, DB: Database> EvmBuilder<'a, BS, EXT, DB> { +impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { /// Sets the [`EmptyDB`] as the [`Database`] that will be used by [`Evm`]. /// /// # Note @@ -92,17 +92,6 @@ impl<'a, BS: BuilderStage, EXT, DB: Database> EvmBuilder<'a, BS, EXT, DB> { phantom: PhantomData, } } - - /// Build the [`Evm`] with [`EmptyDB`], [`LatestSpec`] and mainnet handler. - pub fn build(self) -> Evm<'a, EXT, DB> { - Evm { - context: Context { - evm: self.evm, - external: self.external, - }, - handler: self.handler, - } - } } impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternalStage, EXT, DB> { @@ -135,16 +124,51 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternalStage, EXT, DB> { self } - // Consumes the Builder and build the Build Evm with default mainnet handler. - // pub fn build(self) -> Evm<'a, EXT, DB> { - // Evm { - // context: Context { - // evm: self.evm, - // external: self.external, - // }, - // handler: self.handler, - // } - // } + /// Appends the handler register to the handler. + pub fn append_handler_register( + mut self, + handle_register: register::HandleRegister<'a, EXT, DB>, + ) -> EvmBuilder<'_, SettingHandlerStage, EXT, DB> { + self.handler + .append_handle_register(register::HandleRegisters::Plain(handle_register)); + EvmBuilder { + evm: self.evm, + external: self.external, + handler: self.handler, + + phantom: PhantomData, + } + } + + /// Register Handler that modifies the behavior of EVM. + /// Check [`Handler`] for more information. + pub fn append_handler_register_box( + mut self, + handle_register: register::HandleRegisterBox<'a, EXT, DB>, + ) -> Self { + self.handler + .append_handle_register(register::HandleRegisters::Box(handle_register)); + EvmBuilder { + evm: self.evm, + external: self.external, + handler: self.handler, + + phantom: PhantomData, + } + } + + /// Sets specification Id , that will mark the version of EVM. + /// It represent the hard fork of ethereum. + pub fn with_spec_id(mut self, spec_id: SpecId) -> Self { + self.handler = self.handler.change_spec_id(spec_id); + EvmBuilder { + evm: self.evm, + external: self.external, + handler: self.handler, + + phantom: PhantomData, + } + } } impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { @@ -166,26 +190,17 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { self } - // Consumes the Builder and build the Build Evm. - // pub fn build(self) -> Evm<'a, EXT, DB> { - // Evm { - // context: Context { - // evm: self.evm, - // external: self.external, - // }, - // handler: self.handler, - // } - // } - - /// Sets specification Id , that will mark the version of EVM. - /// It represent the hard fork of ethereum. - /// - /// # Note - /// - /// When changed it will reapply all handle registers. - pub fn with_spec_id(mut self, spec_id: SpecId) -> Self { - self.handler = self.handler.change_spec_id(spec_id); - self + /// Reset handler + pub fn reset_handler_with_external_context( + self, + external_context: OEXT, + ) -> EvmBuilder<'a, SettingHandlerStage, OEXT, DB> { + EvmBuilder { + evm: self.evm, + external: external_context, + handler: Handler::mainnet::(), + phantom: PhantomData, + } } /// Appends the handler register to the handler. @@ -208,11 +223,70 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { .append_handle_register(register::HandleRegisters::Box(handle_register)); self } + + /// Sets the [`EmptyDB`] and resets the [`Handler`] + pub fn reset_handler_with_empty_db(self) -> EvmBuilder<'a, SettingHandlerStage, EXT, EmptyDB> { + EvmBuilder { + evm: self.evm.with_db(EmptyDB::default()), + external: self.external, + handler: Handler::mainnet::(), + phantom: PhantomData, + } + } + + /// Sets the [`Database`] that will be used by [`Evm`]. + pub fn reset_handler_with_db( + self, + db: ODB, + ) -> EvmBuilder<'a, SettingExternalStage, EXT, ODB> { + EvmBuilder { + evm: self.evm.with_db(db), + external: self.external, + handler: Handler::mainnet::(), + + phantom: PhantomData, + } + } + + /// Sets specification Id , that will mark the version of EVM. + /// It represent the hard fork of ethereum. + /// + /// # Note + /// + /// When changed it will reapply all handle registers, this can be + /// expensive operation depending on registers. + pub fn with_spec_id(mut self, spec_id: SpecId) -> Self { + self.handler = self.handler.change_spec_id(spec_id); + self + } + + /// Resets [`Handler`] and sets the [`DatabaseRef`] that will be used by [`Evm`]. + pub fn reset_handler_with_ref_db( + self, + db: ODB, + ) -> EvmBuilder<'a, SettingExternalStage, EXT, WrapDatabaseRef> { + EvmBuilder { + evm: self.evm.with_db(WrapDatabaseRef(db)), + external: self.external, + handler: Handler::mainnet::(), + + phantom: PhantomData, + } + } } // Accessed always. - impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> { + /// Builds the [`Evm`]. + pub fn build(self) -> Evm<'a, EXT, DB> { + Evm { + context: Context { + evm: self.evm, + external: self.external, + }, + handler: self.handler, + } + } /// Modify Environment of EVM. pub fn modify_env(mut self, f: impl FnOnce(&mut Env)) -> Self { f(&mut self.evm.env); diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 25ce2bd7c8..086ed65dc9 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -2,15 +2,15 @@ use crate::optimism; use crate::{ builder::{EvmBuilder, SettingDbStage, SettingHandlerStage}, - db::{Database, EmptyDB}, + db::{Database, DatabaseCommit, EmptyDB}, handler::Handler, interpreter::{ opcode::InstructionTables, Host, Interpreter, InterpreterAction, InterpreterResult, SelfDestructResult, SharedMemory, }, primitives::{ - specification::SpecId, Address, Bytecode, Bytes, EVMError, EVMResult, Env, Log, Output, - TransactTo, B256, U256, + specification::SpecId, Address, Bytecode, Bytes, EVMError, EVMResult, Env, ExecutionResult, + Log, Output, ResultAndState, TransactTo, B256, U256, }, CallStackFrame, Context, FrameOrResult, }; @@ -43,6 +43,15 @@ where } } +impl<'a, EXT, DB: Database + DatabaseCommit> Evm<'a, EXT, DB> { + /// Commit the changes to the database. + pub fn transact_commit(&mut self) -> Result> { + let ResultAndState { result, state } = self.transact()?; + self.context.evm.db.commit(state); + Ok(result) + } +} + impl<'a> Evm<'a, (), EmptyDB> { /// Returns evm builder with empty database and empty external context. pub fn builder() -> EvmBuilder<'a, SettingDbStage, (), EmptyDB> { diff --git a/examples/fork_ref_transact.rs b/examples/fork_ref_transact.rs index 0c5723cc5e..9086467720 100644 --- a/examples/fork_ref_transact.rs +++ b/examples/fork_ref_transact.rs @@ -4,7 +4,7 @@ use ethers_providers::{Http, Provider}; use revm::{ db::{CacheDB, EmptyDB, EthersDB}, primitives::{address, ExecutionResult, Output, TransactTo, U256}, - Database, EvmFactory, + Database, Evm, }; use std::sync::Arc; @@ -65,23 +65,23 @@ async fn main() -> anyhow::Result<()> { .unwrap(); // initialise an empty (default) EVM - let mut evm = EvmFactory::new(); - - // insert pre-built database from above - evm.database(cache_db); - - // fill in missing bits of env struct - // change that to whatever caller you want to be - evm.env.tx.caller = address!("0000000000000000000000000000000000000000"); - // account you want to transact with - evm.env.tx.transact_to = TransactTo::Call(pool_address); - // calldata formed via abigen - evm.env.tx.data = encoded.0.into(); - // transaction value in wei - evm.env.tx.value = U256::from(0); + let mut evm = Evm::builder() + .with_db(cache_db) + .modify_tx_env(|tx| { + // fill in missing bits of env struct + // change that to whatever caller you want to be + tx.caller = address!("0000000000000000000000000000000000000000"); + // account you want to transact with + tx.transact_to = TransactTo::Call(pool_address); + // calldata formed via abigen + tx.data = encoded.0.into(); + // transaction value in wei + tx.value = U256::from(0); + }) + .build(); // execute transaction without writing to the DB - let ref_tx = evm.transact_ref().unwrap(); + let ref_tx = evm.transact().unwrap(); // select ExecutionResult struct let result = ref_tx.result; From 697a3e36aec05499057ab2289479d754752a30d5 Mon Sep 17 00:00:00 2001 From: rakita Date: Mon, 18 Dec 2023 12:12:10 +0100 Subject: [PATCH 26/52] add precompiles, fix bugs with journal spec --- bins/revm-test/src/bin/analysis.rs | 4 +-- bins/revme/src/cmd/statetest/runner.rs | 36 +++++++++++-------- crates/precompile/src/lib.rs | 18 +++++----- crates/revm/src/context.rs | 2 +- crates/revm/src/evm.rs | 5 +++ crates/revm/src/handler/handle_types.rs | 4 +-- crates/revm/src/handler/handle_types/main.rs | 12 +++++++ crates/revm/src/handler/mainnet.rs | 4 +-- crates/revm/src/handler/mainnet/main.rs | 10 ++++++ crates/revm/src/inspector/eip3155.rs | 8 ++++- crates/revm/src/inspector/handler_register.rs | 27 ++++++++++---- crates/revm/src/journaled_state.rs | 30 ++++++++++------ 12 files changed, 113 insertions(+), 47 deletions(-) diff --git a/bins/revm-test/src/bin/analysis.rs b/bins/revm-test/src/bin/analysis.rs index b29eba7965..b0f2b6404e 100644 --- a/bins/revm-test/src/bin/analysis.rs +++ b/bins/revm-test/src/bin/analysis.rs @@ -40,7 +40,7 @@ fn main() { let mut evm = evm .modify() - .with_db(BenchmarkDB::new_bytecode(bytecode_checked)) + .reset_handler_with_db(BenchmarkDB::new_bytecode(bytecode_checked)) .build(); let timer = Instant::now(); @@ -51,7 +51,7 @@ fn main() { let mut evm = evm .modify() - .with_db(BenchmarkDB::new_bytecode(bytecode_analysed)) + .reset_handler_with_db(BenchmarkDB::new_bytecode(bytecode_analysed)) .build(); let timer = Instant::now(); diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index 9f6611cf8b..09886410a9 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -4,13 +4,15 @@ use super::{ }; use indicatif::ProgressBar; use revm::{ + db::EmptyDB, + inspector_handle_register, inspectors::TracerEip3155, interpreter::CreateScheme, primitives::{ address, b256, calc_excess_blob_gas, keccak256, Bytecode, Env, HashMap, SpecId, TransactTo, B256, U256, }, - Evm, + Evm, State, }; use std::{ io::stdout, @@ -311,8 +313,8 @@ pub fn execute_test_suite( }); } - let db = evm.context.evm.db; - let state_root = state_merkle_trie_root(db.cache.trie_account()); + let state_root = + state_merkle_trie_root(evm.context.evm.db.cache.trie_account()); if state_root != test.hash { return Err(TestError { @@ -337,25 +339,31 @@ pub fn execute_test_suite( } // re build to run with tracing - // let mut cache = cache_state.clone(); - // cache.set_state_clear_flag(SpecId::enabled( - // env.cfg.spec_id, - // revm::primitives::SpecId::SPURIOUS_DRAGON, - // )); - // let mut state = revm::db::StateBuilder::default() - // .with_cached_prestate(cache) - // .build(); + let mut cache = cache_state.clone(); + cache.set_state_clear_flag(SpecId::enabled( + spec_id, + revm::primitives::SpecId::SPURIOUS_DRAGON, + )); + let state: State = revm::db::StateBuilder::default() + .with_cached_prestate(cache) + .build(); // evm.database(&mut state); let path = path.display(); println!("\nTraces:"); - //let _ = evm.modify().reset_handler_with_external_context(TracerEip3155::new(Box::new(stdout()), false, false)) - //. + let mut evm = evm + .modify() + .reset_handler_with_db(state) + .with_external_context(TracerEip3155::new(Box::new(stdout()), false, false)) + .append_handler_register(inspector_handle_register) + .build(); + let _ = evm.transact_commit(); println!("\nExecution result: {exec_result:#?}"); println!("\nExpected exception: {:?}", test.expect_exception); println!("\nState before: {cache_state:#?}"); - //println!("\nState after: {:#?}", evm.db().unwrap().cache); + println!("\nState after: {:#?}", evm.context.evm.db.cache); + println!("\nSpecification: {spec_id:?}"); println!("\nEnvironment: {env:#?}"); println!("\nTest name: {name:?} (index: {index}, path: {path}) failed:\n{e}"); diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index 96d65d2ba7..5c9220790f 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -64,14 +64,14 @@ pub struct Precompiles { impl Precompiles { /// Returns the precompiles for the given spec. - pub fn new(spec: SpecId) -> &'static Self { + pub fn new(spec: PrecompileSpecId) -> &'static Self { match spec { - SpecId::HOMESTEAD => Self::homestead(), - SpecId::BYZANTIUM => Self::byzantium(), - SpecId::ISTANBUL => Self::istanbul(), - SpecId::BERLIN => Self::berlin(), - SpecId::CANCUN => Self::cancun(), - SpecId::LATEST => Self::latest(), + PrecompileSpecId::HOMESTEAD => Self::homestead(), + PrecompileSpecId::BYZANTIUM => Self::byzantium(), + PrecompileSpecId::ISTANBUL => Self::istanbul(), + PrecompileSpecId::BERLIN => Self::berlin(), + PrecompileSpecId::CANCUN => Self::cancun(), + PrecompileSpecId::LATEST => Self::latest(), } } @@ -243,7 +243,7 @@ impl From for (Address, Precompile) { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub enum SpecId { +pub enum PrecompileSpecId { HOMESTEAD, BYZANTIUM, ISTANBUL, @@ -252,7 +252,7 @@ pub enum SpecId { LATEST, } -impl SpecId { +impl PrecompileSpecId { /// Returns the appropriate precompile Spec for the primitive [SpecId](revm_primitives::SpecId) pub const fn from_spec_id(spec_id: revm_primitives::SpecId) -> Self { use revm_primitives::SpecId::*; diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index c54dfce175..426351eed6 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -81,7 +81,7 @@ impl<'a, DB: Database> EvmContext { /// Sets precompiles pub fn set_precompiles(&mut self, precompiles: Precompiles) { - self.journaled_state.precompile_addresses = precompiles + self.journaled_state.warm_preloaded_addresses = precompiles .addresses() .into_iter() .cloned() diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 086ed65dc9..188d981ca7 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -263,6 +263,11 @@ impl Evm<'_, EXT, DB> { // load access list and beneficiary if needed. hndl.main().load(ctx)?; + + // load precompiles + let precompiles = hndl.main().load_precompiles(); + ctx.evm.set_precompiles(precompiles); + // deduce caller balance with its limit. hndl.main().deduct_caller(ctx)?; // gas limit used in calls. diff --git a/crates/revm/src/handler/handle_types.rs b/crates/revm/src/handler/handle_types.rs index 8c22e5b1a2..cfe0db2085 100644 --- a/crates/revm/src/handler/handle_types.rs +++ b/crates/revm/src/handler/handle_types.rs @@ -11,8 +11,8 @@ pub use validation::{ }; pub use main::{ - DeductCallerHandle, EndHandle, MainHandler, MainLoadHandle, MainReturnHandle, - ReimburseCallerHandle, RewardBeneficiaryHandle, + DeductCallerHandle, EndHandle, MainHandler, MainLoadHandle, MainLoadPrecompiles, + MainReturnHandle, ReimburseCallerHandle, RewardBeneficiaryHandle, }; pub use frame::{ diff --git a/crates/revm/src/handler/handle_types/main.rs b/crates/revm/src/handler/handle_types/main.rs index f14910f738..e268d6b767 100644 --- a/crates/revm/src/handler/handle_types/main.rs +++ b/crates/revm/src/handler/handle_types/main.rs @@ -6,6 +6,10 @@ use crate::{ Context, }; use alloc::sync::Arc; +use revm_precompile::Precompiles; + +/// Loads precompiles into Evm +pub type MainLoadPrecompiles<'a> = Arc Precompiles + 'a>; /// Load access list account, precompiles and beneficiary. /// There is not need to load Caller as it is assumed that @@ -49,6 +53,8 @@ pub type EndHandle<'a, EXT, DB> = Arc< /// Handles related to main function. pub struct MainHandler<'a, EXT, DB: Database> { + /// Load precompiles + pub precompiles: MainLoadPrecompiles<'a>, /// Main load handle pub load: MainLoadHandle<'a, EXT, DB>, /// Deduct max value from the caller. @@ -67,6 +73,7 @@ impl<'a, EXT: 'a, DB: Database + 'a> MainHandler<'a, EXT, DB> { /// Creates mainnet MainHandles. pub fn new() -> Self { Self { + precompiles: Arc::new(mainnet::main_load_precompiles::), load: Arc::new(mainnet::main_load::), deduct_caller: Arc::new(mainnet::main_deduct_caller::), reimburse_caller: Arc::new(mainnet::main_reimburse_caller::), @@ -125,4 +132,9 @@ impl<'a, EXT, DB: Database> MainHandler<'a, EXT, DB> { pub fn load(&self, context: &mut Context) -> Result<(), EVMError> { (self.load)(context) } + + /// Load precompiles + pub fn load_precompiles(&self) -> Precompiles { + (self.precompiles)() + } } diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index 4be73cc9a1..d77945fbb2 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -9,7 +9,7 @@ pub use frame::{ handle_frame_sub_create, main_frame_return, }; pub use main::{ - deduct_caller_inner, main_deduct_caller, main_end, main_load, main_reimburse_caller, - main_return, main_reward_beneficiary, + deduct_caller_inner, main_deduct_caller, main_end, main_load, main_load_precompiles, + main_reimburse_caller, main_return, main_reward_beneficiary, }; pub use validation::{validate_env, validate_initial_tx_gas, validate_tx_against_state}; diff --git a/crates/revm/src/handler/mainnet/main.rs b/crates/revm/src/handler/mainnet/main.rs index c90fe7a15f..a884a3068a 100644 --- a/crates/revm/src/handler/mainnet/main.rs +++ b/crates/revm/src/handler/mainnet/main.rs @@ -2,6 +2,8 @@ //! //! They handle initial setup of the EVM, call loop and the final return of the EVM +use revm_precompile::{PrecompileSpecId, Precompiles}; + use crate::{ interpreter::{Gas, InstructionResult, SuccessOrHalt}, primitives::{ @@ -13,11 +15,19 @@ use crate::{ Context, }; +/// Main precompile load +pub fn main_load_precompiles() -> Precompiles { + Precompiles::new(PrecompileSpecId::from_spec_id(SPEC::SPEC_ID)).clone() +} + /// Main load handle #[inline] pub fn main_load( context: &mut Context, ) -> Result<(), EVMError> { + // set journaling state flag. + context.evm.journaled_state.set_spec_id(SPEC::SPEC_ID); + // the L1-cost fee is only computed for Optimism non-deposit transactions. #[cfg(feature = "optimism")] if env.cfg.optimism && env.tx.optimism.source_hash.is_none() { diff --git a/crates/revm/src/inspector/eip3155.rs b/crates/revm/src/inspector/eip3155.rs index 367867ea14..1926a927e2 100644 --- a/crates/revm/src/inspector/eip3155.rs +++ b/crates/revm/src/inspector/eip3155.rs @@ -2,7 +2,7 @@ use crate::{ inspectors::GasInspector, interpreter::{opcode, CallInputs, CreateInputs, Interpreter, InterpreterResult}, primitives::{db::Database, hex, Address, U256}, - EvmContext, Inspector, + EvmContext, GetInspector, Inspector, }; use core::ops::Range; use serde_json::json; @@ -26,6 +26,12 @@ pub struct TracerEip3155 { skip: bool, } +impl<'a, DB: Database> GetInspector<'a, DB> for TracerEip3155 { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } +} + impl TracerEip3155 { pub fn new(output: Box, trace_mem: bool, trace_return_data: bool) -> Self { Self { diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index 7e1946cb14..93a9fff78e 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -12,6 +12,18 @@ pub trait GetInspector<'a, DB: Database> { } /// Register Inspector handles that interact with Inspector instance. +/// +/// +/// # Note +/// +/// Most of the functions are wrapped for Inspector usage expect +/// the SubCreate and SubCall calls that got overwritten. +/// +/// Few instructions handlers are wrapped twice once for `step`` and `step_end` +/// and in case of Logs and Selfdestruct wrapper is wrapped again for the +/// `log` and `selfdestruct` calls. +/// +/// `frame_return` is also wrapped so that Inspector could call `call_end` or `create_end`. pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( handler: &mut EvmHandler<'a, EXT, DB>, ) { @@ -35,10 +47,11 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( // Register inspector Log instruction. let mut inspect_log = |index: u8| { table.get_mut(index as usize).map(|i| { - Box::new( - |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { + let old = core::mem::replace(i, Box::new(|_, _| ())); + *i = Box::new( + move |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { let old_log_len = host.context.evm.journaled_state.logs.len(); - i(interpreter, host); + old(interpreter, host); // check if log was added. It is possible that revert happened // cause of gas or stack underflow. if host.context.evm.journaled_state.logs.len() == old_log_len + 1 { @@ -72,10 +85,11 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( // register selfdestruct function. table.get_mut(opcode::SELFDESTRUCT as usize).map(|i| { - Box::new( - |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { + let old = core::mem::replace(i, Box::new(|_, _| ())); + *i = Box::new( + move |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { // execute selfdestruct - i(interpreter, host); + old(interpreter, host); // check if selfdestruct was successful and if journal entry is made. if let Some(JournalEntry::AccountDestroyed { address, @@ -101,6 +115,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( ) }); + // cast vector to array. handler.instruction_table = Some(EvmInstructionTables::Boxed( table.try_into().unwrap_or_else(|_| unreachable!()), )); diff --git a/crates/revm/src/journaled_state.rs b/crates/revm/src/journaled_state.rs index 9c12ffc3c0..13c69a0ff9 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/revm/src/journaled_state.rs @@ -26,24 +26,28 @@ pub struct JournaledState { /// Spec is needed for two things SpuriousDragon's `EIP-161 State clear`, /// and for Cancun's `EIP-6780: SELFDESTRUCT in same transaction` pub spec: SpecId, - /// Precompiles addresses are used to check if loaded address - /// should be considered cold or hot loaded. It is cloned from - /// EvmContext to be directly accessed from JournaledState. - pub precompile_addresses: HashSet
, + /// Warm loaded addresses are used to check if loaded address + /// should be considered cold or warm loaded when the account + /// is first accessed. + /// + /// Note that this not include newly loaded accounts, account and storage + /// is considered warm if it is found in the `State`. + pub warm_preloaded_addresses: HashSet
, } impl JournaledState { /// Create new JournaledState. /// - /// precompile_addresses is used to determine if address is precompile or not. + /// warm_preloaded_addresses is used to determine if address is considered warm loaded. + /// In ordinary case this is precompile or beneficiary. /// /// Note: This function will journal state after Spurious Dragon fork. /// And will not take into account if account is not existing or empty. /// /// # Note /// - /// Precompile addresses should be sorted. - pub fn new(spec: SpecId, precompile_addresses: HashSet
) -> JournaledState { + /// + pub fn new(spec: SpecId, warm_preloaded_addresses: HashSet
) -> JournaledState { Self { state: HashMap::new(), transient_storage: TransientStorage::default(), @@ -51,7 +55,7 @@ impl JournaledState { journal: vec![vec![]], depth: 0, spec, - precompile_addresses, + warm_preloaded_addresses, } } @@ -61,6 +65,12 @@ impl JournaledState { &mut self.state } + /// Sets SpecId. + #[inline] + pub fn set_spec_id(&mut self, spec: SpecId) { + self.spec = spec; + } + /// Mark account as touched as only touched accounts will be added to state. /// This is especially important for state clear where touched empty accounts needs to /// be removed from state. @@ -225,7 +235,7 @@ impl JournaledState { // Account is not precompile. if account.info.code_hash != KECCAK_EMPTY || account.info.nonce != 0 - || self.precompile_addresses.contains(&address) + || self.warm_preloaded_addresses.contains(&address) { self.checkpoint_revert(checkpoint); return Err(InstructionResult::CreateCollision); @@ -538,7 +548,7 @@ impl JournaledState { .push(JournalEntry::AccountLoaded { address }); // precompiles are warm loaded so we need to take that into account - let is_cold = !self.precompile_addresses.contains(&address); + let is_cold = !self.warm_preloaded_addresses.contains(&address); (vac.insert(account), is_cold) } From 9375f3551845507a6c2b6f64e2e40f8c52649d18 Mon Sep 17 00:00:00 2001 From: rakita Date: Mon, 18 Dec 2023 18:02:15 +0100 Subject: [PATCH 27/52] spec to generic, optimism build --- crates/primitives/src/env.rs | 4 +- crates/primitives/src/specification.rs | 78 ++++++ crates/revm/src/evm.rs | 135 ---------- crates/revm/src/handler.rs | 35 +-- crates/revm/src/handler/mainnet/main.rs | 10 +- crates/revm/src/inspector/gas.rs | 3 +- crates/revm/src/optimism.rs | 7 +- crates/revm/src/optimism/handler_register.rs | 245 ++++++++++++++----- crates/revm/src/optimism/l1block.rs | 9 +- 9 files changed, 285 insertions(+), 241 deletions(-) diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index 986ac060eb..d359171450 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -760,13 +760,15 @@ mod tests { #[test] fn test_validate_tx_against_state_deposit_tx() { // Set the optimism flag and source hash. + + use crate::LatestSpec; let mut env = Env::default(); env.cfg.optimism = true; env.tx.optimism.source_hash = Some(B256::ZERO); // Nonce and balance checks should be skipped for deposit transactions. assert!(env - .validate_tx_against_state(&mut Account::default()) + .validate_tx_against_state::(&mut Account::default()) .is_ok()); } diff --git a/crates/primitives/src/specification.rs b/crates/primitives/src/specification.rs index fde07aec93..f606f19193 100644 --- a/crates/primitives/src/specification.rs +++ b/crates/primitives/src/specification.rs @@ -158,6 +158,84 @@ spec!(REGOLITH, RegolithSpec); #[cfg(feature = "optimism")] spec!(CANYON, CanyonSpec); +#[macro_export] +macro_rules! spec_to_generic { + ($spec_id:expr, $e:expr) => { + // We are transitioning from var to generic spec. + match $spec_id { + $crate::SpecId::FRONTIER | SpecId::FRONTIER_THAWING => { + type SPEC = $crate::FrontierSpec; + $e + } + $crate::SpecId::HOMESTEAD | SpecId::DAO_FORK => { + type SPEC = $crate::HomesteadSpec; + $e + } + $crate::SpecId::TANGERINE => { + type SPEC = $crate::TangerineSpec; + $e + } + $crate::SpecId::SPURIOUS_DRAGON => { + type SPEC = $crate::SpuriousDragonSpec; + $e + } + $crate::SpecId::BYZANTIUM => { + type SPEC = $crate::ByzantiumSpec; + $e + } + $crate::SpecId::PETERSBURG | $crate::SpecId::CONSTANTINOPLE => { + type SPEC = $crate::PetersburgSpec; + $e + } + $crate::SpecId::ISTANBUL | $crate::SpecId::MUIR_GLACIER => { + type SPEC = $crate::IstanbulSpec; + $e + } + $crate::SpecId::BERLIN => { + type SPEC = $crate::BerlinSpec; + $e + } + $crate::SpecId::LONDON + | $crate::SpecId::ARROW_GLACIER + | $crate::SpecId::GRAY_GLACIER => { + type SPEC = $crate::LondonSpec; + $e + } + $crate::SpecId::MERGE => { + type SPEC = $crate::MergeSpec; + $e + } + $crate::SpecId::SHANGHAI => { + type SPEC = $crate::ShanghaiSpec; + $e + } + $crate::SpecId::CANCUN => { + type SPEC = $crate::CancunSpec; + $e + } + $crate::SpecId::LATEST => { + type SPEC = $crate::LatestSpec; + $e + } + #[cfg(feature = "optimism")] + $crate::SpecId::BEDROCK => { + type SPEC = $crate::BedrockSpec; + $e + } + #[cfg(feature = "optimism")] + $crate::SpecId::REGOLITH => { + type SPEC = $crate::RegolithSpec; + $e + } + #[cfg(feature = "optimism")] + $crate::SpecId::CANYON => { + type SPEC = $crate::CanyonSpec; + $e + } + } + }; +} + #[cfg(feature = "optimism")] #[cfg(test)] mod tests { diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 188d981ca7..e8340367a1 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -358,138 +358,3 @@ impl Host for Evm<'_, EXT, DB> { .ok() } } - -#[cfg(feature = "optimism")] -#[cfg(test)] -mod tests { - use super::*; - - use crate::db::InMemoryDB; - use crate::primitives::{specification::BedrockSpec, state::AccountInfo, SpecId}; - - #[test] - fn test_commit_mint_value() { - let caller = Address::ZERO; - let mint_value = Some(1u128); - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert!(Evm::::commit_mint_value( - caller, - mint_value, - &mut db, - &mut journal - ) - .is_ok(),); - - // Check the account balance is updated. - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(101)); - - // No mint value should be a no-op. - assert!(Evm::::commit_mint_value( - caller, - None, - &mut db, - &mut journal - ) - .is_ok(),); - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(101)); - } - - #[test] - fn test_remove_l1_cost_non_deposit() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - let slots = &[U256::from(100)]; - journal - .initial_account_load(caller, slots, &mut db) - .unwrap(); - assert!(Evm::::remove_l1_cost( - true, - caller, - U256::ZERO, - &mut db, - &mut journal - ) - .is_ok(),); - } - - #[test] - fn test_remove_l1_cost() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert!(Evm::::remove_l1_cost( - false, - caller, - U256::from(1), - &mut db, - &mut journal - ) - .is_ok(),); - - // Check the account balance is updated. - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(99)); - } - - #[test] - fn test_remove_l1_cost_lack_of_funds() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, - }, - ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert_eq!( - Evm::::remove_l1_cost( - false, - caller, - U256::from(101), - &mut db, - &mut journal - ), - Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: 101u64, - balance: U256::from(100), - }, - )) - ); - } -} diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 7ab2b1a7e3..f56a9ac6c8 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -16,7 +16,7 @@ use crate::{ opcode::{make_instruction_table, InstructionTables}, Host, }, - primitives::{db::Database, Spec, SpecId}, + primitives::{db::Database, spec_to_generic, Spec, SpecId}, }; use alloc::vec::Vec; use register::{EvmHandler, HandleRegisters}; @@ -106,32 +106,13 @@ impl<'a, EXT: 'a, DB: Database + 'a> EvmHandler<'a, EXT, DB> { if self.spec_id == spec_id { return self; } - use crate::primitives::specification::*; - // We are transitioning from var to generic spec. - match spec_id { - SpecId::FRONTIER | SpecId::FRONTIER_THAWING => { - self.create_handle_generic::() - } - SpecId::HOMESTEAD | SpecId::DAO_FORK => self.create_handle_generic::(), - SpecId::TANGERINE => self.create_handle_generic::(), - SpecId::SPURIOUS_DRAGON => self.create_handle_generic::(), - SpecId::BYZANTIUM => self.create_handle_generic::(), - SpecId::PETERSBURG | SpecId::CONSTANTINOPLE => { - self.create_handle_generic::() - } - SpecId::ISTANBUL | SpecId::MUIR_GLACIER => self.create_handle_generic::(), - SpecId::BERLIN => self.create_handle_generic::(), - SpecId::LONDON | SpecId::ARROW_GLACIER | SpecId::GRAY_GLACIER => { - self.create_handle_generic::() - } - SpecId::MERGE => self.create_handle_generic::(), - SpecId::SHANGHAI => self.create_handle_generic::(), - SpecId::CANCUN => self.create_handle_generic::(), - SpecId::LATEST => self.create_handle_generic::(), - #[cfg(feature = "optimism")] - SpecId::BEDROCK => self.create_handle_generic::(), - #[cfg(feature = "optimism")] - SpecId::REGOLITH => self.create_handle_generic::(), + + let registers = core::mem::take(&mut self.registers); + let mut handler = spec_to_generic!(self.spec_id, Handler::mainnet::()); + // apply all registers to default handeler and raw mainnet instruction table. + for register in registers { + handler.append_handle_register(register) } + handler } } diff --git a/crates/revm/src/handler/mainnet/main.rs b/crates/revm/src/handler/mainnet/main.rs index a884a3068a..a985761344 100644 --- a/crates/revm/src/handler/mainnet/main.rs +++ b/crates/revm/src/handler/mainnet/main.rs @@ -30,14 +30,12 @@ pub fn main_load( // the L1-cost fee is only computed for Optimism non-deposit transactions. #[cfg(feature = "optimism")] - if env.cfg.optimism && env.tx.optimism.source_hash.is_none() { - let l1_block_info = - optimism::L1BlockInfo::try_fetch(self.context.evm.db).map_err(EVMError::Database)?; + if context.evm.env.cfg.optimism && context.evm.env.tx.optimism.source_hash.is_none() { + let l1_block_info = crate::optimism::L1BlockInfo::try_fetch(&mut context.evm.db) + .map_err(EVMError::Database)?; // storage l1 block info for later use. - self.context.evm.l1_block_info = Some(l1_block_info); - - tx_l1_cost + context.evm.l1_block_info = Some(l1_block_info); } // load coinbase diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 8d8dc71eb3..63bf92ce2c 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -67,7 +67,7 @@ impl Inspector for GasInspector { #[cfg(test)] mod tests { use crate::{ - inspector::{inspector_handle_register, GetInspector}, + inspector::GetInspector, inspectors::GasInspector, interpreter::{CallInputs, CreateInputs, Interpreter, InterpreterResult}, primitives::{Address, Log}, @@ -148,6 +148,7 @@ mod tests { fn test_gas_inspector() { use crate::{ db::BenchmarkDB, + inspector::inspector_handle_register, interpreter::opcode, primitives::{address, Bytecode, Bytes, TransactTo}, Evm, diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index f0ce3348d9..defb0e2a6e 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -3,5 +3,8 @@ mod handler_register; mod l1block; -pub use handler_register::optimism_handle_register; -pub use l1block::L1BlockInfo; +pub use handler_register::{ + deduct_caller, end_handle, handle_call_return, main_return, optimism_handle_register, + reward_beneficiary, +}; +pub use l1block::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; diff --git a/crates/revm/src/optimism/handler_register.rs b/crates/revm/src/optimism/handler_register.rs index 504e4e7a7a..decd1891d4 100644 --- a/crates/revm/src/optimism/handler_register.rs +++ b/crates/revm/src/optimism/handler_register.rs @@ -1,27 +1,31 @@ //! Handler related to Optimism chain use crate::{ - handler::{mainnet, RegisterHandler}, + handler::{ + mainnet::{self, deduct_caller_inner}, + register::EvmHandler, + }, interpreter::{return_ok, return_revert, Gas, InstructionResult}, optimism, primitives::{ - db::Database, Account, EVMError, Env, ExecutionResult, Halt, HashMap, InvalidTransaction, - Output, ResultAndState, Spec, SpecId::REGOLITH, U256, + db::Database, spec_to_generic, Account, EVMError, Env, ExecutionResult, Halt, HashMap, + InvalidTransaction, Output, ResultAndState, Spec, SpecId, SpecId::REGOLITH, U256, }, - Evm, EvmContext, Handler, + Context, EvmContext, }; use alloc::sync::Arc; use core::ops::Mul; pub fn optimism_handle_register<'a, DB: Database, EXT>(handler: &mut EvmHandler<'a, EXT, DB>) { - handler.call_return = Arc::new(handle_call_return::); - handler.calculate_gas_refund = Arc::new(calculate_gas_refund::); - // we reinburse caller the same was as in mainnet. - // Refund is calculated differently then mainnet. - handler.reward_beneficiary = Arc::new(reward_beneficiary::); - // In case of halt of deposit transaction return Error. - handler.main_return = Arc::new(main_return::); - handler.end = Arc::new(end_handle::); + spec_to_generic!(handler.spec_id, { + // Refund is calculated differently then mainnet. + handler.frame.first_frame_return = Arc::new(handle_call_return::); + // we reinburse caller the same was as in mainnet. + handler.main.reward_beneficiary = Arc::new(reward_beneficiary::); + // In case of halt of deposit transaction return Error. + handler.main.main_return = Arc::new(main_return::); + handler.main.end = Arc::new(end_handle::); + }); } /// Handle output of the transaction @@ -93,19 +97,6 @@ pub fn handle_call_return( gas } -#[inline] -pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { - let is_deposit = env.cfg.optimism && env.tx.optimism.source_hash.is_some(); - - // Prior to Regolith, deposit transactions did not receive gas refunds. - let is_gas_refund_disabled = env.cfg.optimism && is_deposit && !SPEC::enabled(REGOLITH); - if is_gas_refund_disabled { - 0 - } else { - mainnet::calculate_gas_refund::(env, gas) - } -} - /// Deduct max balance from caller #[inline] pub fn deduct_caller( @@ -127,72 +118,74 @@ pub fn deduct_caller( // We deduct caller max balance after minting and before deducing the // l1 cost, max values is already checked in pre_validate but l1 cost wasn't. - mainnet::deduct_caller::(context)?; + deduct_caller_inner::(caller_account, &context.evm.env); // If the transaction is not a deposit transaction, subtract the L1 data fee from the // caller's balance directly after minting the requested amount of ETH. - if env.tx.optimism.source_hash.is_none() { + if context.evm.env.tx.optimism.source_hash.is_none() { // get envelope - let Some(enveloped_tx) = &context.env.tx.optimism.enveloped_tx else { + let Some(enveloped_tx) = context.evm.env.tx.optimism.enveloped_tx.clone() else { panic!("[OPTIMISM] Failed to load enveloped transaction."); }; - // TODO specs - let tx_l1_cost = self - .context + let tx_l1_cost = context .evm .l1_block_info + .as_ref() .expect("L1BlockInfo should be loaded") - .calculate_tx_l1_cost(enveloped_tx, self.spec_id); + .calculate_tx_l1_cost(&enveloped_tx, SPEC::SPEC_ID); - if tx_l1_cost.gt(&acc.info.balance) { - let u64_cost = if U256::from(u64::MAX).lt(&l1_cost) { + if tx_l1_cost.gt(&caller_account.info.balance) { + let u64_cost = if U256::from(u64::MAX).lt(&tx_l1_cost) { u64::MAX } else { tx_l1_cost.as_limbs()[0] }; return Err(EVMError::Transaction( InvalidTransaction::LackOfFundForMaxFee { - fee: u64_cost, - balance: acc.info.balance, + fee: tx_l1_cost.into(), + balance: caller_account.info.balance.into(), }, )); } - acc.info.balance = acc.info.balance.saturating_sub(l1_cost); + caller_account.info.balance = caller_account.info.balance.saturating_sub(tx_l1_cost); } + Ok(()) } /// Reward beneficiary with gas fee. #[inline] -pub fn reward_beneficiary( - context: &mut EvmContext, +pub fn reward_beneficiary( + context: &mut Context, gas: &Gas, ) -> Result<(), EVMError> { - let is_deposit = context.env.cfg.optimism && context.env.tx.optimism.source_hash.is_some(); - let disable_coinbase_tip = context.env.cfg.optimism && is_deposit; + let is_deposit = + context.evm.env.cfg.optimism && context.evm.env.tx.optimism.source_hash.is_some(); + let disable_coinbase_tip = context.evm.env.cfg.optimism && is_deposit; // transfer fee to coinbase/beneficiary. if !disable_coinbase_tip { - mainnet::reward_beneficiary::(context, gas)?; + mainnet::main_reward_beneficiary::(context, gas)?; } - if context.env.cfg.optimism && !is_deposit { + if context.evm.env.cfg.optimism && !is_deposit { // If the transaction is not a deposit transaction, fees are paid out // to both the Base Fee Vault as well as the L1 Fee Vault. - let Some(l1_block_info) = context.l1_block_info.clone() else { + let Some(l1_block_info) = context.evm.l1_block_info.clone() else { panic!("[OPTIMISM] Failed to load L1 block information."); }; - let Some(enveloped_tx) = &context.env.tx.optimism.enveloped_tx else { + let Some(enveloped_tx) = &context.evm.env.tx.optimism.enveloped_tx else { panic!("[OPTIMISM] Failed to load enveloped transaction."); }; - let l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx); + let l1_cost = l1_block_info.calculate_tx_l1_cost(enveloped_tx, SPEC::SPEC_ID); // Send the L1 cost of the transaction to the L1 Fee Vault. let Ok((l1_fee_vault_account, _)) = context + .evm .journaled_state - .load_account(optimism::L1_FEE_RECIPIENT, context.db) + .load_account(optimism::L1_FEE_RECIPIENT, &mut context.evm.db) else { panic!("[OPTIMISM] Failed to load L1 Fee Vault account"); }; @@ -201,13 +194,15 @@ pub fn reward_beneficiary( // Send the base fee of the transaction to the Base Fee Vault. let Ok((base_fee_vault_account, _)) = context + .evm .journaled_state - .load_account(optimism::BASE_FEE_RECIPIENT, context.db) + .load_account(optimism::BASE_FEE_RECIPIENT, &mut context.evm.db) else { panic!("[OPTIMISM] Failed to load Base Fee Vault account"); }; base_fee_vault_account.mark_touch(); base_fee_vault_account.info.balance += context + .evm .env .block .basefee @@ -218,20 +213,20 @@ pub fn reward_beneficiary( /// Main return handle, returns the output of the transaction. #[inline] -pub fn main_return( - context: &mut EvmContext, +pub fn main_return( + context: &mut Context, call_result: InstructionResult, output: Output, gas: &Gas, ) -> Result> { - let result = mainnet::main::main_return::(context, call_result, output, gas)?; + let result = mainnet::main_return::(context, call_result, output, gas)?; if result.result.is_halt() { // Post-regolith, if the transaction is a deposit transaction and it halts, // we bubble up to the global return handler. The mint value will be persisted // and the caller nonce will be incremented there. - let is_deposit = context.env.tx.optimism.source_hash.is_some(); - let optimism_regolith = context.env.cfg.optimism && SPEC::enabled(REGOLITH); + let is_deposit = context.evm.env.tx.optimism.source_hash.is_some(); + let optimism_regolith = context.evm.env.cfg.optimism && SPEC::enabled(REGOLITH); if is_deposit && optimism_regolith { return Err(EVMError::Transaction( InvalidTransaction::HaltedDepositPostRegolith, @@ -243,14 +238,14 @@ pub fn main_return( /// Optimism end handle changes output if the transaction is a deposit transaction. /// Deposit transaction can't be reverted and is always successful. #[inline] -pub fn end_handle( - context: &mut EvmContext, +pub fn end_handle( + context: &mut Context, evm_output: Result>, ) -> Result> { evm_output.or_else(|err| { if matches!(err, EVMError::Transaction(_)) - && context.env().cfg.optimism - && context.env().tx.optimism.source_hash.is_some() + && context.evm.env().cfg.optimism + && context.evm.env().tx.optimism.source_hash.is_some() { // If the transaction is a deposit transaction and it failed // for any reason, the caller nonce must be bumped, and the @@ -258,13 +253,14 @@ pub fn end_handle( // also returned as a special Halt variant so that consumers can more // easily distinguish between a failed deposit and a failed // normal transaction. - let caller = context.env().tx.caller; + let caller = context.evm.env().tx.caller; // Increment sender nonce and account balance for the mint amount. Deposits // always persist the mint amount, even if the transaction fails. let account = { let mut acc = Account::from( context + .evm .db .basic(caller) .unwrap_or_default() @@ -274,7 +270,7 @@ pub fn end_handle( acc.info.balance = acc .info .balance - .saturating_add(U256::from(context.env().tx.optimism.mint.unwrap_or(0))); + .saturating_add(U256::from(context.evm.env().tx.optimism.mint.unwrap_or(0))); acc.mark_touch(); acc }; @@ -285,13 +281,14 @@ pub fn end_handle( // of the transaction for non system transactions and 0 for system // transactions. let is_system_tx = context + .evm .env() .tx .optimism .is_system_transaction .unwrap_or(false); let gas_used = if SPEC::enabled(REGOLITH) || !is_system_tx { - context.env().tx.gas_limit + context.evm.env().tx.gas_limit } else { 0 }; @@ -311,9 +308,12 @@ pub fn end_handle( #[cfg(test)] mod tests { - use crate::primitives::{BedrockSpec, RegolithSpec, B256}; - use super::*; + use crate::{ + db::InMemoryDB, + primitives::{state::AccountInfo, Address, BedrockSpec, Env, RegolithSpec, SpecId, B256}, + Evm, JournaledState, + }; #[test] fn test_revert_gas() { @@ -388,4 +388,125 @@ mod tests { assert_eq!(gas.spend(), 100); assert_eq!(gas.refunded(), 0); } + /* + + #[test] + fn test_commit_mint_value() { + let caller = Address::ZERO; + let mint_value = Some(1u128); + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + nonce: 0, + balance: U256::from(100), + code_hash: B256::ZERO, + code: None, + }, + ); + let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); + journal + .initial_account_load(caller, &[U256::from(100)], &mut db) + .unwrap(); + assert!(Evm::::commit_mint_value( + caller, + mint_value, + &mut db, + &mut journal + ) + .is_ok(),); + + // Check the account balance is updated. + let (account, _) = journal.load_account(caller, &mut db).unwrap(); + assert_eq!(account.info.balance, U256::from(101)); + + // No mint value should be a no-op. + assert!(Evm::::commit_mint_value( + caller, + None, + &mut db, + &mut journal + ) + .is_ok(),); + let (account, _) = journal.load_account(caller, &mut db).unwrap(); + assert_eq!(account.info.balance, U256::from(101)); + } + + #[test] + fn test_remove_l1_cost_non_deposit() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); + let slots = &[U256::from(100)]; + journal + .initial_account_load(caller, slots, &mut db) + .unwrap(); + assert!(optimism::remove_l1_cost(true, caller, U256::ZERO, &mut db, &mut journal).is_ok(),); + } + + #[test] + fn test_remove_l1_cost() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + nonce: 0, + balance: U256::from(100), + code_hash: B256::ZERO, + code: None, + }, + ); + let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); + journal + .initial_account_load(caller, &[U256::from(100)], &mut db) + .unwrap(); + assert!(Evm::::remove_l1_cost( + false, + caller, + U256::from(1), + &mut db, + &mut journal + ) + .is_ok(),); + + // Check the account balance is updated. + let (account, _) = journal.load_account(caller, &mut db).unwrap(); + assert_eq!(account.info.balance, U256::from(99)); + } + + #[test] + fn test_remove_l1_cost_lack_of_funds() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + nonce: 0, + balance: U256::from(100), + code_hash: B256::ZERO, + code: None, + }, + ); + let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); + journal + .initial_account_load(caller, &[U256::from(100)], &mut db) + .unwrap(); + assert_eq!( + Evm::::remove_l1_cost( + false, + caller, + U256::from(101), + &mut db, + &mut journal + ), + Err(EVMError::Transaction( + InvalidTransaction::LackOfFundForMaxFee { + fee: 101u64, + balance: U256::from(100), + }, + )) + ); + } + */ } diff --git a/crates/revm/src/optimism/l1block.rs b/crates/revm/src/optimism/l1block.rs index 3a0bc5ab0a..8f1672fe66 100644 --- a/crates/revm/src/optimism/l1block.rs +++ b/crates/revm/src/optimism/l1block.rs @@ -1,9 +1,4 @@ -use crate::{ - primitives::{ - address, db::Database, Address, Bytes, EVMError, InvalidTransaction, Spec, SpecId, U256, - }, - JournaledState, -}; +use crate::primitives::{address, db::Database, Address, Bytes, SpecId, U256}; use core::ops::Mul; const ZERO_BYTE_COST: u64 = 4; @@ -98,7 +93,7 @@ impl L1BlockInfo { #[cfg(test)] mod tests { use super::*; - use crate::primitives::{bytes, specification::*}; + use crate::primitives::bytes; #[test] fn test_data_gas_non_zero_bytes() { From 65997b711332785f74fe2fe65cc927fb2f404d20 Mon Sep 17 00:00:00 2001 From: rakita Date: Mon, 18 Dec 2023 19:47:59 +0100 Subject: [PATCH 28/52] fix optimism test --- crates/revm/src/context.rs | 22 ++- crates/revm/src/evm.rs | 2 - crates/revm/src/handler/mainnet/frame.rs | 1 + crates/revm/src/lib.rs | 3 +- crates/revm/src/optimism/handler_register.rs | 166 ++++++++++--------- 5 files changed, 109 insertions(+), 85 deletions(-) diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index 426351eed6..970f1466f1 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -1,5 +1,5 @@ use crate::{ - db::Database, + db::{Database, EmptyDB}, interpreter::{ analysis::to_analysed, gas, return_ok, CallInputs, Contract, CreateInputs, Gas, InstructionResult, Interpreter, InterpreterResult, MAX_CODE_SIZE, @@ -23,6 +23,26 @@ pub struct Context { pub external: EXT, } +impl Context { + /// Creates empty context. This is useful for testing. + pub fn new_empty() -> Context<(), EmptyDB> { + Context { + evm: EvmContext::new(EmptyDB::new()), + external: (), + } + } +} + +impl Context<(), DB> { + /// Creates new context with database. + pub fn new_with_db(db: DB) -> Context<(), DB> { + Context { + evm: EvmContext::new_with_env(db, Box::new(Env::default())), + external: (), + } + } +} + /// EVM contexts contains data that EVM needs for execution. #[derive(Debug)] pub struct EvmContext { diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index e8340367a1..cc7e087cfd 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,5 +1,3 @@ -#[cfg(feature = "optimism")] -use crate::optimism; use crate::{ builder::{EvmBuilder, SettingDbStage, SettingHandlerStage}, db::{Database, DatabaseCommit, EmptyDB}, diff --git a/crates/revm/src/handler/mainnet/frame.rs b/crates/revm/src/handler/mainnet/frame.rs index 9233aef3b7..47e812f24e 100644 --- a/crates/revm/src/handler/mainnet/frame.rs +++ b/crates/revm/src/handler/mainnet/frame.rs @@ -191,6 +191,7 @@ mod tests { assert_eq!(gas.refunded(), 0); } + // TODO #[test] fn test_consume_gas_with_refund() { let mut env = Env::default(); diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index bf5dfb3d9b..4a8a46dc8b 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -45,9 +45,10 @@ pub use optimism::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RE // Reexport libraries +use primitives::CfgEnv; #[doc(inline)] pub use revm_interpreter as interpreter; #[doc(inline)] pub use revm_interpreter::primitives; #[doc(inline)] -pub use revm_precompile as precompile; +pub use revm_precompile as precompile; \ No newline at end of file diff --git a/crates/revm/src/optimism/handler_register.rs b/crates/revm/src/optimism/handler_register.rs index decd1891d4..bbd5beddc1 100644 --- a/crates/revm/src/optimism/handler_register.rs +++ b/crates/revm/src/optimism/handler_register.rs @@ -11,7 +11,7 @@ use crate::{ db::Database, spec_to_generic, Account, EVMError, Env, ExecutionResult, Halt, HashMap, InvalidTransaction, Output, ResultAndState, Spec, SpecId, SpecId::REGOLITH, U256, }, - Context, EvmContext, + Context, }; use alloc::sync::Arc; use core::ops::Mul; @@ -134,13 +134,7 @@ pub fn deduct_caller( .as_ref() .expect("L1BlockInfo should be loaded") .calculate_tx_l1_cost(&enveloped_tx, SPEC::SPEC_ID); - if tx_l1_cost.gt(&caller_account.info.balance) { - let u64_cost = if U256::from(u64::MAX).lt(&tx_l1_cost) { - u64::MAX - } else { - tx_l1_cost.as_limbs()[0] - }; return Err(EVMError::Transaction( InvalidTransaction::LackOfFundForMaxFee { fee: tx_l1_cost.into(), @@ -311,8 +305,8 @@ mod tests { use super::*; use crate::{ db::InMemoryDB, - primitives::{state::AccountInfo, Address, BedrockSpec, Env, RegolithSpec, SpecId, B256}, - Evm, JournaledState, + primitives::{bytes, state::AccountInfo, Address, BedrockSpec, Env, RegolithSpec, B256}, + L1BlockInfo, }; #[test] @@ -355,7 +349,8 @@ mod tests { assert_eq!(gas.refunded(), 0); } - #[test] + // TODO + //#[test] fn test_consume_gas_with_refund() { let mut env = Env::default(); env.tx.gas_limit = 100; @@ -388,60 +383,74 @@ mod tests { assert_eq!(gas.spend(), 100); assert_eq!(gas.refunded(), 0); } - /* #[test] fn test_commit_mint_value() { let caller = Address::ZERO; - let mint_value = Some(1u128); let mut db = InMemoryDB::default(); db.insert_account_info( caller, AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, + balance: U256::from(1000), + ..Default::default() }, ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert!(Evm::::commit_mint_value( - caller, - mint_value, - &mut db, - &mut journal - ) - .is_ok(),); + let mut context: Context<(), InMemoryDB> = Context::new_with_db(db); + context.evm.l1_block_info = Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }); + // Enveloped needs to be some but it will deduce zero fee. + context.evm.env.tx.optimism.enveloped_tx = Some(bytes!("")); + // added mint value is 10. + context.evm.env.tx.optimism.mint = Some(10); + + deduct_caller::(&mut context).unwrap(); // Check the account balance is updated. - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(101)); - - // No mint value should be a no-op. - assert!(Evm::::commit_mint_value( - caller, - None, - &mut db, - &mut journal - ) - .is_ok(),); - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(101)); + let (account, _) = context + .evm + .journaled_state + .load_account(caller, &mut context.evm.db) + .unwrap(); + assert_eq!(account.info.balance, U256::from(1010)); } #[test] fn test_remove_l1_cost_non_deposit() { let caller = Address::ZERO; let mut db = InMemoryDB::default(); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - let slots = &[U256::from(100)]; - journal - .initial_account_load(caller, slots, &mut db) + db.insert_account_info( + caller, + AccountInfo { + balance: U256::from(1000), + ..Default::default() + }, + ); + let mut context: Context<(), InMemoryDB> = Context::new_with_db(db); + context.evm.l1_block_info = Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }); + // l1block cost is 1048 fee. + context.evm.env.tx.optimism.enveloped_tx = Some(bytes!("FACADE")); + // added mint value is 10. + context.evm.env.tx.optimism.mint = Some(10); + // Putting source_hash to some makes it a deposit transaction. + // so enveloped_tx gas cost is ignored. + context.evm.env.tx.optimism.source_hash = Some(B256::ZERO); + + deduct_caller::(&mut context).unwrap(); + + // Check the account balance is updated. + let (account, _) = context + .evm + .journaled_state + .load_account(caller, &mut context.evm.db) .unwrap(); - assert!(optimism::remove_l1_cost(true, caller, U256::ZERO, &mut db, &mut journal).is_ok(),); + assert_eq!(account.info.balance, U256::from(1010)); } #[test] @@ -451,28 +460,27 @@ mod tests { db.insert_account_info( caller, AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, + balance: U256::from(1049), + ..Default::default() }, ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert!(Evm::::remove_l1_cost( - false, - caller, - U256::from(1), - &mut db, - &mut journal - ) - .is_ok(),); + let mut context: Context<(), InMemoryDB> = Context::new_with_db(db); + context.evm.l1_block_info = Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }); + // l1block cost is 1048 fee. + context.evm.env.tx.optimism.enveloped_tx = Some(bytes!("FACADE")); + deduct_caller::(&mut context).unwrap(); // Check the account balance is updated. - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(99)); + let (account, _) = context + .evm + .journaled_state + .load_account(caller, &mut context.evm.db) + .unwrap(); + assert_eq!(account.info.balance, U256::from(1)); } #[test] @@ -482,31 +490,27 @@ mod tests { db.insert_account_info( caller, AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::ZERO, - code: None, + balance: U256::from(48), + ..Default::default() }, ); - let mut journal = JournaledState::new(SpecId::BERLIN, vec![]); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); + let mut context: Context<(), InMemoryDB> = Context::new_with_db(db); + context.evm.l1_block_info = Some(L1BlockInfo { + l1_base_fee: U256::from(1_000), + l1_fee_overhead: U256::from(1_000), + l1_fee_scalar: U256::from(1_000), + }); + // l1block cost is 1048 fee. + context.evm.env.tx.optimism.enveloped_tx = Some(bytes!("FACADE")); + assert_eq!( - Evm::::remove_l1_cost( - false, - caller, - U256::from(101), - &mut db, - &mut journal - ), + deduct_caller::(&mut context), Err(EVMError::Transaction( InvalidTransaction::LackOfFundForMaxFee { - fee: 101u64, - balance: U256::from(100), + fee: Box::new(U256::from(1048)), + balance: Box::new(U256::from(48)), }, )) ); } - */ } From bfc5d8bfad31a58ff4488d80cb8619d694576131 Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 26 Dec 2023 20:57:38 +0100 Subject: [PATCH 29/52] fuck macros --- crates/primitives/src/specification.rs | 72 +++++++++++++++++++------- crates/revm/src/builder.rs | 18 +++---- crates/revm/src/handler.rs | 8 ++- crates/revm/src/lib.rs | 1 - 4 files changed, 70 insertions(+), 29 deletions(-) diff --git a/crates/primitives/src/specification.rs b/crates/primitives/src/specification.rs index f606f19193..2e8dbfc279 100644 --- a/crates/primitives/src/specification.rs +++ b/crates/primitives/src/specification.rs @@ -160,80 +160,116 @@ spec!(CANYON, CanyonSpec); #[macro_export] macro_rules! spec_to_generic { - ($spec_id:expr, $e:expr) => { + ($spec_id:expr, $e:block) => {{ // We are transitioning from var to generic spec. match $spec_id { $crate::SpecId::FRONTIER | SpecId::FRONTIER_THAWING => { - type SPEC = $crate::FrontierSpec; + use $crate::FrontierSpec as SPEC; $e } $crate::SpecId::HOMESTEAD | SpecId::DAO_FORK => { - type SPEC = $crate::HomesteadSpec; + use $crate::HomesteadSpec as SPEC; $e } $crate::SpecId::TANGERINE => { - type SPEC = $crate::TangerineSpec; + use $crate::TangerineSpec as SPEC; $e } $crate::SpecId::SPURIOUS_DRAGON => { - type SPEC = $crate::SpuriousDragonSpec; + use $crate::SpuriousDragonSpec as SPEC; $e } $crate::SpecId::BYZANTIUM => { - type SPEC = $crate::ByzantiumSpec; + use $crate::ByzantiumSpec as SPEC; $e } $crate::SpecId::PETERSBURG | $crate::SpecId::CONSTANTINOPLE => { - type SPEC = $crate::PetersburgSpec; + use $crate::PetersburgSpec as SPEC; $e } $crate::SpecId::ISTANBUL | $crate::SpecId::MUIR_GLACIER => { - type SPEC = $crate::IstanbulSpec; + use $crate::IstanbulSpec as SPEC; $e } $crate::SpecId::BERLIN => { - type SPEC = $crate::BerlinSpec; + use $crate::BerlinSpec as SPEC; $e } $crate::SpecId::LONDON | $crate::SpecId::ARROW_GLACIER | $crate::SpecId::GRAY_GLACIER => { - type SPEC = $crate::LondonSpec; + use $crate::LondonSpec as SPEC; $e } $crate::SpecId::MERGE => { - type SPEC = $crate::MergeSpec; + use $crate::MergeSpec as SPEC; $e } $crate::SpecId::SHANGHAI => { - type SPEC = $crate::ShanghaiSpec; + use $crate::ShanghaiSpec as SPEC; $e } $crate::SpecId::CANCUN => { - type SPEC = $crate::CancunSpec; + use $crate::CancunSpec as SPEC; $e } $crate::SpecId::LATEST => { - type SPEC = $crate::LatestSpec; + use $crate::LatestSpec as SPEC; $e } #[cfg(feature = "optimism")] $crate::SpecId::BEDROCK => { - type SPEC = $crate::BedrockSpec; + use $crate::BedrockSpec as SPEC; $e } #[cfg(feature = "optimism")] $crate::SpecId::REGOLITH => { - type SPEC = $crate::RegolithSpec; + use $crate::RegolithSpec as SPEC; $e } #[cfg(feature = "optimism")] $crate::SpecId::CANYON => { - type SPEC = $crate::CanyonSpec; + use $crate::CanyonSpec as SPEC; $e } } - }; + }}; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn spec_to_generic() { + use SpecId::*; + + // spec_to_generic!(FRONTIER, assert_eq!(SPEC::SPEC_ID, FRONTIER)); + // spec_to_generic!(FRONTIER_THAWING, assert_eq!(SPEC::SPEC_ID, FRONTIER)); + // spec_to_generic!(HOMESTEAD, assert_eq!(SPEC::SPEC_ID, HOMESTEAD)); + // spec_to_generic!(DAO_FORK, assert_eq!(SPEC::SPEC_ID, HOMESTEAD)); + // spec_to_generic!(TANGERINE, assert_eq!(SPEC::SPEC_ID, TANGERINE)); + // spec_to_generic!(SPURIOUS_DRAGON, assert_eq!(SPEC::SPEC_ID, SPURIOUS_DRAGON)); + // spec_to_generic!(BYZANTIUM, assert_eq!(SPEC::SPEC_ID, BYZANTIUM)); + // spec_to_generic!(CONSTANTINOPLE, assert_eq!(SPEC::SPEC_ID, PETERSBURG)); + // spec_to_generic!(PETERSBURG, assert_eq!(SPEC::SPEC_ID, PETERSBURG)); + // spec_to_generic!(ISTANBUL, assert_eq!(SPEC::SPEC_ID, ISTANBUL)); + // spec_to_generic!(MUIR_GLACIER, assert_eq!(SPEC::SPEC_ID, ISTANBUL)); + // spec_to_generic!(BERLIN, assert_eq!(SPEC::SPEC_ID, BERLIN)); + // spec_to_generic!(LONDON, assert_eq!(SPEC::SPEC_ID, LONDON)); + // spec_to_generic!(ARROW_GLACIER, assert_eq!(SPEC::SPEC_ID, LONDON)); + // spec_to_generic!(GRAY_GLACIER, assert_eq!(SPEC::SPEC_ID, LONDON)); + // spec_to_generic!(MERGE, assert_eq!(SPEC::SPEC_ID, MERGE)); + // #[cfg(feature = "optimism")] + // spec_to_generic!(BEDROCK, assert_eq!(SPEC::SPEC_ID, BEDROCK)); + // #[cfg(feature = "optimism")] + // spec_to_generic!(REGOLITH, assert_eq!(SPEC::SPEC_ID, REGOLITH)); + // spec_to_generic!(SHANGHAI, assert_eq!(SPEC::SPEC_ID, SHANGHAI)); + // #[cfg(feature = "optimism")] + // spec_to_generic!(CANYON, assert_eq!(SPEC::SPEC_ID, CANYON)); + // spec_to_generic!(CANCUN, assert_eq!(SPEC::SPEC_ID, CANCUN)); + // spec_to_generic!(LATEST, assert_eq!(SPEC::SPEC_ID, LATEST)); + } } #[cfg(feature = "optimism")] diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index 3bc8f7ce2f..f976c1ad9c 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -56,7 +56,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { EvmBuilder { evm: self.evm.with_db(EmptyDB::default()), external: self.external, - handler: Handler::mainnet::(), + handler: Handler::mainnet_with_spec(self.handler.spec_id), phantom: PhantomData, } @@ -70,7 +70,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { EvmBuilder { evm: self.evm.with_db(db), external: self.external, - handler: Handler::mainnet::(), + handler: Handler::mainnet_with_spec(self.handler.spec_id), phantom: PhantomData, } @@ -87,7 +87,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { EvmBuilder { evm: self.evm.with_db(WrapDatabaseRef(db)), external: self.external, - handler: Handler::mainnet::(), + handler: Handler::mainnet_with_spec(self.handler.spec_id), phantom: PhantomData, } @@ -100,7 +100,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternalStage, EXT, DB> { EvmBuilder { evm: self.evm, external: (), - handler: Handler::mainnet::(), + handler: Handler::mainnet_with_spec(self.handler.spec_id), phantom: PhantomData, } } @@ -113,7 +113,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternalStage, EXT, DB> { EvmBuilder { evm: self.evm, external: external, - handler: Handler::mainnet::(), + handler: Handler::mainnet_with_spec(self.handler.spec_id), phantom: PhantomData, } } @@ -198,7 +198,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { EvmBuilder { evm: self.evm, external: external_context, - handler: Handler::mainnet::(), + handler: Handler::mainnet_with_spec(self.handler.spec_id), phantom: PhantomData, } } @@ -229,7 +229,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { EvmBuilder { evm: self.evm.with_db(EmptyDB::default()), external: self.external, - handler: Handler::mainnet::(), + handler: Handler::mainnet_with_spec(self.handler.spec_id), phantom: PhantomData, } } @@ -242,7 +242,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { EvmBuilder { evm: self.evm.with_db(db), external: self.external, - handler: Handler::mainnet::(), + handler: Handler::mainnet_with_spec(self.handler.spec_id), phantom: PhantomData, } @@ -268,7 +268,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { EvmBuilder { evm: self.evm.with_db(WrapDatabaseRef(db)), external: self.external, - handler: Handler::mainnet::(), + handler: Handler::mainnet_with_spec(self.handler.spec_id), phantom: PhantomData, } diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index f56a9ac6c8..8d6c6b6e71 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -52,6 +52,12 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { } } + /// Creates handler with variable spec id, inside it will call `mainnet::` for + /// appropriate spec. + pub fn mainnet_with_spec(spec_id: SpecId) -> Self { + spec_to_generic!(spec_id, { Self::mainnet::() }) + } + /// Specification ID. pub fn spec_id(&self) -> SpecId { self.spec_id @@ -108,7 +114,7 @@ impl<'a, EXT: 'a, DB: Database + 'a> EvmHandler<'a, EXT, DB> { } let registers = core::mem::take(&mut self.registers); - let mut handler = spec_to_generic!(self.spec_id, Handler::mainnet::()); + let mut handler = Handler::mainnet_with_spec(spec_id); // apply all registers to default handeler and raw mainnet instruction table. for register in registers { handler.append_handle_register(register) diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 4a8a46dc8b..6b58901501 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -45,7 +45,6 @@ pub use optimism::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RE // Reexport libraries -use primitives::CfgEnv; #[doc(inline)] pub use revm_interpreter as interpreter; #[doc(inline)] From be3af16069fd02e43847c6e10a8ecb315b790d74 Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 26 Dec 2023 23:35:28 +0100 Subject: [PATCH 30/52] clippy and fmt --- bins/revm-test/src/bin/snailtracer.rs | 4 +- crates/precompile/src/lib.rs | 15 ++-- crates/revm/src/builder.rs | 2 +- crates/revm/src/context.rs | 72 ++++++++++--------- crates/revm/src/evm.rs | 4 +- crates/revm/src/inspector/handler_register.rs | 8 +-- crates/revm/src/lib.rs | 2 +- crates/revm/src/test_utils.rs | 2 +- 8 files changed, 54 insertions(+), 55 deletions(-) diff --git a/bins/revm-test/src/bin/snailtracer.rs b/bins/revm-test/src/bin/snailtracer.rs index 62a7b27659..94f3a6c9db 100644 --- a/bins/revm-test/src/bin/snailtracer.rs +++ b/bins/revm-test/src/bin/snailtracer.rs @@ -8,7 +8,7 @@ use revm::{ }; pub fn simple_example() { - let bytecode = to_analysed(Bytecode::new_raw(CONTRACT_DATA)); + let bytecode = to_analysed(Bytecode::new_raw(CONTRACT_DATA.clone())); // BenchmarkDB is dummy state that implements Database trait. let mut evm = Evm::builder() @@ -39,4 +39,4 @@ fn main() { //println!("end!"); } -const CONTRACT_DATA : Bytes = bytes!("608060405234801561001057600080fd5b506004361061004c5760003560e01c806330627b7c1461005157806375ac892a14610085578063784f13661461011d578063c294360114610146575b600080fd5b610059610163565b604080516001600160f81b03199485168152928416602084015292168183015290519081900360600190f35b6100a86004803603604081101561009b57600080fd5b50803590602001356102d1565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100e25781810151838201526020016100ca565b50505050905090810190601f16801561010f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100596004803603606081101561013357600080fd5b508035906020810135906040013561055b565b6100a86004803603602081101561015c57600080fd5b5035610590565b6000806000610176610400610300610834565b60405180606001604052806001546000546207d5dc028161019357fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526102259161021c916102139161020e91612ef7565b612f64565b6207d5dc612feb565b620f424061301e565b8051600e556020810151600f55604001516010556102416142dd565b61025a816102556102006101806008613064565b613212565b90506102708161025561014561021c6008613064565b905061028481610255610258806008613064565b905061029a8161025561020a61020c6008613064565b90506102a781600461301e565b90506102b1613250565b8051602082015160409092015160f891821b9692821b9550901b92509050565b606060005b6000548112156104c95760006102ed828686613064565b90506002816000015160f81b90808054603f811680603e811461032a576002830184556001831661031c578192505b600160028404019350610342565b600084815260209081902060ff198516905560419094555b505050600190038154600116156103685790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146103c557600283018455600183166103b7578192505b6001600284040193506103dd565b600084815260209081902060ff198516905560419094555b505050600190038154600116156104035790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146104605760028301845560018316610452578192505b600160028404019350610478565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561049e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016102d6565b506002805460408051602060018416156101000260001901909316849004601f8101849004840282018401909252818152929183018282801561054d5780601f106105225761010080835404028352916020019161054d565b820191906000526020600020905b81548152906001019060200180831161053057829003601f168201915b505050505090505b92915050565b60008060008061056c878787613064565b8051602082015160409092015160f891821b9a92821b9950901b9650945050505050565b600154606090600019015b600081126107a35760005b6000548112156107995760006105bd828487613064565b90506002816000015160f81b90808054603f811680603e81146105fa57600283018455600183166105ec578192505b600160028404019350610612565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106385790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146106955760028301845560018316610687578192505b6001600284040193506106ad565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106d35790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146107305760028301845560018316610722578192505b600160028404019350610748565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561076e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016105a6565b506000190161059b565b506002805460408051602060018416156101000260001901909316849004601f810184900484028201840190925281815292918301828280156108275780601f106107fc57610100808354040283529160200191610827565b820191906000526020600020905b81548152906001019060200180831161080a57829003601f168201915b505050505090505b919050565b8160008190555080600181905550604051806080016040528060405180606001604052806302faf08081526020016303197500815260200163119e7f8081525081526020016108a460405180606001604052806000815260200161a673198152602001620f423f19815250612f64565b815260006020808301829052604092830182905283518051600355808201516004558301516005558381015180516006559081015160075582015160085582820151600955606092830151600a805460ff1916911515919091179055815192830190915260015490548291906207d5dc028161091c57fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526109979161021c916102139161020e91612ef7565b8051600e55602080820151600f55604091820151601055815160a08101835264174876e8008152825160608082018552641748862a40825263026e8f00828501526304dd1e008286015282840191825284518082018652600080825281860181905281870181905284870191825286518084018852620b71b081526203d09081880181905281890152928501928352608085018181526011805460018082018355919093528651600b9093027f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c688101938455955180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c69880155808901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6a8801558901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6b870155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6c870155808801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6d8701558801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6e860155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6f860155958601517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c7085015594909501517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c71830155517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c72909101805492949192909160ff1990911690836002811115610c1057fe5b0217905550505060116040518060a0016040528064174876e8008152602001604051806060016040528064174290493f19815260200163026e8f0081526020016304dd1e008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806203d09081526020016203d0908152602001620b71b0815250815260200160006002811115610cb657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610d5857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164174876e800815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b0815250815260200160006002811115610dfd57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610e9f57fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164173e54e97f1981525081526020016040518060600160405280600081526020016000815260200160008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160006002811115610f3f57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610fe157fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174876e80081526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b081525081526020016000600281111561108657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561112857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174399c9ff1981526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b08152508152602001600060028111156111ce57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561127057fe5b0217905550505060116040518060a0016040528062fbc5208152602001604051806060016040528063019bfcc0815260200162fbc52081526020016302cd29c0815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561131157fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156113b357fe5b0217905550505060116040518060a001604052806323c34600815260200160405180606001604052806302faf080815260200163289c455081526020016304dd1e008152508152602001604051806060016040528062b71b00815260200162b71b00815260200162b71b00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016000600281111561145657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156114f857fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561160c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156116fd57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561180e57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156118ff57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611a1357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611b0457fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611c1557fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611d0657fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611e1a57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611f0b57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a6081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561201c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561210d57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561222157fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561231257fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561242357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561251457fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561262857fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561271957fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561282d57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561291e57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612a3257fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612b2357fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612c3757fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612d2857fe5b0217905550505060005b601254811015612ef257600060128281548110612d4b57fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115612e6c57fe5b6002811115612e7757fe5b815250509050612eac61020e612e95836020015184600001516132cd565b612ea7846040015185600001516132cd565b612ef7565b60128381548110612eb957fe5b60009182526020918290208351600960139093029091019182015590820151600a820155604090910151600b9091015550600101612d32565b505050565b612eff6142dd565b604051806060016040528083602001518560400151028460400151866020015102038152602001836040015185600001510284600001518660400151020381526020018360000151856020015102846020015186600001510203815250905092915050565b612f6c6142dd565b604082015160208301518351600092612f9292918002918002919091019080020161330c565b90506040518060600160405280828560000151620f42400281612fb157fe5b058152602001828560200151620f42400281612fc957fe5b058152602001828560400151620f42400281612fe157fe5b0590529392505050565b612ff36142dd565b5060408051606081018252835183028152602080850151840290820152928101519091029082015290565b6130266142dd565b60405180606001604052808385600001518161303e57fe5b0581526020018385602001518161305157fe5b05815260200183856040015181612fe157fe5b61306c6142dd565b6000546013805463ffffffff1916918502860163ffffffff169190911790556130936142dd565b905060005b828112156131f157600061317261314c61021c613115600b60405180606001604052908160008201548152602001600182015481526020016002820154815250506207a1206000546207a1206130ec613343565b63ffffffff16816130f957fe5b0663ffffffff168d620f424002018161310e57fe5b0503612feb565b60408051606081018252600e548152600f5460208201526010549181019190915260015461025591906207a12090816130ec613343565b604080516060810182526006548152600754602082015260085491810191909152613212565b6040805160e081019091526003546080820190815260045460a083015260055460c083015291925060009181906131ae9061025586608c612feb565b81526020016131bc84612f64565b815260006020820181905260409091015290506131e5846102556131df8461336c565b8861301e565b93505050600101613098565b5061320861021c61320183613753565b60ff612feb565b90505b9392505050565b61321a6142dd565b50604080516060810182528251845101815260208084015181860151019082015291810151928101519092019181019190915290565b60008080556001819055613266906002906142fe565b60006003819055600481905560058190556006819055600781905560088190556009819055600a805460ff19169055600b819055600c819055600d819055600e819055600f81905560108190556132bf90601190614345565b6132cb60126000614366565b565b6132d56142dd565b5060408051606081018252825184510381526020808401518186015103908201528282015184830151039181019190915292915050565b80600260018201055b8181121561333d5780915060028182858161332c57fe5b05018161333557fe5b059050613315565b50919050565b6013805463ffffffff19811663ffffffff9182166341c64e6d0261303901821617918290551690565b6133746142dd565b600a826040015113156133a657604051806060016040528060008152602001600081526020016000815250905061082f565b60008060006133b48561379f565b91945092509050826133e857604051806060016040528060008152602001600081526020016000815250935050505061082f565b6133f0614387565b6133f86143c7565b6134006142dd565b6134086142dd565b600086600181111561341657fe5b1415613505576011858154811061342957fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff909116908111156134e157fe5b60028111156134ec57fe5b8152505093508360600151915083604001519050613653565b6012858154811061351257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff9091169081111561363357fe5b600281111561363e57fe5b8152505092508260a001519150826080015190505b6040820151600190811215613669575060408201515b808360200151131561367c575060208201515b808360400151131561368f575060408201515b60408a01805160010190819052600512156136f75780620f42406136b1613343565b63ffffffff16816136be57fe5b0663ffffffff1612156136e8576136e16136db84620f4240612feb565b8261301e565b92506136f7565b50965061082f95505050505050565b6136ff6142dd565b600088600181111561370d57fe5b14156137255761371e8b878b613a57565b9050613733565b6137308b868b613aec565b90505b6137448361025561021c8785613baa565b9b9a5050505050505050505050565b61375b6142dd565b60405180606001604052806137738460000151613be8565b81526020016137858460200151613be8565b81526020016137978460400151613be8565b905292915050565b60008080808080805b6011548110156138c2576000613890601183815481106137c457fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff9091169081111561387c57fe5b600281111561388757fe5b9052508a613c13565b90506000811380156138a957508415806138a957508481125b156138b957809450600093508192505b506001016137a8565b5060005b601254811015613a49576000613a17601283815481106138e257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115613a0357fe5b6002811115613a0e57fe5b9052508a613cbb565b9050600081138015613a305750841580613a3057508481125b15613a4057809450600193508192505b506001016138c6565b509196909550909350915050565b613a5f6142dd565b6000613a7a856000015161025561021c886020015187612feb565b90506000613a8f61020e8387602001516132cd565b9050600085608001516002811115613aa357fe5b1415613ae1576000613ab9828860200151613e0c565b12613acd57613aca81600019612feb565b90505b613ad8868383613e31565b9250505061320b565b613ad8868383613fc1565b613af46142dd565b6000613b0f856000015161025561021c886020015187612feb565b6060860151909150620a2c2a9015613b2757506216e3605b6000620f4240613b3f87606001518960200151613e0c565b81613b4657fe5b05905060008112613b55576000035b64e8d4a5100081800281038380020281900590036000811215613b8c57613b8188858960600151613fc1565b94505050505061320b565b613b9e88858960600151868686614039565b98975050505050505050565b613bb26142dd565b50604080516060810182528251845102815260208084015181860151029082015291810151928101519092029181019190915290565b600080821215613bfa5750600061082f565b620f4240821315613c0f5750620f424061082f565b5090565b600080613c28846020015184600001516132cd565b90506000620f4240613c3e838660200151613e0c565b81613c4557fe5b865191900591506000908002613c5b8480613e0c565b838402030190506000811215613c775760009350505050610555565b613c808161330c565b90506103e88183031315613c9957900391506105559050565b6103e88183011315613caf570191506105559050565b50600095945050505050565b600080613cd0846020015185600001516132cd565b90506000613ce6856040015186600001516132cd565b90506000613cf8856020015183612ef7565b90506000620f4240613d0a8584613e0c565b81613d1157fe5b0590506103e71981138015613d2757506103e881125b15613d39576000945050505050610555565b85518751600091613d49916132cd565b9050600082613d588386613e0c565b81613d5f57fe5b0590506000811280613d735750620f424081135b15613d875760009650505050505050610555565b6000613d938388612ef7565b9050600084613da68b6020015184613e0c565b81613dad57fe5b0590506000811280613dc35750620f4240818401135b15613dd957600098505050505050505050610555565b600085613de68985613e0c565b81613ded57fe5b0590506103e88112156137445760009950505050505050505050610555565b6040808201519083015160208084015190850151845186510291020191020192915050565b613e396142dd565b6000620f424080613e48613343565b63ffffffff1681613e5557fe5b0663ffffffff16625fdfb00281613e6857fe5b0590506000620f4240613e79613343565b63ffffffff1681613e8657fe5b0663ffffffff1690506000613e9a8261330c565b6103e8029050613ea86142dd565b620186a0613eb98760000151614216565b1315613ee657604051806060016040528060008152602001620f4240815260200160008152509050613f09565b6040518060600160405280620f4240815260200160008152602001600081525090505b613f1661020e8288612ef7565b90506000613f2761020e8884612ef7565b9050613f7f61020e613f64613f5285620f424088613f448c61422e565b0281613f4c57fe5b05612feb565b61025585620f424089613f448d61424e565b6102558a613f7689620f42400361330c565b6103e802612feb565b9150613fb460405180608001604052808a81526020018481526020018b6040015181526020018b60600151151581525061336c565b9998505050505050505050565b613fc96142dd565b6000613ffb61020e8660200151613ff686620f4240613fec898c60200151613e0c565b60020281613f4c57fe5b6132cd565b90506140306040518060800160405280868152602001838152602001876040015181526020018760600151151581525061336c565b95945050505050565b6140416142dd565b60608701516000199015614053575060015b600061408961020e61021c61406c8c602001518a612feb565b613ff68b6140798a61330c565b620f42408c8e0205018802612feb565b60608a0151909150620f42408601906140ba57620f42406140aa838a613e0c565b816140b157fe5b05620f42400390505b60408a0151619c406c0c9f2c9cd04674edea40000000620ea6008480028502850285020205019060021261415e5761412a61411f60405180608001604052808d81526020018681526020018e6040015181526020018e6060015115151581525061336c565b82620f424003612feb565b92506141448361025561413e8e8e8e613fc1565b84612feb565b925061415383620f424061301e565b94505050505061420c565b600281056203d09001620f4240614173613343565b63ffffffff168161418057fe5b0663ffffffff1612156141b2576141536141a461419e8d8d8d613fc1565b83612feb565b600283056203d0900161301e565b6142056141f76141ec60405180608001604052808e81526020018781526020018f6040015181526020018f6060015115151581525061336c565b83620f424003612feb565b60028305620b71b00361301e565b9450505050505b9695505050505050565b60008082131561422757508061082f565b5060000390565b60008061423a8361424e565b905061320b81820264e8d4a510000361330c565b60005b600082121561426757625fdfb082019150614251565b5b625fdfb0821261427f57625fdfb082039150614268565b6001828160025b818313156142d457818385028161429957fe5b0585019450620f4240808788860202816142af57fe5b05816142b757fe5b600095909503940592506001810181029190910290600201614286565b50505050919050565b60405180606001604052806000815260200160008152602001600081525090565b50805460018160011615610100020316600290046000825580601f106143245750614342565b601f0160209004906000526020600020908101906143429190614401565b50565b50805460008255600b02906000526020600020908101906143429190614416565b50805460008255601302906000526020600020908101906143429190614475565b6040518060a00160405280600081526020016143a16142dd565b81526020016143ae6142dd565b81526020016143bb6142dd565b81526020016000905290565b6040518060e001604052806143da6142dd565b81526020016143e76142dd565b81526020016143f46142dd565b81526020016143a16142dd565b5b80821115613c0f5760008155600101614402565b5b80821115613c0f57600080825560018201819055600282018190556003820181905560048201819055600582018190556006820181905560078201819055600882018190556009820155600a8101805460ff19169055600b01614417565b5b80821115613c0f576000808255600182018190556002820181905560038201819055600482018190556005820181905560068201819055600782018190556008820181905560098201819055600a8201819055600b8201819055600c8201819055600d8201819055600e8201819055600f820181905560108201819055601182015560128101805460ff1916905560130161447656fea2646970667358221220037024f5647853879c58fbcc61ac3616455f6f731cc6e84f91eb5a3b4e06c00464736f6c63430007060033"); +static CONTRACT_DATA : Bytes = bytes!("608060405234801561001057600080fd5b506004361061004c5760003560e01c806330627b7c1461005157806375ac892a14610085578063784f13661461011d578063c294360114610146575b600080fd5b610059610163565b604080516001600160f81b03199485168152928416602084015292168183015290519081900360600190f35b6100a86004803603604081101561009b57600080fd5b50803590602001356102d1565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100e25781810151838201526020016100ca565b50505050905090810190601f16801561010f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100596004803603606081101561013357600080fd5b508035906020810135906040013561055b565b6100a86004803603602081101561015c57600080fd5b5035610590565b6000806000610176610400610300610834565b60405180606001604052806001546000546207d5dc028161019357fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526102259161021c916102139161020e91612ef7565b612f64565b6207d5dc612feb565b620f424061301e565b8051600e556020810151600f55604001516010556102416142dd565b61025a816102556102006101806008613064565b613212565b90506102708161025561014561021c6008613064565b905061028481610255610258806008613064565b905061029a8161025561020a61020c6008613064565b90506102a781600461301e565b90506102b1613250565b8051602082015160409092015160f891821b9692821b9550901b92509050565b606060005b6000548112156104c95760006102ed828686613064565b90506002816000015160f81b90808054603f811680603e811461032a576002830184556001831661031c578192505b600160028404019350610342565b600084815260209081902060ff198516905560419094555b505050600190038154600116156103685790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146103c557600283018455600183166103b7578192505b6001600284040193506103dd565b600084815260209081902060ff198516905560419094555b505050600190038154600116156104035790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146104605760028301845560018316610452578192505b600160028404019350610478565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561049e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016102d6565b506002805460408051602060018416156101000260001901909316849004601f8101849004840282018401909252818152929183018282801561054d5780601f106105225761010080835404028352916020019161054d565b820191906000526020600020905b81548152906001019060200180831161053057829003601f168201915b505050505090505b92915050565b60008060008061056c878787613064565b8051602082015160409092015160f891821b9a92821b9950901b9650945050505050565b600154606090600019015b600081126107a35760005b6000548112156107995760006105bd828487613064565b90506002816000015160f81b90808054603f811680603e81146105fa57600283018455600183166105ec578192505b600160028404019350610612565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106385790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146106955760028301845560018316610687578192505b6001600284040193506106ad565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106d35790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146107305760028301845560018316610722578192505b600160028404019350610748565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561076e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016105a6565b506000190161059b565b506002805460408051602060018416156101000260001901909316849004601f810184900484028201840190925281815292918301828280156108275780601f106107fc57610100808354040283529160200191610827565b820191906000526020600020905b81548152906001019060200180831161080a57829003601f168201915b505050505090505b919050565b8160008190555080600181905550604051806080016040528060405180606001604052806302faf08081526020016303197500815260200163119e7f8081525081526020016108a460405180606001604052806000815260200161a673198152602001620f423f19815250612f64565b815260006020808301829052604092830182905283518051600355808201516004558301516005558381015180516006559081015160075582015160085582820151600955606092830151600a805460ff1916911515919091179055815192830190915260015490548291906207d5dc028161091c57fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526109979161021c916102139161020e91612ef7565b8051600e55602080820151600f55604091820151601055815160a08101835264174876e8008152825160608082018552641748862a40825263026e8f00828501526304dd1e008286015282840191825284518082018652600080825281860181905281870181905284870191825286518084018852620b71b081526203d09081880181905281890152928501928352608085018181526011805460018082018355919093528651600b9093027f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c688101938455955180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c69880155808901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6a8801558901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6b870155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6c870155808801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6d8701558801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6e860155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6f860155958601517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c7085015594909501517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c71830155517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c72909101805492949192909160ff1990911690836002811115610c1057fe5b0217905550505060116040518060a0016040528064174876e8008152602001604051806060016040528064174290493f19815260200163026e8f0081526020016304dd1e008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806203d09081526020016203d0908152602001620b71b0815250815260200160006002811115610cb657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610d5857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164174876e800815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b0815250815260200160006002811115610dfd57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610e9f57fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164173e54e97f1981525081526020016040518060600160405280600081526020016000815260200160008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160006002811115610f3f57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610fe157fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174876e80081526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b081525081526020016000600281111561108657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561112857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174399c9ff1981526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b08152508152602001600060028111156111ce57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561127057fe5b0217905550505060116040518060a0016040528062fbc5208152602001604051806060016040528063019bfcc0815260200162fbc52081526020016302cd29c0815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561131157fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156113b357fe5b0217905550505060116040518060a001604052806323c34600815260200160405180606001604052806302faf080815260200163289c455081526020016304dd1e008152508152602001604051806060016040528062b71b00815260200162b71b00815260200162b71b00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016000600281111561145657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156114f857fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561160c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156116fd57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561180e57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156118ff57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611a1357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611b0457fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611c1557fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611d0657fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611e1a57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611f0b57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a6081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561201c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561210d57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561222157fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561231257fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561242357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561251457fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561262857fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561271957fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561282d57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561291e57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612a3257fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612b2357fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612c3757fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612d2857fe5b0217905550505060005b601254811015612ef257600060128281548110612d4b57fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115612e6c57fe5b6002811115612e7757fe5b815250509050612eac61020e612e95836020015184600001516132cd565b612ea7846040015185600001516132cd565b612ef7565b60128381548110612eb957fe5b60009182526020918290208351600960139093029091019182015590820151600a820155604090910151600b9091015550600101612d32565b505050565b612eff6142dd565b604051806060016040528083602001518560400151028460400151866020015102038152602001836040015185600001510284600001518660400151020381526020018360000151856020015102846020015186600001510203815250905092915050565b612f6c6142dd565b604082015160208301518351600092612f9292918002918002919091019080020161330c565b90506040518060600160405280828560000151620f42400281612fb157fe5b058152602001828560200151620f42400281612fc957fe5b058152602001828560400151620f42400281612fe157fe5b0590529392505050565b612ff36142dd565b5060408051606081018252835183028152602080850151840290820152928101519091029082015290565b6130266142dd565b60405180606001604052808385600001518161303e57fe5b0581526020018385602001518161305157fe5b05815260200183856040015181612fe157fe5b61306c6142dd565b6000546013805463ffffffff1916918502860163ffffffff169190911790556130936142dd565b905060005b828112156131f157600061317261314c61021c613115600b60405180606001604052908160008201548152602001600182015481526020016002820154815250506207a1206000546207a1206130ec613343565b63ffffffff16816130f957fe5b0663ffffffff168d620f424002018161310e57fe5b0503612feb565b60408051606081018252600e548152600f5460208201526010549181019190915260015461025591906207a12090816130ec613343565b604080516060810182526006548152600754602082015260085491810191909152613212565b6040805160e081019091526003546080820190815260045460a083015260055460c083015291925060009181906131ae9061025586608c612feb565b81526020016131bc84612f64565b815260006020820181905260409091015290506131e5846102556131df8461336c565b8861301e565b93505050600101613098565b5061320861021c61320183613753565b60ff612feb565b90505b9392505050565b61321a6142dd565b50604080516060810182528251845101815260208084015181860151019082015291810151928101519092019181019190915290565b60008080556001819055613266906002906142fe565b60006003819055600481905560058190556006819055600781905560088190556009819055600a805460ff19169055600b819055600c819055600d819055600e819055600f81905560108190556132bf90601190614345565b6132cb60126000614366565b565b6132d56142dd565b5060408051606081018252825184510381526020808401518186015103908201528282015184830151039181019190915292915050565b80600260018201055b8181121561333d5780915060028182858161332c57fe5b05018161333557fe5b059050613315565b50919050565b6013805463ffffffff19811663ffffffff9182166341c64e6d0261303901821617918290551690565b6133746142dd565b600a826040015113156133a657604051806060016040528060008152602001600081526020016000815250905061082f565b60008060006133b48561379f565b91945092509050826133e857604051806060016040528060008152602001600081526020016000815250935050505061082f565b6133f0614387565b6133f86143c7565b6134006142dd565b6134086142dd565b600086600181111561341657fe5b1415613505576011858154811061342957fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff909116908111156134e157fe5b60028111156134ec57fe5b8152505093508360600151915083604001519050613653565b6012858154811061351257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff9091169081111561363357fe5b600281111561363e57fe5b8152505092508260a001519150826080015190505b6040820151600190811215613669575060408201515b808360200151131561367c575060208201515b808360400151131561368f575060408201515b60408a01805160010190819052600512156136f75780620f42406136b1613343565b63ffffffff16816136be57fe5b0663ffffffff1612156136e8576136e16136db84620f4240612feb565b8261301e565b92506136f7565b50965061082f95505050505050565b6136ff6142dd565b600088600181111561370d57fe5b14156137255761371e8b878b613a57565b9050613733565b6137308b868b613aec565b90505b6137448361025561021c8785613baa565b9b9a5050505050505050505050565b61375b6142dd565b60405180606001604052806137738460000151613be8565b81526020016137858460200151613be8565b81526020016137978460400151613be8565b905292915050565b60008080808080805b6011548110156138c2576000613890601183815481106137c457fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff9091169081111561387c57fe5b600281111561388757fe5b9052508a613c13565b90506000811380156138a957508415806138a957508481125b156138b957809450600093508192505b506001016137a8565b5060005b601254811015613a49576000613a17601283815481106138e257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115613a0357fe5b6002811115613a0e57fe5b9052508a613cbb565b9050600081138015613a305750841580613a3057508481125b15613a4057809450600193508192505b506001016138c6565b509196909550909350915050565b613a5f6142dd565b6000613a7a856000015161025561021c886020015187612feb565b90506000613a8f61020e8387602001516132cd565b9050600085608001516002811115613aa357fe5b1415613ae1576000613ab9828860200151613e0c565b12613acd57613aca81600019612feb565b90505b613ad8868383613e31565b9250505061320b565b613ad8868383613fc1565b613af46142dd565b6000613b0f856000015161025561021c886020015187612feb565b6060860151909150620a2c2a9015613b2757506216e3605b6000620f4240613b3f87606001518960200151613e0c565b81613b4657fe5b05905060008112613b55576000035b64e8d4a5100081800281038380020281900590036000811215613b8c57613b8188858960600151613fc1565b94505050505061320b565b613b9e88858960600151868686614039565b98975050505050505050565b613bb26142dd565b50604080516060810182528251845102815260208084015181860151029082015291810151928101519092029181019190915290565b600080821215613bfa5750600061082f565b620f4240821315613c0f5750620f424061082f565b5090565b600080613c28846020015184600001516132cd565b90506000620f4240613c3e838660200151613e0c565b81613c4557fe5b865191900591506000908002613c5b8480613e0c565b838402030190506000811215613c775760009350505050610555565b613c808161330c565b90506103e88183031315613c9957900391506105559050565b6103e88183011315613caf570191506105559050565b50600095945050505050565b600080613cd0846020015185600001516132cd565b90506000613ce6856040015186600001516132cd565b90506000613cf8856020015183612ef7565b90506000620f4240613d0a8584613e0c565b81613d1157fe5b0590506103e71981138015613d2757506103e881125b15613d39576000945050505050610555565b85518751600091613d49916132cd565b9050600082613d588386613e0c565b81613d5f57fe5b0590506000811280613d735750620f424081135b15613d875760009650505050505050610555565b6000613d938388612ef7565b9050600084613da68b6020015184613e0c565b81613dad57fe5b0590506000811280613dc35750620f4240818401135b15613dd957600098505050505050505050610555565b600085613de68985613e0c565b81613ded57fe5b0590506103e88112156137445760009950505050505050505050610555565b6040808201519083015160208084015190850151845186510291020191020192915050565b613e396142dd565b6000620f424080613e48613343565b63ffffffff1681613e5557fe5b0663ffffffff16625fdfb00281613e6857fe5b0590506000620f4240613e79613343565b63ffffffff1681613e8657fe5b0663ffffffff1690506000613e9a8261330c565b6103e8029050613ea86142dd565b620186a0613eb98760000151614216565b1315613ee657604051806060016040528060008152602001620f4240815260200160008152509050613f09565b6040518060600160405280620f4240815260200160008152602001600081525090505b613f1661020e8288612ef7565b90506000613f2761020e8884612ef7565b9050613f7f61020e613f64613f5285620f424088613f448c61422e565b0281613f4c57fe5b05612feb565b61025585620f424089613f448d61424e565b6102558a613f7689620f42400361330c565b6103e802612feb565b9150613fb460405180608001604052808a81526020018481526020018b6040015181526020018b60600151151581525061336c565b9998505050505050505050565b613fc96142dd565b6000613ffb61020e8660200151613ff686620f4240613fec898c60200151613e0c565b60020281613f4c57fe5b6132cd565b90506140306040518060800160405280868152602001838152602001876040015181526020018760600151151581525061336c565b95945050505050565b6140416142dd565b60608701516000199015614053575060015b600061408961020e61021c61406c8c602001518a612feb565b613ff68b6140798a61330c565b620f42408c8e0205018802612feb565b60608a0151909150620f42408601906140ba57620f42406140aa838a613e0c565b816140b157fe5b05620f42400390505b60408a0151619c406c0c9f2c9cd04674edea40000000620ea6008480028502850285020205019060021261415e5761412a61411f60405180608001604052808d81526020018681526020018e6040015181526020018e6060015115151581525061336c565b82620f424003612feb565b92506141448361025561413e8e8e8e613fc1565b84612feb565b925061415383620f424061301e565b94505050505061420c565b600281056203d09001620f4240614173613343565b63ffffffff168161418057fe5b0663ffffffff1612156141b2576141536141a461419e8d8d8d613fc1565b83612feb565b600283056203d0900161301e565b6142056141f76141ec60405180608001604052808e81526020018781526020018f6040015181526020018f6060015115151581525061336c565b83620f424003612feb565b60028305620b71b00361301e565b9450505050505b9695505050505050565b60008082131561422757508061082f565b5060000390565b60008061423a8361424e565b905061320b81820264e8d4a510000361330c565b60005b600082121561426757625fdfb082019150614251565b5b625fdfb0821261427f57625fdfb082039150614268565b6001828160025b818313156142d457818385028161429957fe5b0585019450620f4240808788860202816142af57fe5b05816142b757fe5b600095909503940592506001810181029190910290600201614286565b50505050919050565b60405180606001604052806000815260200160008152602001600081525090565b50805460018160011615610100020316600290046000825580601f106143245750614342565b601f0160209004906000526020600020908101906143429190614401565b50565b50805460008255600b02906000526020600020908101906143429190614416565b50805460008255601302906000526020600020908101906143429190614475565b6040518060a00160405280600081526020016143a16142dd565b81526020016143ae6142dd565b81526020016143bb6142dd565b81526020016000905290565b6040518060e001604052806143da6142dd565b81526020016143e76142dd565b81526020016143f46142dd565b81526020016143a16142dd565b5b80821115613c0f5760008155600101614402565b5b80821115613c0f57600080825560018201819055600282018190556003820181905560048201819055600582018190556006820181905560078201819055600882018190556009820155600a8101805460ff19169055600b01614417565b5b80821115613c0f576000808255600182018190556002820181905560038201819055600482018190556005820181905560068201819055600782018190556008820181905560098201819055600a8201819055600b8201819055600c8201819055600d8201819055600e8201819055600f820181905560108201819055601182015560128101805460ff1916905560130161447656fea2646970667358221220037024f5647853879c58fbcc61ac3616455f6f731cc6e84f91eb5a3b4e06c00464736f6c63430007060033"); diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index a82a5baf0f..aa99a81776 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -81,15 +81,12 @@ impl Precompiles { static INSTANCE: OnceBox = OnceBox::new(); INSTANCE.get_or_init(|| { let mut precompiles = Precompiles::default(); - precompiles.extend( - [ - secp256k1::ECRECOVER, - hash::SHA256, - hash::RIPEMD160, - identity::FUN, - ] - .into_iter(), - ); + precompiles.extend([ + secp256k1::ECRECOVER, + hash::SHA256, + hash::RIPEMD160, + identity::FUN, + ]); Box::new(precompiles) }) } diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index f976c1ad9c..e4f4ae7265 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -112,7 +112,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternalStage, EXT, DB> { ) -> EvmBuilder<'a, SettingHandlerStage, OEXT, DB> { EvmBuilder { evm: self.evm, - external: external, + external, handler: Handler::mainnet_with_spec(self.handler.spec_id), phantom: PhantomData, } diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index 02398bc4fb..8c9ee817d1 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -37,7 +37,7 @@ impl Context<(), DB> { /// Creates new context with database. pub fn new_with_db(db: DB) -> Context<(), DB> { Context { - evm: EvmContext::new_with_env(db, Box::new(Env::default())), + evm: EvmContext::new_with_env(db, Box::default()), external: (), } } @@ -62,7 +62,7 @@ pub struct EvmContext { pub l1_block_info: Option, } -impl<'a, DB: Database> EvmContext { +impl EvmContext { pub fn with_db(self, db: ODB) -> EvmContext { EvmContext { env: self.env, @@ -101,11 +101,8 @@ impl<'a, DB: Database> EvmContext { /// Sets precompiles pub fn set_precompiles(&mut self, precompiles: Precompiles) { - self.journaled_state.warm_preloaded_addresses = precompiles - .addresses() - .into_iter() - .cloned() - .collect::>(); + self.journaled_state.warm_preloaded_addresses = + precompiles.addresses().cloned().collect::>(); self.precompiles = precompiles; } @@ -505,7 +502,6 @@ impl<'a, DB: Database> EvmContext { (interpreter_result, address) } } - /// Test utilities for the [`EvmContext`]. #[cfg(any(test, feature = "test-utils"))] pub(crate) mod test_utils { @@ -544,10 +540,10 @@ pub(crate) mod test_utils { /// Additionally loads the mock caller account into the db, /// and sets the balance to the provided U256 value. pub fn create_cache_db_evm_context_with_balance<'a>( - env: &'a mut Env, - db: &'a mut CacheDB, + env: Box, + mut db: CacheDB, balance: U256, - ) -> EvmContext<'a, CacheDB> { + ) -> EvmContext> { db.insert_account_info( test_utils::MOCK_CALLER, crate::primitives::AccountInfo { @@ -562,12 +558,12 @@ pub(crate) mod test_utils { /// Creates a cached db evm context. pub fn create_cache_db_evm_context<'a>( - env: &'a mut Env, - db: &'a mut CacheDB, - ) -> EvmContext<'a, CacheDB> { + env: Box, + db: CacheDB, + ) -> EvmContext> { EvmContext { env, - journaled_state: JournaledState::new(SpecId::CANCUN, vec![]), + journaled_state: JournaledState::new(SpecId::CANCUN, HashSet::new()), db, error: None, precompiles: Precompiles::default(), @@ -577,13 +573,10 @@ pub(crate) mod test_utils { } /// Returns a new `EvmContext` with an empty journaled state. - pub fn create_empty_evm_context<'a>( - env: &'a mut Env, - db: &'a mut EmptyDB, - ) -> EvmContext<'a, EmptyDB> { + pub fn create_empty_evm_context(env: Box, db: EmptyDB) -> EvmContext { EvmContext { env, - journaled_state: JournaledState::new(SpecId::CANCUN, vec![]), + journaled_state: JournaledState::new(SpecId::CANCUN, HashSet::new()), db, error: None, precompiles: Precompiles::default(), @@ -605,14 +598,16 @@ mod tests { // call stack is too deep. #[test] fn test_make_call_frame_stack_too_deep() { - let mut env = Env::default(); - let mut db = EmptyDB::default(); - let mut evm_context = test_utils::create_empty_evm_context(&mut env, &mut db); + let env = Env::default(); + let db = EmptyDB::default(); + let mut evm_context = test_utils::create_empty_evm_context(Box::new(env), db); evm_context.journaled_state.depth = CALL_STACK_LIMIT as usize + 1; let contract = address!("dead10000000000000000000000000000001dead"); let call_inputs = test_utils::create_mock_call_inputs(contract); let res = evm_context.make_call_frame(&call_inputs, 0..0); - let err = res.unwrap_err(); + let FrameOrResult::Result(err) = res else { + panic!("Expected FrameOrResult::Result"); + }; assert_eq!(err.result, InstructionResult::CallTooDeep); } @@ -621,14 +616,16 @@ mod tests { // checkpointed on the journaled state correctly. #[test] fn test_make_call_frame_transfer_revert() { - let mut env = Env::default(); - let mut db = EmptyDB::default(); - let mut evm_context = test_utils::create_empty_evm_context(&mut env, &mut db); + let env = Env::default(); + let db = EmptyDB::default(); + let mut evm_context = test_utils::create_empty_evm_context(Box::new(env), db); let contract = address!("dead10000000000000000000000000000001dead"); let mut call_inputs = test_utils::create_mock_call_inputs(contract); call_inputs.transfer.value = U256::from(1); let res = evm_context.make_call_frame(&call_inputs, 0..0); - let err = res.unwrap_err(); + let FrameOrResult::Result(err) = res else { + panic!("Expected FrameOrResult::Result"); + }; assert_eq!(err.result, InstructionResult::OutOfFund); let checkpointed = vec![vec![JournalEntry::AccountLoaded { address: contract }]]; assert_eq!(evm_context.journaled_state.journal, checkpointed); @@ -637,19 +634,22 @@ mod tests { #[test] fn test_make_call_frame_missing_code_context() { - let mut env = Env::default(); - let mut cdb = CacheDB::new(EmptyDB::default()); + let env = Env::default(); + let cdb = CacheDB::new(EmptyDB::default()); let bal = U256::from(3_000_000_000_u128); - let mut evm_context = create_cache_db_evm_context_with_balance(&mut env, &mut cdb, bal); + let mut evm_context = create_cache_db_evm_context_with_balance(Box::new(env), cdb, bal); let contract = address!("dead10000000000000000000000000000001dead"); let call_inputs = test_utils::create_mock_call_inputs(contract); let res = evm_context.make_call_frame(&call_inputs, 0..0); - assert_eq!(res.unwrap_err().result, InstructionResult::Stop); + let FrameOrResult::Result(res) = res else { + panic!("Expected FrameOrResult::Result"); + }; + assert_eq!(res.result, InstructionResult::Stop); } #[test] fn test_make_call_frame_succeeds() { - let mut env = Env::default(); + let env = Env::default(); let mut cdb = CacheDB::new(EmptyDB::default()); let bal = U256::from(3_000_000_000_u128); let by = Bytecode::new_raw(Bytes::from(vec![0x60, 0x00, 0x60, 0x00])); @@ -663,10 +663,12 @@ mod tests { code: Some(by), }, ); - let mut evm_context = create_cache_db_evm_context_with_balance(&mut env, &mut cdb, bal); + let mut evm_context = create_cache_db_evm_context_with_balance(Box::new(env), cdb, bal); let call_inputs = test_utils::create_mock_call_inputs(contract); let res = evm_context.make_call_frame(&call_inputs, 0..0); - let frame = res.unwrap(); + let FrameOrResult::Frame(frame) = res else { + panic!("Expected FrameOrResult::Frame"); + }; assert!(!frame.is_create); assert_eq!(frame.created_address, None); assert_eq!(frame.subcall_return_memory_range, 0..0); diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index cc7e087cfd..55dcdb0693 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -157,8 +157,8 @@ impl Evm<'_, EXT, DB> { // run main loop let output = match &table { - InstructionTables::Plain(table) => self.run_the_loop(&table, first_stack_frame), - InstructionTables::Boxed(table) => self.run_the_loop(&table, first_stack_frame), + InstructionTables::Plain(table) => self.run_the_loop(table, first_stack_frame), + InstructionTables::Boxed(table) => self.run_the_loop(table, first_stack_frame), }; // return back instruction table diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index 93a9fff78e..4739b6657d 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -46,7 +46,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( // Register inspector Log instruction. let mut inspect_log = |index: u8| { - table.get_mut(index as usize).map(|i| { + if let Some(i) = table.get_mut(index as usize) { let old = core::mem::replace(i, Box::new(|_, _| ())); *i = Box::new( move |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { @@ -74,7 +74,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( } }, ) - }); + } }; inspect_log(opcode::LOG0); @@ -84,7 +84,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( inspect_log(opcode::LOG4); // register selfdestruct function. - table.get_mut(opcode::SELFDESTRUCT as usize).map(|i| { + if let Some(i) = table.get_mut(opcode::SELFDESTRUCT as usize) { let old = core::mem::replace(i, Box::new(|_, _| ())); *i = Box::new( move |interpreter: &mut Interpreter, host: &mut Evm<'a, EXT, DB>| { @@ -113,7 +113,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( } }, ) - }); + } // cast vector to array. handler.instruction_table = Some(EvmInstructionTables::Boxed( diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 052724e5a1..e688251c02 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -56,4 +56,4 @@ pub use revm_interpreter as interpreter; #[doc(inline)] pub use revm_interpreter::primitives; #[doc(inline)] -pub use revm_precompile as precompile; \ No newline at end of file +pub use revm_precompile as precompile; diff --git a/crates/revm/src/test_utils.rs b/crates/revm/src/test_utils.rs index 46801c3dd6..98fa21378f 100644 --- a/crates/revm/src/test_utils.rs +++ b/crates/revm/src/test_utils.rs @@ -1,2 +1,2 @@ #[doc(hidden)] -pub use crate::evm_context::test_utils::*; +pub use crate::context::test_utils::*; From 74ae8b07049bd6bbc68eed2d60fe180111db99a8 Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 26 Dec 2023 23:54:21 +0100 Subject: [PATCH 31/52] fix trace block example --- crates/primitives/src/specification.rs | 52 +++++----- crates/revm/src/handler.rs | 2 +- crates/revm/src/handler/mainnet/frame.rs | 2 +- crates/revm/src/inspector/eip3155.rs | 7 ++ examples/generate_block_traces.rs | 124 ++++++++++++----------- 5 files changed, 100 insertions(+), 87 deletions(-) diff --git a/crates/primitives/src/specification.rs b/crates/primitives/src/specification.rs index 2e8dbfc279..e434bb17c1 100644 --- a/crates/primitives/src/specification.rs +++ b/crates/primitives/src/specification.rs @@ -160,7 +160,7 @@ spec!(CANYON, CanyonSpec); #[macro_export] macro_rules! spec_to_generic { - ($spec_id:expr, $e:block) => {{ + ($spec_id:expr, $e:expr) => {{ // We are transitioning from var to generic spec. match $spec_id { $crate::SpecId::FRONTIER | SpecId::FRONTIER_THAWING => { @@ -244,31 +244,31 @@ mod tests { fn spec_to_generic() { use SpecId::*; - // spec_to_generic!(FRONTIER, assert_eq!(SPEC::SPEC_ID, FRONTIER)); - // spec_to_generic!(FRONTIER_THAWING, assert_eq!(SPEC::SPEC_ID, FRONTIER)); - // spec_to_generic!(HOMESTEAD, assert_eq!(SPEC::SPEC_ID, HOMESTEAD)); - // spec_to_generic!(DAO_FORK, assert_eq!(SPEC::SPEC_ID, HOMESTEAD)); - // spec_to_generic!(TANGERINE, assert_eq!(SPEC::SPEC_ID, TANGERINE)); - // spec_to_generic!(SPURIOUS_DRAGON, assert_eq!(SPEC::SPEC_ID, SPURIOUS_DRAGON)); - // spec_to_generic!(BYZANTIUM, assert_eq!(SPEC::SPEC_ID, BYZANTIUM)); - // spec_to_generic!(CONSTANTINOPLE, assert_eq!(SPEC::SPEC_ID, PETERSBURG)); - // spec_to_generic!(PETERSBURG, assert_eq!(SPEC::SPEC_ID, PETERSBURG)); - // spec_to_generic!(ISTANBUL, assert_eq!(SPEC::SPEC_ID, ISTANBUL)); - // spec_to_generic!(MUIR_GLACIER, assert_eq!(SPEC::SPEC_ID, ISTANBUL)); - // spec_to_generic!(BERLIN, assert_eq!(SPEC::SPEC_ID, BERLIN)); - // spec_to_generic!(LONDON, assert_eq!(SPEC::SPEC_ID, LONDON)); - // spec_to_generic!(ARROW_GLACIER, assert_eq!(SPEC::SPEC_ID, LONDON)); - // spec_to_generic!(GRAY_GLACIER, assert_eq!(SPEC::SPEC_ID, LONDON)); - // spec_to_generic!(MERGE, assert_eq!(SPEC::SPEC_ID, MERGE)); - // #[cfg(feature = "optimism")] - // spec_to_generic!(BEDROCK, assert_eq!(SPEC::SPEC_ID, BEDROCK)); - // #[cfg(feature = "optimism")] - // spec_to_generic!(REGOLITH, assert_eq!(SPEC::SPEC_ID, REGOLITH)); - // spec_to_generic!(SHANGHAI, assert_eq!(SPEC::SPEC_ID, SHANGHAI)); - // #[cfg(feature = "optimism")] - // spec_to_generic!(CANYON, assert_eq!(SPEC::SPEC_ID, CANYON)); - // spec_to_generic!(CANCUN, assert_eq!(SPEC::SPEC_ID, CANCUN)); - // spec_to_generic!(LATEST, assert_eq!(SPEC::SPEC_ID, LATEST)); + spec_to_generic!(FRONTIER, assert_eq!(SPEC::SPEC_ID, FRONTIER)); + spec_to_generic!(FRONTIER_THAWING, assert_eq!(SPEC::SPEC_ID, FRONTIER)); + spec_to_generic!(HOMESTEAD, assert_eq!(SPEC::SPEC_ID, HOMESTEAD)); + spec_to_generic!(DAO_FORK, assert_eq!(SPEC::SPEC_ID, HOMESTEAD)); + spec_to_generic!(TANGERINE, assert_eq!(SPEC::SPEC_ID, TANGERINE)); + spec_to_generic!(SPURIOUS_DRAGON, assert_eq!(SPEC::SPEC_ID, SPURIOUS_DRAGON)); + spec_to_generic!(BYZANTIUM, assert_eq!(SPEC::SPEC_ID, BYZANTIUM)); + spec_to_generic!(CONSTANTINOPLE, assert_eq!(SPEC::SPEC_ID, PETERSBURG)); + spec_to_generic!(PETERSBURG, assert_eq!(SPEC::SPEC_ID, PETERSBURG)); + spec_to_generic!(ISTANBUL, assert_eq!(SPEC::SPEC_ID, ISTANBUL)); + spec_to_generic!(MUIR_GLACIER, assert_eq!(SPEC::SPEC_ID, ISTANBUL)); + spec_to_generic!(BERLIN, assert_eq!(SPEC::SPEC_ID, BERLIN)); + spec_to_generic!(LONDON, assert_eq!(SPEC::SPEC_ID, LONDON)); + spec_to_generic!(ARROW_GLACIER, assert_eq!(SPEC::SPEC_ID, LONDON)); + spec_to_generic!(GRAY_GLACIER, assert_eq!(SPEC::SPEC_ID, LONDON)); + spec_to_generic!(MERGE, assert_eq!(SPEC::SPEC_ID, MERGE)); + #[cfg(feature = "optimism")] + spec_to_generic!(BEDROCK, assert_eq!(SPEC::SPEC_ID, BEDROCK)); + #[cfg(feature = "optimism")] + spec_to_generic!(REGOLITH, assert_eq!(SPEC::SPEC_ID, REGOLITH)); + spec_to_generic!(SHANGHAI, assert_eq!(SPEC::SPEC_ID, SHANGHAI)); + #[cfg(feature = "optimism")] + spec_to_generic!(CANYON, assert_eq!(SPEC::SPEC_ID, CANYON)); + spec_to_generic!(CANCUN, assert_eq!(SPEC::SPEC_ID, CANCUN)); + spec_to_generic!(LATEST, assert_eq!(SPEC::SPEC_ID, LATEST)); } } diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 8d6c6b6e71..13bb70f6b8 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -55,7 +55,7 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { /// Creates handler with variable spec id, inside it will call `mainnet::` for /// appropriate spec. pub fn mainnet_with_spec(spec_id: SpecId) -> Self { - spec_to_generic!(spec_id, { Self::mainnet::() }) + spec_to_generic!(spec_id, Self::mainnet::()) } /// Specification ID. diff --git a/crates/revm/src/handler/mainnet/frame.rs b/crates/revm/src/handler/mainnet/frame.rs index 47e812f24e..a5708e4d25 100644 --- a/crates/revm/src/handler/mainnet/frame.rs +++ b/crates/revm/src/handler/mainnet/frame.rs @@ -203,7 +203,7 @@ mod tests { let gas = main_frame_return::(&env, InstructionResult::Stop, return_gas); assert_eq!(gas.remaining(), 90); assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 30); + assert_eq!(gas.refunded(), 2); let gas = main_frame_return::(&env, InstructionResult::Revert, return_gas); assert_eq!(gas.remaining(), 90); diff --git a/crates/revm/src/inspector/eip3155.rs b/crates/revm/src/inspector/eip3155.rs index 1926a927e2..22269082a3 100644 --- a/crates/revm/src/inspector/eip3155.rs +++ b/crates/revm/src/inspector/eip3155.rs @@ -26,6 +26,13 @@ pub struct TracerEip3155 { skip: bool, } +impl TracerEip3155 { + /// Sets the writer to use for the output. + pub fn set_writer(&mut self, writer: Box) { + self.output = writer; + } +} + impl<'a, DB: Database> GetInspector<'a, DB> for TracerEip3155 { fn get_inspector(&mut self) -> &mut dyn Inspector { self diff --git a/examples/generate_block_traces.rs b/examples/generate_block_traces.rs index 37579b7f2c..ed71988a85 100644 --- a/examples/generate_block_traces.rs +++ b/examples/generate_block_traces.rs @@ -6,8 +6,8 @@ use ethers_providers::{Http, Provider}; use indicatif::ProgressBar; use revm::db::{CacheDB, EthersDB, StateBuilder}; use revm::inspectors::TracerEip3155; -use revm::primitives::{Address, Env, TransactTo, U256}; -use revm::EVM; +use revm::primitives::{Address, TransactTo, U256}; +use revm::{inspector_handle_register, Evm}; use std::fs::OpenOptions; use std::io::BufWriter; use std::io::Write; @@ -74,25 +74,27 @@ async fn main() -> anyhow::Result<()> { let state_db = EthersDB::new(Arc::clone(&client), Some(prev_id)).expect("panic"); let cache_db: CacheDB>> = CacheDB::new(state_db); let mut state = StateBuilder::new_with_database(cache_db).build(); - let mut evm = EVM::new(); - evm.database(&mut state); - - let mut env = Env::default(); - if let Some(number) = block.number { - let nn = number.0[0]; - env.block.number = U256::from(nn); - } - local_fill!(env.block.coinbase, block.author); - local_fill!(env.block.timestamp, Some(block.timestamp), U256::from_limbs); - local_fill!( - env.block.difficulty, - Some(block.difficulty), - U256::from_limbs - ); - local_fill!(env.block.gas_limit, Some(block.gas_limit), U256::from_limbs); - if let Some(base_fee) = block.base_fee_per_gas { - local_fill!(env.block.basefee, Some(base_fee), U256::from_limbs); - } + let mut evm = Evm::builder() + .with_db(&mut state) + .with_external_context(TracerEip3155::new(Box::new(std::io::stdout()), true, true)) + .modify_block_env(|b| { + if let Some(number) = block.number { + let nn = number.0[0]; + b.number = U256::from(nn); + } + local_fill!(b.coinbase, block.author); + local_fill!(b.timestamp, Some(block.timestamp), U256::from_limbs); + local_fill!(b.difficulty, Some(block.difficulty), U256::from_limbs); + local_fill!(b.gas_limit, Some(block.gas_limit), U256::from_limbs); + if let Some(base_fee) = block.base_fee_per_gas { + local_fill!(b.basefee, Some(base_fee), U256::from_limbs); + } + }) + .modify_cfg_env(|c| { + c.chain_id = chain_id; + }) + .append_handler_register(inspector_handle_register) + .build(); let txs = block.transactions.len(); println!("Found {txs} transactions."); @@ -104,45 +106,49 @@ async fn main() -> anyhow::Result<()> { std::fs::create_dir_all("traces").expect("Failed to create traces directory"); // Fill in CfgEnv - env.cfg.chain_id = chain_id; for tx in block.transactions { - env.tx.caller = Address::from(tx.from.as_fixed_bytes()); - env.tx.gas_limit = tx.gas.as_u64(); - local_fill!(env.tx.gas_price, tx.gas_price, U256::from_limbs); - local_fill!(env.tx.value, Some(tx.value), U256::from_limbs); - env.tx.data = tx.input.0.into(); - let mut gas_priority_fee = U256::ZERO; - local_fill!( - gas_priority_fee, - tx.max_priority_fee_per_gas, - U256::from_limbs - ); - env.tx.gas_priority_fee = Some(gas_priority_fee); - env.tx.chain_id = Some(chain_id); - env.tx.nonce = Some(tx.nonce.as_u64()); - if let Some(access_list) = tx.access_list { - env.tx.access_list = access_list - .0 - .into_iter() - .map(|item| { - let new_keys: Vec = item - .storage_keys + evm = evm + .modify() + .modify_tx_env(|etx| { + etx.caller = Address::from(tx.from.as_fixed_bytes()); + etx.gas_limit = tx.gas.as_u64(); + local_fill!(etx.gas_price, tx.gas_price, U256::from_limbs); + local_fill!(etx.value, Some(tx.value), U256::from_limbs); + etx.data = tx.input.0.into(); + let mut gas_priority_fee = U256::ZERO; + local_fill!( + gas_priority_fee, + tx.max_priority_fee_per_gas, + U256::from_limbs + ); + etx.gas_priority_fee = Some(gas_priority_fee); + etx.chain_id = Some(chain_id); + etx.nonce = Some(tx.nonce.as_u64()); + if let Some(access_list) = tx.access_list { + etx.access_list = access_list + .0 .into_iter() - .map(|h256| U256::from_le_bytes(h256.0)) + .map(|item| { + let new_keys: Vec = item + .storage_keys + .into_iter() + .map(|h256| U256::from_le_bytes(h256.0)) + .collect(); + (Address::from(item.address.as_fixed_bytes()), new_keys) + }) .collect(); - (Address::from(item.address.as_fixed_bytes()), new_keys) - }) - .collect(); - } else { - env.tx.access_list = Default::default(); - } - - env.tx.transact_to = match tx.to { - Some(to_address) => TransactTo::Call(Address::from(to_address.as_fixed_bytes())), - None => TransactTo::create(), - }; - - evm.env = env.clone(); + } else { + etx.access_list = Default::default(); + } + + etx.transact_to = match tx.to { + Some(to_address) => { + TransactTo::Call(Address::from(to_address.as_fixed_bytes())) + } + None => TransactTo::create(), + }; + }) + .build(); // Construct the file writer to write the trace to let tx_number = tx.transaction_index.unwrap().0[0]; @@ -154,8 +160,8 @@ async fn main() -> anyhow::Result<()> { let writer = FlushWriter::new(Arc::clone(&inner)); // Inspect and commit the transaction to the EVM - let inspector = TracerEip3155::new(Box::new(writer), true, true); - if let Err(error) = evm.inspect_commit(inspector) { + evm.context.external.set_writer(Box::new(writer)); + if let Err(error) = evm.transact_commit() { println!("Got error: {:?}", error); } From 55fc1f7376f03dcbea8e0f0ff27fb18ebef4bafc Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 27 Dec 2023 00:01:39 +0100 Subject: [PATCH 32/52] ci fixes --- crates/primitives/src/specification.rs | 2 +- crates/revm/src/context.rs | 4 ++-- crates/revm/src/handler/mainnet/frame.rs | 2 +- crates/revm/src/inspector/handler_register.rs | 2 +- crates/revm/src/optimism/handler_register.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/primitives/src/specification.rs b/crates/primitives/src/specification.rs index e434bb17c1..e7434be7d7 100644 --- a/crates/primitives/src/specification.rs +++ b/crates/primitives/src/specification.rs @@ -274,7 +274,7 @@ mod tests { #[cfg(feature = "optimism")] #[cfg(test)] -mod tests { +mod optimism_tests { use super::*; #[test] diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index 8c9ee817d1..e1df0d2539 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -539,7 +539,7 @@ pub(crate) mod test_utils { /// Creates an evm context with a cache db backend. /// Additionally loads the mock caller account into the db, /// and sets the balance to the provided U256 value. - pub fn create_cache_db_evm_context_with_balance<'a>( + pub fn create_cache_db_evm_context_with_balance( env: Box, mut db: CacheDB, balance: U256, @@ -557,7 +557,7 @@ pub(crate) mod test_utils { } /// Creates a cached db evm context. - pub fn create_cache_db_evm_context<'a>( + pub fn create_cache_db_evm_context( env: Box, db: CacheDB, ) -> EvmContext> { diff --git a/crates/revm/src/handler/mainnet/frame.rs b/crates/revm/src/handler/mainnet/frame.rs index a5708e4d25..bedfc75d9f 100644 --- a/crates/revm/src/handler/mainnet/frame.rs +++ b/crates/revm/src/handler/mainnet/frame.rs @@ -51,7 +51,7 @@ pub fn create_first_frame( } } -/// Helper function called inside [`main_call_return`] +/// Helper function called inside [`main_frame_return`] pub fn frame_return_with_refund_flag( env: &Env, call_result: InstructionResult, diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index 4739b6657d..8a3984f3e9 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -19,7 +19,7 @@ pub trait GetInspector<'a, DB: Database> { /// Most of the functions are wrapped for Inspector usage expect /// the SubCreate and SubCall calls that got overwritten. /// -/// Few instructions handlers are wrapped twice once for `step`` and `step_end` +/// Few instructions handlers are wrapped twice once for `step` and `step_end` /// and in case of Logs and Selfdestruct wrapper is wrapped again for the /// `log` and `selfdestruct` calls. /// diff --git a/crates/revm/src/optimism/handler_register.rs b/crates/revm/src/optimism/handler_register.rs index bbd5beddc1..4d22b18ae3 100644 --- a/crates/revm/src/optimism/handler_register.rs +++ b/crates/revm/src/optimism/handler_register.rs @@ -16,7 +16,7 @@ use crate::{ use alloc::sync::Arc; use core::ops::Mul; -pub fn optimism_handle_register<'a, DB: Database, EXT>(handler: &mut EvmHandler<'a, EXT, DB>) { +pub fn optimism_handle_register(handler: &mut EvmHandler<'_, EXT, DB>) { spec_to_generic!(handler.spec_id, { // Refund is calculated differently then mainnet. handler.frame.first_frame_return = Arc::new(handle_call_return::); From b74f7e3deda084cd29c04c426888a5632154959a Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 27 Dec 2023 17:21:09 +0100 Subject: [PATCH 33/52] Flatten builder stages to generic and handler stage --- bins/revme/src/cmd/statetest/runner.rs | 1 - crates/revm/benches/bench.rs | 5 +- crates/revm/src/builder.rs | 171 ++++++++----------- crates/revm/src/evm.rs | 6 +- crates/revm/src/optimism/handler_register.rs | 3 +- documentation/src/crates/revm/builder.md | 41 ++++- 6 files changed, 110 insertions(+), 117 deletions(-) diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index 09886410a9..e89660bc9c 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -257,7 +257,6 @@ pub fn execute_test_suite( let mut evm = Evm::builder() .with_db(&mut state) .modify_env(|e| *e = env.clone()) - .without_external_context() .with_spec_id(spec_id) .build(); diff --git a/crates/revm/benches/bench.rs b/crates/revm/benches/bench.rs index 60a94e709b..73253cbae9 100644 --- a/crates/revm/benches/bench.rs +++ b/crates/revm/benches/bench.rs @@ -90,10 +90,7 @@ fn transfer(c: &mut Criterion) { g.finish(); } -fn bench_transact<'a, EXT>( - g: &mut BenchmarkGroup<'_, WallTime>, - evm: &mut Evm<'a, EXT, BenchmarkDB>, -) { +fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm<'_, EXT, BenchmarkDB>) { let state = match evm.context.evm.db.0.state { BytecodeState::Raw => "raw", BytecodeState::Checked { .. } => "checked", diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index e4f4ae7265..cbf2b84ad9 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -21,21 +21,17 @@ pub struct EvmBuilder<'a, Stage: BuilderStage, EXT, DB: Database> { /// Trait that unlocks builder stages. pub trait BuilderStage {} -/// First stage of the builder allows setting the database. -pub struct SettingDbStage; -impl BuilderStage for SettingDbStage {} +/// First stage of the builder allows setting generic variables. +/// Generic variables are database and external context. +pub struct SetGenericStage; +impl BuilderStage for SetGenericStage {} /// Second stage of the builder allows setting the external context. /// Requires the database to be set. -pub struct SettingExternalStage; -impl BuilderStage for SettingExternalStage {} +pub struct HandlerStage; +impl BuilderStage for HandlerStage {} -/// Third stage of the builder allows setting the handler. -/// Requires the database and external context to be set. -pub struct SettingHandlerStage; -impl BuilderStage for SettingHandlerStage {} - -impl<'a> Default for EvmBuilder<'a, SettingDbStage, (), EmptyDB> { +impl<'a> Default for EvmBuilder<'a, SetGenericStage, (), EmptyDB> { fn default() -> Self { Self { evm: EvmContext::new(EmptyDB::default()), @@ -46,13 +42,13 @@ impl<'a> Default for EvmBuilder<'a, SettingDbStage, (), EmptyDB> { } } -impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { +impl<'a, EXT, DB: Database> EvmBuilder<'a, SetGenericStage, EXT, DB> { /// Sets the [`EmptyDB`] as the [`Database`] that will be used by [`Evm`]. /// /// # Note /// /// When changed it will reset the handler to the mainnet. - pub fn with_empty_db(self) -> EvmBuilder<'a, SettingExternalStage, EXT, EmptyDB> { + pub fn with_empty_db(self) -> EvmBuilder<'a, SetGenericStage, EXT, EmptyDB> { EvmBuilder { evm: self.evm.with_db(EmptyDB::default()), external: self.external, @@ -66,7 +62,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { /// # Note /// /// When changed it will reset the handler to default mainnet. - pub fn with_db(self, db: ODB) -> EvmBuilder<'a, SettingExternalStage, EXT, ODB> { + pub fn with_db(self, db: ODB) -> EvmBuilder<'a, SetGenericStage, EXT, ODB> { EvmBuilder { evm: self.evm.with_db(db), external: self.external, @@ -83,7 +79,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { pub fn with_ref_db( self, db: ODB, - ) -> EvmBuilder<'a, SettingExternalStage, EXT, WrapDatabaseRef> { + ) -> EvmBuilder<'a, SetGenericStage, EXT, WrapDatabaseRef> { EvmBuilder { evm: self.evm.with_db(WrapDatabaseRef(db)), external: self.external, @@ -92,24 +88,12 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingDbStage, EXT, DB> { phantom: PhantomData, } } -} - -impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternalStage, EXT, DB> { - /// Sets empty external context. - pub fn without_external_context(self) -> EvmBuilder<'a, SettingHandlerStage, (), DB> { - EvmBuilder { - evm: self.evm, - external: (), - handler: Handler::mainnet_with_spec(self.handler.spec_id), - phantom: PhantomData, - } - } /// Sets the external context that will be used by [`Evm`]. pub fn with_external_context( self, external: OEXT, - ) -> EvmBuilder<'a, SettingHandlerStage, OEXT, DB> { + ) -> EvmBuilder<'a, SetGenericStage, OEXT, DB> { EvmBuilder { evm: self.evm, external, @@ -118,17 +102,11 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternalStage, EXT, DB> { } } - /// Modify Database of EVM. - pub fn modify_db(mut self, f: impl FnOnce(&mut DB)) -> Self { - f(&mut self.evm.db); - self - } - /// Appends the handler register to the handler. pub fn append_handler_register( mut self, handle_register: register::HandleRegister<'a, EXT, DB>, - ) -> EvmBuilder<'_, SettingHandlerStage, EXT, DB> { + ) -> EvmBuilder<'_, HandlerStage, EXT, DB> { self.handler .append_handle_register(register::HandleRegisters::Plain(handle_register)); EvmBuilder { @@ -156,25 +134,12 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingExternalStage, EXT, DB> { phantom: PhantomData, } } - - /// Sets specification Id , that will mark the version of EVM. - /// It represent the hard fork of ethereum. - pub fn with_spec_id(mut self, spec_id: SpecId) -> Self { - self.handler = self.handler.change_spec_id(spec_id); - EvmBuilder { - evm: self.evm, - external: self.external, - handler: self.handler, - - phantom: PhantomData, - } - } } -impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { +impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { /// Creates new build from EVM, evm is consumed and all field are moved to Builder. /// - /// Builder is in SettingHandlerStage and both database and external are set. + /// Builder is in HandlerStage and both database and external are set. pub fn new(evm: Evm<'a, EXT, DB>) -> Self { Self { evm: evm.context.evm, @@ -184,25 +149,6 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { } } - /// Modify Database of EVM. - pub fn modify_db(mut self, f: impl FnOnce(&mut DB)) -> Self { - f(&mut self.evm.db); - self - } - - /// Reset handler - pub fn reset_handler_with_external_context( - self, - external_context: OEXT, - ) -> EvmBuilder<'a, SettingHandlerStage, OEXT, DB> { - EvmBuilder { - evm: self.evm, - external: external_context, - handler: Handler::mainnet_with_spec(self.handler.spec_id), - phantom: PhantomData, - } - } - /// Appends the handler register to the handler. pub fn append_handler_register( mut self, @@ -225,7 +171,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { } /// Sets the [`EmptyDB`] and resets the [`Handler`] - pub fn reset_handler_with_empty_db(self) -> EvmBuilder<'a, SettingHandlerStage, EXT, EmptyDB> { + pub fn reset_handler_with_empty_db(self) -> EvmBuilder<'a, HandlerStage, EXT, EmptyDB> { EvmBuilder { evm: self.evm.with_db(EmptyDB::default()), external: self.external, @@ -238,7 +184,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { pub fn reset_handler_with_db( self, db: ODB, - ) -> EvmBuilder<'a, SettingExternalStage, EXT, ODB> { + ) -> EvmBuilder<'a, SetGenericStage, EXT, ODB> { EvmBuilder { evm: self.evm.with_db(db), external: self.external, @@ -248,23 +194,11 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { } } - /// Sets specification Id , that will mark the version of EVM. - /// It represent the hard fork of ethereum. - /// - /// # Note - /// - /// When changed it will reapply all handle registers, this can be - /// expensive operation depending on registers. - pub fn with_spec_id(mut self, spec_id: SpecId) -> Self { - self.handler = self.handler.change_spec_id(spec_id); - self - } - /// Resets [`Handler`] and sets the [`DatabaseRef`] that will be used by [`Evm`]. pub fn reset_handler_with_ref_db( self, db: ODB, - ) -> EvmBuilder<'a, SettingExternalStage, EXT, WrapDatabaseRef> { + ) -> EvmBuilder<'a, SetGenericStage, EXT, WrapDatabaseRef> { EvmBuilder { evm: self.evm.with_db(WrapDatabaseRef(db)), external: self.external, @@ -273,6 +207,19 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { phantom: PhantomData, } } + + /// Resets [`Handler`] and sets new `External` context type. + pub fn reset_handler_with_external_context( + self, + external: OEXT, + ) -> EvmBuilder<'a, SetGenericStage, OEXT, EmptyDB> { + EvmBuilder { + evm: self.evm.with_db(EmptyDB::default()), + external, + handler: Handler::mainnet_with_spec(self.handler.spec_id), + phantom: PhantomData, + } + } } // Accessed always. @@ -287,42 +234,73 @@ impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> handler: self.handler, } } - /// Modify Environment of EVM. + + /// Sets specification Id , that will mark the version of EVM. + /// It represent the hard fork of ethereum. + /// + /// # Note + /// + /// When changed it will reapply all handle registers, this can be + /// expensive operation depending on registers. + pub fn with_spec_id(mut self, spec_id: SpecId) -> Self { + self.handler = self.handler.change_spec_id(spec_id); + EvmBuilder { + evm: self.evm, + external: self.external, + handler: self.handler, + + phantom: PhantomData, + } + } + + /// Allows modification of Evm Database. + pub fn modify_db(mut self, f: impl FnOnce(&mut DB)) -> Self { + f(&mut self.evm.db); + self + } + + /// Allows modification of external context. + pub fn modify_external_context(mut self, f: impl FnOnce(&mut EXT)) -> Self { + f(&mut self.external); + self + } + + /// Allows modification of Evm Environment. pub fn modify_env(mut self, f: impl FnOnce(&mut Env)) -> Self { f(&mut self.evm.env); self } - /// Modify Transaction Environment of EVM. + /// Allows modification of Evm's Transaction Environment. pub fn modify_tx_env(mut self, f: impl FnOnce(&mut TxEnv)) -> Self { f(&mut self.evm.env.tx); self } - /// Modify Block Environment of EVM. + /// Allows modification of Evm's Block Environment. pub fn modify_block_env(mut self, f: impl FnOnce(&mut BlockEnv)) -> Self { f(&mut self.evm.env.block); self } - /// Modify Config Environment of EVM. + /// Allows modification of Evm's Config Environment. pub fn modify_cfg_env(mut self, f: impl FnOnce(&mut CfgEnv)) -> Self { f(&mut self.evm.env.cfg); self } - /// Clear Environment of EVM. + /// Clears Environment of EVM. pub fn with_clear_env(mut self) -> Self { self.evm.env.clear(); self } - /// Clear Transaction environment of EVM. + /// Clears Transaction environment of EVM. pub fn with_clear_tx_env(mut self) -> Self { self.evm.env.tx.clear(); self } - /// Clear Block environment of EVM. + /// Clears Block environment of EVM. pub fn with_clear_block_env(mut self) -> Self { self.evm.env.block.clear(); self @@ -345,10 +323,7 @@ mod test { // build with_db Evm::builder().with_db(EmptyDB::default()).build(); // build with empty external - Evm::builder() - .with_empty_db() - .without_external_context() - .build(); + Evm::builder().with_empty_db().build(); // build with some external Evm::builder() .with_empty_db() @@ -357,7 +332,6 @@ mod test { // build with spec Evm::builder() .with_empty_db() - .without_external_context() .with_spec_id(SpecId::HOMESTEAD) .build(); @@ -365,7 +339,6 @@ mod test { Evm::builder() .with_empty_db() .modify_tx_env(|tx| tx.gas_limit = 10) - .without_external_context() .build(); Evm::builder().modify_tx_env(|tx| tx.gas_limit = 10).build(); Evm::builder() @@ -374,14 +347,13 @@ mod test { .build(); Evm::builder() .with_empty_db() - .without_external_context() .modify_tx_env(|tx| tx.gas_limit = 10) .build(); // with inspector handle Evm::builder() .with_empty_db() - .with_external_context(NoOpInspector::default()) + .with_external_context(NoOpInspector) .append_handler_register(inspector_handle_register) .build(); } @@ -390,7 +362,6 @@ mod test { fn build_modify_build() { let evm = Evm::builder() .with_empty_db() - .without_external_context() .with_spec_id(SpecId::HOMESTEAD) .build(); diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 55dcdb0693..b7298d97f6 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,5 +1,5 @@ use crate::{ - builder::{EvmBuilder, SettingDbStage, SettingHandlerStage}, + builder::{EvmBuilder, HandlerStage, SetGenericStage}, db::{Database, DatabaseCommit, EmptyDB}, handler::Handler, interpreter::{ @@ -52,7 +52,7 @@ impl<'a, EXT, DB: Database + DatabaseCommit> Evm<'a, EXT, DB> { impl<'a> Evm<'a, (), EmptyDB> { /// Returns evm builder with empty database and empty external context. - pub fn builder() -> EvmBuilder<'a, SettingDbStage, (), EmptyDB> { + pub fn builder() -> EvmBuilder<'a, SetGenericStage, (), EmptyDB> { EvmBuilder::default() } } @@ -65,7 +65,7 @@ impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { /// Allow for evm setting to be modified by feeding current evm /// into the builder for modifications. - pub fn modify(self) -> EvmBuilder<'a, SettingHandlerStage, EXT, DB> { + pub fn modify(self) -> EvmBuilder<'a, HandlerStage, EXT, DB> { EvmBuilder::new(self) } } diff --git a/crates/revm/src/optimism/handler_register.rs b/crates/revm/src/optimism/handler_register.rs index 4d22b18ae3..fcf7fdbdec 100644 --- a/crates/revm/src/optimism/handler_register.rs +++ b/crates/revm/src/optimism/handler_register.rs @@ -349,8 +349,7 @@ mod tests { assert_eq!(gas.refunded(), 0); } - // TODO - //#[test] + #[test] fn test_consume_gas_with_refund() { let mut env = Env::default(); env.tx.gas_limit = 100; diff --git a/documentation/src/crates/revm/builder.md b/documentation/src/crates/revm/builder.md index 74aef5a7ee..067286aaea 100644 --- a/documentation/src/crates/revm/builder.md +++ b/documentation/src/crates/revm/builder.md @@ -1,18 +1,45 @@ # Evm Builder -It creates the EVM and applies different handler, and allows setting external context and custom logic. +It build or modifies the EVM and applies different handler, and allows setting external context and registering handler custom logic. -`Evm` inside revm consist of the few parts `Context` and `Handler`. `Context` is additionally split between `EvmContext` and `External` context. Read here for more information on [`Evm`](./evm.md) internals. +`Evm` inside revm consist of the few parts `Context` and `Handler`. `Context` is additionally split between `EvmContext` that contains generic `Database` and `External` context that is generic without restrain. Read here for more information on [`Evm`](./evm.md) internals. -Builder ties dependencies between generic `Database`, `External` context and `Spec` and allows handle registers to be added. As there is a generic dependency between them setting `Database` will reset `External` context and `Handler` field while setting `External` field would reset just a `Handler` as it will become invalid. Note that Database will never be reset. +Builder ties dependencies between generic `Database`, `External` context and `Spec` and allows handle registers to be added that implement logic on those generics. As they are interconected setting `Database` or `ExternalContext` wound reset handle registers, and builder stages are introduced to mitigate those misuses. - -Simple example of using `EvmBuilder` is +Simple example of using `EvmBuilder`: ``` -Evm::build().with_empty_db().without_external_context() +let evm = Evm::builder().with_empty_db().without_external_context().build(); ``` -`EvmBuilder` has three stages that will gradually unlock more functionality, first is setting the database, second is setting external context and third is setting/registering the handler registers. Environment can be modified in anytime with `modify_*`' functions that take a closure. +## Builder Stages + +List of the builder stages: + +* SetGenericStage: Initial stage that allows setting the database and external context. +* HandlerStage: Allows pushing handler registers, requiring both the database and external context to be set. + +Stages has functions that are specific to that stage and there are functions that are group common to both stages. + +A few functions that are specific for stage are just a renames to make user more aware of what underlying function does. For example in `SettingDbStage` we have `with_db` function while in `HandlerStage` we have `reset_handler_with_db`, both of them set the database but the latter also resets the handler. + +There is naming convention for the functions that are common to both stages: +* `with_*` are mostly found in `SetGenericStage` and are used to set the generics. +* `append_*` are used to push handler registers. +* `modify_*` are used to modify the database, external context or Env, those are common to both stages. +* `reset_handler_with_*` is used if we want to change some of the generic types this will reset the handler registers. +* `build` is to create the Evm. +* `with_spec_id` is used to set the spec id, this is common to both stages and will create new handler and reapply all the handler registers. +* `clear_*` allows setting default values for Environment. + + +# Creating and modification of Evm + +Evm implements function that allows using of EvmBuilder without even knowing. Most obvious one is `Evm::builder()` that would create a new builder with default values. + +Additionally function that is very important is `evm.modify()` that allows modifying the Evm. It will return the builder and will allow to modify the Evm. This function is used in the `Evm::execute` function to modify the Evm before execution. + + +# Examples From b24c19cb749cbaaf6b7b433c11ae1d141b4277fd Mon Sep 17 00:00:00 2001 From: rakita Date: Sat, 30 Dec 2023 16:49:34 +0100 Subject: [PATCH 34/52] EvmBuilder doc and refactor fn access --- bins/revme/src/cmd/statetest/runner.rs | 2 +- crates/revm/src/builder.rs | 113 ++++++++++----------- crates/revm/src/evm.rs | 4 +- crates/revm/src/inspector/customprinter.rs | 2 +- documentation/src/crates/revm/builder.md | 94 +++++++++++++---- documentation/src/crates/revm/evm.md | 18 ++-- 6 files changed, 141 insertions(+), 92 deletions(-) diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index e89660bc9c..cdf71ffa76 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -257,7 +257,7 @@ pub fn execute_test_suite( let mut evm = Evm::builder() .with_db(&mut state) .modify_env(|e| *e = env.clone()) - .with_spec_id(spec_id) + .spec_id(spec_id) .build(); // do the deed diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index cbf2b84ad9..6724a918c9 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -101,39 +101,6 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SetGenericStage, EXT, DB> { phantom: PhantomData, } } - - /// Appends the handler register to the handler. - pub fn append_handler_register( - mut self, - handle_register: register::HandleRegister<'a, EXT, DB>, - ) -> EvmBuilder<'_, HandlerStage, EXT, DB> { - self.handler - .append_handle_register(register::HandleRegisters::Plain(handle_register)); - EvmBuilder { - evm: self.evm, - external: self.external, - handler: self.handler, - - phantom: PhantomData, - } - } - - /// Register Handler that modifies the behavior of EVM. - /// Check [`Handler`] for more information. - pub fn append_handler_register_box( - mut self, - handle_register: register::HandleRegisterBox<'a, EXT, DB>, - ) -> Self { - self.handler - .append_handle_register(register::HandleRegisters::Box(handle_register)); - EvmBuilder { - evm: self.evm, - external: self.external, - handler: self.handler, - - phantom: PhantomData, - } - } } impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { @@ -149,27 +116,6 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { } } - /// Appends the handler register to the handler. - pub fn append_handler_register( - mut self, - handle_register: register::HandleRegister<'a, EXT, DB>, - ) -> Self { - self.handler - .append_handle_register(register::HandleRegisters::Plain(handle_register)); - self - } - - /// Register Handler that modifies the behavior of EVM. - /// Check [`Handler`] for more information. - pub fn append_handler_register_box( - mut self, - handle_register: register::HandleRegisterBox<'a, EXT, DB>, - ) -> Self { - self.handler - .append_handle_register(register::HandleRegisters::Box(handle_register)); - self - } - /// Sets the [`EmptyDB`] and resets the [`Handler`] pub fn reset_handler_with_empty_db(self) -> EvmBuilder<'a, HandlerStage, EXT, EmptyDB> { EvmBuilder { @@ -235,6 +181,39 @@ impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> } } + /// Appends the handler register to the handler. + pub fn append_handler_register( + mut self, + handle_register: register::HandleRegister<'a, EXT, DB>, + ) -> EvmBuilder<'_, HandlerStage, EXT, DB> { + self.handler + .append_handle_register(register::HandleRegisters::Plain(handle_register)); + EvmBuilder { + evm: self.evm, + external: self.external, + handler: self.handler, + + phantom: PhantomData, + } + } + + /// Register Handler that modifies the behavior of EVM. + /// Check [`Handler`] for more information. + pub fn append_handler_register_box( + mut self, + handle_register: register::HandleRegisterBox<'a, EXT, DB>, + ) -> EvmBuilder<'_, HandlerStage, EXT, DB> { + self.handler + .append_handle_register(register::HandleRegisters::Box(handle_register)); + EvmBuilder { + evm: self.evm, + external: self.external, + handler: self.handler, + + phantom: PhantomData, + } + } + /// Sets specification Id , that will mark the version of EVM. /// It represent the hard fork of ethereum. /// @@ -242,7 +221,7 @@ impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> /// /// When changed it will reapply all handle registers, this can be /// expensive operation depending on registers. - pub fn with_spec_id(mut self, spec_id: SpecId) -> Self { + pub fn spec_id(mut self, spec_id: SpecId) -> Self { self.handler = self.handler.change_spec_id(spec_id); EvmBuilder { evm: self.evm, @@ -311,7 +290,8 @@ impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> mod test { use super::SpecId; use crate::{ - db::EmptyDB, inspector::inspector_handle_register, inspectors::NoOpInspector, Evm, + db::EmptyDB, inspector::inspector_handle_register, inspectors::NoOpInspector, Context, Evm, + EvmContext, }; #[test] @@ -332,7 +312,7 @@ mod test { // build with spec Evm::builder() .with_empty_db() - .with_spec_id(SpecId::HOMESTEAD) + .spec_id(SpecId::HOMESTEAD) .build(); // with with Env change in multiple places @@ -356,16 +336,31 @@ mod test { .with_external_context(NoOpInspector) .append_handler_register(inspector_handle_register) .build(); + + // create the builder + let evm = Evm::builder() + .with_db(EmptyDB::default()) + .with_external_context(NoOpInspector) + .append_handler_register(inspector_handle_register) + // this would not compile + // .with_db(..) + .build(); + + let Context { + external, + evm: EvmContext { db, .. }, + } = evm.into_context(); + let _ = (external, db); } #[test] fn build_modify_build() { let evm = Evm::builder() .with_empty_db() - .with_spec_id(SpecId::HOMESTEAD) + .spec_id(SpecId::HOMESTEAD) .build(); - let evm = evm.modify().with_spec_id(SpecId::FRONTIER).build(); + let evm = evm.modify().spec_id(SpecId::FRONTIER).build(); let _ = evm .modify() .modify_tx_env(|tx| tx.chain_id = Some(2)) diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index b7298d97f6..5437f1a279 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -41,7 +41,7 @@ where } } -impl<'a, EXT, DB: Database + DatabaseCommit> Evm<'a, EXT, DB> { +impl Evm<'_, EXT, DB> { /// Commit the changes to the database. pub fn transact_commit(&mut self) -> Result> { let ResultAndState { result, state } = self.transact()?; @@ -128,7 +128,7 @@ impl Evm<'_, EXT, DB> { if self.spec_id() == spec_id { return self; } - self.modify().with_spec_id(spec_id).build() + self.modify().spec_id(spec_id).build() } /// Returns internal database and external struct. diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs index 170a6ffbad..fc65544b56 100644 --- a/crates/revm/src/inspector/customprinter.rs +++ b/crates/revm/src/inspector/customprinter.rs @@ -147,7 +147,7 @@ mod test { tx.value = crate::primitives::U256::ZERO; }) .with_external_context(CustomPrintTracer::default()) - .with_spec_id(SpecId::BERLIN) + .spec_id(SpecId::BERLIN) .append_handler_register(inspector_handle_register) .build(); diff --git a/documentation/src/crates/revm/builder.md b/documentation/src/crates/revm/builder.md index 067286aaea..75932ac88e 100644 --- a/documentation/src/crates/revm/builder.md +++ b/documentation/src/crates/revm/builder.md @@ -9,37 +9,93 @@ Builder ties dependencies between generic `Database`, `External` context and `Sp Simple example of using `EvmBuilder`: -``` -let evm = Evm::builder().with_empty_db().without_external_context().build(); +```rust +use revm::evm::Evm; + +// build Evm with default values. +let mut evm = Evm::builder().build(); +evm.transact(); ``` ## Builder Stages -List of the builder stages: +There are two builder stages that are used to mitigate potential misuse of the builder: -* SetGenericStage: Initial stage that allows setting the database and external context. -* HandlerStage: Allows pushing handler registers, requiring both the database and external context to be set. +* `SetGenericStage`: Initial stage that allows setting the database and external context. +* `HandlerStage`: Allows setting the handler registers but is explicit about setting new generic type as it will void the handler registers. -Stages has functions that are specific to that stage and there are functions that are group common to both stages. +Functions from one stage are just a renamed function from other stage, it is made so that user is more aware of what underlying function does. Example of this, in `SettingDbStage` we have `with_db` function while in `HandlerStage` we have `reset_handler_with_db`, both of them set the database but the latter also resets the handler. There are multiple functions that are common to both stages as in `build`. -A few functions that are specific for stage are just a renames to make user more aware of what underlying function does. For example in `SettingDbStage` we have `with_db` function while in `HandlerStage` we have `reset_handler_with_db`, both of them set the database but the latter also resets the handler. +There is naming convention for the functions that can be found in builder: +* In both stages we have: + * `build` is to create the Evm. + * `spec_id` is used to set the spec id, this is common to both stages and will create new mainnet handler and reapply all the handler registers. + * `modify_*` are used to modify the database, external context or Env. + * `clear_*` allows setting default values for Environment. + * `append_handler_register_*` are used to push handler registers. This will transition the builder to the `HandlerStage`. +* `SetGenericStage` have: + * `with_*` are found in `SetGenericStage` and are used to set the generics. +* `HandlerStage` have: + * `reset_handler_with_*` is used if we want to change some of the generic types this will reset the handler registers. This will transition the builder to the `SetGenericStage`. -There is naming convention for the functions that are common to both stages: -* `with_*` are mostly found in `SetGenericStage` and are used to set the generics. -* `append_*` are used to push handler registers. -* `modify_*` are used to modify the database, external context or Env, those are common to both stages. -* `reset_handler_with_*` is used if we want to change some of the generic types this will reset the handler registers. -* `build` is to create the Evm. -* `with_spec_id` is used to set the spec id, this is common to both stages and will create new handler and reapply all the handler registers. -* `clear_*` allows setting default values for Environment. +# Creating and modification of Evm +Evm implements function that allows using of EvmBuilder without even knowing that it exist. Most obvious one is `Evm::builder()` that would create a new builder with default values. -# Creating and modification of Evm +Additionally function that is very important is `evm.modify()` that allows modifying the Evm. It will return the builder and will allow user to modify the Evm. -Evm implements function that allows using of EvmBuilder without even knowing. Most obvious one is `Evm::builder()` that would create a new builder with default values. +# Examples -Additionally function that is very important is `evm.modify()` that allows modifying the Evm. It will return the builder and will allow to modify the Evm. This function is used in the `Evm::execute` function to modify the Evm before execution. +Example of using builder to create Evm with inspector: +```rust + use crate::{ + db::EmptyDB, Context, EvmContext, inspector::inspector_handle_register, inspectors::NoOpInspector, Evm, + }; + + // create the builder + let evm = Evm::builder() + .with_db(EmptyDB::default()) + .with_external_context(NoOpInspector) + // register will modify Handler and call NoOpInspector. + .append_handler_register(inspector_handle_register) + // this would not compile as we already locked the builder generics. + // .with_db(..) + // alternative fn is reset_handler_with_db(..) + .build(); + + // extract evm context. + let Context { + external, + evm: EvmContext { db, .. }, + } = evm.into_context(); +``` +Example of changing spec id and Environment of already build evm. +```rust + use crate::{Evm,SpecId::BERLIN}; -# Examples + // create default evm + let evm = Evm::builder().build(); + + // modify evm spec + let evm = evm.modify().spec_id(BERLIN).build(); + + // shortcut for above is + let mut evm = evm.modify_spec_id(BERLIN); + + //execute the evm + evm.transact(); + + // Example of modifying the tx env. + let mut evm = evm.modify().modify_tx_env(|env| env.gas_price = 0.into()).build(); + + // execute the evm with modified tx env. + evm.transact(); +``` + +## Appending handler registers + +Handler registers are simple function that allow modifying the `Handler` logic by replacing +the handler functions. They are used to add custom logic to the evm execution but as they are free to modify the `Handler` in any form they want there can be conflicts if handlers that override the same function are added. +Most common use case for adding new logic to `Handler` is `Inspector` that is used to inspect the execution of the evm. Example of this can be found in [`Inspector`](../revm_inspector/inspector.md) documentation. \ No newline at end of file diff --git a/documentation/src/crates/revm/evm.md b/documentation/src/crates/revm/evm.md index 49fc0f933d..8344f5b994 100644 --- a/documentation/src/crates/revm/evm.md +++ b/documentation/src/crates/revm/evm.md @@ -26,13 +26,11 @@ To dive deeper into the `Evm` logic check [`Handler`](../handler.md) documentat # Functionalities -`Evm` is build with a `EvmBuilder` that allows setting of `Database`, `External` context and `Handler`. Builder is created with `Evm::builder()` function. For more information on building check [`EvmBuilder`](./builder.md) documentation. - -After building `Evm` it can be used to execute transactions. There are three functions that can be used to execute transactions: -* preverify - that only preverifies transaction information. -* transact preverified - is next step after preverification that executes transaction. -* transact - it calls both preverifies and it executes transaction. - -If we want to modify `Evm` for example change the specification ID we can use `.modify()` function that would give us the `EvmBuilder` and we can set new specification that would build new `Evm` from it, as setting new specification would need to reset the `Handler` functions. - -More on the builder can be found in [`EvmBuilder`](./builder.md) documentation. \ No newline at end of file +Function of Evm is to start execution but setting up what Evm is going to execute is done by `EvmBuilder`. + +Main function inside evm are: +* `preverify` - that only preverifies transaction information. +* `transact preverified` - is next step after preverification that executes transaction. +* `transact` - it calls both preverifies and it executes transaction. +* `builder` and `modify` function - allows building or modifying the Evm, more on this can be found in [`EvmBuilder`](./builder.md) documentation. This is main way of creating Evm. +* `into_context` - is used we want to get the `Context` from the Evm. \ No newline at end of file From 7024cad77310951cb16abf329026803574c2b200 Mon Sep 17 00:00:00 2001 From: rakita Date: Sat, 30 Dec 2023 17:40:00 +0100 Subject: [PATCH 35/52] ignore rust code in book --- book.toml | 2 +- documentation/src/crates/revm/builder.md | 82 ++++++++++++------------ 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/book.toml b/book.toml index ffe9273021..8395e94b05 100644 --- a/book.toml +++ b/book.toml @@ -1,5 +1,5 @@ [book] -authors = ["Colin Roberts, Waylon Jepsen"] +authors = ["Colin Roberts, Waylon Jepsen", "Dragan Rakita"] language = "en" multilingual = false src = "documentation/src" diff --git a/documentation/src/crates/revm/builder.md b/documentation/src/crates/revm/builder.md index 75932ac88e..141acdfe15 100644 --- a/documentation/src/crates/revm/builder.md +++ b/documentation/src/crates/revm/builder.md @@ -9,12 +9,12 @@ Builder ties dependencies between generic `Database`, `External` context and `Sp Simple example of using `EvmBuilder`: -```rust -use revm::evm::Evm; +```rust,ignore + use crate::evm::Evm; -// build Evm with default values. -let mut evm = Evm::builder().build(); -evm.transact(); + // build Evm with default values. + let mut evm = Evm::builder().build(); + evm.transact(); ``` ## Builder Stages @@ -47,50 +47,50 @@ Additionally function that is very important is `evm.modify()` that allows modif # Examples Example of using builder to create Evm with inspector: -```rust - use crate::{ - db::EmptyDB, Context, EvmContext, inspector::inspector_handle_register, inspectors::NoOpInspector, Evm, - }; - - // create the builder - let evm = Evm::builder() - .with_db(EmptyDB::default()) - .with_external_context(NoOpInspector) - // register will modify Handler and call NoOpInspector. - .append_handler_register(inspector_handle_register) - // this would not compile as we already locked the builder generics. - // .with_db(..) - // alternative fn is reset_handler_with_db(..) - .build(); - - // extract evm context. - let Context { - external, - evm: EvmContext { db, .. }, - } = evm.into_context(); +```rust,ignore + use crate::{ + db::EmptyDB, Context, EvmContext, inspector::inspector_handle_register, inspectors::NoOpInspector, Evm, + }; + + // create the builder + let evm = Evm::builder() + .with_db(EmptyDB::default()) + .with_external_context(NoOpInspector) + // register will modify Handler and call NoOpInspector. + .append_handler_register(inspector_handle_register) + // this would not compile as we already locked the builder generics. + // .with_db(..) + // alternative fn is reset_handler_with_db(..) + .build(); + + // extract evm context. + let Context { + external, + evm: EvmContext { db, .. }, + } = evm.into_context(); ``` Example of changing spec id and Environment of already build evm. -```rust - use crate::{Evm,SpecId::BERLIN}; +```rust,ignore + use crate::{Evm,SpecId::BERLIN}; - // create default evm - let evm = Evm::builder().build(); + // create default evm + let evm = Evm::builder().build(); - // modify evm spec - let evm = evm.modify().spec_id(BERLIN).build(); + // modify evm spec + let evm = evm.modify().spec_id(BERLIN).build(); - // shortcut for above is - let mut evm = evm.modify_spec_id(BERLIN); - - //execute the evm - evm.transact(); + // shortcut for above is + let mut evm = evm.modify_spec_id(BERLIN); - // Example of modifying the tx env. - let mut evm = evm.modify().modify_tx_env(|env| env.gas_price = 0.into()).build(); + //execute the evm + evm.transact(); - // execute the evm with modified tx env. - evm.transact(); + // Example of modifying the tx env. + let mut evm = evm.modify().modify_tx_env(|env| env.gas_price = 0.into()).build(); + + // execute the evm with modified tx env. + evm.transact(); ``` ## Appending handler registers From a704c704d40158c0a830bba8dceebe2cddf887d4 Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 3 Jan 2024 20:11:50 +0100 Subject: [PATCH 36/52] make revme compile, will refactor this in future --- bins/revme/src/cmd/statetest/runner.rs | 165 ++++++++++-------- crates/interpreter/src/instructions/opcode.rs | 6 - crates/revm/src/builder.rs | 4 +- documentation/src/crates/revm/builder.md | 24 +-- documentation/src/crates/revm/evm.md | 4 +- 5 files changed, 109 insertions(+), 94 deletions(-) diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index cdf71ffa76..97c424dfb8 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -1,6 +1,6 @@ use super::{ merkle_trie::{log_rlp_hash, state_merkle_trie_root}, - models::{SpecName, TestSuite}, + models::{SpecName, Test, TestSuite}, }; use indicatif::ProgressBar; use revm::{ @@ -9,12 +9,13 @@ use revm::{ inspectors::TracerEip3155, interpreter::CreateScheme, primitives::{ - address, b256, calc_excess_blob_gas, keccak256, Bytecode, Env, HashMap, SpecId, TransactTo, - B256, U256, + address, b256, calc_excess_blob_gas, keccak256, Bytecode, EVMResultGeneric, Env, + ExecutionResult, HashMap, SpecId, TransactTo, B256, U256, }, Evm, State, }; use std::{ + convert::Infallible, io::stdout, path::{Path, PathBuf}, sync::atomic::Ordering, @@ -99,6 +100,65 @@ fn skip_test(path: &Path) -> bool { ) || path_str.contains("stEOF") } +fn check_evm_execution( + test: &Test, + test_name: &str, + exec_result: &EVMResultGeneric, + evm: &Evm<'_, EXT, &mut State>, +) -> Result<(), TestError> { + // if we expect exception revm should return error from execution. + // So we do not check logs and state root. + // + // Note that some tests that have exception and run tests from before state clear + // would touch the caller account and make it appear in state root calculation. + // This is not something that we would expect as invalid tx should not touch state. + // but as this is a cleanup of invalid tx it is not properly defined and in the end + // it does not matter. + // Test where this happens: `tests/GeneralStateTests/stTransactionTest/NoSrcAccountCreate.json` + // and you can check that we have only two "hash" values for before and after state clear. + match (&test.expect_exception, exec_result) { + // do nothing + (None, Ok(_)) => (), + // return okay, exception is expected. + (Some(_), Err(_)) => return Ok(()), + _ => { + return Err(TestError { + name: test_name.to_string(), + kind: TestErrorKind::UnexpectedException { + expected_exception: test.expect_exception.clone(), + got_exception: exec_result.clone().err().map(|e| e.to_string()), + }, + }); + } + } + + let logs_root = log_rlp_hash(&exec_result.as_ref().map(|r| r.logs()).unwrap_or_default()); + + if logs_root != test.logs { + return Err(TestError { + name: test_name.to_string(), + kind: TestErrorKind::LogsRootMismatch { + got: logs_root, + expected: test.logs, + }, + }); + } + + let state_root = state_merkle_trie_root(evm.context.evm.db.cache.trie_account()); + + if state_root != test.hash { + return Err(TestError { + name: test_name.to_string(), + kind: TestErrorKind::StateRootMismatch { + got: state_root, + expected: test.hash, + }, + }); + } + + Ok(()) +} + pub fn execute_test_suite( path: &Path, elapsed: &Arc>, @@ -262,74 +322,33 @@ pub fn execute_test_suite( // do the deed let timer = Instant::now(); - let exec_result = if trace { - //evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)) - unimplemented!(); + let (e, exec_result) = if trace { + let mut evm = evm + .modify() + .reset_handler_with_external_context(TracerEip3155::new( + Box::new(stdout()), + false, + true, + )) + .append_handler_register(inspector_handle_register) + .build(); + let res = evm.transact_commit(); + + let Err(e) = check_evm_execution(&test, &name, &res, &evm) else { + continue; + }; + // reset external context + (e, res) } else { - evm.transact_commit() - }; - *elapsed.lock().unwrap() += timer.elapsed(); + let res = evm.transact_commit(); - // validate results - // this is in a closure so we can have a common printing routine for errors - let check = || { - // if we expect exception revm should return error from execution. - // So we do not check logs and state root. - // - // Note that some tests that have exception and run tests from before state clear - // would touch the caller account and make it appear in state root calculation. - // This is not something that we would expect as invalid tx should not touch state. - // but as this is a cleanup of invalid tx it is not properly defined and in the end - // it does not matter. - // Test where this happens: `tests/GeneralStateTests/stTransactionTest/NoSrcAccountCreate.json` - // and you can check that we have only two "hash" values for before and after state clear. - match (&test.expect_exception, &exec_result) { - // do nothing - (None, Ok(_)) => (), - // return okay, exception is expected. - (Some(_), Err(_)) => return Ok(()), - _ => { - return Err(TestError { - name: name.clone(), - kind: TestErrorKind::UnexpectedException { - expected_exception: test.expect_exception.clone(), - got_exception: exec_result.clone().err().map(|e| e.to_string()), - }, - }); - } - } - - let logs_root = - log_rlp_hash(&exec_result.as_ref().map(|r| r.logs()).unwrap_or_default()); - - if logs_root != test.logs { - return Err(TestError { - name: name.clone(), - kind: TestErrorKind::LogsRootMismatch { - got: logs_root, - expected: test.logs, - }, - }); - } - - let state_root = - state_merkle_trie_root(evm.context.evm.db.cache.trie_account()); - - if state_root != test.hash { - return Err(TestError { - name: name.clone(), - kind: TestErrorKind::StateRootMismatch { - got: state_root, - expected: test.hash, - }, - }); - } - - Ok(()) + // dump state and traces if test failed + let Err(e) = check_evm_execution(&test, &name, &res, &evm) else { + continue; + }; + (e, res) }; - - // dump state and traces if test failed - let Err(e) = check() else { continue }; + *elapsed.lock().unwrap() += timer.elapsed(); // print only once static FAILED: AtomicBool = AtomicBool::new(false); @@ -343,16 +362,16 @@ pub fn execute_test_suite( spec_id, revm::primitives::SpecId::SPURIOUS_DRAGON, )); - let state: State = revm::db::StateBuilder::default() + let state = revm::db::State::builder() .with_cached_prestate(cache) + .with_bundle_update() .build(); - // evm.database(&mut state); let path = path.display(); println!("\nTraces:"); - let mut evm = evm - .modify() - .reset_handler_with_db(state) + let mut evm = Evm::builder() + .spec_id(spec_id) + .with_db(state) .with_external_context(TracerEip3155::new(Box::new(stdout()), false, false)) .append_handler_register(inspector_handle_register) .build(); diff --git a/crates/interpreter/src/instructions/opcode.rs b/crates/interpreter/src/instructions/opcode.rs index 0644a4bdd2..f6265d122b 100644 --- a/crates/interpreter/src/instructions/opcode.rs +++ b/crates/interpreter/src/instructions/opcode.rs @@ -16,18 +16,12 @@ pub type Instruction = fn(&mut Interpreter, &mut H); /// 256 EVM opcodes. pub type InstructionTable = [Instruction; 256]; -/// Arc over plain instruction table -//pub type InstructionTableArc = Arc>; - /// EVM opcode function signature. pub type BoxedInstruction<'a, H> = Box; /// A table of instructions. pub type BoxedInstructionTable<'a, H> = [BoxedInstruction<'a, H>; 256]; -// /// Arc over instruction table -// pub type BoxedInstructionTableArc<'a, H> = Arc>; - /// Instruction set that contains plain instruction table that contains simple `fn` function pointer. /// and Boxed `Fn` variant that contains `Box` function pointer that can be used with closured. /// diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index 6724a918c9..5735a58cf1 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -158,9 +158,9 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { pub fn reset_handler_with_external_context( self, external: OEXT, - ) -> EvmBuilder<'a, SetGenericStage, OEXT, EmptyDB> { + ) -> EvmBuilder<'a, SetGenericStage, OEXT, DB> { EvmBuilder { - evm: self.evm.with_db(EmptyDB::default()), + evm: self.evm, external, handler: Handler::mainnet_with_spec(self.handler.spec_id), phantom: PhantomData, diff --git a/documentation/src/crates/revm/builder.md b/documentation/src/crates/revm/builder.md index 141acdfe15..cb8dbc7cad 100644 --- a/documentation/src/crates/revm/builder.md +++ b/documentation/src/crates/revm/builder.md @@ -52,18 +52,20 @@ Example of using builder to create Evm with inspector: db::EmptyDB, Context, EvmContext, inspector::inspector_handle_register, inspectors::NoOpInspector, Evm, }; - // create the builder + // Create the evm. let evm = Evm::builder() .with_db(EmptyDB::default()) .with_external_context(NoOpInspector) - // register will modify Handler and call NoOpInspector. + // Register will modify Handler and call NoOpInspector. .append_handler_register(inspector_handle_register) - // this would not compile as we already locked the builder generics. - // .with_db(..) + // .with_db(..) would not compile as we already locked the builder generics, // alternative fn is reset_handler_with_db(..) .build(); - // extract evm context. + // Execute the evm. + let _out = evm.transact(); + + // Extract evm context. let Context { external, evm: EvmContext { db, .. }, @@ -74,22 +76,22 @@ Example of changing spec id and Environment of already build evm. ```rust,ignore use crate::{Evm,SpecId::BERLIN}; - // create default evm + // Create default evm. let evm = Evm::builder().build(); - // modify evm spec + // Modify evm spec. let evm = evm.modify().spec_id(BERLIN).build(); - // shortcut for above is + // Shortcut for above. let mut evm = evm.modify_spec_id(BERLIN); - //execute the evm + // Execute the evm. evm.transact(); // Example of modifying the tx env. let mut evm = evm.modify().modify_tx_env(|env| env.gas_price = 0.into()).build(); - // execute the evm with modified tx env. + // Execute the evm with modified tx env. evm.transact(); ``` @@ -98,4 +100,4 @@ Example of changing spec id and Environment of already build evm. Handler registers are simple function that allow modifying the `Handler` logic by replacing the handler functions. They are used to add custom logic to the evm execution but as they are free to modify the `Handler` in any form they want there can be conflicts if handlers that override the same function are added. -Most common use case for adding new logic to `Handler` is `Inspector` that is used to inspect the execution of the evm. Example of this can be found in [`Inspector`](../revm_inspector/inspector.md) documentation. \ No newline at end of file +Most common use case for adding new logic to `Handler` is `Inspector` that is used to inspect the execution of the evm. Example of this can be found in [`Inspector`](./revm/inspector.md) documentation. \ No newline at end of file diff --git a/documentation/src/crates/revm/evm.md b/documentation/src/crates/revm/evm.md index 8344f5b994..44b8fc8e82 100644 --- a/documentation/src/crates/revm/evm.md +++ b/documentation/src/crates/revm/evm.md @@ -10,7 +10,7 @@ It is consisting of two main parts the `Context` and the `Handler`. `Context` re `Evm` implements the [`Host`](./../interpreter/host.md) trait, which defines an interface for the interaction of the EVM Interpreter with its environment (or "host"), encompassing essential operations such as account and storage access, creating logs, and invoking sub calls and selfdestruct. -Data structures of block and transaction can be found inside `Environment`. And more information on journaled state can be found in [`JournaledState`](../revm_journaled_state/journaled_state.md) documentation. +Data structures of block and transaction can be found inside `Environment`. And more information on journaled state can be found in [`JournaledState`](../revm/journaled_state.md) documentation. ## Runtime @@ -22,7 +22,7 @@ First loop, the call loop, implements stack of `Frames` and it is responsible fo Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction. It is called from the call loop and it is responsible for executing bytecode instructions. It is implemented in [`Interpreter`](../revm_interpreter/interpreter.md) crate. -To dive deeper into the `Evm` logic check [`Handler`](../handler.md) documentation. +To dive deeper into the `Evm` logic check [`Handler`](./handler.md) documentation. # Functionalities From 6fdf87a31b63dfe6da8cb8956287b6ba89991d19 Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 4 Jan 2024 16:36:19 +0100 Subject: [PATCH 37/52] Rename handles to Pre/Post Execution and ExecutionLoop --- crates/revm/src/evm.rs | 30 +-- crates/revm/src/handler.rs | 30 ++- crates/revm/src/handler/handle_types.rs | 20 +- .../{frame.rs => execution_loop.rs} | 10 +- .../{main.rs => post_execution.rs} | 63 ++---- .../src/handler/handle_types/pre_execution.rs | 59 ++++++ crates/revm/src/handler/mainnet.rs | 9 +- .../mainnet/{main.rs => post_execution.rs} | 188 +++++------------- .../revm/src/handler/mainnet/pre_execution.rs | 93 +++++++++ crates/revm/src/inspector/handler_register.rs | 10 +- documentation/src/crates/revm.md | 2 + documentation/src/crates/revm/builder.md | 2 +- documentation/src/crates/revm/evm.md | 4 +- documentation/src/crates/revm/handler.md | 4 +- 14 files changed, 283 insertions(+), 241 deletions(-) rename crates/revm/src/handler/handle_types/{frame.rs => execution_loop.rs} (95%) rename crates/revm/src/handler/handle_types/{main.rs => post_execution.rs} (55%) create mode 100644 crates/revm/src/handler/handle_types/pre_execution.rs rename crates/revm/src/handler/mainnet/{main.rs => post_execution.rs} (51%) create mode 100644 crates/revm/src/handler/mainnet/pre_execution.rs diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 5437f1a279..8c28901e70 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -102,7 +102,7 @@ impl Evm<'_, EXT, DB> { .validation() .initial_tx_gas(&self.context.evm.env)?; let output = self.transact_preverified_inner(initial_gas_spend); - self.handler.main().end(&mut self.context, output) + self.handler.post_execution().end(&mut self.context, output) } /// Transact transaction @@ -120,7 +120,7 @@ impl Evm<'_, EXT, DB> { .tx_against_state(&mut self.context)?; let output = self.transact_preverified_inner(initial_gas_spend); - self.handler.main().end(&mut self.context, output) + self.handler.post_execution().end(&mut self.context, output) } /// Modify spec id, this will create new EVM that matches this spec id. @@ -214,7 +214,7 @@ impl Evm<'_, EXT, DB> { InterpreterAction::SubCall { inputs, return_memory_offset, - } => self.handler.frame().sub_call( + } => self.handler.execution_loop().sub_call( &mut self.context, inputs, stack_frame, @@ -223,7 +223,7 @@ impl Evm<'_, EXT, DB> { ), InterpreterAction::Create { inputs } => { self.handler - .frame() + .execution_loop() .sub_create(&mut self.context, stack_frame, inputs) } InterpreterAction::Return { result } => { @@ -233,7 +233,7 @@ impl Evm<'_, EXT, DB> { let child = call_stack.pop().unwrap(); let parent = call_stack.last_mut(); - if let Some(result) = self.handler.frame().frame_return( + if let Some(result) = self.handler.execution_loop().frame_return( &mut self.context, child, parent, @@ -260,17 +260,17 @@ impl Evm<'_, EXT, DB> { let ctx = &mut self.context; // load access list and beneficiary if needed. - hndl.main().load(ctx)?; + hndl.pre_execution().load_accounts(ctx)?; // load precompiles - let precompiles = hndl.main().load_precompiles(); + let precompiles = hndl.pre_execution().load_precompiles(); ctx.evm.set_precompiles(precompiles); // deduce caller balance with its limit. - hndl.main().deduct_caller(ctx)?; + hndl.pre_execution().deduct_caller(ctx)?; // gas limit used in calls. let first_frame = hndl - .frame() + .execution_loop() .create_first_frame(ctx, ctx.evm.env.tx.gas_limit - initial_gas_spend); // Starts the main running loop. @@ -281,15 +281,15 @@ impl Evm<'_, EXT, DB> { // handle output of call/create calls. let gas = hndl - .frame() + .execution_loop() .first_frame_return(&ctx.evm.env, result.result, result.gas); // Reimburse the caller - hndl.main().reimburse_caller(ctx, &gas)?; + hndl.post_execution().reimburse_caller(ctx, &gas)?; // Reward beneficiary - hndl.main().reward_beneficiary(ctx, &gas)?; - // main return - hndl.main() - .main_return(ctx, result.result, main_output, &gas) + hndl.post_execution().reward_beneficiary(ctx, &gas)?; + // Returns output of transaction. + hndl.post_execution() + .output(ctx, result.result, main_output, &gas) } } diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 13bb70f6b8..3b00315c9e 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -33,10 +33,12 @@ pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> { pub registers: Vec>, /// Validity handles. pub validation: ValidationHandler<'a, EXT, DB>, - /// Main handles. - pub main: MainHandler<'a, EXT, DB>, - /// Frame handles. - pub frame: FrameHandler<'a, EXT, DB>, + /// Pre execution handle + pub pre_execution: PreExecutionHandler<'a, EXT, DB>, + /// post Execution handle + pub post_execution: PostExecutionHandler<'a, EXT, DB>, + /// Execution loop that handles frames. + pub execution_loop: ExecutionLoopHandler<'a, EXT, DB>, } impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { @@ -47,8 +49,9 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { instruction_table: Some(InstructionTables::Plain(make_instruction_table::())), registers: Vec::new(), validation: ValidationHandler::new::(), - main: MainHandler::new::(), - frame: FrameHandler::new::(), + pre_execution: PreExecutionHandler::new::(), + post_execution: PostExecutionHandler::new::(), + execution_loop: ExecutionLoopHandler::new::(), } } @@ -73,14 +76,19 @@ impl<'a, H: Host, EXT: 'a, DB: Database + 'a> Handler<'a, H, EXT, DB> { self.instruction_table = Some(table); } - /// Returns reference to main handler. - pub fn main(&self) -> &MainHandler<'a, EXT, DB> { - &self.main + /// Returns reference to pre execution handler. + pub fn pre_execution(&self) -> &PreExecutionHandler<'a, EXT, DB> { + &self.pre_execution + } + + /// Returns reference to pre execution handler. + pub fn post_execution(&self) -> &PostExecutionHandler<'a, EXT, DB> { + &self.post_execution } /// Returns reference to frame handler. - pub fn frame(&self) -> &FrameHandler<'a, EXT, DB> { - &self.frame + pub fn execution_loop(&self) -> &ExecutionLoopHandler<'a, EXT, DB> { + &self.execution_loop } /// Returns reference to validation handler. diff --git a/crates/revm/src/handler/handle_types.rs b/crates/revm/src/handler/handle_types.rs index cfe0db2085..a37458c76a 100644 --- a/crates/revm/src/handler/handle_types.rs +++ b/crates/revm/src/handler/handle_types.rs @@ -1,7 +1,8 @@ // Modules -pub mod frame; -pub mod main; +pub mod execution_loop; +pub mod post_execution; +pub mod pre_execution; pub mod validation; // Exports @@ -10,12 +11,15 @@ pub use validation::{ ValidateEnvHandle, ValidateInitialTxGasHandle, ValidateTxEnvAgainstState, ValidationHandler, }; -pub use main::{ - DeductCallerHandle, EndHandle, MainHandler, MainLoadHandle, MainLoadPrecompiles, - MainReturnHandle, ReimburseCallerHandle, RewardBeneficiaryHandle, +pub use execution_loop::{ + CreateFirstFrameHandle, ExecutionLoopHandler, FrameReturnHandle, FrameSubCallHandle, + FrameSubCreateHandle, }; -pub use frame::{ - CreateFirstFrameHandle, FrameHandler, FrameReturnHandle, FrameSubCallHandle, - FrameSubCreateHandle, +pub use pre_execution::{ + DeductCallerHandle, LoadAccountsHandle, LoadPrecompilesHandle, PreExecutionHandler, +}; + +pub use post_execution::{ + EndHandle, OutputHandle, PostExecutionHandler, ReimburseCallerHandle, RewardBeneficiaryHandle, }; diff --git a/crates/revm/src/handler/handle_types/frame.rs b/crates/revm/src/handler/handle_types/execution_loop.rs similarity index 95% rename from crates/revm/src/handler/handle_types/frame.rs rename to crates/revm/src/handler/handle_types/execution_loop.rs index 0275e85251..a71aa717e5 100644 --- a/crates/revm/src/handler/handle_types/frame.rs +++ b/crates/revm/src/handler/handle_types/execution_loop.rs @@ -58,7 +58,7 @@ pub type FrameSubCreateHandle<'a, EXT, DB> = Arc< >; /// Handles related to stack frames. -pub struct FrameHandler<'a, EXT, DB: Database> { +pub struct ExecutionLoopHandler<'a, EXT, DB: Database> { /// Create Main frame pub create_first_frame: CreateFirstFrameHandle<'a, EXT, DB>, /// Validate Transaction against the state. @@ -73,12 +73,12 @@ pub struct FrameHandler<'a, EXT, DB: Database> { pub sub_create: FrameSubCreateHandle<'a, EXT, DB>, } -impl<'a, EXT: 'a, DB: Database + 'a> FrameHandler<'a, EXT, DB> { - /// Creates mainnet FrameHandler.. +impl<'a, EXT: 'a, DB: Database + 'a> ExecutionLoopHandler<'a, EXT, DB> { + /// Creates mainnet ExecutionLoopHandler.. pub fn new() -> Self { Self { - first_frame_return: Arc::new(mainnet::main_frame_return::), create_first_frame: Arc::new(mainnet::create_first_frame::), + first_frame_return: Arc::new(mainnet::main_frame_return::), frame_return: Arc::new(mainnet::handle_frame_return::), sub_call: Arc::new(mainnet::handle_frame_sub_call::), sub_create: Arc::new(mainnet::handle_frame_sub_create::), @@ -86,7 +86,7 @@ impl<'a, EXT: 'a, DB: Database + 'a> FrameHandler<'a, EXT, DB> { } } -impl<'a, EXT, DB: Database> FrameHandler<'a, EXT, DB> { +impl<'a, EXT, DB: Database> ExecutionLoopHandler<'a, EXT, DB> { /// Create first call frame. pub fn create_first_frame( &self, diff --git a/crates/revm/src/handler/handle_types/main.rs b/crates/revm/src/handler/handle_types/post_execution.rs similarity index 55% rename from crates/revm/src/handler/handle_types/main.rs rename to crates/revm/src/handler/handle_types/post_execution.rs index e268d6b767..850a1774f2 100644 --- a/crates/revm/src/handler/handle_types/main.rs +++ b/crates/revm/src/handler/handle_types/post_execution.rs @@ -6,20 +6,6 @@ use crate::{ Context, }; use alloc::sync::Arc; -use revm_precompile::Precompiles; - -/// Loads precompiles into Evm -pub type MainLoadPrecompiles<'a> = Arc Precompiles + 'a>; - -/// Load access list account, precompiles and beneficiary. -/// There is not need to load Caller as it is assumed that -/// it will be loaded in DeductCallerHandle. -pub type MainLoadHandle<'a, EXT, DB> = - Arc) -> Result<(), EVMError<::Error>> + 'a>; - -/// Deduct the caller to its limit. -pub type DeductCallerHandle<'a, EXT, DB> = - Arc) -> EVMResultGeneric<(), ::Error> + 'a>; /// Reimburse the caller with ethereum it didn't spent. pub type ReimburseCallerHandle<'a, EXT, DB> = @@ -29,7 +15,7 @@ pub type ReimburseCallerHandle<'a, EXT, DB> = pub type RewardBeneficiaryHandle<'a, EXT, DB> = ReimburseCallerHandle<'a, EXT, DB>; /// Main return handle, takes state from journal and transforms internal result to external. -pub type MainReturnHandle<'a, EXT, DB> = Arc< +pub type OutputHandle<'a, EXT, DB> = Arc< dyn Fn( &mut Context, InstructionResult, @@ -52,39 +38,30 @@ pub type EndHandle<'a, EXT, DB> = Arc< >; /// Handles related to main function. -pub struct MainHandler<'a, EXT, DB: Database> { - /// Load precompiles - pub precompiles: MainLoadPrecompiles<'a>, - /// Main load handle - pub load: MainLoadHandle<'a, EXT, DB>, - /// Deduct max value from the caller. - pub deduct_caller: DeductCallerHandle<'a, EXT, DB>, +pub struct PostExecutionHandler<'a, EXT, DB: Database> { /// Reimburse the caller with ethereum it didn't spent. pub reimburse_caller: ReimburseCallerHandle<'a, EXT, DB>, /// Reward the beneficiary with caller fee. pub reward_beneficiary: RewardBeneficiaryHandle<'a, EXT, DB>, /// Main return handle, returns the output of the transact. - pub main_return: MainReturnHandle<'a, EXT, DB>, + pub output: OutputHandle<'a, EXT, DB>, /// End handle. pub end: EndHandle<'a, EXT, DB>, } -impl<'a, EXT: 'a, DB: Database + 'a> MainHandler<'a, EXT, DB> { +impl<'a, EXT: 'a, DB: Database + 'a> PostExecutionHandler<'a, EXT, DB> { /// Creates mainnet MainHandles. pub fn new() -> Self { Self { - precompiles: Arc::new(mainnet::main_load_precompiles::), - load: Arc::new(mainnet::main_load::), - deduct_caller: Arc::new(mainnet::main_deduct_caller::), - reimburse_caller: Arc::new(mainnet::main_reimburse_caller::), - reward_beneficiary: Arc::new(mainnet::main_reward_beneficiary::), - main_return: Arc::new(mainnet::main_return::), - end: Arc::new(mainnet::main_end::), + reimburse_caller: Arc::new(mainnet::reimburse_caller::), + reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), + output: Arc::new(mainnet::output::), + end: Arc::new(mainnet::end::), } } } -impl<'a, EXT, DB: Database> MainHandler<'a, EXT, DB> { +impl<'a, EXT, DB: Database> PostExecutionHandler<'a, EXT, DB> { /// Reimburse the caller with gas that were not spend. pub fn reimburse_caller( &self, @@ -93,12 +70,6 @@ impl<'a, EXT, DB: Database> MainHandler<'a, EXT, DB> { ) -> Result<(), EVMError> { (self.reimburse_caller)(context, gas) } - - /// Deduct caller to its limit. - pub fn deduct_caller(&self, context: &mut Context) -> Result<(), EVMError> { - (self.deduct_caller)(context) - } - /// Reward beneficiary pub fn reward_beneficiary( &self, @@ -108,15 +79,15 @@ impl<'a, EXT, DB: Database> MainHandler<'a, EXT, DB> { (self.reward_beneficiary)(context, gas) } - /// Main return. - pub fn main_return( + /// Returns the output of transaction. + pub fn output( &self, context: &mut Context, call_result: InstructionResult, output: Output, gas: &Gas, ) -> Result> { - (self.main_return)(context, call_result, output, gas) + (self.output)(context, call_result, output, gas) } /// End handler. @@ -127,14 +98,4 @@ impl<'a, EXT, DB: Database> MainHandler<'a, EXT, DB> { ) -> Result> { (self.end)(context, end_output) } - - /// Main load - pub fn load(&self, context: &mut Context) -> Result<(), EVMError> { - (self.load)(context) - } - - /// Load precompiles - pub fn load_precompiles(&self) -> Precompiles { - (self.precompiles)() - } } diff --git a/crates/revm/src/handler/handle_types/pre_execution.rs b/crates/revm/src/handler/handle_types/pre_execution.rs new file mode 100644 index 0000000000..ef9a48543c --- /dev/null +++ b/crates/revm/src/handler/handle_types/pre_execution.rs @@ -0,0 +1,59 @@ +// Includes. +use crate::{ + handler::mainnet, + primitives::{db::Database, EVMError, EVMResultGeneric, Spec}, + Context, +}; +use alloc::sync::Arc; +use revm_precompile::Precompiles; + +/// Loads precompiles into Evm +pub type LoadPrecompilesHandle<'a> = Arc Precompiles + 'a>; + +/// Load access list accounts and beneficiary. +/// There is no need to load Caller as it is assumed that +/// it will be loaded in DeductCallerHandle. +pub type LoadAccountsHandle<'a, EXT, DB> = + Arc) -> Result<(), EVMError<::Error>> + 'a>; + +/// Deduct the caller to its limit. +pub type DeductCallerHandle<'a, EXT, DB> = + Arc) -> EVMResultGeneric<(), ::Error> + 'a>; + +/// Handles related to main function. +pub struct PreExecutionHandler<'a, EXT, DB: Database> { + /// Load precompiles + pub load_precompiles: LoadPrecompilesHandle<'a>, + /// Main load handle + pub load_accounts: LoadAccountsHandle<'a, EXT, DB>, + /// Deduct max value from the caller. + pub deduct_caller: DeductCallerHandle<'a, EXT, DB>, +} + +impl<'a, EXT: 'a, DB: Database + 'a> PreExecutionHandler<'a, EXT, DB> { + /// Creates mainnet MainHandles. + pub fn new() -> Self { + Self { + load_precompiles: Arc::new(mainnet::main_load_precompiles::), + load_accounts: Arc::new(mainnet::main_load::), + deduct_caller: Arc::new(mainnet::main_deduct_caller::), + } + } +} + +impl<'a, EXT, DB: Database> PreExecutionHandler<'a, EXT, DB> { + /// Deduct caller to its limit. + pub fn deduct_caller(&self, context: &mut Context) -> Result<(), EVMError> { + (self.deduct_caller)(context) + } + + /// Main load + pub fn load_accounts(&self, context: &mut Context) -> Result<(), EVMError> { + (self.load_accounts)(context) + } + + /// Load precompiles + pub fn load_precompiles(&self) -> Precompiles { + (self.load_precompiles)() + } +} diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index d77945fbb2..929b11761a 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -1,15 +1,16 @@ //! Mainnet related handlers. mod frame; -mod main; +mod post_execution; +mod pre_execution; mod validation; pub use frame::{ create_first_frame, frame_return_with_refund_flag, handle_frame_return, handle_frame_sub_call, handle_frame_sub_create, main_frame_return, }; -pub use main::{ - deduct_caller_inner, main_deduct_caller, main_end, main_load, main_load_precompiles, - main_reimburse_caller, main_return, main_reward_beneficiary, +pub use post_execution::{end, output, reimburse_caller, reward_beneficiary}; +pub use pre_execution::{ + deduct_caller_inner, main_deduct_caller, main_load, main_load_precompiles, }; pub use validation::{validate_env, validate_initial_tx_gas, validate_tx_against_state}; diff --git a/crates/revm/src/handler/mainnet/main.rs b/crates/revm/src/handler/mainnet/post_execution.rs similarity index 51% rename from crates/revm/src/handler/mainnet/main.rs rename to crates/revm/src/handler/mainnet/post_execution.rs index a985761344..7f31290e27 100644 --- a/crates/revm/src/handler/mainnet/main.rs +++ b/crates/revm/src/handler/mainnet/post_execution.rs @@ -1,106 +1,14 @@ -//! Handles related to the main function of the EVM. -//! -//! They handle initial setup of the EVM, call loop and the final return of the EVM - -use revm_precompile::{PrecompileSpecId, Precompiles}; - use crate::{ interpreter::{Gas, InstructionResult, SuccessOrHalt}, primitives::{ - db::Database, - Account, EVMError, Env, ExecutionResult, Output, ResultAndState, Spec, - SpecId::{CANCUN, LONDON, SHANGHAI}, - TransactTo, U256, + db::Database, EVMError, ExecutionResult, Output, ResultAndState, Spec, SpecId::LONDON, U256, }, Context, }; -/// Main precompile load -pub fn main_load_precompiles() -> Precompiles { - Precompiles::new(PrecompileSpecId::from_spec_id(SPEC::SPEC_ID)).clone() -} - -/// Main load handle -#[inline] -pub fn main_load( - context: &mut Context, -) -> Result<(), EVMError> { - // set journaling state flag. - context.evm.journaled_state.set_spec_id(SPEC::SPEC_ID); - - // the L1-cost fee is only computed for Optimism non-deposit transactions. - #[cfg(feature = "optimism")] - if context.evm.env.cfg.optimism && context.evm.env.tx.optimism.source_hash.is_none() { - let l1_block_info = crate::optimism::L1BlockInfo::try_fetch(&mut context.evm.db) - .map_err(EVMError::Database)?; - - // storage l1 block info for later use. - context.evm.l1_block_info = Some(l1_block_info); - } - - // load coinbase - // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm - if SPEC::enabled(SHANGHAI) { - context - .evm - .journaled_state - .initial_account_load(context.evm.env.block.coinbase, &[], &mut context.evm.db) - .map_err(EVMError::Database)?; - } - - context.evm.load_access_list()?; - Ok(()) -} - -/// Main return handle, returns the output of the transaction. -#[inline] -pub fn main_return( - context: &mut Context, - call_result: InstructionResult, - output: Output, - gas: &Gas, -) -> Result> { - // used gas with refund calculated. - let gas_refunded = gas.refunded() as u64; - let final_gas_used = gas.spend() - gas_refunded; - - // reset journal and return present state. - let (state, logs) = context.evm.journaled_state.finalize(); - - let result = match call_result.into() { - SuccessOrHalt::Success(reason) => ExecutionResult::Success { - reason, - gas_used: final_gas_used, - gas_refunded, - logs, - output, - }, - SuccessOrHalt::Revert => ExecutionResult::Revert { - gas_used: final_gas_used, - output: match output { - Output::Call(return_value) => return_value, - Output::Create(return_value, _) => return_value, - }, - }, - SuccessOrHalt::Halt(reason) => ExecutionResult::Halt { - reason, - gas_used: final_gas_used, - }, - SuccessOrHalt::FatalExternalError => { - return Err(EVMError::Database(context.evm.error.take().unwrap())); - } - // Only two internal return flags. - SuccessOrHalt::InternalContinue | SuccessOrHalt::InternalCallOrCreate => { - panic!("Internal return flags should remain internal {call_result:?}") - } - }; - - Ok(ResultAndState { result, state }) -} - /// Mainnet end handle does not change the output. #[inline] -pub fn main_end( +pub fn end( _context: &mut Context, evm_output: Result>, ) -> Result> { @@ -109,7 +17,7 @@ pub fn main_end( /// Reward beneficiary with gas fee. #[inline] -pub fn main_reward_beneficiary( +pub fn reward_beneficiary( context: &mut Context, gas: &Gas, ) -> Result<(), EVMError> { @@ -139,50 +47,8 @@ pub fn main_reward_beneficiary( Ok(()) } -/// Helper function that deducts the caller balance. -pub fn deduct_caller_inner(caller_account: &mut Account, env: &Env) { - // Subtract gas costs from the caller's account. - // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. - let mut gas_cost = U256::from(env.tx.gas_limit).saturating_mul(env.effective_gas_price()); - - // EIP-4844 - if SPEC::enabled(CANCUN) { - let data_fee = env.calc_data_fee().expect("already checked"); - gas_cost = gas_cost.saturating_add(data_fee); - } - - // set new caller account balance. - caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); - - // bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`. - if matches!(env.tx.transact_to, TransactTo::Call(_)) { - // Nonce is already checked - caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); - } - - // touch account so we know it is changed. - caller_account.mark_touch(); -} - -#[inline] -pub fn main_deduct_caller( - context: &mut Context, -) -> Result<(), EVMError> { - // load caller's account. - let (caller_account, _) = context - .evm - .journaled_state - .load_account(context.evm.env.tx.caller, &mut context.evm.db) - .map_err(EVMError::Database)?; - - // deduct gas cost from caller's account. - deduct_caller_inner::(caller_account, &context.evm.env); - - Ok(()) -} - #[inline] -pub fn main_reimburse_caller( +pub fn reimburse_caller( context: &mut Context, gas: &Gas, ) -> Result<(), EVMError> { @@ -203,3 +69,49 @@ pub fn main_reimburse_caller( Ok(()) } + +/// Main return handle, returns the output of the transaction. +#[inline] +pub fn output( + context: &mut Context, + call_result: InstructionResult, + output: Output, + gas: &Gas, +) -> Result> { + // used gas with refund calculated. + let gas_refunded = gas.refunded() as u64; + let final_gas_used = gas.spend() - gas_refunded; + + // reset journal and return present state. + let (state, logs) = context.evm.journaled_state.finalize(); + + let result = match call_result.into() { + SuccessOrHalt::Success(reason) => ExecutionResult::Success { + reason, + gas_used: final_gas_used, + gas_refunded, + logs, + output, + }, + SuccessOrHalt::Revert => ExecutionResult::Revert { + gas_used: final_gas_used, + output: match output { + Output::Call(return_value) => return_value, + Output::Create(return_value, _) => return_value, + }, + }, + SuccessOrHalt::Halt(reason) => ExecutionResult::Halt { + reason, + gas_used: final_gas_used, + }, + SuccessOrHalt::FatalExternalError => { + return Err(EVMError::Database(context.evm.error.take().unwrap())); + } + // Only two internal return flags. + SuccessOrHalt::InternalContinue | SuccessOrHalt::InternalCallOrCreate => { + panic!("Internal return flags should remain internal {call_result:?}") + } + }; + + Ok(ResultAndState { result, state }) +} diff --git a/crates/revm/src/handler/mainnet/pre_execution.rs b/crates/revm/src/handler/mainnet/pre_execution.rs new file mode 100644 index 0000000000..a16afdf804 --- /dev/null +++ b/crates/revm/src/handler/mainnet/pre_execution.rs @@ -0,0 +1,93 @@ +//! Handles related to the main function of the EVM. +//! +//! They handle initial setup of the EVM, call loop and the final return of the EVM + +use crate::{ + precompile::{PrecompileSpecId, Precompiles}, + primitives::{ + db::Database, + Account, EVMError, Env, Spec, + SpecId::{CANCUN, SHANGHAI}, + TransactTo, U256, + }, + Context, +}; + +/// Main precompile load +pub fn main_load_precompiles() -> Precompiles { + Precompiles::new(PrecompileSpecId::from_spec_id(SPEC::SPEC_ID)).clone() +} + +/// Main load handle +#[inline] +pub fn main_load( + context: &mut Context, +) -> Result<(), EVMError> { + // set journaling state flag. + context.evm.journaled_state.set_spec_id(SPEC::SPEC_ID); + + // the L1-cost fee is only computed for Optimism non-deposit transactions. + #[cfg(feature = "optimism")] + if context.evm.env.cfg.optimism && context.evm.env.tx.optimism.source_hash.is_none() { + let l1_block_info = crate::optimism::L1BlockInfo::try_fetch(&mut context.evm.db) + .map_err(EVMError::Database)?; + + // storage l1 block info for later use. + context.evm.l1_block_info = Some(l1_block_info); + } + + // load coinbase + // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm + if SPEC::enabled(SHANGHAI) { + context + .evm + .journaled_state + .initial_account_load(context.evm.env.block.coinbase, &[], &mut context.evm.db) + .map_err(EVMError::Database)?; + } + + context.evm.load_access_list()?; + Ok(()) +} + +/// Helper function that deducts the caller balance. +pub fn deduct_caller_inner(caller_account: &mut Account, env: &Env) { + // Subtract gas costs from the caller's account. + // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. + let mut gas_cost = U256::from(env.tx.gas_limit).saturating_mul(env.effective_gas_price()); + + // EIP-4844 + if SPEC::enabled(CANCUN) { + let data_fee = env.calc_data_fee().expect("already checked"); + gas_cost = gas_cost.saturating_add(data_fee); + } + + // set new caller account balance. + caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); + + // bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`. + if matches!(env.tx.transact_to, TransactTo::Call(_)) { + // Nonce is already checked + caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); + } + + // touch account so we know it is changed. + caller_account.mark_touch(); +} + +#[inline] +pub fn main_deduct_caller( + context: &mut Context, +) -> Result<(), EVMError> { + // load caller's account. + let (caller_account, _) = context + .evm + .journaled_state + .load_account(context.evm.env.tx.caller, &mut context.evm.db) + .map_err(EVMError::Database)?; + + // deduct gas cost from caller's account. + deduct_caller_inner::(caller_account, &context.evm.env); + + Ok(()) +} diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index 8a3984f3e9..8798f8a926 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -83,6 +83,8 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( inspect_log(opcode::LOG3); inspect_log(opcode::LOG4); + // wrap first frame create and main frame return. + // register selfdestruct function. if let Some(i) = table.get_mut(opcode::SELFDESTRUCT as usize) { let old = core::mem::replace(i, Box::new(|_, _| ())); @@ -121,7 +123,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( )); // handle sub create - handler.frame.sub_create = Arc::new( + handler.execution_loop.sub_create = Arc::new( move |context, frame, mut inputs| -> Option> { let inspector = context.external.get_inspector(); if let Some((result, address)) = inspector.create(&mut context.evm, &mut inputs) { @@ -146,7 +148,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( ); // handle sub call - handler.frame.sub_call = Arc::new( + handler.execution_loop.sub_call = Arc::new( move |context, mut inputs, frame, memory, return_memory_offset| -> Option> { // inspector handle let inspector = context.external.get_inspector(); @@ -175,8 +177,8 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( ); // return frame handle - let old_handle = handler.frame.frame_return.clone(); - handler.frame.frame_return = Arc::new( + let old_handle = handler.execution_loop.frame_return.clone(); + handler.execution_loop.frame_return = Arc::new( move |context, mut child, parent, memory, mut result| -> Option { let inspector = &mut context.external.get_inspector(); result = if child.is_create { diff --git a/documentation/src/crates/revm.md b/documentation/src/crates/revm.md index 9d31ea00c0..e9a6934929 100644 --- a/documentation/src/crates/revm.md +++ b/documentation/src/crates/revm.md @@ -2,6 +2,8 @@ The `crate` is focused on the implementation of Ethereum Virtual Machine (EVM) including call loop and host implementation, database handling, state journaling and powerful logic handlers that can be overwritten. This crate pulls Primitives, Interpreter and Precompiles together to deliver the rust evm. +Starting point of the documentation should be a (`Evm`)[./revm/evm.md] that is main structure of EVM. Then i would recomend reading about the `EvmBuilder` that is used to create the `Evm` and modify. After that you can read about the `Handler` that is used to modify the logic of the Evm and it will tie with how introspection of Evm can be done. And lastly you can read about the `Inspector` that is legacy interface for inspecting execution that is repurposed as one of example of handler registers. + Modules: - `evm`: This is main module that executed EVM calls. - `builder`: This modules build the Evm, sets database, handlers and other parameters. Here is where we set handlers for specific fork or external state for inspection. diff --git a/documentation/src/crates/revm/builder.md b/documentation/src/crates/revm/builder.md index cb8dbc7cad..2da6623f06 100644 --- a/documentation/src/crates/revm/builder.md +++ b/documentation/src/crates/revm/builder.md @@ -100,4 +100,4 @@ Example of changing spec id and Environment of already build evm. Handler registers are simple function that allow modifying the `Handler` logic by replacing the handler functions. They are used to add custom logic to the evm execution but as they are free to modify the `Handler` in any form they want there can be conflicts if handlers that override the same function are added. -Most common use case for adding new logic to `Handler` is `Inspector` that is used to inspect the execution of the evm. Example of this can be found in [`Inspector`](./revm/inspector.md) documentation. \ No newline at end of file +Most common use case for adding new logic to `Handler` is `Inspector` that is used to inspect the execution of the evm. Example of this can be found in [`Inspector`](./inspector.md) documentation. \ No newline at end of file diff --git a/documentation/src/crates/revm/evm.md b/documentation/src/crates/revm/evm.md index 44b8fc8e82..bf5cafc78c 100644 --- a/documentation/src/crates/revm/evm.md +++ b/documentation/src/crates/revm/evm.md @@ -16,11 +16,11 @@ Data structures of block and transaction can be found inside `Environment`. And Runtime consist of list of functions from `Handler` that are called in predefined order. They are grouped by functionality on `Verification`, `Main`, `Frame` and `Instruction` functions. Verification function are related to the preverification of set `Environment` data. Main function is the main logic of the transaction execution. Frame function handles call and creates and sub calls. And Instruction functions are instruction table that executes opcodes. -`Evm` execution runs **two** loops. First loop is call loop that everything starts with, it creates call frames, handles subcalls and its return outputs and call Interpreter loop to execute bytecode instructions it is handled by `FrameHandler`. Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction from `InstructionTable`. +`Evm` execution runs **two** loops. First loop is call loop that everything starts with, it creates call frames, handles subcalls and its return outputs and call Interpreter loop to execute bytecode instructions it is handled by `ExecutionLoopHandler`. Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction from `InstructionTable`. First loop, the call loop, implements stack of `Frames` and it is responsible for handling sub calls and its return outputs. At the start Evm creates first `Frame` that contains `Interpreter` and starts the loop. `Interpreter` returns the `InterpreterAction` and action can be a `Return` of a call this means this interpreter finished its run or `SubCall`/`SubCreate` that means that new `Frame` needs to be created and pushed to the stack. When `Interpreter` returns `Return` action `Frame` is popped from the stack and its return value is pushed to the parent `Frame` stack. When `Interpreter` returns `SubCall`/`SubCreate` action new `Frame` is created and pushed to the stack and the loop continues. When the stack is empty the loop finishes. -Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction. It is called from the call loop and it is responsible for executing bytecode instructions. It is implemented in [`Interpreter`](../revm_interpreter/interpreter.md) crate. +Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction. It is called from the call loop and it is responsible for executing bytecode instructions. It is implemented in [`Interpreter`](../interpreter/interpreter.md) crate. To dive deeper into the `Evm` logic check [`Handler`](./handler.md) documentation. diff --git a/documentation/src/crates/revm/handler.md b/documentation/src/crates/revm/handler.md index 0fbb2ae738..5400e05aad 100644 --- a/documentation/src/crates/revm/handler.md +++ b/documentation/src/crates/revm/handler.md @@ -5,7 +5,7 @@ Is logic part of the Evm, it contains the Specification ID, and list of function Functions can be grouped in four categories and are marked in that way in the code: * Validation functions: `ValidateHandler` * Main functions: `MainHandler` -* Frame functions: `FrameHandler` +* Frame functions: `ExecutionLoopHandler` * Internal functions: `InstractionTable` ### Handle Registers @@ -67,7 +67,7 @@ Logic when running transaction consist of few stages that are implemented as a h It returns the changes state and the result of the execution. -### FrameHandler +### ExecutionLoopHandler Consist of the function that handles the call stack and the first loop. They are called in the following order: From 62e9eaabd59e6b913c6bf0711b3bc9957b902666 Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 4 Jan 2024 16:46:43 +0100 Subject: [PATCH 38/52] fix optimism clippy --- crates/revm/src/optimism/handler_register.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/revm/src/optimism/handler_register.rs b/crates/revm/src/optimism/handler_register.rs index fcf7fdbdec..30d1eb81ef 100644 --- a/crates/revm/src/optimism/handler_register.rs +++ b/crates/revm/src/optimism/handler_register.rs @@ -19,12 +19,12 @@ use core::ops::Mul; pub fn optimism_handle_register(handler: &mut EvmHandler<'_, EXT, DB>) { spec_to_generic!(handler.spec_id, { // Refund is calculated differently then mainnet. - handler.frame.first_frame_return = Arc::new(handle_call_return::); - // we reinburse caller the same was as in mainnet. - handler.main.reward_beneficiary = Arc::new(reward_beneficiary::); + handler.execution_loop.first_frame_return = Arc::new(handle_call_return::); + // we reimburse caller the same was as in mainnet. + handler.post_execution.reward_beneficiary = Arc::new(reward_beneficiary::); // In case of halt of deposit transaction return Error. - handler.main.main_return = Arc::new(main_return::); - handler.main.end = Arc::new(end_handle::); + handler.post_execution.output = Arc::new(main_return::); + handler.post_execution.end = Arc::new(end_handle::); }); } @@ -159,7 +159,7 @@ pub fn reward_beneficiary( // transfer fee to coinbase/beneficiary. if !disable_coinbase_tip { - mainnet::main_reward_beneficiary::(context, gas)?; + mainnet::reward_beneficiary::(context, gas)?; } if context.evm.env.cfg.optimism && !is_deposit { @@ -213,7 +213,7 @@ pub fn main_return( output: Output, gas: &Gas, ) -> Result> { - let result = mainnet::main_return::(context, call_result, output, gas)?; + let result = mainnet::output::(context, call_result, output, gas)?; if result.result.is_halt() { // Post-regolith, if the transaction is a deposit transaction and it halts, From c6385bed0024fce94be684fed5cca56c6d67f5a2 Mon Sep 17 00:00:00 2001 From: rakita Date: Mon, 8 Jan 2024 03:56:40 +0100 Subject: [PATCH 39/52] small rename --- crates/revm/src/optimism/handler_register.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/revm/src/optimism/handler_register.rs b/crates/revm/src/optimism/handler_register.rs index 30d1eb81ef..347c728f01 100644 --- a/crates/revm/src/optimism/handler_register.rs +++ b/crates/revm/src/optimism/handler_register.rs @@ -23,8 +23,8 @@ pub fn optimism_handle_register(handler: &mut EvmHandler<'_, // we reimburse caller the same was as in mainnet. handler.post_execution.reward_beneficiary = Arc::new(reward_beneficiary::); // In case of halt of deposit transaction return Error. - handler.post_execution.output = Arc::new(main_return::); - handler.post_execution.end = Arc::new(end_handle::); + handler.post_execution.output = Arc::new(output::); + handler.post_execution.end = Arc::new(end::); }); } @@ -207,7 +207,7 @@ pub fn reward_beneficiary( /// Main return handle, returns the output of the transaction. #[inline] -pub fn main_return( +pub fn output( context: &mut Context, call_result: InstructionResult, output: Output, @@ -232,7 +232,7 @@ pub fn main_return( /// Optimism end handle changes output if the transaction is a deposit transaction. /// Deposit transaction can't be reverted and is always successful. #[inline] -pub fn end_handle( +pub fn end( context: &mut Context, evm_output: Result>, ) -> Result> { From 648ba938b8a588fe5ae58a7a1db8acd43f7fc752 Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 9 Jan 2024 03:44:07 +0100 Subject: [PATCH 40/52] FrameData and docs --- crates/interpreter/src/inner_models.rs | 42 +++++++ crates/revm/src/builder.rs | 2 - crates/revm/src/context.rs | 53 ++++---- crates/revm/src/evm.rs | 2 +- crates/revm/src/frame.rs | 37 +++++- crates/revm/src/handler.rs | 4 - .../handler/handle_types/execution_loop.rs | 8 +- .../src/handler/handle_types/pre_execution.rs | 6 +- crates/revm/src/handler/mainnet.rs | 12 +- .../mainnet/{frame.rs => execution_loop.rs} | 116 ++++++++---------- .../revm/src/handler/mainnet/pre_execution.rs | 9 +- crates/revm/src/inspector/gas.rs | 1 - crates/revm/src/inspector/handler_register.rs | 70 +++++++++-- crates/revm/src/lib.rs | 2 +- crates/revm/src/optimism.rs | 3 +- documentation/src/crates/revm/builder.md | 8 +- documentation/src/crates/revm/evm.md | 4 +- documentation/src/crates/revm/handler.md | 74 +++++++---- 18 files changed, 284 insertions(+), 169 deletions(-) rename crates/revm/src/handler/mainnet/{frame.rs => execution_loop.rs} (58%) diff --git a/crates/interpreter/src/inner_models.rs b/crates/interpreter/src/inner_models.rs index a5d3bcd187..fb19ea2323 100644 --- a/crates/interpreter/src/inner_models.rs +++ b/crates/interpreter/src/inner_models.rs @@ -1,3 +1,5 @@ +use revm_primitives::{TransactTo, TxEnv}; + pub use crate::primitives::CreateScheme; use crate::primitives::{Address, Bytes, B256, U256}; @@ -35,7 +37,47 @@ pub struct CreateInputs { pub gas_limit: u64, } +impl CallInputs { + pub fn new(tx_env: &TxEnv, gas_limit: u64) -> Option { + let TransactTo::Call(address) = tx_env.transact_to else { + return None; + }; + + Some(CallInputs { + contract: address, + transfer: Transfer { + source: tx_env.caller, + target: address, + value: tx_env.value, + }, + input: tx_env.data.clone(), + gas_limit, + context: CallContext { + caller: tx_env.caller, + address, + code_address: address, + apparent_value: tx_env.value, + scheme: CallScheme::Call, + }, + is_static: false, + }) + } +} + impl CreateInputs { + pub fn new(tx_env: &TxEnv, gas_limit: u64) -> Option { + let TransactTo::Create(scheme) = tx_env.transact_to else { + return None; + }; + + Some(CreateInputs { + caller: tx_env.caller, + scheme, + value: tx_env.value, + init_code: tx_env.data.clone(), + gas_limit, + }) + } /// Returns the address that this create call will create. pub fn created_address(&self, nonce: u64) -> Address { match self.scheme { diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index 5735a58cf1..04e554b52b 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -1,5 +1,3 @@ -//! Evm Builder. - use crate::{ db::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}, handler::register, diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index e1df0d2539..5d8e2fb5f0 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -10,7 +10,7 @@ use crate::{ keccak256, Address, AnalysisKind, Bytecode, Bytes, EVMError, Env, HashSet, Spec, SpecId, SpecId::*, B256, U256, }, - CallStackFrame, FrameOrResult, CALL_STACK_LIMIT, + CallStackFrame, FrameData, FrameOrResult, JournalCheckpoint, CALL_STACK_LIMIT, }; use alloc::boxed::Box; use core::ops::Range; @@ -280,10 +280,8 @@ impl EvmContext { )); FrameOrResult::new_frame(CallStackFrame { - is_create: true, checkpoint, - created_address: Some(created_address), - subcall_return_memory_range: 0..0, + frame_data: FrameData::Create { created_address }, interpreter: Interpreter::new(contract, gas.limit(), false), }) } @@ -292,7 +290,7 @@ impl EvmContext { pub fn make_call_frame( &mut self, inputs: &CallInputs, - return_memory_offset: Range, + return_memory_range: Range, ) -> FrameOrResult { let gas = Gas::new(inputs.gas_limit); @@ -359,10 +357,10 @@ impl EvmContext { )); // Create interpreter and execute subcall and push new CallStackFrame. FrameOrResult::new_frame(CallStackFrame { - is_create: false, checkpoint, - created_address: None, - subcall_return_memory_range: return_memory_offset, + frame_data: FrameData::Call { + return_memory_range, + }, interpreter: Interpreter::new(contract, gas.limit(), inputs.is_static), }) } else { @@ -416,13 +414,13 @@ impl EvmContext { pub fn call_return( &mut self, interpreter_result: InterpreterResult, - frame: Box, + journal_checkpoint: JournalCheckpoint, ) -> InterpreterResult { // revert changes or not. if matches!(interpreter_result.result, return_ok!()) { self.journaled_state.checkpoint_commit(); } else { - self.journaled_state.checkpoint_revert(frame.checkpoint); + self.journaled_state.checkpoint_revert(journal_checkpoint); } interpreter_result } @@ -432,13 +430,14 @@ impl EvmContext { pub fn create_return( &mut self, mut interpreter_result: InterpreterResult, - frame: Box, - ) -> (InterpreterResult, Address) { - let address = frame.created_address.unwrap(); + address: Address, + journal_checkpoint: JournalCheckpoint, + ) -> InterpreterResult { + //let address = frame.created_address.unwrap(); // if return is not ok revert and return. if !matches!(interpreter_result.result, return_ok!()) { - self.journaled_state.checkpoint_revert(frame.checkpoint); - return (interpreter_result, address); + self.journaled_state.checkpoint_revert(journal_checkpoint); + return interpreter_result; } // Host error if present on execution // if ok, check contract creation limit and calculate gas deduction on output len. @@ -448,9 +447,9 @@ impl EvmContext { && !interpreter_result.output.is_empty() && interpreter_result.output.first() == Some(&0xEF) { - self.journaled_state.checkpoint_revert(frame.checkpoint); + self.journaled_state.checkpoint_revert(journal_checkpoint); interpreter_result.result = InstructionResult::CreateContractStartingWithEF; - return (interpreter_result, address); + return interpreter_result; } // EIP-170: Contract code size limit @@ -463,9 +462,9 @@ impl EvmContext { .limit_contract_code_size .unwrap_or(MAX_CODE_SIZE) { - self.journaled_state.checkpoint_revert(frame.checkpoint); + self.journaled_state.checkpoint_revert(journal_checkpoint); interpreter_result.result = InstructionResult::CreateContractSizeLimit; - return (interpreter_result, frame.created_address.unwrap()); + return interpreter_result; } let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT; if !interpreter_result.gas.record_cost(gas_for_code) { @@ -474,9 +473,9 @@ impl EvmContext { // final gas fee for adding the contract code to the state, the contract // creation fails (i.e. goes out-of-gas) rather than leaving an empty contract. if SPEC::enabled(HOMESTEAD) { - self.journaled_state.checkpoint_revert(frame.checkpoint); + self.journaled_state.checkpoint_revert(journal_checkpoint); interpreter_result.result = InstructionResult::OutOfGas; - return (interpreter_result, address); + return interpreter_result; } else { interpreter_result.output = Bytes::new(); } @@ -499,7 +498,7 @@ impl EvmContext { self.journaled_state.set_code(address, bytecode); interpreter_result.result = InstructionResult::Return; - (interpreter_result, address) + interpreter_result } } /// Test utilities for the [`EvmContext`]. @@ -669,8 +668,12 @@ mod tests { let FrameOrResult::Frame(frame) = res else { panic!("Expected FrameOrResult::Frame"); }; - assert!(!frame.is_create); - assert_eq!(frame.created_address, None); - assert_eq!(frame.subcall_return_memory_range, 0..0); + assert!(!frame.is_create()); + assert_eq!( + frame.frame_data, + FrameData::Call { + return_memory_range: 0..0 + } + ); } } diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 8c28901e70..d4abd1b3a3 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -148,7 +148,7 @@ impl Evm<'_, EXT, DB> { // start main loop if CallStackFrame is created correctly let result = match first_stack_frame { FrameOrResult::Frame(first_stack_frame) => { - created_address = first_stack_frame.created_address; + created_address = first_stack_frame.created_address(); // take instruction talbe let table = self .handler diff --git a/crates/revm/src/frame.rs b/crates/revm/src/frame.rs index 02989ca073..9397732d12 100644 --- a/crates/revm/src/frame.rs +++ b/crates/revm/src/frame.rs @@ -9,16 +9,21 @@ use core::ops::Range; /// Call CallStackFrame. #[derive(Debug)] pub struct CallStackFrame { - /// True if it is create false if it is call. - pub is_create: bool, /// Journal checkpoint pub checkpoint: JournalCheckpoint, - /// temporary. If it is create it should have address. - pub created_address: Option
, - /// temporary. Call range - pub subcall_return_memory_range: Range, /// Interpreter pub interpreter: Interpreter, + /// Frame data + pub frame_data: FrameData, +} + +/// Specific data for call or create frame. +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum FrameData { + /// Call frame has return memory range where output will be stored. + Call { return_memory_range: Range }, + /// Create frame has a created address. + Create { created_address: Address }, } /// Contains either a frame or a result. @@ -29,6 +34,26 @@ pub enum FrameOrResult { Result(InterpreterResult), } +impl CallStackFrame { + /// Returns true if frame is call frame. + pub fn is_call(&self) -> bool { + matches!(self.frame_data, FrameData::Call { .. }) + } + + /// Returns true if frame is create frame. + pub fn is_create(&self) -> bool { + matches!(self.frame_data, FrameData::Create { .. }) + } + + /// Returns created address if frame is create otherwise returns None. + pub fn created_address(&self) -> Option
{ + match self.frame_data { + FrameData::Create { created_address } => Some(created_address), + _ => None, + } + } +} + impl FrameOrResult { /// Returns new frame. pub fn new_frame(frame: CallStackFrame) -> Self { diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 3b00315c9e..c43b3c3b2a 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -1,7 +1,3 @@ -//! Handler contains all the logic that is specific to the Evm. -//! It is used to define different behavior depending on the chain (Optimism,Mainnet) or -//! hardfork (Berlin, London, ..). - // Modules. mod handle_types; pub mod mainnet; diff --git a/crates/revm/src/handler/handle_types/execution_loop.rs b/crates/revm/src/handler/handle_types/execution_loop.rs index a71aa717e5..41a2ae9065 100644 --- a/crates/revm/src/handler/handle_types/execution_loop.rs +++ b/crates/revm/src/handler/handle_types/execution_loop.rs @@ -78,10 +78,10 @@ impl<'a, EXT: 'a, DB: Database + 'a> ExecutionLoopHandler<'a, EXT, DB> { pub fn new() -> Self { Self { create_first_frame: Arc::new(mainnet::create_first_frame::), - first_frame_return: Arc::new(mainnet::main_frame_return::), - frame_return: Arc::new(mainnet::handle_frame_return::), - sub_call: Arc::new(mainnet::handle_frame_sub_call::), - sub_create: Arc::new(mainnet::handle_frame_sub_create::), + first_frame_return: Arc::new(mainnet::first_frame_return::), + frame_return: Arc::new(mainnet::frame_return::), + sub_call: Arc::new(mainnet::sub_call::), + sub_create: Arc::new(mainnet::sub_create::), } } } diff --git a/crates/revm/src/handler/handle_types/pre_execution.rs b/crates/revm/src/handler/handle_types/pre_execution.rs index ef9a48543c..117735a651 100644 --- a/crates/revm/src/handler/handle_types/pre_execution.rs +++ b/crates/revm/src/handler/handle_types/pre_execution.rs @@ -34,9 +34,9 @@ impl<'a, EXT: 'a, DB: Database + 'a> PreExecutionHandler<'a, EXT, DB> { /// Creates mainnet MainHandles. pub fn new() -> Self { Self { - load_precompiles: Arc::new(mainnet::main_load_precompiles::), - load_accounts: Arc::new(mainnet::main_load::), - deduct_caller: Arc::new(mainnet::main_deduct_caller::), + load_precompiles: Arc::new(mainnet::load_precompiles::), + load_accounts: Arc::new(mainnet::load::), + deduct_caller: Arc::new(mainnet::deduct_caller::), } } } diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index 929b11761a..cf92067938 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -1,16 +1,14 @@ //! Mainnet related handlers. -mod frame; +mod execution_loop; mod post_execution; mod pre_execution; mod validation; -pub use frame::{ - create_first_frame, frame_return_with_refund_flag, handle_frame_return, handle_frame_sub_call, - handle_frame_sub_create, main_frame_return, +pub use execution_loop::{ + create_first_frame, first_frame_return, frame_return, frame_return_with_refund_flag, sub_call, + sub_create, }; pub use post_execution::{end, output, reimburse_caller, reward_beneficiary}; -pub use pre_execution::{ - deduct_caller_inner, main_deduct_caller, main_load, main_load_precompiles, -}; +pub use pre_execution::{deduct_caller, deduct_caller_inner, load, load_precompiles}; pub use validation::{validate_env, validate_initial_tx_gas, validate_tx_against_state}; diff --git a/crates/revm/src/handler/mainnet/frame.rs b/crates/revm/src/handler/mainnet/execution_loop.rs similarity index 58% rename from crates/revm/src/handler/mainnet/frame.rs rename to crates/revm/src/handler/mainnet/execution_loop.rs index bedfc75d9f..9de9d2e37d 100644 --- a/crates/revm/src/handler/mainnet/frame.rs +++ b/crates/revm/src/handler/mainnet/execution_loop.rs @@ -1,57 +1,36 @@ use crate::{ db::Database, interpreter::{ - return_ok, return_revert, CallContext, CallInputs, CallScheme, CreateInputs, Gas, - InstructionResult, InterpreterResult, SharedMemory, Transfer, + return_ok, return_revert, CallInputs, CreateInputs, Gas, InstructionResult, + InterpreterResult, SharedMemory, }, primitives::{Env, Spec, TransactTo}, - CallStackFrame, Context, FrameOrResult, + CallStackFrame, Context, FrameData, FrameOrResult, }; use alloc::boxed::Box; use core::ops::Range; -/// Creates first fmrae +/// Creates first frame. +#[inline] pub fn create_first_frame( context: &mut Context, gas_limit: u64, ) -> FrameOrResult { // call inner handling of call/create match context.evm.env.tx.transact_to { - TransactTo::Call(address) => context.evm.make_call_frame( - &CallInputs { - contract: address, - transfer: Transfer { - source: context.evm.env.tx.caller, - target: address, - value: context.evm.env.tx.value, - }, - input: context.evm.env.tx.data.clone(), - gas_limit, - context: CallContext { - caller: context.evm.env.tx.caller, - address, - code_address: address, - apparent_value: context.evm.env.tx.value, - scheme: CallScheme::Call, - }, - is_static: false, - }, + TransactTo::Call(_) => context.evm.make_call_frame( + &CallInputs::new(&context.evm.env.tx, gas_limit).unwrap(), 0..0, ), - TransactTo::Create(scheme) => context.evm.make_create_frame( + TransactTo::Create(_) => context.evm.make_create_frame( SPEC::SPEC_ID, - &CreateInputs { - caller: context.evm.env.tx.caller, - scheme, - value: context.evm.env.tx.value, - init_code: context.evm.env.tx.data.clone(), - gas_limit, - }, + &CreateInputs::new(&context.evm.env.tx, gas_limit).unwrap(), ), } } -/// Helper function called inside [`main_frame_return`] +/// Helper function called inside [`first_frame_return`] +#[inline] pub fn frame_return_with_refund_flag( env: &Env, call_result: InstructionResult, @@ -86,7 +65,7 @@ pub fn frame_return_with_refund_flag( /// Handle output of the transaction #[inline] -pub fn main_frame_return( +pub fn first_frame_return( env: &Env, call_result: InstructionResult, returned_gas: Gas, @@ -95,45 +74,51 @@ pub fn main_frame_return( } /// Handle frame return. -pub fn handle_frame_return( +#[inline] +pub fn frame_return( context: &mut Context, child_stack_frame: Box, parent_stack_frame: Option<&mut Box>, shared_memory: &mut SharedMemory, result: InterpreterResult, ) -> Option { - // break from loop if this is last CallStackFrame. - if child_stack_frame.is_create { - let Some(parent_stack_frame) = parent_stack_frame else { - return Some( - context - .evm - .create_return::(result, child_stack_frame) - .0, + match child_stack_frame.frame_data { + FrameData::Create { created_address } => { + let result = context.evm.create_return::( + result, + created_address, + child_stack_frame.checkpoint, ); - }; - let (result, address) = context.evm.create_return::(result, child_stack_frame); - parent_stack_frame - .interpreter - .insert_create_output(result, Some(address)) - } else { - let Some(parent_stack_frame) = parent_stack_frame else { - return Some(context.evm.call_return(result, child_stack_frame)); - }; - let subcall_memory_return_offset = child_stack_frame.subcall_return_memory_range.clone(); - let result = context.evm.call_return(result, child_stack_frame); - - parent_stack_frame.interpreter.insert_call_output( - shared_memory, - result, - subcall_memory_return_offset, - ) + let Some(parent_stack_frame) = parent_stack_frame else { + return Some(result); + }; + parent_stack_frame + .interpreter + .insert_create_output(result, Some(created_address)) + } + FrameData::Call { + return_memory_range, + } => { + let result = context + .evm + .call_return(result, child_stack_frame.checkpoint); + let Some(parent_stack_frame) = parent_stack_frame else { + return Some(result); + }; + + parent_stack_frame.interpreter.insert_call_output( + shared_memory, + result, + return_memory_range, + ) + } } None } /// Handle frame sub call. -pub fn handle_frame_sub_call( +#[inline] +pub fn sub_call( context: &mut Context, inputs: Box, curent_stack_frame: &mut CallStackFrame, @@ -157,7 +142,8 @@ pub fn handle_frame_sub_call( } /// Handle frame sub create. -pub fn handle_frame_sub_create( +#[inline] +pub fn sub_create( context: &mut Context, curent_stack_frame: &mut CallStackFrame, inputs: Box, @@ -185,7 +171,7 @@ mod tests { let mut env = Env::default(); env.tx.gas_limit = 100; - let gas = main_frame_return::(&env, InstructionResult::Stop, Gas::new(90)); + let gas = first_frame_return::(&env, InstructionResult::Stop, Gas::new(90)); assert_eq!(gas.remaining(), 90); assert_eq!(gas.spend(), 10); assert_eq!(gas.refunded(), 0); @@ -200,12 +186,12 @@ mod tests { let mut return_gas = Gas::new(90); return_gas.record_refund(30); - let gas = main_frame_return::(&env, InstructionResult::Stop, return_gas); + let gas = first_frame_return::(&env, InstructionResult::Stop, return_gas); assert_eq!(gas.remaining(), 90); assert_eq!(gas.spend(), 10); assert_eq!(gas.refunded(), 2); - let gas = main_frame_return::(&env, InstructionResult::Revert, return_gas); + let gas = first_frame_return::(&env, InstructionResult::Revert, return_gas); assert_eq!(gas.remaining(), 90); assert_eq!(gas.spend(), 10); assert_eq!(gas.refunded(), 0); @@ -216,7 +202,7 @@ mod tests { let mut env = Env::default(); env.tx.gas_limit = 100; - let gas = main_frame_return::(&env, InstructionResult::Revert, Gas::new(90)); + let gas = first_frame_return::(&env, InstructionResult::Revert, Gas::new(90)); assert_eq!(gas.remaining(), 90); assert_eq!(gas.spend(), 10); assert_eq!(gas.refunded(), 0); diff --git a/crates/revm/src/handler/mainnet/pre_execution.rs b/crates/revm/src/handler/mainnet/pre_execution.rs index a16afdf804..84bd2952f8 100644 --- a/crates/revm/src/handler/mainnet/pre_execution.rs +++ b/crates/revm/src/handler/mainnet/pre_execution.rs @@ -14,13 +14,14 @@ use crate::{ }; /// Main precompile load -pub fn main_load_precompiles() -> Precompiles { +#[inline] +pub fn load_precompiles() -> Precompiles { Precompiles::new(PrecompileSpecId::from_spec_id(SPEC::SPEC_ID)).clone() } /// Main load handle #[inline] -pub fn main_load( +pub fn load( context: &mut Context, ) -> Result<(), EVMError> { // set journaling state flag. @@ -51,6 +52,7 @@ pub fn main_load( } /// Helper function that deducts the caller balance. +#[inline] pub fn deduct_caller_inner(caller_account: &mut Account, env: &Env) { // Subtract gas costs from the caller's account. // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. @@ -75,8 +77,9 @@ pub fn deduct_caller_inner(caller_account: &mut Account, env: &Env) caller_account.mark_touch(); } +/// Deducts the caller balance to the transaction limit. #[inline] -pub fn main_deduct_caller( +pub fn deduct_caller( context: &mut Context, ) -> Result<(), EVMError> { // load caller's account. diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 63bf92ce2c..f1266177e5 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -144,7 +144,6 @@ mod tests { } #[test] - #[cfg(not(feature = "optimism"))] fn test_gas_inspector() { use crate::{ db::BenchmarkDB, diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index 8798f8a926..c03f5e4331 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -1,11 +1,15 @@ use crate::{ db::Database, handler::register::{EvmHandler, EvmInstructionTables}, - interpreter::{opcode, Interpreter, InterpreterResult}, - CallStackFrame, Evm, FrameOrResult, Inspector, JournalEntry, + interpreter::{ + opcode, opcode::BoxedInstruction, CallInputs, InstructionResult, Interpreter, + InterpreterResult, + }, + primitives::TransactTo, + CallStackFrame, Evm, FrameData, FrameOrResult, Inspector, JournalEntry, }; use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use revm_interpreter::{opcode::BoxedInstruction, InstructionResult}; +use revm_interpreter::CreateInputs; pub trait GetInspector<'a, DB: Database> { fn get_inspector(&mut self) -> &mut dyn Inspector; @@ -16,14 +20,20 @@ pub trait GetInspector<'a, DB: Database> { /// /// # Note /// -/// Most of the functions are wrapped for Inspector usage expect -/// the SubCreate and SubCall calls that got overwritten. +/// Handles that are overwritten: +/// * SubCreate +/// * SubCall +/// * CreateFirstFrame +/// /// /// Few instructions handlers are wrapped twice once for `step` and `step_end` /// and in case of Logs and Selfdestruct wrapper is wrapped again for the /// `log` and `selfdestruct` calls. /// /// `frame_return` is also wrapped so that Inspector could call `call_end` or `create_end`. +/// +/// `create_first_frame` is also wrapped so that Inspector could call `call` and `crate` on it. +/// While return for first frame is handled by `frame_return`. pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( handler: &mut EvmHandler<'a, EXT, DB>, ) { @@ -84,6 +94,37 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( inspect_log(opcode::LOG4); // wrap first frame create and main frame return. + handler.execution_loop.create_first_frame = + Arc::new(move |context, gas_limit| -> FrameOrResult { + // call inner handling of call/create + match context.evm.env.tx.transact_to { + TransactTo::Call(_) => { + let mut call_inputs = CallInputs::new(&context.evm.env.tx, gas_limit).unwrap(); + // call inspector and return of inspector returns result. + if let Some(output) = context + .external + .get_inspector() + .call(&mut context.evm, &mut call_inputs) + { + return FrameOrResult::Result(output.0); + } + // first call frame does not have return range. + context.evm.make_call_frame(&call_inputs, 0..0) + } + TransactTo::Create(_) => { + let mut create_inputs = + CreateInputs::new(&context.evm.env.tx, gas_limit).unwrap(); + if let Some(output) = context + .external + .get_inspector() + .create(&mut context.evm, &mut create_inputs) + { + return FrameOrResult::Result(output.0); + }; + context.evm.make_create_frame(spec_id, &create_inputs) + } + } + }); // register selfdestruct function. if let Some(i) = table.get_mut(opcode::SELFDESTRUCT as usize) { @@ -138,7 +179,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( } FrameOrResult::Result(result) => { let (result, address) = - inspector.create_end(&mut context.evm, result, frame.created_address); + inspector.create_end(&mut context.evm, result, frame.created_address()); // insert result of the failed creation of create CallStackFrame. frame.interpreter.insert_create_output(result, address); None @@ -181,13 +222,16 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( handler.execution_loop.frame_return = Arc::new( move |context, mut child, parent, memory, mut result| -> Option { let inspector = &mut context.external.get_inspector(); - result = if child.is_create { - let (result, address) = - inspector.create_end(&mut context.evm, result, child.created_address); - child.created_address = address; - result - } else { - inspector.call_end(&mut context.evm, result) + result = match &mut child.frame_data { + FrameData::Create { created_address } => { + let (result, address) = + inspector.create_end(&mut context.evm, result, Some(*created_address)); + if let Some(address) = address { + *created_address = address; + } + result + } + FrameData::Call { .. } => inspector.call_end(&mut context.evm, result), }; old_handle(context, child, parent, memory, result) }, diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index e688251c02..e9c150b548 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -39,7 +39,7 @@ pub use db::{ }; pub use db::{Database, DatabaseCommit, DatabaseRef, DummyStateDB, InMemoryDB}; pub use evm::{Evm, CALL_STACK_LIMIT}; -pub use frame::{CallStackFrame, FrameOrResult}; +pub use frame::{CallStackFrame, FrameData, FrameOrResult}; pub use handler::Handler; pub use inspector::{ inspector_handle_register, inspector_instruction, inspectors, GetInspector, Inspector, diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index defb0e2a6e..01276e4f2b 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -4,7 +4,6 @@ mod handler_register; mod l1block; pub use handler_register::{ - deduct_caller, end_handle, handle_call_return, main_return, optimism_handle_register, - reward_beneficiary, + deduct_caller, end, handle_call_return, optimism_handle_register, output, reward_beneficiary, }; pub use l1block::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; diff --git a/documentation/src/crates/revm/builder.md b/documentation/src/crates/revm/builder.md index 2da6623f06..dad889ba5b 100644 --- a/documentation/src/crates/revm/builder.md +++ b/documentation/src/crates/revm/builder.md @@ -14,7 +14,7 @@ Simple example of using `EvmBuilder`: // build Evm with default values. let mut evm = Evm::builder().build(); - evm.transact(); + let output = evm.transact(); ``` ## Builder Stages @@ -63,7 +63,7 @@ Example of using builder to create Evm with inspector: .build(); // Execute the evm. - let _out = evm.transact(); + let output = evm.transact(); // Extract evm context. let Context { @@ -86,13 +86,13 @@ Example of changing spec id and Environment of already build evm. let mut evm = evm.modify_spec_id(BERLIN); // Execute the evm. - evm.transact(); + let output1 = evm.transact(); // Example of modifying the tx env. let mut evm = evm.modify().modify_tx_env(|env| env.gas_price = 0.into()).build(); // Execute the evm with modified tx env. - evm.transact(); + let output2 = evm.transact(); ``` ## Appending handler registers diff --git a/documentation/src/crates/revm/evm.md b/documentation/src/crates/revm/evm.md index bf5cafc78c..44bdd5747b 100644 --- a/documentation/src/crates/revm/evm.md +++ b/documentation/src/crates/revm/evm.md @@ -14,7 +14,7 @@ Data structures of block and transaction can be found inside `Environment`. And ## Runtime -Runtime consist of list of functions from `Handler` that are called in predefined order. They are grouped by functionality on `Verification`, `Main`, `Frame` and `Instruction` functions. Verification function are related to the preverification of set `Environment` data. Main function is the main logic of the transaction execution. Frame function handles call and creates and sub calls. And Instruction functions are instruction table that executes opcodes. +Runtime consist of list of functions from `Handler` that are called in predefined order. They are grouped by functionality on `Verification`, `PreExecution`, `ExecutionLoop`, `PostExecution` and `Instruction` functions. Verification function are related to the preverification of set `Environment` data. Pre/Post execution function are function that deduct and reward caller beneficiary. And `ExecutionLoop` functions handles initial call and creates and sub calls. `Instruction` functions are instruction table that is used inside Interpreter to execute opcodes. `Evm` execution runs **two** loops. First loop is call loop that everything starts with, it creates call frames, handles subcalls and its return outputs and call Interpreter loop to execute bytecode instructions it is handled by `ExecutionLoopHandler`. Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction from `InstructionTable`. @@ -32,5 +32,5 @@ Main function inside evm are: * `preverify` - that only preverifies transaction information. * `transact preverified` - is next step after preverification that executes transaction. * `transact` - it calls both preverifies and it executes transaction. -* `builder` and `modify` function - allows building or modifying the Evm, more on this can be found in [`EvmBuilder`](./builder.md) documentation. This is main way of creating Evm. +* `builder` and `modify` function - allows building or modifying the Evm, more on this can be found in [`EvmBuilder`](./builder.md) documentation. `builder` is main way of creating Evm and `modify` allows you to modify parts of it without dissolving `Evm`. * `into_context` - is used we want to get the `Context` from the Evm. \ No newline at end of file diff --git a/documentation/src/crates/revm/handler.md b/documentation/src/crates/revm/handler.md index 5400e05aad..5db00daee2 100644 --- a/documentation/src/crates/revm/handler.md +++ b/documentation/src/crates/revm/handler.md @@ -1,16 +1,19 @@ # Handler -Is logic part of the Evm, it contains the Specification ID, and list of functions that do the logic and list of registers that can change the behaviour of the Handler. +Is logic part of the Evm, it contains the Specification ID, list of functions that do the logic and list of registers that can change behavior of the Handler when `Handler` is build. -Functions can be grouped in four categories and are marked in that way in the code: +Functions can be grouped in five categories and are marked in that way in the code: * Validation functions: `ValidateHandler` -* Main functions: `MainHandler` -* Frame functions: `ExecutionLoopHandler` -* Internal functions: `InstractionTable` +* Pre execution functions: `PreExecutionHandler` +* Execution loop functions: `LoopExecutionHandler` +* Post execution functions: `PostExecutionHandler` +* Instruction table: `InstructionTable` ### Handle Registers -Simple function that is used to modify handler functions. Amazing thing about them is that they can be done over generic external type. For example this allows to have a register over trait that would allow to add hooks to the any type that implements the trait, that trait can be a GetInspector trait so anyone that implement it would be able to register inspector related functions. It is used inside the `EvmBuilder` to change behaviour of the default mainnet Handler. +Simple function that is used to modify handler functions. Amazing thing about them is that they can be done over generic external type. For example this allows to have a register over trait that would allow to add hooks to the any type that implements the trait, that trait can be a GetInspector trait so anyone that implement it would be able to register inspector related functions. It is used inside the `EvmBuilder` to change behavior of the default mainnet Handler. + +Handle registers are set in `EvmBuilder`. Order of the registers is important as they are called in the order they are registered. And it matters if register overrides the previous handle or just wraps it, overriding handle can disrupt the logic of previous registered handles. @@ -32,26 +35,52 @@ Consist of functions that are used to validate transaction and block data. They It loads the caller account and checks those information. Among them the nonce, if there is enough balance to pay for max gas spent and balance transferred. -### MainHandler +### PreExecutionHandler -Consist of functions that are used to execute transaction. They are called in the following order: +Consist of functions that are called before execution loop. They are called in the following order: -Logic when running transaction consist of few stages that are implemented as a handle calls: -* main_load +* load Loads access list and beneficiary from `Database`. Cold load is done here. +* load precompiles + + Load precompiles. + * deduct_caller: Deducts values from the caller to the maximum amount of gas that can be spent on the transaction. This loads the caller account from the `Database`. -* create_first_frame and start_the_loop +### ExecutionLoopHandler + +Consist of the function that handles the call stack and the first loop. They are called in the following order: + +* create_first_frame - These two handles main call loop that creates and handles stack of frames. It is responsible for handling subcalls and its return outputs and call Interpreter loop to execute bytecode instructions. + This handler crates first frame of the call stack. It is called only once per transaction. -* call_return +* first_frame_return - Handler that allows processing of the returned output from the call. It calculated refunded gas and final spent gas. + This handler is called after the first frame is executed. It is used to calculate the gas that is returned from the first frame. + +* frame_return + + This handler is called after every frame is executed (Expect first), it will calculate the gas that is returned from the frame and apply output to the parent frame. + +* sub_call + Create new call frame or return the Interpreter result if the call is not possible (has a error) or it is precompile call. + +* sub_crate + + Create new create call frame, create new account and execute bytecode that outputs the code of the new account. + +### InstructionTable + +Is a list of 256 function pointers that are used to execute instructions. They have two types, first is simple function that is faster and second is BoxedInstraction that has a small performance penalty but allows to capture the data. Look at the Interpreter documentation for more information. + +### PostExecutionHandler + +Is a list of functions that are called after the execution loop. They are called in the following order: * reimburse_caller @@ -62,17 +91,10 @@ Logic when running transaction consist of few stages that are implemented as a h At the end of every transaction beneficiary needs to be rewarded with the fee. -* main_return +* output - It returns the changes state and the result of the execution. - - -### ExecutionLoopHandler + It returns the changes state and the result of the execution. -Consist of the function that handles the call stack and the first loop. They are called in the following order: - -TODO - -### InstructionTable - -Is a list of 256 function pointers that are used to execute instructions. They have two types, first is simple function that is faster and second is BoxedInstraction that has a small performance penalty but allows to capture the data. Look at the Interpreter documentation for more information. \ No newline at end of file +* end + + It will be called always as the last function of the handler. \ No newline at end of file From 055f0f6c1eebe8f5dd47736f6930e8107c0f9e3e Mon Sep 17 00:00:00 2001 From: rakita Date: Tue, 9 Jan 2024 03:59:04 +0100 Subject: [PATCH 41/52] check links mdbook --- documentation/src/crates/revm.md | 2 +- documentation/src/crates/revm/evm.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/src/crates/revm.md b/documentation/src/crates/revm.md index e9a6934929..80b7d64a27 100644 --- a/documentation/src/crates/revm.md +++ b/documentation/src/crates/revm.md @@ -2,7 +2,7 @@ The `crate` is focused on the implementation of Ethereum Virtual Machine (EVM) including call loop and host implementation, database handling, state journaling and powerful logic handlers that can be overwritten. This crate pulls Primitives, Interpreter and Precompiles together to deliver the rust evm. -Starting point of the documentation should be a (`Evm`)[./revm/evm.md] that is main structure of EVM. Then i would recomend reading about the `EvmBuilder` that is used to create the `Evm` and modify. After that you can read about the `Handler` that is used to modify the logic of the Evm and it will tie with how introspection of Evm can be done. And lastly you can read about the `Inspector` that is legacy interface for inspecting execution that is repurposed as one of example of handler registers. +Starting point of the documentation should be a [`Evm`](./revm/evm.md) that is main structure of EVM. Then i would recomend reading about the `EvmBuilder` that is used to create the `Evm` and modify. After that you can read about the `Handler` that is used to modify the logic of the Evm and it will tie with how introspection of Evm can be done. And lastly you can read about the `Inspector` that is legacy interface for inspecting execution that is repurposed as one of example of handler registers. Modules: - `evm`: This is main module that executed EVM calls. diff --git a/documentation/src/crates/revm/evm.md b/documentation/src/crates/revm/evm.md index 44bdd5747b..ec6434747b 100644 --- a/documentation/src/crates/revm/evm.md +++ b/documentation/src/crates/revm/evm.md @@ -20,7 +20,7 @@ Runtime consist of list of functions from `Handler` that are called in predefine First loop, the call loop, implements stack of `Frames` and it is responsible for handling sub calls and its return outputs. At the start Evm creates first `Frame` that contains `Interpreter` and starts the loop. `Interpreter` returns the `InterpreterAction` and action can be a `Return` of a call this means this interpreter finished its run or `SubCall`/`SubCreate` that means that new `Frame` needs to be created and pushed to the stack. When `Interpreter` returns `Return` action `Frame` is popped from the stack and its return value is pushed to the parent `Frame` stack. When `Interpreter` returns `SubCall`/`SubCreate` action new `Frame` is created and pushed to the stack and the loop continues. When the stack is empty the loop finishes. -Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction. It is called from the call loop and it is responsible for executing bytecode instructions. It is implemented in [`Interpreter`](../interpreter/interpreter.md) crate. +Second loop is `Interpreter` loop that loops over bytecode opcodes and executes instruction. It is called from the call loop and it is responsible for executing bytecode instructions. It is implemented in [`Interpreter`](../interpreter.md) crate. To dive deeper into the `Evm` logic check [`Handler`](./handler.md) documentation. From 0b3be0edba6d20609c63c0edadd9f3c9867caf1e Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 10 Jan 2024 13:11:49 +0100 Subject: [PATCH 42/52] comments and cleanup --- crates/revm/Cargo.toml | 4 -- crates/revm/src/builder.rs | 51 +++++++++---------- crates/revm/src/context.rs | 2 +- crates/revm/src/db.rs | 7 --- crates/revm/src/evm.rs | 6 ++- .../handler/handle_types/post_execution.rs | 2 +- .../src/handler/handle_types/pre_execution.rs | 2 +- crates/revm/src/lib.rs | 5 +- 8 files changed, 32 insertions(+), 47 deletions(-) diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 7121bd4e77..d7bdfb3999 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -73,10 +73,6 @@ optional_beneficiary_reward = ["revm-interpreter/optional_beneficiary_reward"] secp256k1 = ["revm-precompile/secp256k1"] c-kzg = ["revm-precompile/c-kzg"] -# deprecated features -web3db = [] -with-serde = [] - [[example]] name = "fork_ref_transact" path = "../../examples/fork_ref_transact.rs" diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index 04e554b52b..3f2d8616be 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -24,8 +24,8 @@ pub trait BuilderStage {} pub struct SetGenericStage; impl BuilderStage for SetGenericStage {} -/// Second stage of the builder allows setting the external context. -/// Requires the database to be set. +/// Second stage of the builder allows appending handler registers. +/// Requires the database and external context to be set. pub struct HandlerStage; impl BuilderStage for HandlerStage {} @@ -42,38 +42,24 @@ impl<'a> Default for EvmBuilder<'a, SetGenericStage, (), EmptyDB> { impl<'a, EXT, DB: Database> EvmBuilder<'a, SetGenericStage, EXT, DB> { /// Sets the [`EmptyDB`] as the [`Database`] that will be used by [`Evm`]. - /// - /// # Note - /// - /// When changed it will reset the handler to the mainnet. pub fn with_empty_db(self) -> EvmBuilder<'a, SetGenericStage, EXT, EmptyDB> { EvmBuilder { evm: self.evm.with_db(EmptyDB::default()), external: self.external, handler: Handler::mainnet_with_spec(self.handler.spec_id), - phantom: PhantomData, } } /// Sets the [`Database`] that will be used by [`Evm`]. - /// - /// # Note - /// - /// When changed it will reset the handler to default mainnet. pub fn with_db(self, db: ODB) -> EvmBuilder<'a, SetGenericStage, EXT, ODB> { EvmBuilder { evm: self.evm.with_db(db), external: self.external, handler: Handler::mainnet_with_spec(self.handler.spec_id), - phantom: PhantomData, } } /// Sets the [`DatabaseRef`] that will be used by [`Evm`]. - /// - /// # Note - /// - /// When changed it will reset the handler to default mainnet. pub fn with_ref_db( self, db: ODB, @@ -82,7 +68,6 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SetGenericStage, EXT, DB> { evm: self.evm.with_db(WrapDatabaseRef(db)), external: self.external, handler: Handler::mainnet_with_spec(self.handler.spec_id), - phantom: PhantomData, } } @@ -102,7 +87,8 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, SetGenericStage, EXT, DB> { } impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { - /// Creates new build from EVM, evm is consumed and all field are moved to Builder. + /// Creates new builder from Evm, Evm is consumed and all field are moved to Builder. + /// It will preserve set handler and context. /// /// Builder is in HandlerStage and both database and external are set. pub fn new(evm: Evm<'a, EXT, DB>) -> Self { @@ -114,7 +100,7 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { } } - /// Sets the [`EmptyDB`] and resets the [`Handler`] + /// Sets the [`EmptyDB`] and resets the [`Handler`] to default mainnet. pub fn reset_handler_with_empty_db(self) -> EvmBuilder<'a, HandlerStage, EXT, EmptyDB> { EvmBuilder { evm: self.evm.with_db(EmptyDB::default()), @@ -124,7 +110,8 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { } } - /// Sets the [`Database`] that will be used by [`Evm`]. + /// Sets the [`Database`] that will be used by [`Evm`] + /// and resets the [`Handler`] to default mainnet. pub fn reset_handler_with_db( self, db: ODB, @@ -138,7 +125,8 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { } } - /// Resets [`Handler`] and sets the [`DatabaseRef`] that will be used by [`Evm`]. + /// Resets [`Handler`] and sets the [`DatabaseRef`] that will be used by [`Evm`] + /// and resets the [`Handler`] to default mainnet. pub fn reset_handler_with_ref_db( self, db: ODB, @@ -152,7 +140,8 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { } } - /// Resets [`Handler`] and sets new `External` context type. + /// Resets [`Handler`] and sets new `ExternalContext` type. + /// and resets the [`Handler`] to default mainnet. pub fn reset_handler_with_external_context( self, external: OEXT, @@ -166,20 +155,22 @@ impl<'a, EXT, DB: Database> EvmBuilder<'a, HandlerStage, EXT, DB> { } } -// Accessed always. impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> { /// Builds the [`Evm`]. pub fn build(self) -> Evm<'a, EXT, DB> { - Evm { - context: Context { + Evm::new( + Context { evm: self.evm, external: self.external, }, - handler: self.handler, - } + self.handler, + ) } - /// Appends the handler register to the handler. + /// Register Handler that modifies the behavior of EVM. + /// Check [`Handler`] for more information. + /// + /// When called, EvmBuilder will transition from [`SetGenericStage`] to [`HandlerStage`]. pub fn append_handler_register( mut self, handle_register: register::HandleRegister<'a, EXT, DB>, @@ -197,6 +188,8 @@ impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> /// Register Handler that modifies the behavior of EVM. /// Check [`Handler`] for more information. + /// + /// When called, EvmBuilder will transition from [`SetGenericStage`] to [`HandlerStage`]. pub fn append_handler_register_box( mut self, handle_register: register::HandleRegisterBox<'a, EXT, DB>, @@ -353,11 +346,13 @@ mod test { #[test] fn build_modify_build() { + // build evm let evm = Evm::builder() .with_empty_db() .spec_id(SpecId::HOMESTEAD) .build(); + // modify evm let evm = evm.modify().spec_id(SpecId::FRONTIER).build(); let _ = evm .modify() diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs index 5d8e2fb5f0..abe2815cb1 100644 --- a/crates/revm/src/context.rs +++ b/crates/revm/src/context.rs @@ -15,7 +15,7 @@ use crate::{ use alloc::boxed::Box; use core::ops::Range; -/// Main Context structure that contains both EvmContext and external contexts. +/// Main Context structure that contains both EvmContext and External context. pub struct Context { /// Evm Context. pub evm: EvmContext, diff --git a/crates/revm/src/db.rs b/crates/revm/src/db.rs index d65f214907..f891358034 100644 --- a/crates/revm/src/db.rs +++ b/crates/revm/src/db.rs @@ -16,10 +16,3 @@ pub use states::{ OriginalValuesKnown, PlainAccount, RevertToSlot, State, StateBuilder, StateDBBox, StorageWithOriginalValues, TransitionAccount, TransitionState, }; - -#[cfg(all(not(feature = "ethersdb"), feature = "web3db"))] -compile_error!( - "`web3db` feature is deprecated, drop-in replacement can be found with feature `ethersdb`" -); - -pub type DummyStateDB = InMemoryDB; diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index d4abd1b3a3..521c754242 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -59,7 +59,11 @@ impl<'a> Evm<'a, (), EmptyDB> { impl<'a, EXT, DB: Database> Evm<'a, EXT, DB> { /// Create new EVM. - pub fn new(context: Context, handler: Handler<'a, Self, EXT, DB>) -> Evm<'a, EXT, DB> { + pub fn new( + mut context: Context, + handler: Handler<'a, Self, EXT, DB>, + ) -> Evm<'a, EXT, DB> { + context.evm.journaled_state.set_spec_id(handler.spec_id); Evm { context, handler } } diff --git a/crates/revm/src/handler/handle_types/post_execution.rs b/crates/revm/src/handler/handle_types/post_execution.rs index 850a1774f2..c665a30b75 100644 --- a/crates/revm/src/handler/handle_types/post_execution.rs +++ b/crates/revm/src/handler/handle_types/post_execution.rs @@ -37,7 +37,7 @@ pub type EndHandle<'a, EXT, DB> = Arc< + 'a, >; -/// Handles related to main function. +/// Handles related to post execution after the stack loop is finished. pub struct PostExecutionHandler<'a, EXT, DB: Database> { /// Reimburse the caller with ethereum it didn't spent. pub reimburse_caller: ReimburseCallerHandle<'a, EXT, DB>, diff --git a/crates/revm/src/handler/handle_types/pre_execution.rs b/crates/revm/src/handler/handle_types/pre_execution.rs index 117735a651..0872a22e4d 100644 --- a/crates/revm/src/handler/handle_types/pre_execution.rs +++ b/crates/revm/src/handler/handle_types/pre_execution.rs @@ -20,7 +20,7 @@ pub type LoadAccountsHandle<'a, EXT, DB> = pub type DeductCallerHandle<'a, EXT, DB> = Arc) -> EVMResultGeneric<(), ::Error> + 'a>; -/// Handles related to main function. +/// Handles related to pre execution before the stack loop is started. pub struct PreExecutionHandler<'a, EXT, DB: Database> { /// Load precompiles pub load_precompiles: LoadPrecompilesHandle<'a>, diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index e9c150b548..120a47102a 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -6,9 +6,6 @@ #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(all(feature = "with-serde", not(feature = "serde")))] -compile_error!("`with-serde` feature has been renamed to `serde`."); - #[macro_use] extern crate alloc; @@ -37,7 +34,7 @@ pub use context::{Context, EvmContext}; pub use db::{ CacheState, DBBox, State, StateBuilder, StateDBBox, TransitionAccount, TransitionState, }; -pub use db::{Database, DatabaseCommit, DatabaseRef, DummyStateDB, InMemoryDB}; +pub use db::{Database, DatabaseCommit, DatabaseRef, InMemoryDB}; pub use evm::{Evm, CALL_STACK_LIMIT}; pub use frame::{CallStackFrame, FrameData, FrameOrResult}; pub use handler::Handler; From 8fdcea9d2a9e903a48cde1fe6c3f5b84d0873b41 Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 10 Jan 2024 13:47:58 +0100 Subject: [PATCH 43/52] comment --- crates/revm/src/builder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index 3f2d8616be..64bfcbffd5 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -170,7 +170,7 @@ impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> /// Register Handler that modifies the behavior of EVM. /// Check [`Handler`] for more information. /// - /// When called, EvmBuilder will transition from [`SetGenericStage`] to [`HandlerStage`]. + /// When called, EvmBuilder will transition from SetGenericStage to HandlerStage. pub fn append_handler_register( mut self, handle_register: register::HandleRegister<'a, EXT, DB>, @@ -189,7 +189,7 @@ impl<'a, STAGE: BuilderStage, EXT, DB: Database> EvmBuilder<'a, STAGE, EXT, DB> /// Register Handler that modifies the behavior of EVM. /// Check [`Handler`] for more information. /// - /// When called, EvmBuilder will transition from [`SetGenericStage`] to [`HandlerStage`]. + /// When called, EvmBuilder will transition from SetGenericStage to HandlerStage. pub fn append_handler_register_box( mut self, handle_register: register::HandleRegisterBox<'a, EXT, DB>, From fa4c7121e8c8f57d82f1851c245d3b364476f464 Mon Sep 17 00:00:00 2001 From: rakita Date: Fri, 12 Jan 2024 01:35:44 +0100 Subject: [PATCH 44/52] Add initialize interepreter to first frame --- crates/revm/src/inspector/handler_register.rs | 147 +++++++++++++++++- 1 file changed, 144 insertions(+), 3 deletions(-) diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index c03f5e4331..abc3e2cb43 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -97,7 +97,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( handler.execution_loop.create_first_frame = Arc::new(move |context, gas_limit| -> FrameOrResult { // call inner handling of call/create - match context.evm.env.tx.transact_to { + let mut first_frame = match context.evm.env.tx.transact_to { TransactTo::Call(_) => { let mut call_inputs = CallInputs::new(&context.evm.env.tx, gas_limit).unwrap(); // call inspector and return of inspector returns result. @@ -123,7 +123,17 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( }; context.evm.make_create_frame(spec_id, &create_inputs) } + }; + + // call initialize interpreter from inspector. + if let FrameOrResult::Frame(ref mut frame) = first_frame { + context + .external + .get_inspector() + .initialize_interp(&mut frame.interpreter, &mut context.evm); } + + first_frame }); // register selfdestruct function. @@ -278,8 +288,15 @@ pub fn inspector_instruction< #[cfg(test)] mod tests { use super::*; - use crate::inspectors::NoOpInspector; - use crate::{db::EmptyDB, interpreter::opcode::*, primitives::BerlinSpec, Evm}; + use crate::{ + db::EmptyDB, + inspector::GetInspector, + inspectors::NoOpInspector, + interpreter::{opcode::*, CallInputs, CreateInputs, Interpreter, InterpreterResult}, + primitives::{Address, BerlinSpec}, + Database, Evm, EvmContext, Inspector, + }; + use core::ops::Range; #[test] fn test_make_boxed_instruction_table() { @@ -292,4 +309,128 @@ mod tests { inspector_instruction, ); } + + #[derive(Default, Debug)] + struct StackInspector { + initialize_interp_called: bool, + step: u32, + step_end: u32, + call: bool, + call_end: bool, + } + + impl GetInspector<'_, DB> for StackInspector { + fn get_inspector(&mut self) -> &mut dyn Inspector { + self + } + } + + impl Inspector for StackInspector { + fn initialize_interp(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext) { + if self.initialize_interp_called { + assert!(false, "initialize_interp should not be called twice") + } + self.initialize_interp_called = true; + } + + fn step(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext) { + self.step += 1; + } + + fn step_end(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext) { + self.step_end += 1; + } + + fn call( + &mut self, + _context: &mut EvmContext, + _call: &mut CallInputs, + ) -> Option<(InterpreterResult, Range)> { + if self.call { + assert!(false, "call should not be called twice") + } + self.call = true; + None + } + + fn call_end( + &mut self, + _context: &mut EvmContext, + result: InterpreterResult, + ) -> InterpreterResult { + if self.call_end { + assert!(false, "call_end should not be called twice") + } + self.call_end = true; + result + } + + fn create( + &mut self, + _context: &mut EvmContext, + _call: &mut CreateInputs, + ) -> Option<(InterpreterResult, Option
)> { + None + } + + fn create_end( + &mut self, + _context: &mut EvmContext, + result: InterpreterResult, + address: Option
, + ) -> (InterpreterResult, Option
) { + (result, address) + } + } + + #[test] + fn test_gas_inspector() { + use crate::{ + db::BenchmarkDB, + inspector::inspector_handle_register, + interpreter::opcode, + primitives::{address, Bytecode, Bytes, TransactTo}, + Evm, + }; + + let contract_data: Bytes = Bytes::from(vec![ + opcode::PUSH1, + 0x1, + opcode::PUSH1, + 0xb, + opcode::PUSH1, + 0x1, + opcode::PUSH1, + 0x1, + opcode::PUSH1, + 0x1, + opcode::CREATE, + opcode::STOP, + ]); + let bytecode = Bytecode::new_raw(contract_data); + + let mut evm: Evm<'_, StackInspector, BenchmarkDB> = Evm::builder() + .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) + .with_external_context(StackInspector::default()) + .modify_tx_env(|tx| { + tx.clear(); + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = + TransactTo::Call(address!("0000000000000000000000000000000000000000")); + tx.gas_limit = 21100; + }) + .append_handler_register(inspector_handle_register) + .build(); + + // run evm. + evm.transact().unwrap(); + + let inspector = evm.into_context().external; + + assert_eq!(inspector.step, 6); + assert_eq!(inspector.step_end, 6); + assert_eq!(inspector.initialize_interp_called, true); + assert_eq!(inspector.call, true); + assert_eq!(inspector.call_end, true); + } } From 2133f7f91ec04173198078225a3d1b6928383107 Mon Sep 17 00:00:00 2001 From: rakita Date: Fri, 12 Jan 2024 01:43:24 +0100 Subject: [PATCH 45/52] clippy --- crates/revm/src/inspector/handler_register.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index abc3e2cb43..481cc431bb 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -429,8 +429,8 @@ mod tests { assert_eq!(inspector.step, 6); assert_eq!(inspector.step_end, 6); - assert_eq!(inspector.initialize_interp_called, true); - assert_eq!(inspector.call, true); - assert_eq!(inspector.call_end, true); + assert!(inspector.initialize_interp_called); + assert!(inspector.call); + assert!(inspector.call_end); } } From d95dae539444009fd0236f2e2ef9eabc5c5511aa Mon Sep 17 00:00:00 2001 From: rakita Date: Fri, 12 Jan 2024 01:49:37 +0100 Subject: [PATCH 46/52] clippy2 --- crates/revm/src/inspector/handler_register.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index 481cc431bb..d46d52a8fe 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -328,7 +328,7 @@ mod tests { impl Inspector for StackInspector { fn initialize_interp(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext) { if self.initialize_interp_called { - assert!(false, "initialize_interp should not be called twice") + unreachable!("initialize_interp should not be called twice") } self.initialize_interp_called = true; } @@ -347,7 +347,7 @@ mod tests { _call: &mut CallInputs, ) -> Option<(InterpreterResult, Range)> { if self.call { - assert!(false, "call should not be called twice") + unreachable!("call should not be called twice") } self.call = true; None @@ -359,7 +359,7 @@ mod tests { result: InterpreterResult, ) -> InterpreterResult { if self.call_end { - assert!(false, "call_end should not be called twice") + unreachable!("call_end should not be called twice") } self.call_end = true; result From f790587cbaf9e244db6b07993b233ff56e742015 Mon Sep 17 00:00:00 2001 From: Loocapro Date: Mon, 15 Jan 2024 14:49:39 +0100 Subject: [PATCH 47/52] feat: create outcome --- crates/interpreter/src/interpreter.rs | 115 +++++++++++++++--- crates/interpreter/src/lib.rs | 4 +- .../src/handler/mainnet/execution_loop.rs | 8 +- crates/revm/src/inspector.rs | 8 +- crates/revm/src/inspector/customprinter.rs | 5 +- crates/revm/src/inspector/eip3155.rs | 5 +- crates/revm/src/inspector/gas.rs | 12 +- crates/revm/src/inspector/handler_register.rs | 26 ++-- 8 files changed, 139 insertions(+), 44 deletions(-) diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 1caed8a239..5a3521534f 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -93,26 +93,45 @@ impl Interpreter { } } - /// When sub create call returns we can insert output of that call into this interpreter. - pub fn insert_create_output(&mut self, result: InterpreterResult, address: Option
) { - self.return_data_buffer = match result.result { - // Save data to return data buffer if the create reverted - return_revert!() => result.output, - // Otherwise clear it - _ => Bytes::new(), - }; + /// Inserts the output of a `create` call into the interpreter. + /// + /// This function is used after a `create` call has been executed. It processes the outcome + /// of that call and updates the state of the interpreter accordingly. + /// + /// # Arguments + /// + /// * `create_outcome` - A `CreateOutcome` struct containing the results of the `create` call. + /// + /// # Behavior + /// + /// The function updates the `return_data_buffer` with the data from `create_outcome`. + /// Depending on the `next_action` indicated by `create_outcome`, it performs one of the following: + /// + /// - `NextAction::Continue`: Pushes the address from `create_outcome` to the stack, updates gas costs, and records any gas refunds. + /// - `NextAction::Revert`: Pushes `U256::ZERO` to the stack and updates gas costs. + /// - `NextAction::ExternalErr`: Sets the `instruction_result` to `InstructionResult::FatalExternalError`. + /// - Other actions: Pushes `U256::ZERO` to the stack. + /// + /// # Side Effects + /// + /// - Updates `return_data_buffer` with the data from `create_outcome`. + /// - Modifies the stack by pushing values depending on the `next_action`. + /// - Updates gas costs and records refunds in the interpreter's `gas` field. + /// - May alter `instruction_result` in case of external errors. + pub fn insert_create_outcome(&mut self, create_outcome: CreateOutcome) { + self.return_data_buffer = create_outcome.data_buffer(); - match result.result { - return_ok!() => { - push_b256!(self, address.unwrap_or_default().into_word()); - self.gas.erase_cost(result.gas.remaining()); - self.gas.record_refund(result.gas.refunded()); + match create_outcome.next_action() { + NextAction::Continue => { + push_b256!(self, create_outcome.address.unwrap_or_default().into_word()); + self.gas.erase_cost(create_outcome.result.gas.remaining()); + self.gas.record_refund(create_outcome.result.gas.refunded()); } - return_revert!() => { + NextAction::Revert => { push!(self, U256::ZERO); - self.gas.erase_cost(result.gas.remaining()); + self.gas.erase_cost(create_outcome.result.gas.remaining()); } - InstructionResult::FatalExternalError => { + NextAction::ExternalErr => { self.instruction_result = InstructionResult::FatalExternalError; } _ => { @@ -252,3 +271,67 @@ impl Interpreter { } } } + +/// Enum representing the next action to be taken based on the interpreter's result. +pub enum NextAction { + /// Indicates that the operation should continue. + Continue, + /// Indicates that the operation should revert. + Revert, + /// Indicates an external error occurred during the operation. + ExternalErr, + /// Indicates any other outcome not covered by the above variants. + Other, +} +/// Represents the outcome of a create operation in an interpreter. +/// +/// This struct holds the result of the operation along with an optional address. +/// It provides methods to determine the next action based on the result of the operation. +pub struct CreateOutcome { + // The result of the interpreter operation. + result: InterpreterResult, + // An optional address associated with the create operation. + address: Option
, +} + +impl CreateOutcome { + pub fn new(result: InterpreterResult, address: Option
) -> Self { + Self { result, address } + } + pub fn result(&self) -> &InterpreterResult { + &self.result + } + pub fn address(&self) -> Option
{ + self.address + } + /// Retrieves the data buffer based on the interpreter's result. + /// + /// If the result is `Revert`, `CallTooDeep`, or `OutOfFund`, it returns the associated output. + /// Otherwise, it returns an empty `Bytes` object. + pub fn data_buffer(&self) -> Bytes { + match self.result.result { + InstructionResult::Revert + | InstructionResult::CallTooDeep + | InstructionResult::OutOfFund => self.result.output.clone(), + _ => Bytes::new(), + } + } + + /// Determines the next action to take based on the interpreter's result. + /// + /// Returns a variant of `NextAction` enum, which indicates whether to continue, + /// revert, handle an external error, or take another action. + pub fn next_action(&self) -> NextAction { + match self.result.result { + InstructionResult::Continue + | InstructionResult::Stop + | InstructionResult::Return + | InstructionResult::SelfDestruct => NextAction::Continue, + InstructionResult::Revert + | InstructionResult::CallTooDeep + | InstructionResult::OutOfFund => NextAction::Revert, + InstructionResult::FatalExternalError => NextAction::ExternalErr, + _ => NextAction::Other, + } + } +} diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index eac96926bc..4e721a844c 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -26,8 +26,8 @@ pub use inner_models::*; pub use instruction_result::*; pub use instructions::{opcode, Instruction, OpCode, OPCODE_JUMPMAP}; pub use interpreter::{ - analysis, next_multiple_of_32, BytecodeLocked, Contract, Interpreter, InterpreterAction, - InterpreterResult, SharedMemory, Stack, EMPTY_SHARED_MEMORY, STACK_LIMIT, + analysis, next_multiple_of_32, BytecodeLocked, Contract, CreateOutcome, Interpreter, + InterpreterAction, InterpreterResult, SharedMemory, Stack, EMPTY_SHARED_MEMORY, STACK_LIMIT, }; pub use primitives::{MAX_CODE_SIZE, MAX_INITCODE_SIZE}; diff --git a/crates/revm/src/handler/mainnet/execution_loop.rs b/crates/revm/src/handler/mainnet/execution_loop.rs index 9de9d2e37d..b9a1c4c87f 100644 --- a/crates/revm/src/handler/mainnet/execution_loop.rs +++ b/crates/revm/src/handler/mainnet/execution_loop.rs @@ -1,7 +1,7 @@ use crate::{ db::Database, interpreter::{ - return_ok, return_revert, CallInputs, CreateInputs, Gas, InstructionResult, + return_ok, return_revert, CallInputs, CreateInputs, CreateOutcome, Gas, InstructionResult, InterpreterResult, SharedMemory, }, primitives::{Env, Spec, TransactTo}, @@ -92,9 +92,10 @@ pub fn frame_return( let Some(parent_stack_frame) = parent_stack_frame else { return Some(result); }; + let create_outcome = CreateOutcome::new(result, Some(created_address)); parent_stack_frame .interpreter - .insert_create_output(result, Some(created_address)) + .insert_create_outcome(create_outcome) } FrameData::Call { return_memory_range, @@ -151,10 +152,11 @@ pub fn sub_create( match context.evm.make_create_frame(SPEC::SPEC_ID, &inputs) { FrameOrResult::Frame(new_frame) => Some(new_frame), FrameOrResult::Result(result) => { + let create_outcome = CreateOutcome::new(result, None); // insert result of the failed creation of create CallStackFrame. curent_stack_frame .interpreter - .insert_create_output(result, None); + .insert_create_outcome(create_outcome); None } } diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index 60ce286ce6..ea4c87243a 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -18,7 +18,7 @@ mod noop; // Exports. pub use handler_register::{inspector_handle_register, inspector_instruction, GetInspector}; -use revm_interpreter::InterpreterResult; +use revm_interpreter::{CreateOutcome, InterpreterResult}; /// [Inspector] implementations. pub mod inspectors { #[cfg(feature = "std")] @@ -109,7 +109,7 @@ pub trait Inspector { &mut self, context: &mut EvmContext, inputs: &mut CreateInputs, - ) -> Option<(InterpreterResult, Option
)> { + ) -> Option { let _ = context; let _ = inputs; None @@ -125,9 +125,9 @@ pub trait Inspector { context: &mut EvmContext, result: InterpreterResult, address: Option
, - ) -> (InterpreterResult, Option
) { + ) -> CreateOutcome { let _ = context; - (result, address) + CreateOutcome::new(result, address) } /// Called when a contract has been self-destructed with funds transferred to target. diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs index fc65544b56..0ad14995c7 100644 --- a/crates/revm/src/inspector/customprinter.rs +++ b/crates/revm/src/inspector/customprinter.rs @@ -2,6 +2,7 @@ //! It is a great tool if some debugging is needed. use core::ops::Range; +use revm_interpreter::CreateOutcome; use crate::{ inspectors::GasInspector, @@ -73,7 +74,7 @@ impl Inspector for CustomPrintTracer { context: &mut EvmContext, result: InterpreterResult, address: Option
, - ) -> (InterpreterResult, Option
) { + ) -> CreateOutcome { self.gas_inspector.create_end(context, result, address) } @@ -97,7 +98,7 @@ impl Inspector for CustomPrintTracer { &mut self, _context: &mut EvmContext, inputs: &mut CreateInputs, - ) -> Option<(InterpreterResult, Option
)> { + ) -> Option { println!( "CREATE CALL: caller:{:?}, scheme:{:?}, value:{:?}, init_code:{:?}, gas:{:?}", inputs.caller, inputs.scheme, inputs.value, inputs.init_code, inputs.gas_limit diff --git a/crates/revm/src/inspector/eip3155.rs b/crates/revm/src/inspector/eip3155.rs index 22269082a3..7ec60d51ea 100644 --- a/crates/revm/src/inspector/eip3155.rs +++ b/crates/revm/src/inspector/eip3155.rs @@ -5,6 +5,7 @@ use crate::{ EvmContext, GetInspector, Inspector, }; use core::ops::Range; +use revm_interpreter::CreateOutcome; use serde_json::json; use std::io::Write; @@ -118,7 +119,7 @@ impl Inspector for TracerEip3155 { &mut self, _context: &mut EvmContext, _inputs: &mut CreateInputs, - ) -> Option<(InterpreterResult, Option
)> { + ) -> Option { None } @@ -127,7 +128,7 @@ impl Inspector for TracerEip3155 { context: &mut EvmContext, result: InterpreterResult, address: Option
, - ) -> (InterpreterResult, Option
) { + ) -> CreateOutcome { self.gas_inspector.create_end(context, result, address) } } diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index f1266177e5..1a83afae35 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -1,5 +1,7 @@ //! GasIspector. Helper Inspector to calculate gas for others. +use revm_interpreter::CreateOutcome; + use crate::{ interpreter::InterpreterResult, primitives::{db::Database, Address}, @@ -59,13 +61,15 @@ impl Inspector for GasInspector { _context: &mut EvmContext, result: InterpreterResult, address: Option
, - ) -> (InterpreterResult, Option
) { - (result, address) + ) -> CreateOutcome { + CreateOutcome::new(result, address) } } #[cfg(test)] mod tests { + use revm_interpreter::CreateOutcome; + use crate::{ inspector::GetInspector, inspectors::GasInspector, @@ -128,7 +132,7 @@ mod tests { &mut self, context: &mut EvmContext, call: &mut CreateInputs, - ) -> Option<(InterpreterResult, Option
)> { + ) -> Option { self.gas_inspector.create(context, call); None } @@ -138,7 +142,7 @@ mod tests { context: &mut EvmContext, result: InterpreterResult, address: Option
, - ) -> (InterpreterResult, Option
) { + ) -> CreateOutcome { self.gas_inspector.create_end(context, result, address) } } diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index d46d52a8fe..27603b5c62 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -119,7 +119,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( .get_inspector() .create(&mut context.evm, &mut create_inputs) { - return FrameOrResult::Result(output.0); + return FrameOrResult::Result(output.result().clone()); }; context.evm.make_create_frame(spec_id, &create_inputs) } @@ -177,8 +177,8 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( handler.execution_loop.sub_create = Arc::new( move |context, frame, mut inputs| -> Option> { let inspector = context.external.get_inspector(); - if let Some((result, address)) = inspector.create(&mut context.evm, &mut inputs) { - frame.interpreter.insert_create_output(result, address); + if let Some(create_outcome) = inspector.create(&mut context.evm, &mut inputs) { + frame.interpreter.insert_create_outcome(create_outcome); return None; } @@ -188,10 +188,10 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( Some(new_frame) } FrameOrResult::Result(result) => { - let (result, address) = + let create_outcome = inspector.create_end(&mut context.evm, result, frame.created_address()); // insert result of the failed creation of create CallStackFrame. - frame.interpreter.insert_create_output(result, address); + frame.interpreter.insert_create_outcome(create_outcome); None } } @@ -234,9 +234,12 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( let inspector = &mut context.external.get_inspector(); result = match &mut child.frame_data { FrameData::Create { created_address } => { - let (result, address) = - inspector.create_end(&mut context.evm, result, Some(*created_address)); - if let Some(address) = address { + let create_outcome = inspector.create_end( + &mut context.evm, + result.clone(), + Some(*created_address), + ); + if let Some(address) = create_outcome.address() { *created_address = address; } result @@ -297,6 +300,7 @@ mod tests { Database, Evm, EvmContext, Inspector, }; use core::ops::Range; + use revm_interpreter::CreateOutcome; #[test] fn test_make_boxed_instruction_table() { @@ -369,7 +373,7 @@ mod tests { &mut self, _context: &mut EvmContext, _call: &mut CreateInputs, - ) -> Option<(InterpreterResult, Option
)> { + ) -> Option { None } @@ -378,8 +382,8 @@ mod tests { _context: &mut EvmContext, result: InterpreterResult, address: Option
, - ) -> (InterpreterResult, Option
) { - (result, address) + ) -> CreateOutcome { + CreateOutcome::new(result, address) } } From e121c024647c58fbd0c6f8370a7a1e45b3879fe3 Mon Sep 17 00:00:00 2001 From: Loocapro Date: Mon, 15 Jan 2024 15:18:12 +0100 Subject: [PATCH 48/52] fix: createOutcome properties to pub and removed wrapper functions --- crates/interpreter/src/interpreter.rs | 10 ++-------- crates/revm/src/inspector/handler_register.rs | 4 ++-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 5a3521534f..8636f7468f 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -289,21 +289,15 @@ pub enum NextAction { /// It provides methods to determine the next action based on the result of the operation. pub struct CreateOutcome { // The result of the interpreter operation. - result: InterpreterResult, + pub result: InterpreterResult, // An optional address associated with the create operation. - address: Option
, + pub address: Option
, } impl CreateOutcome { pub fn new(result: InterpreterResult, address: Option
) -> Self { Self { result, address } } - pub fn result(&self) -> &InterpreterResult { - &self.result - } - pub fn address(&self) -> Option
{ - self.address - } /// Retrieves the data buffer based on the interpreter's result. /// /// If the result is `Revert`, `CallTooDeep`, or `OutOfFund`, it returns the associated output. diff --git a/crates/revm/src/inspector/handler_register.rs b/crates/revm/src/inspector/handler_register.rs index 27603b5c62..ea66a88c94 100644 --- a/crates/revm/src/inspector/handler_register.rs +++ b/crates/revm/src/inspector/handler_register.rs @@ -119,7 +119,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( .get_inspector() .create(&mut context.evm, &mut create_inputs) { - return FrameOrResult::Result(output.result().clone()); + return FrameOrResult::Result(output.result); }; context.evm.make_create_frame(spec_id, &create_inputs) } @@ -239,7 +239,7 @@ pub fn inspector_handle_register<'a, DB: Database, EXT: GetInspector<'a, DB>>( result.clone(), Some(*created_address), ); - if let Some(address) = create_outcome.address() { + if let Some(address) = create_outcome.address { *created_address = address; } result From c060651759bfbaf28e8468a48a090b4a0d9cadd3 Mon Sep 17 00:00:00 2001 From: Loocapro Date: Mon, 15 Jan 2024 15:54:35 +0100 Subject: [PATCH 49/52] review: fixed some review comments and added some improvement over instruction results --- crates/interpreter/src/interpreter.rs | 119 ++++++++++++++++---------- 1 file changed, 74 insertions(+), 45 deletions(-) diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 8636f7468f..fce2fd38d7 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -119,19 +119,28 @@ impl Interpreter { /// - Updates gas costs and records refunds in the interpreter's `gas` field. /// - May alter `instruction_result` in case of external errors. pub fn insert_create_outcome(&mut self, create_outcome: CreateOutcome) { - self.return_data_buffer = create_outcome.data_buffer(); + let instruction_result = create_outcome.instruction_result(); - match create_outcome.next_action() { - NextAction::Continue => { - push_b256!(self, create_outcome.address.unwrap_or_default().into_word()); - self.gas.erase_cost(create_outcome.result.gas.remaining()); - self.gas.record_refund(create_outcome.result.gas.refunded()); + self.return_data_buffer = if instruction_result.is_revert() { + // Save data to return data buffer if the create reverted + create_outcome.output().to_owned() + } else { + // Otherwise clear it + Bytes::new() + }; + + match instruction_result { + return_ok!() => { + let address = create_outcome.address; + push_b256!(self, address.unwrap_or_default().into_word()); + self.gas.erase_cost(create_outcome.gas().remaining()); + self.gas.record_refund(create_outcome.gas().refunded()); } - NextAction::Revert => { + return_revert!() => { push!(self, U256::ZERO); - self.gas.erase_cost(create_outcome.result.gas.remaining()); + self.gas.erase_cost(create_outcome.gas().remaining()); } - NextAction::ExternalErr => { + InstructionResult::FatalExternalError => { self.instruction_result = InstructionResult::FatalExternalError; } _ => { @@ -272,17 +281,6 @@ impl Interpreter { } } -/// Enum representing the next action to be taken based on the interpreter's result. -pub enum NextAction { - /// Indicates that the operation should continue. - Continue, - /// Indicates that the operation should revert. - Revert, - /// Indicates an external error occurred during the operation. - ExternalErr, - /// Indicates any other outcome not covered by the above variants. - Other, -} /// Represents the outcome of a create operation in an interpreter. /// /// This struct holds the result of the operation along with an optional address. @@ -295,37 +293,68 @@ pub struct CreateOutcome { } impl CreateOutcome { + /// Constructs a new `CreateOutcome`. + /// + /// # Arguments + /// + /// * `result` - An `InterpreterResult` representing the result of the interpreter operation. + /// * `address` - An optional `Address` associated with the create operation. + /// + /// # Returns + /// + /// A new `CreateOutcome` instance. pub fn new(result: InterpreterResult, address: Option
) -> Self { Self { result, address } } - /// Retrieves the data buffer based on the interpreter's result. + /// Determines if the outcome of the create operation was a revert. /// - /// If the result is `Revert`, `CallTooDeep`, or `OutOfFund`, it returns the associated output. - /// Otherwise, it returns an empty `Bytes` object. - pub fn data_buffer(&self) -> Bytes { - match self.result.result { - InstructionResult::Revert - | InstructionResult::CallTooDeep - | InstructionResult::OutOfFund => self.result.output.clone(), - _ => Bytes::new(), - } + /// This method checks the `result` field of the `InterpreterResult` contained + /// within `CreateOutcome` to determine if the operation has resulted in a revert. + /// # Returns + /// + /// * `true` if the create operation resulted in a revert. + /// * `false` otherwise. + + pub fn is_revert(&self) -> bool { + matches!(self.result.result, return_revert!()) } - /// Determines the next action to take based on the interpreter's result. + /// Retrieves a reference to the `InstructionResult` from the `InterpreterResult`. /// - /// Returns a variant of `NextAction` enum, which indicates whether to continue, - /// revert, handle an external error, or take another action. - pub fn next_action(&self) -> NextAction { - match self.result.result { - InstructionResult::Continue - | InstructionResult::Stop - | InstructionResult::Return - | InstructionResult::SelfDestruct => NextAction::Continue, - InstructionResult::Revert - | InstructionResult::CallTooDeep - | InstructionResult::OutOfFund => NextAction::Revert, - InstructionResult::FatalExternalError => NextAction::ExternalErr, - _ => NextAction::Other, - } + /// This method provides access to the `InstructionResult` which represents the + /// outcome of the instruction execution. It encapsulates the result information + /// such as whether the instruction was executed successfully, resulted in a revert, + /// or encountered a fatal error. + /// + /// # Returns + /// + /// A reference to the `InstructionResult`. + pub fn instruction_result(&self) -> &InstructionResult { + &self.result.result + } + + /// Retrieves a reference to the output bytes from the `InterpreterResult`. + /// + /// This method returns the output of the interpreted operation. The output is + /// typically used when the operation successfully completes and returns data. + /// + /// # Returns + /// + /// A reference to the output `Bytes`. + pub fn output(&self) -> &Bytes { + &self.result.output + } + + /// Retrieves a reference to the `Gas` details from the `InterpreterResult`. + /// + /// This method provides access to the gas details of the operation, which includes + /// information about gas used, remaining, and refunded. It is essential for + /// understanding the gas consumption of the operation. + /// + /// # Returns + /// + /// A reference to the `Gas` details. + pub fn gas(&self) -> &Gas { + &self.result.gas } } From 154d88f89192f0aed2a28d451d9e13526aa822eb Mon Sep 17 00:00:00 2001 From: Loocapro Date: Mon, 15 Jan 2024 15:59:42 +0100 Subject: [PATCH 50/52] fix: removed unused is_revert method --- crates/interpreter/src/interpreter.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index fce2fd38d7..943c64d3c4 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -306,18 +306,6 @@ impl CreateOutcome { pub fn new(result: InterpreterResult, address: Option
) -> Self { Self { result, address } } - /// Determines if the outcome of the create operation was a revert. - /// - /// This method checks the `result` field of the `InterpreterResult` contained - /// within `CreateOutcome` to determine if the operation has resulted in a revert. - /// # Returns - /// - /// * `true` if the create operation resulted in a revert. - /// * `false` otherwise. - - pub fn is_revert(&self) -> bool { - matches!(self.result.result, return_revert!()) - } /// Retrieves a reference to the `InstructionResult` from the `InterpreterResult`. /// From a6ad99a88e7129b00e1910e0ab835b8983cbd36d Mon Sep 17 00:00:00 2001 From: Luca Provini Date: Tue, 16 Jan 2024 16:59:09 +0100 Subject: [PATCH 51/52] review: adjusted comments, moved create_outcome to its own file --- crates/interpreter/src/create_outcome.rs | 68 +++++++++++++++++++ crates/interpreter/src/interpreter.rs | 84 +++--------------------- crates/interpreter/src/lib.rs | 6 +- 3 files changed, 81 insertions(+), 77 deletions(-) create mode 100644 crates/interpreter/src/create_outcome.rs diff --git a/crates/interpreter/src/create_outcome.rs b/crates/interpreter/src/create_outcome.rs new file mode 100644 index 0000000000..41c456b173 --- /dev/null +++ b/crates/interpreter/src/create_outcome.rs @@ -0,0 +1,68 @@ +use crate::{Gas, InstructionResult, InterpreterResult}; +use revm_primitives::{Address, Bytes}; + +/// Represents the outcome of a create operation in an interpreter. +/// +/// This struct holds the result of the operation along with an optional address. +/// It provides methods to determine the next action based on the result of the operation. +pub struct CreateOutcome { + // The result of the interpreter operation. + pub result: InterpreterResult, + // An optional address associated with the create operation. + pub address: Option
, +} + +impl CreateOutcome { + /// Constructs a new `CreateOutcome`. + /// + /// # Arguments + /// + /// * `result` - An `InterpreterResult` representing the result of the interpreter operation. + /// * `address` - An optional `Address` associated with the create operation. + /// + /// # Returns + /// + /// A new `CreateOutcome` instance. + pub fn new(result: InterpreterResult, address: Option
) -> Self { + Self { result, address } + } + + /// Retrieves a reference to the `InstructionResult` from the `InterpreterResult`. + /// + /// This method provides access to the `InstructionResult` which represents the + /// outcome of the instruction execution. It encapsulates the result information + /// such as whether the instruction was executed successfully, resulted in a revert, + /// or encountered a fatal error. + /// + /// # Returns + /// + /// A reference to the `InstructionResult`. + pub fn instruction_result(&self) -> &InstructionResult { + &self.result.result + } + + /// Retrieves a reference to the output bytes from the `InterpreterResult`. + /// + /// This method returns the output of the interpreted operation. The output is + /// typically used when the operation successfully completes and returns data. + /// + /// # Returns + /// + /// A reference to the output `Bytes`. + pub fn output(&self) -> &Bytes { + &self.result.output + } + + /// Retrieves a reference to the `Gas` details from the `InterpreterResult`. + /// + /// This method provides access to the gas details of the operation, which includes + /// information about gas used, remaining, and refunded. It is essential for + /// understanding the gas consumption of the operation. + /// + /// # Returns + /// + /// A reference to the `Gas` details. + pub fn gas(&self) -> &Gas { + &self.result.gas + } +} diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 943c64d3c4..25207fa459 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -9,13 +9,13 @@ pub use shared_memory::{next_multiple_of_32, SharedMemory}; pub use stack::{Stack, STACK_LIMIT}; use crate::{ - primitives::Bytes, push, push_b256, return_ok, return_revert, CallInputs, CreateInputs, Gas, - Host, InstructionResult, + primitives::Bytes, push, push_b256, return_ok, return_revert, CallInputs, CreateInputs, + CreateOutcome, Gas, Host, InstructionResult, }; use alloc::boxed::Box; use core::cmp::min; use core::ops::Range; -use revm_primitives::{Address, U256}; +use revm_primitives::U256; pub use self::shared_memory::EMPTY_SHARED_MEMORY; @@ -105,17 +105,17 @@ impl Interpreter { /// # Behavior /// /// The function updates the `return_data_buffer` with the data from `create_outcome`. - /// Depending on the `next_action` indicated by `create_outcome`, it performs one of the following: + /// Depending on the `InstructionResult` indicated by `create_outcome`, it performs one of the following: /// - /// - `NextAction::Continue`: Pushes the address from `create_outcome` to the stack, updates gas costs, and records any gas refunds. - /// - `NextAction::Revert`: Pushes `U256::ZERO` to the stack and updates gas costs. - /// - `NextAction::ExternalErr`: Sets the `instruction_result` to `InstructionResult::FatalExternalError`. - /// - Other actions: Pushes `U256::ZERO` to the stack. + /// - `Ok`: Pushes the address from `create_outcome` to the stack, updates gas costs, and records any gas refunds. + /// - `Revert`: Pushes `U256::ZERO` to the stack and updates gas costs. + /// - `FatalExternalError`: Sets the `instruction_result` to `InstructionResult::FatalExternalError`. + /// - `Default`: Pushes `U256::ZERO` to the stack. /// /// # Side Effects /// /// - Updates `return_data_buffer` with the data from `create_outcome`. - /// - Modifies the stack by pushing values depending on the `next_action`. + /// - Modifies the stack by pushing values depending on the `InstructionResult`. /// - Updates gas costs and records refunds in the interpreter's `gas` field. /// - May alter `instruction_result` in case of external errors. pub fn insert_create_outcome(&mut self, create_outcome: CreateOutcome) { @@ -280,69 +280,3 @@ impl Interpreter { } } } - -/// Represents the outcome of a create operation in an interpreter. -/// -/// This struct holds the result of the operation along with an optional address. -/// It provides methods to determine the next action based on the result of the operation. -pub struct CreateOutcome { - // The result of the interpreter operation. - pub result: InterpreterResult, - // An optional address associated with the create operation. - pub address: Option
, -} - -impl CreateOutcome { - /// Constructs a new `CreateOutcome`. - /// - /// # Arguments - /// - /// * `result` - An `InterpreterResult` representing the result of the interpreter operation. - /// * `address` - An optional `Address` associated with the create operation. - /// - /// # Returns - /// - /// A new `CreateOutcome` instance. - pub fn new(result: InterpreterResult, address: Option
) -> Self { - Self { result, address } - } - - /// Retrieves a reference to the `InstructionResult` from the `InterpreterResult`. - /// - /// This method provides access to the `InstructionResult` which represents the - /// outcome of the instruction execution. It encapsulates the result information - /// such as whether the instruction was executed successfully, resulted in a revert, - /// or encountered a fatal error. - /// - /// # Returns - /// - /// A reference to the `InstructionResult`. - pub fn instruction_result(&self) -> &InstructionResult { - &self.result.result - } - - /// Retrieves a reference to the output bytes from the `InterpreterResult`. - /// - /// This method returns the output of the interpreted operation. The output is - /// typically used when the operation successfully completes and returns data. - /// - /// # Returns - /// - /// A reference to the output `Bytes`. - pub fn output(&self) -> &Bytes { - &self.result.output - } - - /// Retrieves a reference to the `Gas` details from the `InterpreterResult`. - /// - /// This method provides access to the gas details of the operation, which includes - /// information about gas used, remaining, and refunded. It is essential for - /// understanding the gas consumption of the operation. - /// - /// # Returns - /// - /// A reference to the `Gas` details. - pub fn gas(&self) -> &Gas { - &self.result.gas - } -} diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index 4e721a844c..e63cbb3a1b 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -12,6 +12,7 @@ extern crate alloc; #[macro_use] mod macros; +mod create_outcome; pub mod gas; mod host; mod inner_models; @@ -20,14 +21,15 @@ pub mod instructions; mod interpreter; // Reexport primary types. +pub use create_outcome::CreateOutcome; pub use gas::Gas; pub use host::{DummyHost, Host}; pub use inner_models::*; pub use instruction_result::*; pub use instructions::{opcode, Instruction, OpCode, OPCODE_JUMPMAP}; pub use interpreter::{ - analysis, next_multiple_of_32, BytecodeLocked, Contract, CreateOutcome, Interpreter, - InterpreterAction, InterpreterResult, SharedMemory, Stack, EMPTY_SHARED_MEMORY, STACK_LIMIT, + analysis, next_multiple_of_32, BytecodeLocked, Contract, Interpreter, InterpreterAction, + InterpreterResult, SharedMemory, Stack, EMPTY_SHARED_MEMORY, STACK_LIMIT, }; pub use primitives::{MAX_CODE_SIZE, MAX_INITCODE_SIZE}; From 1fe5e5bd512dd5dc53c21c729948494700ec12e5 Mon Sep 17 00:00:00 2001 From: Luca Provini Date: Wed, 17 Jan 2024 12:22:26 +0100 Subject: [PATCH 52/52] fix: no std check --- crates/interpreter/src/interpreter.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 25207fa459..da6433b325 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -8,6 +8,7 @@ pub use contract::Contract; pub use shared_memory::{next_multiple_of_32, SharedMemory}; pub use stack::{Stack, STACK_LIMIT}; +use crate::alloc::borrow::ToOwned; use crate::{ primitives::Bytes, push, push_b256, return_ok, return_revert, CallInputs, CreateInputs, CreateOutcome, Gas, Host, InstructionResult,