├── src ├── db │ ├── sync │ │ ├── child.rs │ │ ├── error.rs │ │ ├── mod.rs │ │ ├── builder.rs │ │ └── cache.rs │ ├── mod.rs │ └── alloy.rs ├── lifecycle │ ├── mod.rs │ ├── receipt.rs │ ├── postflight.rs │ └── output.rs ├── fill │ ├── mod.rs │ ├── noop.rs │ ├── fillers.rs │ └── traits.rs ├── driver │ ├── mod.rs │ ├── bundle.rs │ ├── block.rs │ └── chain.rs ├── evm │ ├── mod.rs │ ├── struct.rs │ ├── need_cfg.rs │ ├── has_block.rs │ ├── errored.rs │ ├── builder.rs │ ├── factory.rs │ ├── need_block.rs │ ├── need_tx.rs │ └── states.rs ├── helpers.rs ├── inspectors │ ├── with_output.rs │ ├── mod.rs │ ├── tracing.rs │ ├── timeout.rs │ ├── set.rs │ ├── layer.rs │ └── spanning.rs ├── macros.rs ├── system │ ├── eip4895.rs │ ├── fill.rs │ ├── eip2935.rs │ ├── eip4788.rs │ ├── eip7251.rs │ ├── eip7002.rs │ └── mod.rs ├── journal │ ├── mod.rs │ ├── update.rs │ └── index.rs ├── ext.rs └── test_utils.rs ├── .gitignore ├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ └── rust-ci.yml ├── clippy.toml ├── assets └── states.png ├── rustfmt.toml ├── LICENSE-MIT ├── CONTRIBUTING.md ├── examples ├── basic_transact.rs └── fork_ref_transact.rs ├── README.md ├── Cargo.toml └── LICENSE-APACHE /src/db/sync/child.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | @prestwich @evalir -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | too-large-for-stack = 128 2 | -------------------------------------------------------------------------------- /assets/states.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/init4tech/trevm/HEAD/assets/states.png -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | use_field_init_shorthand = true 3 | use_small_heuristics = "Max" 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" -------------------------------------------------------------------------------- /src/lifecycle/mod.rs: -------------------------------------------------------------------------------- 1 | mod output; 2 | pub use output::BlockOutput; 3 | 4 | mod postflight; 5 | pub use postflight::{PostTx, PostflightResult}; 6 | 7 | mod receipt; 8 | pub use receipt::ethereum_receipt; 9 | -------------------------------------------------------------------------------- /src/fill/mod.rs: -------------------------------------------------------------------------------- 1 | mod alloy; 2 | 3 | /// Provided fillers that adjust the [`Cfg`], [`Block`] or [`Tx`] environment. 4 | pub mod fillers; 5 | 6 | mod noop; 7 | pub use noop::{NoopBlock, NoopCfg}; 8 | 9 | mod traits; 10 | pub use traits::{Block, Cfg, Tx}; 11 | -------------------------------------------------------------------------------- /.github/workflows/rust-ci.yml: -------------------------------------------------------------------------------- 1 | name: Rust CI 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [main] 7 | pull_request: 8 | env: 9 | CARGO_TERM_COLOR: always 10 | 11 | jobs: 12 | rust-library-base: 13 | uses: init4tech/actions/.github/workflows/rust-library-base.yml@main 14 | -------------------------------------------------------------------------------- /src/driver/mod.rs: -------------------------------------------------------------------------------- 1 | mod alloy; 2 | pub use alloy::{BundleError, BundleProcessor}; 3 | 4 | mod block; 5 | pub use block::{BlockDriver, DriveBlockResult, RunTxResult}; 6 | 7 | mod bundle; 8 | pub use bundle::{BundleDriver, DriveBundleResult}; 9 | 10 | mod chain; 11 | pub use chain::{ChainDriver, DriveChainResult}; 12 | -------------------------------------------------------------------------------- /src/evm/mod.rs: -------------------------------------------------------------------------------- 1 | mod builder; 2 | pub use builder::{TrevmBuilder, TrevmBuilderError}; 3 | 4 | mod factory; 5 | pub use factory::EvmFactory; 6 | 7 | pub(crate) mod states; 8 | 9 | mod r#struct; 10 | pub use r#struct::Trevm; 11 | 12 | // Impl blocks for Evm states 13 | mod all_states; 14 | mod errored; 15 | mod has_block; 16 | mod has_cfg; 17 | mod has_tx; 18 | mod need_block; 19 | mod need_cfg; 20 | mod need_tx; 21 | mod ready; 22 | mod transacted; 23 | -------------------------------------------------------------------------------- /src/fill/noop.rs: -------------------------------------------------------------------------------- 1 | use revm::context::{BlockEnv, CfgEnv}; 2 | 3 | use crate::{Block, Cfg}; 4 | 5 | /// A no-op block filler. 6 | #[derive(Debug, Clone, Copy)] 7 | pub struct NoopBlock; 8 | 9 | impl Block for NoopBlock { 10 | fn fill_block_env(&self, _: &mut BlockEnv) {} 11 | } 12 | 13 | /// A no-op configuration filler. 14 | #[derive(Debug, Clone, Copy)] 15 | pub struct NoopCfg; 16 | 17 | impl Cfg for NoopCfg { 18 | fn fill_cfg_env(&self, _env: &mut CfgEnv) {} 19 | } 20 | -------------------------------------------------------------------------------- /src/lifecycle/receipt.rs: -------------------------------------------------------------------------------- 1 | use alloy::consensus::{Receipt, ReceiptWithBloom}; 2 | use revm::context::result::ExecutionResult; 3 | 4 | /// Create an Ethereum [`ReceiptEnvelope`] from an execution result. 5 | /// 6 | /// [`ReceiptEnvelope`]: alloy::consensus::ReceiptEnvelope 7 | pub fn ethereum_receipt(result: ExecutionResult, prior_gas_used: u64) -> ReceiptWithBloom { 8 | let cumulative_gas_used = prior_gas_used.saturating_add(result.gas_used()); 9 | 10 | Receipt { status: result.is_success().into(), cumulative_gas_used, logs: result.into_logs() } 11 | .into() 12 | } 13 | -------------------------------------------------------------------------------- /src/db/mod.rs: -------------------------------------------------------------------------------- 1 | /// Concurrent version of [`revm::database::State`] 2 | #[cfg(feature = "concurrent-db")] 3 | pub mod sync; 4 | 5 | /// Database abstraction traits. 6 | mod traits; 7 | pub use traits::{ArcUpgradeError, CachingDb, DbConnect, StateAcc, TryCachingDb, TryStateAcc}; 8 | 9 | /// Cache-on-write database. A memory cache that caches only on write, not on 10 | /// read. Intended to wrap some other caching database. 11 | pub mod cow; 12 | 13 | #[cfg(feature = "alloy-db")] 14 | /// Alloy-powered revm Database implementation that fetches data over the network. 15 | pub mod alloy; 16 | -------------------------------------------------------------------------------- /src/db/sync/error.rs: -------------------------------------------------------------------------------- 1 | use crate::db::ArcUpgradeError; 2 | 3 | /// Errors that can occur when working with a concurrent state. 4 | #[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)] 5 | pub enum ConcurrentStateError { 6 | /// Failed to upgrade the arc. 7 | #[error("{0}")] 8 | Arc(#[from] ArcUpgradeError), 9 | 10 | /// This DB is not the parent of the child. 11 | #[error("Child belongs to a different parent")] 12 | NotParent, 13 | } 14 | 15 | impl ConcurrentStateError { 16 | /// Create a new error for when the DB is not the parent of the child. 17 | pub const fn not_parent() -> Self { 18 | Self::NotParent 19 | } 20 | 21 | /// Create a new error for when the arc upgrade fails. 22 | pub const fn not_unique() -> Self { 23 | Self::Arc(ArcUpgradeError::NotUnique) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/driver/bundle.rs: -------------------------------------------------------------------------------- 1 | use crate::{helpers::Ctx, EvmBundleDriverErrored, EvmNeedsTx}; 2 | use revm::{ 3 | context::result::EVMError, inspector::NoOpInspector, Database, DatabaseCommit, Inspector, 4 | }; 5 | 6 | /// The result of driving a bundle to completion. 7 | pub type DriveBundleResult = 8 | Result, EvmBundleDriverErrored>; 9 | 10 | /// Driver for a bundle of transactions. This trait allows a type to specify the 11 | /// entire lifecycle of a bundle, simulating the entire list of transactions. 12 | pub trait BundleDriver 13 | where 14 | Db: Database + DatabaseCommit, 15 | Insp: Inspector>, 16 | { 17 | /// An error type for this driver. 18 | type Error: core::error::Error + From>; 19 | 20 | /// Run the transactions contained in the bundle. 21 | fn run_bundle(&mut self, trevm: EvmNeedsTx) -> DriveBundleResult; 22 | 23 | /// Run post 24 | fn post_bundle(&mut self, trevm: &EvmNeedsTx) -> Result<(), Self::Error>; 25 | } 26 | -------------------------------------------------------------------------------- /src/db/sync/mod.rs: -------------------------------------------------------------------------------- 1 | mod builder; 2 | pub use builder::ConcurrentStateBuilder; 3 | 4 | mod cache; 5 | pub use cache::ConcurrentCacheState; 6 | 7 | mod error; 8 | pub use error::ConcurrentStateError; 9 | 10 | mod state; 11 | pub use state::{Child, ConcurrentState, ConcurrentStateInfo}; 12 | 13 | use crate::db::StateAcc; 14 | use alloy::primitives::B256; 15 | use revm::{ 16 | database::{states::bundle_state::BundleRetention, BundleState}, 17 | DatabaseRef, 18 | }; 19 | use std::collections::BTreeMap; 20 | 21 | impl StateAcc for ConcurrentState { 22 | fn set_state_clear_flag(&mut self, flag: bool) { 23 | Self::set_state_clear_flag(self, flag) 24 | } 25 | 26 | fn merge_transitions(&mut self, retention: BundleRetention) { 27 | Self::merge_transitions(self, retention) 28 | } 29 | 30 | fn take_bundle(&mut self) -> BundleState { 31 | Self::take_bundle(self) 32 | } 33 | 34 | fn set_block_hashes(&mut self, block_hashes: &BTreeMap) { 35 | self.info.block_hashes.write().unwrap().extend(block_hashes) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/helpers.rs: -------------------------------------------------------------------------------- 1 | use revm::{ 2 | context::{BlockEnv, CfgEnv, TxEnv}, 3 | context_interface::context::ContextError, 4 | handler::{instructions::EthInstructions, EthFrame, EthPrecompiles}, 5 | inspector::NoOpInspector, 6 | interpreter::{interpreter::EthInterpreter, InstructionContext, InterpreterTypes}, 7 | Context, Database, Journal, 8 | }; 9 | 10 | /// [`revm::Context`] with default env types and adjustable DB 11 | pub type Ctx, C = ()> = Context; 12 | 13 | /// EVM with default env types and adjustable DB. 14 | pub type Evm, Prec = EthPrecompiles> = 15 | revm::context::Evm, Insp, Inst, Prec, EthFrame>; 16 | 17 | /// Handler table for EVM opcodes. 18 | pub type Instructions = EthInstructions>; 19 | 20 | /// The handler type for an EVM opcode. 21 | pub type Instruction = revm::interpreter::Instruction>; 22 | 23 | /// An [`Instruction`] that sets a [`ContextError`] in the [`Ctx`] whenever it 24 | /// is executed. 25 | pub fn forbidden(ctx: InstructionContext<'_, Ctx, Int>) { 26 | ctx.host.error = Err(ContextError::Custom("forbidden opcode".to_string())); 27 | } 28 | -------------------------------------------------------------------------------- /src/evm/struct.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | helpers::{Ctx, Evm}, 3 | EvmNeedsCfg, NeedsCfg, 4 | }; 5 | use core::fmt; 6 | use revm::{inspector::NoOpInspector, Database, Inspector}; 7 | 8 | /// Trevm provides a type-safe interface to the EVM, using the typestate pattern. 9 | /// 10 | /// See the [crate-level documentation](crate) for more information. 11 | pub struct Trevm 12 | where 13 | Db: Database, 14 | Insp: Inspector>, 15 | { 16 | pub(crate) inner: Box>, 17 | pub(crate) state: TrevmState, 18 | } 19 | 20 | impl fmt::Debug for Trevm 21 | where 22 | Db: Database, 23 | Insp: Inspector>, 24 | { 25 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 26 | f.debug_struct("Trevm").finish_non_exhaustive() 27 | } 28 | } 29 | 30 | impl AsRef> for Trevm 31 | where 32 | Db: Database, 33 | Insp: Inspector>, 34 | { 35 | fn as_ref(&self) -> &Evm { 36 | &self.inner 37 | } 38 | } 39 | 40 | impl From> for EvmNeedsCfg 41 | where 42 | Db: Database, 43 | Insp: Inspector>, 44 | { 45 | fn from(inner: Evm) -> Self { 46 | Self { inner: Box::new(inner), state: NeedsCfg::new() } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/driver/block.rs: -------------------------------------------------------------------------------- 1 | use crate::{helpers::Ctx, Block, EvmBlockDriverErrored, EvmNeedsBlock, EvmNeedsTx}; 2 | use revm::{ 3 | context::result::EVMError, inspector::NoOpInspector, Database, DatabaseCommit, Inspector, 4 | }; 5 | 6 | /// The result of running transactions for a block driver. 7 | pub type RunTxResult = 8 | Result, EvmBlockDriverErrored>; 9 | 10 | /// The result of driving a block to completion. 11 | pub type DriveBlockResult = 12 | Result, EvmBlockDriverErrored>; 13 | 14 | /// Driver for a single trevm block. This trait allows a type to specify the 15 | /// entire lifecycle of a trevm block, from opening the block to driving the 16 | /// trevm to completion. 17 | pub trait BlockDriver 18 | where 19 | Db: Database + DatabaseCommit, 20 | Insp: Inspector>, 21 | { 22 | /// The [`Block`] filler for this driver. 23 | type Block: Block; 24 | 25 | /// An error type for this driver. 26 | type Error: core::error::Error + From>; 27 | 28 | /// Get a reference to the block filler for this driver. 29 | fn block(&self) -> &Self::Block; 30 | 31 | /// Run the transactions for the block. 32 | fn run_txns(&mut self, trevm: EvmNeedsTx) -> RunTxResult; 33 | /// Run post 34 | fn post_block(&mut self, trevm: &EvmNeedsBlock) -> Result<(), Self::Error>; 35 | } 36 | -------------------------------------------------------------------------------- /src/inspectors/with_output.rs: -------------------------------------------------------------------------------- 1 | use crate::{helpers::Ctx, Trevm}; 2 | use revm::{inspector::CountInspector, Database}; 3 | 4 | /// An inspector that can produce an output value after EVM execution. 5 | pub trait InspectorWithOutput: revm::inspector::Inspector { 6 | /// The type of output produced by the inspector. 7 | type Output; 8 | 9 | /// Returns `true` if the inspector has a valid output value. 10 | fn has_output(&self) -> bool; 11 | 12 | /// Reset the output value to the default state, discarding any current 13 | /// value. 14 | fn reset_output(&mut self) { 15 | self.take_output(); 16 | } 17 | 18 | /// Take the output value from the inspector, resetting it to the default 19 | /// state. 20 | fn take_output(&mut self) -> Self::Output; 21 | } 22 | 23 | impl Trevm 24 | where 25 | Insp: InspectorWithOutput>, 26 | { 27 | /// Take the output value from the inspector. 28 | /// 29 | /// This will also reset the output value in the inspector. 30 | pub fn take_output(&mut self) -> Insp::Output { 31 | self.inspector_mut().take_output() 32 | } 33 | } 34 | 35 | impl InspectorWithOutput for CountInspector { 36 | type Output = Self; 37 | 38 | fn has_output(&self) -> bool { 39 | self.total_opcodes() > 0 40 | } 41 | 42 | fn reset_output(&mut self) { 43 | self.clear(); 44 | } 45 | 46 | fn take_output(&mut self) -> Self::Output { 47 | std::mem::take(self) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Trevm 2 | 3 | :balloon: Thanks for your help improving the project! We are so happy to have 4 | you! 5 | 6 | ## Conduct 7 | 8 | This repo adheres to the [Rust Code of Conduct][coc]. This describes 9 | the _minimum_ behavior expected from all contributors. Failure to maintain civil 10 | behavior will result in a ban. 11 | 12 | [coc]: https://www.rust-lang.org/policies/code-of-conduct 13 | 14 | ## Pull Requests 15 | 16 | Before making a large change, it is usually a good idea to first open an issue 17 | describing the change to solicit feedback and guidance. This will increase the 18 | likelihood of the PR getting merged. 19 | 20 | When opening a PR **please select the "Allow Edits From Maintainers" option**. 21 | We maintain code quality and style standards, and require commit signing. This 22 | option allows us to make small changes to your PR to bring it in line with 23 | these standards. It helps us get your PR in faster, and with less work from you. 24 | 25 | ## Development Basics 26 | 27 | Before submitting a PR we recommend you run the following commands to ensure 28 | your code is properly formatted and passes all tests: 29 | 30 | ```sh 31 | cargo +nightly fmt --all 32 | cargo clippy --all-features 33 | cargo test --all-features 34 | cargo test --no-default-features 35 | ``` 36 | 37 | ### Contributions Related to Spelling, Grammar, and other trivial changes 38 | 39 | At this time, we will not be accepting contributions that only fix spelling or 40 | grammatical errors in documentation, code or 41 | elsewhere. 42 | -------------------------------------------------------------------------------- /src/inspectors/mod.rs: -------------------------------------------------------------------------------- 1 | mod layer; 2 | pub use layer::Layered; 3 | 4 | mod timeout; 5 | pub use timeout::TimeLimit; 6 | 7 | #[cfg(feature = "tracing-inspectors")] 8 | mod tracing; 9 | #[cfg(feature = "tracing-inspectors")] 10 | pub use tracing::TracingInspectorOutput; 11 | 12 | mod set; 13 | pub use set::InspectorSet; 14 | 15 | mod spanning; 16 | pub use spanning::SpanningInspector; 17 | 18 | mod with_output; 19 | pub use with_output::InspectorWithOutput; 20 | 21 | #[cfg(test)] 22 | mod test { 23 | use super::*; 24 | use crate::{test_utils::TestInspector, NoopBlock, NoopCfg}; 25 | use revm::{database::InMemoryDB, inspector::InspectorEvmTr, primitives::B256}; 26 | use std::time::Duration; 27 | 28 | #[test] 29 | fn test_timeout() { 30 | let inspector = 31 | Layered::new(TimeLimit::new(Duration::from_micros(10)), SpanningInspector::at_info()) 32 | .wrap_around(TestInspector::default()); 33 | 34 | let mut trevm = crate::TrevmBuilder::new() 35 | .with_db(InMemoryDB::default()) 36 | .with_insp(inspector) 37 | .build_trevm() 38 | .fill_cfg(&NoopCfg) 39 | .fill_block(&NoopBlock); 40 | 41 | let err = trevm.apply_eip4788(B256::repeat_byte(0xaa)).unwrap_err(); 42 | assert!(matches!(err, revm::context::result::EVMError::Custom(_))); 43 | assert!(format!("{err}").contains("timeout during evm execution")); 44 | 45 | assert!(trevm.inner_mut_unchecked().inspector().outer().outer().has_elapsed()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/driver/chain.rs: -------------------------------------------------------------------------------- 1 | use crate::{helpers::Ctx, BlockDriver, EvmChainDriverErrored, EvmNeedsBlock}; 2 | use revm::{ 3 | context::result::EVMError, inspector::NoOpInspector, primitives::hardfork::SpecId, Database, 4 | DatabaseCommit, Inspector, 5 | }; 6 | 7 | /// The result of driving a chain to completion. 8 | pub type DriveChainResult = 9 | Result, EvmChainDriverErrored>; 10 | 11 | /// Driver for a chain of blocks. 12 | pub trait ChainDriver 13 | where 14 | Db: Database + DatabaseCommit, 15 | Insp: Inspector>, 16 | { 17 | /// The block driver for this chain. 18 | type BlockDriver: BlockDriver; 19 | 20 | /// An error type for this driver. 21 | type Error: core::error::Error 22 | + From> 23 | + From<>::Error>; 24 | 25 | /// Get the spec id for a block. 26 | fn spec_id_for(&self, block: &>::Block) -> SpecId; 27 | 28 | /// Get the blocks in this chain. The blocks should be in order, and this 29 | /// function MUST NOT return an empty slice. 30 | fn blocks(&mut self) -> &mut [Self::BlockDriver]; 31 | 32 | /// Checks that run between blocks, e.g. 1559 base fee calculation, 33 | /// or parent-child relationships. 34 | /// 35 | /// The `idx` parameter is the index of the block in the chain. 36 | fn interblock( 37 | &mut self, 38 | trevm: &EvmNeedsBlock, 39 | idx: usize, 40 | ) -> Result<(), Self::Error>; 41 | } 42 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Unwraps a Result, returning the value if successful, or returning an errored `Trevm` if not. 2 | #[macro_export] 3 | #[deprecated = "Please use `trevm_tri!` instead"] 4 | macro_rules! unwrap_or_trevm_err { 5 | ($e:expr, $trevm:expr) => { 6 | match $e { 7 | Ok(val) => val, 8 | Err(e) => return Err($trevm.errored(e.into())), 9 | } 10 | }; 11 | } 12 | 13 | /// Unwraps a Result, returning the value if successful, or returning an errored `Trevm` if not. 14 | #[macro_export] 15 | macro_rules! trevm_try { 16 | ($e:expr, $trevm:expr) => { 17 | match $e { 18 | Ok(val) => val, 19 | Err(e) => return Err($trevm.errored(e.into())), 20 | } 21 | }; 22 | } 23 | 24 | /// Executes a condition, returning an errored `Trevm` if not successful. 25 | #[macro_export] 26 | macro_rules! trevm_ensure { 27 | ($cond:expr, $trevm:expr, $err:expr) => { 28 | if !$cond { 29 | trevm_bail!($trevm, $err); 30 | } 31 | }; 32 | } 33 | 34 | /// Returns an errored `Trevm` with the provided error. 35 | #[macro_export] 36 | macro_rules! trevm_bail { 37 | ($trevm:expr, $err:expr) => { 38 | return Err($trevm.errored($err)) 39 | }; 40 | } 41 | 42 | /// Macro for gas estimation binary search loop. 43 | #[cfg(feature = "estimate_gas")] 44 | macro_rules! estimate_and_adjust { 45 | ($best:ident, $est:ident, $trevm:ident, $gas_limit:ident, $range:ident) => { 46 | ::tracing::trace!( 47 | best = $best.gas_used(), 48 | max = %$range.max(), 49 | min = %$range.min(), 50 | needle = $gas_limit, 51 | "running gas estimation" 52 | ); 53 | ($est, $trevm) = $trevm.run_estimate(&$gas_limit.into())?; 54 | if $est.is_success() { 55 | $best = $est.clone(); 56 | } 57 | if let Err(e) = $est.adjust_binary_search_range(&mut $range) { 58 | ::tracing::trace!( 59 | %e, 60 | "error adjusting binary search range" 61 | ); 62 | return Ok((e, $trevm)); 63 | } 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /src/inspectors/tracing.rs: -------------------------------------------------------------------------------- 1 | use crate::{helpers::Ctx, inspectors::InspectorWithOutput}; 2 | use alloy::rpc::types::trace::geth::FourByteFrame; 3 | use revm::Database; 4 | use revm_inspectors::tracing::{ 5 | CallTraceArena, FourByteInspector, GethTraceBuilder, ParityTraceBuilder, TracingInspector, 6 | }; 7 | 8 | /// Output produced by the [`TracingInspector`]. 9 | #[derive(Clone, Debug)] 10 | pub struct TracingInspectorOutput { 11 | traces: CallTraceArena, 12 | } 13 | 14 | impl TracingInspectorOutput { 15 | /// Creates a new output instance. 16 | pub const fn new(traces: CallTraceArena) -> Self { 17 | Self { traces } 18 | } 19 | 20 | /// Returns a reference to the traces produced by the inspector. 21 | pub const fn traces(&self) -> &CallTraceArena { 22 | &self.traces 23 | } 24 | 25 | /// Consumes the output and produces a Parity trace builder. 26 | pub fn into_parity_builder(self) -> ParityTraceBuilder { 27 | // NB: the second arguments are actually currently unused. This is 28 | // weird. 29 | ParityTraceBuilder::new(self.traces.into_nodes(), None, Default::default()) 30 | } 31 | 32 | /// Consumes the output and produces a Geth trace builder. 33 | pub fn into_geth_builder(self) -> GethTraceBuilder<'static> { 34 | GethTraceBuilder::new(self.traces.into_nodes()) 35 | } 36 | } 37 | 38 | impl InspectorWithOutput> for TracingInspector { 39 | type Output = TracingInspectorOutput; 40 | 41 | fn has_output(&self) -> bool { 42 | !self.traces().nodes().is_empty() 43 | } 44 | 45 | fn reset_output(&mut self) { 46 | self.fuse(); 47 | } 48 | 49 | fn take_output(&mut self) -> Self::Output { 50 | let this = self.clone(); 51 | self.fuse(); 52 | TracingInspectorOutput::new(this.into_traces()) 53 | } 54 | } 55 | 56 | impl InspectorWithOutput> for FourByteInspector { 57 | type Output = FourByteFrame; 58 | 59 | fn has_output(&self) -> bool { 60 | !self.inner().is_empty() 61 | } 62 | 63 | fn reset_output(&mut self) { 64 | *self = Self::default(); 65 | } 66 | 67 | fn take_output(&mut self) -> Self::Output { 68 | std::mem::take(self).into() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/system/eip4895.rs: -------------------------------------------------------------------------------- 1 | use crate::{helpers::Ctx, EvmExtUnchecked, EvmNeedsTx}; 2 | use alloy::{ 3 | eips::eip4895::Withdrawal, 4 | primitives::{map::HashMap, U256}, 5 | }; 6 | use revm::{context::result::EVMError, Database, DatabaseCommit, Inspector}; 7 | 8 | impl EvmNeedsTx 9 | where 10 | Db: Database + DatabaseCommit, 11 | Insp: Inspector>, 12 | { 13 | /// Apply the withdrawals to the EVM state. 14 | pub fn apply_withdrawals<'b>( 15 | &mut self, 16 | withdrawals: impl IntoIterator, 17 | ) -> Result<(), EVMError> { 18 | // We need to apply the withdrawals by incrementing the balances of the 19 | // respective accounts, then committing the changes to the database. 20 | let mut changes = HashMap::default(); 21 | 22 | let increments = withdrawals 23 | .into_iter() 24 | .map(|withdrawal| (withdrawal.address, withdrawal.amount as u128)) 25 | .filter(|(_, amount)| *amount != 0); 26 | 27 | for (address, amount) in increments { 28 | let mut acct = 29 | self.inner_mut_unchecked().account(address).map_err(EVMError::Database)?; 30 | acct.info.balance = acct.info.balance.saturating_add(U256::from(amount)); 31 | acct.mark_touch(); 32 | changes.insert(address, acct); 33 | } 34 | 35 | self.commit_unchecked(changes); 36 | Ok(()) 37 | } 38 | } 39 | 40 | #[cfg(test)] 41 | mod test { 42 | use alloy::{ 43 | eips::eip4895::{Withdrawal, GWEI_TO_WEI}, 44 | primitives::{Address, U256}, 45 | }; 46 | 47 | use crate::{NoopBlock, NoopCfg}; 48 | 49 | const USER: Address = Address::with_last_byte(0x42); 50 | 51 | const WITHDRAWALS: &[Withdrawal] = 52 | &[Withdrawal { validator_index: 1, index: 1, address: USER, amount: 100 * GWEI_TO_WEI }]; 53 | 54 | #[test] 55 | fn test_eip4895() { 56 | let mut trevm = crate::test_utils::test_trevm().fill_cfg(&NoopCfg).fill_block(&NoopBlock); 57 | 58 | assert_eq!(trevm.try_read_balance(USER).unwrap(), U256::ZERO); 59 | 60 | trevm.apply_withdrawals(WITHDRAWALS).unwrap(); 61 | 62 | assert_eq!(trevm.try_read_balance(USER).unwrap(), U256::from(100 * GWEI_TO_WEI)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/lifecycle/postflight.rs: -------------------------------------------------------------------------------- 1 | use revm::context::result::ResultAndState; 2 | 3 | /// Control flow for transaction execution. 4 | /// 5 | /// This enum is used to determine whether to apply or discard the state 6 | /// changes after a transaction is executed. 7 | #[derive(Debug, Clone, Copy)] 8 | pub enum PostflightResult { 9 | /// Discard the state changes 10 | Discard(&'static str), 11 | /// Apply the state changes 12 | Apply, 13 | } 14 | 15 | impl From for PostflightResult { 16 | fn from(b: bool) -> Self { 17 | if b { 18 | Self::Apply 19 | } else { 20 | Self::Discard("") 21 | } 22 | } 23 | } 24 | 25 | impl From<&'static str> for PostflightResult { 26 | fn from(s: &'static str) -> Self { 27 | Self::Discard(s) 28 | } 29 | } 30 | 31 | impl From> for PostflightResult { 32 | fn from(s: Option<&'static str>) -> Self { 33 | s.map(Self::Discard).unwrap_or(Self::Apply) 34 | } 35 | } 36 | 37 | impl PostflightResult { 38 | /// Returns `true` if the result is `Discard`. 39 | pub const fn is_discard(&self) -> bool { 40 | matches!(self, Self::Discard(_)) 41 | } 42 | 43 | /// Returns the discard reason if the result is `Discard`. 44 | pub const fn as_discard_reason(&self) -> Option<&'static str> { 45 | match self { 46 | Self::Discard(reason) => Some(reason), 47 | _ => None, 48 | } 49 | } 50 | 51 | /// Returns `true` if the result is `Apply`. 52 | pub const fn is_apply(&self) -> bool { 53 | matches!(self, Self::Apply) 54 | } 55 | } 56 | 57 | /// Discard the transaction if the condition is true, providing a discard 58 | /// reason. 59 | #[macro_export] 60 | macro_rules! discard_if { 61 | ($a:expr, $reason:literal) => { 62 | if $a { 63 | tracing::debug!(reason = $reason, "Discarding transaction"); 64 | return $crate::PostflightResult::Discard($reason); 65 | } 66 | }; 67 | } 68 | 69 | /// Inspect the outcome of a transaction execution, and determine whether to 70 | /// apply or discard the state changes. 71 | pub trait PostTx { 72 | /// Check the result of the EVM execution, potentially mutating self. 73 | fn run_post_tx(&mut self, result: &ResultAndState) -> PostflightResult; 74 | } 75 | 76 | impl PostTx for T 77 | where 78 | T: for<'a> FnMut(&'a ResultAndState) -> O, 79 | O: Into, 80 | { 81 | fn run_post_tx(&mut self, result: &ResultAndState) -> PostflightResult { 82 | self(result).into() 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/journal/mod.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for serializing revm's [`BundleState`] into a canonical format. 2 | //! 3 | //! The [`BundleState`] represents the accumulated state changes of one or more 4 | //! transactions. It is produced when revm is run with a [`State`] and the 5 | //! [`StateBuilder::with_bundle_update`] option. It is useful for aggregating 6 | //! state changes across multiple transactions, so that those changes may be 7 | //! stored in a DB, or otherwise processed in a batch. 8 | //! 9 | //! This module contains utilities for serializing the [`BundleState`] in a 10 | //! canonical format that can be stored to disk or sent over the wire. The core 11 | //! type is the [`BundleStateIndex`], which is a sorted list of [`AcctDiff`]s 12 | //! along with new contract bytecode. 13 | //! 14 | //! Each [`AcctDiff`] represents the state changes for a single account, and 15 | //! contains the pre- and post-state of the account, along with sorted changes 16 | //! to the account's storage. 17 | //! 18 | //! The coding scheme is captured by the [`JournalEncode`] and [`JournalDecode`] 19 | //! traits. These traits provide a very simple binary encoding. The encoding 20 | //! prioritizes legibility and simplicity over compactness. We assume that it 21 | //! will be compressed before being stored or sent. 22 | //! 23 | //! # Usage Example 24 | //! 25 | //! ``` 26 | //! # use revm::database::BundleState; 27 | //! # use trevm::journal::{BundleStateIndex, JournalEncode, JournalDecode, JournalDecodeError}; 28 | //! # fn make_index(bundle_state: &BundleState) -> Result<(), JournalDecodeError> { 29 | //! // Make an index over a bundle state. 30 | //! let index = BundleStateIndex::from(bundle_state); 31 | //! 32 | //! // We can serialize it and deserialize it :) 33 | //! let serialized_index = index.encoded(); 34 | //! let decoded = BundleStateIndex::decode(&mut serialized_index.as_ref())?; 35 | //! assert_eq!(index, decoded); 36 | //! 37 | //! // It contains information about accounts 38 | //! for (addr, diff) in index.state { 39 | //! println!("Balance of {addr} changed by {}", diff.balance_change()); 40 | //! } 41 | //! 42 | //! // And about bytecode 43 | //! let contract_count = index.new_contracts.len(); 44 | //! println!("{contract_count} new contracts deployed!"); 45 | //! # Ok(()) 46 | //! # } 47 | //! ``` 48 | //! 49 | //! [`StateBuilder::with_bundle_update`]: revm::database::StateBuilder::with_bundle_update 50 | //! [`State`]: revm::database::State 51 | //! [`BundleState`]: revm::database::BundleState 52 | //! [reth]: https://github.com/paradigmxyz/reth 53 | 54 | mod coder; 55 | pub use coder::{JournalDecode, JournalDecodeError, JournalEncode}; 56 | 57 | mod index; 58 | pub use index::{AcctDiff, BundleStateIndex, InfoOutcome}; 59 | 60 | mod update; 61 | pub use update::BlockUpdate; 62 | -------------------------------------------------------------------------------- /src/system/fill.rs: -------------------------------------------------------------------------------- 1 | use crate::Tx; 2 | use alloy::primitives::{address, Address, Bytes, U256}; 3 | use revm::context::{TransactionType, TxEnv}; 4 | 5 | /// System smart contract calls as specified in [EIP-4788], [EIP-7002], 6 | /// and [EIP-7251]. 7 | /// 8 | /// By default, these calls are sent from a special system caller address 9 | /// specified in the EIPs, but this can be overridden using the 10 | /// [`SystemTx::new_with_caller`] method. 11 | /// 12 | /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 13 | /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 14 | /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 15 | #[derive(Debug, Clone, PartialEq, Eq)] 16 | pub struct SystemTx { 17 | /// The target address of the system call. 18 | pub target: Address, 19 | /// The input data of the system call. 20 | pub input: Bytes, 21 | /// The caller address of the system call. 22 | pub caller: Address, 23 | } 24 | 25 | /// The system caller as specified in [EIP-4788], [EIP-7002], and [EIP-7251]. 26 | /// 27 | /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 28 | /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 29 | /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 30 | pub const DEFAULT_SYSTEM_CALLER: Address = address!("fffffffffffffffffffffffffffffffffffffffe"); 31 | 32 | impl SystemTx { 33 | /// Instantiate a new [`SystemTx`]. 34 | pub const fn new(target: Address, input: Bytes) -> Self { 35 | Self { caller: DEFAULT_SYSTEM_CALLER, target, input } 36 | } 37 | 38 | /// Instantiate a new [`SystemTx`] with a custom caller address. 39 | pub const fn new_with_caller(target: Address, input: Bytes, caller: Address) -> Self { 40 | Self { caller, target, input } 41 | } 42 | } 43 | 44 | impl Tx for SystemTx { 45 | fn fill_tx_env(&self, tx_env: &mut TxEnv) { 46 | let TxEnv { 47 | tx_type, 48 | caller, 49 | gas_limit, 50 | gas_price, 51 | kind, 52 | value, 53 | data, 54 | nonce, 55 | chain_id, 56 | access_list, 57 | gas_priority_fee, 58 | blob_hashes, 59 | max_fee_per_blob_gas, 60 | authorization_list, 61 | } = tx_env; 62 | 63 | *tx_type = TransactionType::Custom as u8; 64 | 65 | *caller = self.caller; 66 | *gas_limit = 30_000_000; 67 | // 0 gas price 68 | *gas_price = 0; 69 | *kind = self.target.into(); 70 | *value = U256::ZERO; 71 | *data = self.input.clone(); 72 | *nonce = 0; 73 | 74 | // disable chain id checks 75 | chain_id.take(); 76 | // set priority fee to 0 77 | gas_priority_fee.take(); 78 | // disable eip-2930 79 | access_list.0.clear(); 80 | // disable eip-4844 81 | blob_hashes.clear(); 82 | *max_fee_per_blob_gas = 0; 83 | // disable eip-7702 84 | authorization_list.clear(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /examples/basic_transact.rs: -------------------------------------------------------------------------------- 1 | //! Simple TREVM example that demonstrates how to execute a transaction on a contract. 2 | //! It simply loads the contract bytecode and executes a transaction. 3 | 4 | use revm::context::TransactTo; 5 | use trevm::{ 6 | revm::{ 7 | bytecode::Bytecode, 8 | database::InMemoryDB, 9 | inspector::inspectors::TracerEip3155, 10 | primitives::{hex, Address, U256}, 11 | state::AccountInfo, 12 | }, 13 | trevm_aliases, NoopBlock, NoopCfg, TrevmBuilder, Tx, 14 | }; 15 | 16 | /// Foundry's default Counter.sol contract bytecode. 17 | const CONTRACT_BYTECODE: &str = "0x6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fb5c1cb1460415780638381f58a146053578063d09de08a14606d575b600080fd5b6051604c3660046083565b600055565b005b605b60005481565b60405190815260200160405180910390f35b6051600080549080607c83609b565b9190505550565b600060208284031215609457600080fd5b5035919050565b60006001820160ba57634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220091e48831e9eee32d4571d6291233a4fdaaa34b7dced8770f36f5368be825c5264736f6c63430008190033"; 18 | 19 | /// The address of Counter.sol 20 | const CONTRACT_ADDR: Address = Address::with_last_byte(32); 21 | 22 | /// The input data for the Counter.sol program. We're calling setNumber(10) 23 | const PROGRAM_INPUT: &str = 24 | "0x3fb5c1cb000000000000000000000000000000000000000000000000000000000000000a"; 25 | 26 | /// The caller address 27 | const CALLER_ADDR: Address = Address::with_last_byte(1); 28 | 29 | struct SampleTx; 30 | 31 | impl Tx for SampleTx { 32 | fn fill_tx_env(&self, tx_env: &mut revm::context::TxEnv) { 33 | tx_env.caller = CALLER_ADDR; 34 | tx_env.kind = TransactTo::Call(CONTRACT_ADDR); 35 | tx_env.data = hex::decode(PROGRAM_INPUT).unwrap().into(); 36 | } 37 | } 38 | 39 | // Produce aliases for the Trevm type 40 | trevm_aliases!(TracerEip3155, InMemoryDB); 41 | 42 | fn main() { 43 | let mut db = revm::database::InMemoryDB::default(); 44 | 45 | let bytecode = Bytecode::new_raw(hex::decode(CONTRACT_BYTECODE).unwrap().into()); 46 | let acc_info = AccountInfo::new(U256::ZERO, 1, bytecode.hash_slow(), bytecode); 47 | 48 | // insert both the contract code to the contract cache, and the account info to the account cache 49 | db.insert_contract(&mut acc_info.clone()); 50 | db.insert_account_info(CONTRACT_ADDR, acc_info); 51 | 52 | let insp = TracerEip3155::new(Box::new(std::io::stdout())); 53 | 54 | let trevm = TrevmBuilder::new() 55 | .with_db(db) 56 | .with_insp(insp) 57 | .build_trevm() 58 | .fill_cfg(&NoopCfg) 59 | .fill_block(&NoopBlock); 60 | 61 | let account = trevm.read_account_ref(CONTRACT_ADDR).unwrap(); 62 | println!("account: {account:?}"); 63 | 64 | let evm = trevm.fill_tx(&SampleTx).run(); 65 | 66 | match evm { 67 | Ok(res) => { 68 | let res = res.result_and_state(); 69 | println!("Execution result: {res:#?}"); 70 | } 71 | Err(e) => { 72 | println!("Execution error: {e:?}"); 73 | } 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Trevm 2 | 3 | Trevm is a [typestate] API wrapper for [revm]. It provides an ergonomic way to 4 | interact with the revm API, shortcuts for common tasks, and straightforward API 5 | extensibility. Trevm does NOT try to provide low-level EVM management, but 6 | rather to shortcut and simplify common tasks like simulating transactions. 7 | 8 | Trevm is NOT a replacement for revm. It is a wrapper around the revm API. It is 9 | NOT a drop-in upgrade, integrating trevm into your project will require changes 10 | to your code. 11 | 12 | See the [documentation on docs.rs] for information on usage. 13 | 14 | ## Why use Trevm? 15 | 16 | Trevm is for building complex high-level flows on top of the low-level revm 17 | API. It provides a state machine that ensures you can only take actions that are 18 | valid in the current state. This makes it easier to reason about the state of 19 | your EVM instance and ensures you don't make mistakes. 20 | 21 | Trevm is useful for: 22 | 23 | - full node implementations 24 | - block builders 25 | - searchers 26 | - any other transaction simulation usecase 27 | 28 | ## Note on Trevm Versioning 29 | 30 | Trevm generally uses [semantic versioning](https://semver.org/). While pre-1.0, 31 | we also strive to indicate the MAJOR version of revm in the MINOR version of 32 | trevm. For example, trevm `0.19.x` SHOULD BE compatible with revm `19.x.x`. In 33 | general, we will try to maintain compatibility with the latest revm version, 34 | and will not backport trevm fixes to older trevm or revm versions. It is 35 | generally not advised to use old revm versions, as the EVM is a living spec. 36 | 37 | In order to maintain this relationship (that trevm MINOR == revm MAJOR) we will 38 | sometimes make breaking changes in patch versions. This is technically semver 39 | compliant pre-1.0, but will cause build breakage downstream for users of those 40 | features. We will take care to document breaking changes in patch releases 41 | via github release notes. 42 | 43 | ## Limitations 44 | 45 | Trevm is a work in progress and is not feature complete. In particular, trevm 46 | does not currently have lifecycle support for blocks before Shanghai. This means 47 | it cannot produce correct post-block states for blocks before Shanghai. 48 | 49 | ### The Trevm State Machine 50 | 51 | Trevm provides a state machine that represents the internal state of a revm EVM 52 | instance. It ensures that at each step you can only take actions that are valid 53 | in the current state. This is done by using the [typestate] pattern. 54 | 55 | As you progress between states, the API will change to reflect the available 56 | operations. For example, you can't call `open_block()` on a `EvmNeedsTx` state, 57 | as the block is already open. You can't call `close_block()` on a `EvmReady` 58 | state, without explicitly clearing or running the transaction that has been 59 | ready. 60 | 61 | ![typestates are cool](./assets/states.png) 62 | 63 | [typestate]: https://cliffle.com/blog/rust-typestate/ 64 | [revm]: https://github.com/bluealloy/revm 65 | [docs.rs]: https://docs.rs/trevm/latest/trevm/ 66 | [documentation on docs.rs]: https://docs.rs/trevm/latest/trevm/ 67 | -------------------------------------------------------------------------------- /src/journal/update.rs: -------------------------------------------------------------------------------- 1 | use crate::journal::{BundleStateIndex, JournalDecode, JournalDecodeError, JournalEncode}; 2 | use alloy::primitives::{keccak256, Bytes, B256}; 3 | use std::sync::OnceLock; 4 | 5 | /// Journal associated with a block 6 | #[derive(Debug, Clone, PartialEq, Eq)] 7 | pub struct BlockUpdate<'a> { 8 | /// The height of the block. 9 | height: u64, 10 | 11 | /// The previous journal hash. 12 | prev_journal_hash: B256, 13 | 14 | /// The indexed changes. 15 | journal: BundleStateIndex<'a>, 16 | 17 | /// The serialized journal 18 | serialized: OnceLock, 19 | 20 | /// The hash of the serialized journal 21 | hash: OnceLock, 22 | } 23 | 24 | impl<'a> BlockUpdate<'a> { 25 | /// Create a new block update. 26 | pub const fn new(height: u64, prev_journal_hash: B256, journal: BundleStateIndex<'a>) -> Self { 27 | Self { 28 | height, 29 | prev_journal_hash, 30 | journal, 31 | serialized: OnceLock::new(), 32 | hash: OnceLock::new(), 33 | } 34 | } 35 | 36 | /// Get the height of the block. 37 | pub const fn height(&self) -> u64 { 38 | self.height 39 | } 40 | 41 | /// Get the previous journal hash. 42 | pub const fn prev_journal_hash(&self) -> B256 { 43 | self.prev_journal_hash 44 | } 45 | 46 | /// Get the journal index. 47 | pub const fn journal(&self) -> &BundleStateIndex<'a> { 48 | &self.journal 49 | } 50 | 51 | /// Decompose the block update into its parts. 52 | pub fn into_parts(self) -> (u64, B256, BundleStateIndex<'a>) { 53 | (self.height, self.prev_journal_hash, self.journal) 54 | } 55 | 56 | /// Serialize the block update. 57 | pub fn serialized(&self) -> &[u8] { 58 | self.serialized.get_or_init(|| JournalEncode::encoded(self)).as_ref() 59 | } 60 | 61 | /// Serialize and hash the block update. 62 | pub fn journal_hash(&self) -> B256 { 63 | *self.hash.get_or_init(|| keccak256(self.serialized())) 64 | } 65 | } 66 | 67 | impl JournalEncode for BlockUpdate<'_> { 68 | fn serialized_size(&self) -> usize { 69 | 8 + 32 + self.journal.serialized_size() 70 | } 71 | 72 | fn encode(&self, buf: &mut dyn alloy::rlp::BufMut) { 73 | self.height.encode(buf); 74 | self.prev_journal_hash.encode(buf); 75 | self.journal.encode(buf); 76 | } 77 | } 78 | 79 | impl JournalDecode for BlockUpdate<'static> { 80 | fn decode(buf: &mut &[u8]) -> Result { 81 | let original = *buf; 82 | let height = JournalDecode::decode(buf)?; 83 | let prev_journal_hash = JournalDecode::decode(buf)?; 84 | let journal = JournalDecode::decode(buf)?; 85 | 86 | let bytes_read = original.len() - buf.len(); 87 | let original = &original[..bytes_read]; 88 | 89 | Ok(Self { 90 | height, 91 | prev_journal_hash, 92 | journal, 93 | serialized: OnceLock::from(Bytes::copy_from_slice(original)), 94 | hash: OnceLock::from(keccak256(original)), 95 | }) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/evm/need_cfg.rs: -------------------------------------------------------------------------------- 1 | use crate::{helpers::Ctx, Cfg, EvmNeedsBlock, EvmNeedsCfg}; 2 | use revm::{Database, Inspector}; 3 | 4 | impl EvmNeedsCfg 5 | where 6 | Db: Database, 7 | Insp: Inspector>, 8 | { 9 | /// Fill the configuration environment. 10 | pub fn fill_cfg(mut self, filler: &T) -> EvmNeedsBlock { 11 | filler.fill_cfg(&mut self.inner); 12 | // SAFETY: Same size and repr. Only phantomdata type changes 13 | unsafe { core::mem::transmute(self) } 14 | } 15 | } 16 | 17 | // Some code above and documentation is adapted from the revm crate, and is 18 | // reproduced here under the terms of the MIT license. 19 | // 20 | // MIT License 21 | // 22 | // Copyright (c) 2021-2024 draganrakita 23 | // 24 | // Permission is hereby granted, free of charge, to any person obtaining a copy 25 | // of this software and associated documentation files (the "Software"), to deal 26 | // in the Software without restriction, including without limitation the rights 27 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 28 | // copies of the Software, and to permit persons to whom the Software is 29 | // furnished to do so, subject to the following conditions: 30 | // 31 | // The above copyright notice and this permission notice shall be included in all 32 | // copies or substantial portions of the Software. 33 | // 34 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 35 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 37 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 38 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 39 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 40 | // SOFTWARE. 41 | 42 | // Some code above is reproduced from `reth`. It is reused here under the MIT 43 | // license. 44 | // 45 | // The MIT License (MIT) 46 | // 47 | // Copyright (c) 2022-2024 Reth Contributors 48 | // 49 | // Permission is hereby granted, free of charge, to any person obtaining a copy 50 | // of this software and associated documentation files (the "Software"), to deal 51 | // in the Software without restriction, including without limitation the rights 52 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 53 | // copies of the Software, and to permit persons to whom the Software is 54 | // furnished to do so, subject to the following conditions: 55 | // 56 | // The above copyright notice and this permission notice shall be included in 57 | // all copies or substantial portions of the Software. 58 | // 59 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 60 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 61 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 62 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 63 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 64 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 65 | // THE SOFTWARE. 66 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trevm" 3 | version = "0.31.2" 4 | rust-version = "1.83.0" 5 | edition = "2021" 6 | authors = ["init4"] 7 | homepage = "https://github.com/init4tech/trevm" 8 | repository = "https://github.com/init4tech/trevm" 9 | license = "MIT OR Apache-2.0" 10 | description = "A typestate API wrapper for the revm EVM implementation" 11 | 12 | [lints.rust] 13 | missing-debug-implementations = "warn" 14 | missing-docs = "warn" 15 | unreachable-pub = "warn" 16 | unused-must-use = "deny" 17 | rust-2018-idioms = "deny" 18 | unnameable-types = "warn" 19 | 20 | [lints.rustdoc] 21 | all = "warn" 22 | 23 | [lints.clippy] 24 | missing-const-for-fn = "warn" 25 | use-self = "warn" 26 | option-if-let-else = "warn" 27 | redundant-clone = "warn" 28 | 29 | [[example]] 30 | name = "basic_transact" 31 | 32 | [[example]] 33 | name = "fork_ref_transact" 34 | required-features = ["alloy-db"] 35 | 36 | [dependencies] 37 | alloy = { version = "1.0.35", default-features = false, features = [ 38 | "consensus", 39 | "rpc-types-mev", 40 | "eips", 41 | "k256", 42 | "std", 43 | "rlp", 44 | "sol-types", 45 | ] } 46 | 47 | revm = { version = "31", default-features = false } 48 | revm-inspectors = { version = "0.32", optional = true } 49 | 50 | dashmap = { version = "6.1.0", optional = true } 51 | tracing = { version = "0.1.41", optional = true } 52 | thiserror = "2.0.11" 53 | 54 | tokio = { version = "1.44", optional = true } 55 | 56 | [dev-dependencies] 57 | revm = { version = "31", features = ["serde-json", "std", "alloydb"] } 58 | trevm = { path = ".", features = ["test-utils"] } 59 | 60 | alloy = { version = "1.0.35", features = ["providers", "transports"] } 61 | 62 | # misc 63 | eyre = "0.6" 64 | serde_json = { version = "1.0", default-features = false, features = ["alloc"] } 65 | tokio = { version = "1.39", features = ["macros", "rt-multi-thread"] } 66 | 67 | 68 | [features] 69 | default = [ 70 | "call", 71 | "concurrent-db", 72 | "estimate_gas", 73 | "tracing-inspectors", 74 | "revm/std", 75 | "revm/c-kzg", 76 | "revm/blst", 77 | "revm/portable", 78 | "revm/secp256k1", 79 | ] 80 | 81 | alloy-db = ["dep:tokio", "alloy/providers"] 82 | 83 | call = ["optional_eip3607", "optional_no_base_fee"] 84 | 85 | concurrent-db = ["dep:dashmap"] 86 | 87 | estimate_gas = ["optional_eip3607", "optional_no_base_fee", "dep:tracing"] 88 | 89 | test-utils = ["revm/std", "revm/serde-json", "revm/alloydb", "alloy/signers", "alloy/signer-local"] 90 | 91 | secp256k1 = ["revm/secp256k1"] 92 | c-kzg = ["revm/c-kzg"] 93 | blst = ["revm/blst"] 94 | 95 | portable = ["revm/portable"] 96 | 97 | dev = [ 98 | "memory_limit", 99 | "optional_balance_check", 100 | "optional_block_gas_limit", 101 | "optional_eip3607", 102 | "optional_no_base_fee", 103 | ] 104 | 105 | memory_limit = ["revm/memory_limit"] 106 | optional_balance_check = ["revm/optional_balance_check"] 107 | optional_block_gas_limit = ["revm/optional_block_gas_limit"] 108 | optional_eip3607 = ["revm/optional_eip3607"] 109 | optional_no_base_fee = ["revm/optional_no_base_fee"] 110 | full_env_cfg = [ 111 | "optional_balance_check", 112 | "optional_block_gas_limit", 113 | "optional_eip3607", 114 | "optional_no_base_fee", 115 | ] 116 | tracing-inspectors = ["dep:revm-inspectors", "alloy/rpc-types-trace"] 117 | -------------------------------------------------------------------------------- /examples/fork_ref_transact.rs: -------------------------------------------------------------------------------- 1 | //! This example demonstrates how to query storage slots of a contract, using 2 | //! [`AlloyDb`]. 3 | 4 | use alloy::{ 5 | eips::BlockId, 6 | primitives::{address, Address, TxKind, U256}, 7 | providers::ProviderBuilder, 8 | sol, 9 | sol_types::SolCall, 10 | }; 11 | use revm::{context::TxEnv, database::WrapDatabaseAsync}; 12 | use trevm::{db::alloy::AlloyDb, revm::database::CacheDB, NoopBlock, NoopCfg, TrevmBuilder, Tx}; 13 | 14 | sol! { 15 | #[allow(missing_docs)] 16 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 17 | } 18 | 19 | struct GetReservesFiller; 20 | 21 | impl Tx for GetReservesFiller { 22 | fn fill_tx_env(&self, tx_env: &mut TxEnv) { 23 | tx_env.caller = Address::with_last_byte(0); 24 | // ETH/USDT pair on Uniswap V2 25 | tx_env.kind = TxKind::Call(POOL_ADDRESS); 26 | // calldata formed via alloy's abi encoder 27 | tx_env.data = getReservesCall::new(()).abi_encode().into(); 28 | // transaction value in wei 29 | tx_env.value = U256::from(0); 30 | } 31 | } 32 | 33 | const POOL_ADDRESS: Address = address!("0d4a11d5EEaaC28EC3F61d100daF4d40471f1852"); 34 | 35 | #[tokio::main] 36 | async fn main() -> eyre::Result<()> { 37 | // create ethers client and wrap it in Arc 38 | let rpc_url = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; 39 | 40 | let client = ProviderBuilder::new().connect_http(rpc_url.parse()?); 41 | 42 | // ----------------------------------------------------------- // 43 | // Storage slots of UniV2Pair contract // 44 | // =========================================================== // 45 | // storage[5] = factory: address // 46 | // storage[6] = token0: address // 47 | // storage[7] = token1: address // 48 | // storage[8] = (res0, res1, ts): (uint112, uint112, uint32) // 49 | // storage[9] = price0CumulativeLast: uint256 // 50 | // storage[10] = price1CumulativeLast: uint256 // 51 | // storage[11] = kLast: uint256 // 52 | // =========================================================== // 53 | 54 | // initialize new AlloyDB 55 | let alloydb = WrapDatabaseAsync::new(AlloyDb::new(client, BlockId::default())).unwrap(); 56 | 57 | // initialise empty in-memory-db 58 | let cache_db = CacheDB::new(alloydb); 59 | 60 | // initialise an empty (default) EVM 61 | let evm = TrevmBuilder::new() 62 | .with_db(cache_db) 63 | .build_trevm() 64 | .fill_cfg(&NoopCfg) 65 | .fill_block(&NoopBlock) 66 | .fill_tx(&GetReservesFiller) 67 | .run() 68 | .inspect_err(|e| panic!("Execution error {e:?}")) 69 | .unwrap(); 70 | 71 | // Inspect the outcome of a transaction execution, and get the return value 72 | println!("Execution result: {:#?}", evm.result()); 73 | let output = evm.output().expect("Execution halted"); 74 | 75 | // decode bytes to reserves + ts via alloy's abi decode 76 | let return_vals = getReservesCall::abi_decode_returns_validate(output)?; 77 | 78 | // Print emulated getReserves() call output 79 | println!("Reserve0: {:#?}", return_vals.reserve0); 80 | println!("Reserve1: {:#?}", return_vals.reserve1); 81 | println!("Timestamp: {:#?}", return_vals.blockTimestampLast); 82 | 83 | Ok(()) 84 | } 85 | -------------------------------------------------------------------------------- /src/system/eip2935.rs: -------------------------------------------------------------------------------- 1 | use crate::{helpers::Ctx, EvmNeedsTx}; 2 | use alloy::primitives::U256; 3 | use revm::{ 4 | context::{result::EVMError, ContextTr}, 5 | primitives::hardfork::SpecId, 6 | Database, DatabaseCommit, Inspector, 7 | }; 8 | 9 | pub use alloy::eips::eip2935::{ 10 | HISTORY_SERVE_WINDOW, HISTORY_STORAGE_ADDRESS, HISTORY_STORAGE_CODE, 11 | }; 12 | 13 | use super::checked_insert_code; 14 | 15 | /// The slot for the [EIP-2935] blockhash storage. 16 | /// 17 | /// [EIP-2935]: https://eips.ethereum.org/EIPS/eip-2935 18 | pub fn eip2935_slot(block_num: u64) -> U256 { 19 | U256::from(block_num % HISTORY_SERVE_WINDOW as u64) 20 | } 21 | 22 | impl EvmNeedsTx 23 | where 24 | Db: Database + DatabaseCommit, 25 | Insp: Inspector>, 26 | { 27 | /// Apply the pre-block logic for [EIP-2935]. This logic was introduced in 28 | /// Prague and updates historical block hashes in a special system 29 | /// contract. Unlike other system actions, this is NOT modeled as a 30 | /// transaction. 31 | /// 32 | /// [EIP-2935]: https://eips.ethereum.org/EIPS/eip-2935 33 | pub fn apply_eip2935(&mut self) -> Result<(), EVMError> { 34 | if self.spec_id() < SpecId::PRAGUE || self.block_number().is_zero() { 35 | return Ok(()); 36 | } 37 | 38 | checked_insert_code( 39 | self.inner_mut_unchecked(), 40 | HISTORY_STORAGE_ADDRESS, 41 | &HISTORY_STORAGE_CODE, 42 | )?; 43 | 44 | let block_num = self.block().number; 45 | let prev_block = block_num.saturating_sub(U256::ONE); 46 | 47 | // Update the EVM state with the new value. 48 | let slot = eip2935_slot(prev_block.to()); 49 | 50 | let parent_block_hash = self 51 | .inner_mut_unchecked() 52 | .db_mut() 53 | .block_hash(prev_block.to()) 54 | .map_err(EVMError::Database)?; 55 | 56 | self.try_set_storage_unchecked(HISTORY_STORAGE_ADDRESS, slot, parent_block_hash.into()) 57 | .map_err(EVMError::Database)?; 58 | 59 | Ok(()) 60 | } 61 | } 62 | 63 | #[cfg(test)] 64 | mod test { 65 | use super::*; 66 | use crate::{NoopBlock, NoopCfg}; 67 | use alloy::primitives::B256; 68 | use revm::bytecode::Bytecode; 69 | 70 | #[test] 71 | fn test_eip2935() { 72 | let block_num = 5; 73 | let prev_block_num = U256::from(4); 74 | let prev_hash = B256::repeat_byte(0xaa); 75 | let slot = eip2935_slot(prev_block_num.to()); 76 | 77 | // we create a trevm instance with the block number set to 1 78 | let mut trevm = crate::test_utils::test_trevm().fill_cfg(&NoopCfg).fill_block(&NoopBlock); 79 | 80 | trevm.inner_mut_unchecked().modify_block(|block| { 81 | block.number = U256::from(block_num); 82 | }); 83 | 84 | // we set the previous block hash in the cachedb, as it will be loaded 85 | // during eip application 86 | trevm.inner_mut_unchecked().db_mut().cache.block_hashes.insert(prev_block_num, prev_hash); 87 | 88 | trevm.apply_eip2935().unwrap(); 89 | 90 | assert_eq!( 91 | trevm.try_read_storage(HISTORY_STORAGE_ADDRESS, slot).unwrap().to_be_bytes(), 92 | prev_hash 93 | ); 94 | assert_eq!( 95 | trevm.try_read_code(HISTORY_STORAGE_ADDRESS).unwrap().unwrap(), 96 | Bytecode::new_raw(HISTORY_STORAGE_CODE.clone()) 97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/inspectors/timeout.rs: -------------------------------------------------------------------------------- 1 | use crate::helpers::Ctx; 2 | use revm::{ 3 | context_interface::context::ContextError, 4 | interpreter::{ 5 | CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterTypes, 6 | }, 7 | Database, Inspector, 8 | }; 9 | use std::time::{Duration, Instant}; 10 | 11 | /// A revm [`Inspector`] that limits wallclock time spent on execution. 12 | /// 13 | /// This inspector will stop execution at the beginning and end of each 14 | /// callframe after the timeout has been reached. Specifically, it will stop 15 | /// when: 16 | /// - any callframe is aborted (e.g. due to execeeding its gas limit) 17 | /// - any of the following instructions are executed: 18 | /// - `CALL` 19 | /// - `CREATE` 20 | /// - `RETURN` 21 | /// - `STOP` 22 | /// - `REVERT` 23 | /// - any invalid opcode 24 | /// 25 | /// When execution is terminated by the timer, it will result in a 26 | /// [`ContextError::Custom`], which will be propagated as an 27 | /// [`EVMError::Custom`] to the caller of the EVM interpreter. 28 | /// 29 | /// ## Usage Note 30 | /// 31 | /// When the timeout is triggered, the inspector will overwrite the 32 | /// [`CallOutcome`] or [`CreateOutcome`]. This means that if other inspectors 33 | /// have already run, they may have inspected data that is no longer valid. 34 | /// 35 | /// To avoid this, run this inspector FIRST on any multi-inspector setup. I.e. 36 | /// in a stack, this should be the OUTERMOST inspector. This ensures that 37 | /// invalid data is not inspected, and that other inspectors do not consume 38 | /// memory or compute resources inspecting data that is guaranteed to be 39 | /// discarded. 40 | /// 41 | /// [EIP-150]: https://eips.ethereum.org/EIPS/eip-150 42 | /// [`EVMError::Custom`]: revm::context::result::EVMError::Custom 43 | #[derive(Debug, Clone, Copy)] 44 | pub struct TimeLimit { 45 | duration: Duration, 46 | execution_start: Instant, 47 | } 48 | 49 | impl TimeLimit { 50 | /// Create a new [`TimeLimit`] inspector. 51 | /// 52 | /// The inspector will stop execution after the given duration has passed. 53 | pub fn new(duration: Duration) -> Self { 54 | Self { duration, execution_start: Instant::now() } 55 | } 56 | 57 | /// Check if the timeout has been reached. 58 | pub fn has_elapsed(&self) -> bool { 59 | self.execution_start.elapsed() >= self.duration 60 | } 61 | 62 | /// Set the execution start time to [`Instant::now`]. This is invoked during [`Inspector::initialize_interp`]. 63 | pub fn reset(&mut self) { 64 | self.execution_start = Instant::now(); 65 | } 66 | 67 | /// Get the amount of time that has elapsed since execution start. 68 | pub fn elapsed(&self) -> Duration { 69 | self.execution_start.elapsed() 70 | } 71 | } 72 | 73 | macro_rules! check_timeout { 74 | ($self:ident, $ctx:ident) => { 75 | if $self.has_elapsed() { 76 | $ctx.error = Err(ContextError::Custom("timeout during evm execution".to_string())); 77 | } 78 | }; 79 | } 80 | 81 | impl Inspector, Int> for TimeLimit { 82 | fn initialize_interp(&mut self, _interp: &mut Interpreter, _ctx: &mut Ctx) { 83 | self.reset(); 84 | } 85 | 86 | fn call(&mut self, ctx: &mut Ctx, _inputs: &mut CallInputs) -> Option { 87 | check_timeout!(self, ctx); 88 | 89 | None 90 | } 91 | 92 | fn call_end(&mut self, ctx: &mut Ctx, _inputs: &CallInputs, _outcome: &mut CallOutcome) { 93 | check_timeout!(self, ctx); 94 | } 95 | 96 | fn create(&mut self, ctx: &mut Ctx, _inputs: &mut CreateInputs) -> Option { 97 | check_timeout!(self, ctx); 98 | 99 | None 100 | } 101 | 102 | fn create_end( 103 | &mut self, 104 | ctx: &mut Ctx, 105 | _inputs: &CreateInputs, 106 | _outcome: &mut CreateOutcome, 107 | ) { 108 | check_timeout!(self, ctx); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/lifecycle/output.rs: -------------------------------------------------------------------------------- 1 | use alloy::{ 2 | consensus::{ReceiptEnvelope, TxReceipt}, 3 | primitives::{Address, Bloom, Bytes, Log}, 4 | }; 5 | use std::sync::OnceLock; 6 | 7 | /// Information externalized during block execution. 8 | /// 9 | /// This struct is used to collect the results of executing a block of 10 | /// transactions. It accumulates the receipts and senders of the transactions. 11 | #[derive(Debug, Clone, PartialEq, Eq)] 12 | pub struct BlockOutput { 13 | /// The receipts of the transactions in the block, in order. 14 | receipts: Vec, 15 | 16 | /// The senders of the transactions in the block, in order. 17 | senders: Vec
, 18 | 19 | /// The logs bloom of the block. 20 | bloom: OnceLock, 21 | } 22 | 23 | impl Default for BlockOutput { 24 | fn default() -> Self { 25 | Self::with_capacity(0) 26 | } 27 | } 28 | 29 | impl> BlockOutput { 30 | /// Create a new block output with memory allocated to hold `capacity` 31 | /// transaction outcomes. 32 | pub fn with_capacity(capacity: usize) -> Self { 33 | Self { 34 | receipts: Vec::with_capacity(capacity), 35 | senders: Vec::with_capacity(capacity), 36 | bloom: Default::default(), 37 | } 38 | } 39 | 40 | fn seal(&self) { 41 | self.bloom.get_or_init(|| { 42 | let mut bloom = Bloom::default(); 43 | for log in self.logs() { 44 | bloom.accrue_log(log); 45 | } 46 | bloom 47 | }); 48 | } 49 | 50 | fn unseal(&mut self) { 51 | self.bloom.take(); 52 | } 53 | 54 | /// Reserve memory for `capacity` transaction outcomes. 55 | pub fn reserve(&mut self, capacity: usize) { 56 | self.receipts.reserve(capacity); 57 | self.senders.reserve(capacity); 58 | } 59 | 60 | /// Get a reference to the receipts of the transactions in the block. 61 | #[allow(clippy::missing_const_for_fn)] // false positive 62 | pub fn receipts(&self) -> &[T] { 63 | &self.receipts 64 | } 65 | 66 | /// Get an iterator over the logs of the transactions in the block. 67 | pub fn logs(&self) -> impl Iterator { 68 | self.receipts.iter().flat_map(|r| r.logs()) 69 | } 70 | 71 | /// Get the logs bloom of the block. 72 | pub fn logs_bloom(&self) -> Bloom { 73 | self.seal(); 74 | self.bloom.get().cloned().unwrap() 75 | } 76 | 77 | /// Get a reference the senders of the transactions in the block. 78 | #[allow(clippy::missing_const_for_fn)] // false positive 79 | pub fn senders(&self) -> &[Address] { 80 | &self.senders 81 | } 82 | 83 | /// Get the cumulative gas used in the block. 84 | pub fn cumulative_gas_used(&self) -> u64 { 85 | self.receipts().last().map(TxReceipt::cumulative_gas_used).unwrap_or_default() 86 | } 87 | 88 | /// Accumulate the result of a transaction execution. If `parse_deposits` is 89 | /// true, the logs of the transaction will be scanned for deposit events 90 | /// according to the [EIP-6110] specification. 91 | /// 92 | /// [EIP-6110]: https://eips.ethereum.org/EIPS/eip-6110 93 | pub fn push_result(&mut self, receipt: T, sender: Address) { 94 | self.unseal(); 95 | self.push_receipt(receipt); 96 | self.push_sender(sender); 97 | } 98 | 99 | /// Push a receipt onto the list of receipts. 100 | fn push_receipt(&mut self, receipt: T) { 101 | self.receipts.push(receipt); 102 | } 103 | 104 | /// Push a sender onto the list of senders. 105 | fn push_sender(&mut self, sender: Address) { 106 | self.senders.push(sender); 107 | } 108 | 109 | /// Find deposits in the logs of the transactions in the block. 110 | pub fn find_deposit_requests(&self) -> impl Iterator + use<'_, T> { 111 | crate::system::eip6110::check_logs_for_deposits( 112 | self.receipts().iter().flat_map(TxReceipt::logs), 113 | ) 114 | } 115 | 116 | /// Deconstruct the block output into its parts. 117 | pub fn into_parts(self) -> (Vec, Vec
, Bloom) { 118 | let bloom = self.logs_bloom(); 119 | (self.receipts, self.senders, bloom) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/system/eip4788.rs: -------------------------------------------------------------------------------- 1 | use super::{checked_insert_code, execute_system_tx}; 2 | use crate::{helpers::Ctx, system::SystemTx, EvmNeedsTx}; 3 | use alloy::primitives::{Address, Bytes, B256, U256}; 4 | use revm::{ 5 | context::result::EVMError, primitives::hardfork::SpecId, Database, DatabaseCommit, Inspector, 6 | }; 7 | 8 | /// The number of beacon roots to store in the beacon roots contract. 9 | pub const HISTORY_BUFFER_LENGTH: u64 = 8191; 10 | 11 | pub use alloy::eips::eip4788::{BEACON_ROOTS_ADDRESS, BEACON_ROOTS_CODE}; 12 | 13 | impl SystemTx { 14 | /// Instantiate a system call for the pre-block beacon roots as specified in 15 | /// [EIP-4788]. 16 | /// 17 | /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 18 | pub fn eip4788(parent_beacon_root: B256) -> Self { 19 | Self::eip4788_with_target(parent_beacon_root, BEACON_ROOTS_ADDRESS) 20 | } 21 | 22 | /// Instantiate a system call for the pre-block beacon roots as specified in 23 | /// [EIP-4788], with a custom target address. 24 | /// 25 | /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 26 | pub fn eip4788_with_target(parent_beacon_root: B256, target: Address) -> Self { 27 | Self::new(target, Bytes::from(parent_beacon_root)) 28 | } 29 | 30 | /// Instantiate a system call for the pre-block beacon roots as specified in 31 | /// [EIP-4788], with a custom target address and caller address. 32 | /// 33 | /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 34 | pub fn eip4788_with_target_and_caller( 35 | parent_beacon_root: B256, 36 | target: Address, 37 | caller: Address, 38 | ) -> Self { 39 | Self::new_with_caller(target, Bytes::from(parent_beacon_root), caller) 40 | } 41 | } 42 | 43 | /// The slot for the [EIP-4788] timestamp storage. 44 | /// 45 | /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 46 | pub fn eip4788_timestamp_slot(timestamp: u64) -> U256 { 47 | U256::from(timestamp % HISTORY_BUFFER_LENGTH) 48 | } 49 | 50 | /// The slot for the [EIP-4788] root storage. 51 | /// 52 | /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 53 | pub fn eip4788_root_slot(timestamp: u64) -> U256 { 54 | eip4788_timestamp_slot(timestamp) + U256::from(HISTORY_BUFFER_LENGTH) 55 | } 56 | 57 | impl EvmNeedsTx 58 | where 59 | Db: Database + DatabaseCommit, 60 | Insp: Inspector>, 61 | { 62 | /// Apply a system transaction as specified in [EIP-4788]. The EIP-4788 63 | /// pre-block action was introduced in Cancun, and calls the beacon root 64 | /// contract to update the historical beacon root. 65 | /// 66 | /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 67 | pub fn apply_eip4788(&mut self, parent_beacon_root: B256) -> Result<(), EVMError> { 68 | if self.spec_id() < SpecId::CANCUN { 69 | return Ok(()); 70 | } 71 | 72 | checked_insert_code(self.inner_mut_unchecked(), BEACON_ROOTS_ADDRESS, &BEACON_ROOTS_CODE)?; 73 | execute_system_tx(self.inner_mut_unchecked(), &SystemTx::eip4788(parent_beacon_root)) 74 | .map(drop) 75 | } 76 | } 77 | 78 | #[cfg(test)] 79 | mod test { 80 | use super::*; 81 | use crate::{NoopBlock, NoopCfg}; 82 | use alloy::primitives::U256; 83 | use revm::bytecode::Bytecode; 84 | 85 | #[test] 86 | fn test_eip4788() { 87 | let timestamp = 8; 88 | 89 | let mut trevm = crate::test_utils::test_trevm().fill_cfg(&NoopCfg).fill_block(&NoopBlock); 90 | 91 | trevm.inner_mut_unchecked().modify_block(|block| { 92 | block.timestamp = U256::from(timestamp); 93 | }); 94 | 95 | let parent_beacon_root = B256::repeat_byte(0xaa); 96 | 97 | trevm.apply_eip4788(parent_beacon_root).unwrap(); 98 | 99 | let ts_slot = eip4788_timestamp_slot(timestamp); 100 | let root_slot = eip4788_root_slot(timestamp); 101 | 102 | assert_eq!( 103 | trevm.try_read_storage(BEACON_ROOTS_ADDRESS, ts_slot).unwrap(), 104 | U256::from(timestamp) 105 | ); 106 | assert_eq!( 107 | trevm.try_read_storage(BEACON_ROOTS_ADDRESS, root_slot).unwrap().to_be_bytes(), 108 | parent_beacon_root 109 | ); 110 | assert_eq!( 111 | trevm.try_read_code(BEACON_ROOTS_ADDRESS).unwrap().unwrap(), 112 | Bytecode::new_raw(BEACON_ROOTS_CODE.clone()), 113 | ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/fill/fillers.rs: -------------------------------------------------------------------------------- 1 | use crate::fill::traits::{Cfg, Tx}; 2 | use revm::context::{BlockEnv, CfgEnv, TxEnv}; 3 | 4 | /// A [`Cfg`] that disables gas-related checks and payment of the 5 | /// beneficiary reward, while leaving other cfg options unchanged. 6 | /// 7 | /// ## Warning 8 | /// 9 | /// This filler relies on the following optional features: 10 | /// - `optional_balance_check` 11 | /// - `optional_beneficiary_reward` 12 | /// - `optional_gas_refund` 13 | /// - `optional_no_base_fee` 14 | /// 15 | /// It will disable the corresponding checks if the features are enabled. **If 16 | /// none of the features are enabled, this filler will do nothing.** 17 | #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] 18 | pub struct DisableGasChecks; 19 | 20 | impl Cfg for DisableGasChecks { 21 | #[allow(unused_variables)] 22 | fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) { 23 | #[cfg(feature = "optional_balance_check")] 24 | { 25 | cfg_env.disable_balance_check = true; 26 | } 27 | #[cfg(feature = "optional_no_base_fee")] 28 | { 29 | cfg_env.disable_base_fee = true; 30 | } 31 | } 32 | } 33 | 34 | /// A [`Cfg`] that disables the chain ID check, while leaving other [`CfgEnv`] 35 | /// attributes untouched. 36 | #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] 37 | pub struct DisableChainIdCheck; 38 | 39 | impl Cfg for DisableChainIdCheck { 40 | fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) { 41 | { 42 | cfg_env.tx_chain_id_check = false; 43 | } 44 | } 45 | } 46 | 47 | /// A [`Cfg`] that disables the nonce check, while leaving other [`CfgEnv`] 48 | /// attributes untouched. 49 | #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] 50 | pub struct DisableNonceCheck; 51 | 52 | impl Cfg for DisableNonceCheck { 53 | #[allow(unused_variables)] 54 | fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) { 55 | cfg_env.disable_nonce_check = true; 56 | } 57 | } 58 | 59 | /// Prime the EVM for gas estimation. This filler is all of [`Cfg`], and 60 | /// [`Tx`]. It is used internally by [`crate::Trevm::estimate_gas`], and is 61 | /// considered a low-level API. Generally it is not correct to import this 62 | /// type. 63 | #[cfg(feature = "estimate_gas")] 64 | #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] 65 | #[repr(transparent)] 66 | pub(crate) struct GasEstimationFiller { 67 | pub(crate) gas_limit: u64, 68 | } 69 | 70 | #[cfg(feature = "estimate_gas")] 71 | impl From for GasEstimationFiller { 72 | fn from(gas_limit: u64) -> Self { 73 | Self { gas_limit } 74 | } 75 | } 76 | 77 | #[cfg(feature = "estimate_gas")] 78 | impl Cfg for GasEstimationFiller { 79 | fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) { 80 | cfg_env.disable_base_fee = true; 81 | cfg_env.disable_eip3607 = true; 82 | DisableNonceCheck.fill_cfg_env(cfg_env); 83 | } 84 | 85 | fn fill_cfg( 86 | &self, 87 | evm: &mut revm::context::Evm, Insp, Inst, Prec, Frame>, 88 | ) { 89 | evm.ctx.modify_cfg(|cfg_env| self.fill_cfg_env(cfg_env)); 90 | 91 | let chain_id = evm.ctx.cfg.chain_id; 92 | 93 | evm.ctx.modify_tx(|tx_env| { 94 | tx_env.chain_id = Some(chain_id); 95 | }); 96 | } 97 | } 98 | 99 | #[cfg(feature = "estimate_gas")] 100 | impl Tx for GasEstimationFiller { 101 | fn fill_tx_env(&self, tx_env: &mut TxEnv) { 102 | tx_env.gas_limit = self.gas_limit; 103 | } 104 | } 105 | 106 | #[cfg(feature = "call")] 107 | pub(crate) struct CallFiller { 108 | pub gas_limit: u64, 109 | } 110 | 111 | #[cfg(feature = "call")] 112 | impl Cfg for CallFiller { 113 | fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) { 114 | cfg_env.disable_base_fee = true; 115 | cfg_env.disable_eip3607 = true; 116 | DisableNonceCheck.fill_cfg_env(cfg_env); 117 | } 118 | 119 | fn fill_cfg( 120 | &self, 121 | evm: &mut revm::context::Evm, Insp, Inst, Prec, Frame>, 122 | ) { 123 | evm.ctx.modify_cfg(|cfg_env| self.fill_cfg_env(cfg_env)); 124 | 125 | let chain_id = evm.ctx.cfg.chain_id; 126 | 127 | evm.ctx.modify_tx(|tx_env| { 128 | tx_env.chain_id = Some(chain_id); 129 | }); 130 | } 131 | } 132 | 133 | #[cfg(feature = "call")] 134 | impl crate::Block for CallFiller { 135 | fn fill_block_env(&self, block_env: &mut BlockEnv) { 136 | block_env.gas_limit = self.gas_limit; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/inspectors/set.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | 3 | use revm::{ 4 | interpreter::{ 5 | CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterTypes, 6 | }, 7 | primitives::{Address, Log, U256}, 8 | Inspector, 9 | }; 10 | 11 | /// A stack of [`Inspector`]s. 12 | /// 13 | /// This is a thin wrapper around a [`VecDeque`] of inspectors. 14 | #[derive(Default)] 15 | pub struct InspectorSet { 16 | inspectors: VecDeque>>, 17 | } 18 | 19 | impl core::ops::Deref for InspectorSet { 20 | type Target = VecDeque>>; 21 | 22 | fn deref(&self) -> &Self::Target { 23 | &self.inspectors 24 | } 25 | } 26 | 27 | impl core::ops::DerefMut for InspectorSet { 28 | fn deref_mut(&mut self) -> &mut Self::Target { 29 | &mut self.inspectors 30 | } 31 | } 32 | 33 | impl core::fmt::Debug for InspectorSet { 34 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 35 | f.debug_struct("InspectorStack").field("inspectors", &self.inspectors.len()).finish() 36 | } 37 | } 38 | 39 | impl InspectorSet 40 | where 41 | Int: InterpreterTypes, 42 | { 43 | /// Instantiate a new empty inspector stack. 44 | pub fn new() -> Self { 45 | Self { inspectors: Default::default() } 46 | } 47 | 48 | /// Instantiate a new empty stack with pre-allocated capacity. 49 | pub fn with_capacity(cap: usize) -> Self { 50 | Self { inspectors: VecDeque::with_capacity(cap) } 51 | } 52 | 53 | /// Push an inspector to the back of the stack. 54 | pub fn push_back + 'static>(&mut self, inspector: I) { 55 | self.inspectors.push_back(Box::new(inspector)); 56 | } 57 | 58 | /// Push an inspector to the front of the stack. 59 | pub fn push_front + 'static>(&mut self, inspector: I) { 60 | self.inspectors.push_front(Box::new(inspector)); 61 | } 62 | 63 | /// Pop an inspector from the back of the stack. 64 | pub fn pop_back(&mut self) -> Option>> { 65 | self.inspectors.pop_back() 66 | } 67 | 68 | /// Pop an inspector from the front of the stack. 69 | pub fn pop_front(&mut self) -> Option>> { 70 | self.inspectors.pop_front() 71 | } 72 | } 73 | 74 | impl Inspector for InspectorSet 75 | where 76 | Int: InterpreterTypes, 77 | { 78 | fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut Ctx) { 79 | self.inspectors.iter_mut().for_each(|i| i.initialize_interp(interp, context)); 80 | } 81 | 82 | fn step(&mut self, interp: &mut Interpreter, context: &mut Ctx) { 83 | self.inspectors.iter_mut().for_each(|i| i.step(interp, context)); 84 | } 85 | 86 | fn step_end(&mut self, interp: &mut Interpreter, context: &mut Ctx) { 87 | self.inspectors.iter_mut().for_each(|i| i.step_end(interp, context)); 88 | } 89 | 90 | fn log(&mut self, interp: &mut Interpreter, context: &mut Ctx, log: Log) { 91 | self.inspectors.iter_mut().for_each(|i| i.log(interp, context, log.clone())); 92 | } 93 | 94 | fn call(&mut self, context: &mut Ctx, inputs: &mut CallInputs) -> Option { 95 | for inspector in self.inspectors.iter_mut() { 96 | let outcome = inspector.call(context, inputs); 97 | if outcome.is_some() { 98 | return outcome; 99 | } 100 | } 101 | None 102 | } 103 | 104 | fn call_end(&mut self, context: &mut Ctx, inputs: &CallInputs, outcome: &mut CallOutcome) { 105 | self.inspectors.iter_mut().for_each(|i| i.call_end(context, inputs, outcome)) 106 | } 107 | 108 | fn create(&mut self, context: &mut Ctx, inputs: &mut CreateInputs) -> Option { 109 | for inspector in self.inspectors.iter_mut() { 110 | let outcome = inspector.create(context, inputs); 111 | if outcome.is_some() { 112 | return outcome; 113 | } 114 | } 115 | None 116 | } 117 | 118 | fn create_end( 119 | &mut self, 120 | context: &mut Ctx, 121 | inputs: &CreateInputs, 122 | outcome: &mut CreateOutcome, 123 | ) { 124 | self.inspectors.iter_mut().for_each(|i| i.create_end(context, inputs, outcome)) 125 | } 126 | 127 | fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { 128 | self.inspectors.iter_mut().for_each(|i| i.selfdestruct(contract, target, value)) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/inspectors/layer.rs: -------------------------------------------------------------------------------- 1 | use revm::{ 2 | interpreter::{ 3 | CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterTypes, 4 | }, 5 | primitives::{Address, Log, U256}, 6 | Inspector, 7 | }; 8 | 9 | /// A layer in a stack of inspectors. Contains its own inspector and an 10 | /// inner inspector. This is used to create a stack of inspectors that can 11 | /// be used to inspect the execution of a contract. 12 | /// 13 | /// Use `Layered` when you need to retain type information about the inner 14 | /// inspectors. 15 | /// 16 | /// The current inspector will be invoked first, then the inner inspector. 17 | /// For functions that may return values (e.g. [`Inspector::call`]), if the 18 | /// current inspector returns a value, the inner inspector will not be invoked. 19 | #[derive(Clone, Debug)] 20 | pub struct Layered { 21 | outer: Outer, 22 | inner: Inner, 23 | } 24 | 25 | impl Layered { 26 | /// Create a new [`Layered`] inspector with the given current and inner 27 | /// inspectors. 28 | pub const fn new(outer: Outer, inner: Inner) -> Self { 29 | Self { outer, inner } 30 | } 31 | 32 | /// Wrap this inspector in another, creating a new [`Layered`] inspector. 33 | /// with this as the inner inspector. 34 | pub const fn wrap_in(self, outer: Other) -> Layered { 35 | Layered { outer, inner: self } 36 | } 37 | 38 | /// Wrap this inspector around another, creating a new [`Layered`] inspector 39 | /// with this as the outer inspector. 40 | pub const fn wrap_around(self, inner: Other) -> Layered { 41 | Layered { outer: self, inner } 42 | } 43 | 44 | /// Decompose the [`Layered`] inspector into its outer and inner 45 | /// inspectors. 46 | pub fn into_parts(self) -> (Outer, Inner) { 47 | (self.outer, self.inner) 48 | } 49 | 50 | /// Get a reference to the outer inspector. 51 | pub const fn outer(&self) -> &Outer { 52 | &self.outer 53 | } 54 | 55 | /// Get a mutable reference to the outer inspector. 56 | pub const fn outer_mut(&mut self) -> &mut Outer { 57 | &mut self.outer 58 | } 59 | 60 | /// Get a reference to the inner inspector. 61 | pub const fn inner(&self) -> &Inner { 62 | &self.inner 63 | } 64 | 65 | /// Get a mutable reference to the inner inspector. 66 | pub const fn inner_mut(&mut self) -> &mut Inner { 67 | &mut self.inner 68 | } 69 | } 70 | 71 | impl Inspector for Layered 72 | where 73 | Outer: Inspector, 74 | Inner: Inspector, 75 | { 76 | fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut Ctx) { 77 | self.outer.initialize_interp(interp, context); 78 | self.inner.initialize_interp(interp, context); 79 | } 80 | 81 | fn step(&mut self, interp: &mut Interpreter, context: &mut Ctx) { 82 | self.outer.step(interp, context); 83 | self.inner.step(interp, context); 84 | } 85 | 86 | fn step_end(&mut self, interp: &mut Interpreter, context: &mut Ctx) { 87 | self.outer.step_end(interp, context); 88 | self.inner.step_end(interp, context); 89 | } 90 | 91 | fn log(&mut self, interp: &mut Interpreter, context: &mut Ctx, log: Log) { 92 | self.outer.log(interp, context, log.clone()); 93 | self.inner.log(interp, context, log); 94 | } 95 | 96 | fn call(&mut self, context: &mut Ctx, inputs: &mut CallInputs) -> Option { 97 | if let Some(outcome) = self.outer.call(context, inputs) { 98 | return Some(outcome); 99 | } 100 | self.inner.call(context, inputs) 101 | } 102 | 103 | fn call_end(&mut self, context: &mut Ctx, inputs: &CallInputs, outcome: &mut CallOutcome) { 104 | self.outer.call_end(context, inputs, outcome); 105 | self.inner.call_end(context, inputs, outcome); 106 | } 107 | 108 | fn create(&mut self, context: &mut Ctx, inputs: &mut CreateInputs) -> Option { 109 | if let Some(outcome) = self.outer.create(context, inputs) { 110 | return Some(outcome); 111 | } 112 | self.inner.create(context, inputs) 113 | } 114 | 115 | fn create_end( 116 | &mut self, 117 | context: &mut Ctx, 118 | inputs: &CreateInputs, 119 | outcome: &mut CreateOutcome, 120 | ) { 121 | self.outer.create_end(context, inputs, outcome); 122 | self.inner.create_end(context, inputs, outcome); 123 | } 124 | 125 | fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { 126 | self.outer.selfdestruct(contract, target, value); 127 | self.inner.selfdestruct(contract, target, value); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/evm/has_block.rs: -------------------------------------------------------------------------------- 1 | use crate::{helpers::Ctx, Block, EvmErrored, HasBlock, Trevm}; 2 | use alloy::primitives::{Address, U256}; 3 | use revm::{ 4 | context::{Block as _, BlockEnv, ContextSetters, ContextTr}, 5 | Database, Inspector, 6 | }; 7 | 8 | impl Trevm 9 | where 10 | Db: Database, 11 | Insp: Inspector>, 12 | TrevmState: HasBlock, 13 | { 14 | /// Get a reference to the current block environment. 15 | pub fn block(&self) -> &BlockEnv { 16 | self.inner.block() 17 | } 18 | 19 | /// Get the current block gas limit. 20 | pub fn block_gas_limit(&self) -> u64 { 21 | self.block().gas_limit() 22 | } 23 | 24 | /// Get the current block number. 25 | pub fn block_number(&self) -> U256 { 26 | self.block().number() 27 | } 28 | 29 | /// Get the current block timestamp. 30 | pub fn block_timestamp(&self) -> U256 { 31 | self.block().timestamp() 32 | } 33 | 34 | /// Get the block beneficiary address. 35 | pub fn beneficiary(&self) -> Address { 36 | self.block().beneficiary() 37 | } 38 | 39 | /// Run a function with the provided block, then restore the previous block. 40 | pub fn with_block(mut self, b: &B, f: F) -> Trevm 41 | where 42 | B: Block, 43 | F: FnOnce(Self) -> Trevm, 44 | NewState: HasBlock, 45 | { 46 | let previous = self.inner.block().clone(); 47 | b.fill_block(&mut self.inner); 48 | 49 | let mut this = f(self); 50 | this.inner.ctx.set_block(previous); 51 | this 52 | } 53 | 54 | /// Run a fallible function with the provided block, then restore the previous block. 55 | pub fn try_with_block( 56 | mut self, 57 | b: &B, 58 | f: F, 59 | ) -> Result, EvmErrored> 60 | where 61 | F: FnOnce(Self) -> Result, EvmErrored>, 62 | B: Block, 63 | NewState: HasBlock, 64 | { 65 | let previous = self.inner.block().clone(); 66 | b.fill_block(&mut self.inner); 67 | 68 | match f(self) { 69 | Ok(mut evm) => { 70 | evm.inner.ctx.set_block(previous); 71 | Ok(evm) 72 | } 73 | Err(mut evm) => { 74 | evm.inner.ctx.set_block(previous); 75 | Err(evm) 76 | } 77 | } 78 | } 79 | } 80 | 81 | // Some code above and documentation is adapted from the revm crate, and is 82 | // reproduced here under the terms of the MIT license. 83 | // 84 | // MIT License 85 | // 86 | // Copyright (c) 2021-2024 draganrakita 87 | // 88 | // Permission is hereby granted, free of charge, to any person obtaining a copy 89 | // of this software and associated documentation files (the "Software"), to deal 90 | // in the Software without restriction, including without limitation the rights 91 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 92 | // copies of the Software, and to permit persons to whom the Software is 93 | // furnished to do so, subject to the following conditions: 94 | // 95 | // The above copyright notice and this permission notice shall be included in all 96 | // copies or substantial portions of the Software. 97 | // 98 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 99 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 100 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 101 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 102 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 103 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 104 | // SOFTWARE. 105 | 106 | // Some code above is reproduced from `reth`. It is reused here under the MIT 107 | // license. 108 | // 109 | // The MIT License (MIT) 110 | // 111 | // Copyright (c) 2022-2024 Reth Contributors 112 | // 113 | // Permission is hereby granted, free of charge, to any person obtaining a copy 114 | // of this software and associated documentation files (the "Software"), to deal 115 | // in the Software without restriction, including without limitation the rights 116 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 117 | // copies of the Software, and to permit persons to whom the Software is 118 | // furnished to do so, subject to the following conditions: 119 | // 120 | // The above copyright notice and this permission notice shall be included in 121 | // all copies or substantial portions of the Software. 122 | // 123 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 124 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 125 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 126 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 127 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 128 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 129 | // THE SOFTWARE. 130 | -------------------------------------------------------------------------------- /src/system/eip7251.rs: -------------------------------------------------------------------------------- 1 | use super::{checked_insert_code, execute_system_tx}; 2 | use crate::{helpers::Ctx, system::SystemTx, EvmNeedsTx}; 3 | use alloy::{ 4 | eips::eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_CODE, 5 | primitives::{Address, Bytes}, 6 | }; 7 | use revm::{ 8 | context::result::EVMError, primitives::hardfork::SpecId, Database, DatabaseCommit, Inspector, 9 | }; 10 | 11 | /// The address for the [EIP-7251] consolidation requests contract 12 | /// 13 | /// [`EIP-7251`]: https://eips.ethereum.org/EIPS/eip-7251 14 | pub use alloy::eips::eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS; 15 | 16 | /// The size of a consolidation request in bytes. 17 | pub const CONSOLIDATION_REQUEST_BYTES: usize = 20 + 48 + 48; 18 | 19 | impl SystemTx { 20 | /// Instantiate a system call for the post-block consolidation requests as 21 | /// specified in [EIP-7251]. 22 | /// 23 | /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 24 | pub const fn eip7251() -> Self { 25 | Self::eip7251_with_target(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS) 26 | } 27 | 28 | /// Instantiate a system call for the post-block consolidation requests as 29 | /// specified in [EIP-7251], with a custom target address. 30 | /// 31 | /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 32 | pub const fn eip7251_with_target(target: Address) -> Self { 33 | Self::new(target, Bytes::new()) 34 | } 35 | 36 | /// Instantiate a system call for the post-block consolidation requests as 37 | /// specified in [EIP-7251], with a custom target address and caller 38 | /// address. 39 | /// 40 | /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 41 | pub const fn eip7251_with_target_and_caller(target: Address, caller: Address) -> Self { 42 | Self::new_with_caller(target, Bytes::new(), caller) 43 | } 44 | } 45 | 46 | impl EvmNeedsTx 47 | where 48 | Db: Database + DatabaseCommit, 49 | Insp: Inspector>, 50 | { 51 | /// Apply a system transaction as specified in [EIP-7251]. The EIP-7251 52 | /// post-block action calls the consolidation request contract to process 53 | /// consolidation requests. 54 | /// 55 | /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 56 | pub fn apply_eip7251(&mut self) -> Result> { 57 | if self.spec_id() < SpecId::PRAGUE { 58 | return Ok(Bytes::new()); 59 | } 60 | 61 | checked_insert_code( 62 | self.inner_mut_unchecked(), 63 | CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, 64 | &CONSOLIDATION_REQUEST_PREDEPLOY_CODE, 65 | )?; 66 | 67 | let res = execute_system_tx(self.inner_mut_unchecked(), &SystemTx::eip7251())?; 68 | 69 | // We make assumptions here: 70 | // - The system transaction never reverts. 71 | // - The system transaction always has an output. 72 | // - The system contract produces correct output. 73 | // - The output is a list of consolidation requests. 74 | // - The output does not contain incomplete requests. 75 | 76 | let Some(output) = res.output() else { 77 | panic!("execution halted during consolidation request system contract execution") 78 | }; 79 | 80 | Ok(output.clone()) 81 | } 82 | } 83 | 84 | #[cfg(test)] 85 | mod test { 86 | use super::*; 87 | use crate::{NoopBlock, NoopCfg, Tx}; 88 | use alloy::{ 89 | consensus::constants::ETH_TO_WEI, 90 | primitives::{fixed_bytes, FixedBytes, TxKind, U256}, 91 | }; 92 | use revm::context::TxEnv; 93 | 94 | const WITHDRAWAL_ADDR: Address = Address::with_last_byte(0x42); 95 | const VALIDATOR_PUBKEY: FixedBytes<48> = fixed_bytes!("111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); 96 | const TARGET_VALIDATOR_PUBKEY: FixedBytes<48> = fixed_bytes!("111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); 97 | 98 | struct ConsolidationTx; 99 | 100 | impl Tx for ConsolidationTx { 101 | fn fill_tx_env(&self, tx_env: &mut TxEnv) { 102 | let input: Bytes = 103 | [&VALIDATOR_PUBKEY[..], &TARGET_VALIDATOR_PUBKEY[..]].concat().into(); 104 | 105 | tx_env.caller = WITHDRAWAL_ADDR; 106 | tx_env.data = input; 107 | tx_env.kind = TxKind::Call(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS); 108 | // `MIN_CONSOLIDATION_REQUEST_FEE` 109 | tx_env.value = U256::from(1); 110 | } 111 | } 112 | 113 | #[test] 114 | fn test_eip7251() { 115 | let mut trevm = crate::test_utils::test_trevm().fill_cfg(&NoopCfg).fill_block(&NoopBlock); 116 | 117 | // insert the code 118 | trevm.apply_eip7251().unwrap(); 119 | 120 | trevm.test_increase_balance(WITHDRAWAL_ADDR, U256::from(100 * ETH_TO_WEI)); 121 | 122 | let mut trevm = trevm.run_tx(&ConsolidationTx).unwrap().accept_state(); 123 | 124 | let requests = trevm.apply_eip7251().unwrap(); 125 | 126 | assert_eq!(&requests[..20], WITHDRAWAL_ADDR.as_slice()); 127 | assert_eq!(&requests[20..68], VALIDATOR_PUBKEY.as_slice()); 128 | assert_eq!(&requests[68..], TARGET_VALIDATOR_PUBKEY.as_slice()); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/system/eip7002.rs: -------------------------------------------------------------------------------- 1 | use super::{checked_insert_code, execute_system_tx}; 2 | use crate::helpers::Ctx; 3 | use crate::{system::SystemTx, EvmNeedsTx}; 4 | use alloy::eips::eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_CODE; 5 | use alloy::primitives::{Address, Bytes}; 6 | use revm::Inspector; 7 | use revm::{context::result::EVMError, primitives::hardfork::SpecId, Database, DatabaseCommit}; 8 | 9 | /// The address for the [EIP-7002] withdrawal requests contract. 10 | /// 11 | /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 12 | pub use alloy::eips::eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS; 13 | 14 | /// The size of a withdrawal request in bytes. 15 | pub const WITHDRAWAL_REQUEST_BYTES: usize = 20 + 48 + 8; 16 | 17 | impl SystemTx { 18 | /// Instantiate a system call for the post-block withdrawal requests as 19 | /// specified in [EIP-7002]. 20 | /// 21 | /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 22 | pub const fn eip7002() -> Self { 23 | Self::eip7002_with_target(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS) 24 | } 25 | 26 | /// Instantiate a system call for the post-block withdrawal requests as 27 | /// specified in [EIP-7002], with a custom target address. 28 | /// 29 | /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 30 | pub const fn eip7002_with_target(target: Address) -> Self { 31 | Self::new(target, Bytes::new()) 32 | } 33 | 34 | /// Instantiate a system call for the post-block withdrawal requests as 35 | /// specified in [EIP-7002], with a custom target address and caller 36 | /// address. 37 | /// 38 | /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 39 | pub const fn eip7002_with_target_and_caller(target: Address, caller: Address) -> Self { 40 | Self::new_with_caller(target, Bytes::new(), caller) 41 | } 42 | } 43 | 44 | impl EvmNeedsTx 45 | where 46 | Db: Database + DatabaseCommit, 47 | Insp: Inspector>, 48 | { 49 | /// Apply a system transaction as specified in [EIP-7002]. The EIP-7002 50 | /// post-block action was introduced in Prague, and calls the withdrawal 51 | /// request contract to accumulate withdrawal requests. 52 | /// 53 | /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 54 | pub fn apply_eip7002(&mut self) -> Result> { 55 | if self.spec_id() < SpecId::PRAGUE { 56 | return Ok(Bytes::new()); 57 | } 58 | 59 | checked_insert_code( 60 | self.inner_mut_unchecked(), 61 | WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, 62 | &WITHDRAWAL_REQUEST_PREDEPLOY_CODE, 63 | )?; 64 | 65 | let res = execute_system_tx(self.inner_mut_unchecked(), &SystemTx::eip7002())?; 66 | 67 | // We make assumptions here: 68 | // - The system transaction never reverts. 69 | // - The system transaction always has an output. 70 | // - The system contract produces correct output. 71 | // - The output is a list of withdrawal requests. 72 | // - The output does not contain incomplete requests. 73 | 74 | let Some(output) = res.output() else { 75 | panic!("execution halted during withdrawal request system contract execution") 76 | }; 77 | 78 | Ok(output.clone()) 79 | } 80 | } 81 | 82 | #[cfg(test)] 83 | mod test { 84 | use super::*; 85 | use alloy::{ 86 | consensus::constants::ETH_TO_WEI, 87 | primitives::{fixed_bytes, FixedBytes, TxKind, U256}, 88 | }; 89 | use revm::context::TxEnv; 90 | 91 | use crate::{NoopBlock, NoopCfg, Tx}; 92 | 93 | const WITHDRAWAL_ADDR: Address = Address::with_last_byte(0x42); 94 | const WITHDRAWAL_AMOUNT: FixedBytes<8> = fixed_bytes!("2222222222222222"); 95 | const VALIDATOR_PUBKEY: FixedBytes<48> = fixed_bytes!("111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); 96 | 97 | struct WithdrawalTx; 98 | 99 | impl Tx for WithdrawalTx { 100 | fn fill_tx_env(&self, tx_env: &mut TxEnv) { 101 | // https://github.com/lightclient/7002asm/blob/e0d68e04d15f25057af7b6d180423d94b6b3bdb3/test/Contract.t.sol.in#L49-L64 102 | let input: Bytes = [&VALIDATOR_PUBKEY[..], &WITHDRAWAL_AMOUNT[..]].concat().into(); 103 | 104 | tx_env.caller = WITHDRAWAL_ADDR; 105 | tx_env.data = input; 106 | tx_env.kind = TxKind::Call(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS); 107 | // `MIN_WITHDRAWAL_REQUEST_FEE` 108 | tx_env.value = U256::from(1); 109 | } 110 | } 111 | 112 | #[test] 113 | fn test_eip7002() { 114 | let mut trevm = crate::test_utils::test_trevm().fill_cfg(&NoopCfg).fill_block(&NoopBlock); 115 | 116 | // insert the code 117 | trevm.apply_eip7002().unwrap(); 118 | 119 | trevm.test_increase_balance(WITHDRAWAL_ADDR, U256::from(100 * ETH_TO_WEI)); 120 | 121 | let mut trevm = trevm.run_tx(&WithdrawalTx).unwrap().accept_state(); 122 | 123 | let requests = trevm.apply_eip7002().unwrap(); 124 | 125 | assert_eq!(requests.len(), WITHDRAWAL_REQUEST_BYTES); 126 | 127 | assert_eq!(&requests[0..20], WITHDRAWAL_ADDR.as_slice()); 128 | assert_eq!(&requests[20..68], VALIDATOR_PUBKEY.as_slice()); 129 | assert_eq!(&requests[68..], WITHDRAWAL_AMOUNT.as_slice()); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/evm/errored.rs: -------------------------------------------------------------------------------- 1 | use crate::{helpers::Ctx, ErroredState, EvmErrored, EvmNeedsTx, NeedsTx, Trevm}; 2 | use revm::{ 3 | context::result::{EVMError, InvalidTransaction}, 4 | Database, Inspector, 5 | }; 6 | 7 | impl EvmErrored 8 | where 9 | Db: Database, 10 | Insp: Inspector>, 11 | { 12 | /// Get a reference to the error. 13 | pub const fn error(&self) -> &E { 14 | &self.state.error 15 | } 16 | 17 | /// Inspect the error with a closure. 18 | pub fn inspect_err(&self, f: F) -> T 19 | where 20 | F: FnOnce(&E) -> T, 21 | { 22 | f(self.error()) 23 | } 24 | 25 | /// Discard the error and return the EVM. 26 | pub fn discard_error(self) -> EvmNeedsTx { 27 | Trevm { inner: self.inner, state: NeedsTx::new() } 28 | } 29 | 30 | /// Convert the error into an [`EVMError`]. 31 | pub fn into_error(self) -> E { 32 | self.state.error 33 | } 34 | 35 | /// Reset the EVM, returning the error and the EVM ready for the next 36 | /// transaction. 37 | pub fn take_err(self) -> (E, EvmNeedsTx) { 38 | let Self { inner, state: ErroredState { error } } = self; 39 | (error, Trevm { inner, state: NeedsTx::new() }) 40 | } 41 | 42 | /// Transform the error into a new error type. 43 | pub fn err_into>(self) -> EvmErrored { 44 | self.map_err(Into::into) 45 | } 46 | 47 | /// Map the error to a new error type. 48 | pub fn map_err(self, f: F) -> EvmErrored 49 | where 50 | F: FnOnce(E) -> NewErr, 51 | { 52 | Trevm { inner: self.inner, state: ErroredState { error: f(self.state.error) } } 53 | } 54 | } 55 | 56 | impl EvmErrored 57 | where 58 | Db: Database, 59 | Insp: Inspector>, 60 | { 61 | /// Check if the error is a transaction error. This is provided as a 62 | /// convenience function for common cases, as Transaction errors should 63 | /// usually be discarded. 64 | pub const fn is_transaction_error(&self) -> bool { 65 | matches!(self.state.error, EVMError::Transaction(_)) 66 | } 67 | 68 | /// Fallible cast to a [`InvalidTransaction`]. 69 | pub const fn as_transaction_error(&self) -> Option<&InvalidTransaction> { 70 | match &self.state.error { 71 | EVMError::Transaction(err) => Some(err), 72 | _ => None, 73 | } 74 | } 75 | 76 | /// Discard the error if it is a transaction error, returning the EVM. If 77 | /// the error is not a transaction error, return self 78 | pub fn discard_transaction_error(self) -> Result, Self> { 79 | if self.is_transaction_error() { 80 | Ok(self.discard_error()) 81 | } else { 82 | Err(self) 83 | } 84 | } 85 | } 86 | 87 | // Some code above and documentation is adapted from the revm crate, and is 88 | // reproduced here under the terms of the MIT license. 89 | // 90 | // MIT License 91 | // 92 | // Copyright (c) 2021-2024 draganrakita 93 | // 94 | // Permission is hereby granted, free of charge, to any person obtaining a copy 95 | // of this software and associated documentation files (the "Software"), to deal 96 | // in the Software without restriction, including without limitation the rights 97 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 98 | // copies of the Software, and to permit persons to whom the Software is 99 | // furnished to do so, subject to the following conditions: 100 | // 101 | // The above copyright notice and this permission notice shall be included in all 102 | // copies or substantial portions of the Software. 103 | // 104 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 105 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 106 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 107 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 108 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 109 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 110 | // SOFTWARE. 111 | 112 | // Some code above is reproduced from `reth`. It is reused here under the MIT 113 | // license. 114 | // 115 | // The MIT License (MIT) 116 | // 117 | // Copyright (c) 2022-2024 Reth Contributors 118 | // 119 | // Permission is hereby granted, free of charge, to any person obtaining a copy 120 | // of this software and associated documentation files (the "Software"), to deal 121 | // in the Software without restriction, including without limitation the rights 122 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 123 | // copies of the Software, and to permit persons to whom the Software is 124 | // furnished to do so, subject to the following conditions: 125 | // 126 | // The above copyright notice and this permission notice shall be included in 127 | // all copies or substantial portions of the Software. 128 | // 129 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 130 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 131 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 132 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 133 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 134 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 135 | // THE SOFTWARE. 136 | -------------------------------------------------------------------------------- /src/system/mod.rs: -------------------------------------------------------------------------------- 1 | //! Helpers for system actions including [EIP-4788], [EIP-6110], [EIP-7002] and 2 | //! [EIP-7251]. 3 | //! 4 | //! System actions are special state changes or smart contract calls made 5 | //! before or after transaction exection. These actions are introduced via 6 | //! hardfork. System actions are sometimes modeled as transactions with special 7 | //! properties (as in [EIP-4788], [EIP-7002] and [EIP-7251]) or as special state 8 | //! changes outside of the transaction lifecycle (as in [EIP-6110]). 9 | //! 10 | //! System transactions are modeled by the [`SystemTx`] struct, which implements 11 | //! the [`Tx`] trait. The system transactions are sent from a special system 12 | //! caller address: [`DEFAULT_SYSTEM_CALLER`]. Note that the system caller is 13 | //! specified independently in each EIP, which allows introduction off 14 | //! different system callers in future EIPs 15 | //! 16 | //! [`Tx`]: crate::Tx 17 | //! 18 | //! [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 19 | //! [EIP-6110]: https://eips.ethereum.org/EIPS/eip-6110 20 | //! [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 21 | //! [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 22 | 23 | mod fill; 24 | pub use fill::{SystemTx, DEFAULT_SYSTEM_CALLER}; 25 | 26 | /// Helpers for Prague historical block hash [EIP-2935] system actions. 27 | /// 28 | /// [EIP-2935]: https://eips.ethereum.org/EIPS/eip-2935 29 | pub mod eip2935; 30 | 31 | /// Helpers for Cancun beacon root [EIP-4788] system actions. 32 | /// 33 | /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 34 | pub mod eip4788; 35 | 36 | /// Helpers for Cancun withdrawal [EIP-4895] system actions. 37 | /// 38 | /// [EIP-4895]: https://eips.ethereum.org/EIPS/eip-4895 39 | pub mod eip4895; 40 | 41 | /// Helpers for Shanghai withdrawal [EIP-6110] system actions. 42 | /// 43 | /// [EIP-6110]: https://eips.ethereum.org/EIPS/eip-6110 44 | pub mod eip6110; 45 | 46 | /// Helpers for Prague withdrawal request [EIP-7002] system actions. 47 | /// 48 | /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 49 | pub mod eip7002; 50 | 51 | /// Helpers for Prague consolidation request [EIP-7251] system actions. 52 | /// 53 | /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 54 | pub mod eip7251; 55 | 56 | /// The maximum blob gas limit for a block in Cancun. 57 | pub const MAX_BLOB_GAS_PER_BLOCK_CANCUN: u64 = 786_432; 58 | 59 | /// The maximum blob gas limit for a block in Prague. 60 | pub const MAX_BLOB_GAS_PER_BLOCK_PRAGUE: u64 = 1_179_648; 61 | 62 | /// The maximum blob gas limit for a block in Osaka, pre BPO-1. 63 | pub const MAX_BLOB_GAS_PER_BLOCK_OSAKA: u64 = 1_749_648; 64 | 65 | use crate::{ 66 | helpers::{Ctx, Evm}, 67 | EvmExtUnchecked, Tx, 68 | }; 69 | use alloy::primitives::{Address, Bytes}; 70 | use revm::{ 71 | bytecode::Bytecode, 72 | context::{ 73 | result::{EVMError, ExecutionResult, ResultAndState}, 74 | Block, ContextTr, Transaction, 75 | }, 76 | primitives::KECCAK_EMPTY, 77 | Database, DatabaseCommit, InspectEvm, Inspector, 78 | }; 79 | 80 | fn checked_insert_code( 81 | evm: &mut Evm, 82 | address: Address, 83 | code: &Bytes, 84 | ) -> Result<(), EVMError> 85 | where 86 | Db: Database + DatabaseCommit, 87 | { 88 | if evm.account(address).map_err(EVMError::Database)?.info.code_hash == KECCAK_EMPTY { 89 | evm.set_bytecode(address, Bytecode::new_raw(code.clone())).map_err(EVMError::Database)?; 90 | } 91 | Ok(()) 92 | } 93 | 94 | /// Clean up the system call, restoring the block env. 95 | fn cleanup_syscall( 96 | evm: &mut Evm, 97 | result: &mut ResultAndState, 98 | syscall: &SystemTx, 99 | old_gas_limit: u64, 100 | old_base_fee: u64, 101 | previous_nonce_check: bool, 102 | ) where 103 | Db: Database + DatabaseCommit, 104 | { 105 | let coinbase = evm.block().beneficiary(); 106 | 107 | // Restore the block environment 108 | evm.modify_block(|block| { 109 | block.gas_limit = old_gas_limit; 110 | block.basefee = old_base_fee; 111 | }); 112 | 113 | // Restore the nonce check 114 | evm.ctx.modify_cfg(|cfg| cfg.disable_nonce_check = previous_nonce_check); 115 | 116 | // Remove the system caller and fees from the state 117 | let state = &mut result.state; 118 | state.remove(&syscall.caller); 119 | state.remove(&coinbase); 120 | } 121 | 122 | /// Apply a system transaction as specified in [EIP-4788], [EIP-7002], or 123 | /// [EIP-7251]. This function will execute the system transaction and apply 124 | /// the result if non-error, cleaning up any extraneous state changes, and 125 | /// restoring the block environment. 126 | /// 127 | /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 128 | /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 129 | /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 130 | pub(crate) fn execute_system_tx( 131 | evm: &mut Evm, 132 | syscall: &SystemTx, 133 | ) -> Result> 134 | where 135 | Db: Database + DatabaseCommit, 136 | Insp: Inspector>, 137 | { 138 | syscall.fill_tx(evm); 139 | 140 | let limit = evm.tx().gas_limit(); 141 | 142 | let block = &mut evm.ctx.block; 143 | 144 | let old_gas_limit = core::mem::replace(&mut block.gas_limit, limit); 145 | let old_base_fee = core::mem::take(&mut block.basefee); 146 | let previous_nonce_check = std::mem::replace(&mut evm.ctx.cfg.disable_nonce_check, true); 147 | 148 | let mut result = evm.inspect_tx(evm.tx().clone())?; 149 | 150 | // Cleanup the syscall. 151 | cleanup_syscall(evm, &mut result, syscall, old_gas_limit, old_base_fee, previous_nonce_check); 152 | 153 | evm.ctx.db_mut().commit(result.state); 154 | 155 | // apply result, remove receipt from block outputs. 156 | Ok(result.result) 157 | } 158 | -------------------------------------------------------------------------------- /src/db/alloy.rs: -------------------------------------------------------------------------------- 1 | use alloy::{ 2 | eips::BlockId, 3 | primitives::{StorageValue, U256}, 4 | providers::{ 5 | network::{primitives::HeaderResponse, BlockResponse}, 6 | Network, Provider, 7 | }, 8 | transports::TransportError, 9 | }; 10 | use core::error::Error; 11 | use revm::{ 12 | database_interface::{async_db::DatabaseAsyncRef, DBErrorMarker}, 13 | primitives::{Address, B256}, 14 | state::{AccountInfo, Bytecode}, 15 | }; 16 | use std::fmt::Display; 17 | 18 | /// A type alias for the storage key used in the database. 19 | /// We use this instead of alloy's [`alloy::primitives::StorageKey`] as Revm requires 20 | /// the actual type to be an [`U256`] instead of a [`B256`]. 21 | pub type StorageKey = U256; 22 | 23 | /// An error that can occur when using [`AlloyDb`]. 24 | #[derive(Debug)] 25 | pub struct DBTransportError(pub TransportError); 26 | 27 | impl DBErrorMarker for DBTransportError {} 28 | 29 | impl Display for DBTransportError { 30 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 31 | write!(f, "Transport error: {}", self.0) 32 | } 33 | } 34 | 35 | impl Error for DBTransportError {} 36 | 37 | impl From for DBTransportError { 38 | fn from(e: TransportError) -> Self { 39 | Self(e) 40 | } 41 | } 42 | 43 | /// An alloy-powered REVM [`Database`][revm::database_interface::Database]. 44 | /// 45 | /// When accessing the database, it'll use the given provider to fetch the corresponding account's data. 46 | #[derive(Debug)] 47 | pub struct AlloyDb> { 48 | /// The provider to fetch the data from. 49 | provider: P, 50 | /// The block number on which the queries will be based on. 51 | block_number: BlockId, 52 | _marker: core::marker::PhantomData N>, 53 | } 54 | 55 | impl> AlloyDb { 56 | /// Creates a new AlloyDB instance, with a [`Provider`] and a block. 57 | pub fn new(provider: P, block_number: BlockId) -> Self { 58 | Self { provider, block_number, _marker: core::marker::PhantomData } 59 | } 60 | 61 | /// Sets the block number on which the queries will be based on. 62 | pub const fn set_block_number(&mut self, block_number: BlockId) { 63 | self.block_number = block_number; 64 | } 65 | } 66 | 67 | impl> DatabaseAsyncRef for AlloyDb { 68 | type Error = DBTransportError; 69 | 70 | async fn basic_async_ref(&self, address: Address) -> Result, Self::Error> { 71 | let nonce = self.provider.get_transaction_count(address).block_id(self.block_number); 72 | let balance = self.provider.get_balance(address).block_id(self.block_number); 73 | let code = self.provider.get_code_at(address).block_id(self.block_number); 74 | 75 | let (nonce, balance, code) = tokio::join!(nonce, balance, code,); 76 | 77 | let balance = balance?; 78 | let code = Bytecode::new_raw(code?.0.into()); 79 | let code_hash = code.hash_slow(); 80 | let nonce = nonce?; 81 | 82 | Ok(Some(AccountInfo::new(balance, nonce, code_hash, code))) 83 | } 84 | 85 | async fn block_hash_async_ref(&self, number: u64) -> Result { 86 | let block = self 87 | .provider 88 | // We know number <= u64::MAX, so we can safely convert it to u64 89 | .get_block_by_number(number.into()) 90 | .await?; 91 | // If the number is given, the block is supposed to be finalized, so unwrapping is safe. 92 | Ok(B256::new(*block.unwrap().header().hash())) 93 | } 94 | 95 | async fn code_by_hash_async_ref(&self, _code_hash: B256) -> Result { 96 | panic!("This should not be called, as the code is already loaded"); 97 | // This is not needed, as the code is already loaded with basic_ref 98 | } 99 | 100 | async fn storage_async_ref( 101 | &self, 102 | address: Address, 103 | index: StorageKey, 104 | ) -> Result { 105 | Ok(self.provider.get_storage_at(address, index).block_id(self.block_number).await?) 106 | } 107 | } 108 | 109 | #[cfg(test)] 110 | mod tests { 111 | use super::*; 112 | use alloy::providers::ProviderBuilder; 113 | use revm::database_interface::{DatabaseRef, WrapDatabaseAsync}; 114 | 115 | #[test] 116 | #[ignore = "flaky RPC"] 117 | fn can_get_basic() { 118 | let client = ProviderBuilder::new().connect_http( 119 | "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27".parse().unwrap(), 120 | ); 121 | let alloydb = AlloyDb::new(client, BlockId::from(16148323)); 122 | let wrapped_alloydb = WrapDatabaseAsync::new(alloydb).unwrap(); 123 | 124 | // ETH/USDT pair on Uniswap V2 125 | let address: Address = "0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852".parse().unwrap(); 126 | 127 | let acc_info = wrapped_alloydb.basic_ref(address).unwrap().unwrap(); 128 | assert!(acc_info.exists()); 129 | } 130 | } 131 | 132 | // This code has been reproduced from the original AlloyDB implementation 133 | // contained in revm. 134 | // 135 | // The original license is included below: 136 | // 137 | // MIT License 138 | // Copyright (c) 2021-2025 draganrakita 139 | // Permission is hereby granted, free of charge, to any person obtaining a copy 140 | // of this software and associated documentation files (the "Software"), to deal 141 | // in the Software without restriction, including without limitation the rights 142 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 143 | // copies of the Software, and to permit persons to whom the Software is 144 | // furnished to do so, subject to the following conditions: 145 | // The above copyright notice and this permission notice shall be included in all 146 | // copies or substantial portions of the Software. 147 | // 148 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 149 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 150 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 151 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 152 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 153 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 154 | // SOFTWARE. 155 | -------------------------------------------------------------------------------- /src/evm/builder.rs: -------------------------------------------------------------------------------- 1 | use crate::{evm::Trevm, helpers::Ctx, EvmNeedsCfg}; 2 | use revm::{ 3 | handler::EthPrecompiles, inspector::NoOpInspector, precompile::Precompiles, 4 | primitives::hardfork::SpecId, Database, Inspector, MainBuilder, 5 | }; 6 | 7 | /// Error that can occur when building a Trevm instance. 8 | #[derive(Debug, Clone, thiserror::Error)] 9 | #[non_exhaustive] 10 | pub enum TrevmBuilderError { 11 | /// Database not set. 12 | #[error("Database not set")] 13 | DatabaseNotSet, 14 | } 15 | 16 | #[allow(unnameable_types)] 17 | #[derive(Debug, Copy, Clone)] 18 | pub struct BuilderNeedsDb { 19 | _private: (), 20 | } 21 | 22 | #[allow(unnameable_types)] 23 | #[derive(Debug, Copy, Clone)] 24 | pub struct BuilderReady { 25 | db: Db, 26 | } 27 | 28 | /// A builder for [`Trevm`] that allows configuring the EVM. 29 | #[derive(Debug, Clone)] 30 | pub struct TrevmBuilder { 31 | pub(crate) insp: Insp, 32 | pub(crate) spec: SpecId, 33 | pub(crate) precompiles: Option<&'static Precompiles>, 34 | pub(crate) state: State, 35 | } 36 | 37 | impl TrevmBuilder { 38 | /// Create a new builder with the default database and inspector. 39 | #[allow(clippy::new_without_default)] // default would make bad devex :( 40 | pub const fn new() -> Self { 41 | Self { 42 | insp: NoOpInspector, 43 | spec: SpecId::PRAGUE, 44 | precompiles: None, 45 | state: BuilderNeedsDb { _private: () }, 46 | } 47 | } 48 | } 49 | 50 | impl TrevmBuilder { 51 | /// Set the database for the EVM. 52 | pub fn with_db(self, db: Odb) -> TrevmBuilder> 53 | where 54 | Odb: Database, 55 | { 56 | TrevmBuilder { 57 | insp: self.insp, 58 | spec: self.spec, 59 | precompiles: self.precompiles, 60 | state: BuilderReady { db }, 61 | } 62 | } 63 | 64 | /// Set the inspector for the EVM. 65 | /// 66 | /// Equivalent to [`Self::with_inspector`]. 67 | pub fn with_insp(self, insp: OInsp) -> TrevmBuilder { 68 | TrevmBuilder { insp, spec: self.spec, precompiles: self.precompiles, state: self.state } 69 | } 70 | 71 | /// Set the inspector for the EVM. 72 | /// 73 | /// Equivalent to [`Self::with_insp`]. 74 | pub fn with_inspector(self, insp: OInsp) -> TrevmBuilder { 75 | self.with_insp(insp) 76 | } 77 | 78 | /// Set the spec id for the EVM. 79 | pub const fn with_spec_id(mut self, spec: SpecId) -> Self { 80 | self.spec = spec; 81 | self 82 | } 83 | 84 | /// Set the precompiles for the EVM. 85 | /// 86 | /// The precompiles must be a static reference to a precompiles instance. 87 | /// If not using a built-in [`Precompiles`], it is generally recommended to 88 | /// use a `OnceLock` to create this borrow. 89 | pub const fn with_precompiles(mut self, precompiles: &'static Precompiles) -> Self { 90 | self.precompiles = Some(precompiles); 91 | self 92 | } 93 | 94 | /// Set the precompiles for the EVM from the current spec id. 95 | pub fn with_precompiles_from_spec(mut self) -> Self { 96 | self.precompiles = Some(Precompiles::new(self.spec.into())); 97 | self 98 | } 99 | } 100 | 101 | impl TrevmBuilder> { 102 | /// Build the Trevm instance. 103 | pub fn build_trevm(self) -> EvmNeedsCfg 104 | where 105 | Db: Database, 106 | Insp: Inspector>, 107 | { 108 | let db = self.state.db; 109 | let ctx = Ctx::new(db, self.spec); 110 | 111 | let mut evm = ctx.build_mainnet_with_inspector(self.insp); 112 | 113 | if let Some(precompiles) = self.precompiles { 114 | evm.precompiles = EthPrecompiles { precompiles, spec: self.spec }; 115 | } 116 | 117 | Trevm::from(evm) 118 | } 119 | } 120 | 121 | // Some code above and documentation is adapted from the revm crate, and is 122 | // reproduced here under the terms of the MIT license. 123 | // 124 | // MIT License 125 | // 126 | // Copyright (c) 2021-2024 draganrakita 127 | // 128 | // Permission is hereby granted, free of charge, to any person obtaining a copy 129 | // of this software and associated documentation files (the "Software"), to deal 130 | // in the Software without restriction, including without limitation the rights 131 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 132 | // copies of the Software, and to permit persons to whom the Software is 133 | // furnished to do so, subject to the following conditions: 134 | // 135 | // The above copyright notice and this permission notice shall be included in all 136 | // copies or substantial portions of the Software. 137 | // 138 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 139 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 140 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 141 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 142 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 143 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 144 | // SOFTWARE. 145 | 146 | // Some code above is reproduced from `reth`. It is reused here under the MIT 147 | // license. 148 | // 149 | // The MIT License (MIT) 150 | // 151 | // Copyright (c) 2022-2024 Reth Contributors 152 | // 153 | // Permission is hereby granted, free of charge, to any person obtaining a copy 154 | // of this software and associated documentation files (the "Software"), to deal 155 | // in the Software without restriction, including without limitation the rights 156 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 157 | // copies of the Software, and to permit persons to whom the Software is 158 | // furnished to do so, subject to the following conditions: 159 | // 160 | // The above copyright notice and this permission notice shall be included in 161 | // all copies or substantial portions of the Software. 162 | // 163 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 164 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 165 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 166 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 167 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 168 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 169 | // THE SOFTWARE. 170 | -------------------------------------------------------------------------------- /src/evm/factory.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | db::DbConnect, helpers::Ctx, Block, Cfg, EvmErrored, EvmNeedsBlock, EvmNeedsCfg, EvmNeedsTx, 3 | EvmReady, EvmTransacted, Tx, 4 | }; 5 | use revm::{ 6 | context::result::{EVMError, ResultAndState}, 7 | Database, Inspector, 8 | }; 9 | use std::format; 10 | 11 | /// Trait for types that can create EVM instances. 12 | /// 13 | /// Factories should contain configuration information like `Insp` types, and 14 | /// database connections. They are intended to enable parallel instantiation 15 | /// of multiple EVMs in multiple threads sharing some configuration or backing 16 | /// store. 17 | /// 18 | /// The lifetime on this trait allows the resulting EVM to borrow from the 19 | /// connector. E.g. the connector may contain some `Db` and the resulting EVM 20 | /// may contain `&Db`. This allows for (e.g.) shared caches between EVMs on 21 | /// multiple threads. 22 | pub trait EvmFactory: DbConnect { 23 | /// The `Insp` type used in the resulting EVM. 24 | /// 25 | /// Recommend using [`NoOpInspector`] for most use cases. 26 | /// 27 | /// [`NoOpInspector`]: revm::inspector::NoOpInspector 28 | type Insp: Sync + Inspector>; 29 | 30 | /// Create a new EVM instance with the given database connection and 31 | /// extension. 32 | fn create(&self) -> Result, Self::Error>; 33 | 34 | /// Create a new EVM instance and parameterize it with a [`Cfg`]. 35 | fn create_with_cfg( 36 | &self, 37 | cfg: &C, 38 | ) -> Result, Self::Error> 39 | where 40 | C: Cfg, 41 | { 42 | self.create().map(|evm| evm.fill_cfg(cfg)) 43 | } 44 | 45 | /// Create a new EVM instance and parameterize it with a [`Cfg`] and a 46 | /// [`Block`]. 47 | fn create_with_block( 48 | &self, 49 | cfg: &C, 50 | block: &B, 51 | ) -> Result, Self::Error> 52 | where 53 | C: Cfg, 54 | B: Block, 55 | { 56 | self.create_with_cfg(cfg).map(|evm| evm.fill_block(block)) 57 | } 58 | 59 | /// Create a new EVM instance, and parameterize it with a [`Cfg`], a 60 | /// [`Block`], and a [`Tx`], yielding an [`EvmReady`]. 61 | fn create_with_tx( 62 | &self, 63 | cfg: &C, 64 | block: &B, 65 | tx: &T, 66 | ) -> Result, Self::Error> 67 | where 68 | C: Cfg, 69 | B: Block, 70 | T: Tx, 71 | { 72 | self.create_with_block(cfg, block).map(|evm| evm.fill_tx(tx)) 73 | } 74 | 75 | /// Create a new EVM instance, parameterize it with a [`Cfg`], a 76 | /// [`Block`], and a [`Tx`], and run the transaction, yielding either 77 | /// [`EvmTransacted`] or [`EvmErrored`]. 78 | #[allow(clippy::type_complexity)] 79 | fn transact( 80 | &self, 81 | cfg: &C, 82 | block: &B, 83 | tx: &T, 84 | ) -> Result< 85 | Result, EvmErrored>, 86 | Self::Error, 87 | > 88 | where 89 | C: Cfg, 90 | B: Block, 91 | T: Tx, 92 | { 93 | let evm = self.create_with_tx(cfg, block, tx)?; 94 | Ok(evm.run()) 95 | } 96 | 97 | /// Run a transaction, take the [`ResultAndState`], and discard the Evm. 98 | /// This is a high-level shortcut function. 99 | fn run( 100 | &self, 101 | cfg: &C, 102 | block: &B, 103 | tx: &T, 104 | ) -> Result::Error>> 105 | where 106 | C: Cfg, 107 | B: Block, 108 | T: Tx, 109 | { 110 | let trevm = self.transact(cfg, block, tx).map_err(|e| EVMError::Custom(format!("{e}")))?; 111 | 112 | match trevm { 113 | Ok(t) => Ok(t.into_result_and_state()), 114 | Err(t) => Err(t.into_error()), 115 | } 116 | } 117 | } 118 | 119 | // Some code above and documentation is adapted from the revm crate, and is 120 | // reproduced here under the terms of the MIT license. 121 | // 122 | // MIT License 123 | // 124 | // Copyright (c) 2021-2024 draganrakita 125 | // 126 | // Permission is hereby granted, free of charge, to any person obtaining a copy 127 | // of this software and associated documentation files (the "Software"), to deal 128 | // in the Software without restriction, including without limitation the rights 129 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 130 | // copies of the Software, and to permit persons to whom the Software is 131 | // furnished to do so, subject to the following conditions: 132 | // 133 | // The above copyright notice and this permission notice shall be included in all 134 | // copies or substantial portions of the Software. 135 | // 136 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 137 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 138 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 139 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 140 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 141 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 142 | // SOFTWARE. 143 | 144 | // Some code above is reproduced from `reth`. It is reused here under the MIT 145 | // license. 146 | // 147 | // The MIT License (MIT) 148 | // 149 | // Copyright (c) 2022-2024 Reth Contributors 150 | // 151 | // Permission is hereby granted, free of charge, to any person obtaining a copy 152 | // of this software and associated documentation files (the "Software"), to deal 153 | // in the Software without restriction, including without limitation the rights 154 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 155 | // copies of the Software, and to permit persons to whom the Software is 156 | // furnished to do so, subject to the following conditions: 157 | // 158 | // The above copyright notice and this permission notice shall be included in 159 | // all copies or substantial portions of the Software. 160 | // 161 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 162 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 163 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 164 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 165 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 166 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 167 | // THE SOFTWARE. 168 | -------------------------------------------------------------------------------- /src/ext.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::{Address, B256, U256}; 2 | use revm::{ 3 | context::{ContextTr, Evm}, 4 | primitives::HashMap, 5 | state::{Account, AccountInfo, Bytecode, EvmState, EvmStorageSlot}, 6 | Database, DatabaseCommit, 7 | }; 8 | 9 | /// Extension trait for [`revm::context::Evm`] with convenience functions for 10 | /// reading and modifying state. 11 | pub trait EvmExtUnchecked { 12 | /// Get a mutable reference to the database. 13 | fn db_mut_ext(&mut self) -> &mut Db; 14 | 15 | /// Load an account from the database. 16 | fn account(&mut self, address: Address) -> Result { 17 | let info = self.db_mut_ext().basic(address)?; 18 | let created = info.is_none(); 19 | let mut acct = Account { info: info.unwrap_or_default(), ..Default::default() }; 20 | if created { 21 | acct.mark_created(); 22 | } 23 | Ok(acct) 24 | } 25 | 26 | /// Load a storage slot from an account. 27 | fn storage(&mut self, address: Address, index: U256) -> Result { 28 | self.db_mut_ext().storage(address, index) 29 | } 30 | 31 | /// Load the bytecode for a contract. Returns empty bytecode if the account 32 | /// does not currently have contract code. 33 | fn bytecode(&mut self, code_hash: B256) -> Result { 34 | self.db_mut_ext().code_by_hash(code_hash) 35 | } 36 | 37 | /// Modify multiple accounts with a closure and commit the modified 38 | /// accounts. 39 | fn modify_accounts( 40 | &mut self, 41 | changes: I, 42 | f: F, 43 | ) -> Result, Db::Error> 44 | where 45 | I: IntoIterator, 46 | F: Fn(&mut AccountInfo), 47 | Db: DatabaseCommit, 48 | { 49 | let mut state = HashMap::default(); 50 | let mut old = HashMap::default(); 51 | for addr in changes.into_iter() { 52 | let mut acct = self.account(addr)?; 53 | old.insert(addr, acct.info.clone()); 54 | f(&mut acct.info); 55 | acct.mark_touch(); 56 | state.insert(addr, acct); 57 | } 58 | self.db_mut_ext().commit(state); 59 | Ok(old) 60 | } 61 | 62 | /// Modify an account with a closure and commit the modified account. 63 | fn modify_account(&mut self, addr: Address, f: F) -> Result 64 | where 65 | F: FnOnce(&mut AccountInfo), 66 | Db: DatabaseCommit, 67 | { 68 | let mut acct = self.account(addr)?; 69 | let old = acct.info.clone(); 70 | f(&mut acct.info); 71 | acct.mark_touch(); 72 | 73 | let changes: EvmState = [(addr, acct)].into_iter().collect(); 74 | self.db_mut_ext().commit(changes); 75 | Ok(old) 76 | } 77 | 78 | /// Set the storage value of an account, returning the previous value. 79 | fn set_storage(&mut self, address: Address, index: U256, value: U256) -> Result 80 | where 81 | Db: DatabaseCommit, 82 | { 83 | let mut acct = self.account(address)?; 84 | let old = self.storage(address, index)?; 85 | 86 | let change = EvmStorageSlot::new_changed(old, value, 0); 87 | acct.storage.insert(index, change); 88 | acct.mark_touch(); 89 | 90 | let changes = [(address, acct)].into_iter().collect(); 91 | self.db_mut_ext().commit(changes); 92 | Ok(old) 93 | } 94 | 95 | /// Set the bytecode for an account, returns the previous bytecode. 96 | fn set_bytecode( 97 | &mut self, 98 | address: Address, 99 | bytecode: Bytecode, 100 | ) -> Result, Db::Error> 101 | where 102 | Db: DatabaseCommit, 103 | { 104 | let mut acct = self.account(address)?; 105 | let old = self.db_mut_ext().code_by_hash(acct.info.code_hash).map(|old| { 106 | if old.is_empty() { 107 | None 108 | } else { 109 | Some(old) 110 | } 111 | })?; 112 | acct.info.code_hash = bytecode.hash_slow(); 113 | acct.info.code = Some(bytecode); 114 | acct.mark_touch(); 115 | 116 | let changes = [(address, acct)].into_iter().collect(); 117 | self.db_mut_ext().commit(changes); 118 | Ok(old) 119 | } 120 | 121 | /// Increase the balance of an account, returning the previous balance. 122 | fn increase_balance(&mut self, address: Address, amount: U256) -> Result 123 | where 124 | Db: DatabaseCommit, 125 | { 126 | self.modify_account(address, |info| info.balance = info.balance.saturating_add(amount)) 127 | .map(|info| info.balance) 128 | } 129 | 130 | /// Decrease the balance of an account, returning the previous balance. 131 | fn decrease_balance(&mut self, address: Address, amount: U256) -> Result 132 | where 133 | Db: DatabaseCommit, 134 | { 135 | self.modify_account(address, |info| info.balance = info.balance.saturating_sub(amount)) 136 | .map(|info| info.balance) 137 | } 138 | 139 | /// Set the balance of an account, returning the previous balance. 140 | fn set_balance(&mut self, address: Address, amount: U256) -> Result 141 | where 142 | Db: DatabaseCommit, 143 | { 144 | self.modify_account(address, |info| info.balance = amount).map(|info| info.balance) 145 | } 146 | 147 | /// Set the nonce of an account, returning the previous nonce. 148 | fn set_nonce(&mut self, address: Address, nonce: u64) -> Result 149 | where 150 | Db: DatabaseCommit, 151 | { 152 | self.modify_account(address, |info| info.nonce = nonce).map(|info| info.nonce) 153 | } 154 | 155 | /// Increment the nonce of an account, returning the new nonce. 156 | fn increment_nonce(&mut self, address: Address) -> Result 157 | where 158 | Db: DatabaseCommit, 159 | { 160 | self.modify_account(address, |info| info.nonce = info.nonce.saturating_add(1)) 161 | .map(|info| info.nonce) 162 | } 163 | 164 | /// Decrement the nonce of an account, returning the new nonce. 165 | fn decrement_nonce(&mut self, address: Address) -> Result 166 | where 167 | Db: DatabaseCommit, 168 | { 169 | self.modify_account(address, |info| info.nonce = info.nonce.saturating_sub(1)) 170 | .map(|info| info.nonce) 171 | } 172 | } 173 | 174 | impl EvmExtUnchecked for Evm 175 | where 176 | Ctx: ContextTr, 177 | { 178 | fn db_mut_ext(&mut self) -> &mut Ctx::Db { 179 | self.ctx.db_mut() 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/evm/need_block.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | db::{StateAcc, TryStateAcc}, 3 | driver::DriveBlockResult, 4 | helpers::Ctx, 5 | Block, BlockDriver, ChainDriver, DriveChainResult, EvmErrored, EvmNeedsBlock, EvmNeedsTx, 6 | }; 7 | use revm::{ 8 | context::ContextTr, 9 | database::{states::bundle_state::BundleRetention, BundleState}, 10 | Database, DatabaseCommit, Inspector, 11 | }; 12 | 13 | impl EvmNeedsBlock 14 | where 15 | Db: Database, 16 | Insp: Inspector>, 17 | { 18 | /// Open a block, apply some logic, and return the EVM ready for the next 19 | /// block. 20 | pub fn drive_block(self, driver: &mut D) -> DriveBlockResult 21 | where 22 | D: BlockDriver, 23 | Db: DatabaseCommit, 24 | { 25 | let trevm = self.fill_block(driver.block()); 26 | let trevm = driver.run_txns(trevm)?; 27 | 28 | let trevm = trevm.close_block(); 29 | 30 | match driver.post_block(&trevm) { 31 | Ok(_) => Ok(trevm), 32 | Err(e) => Err(trevm.errored(e)), 33 | } 34 | } 35 | 36 | /// Drive trevm through a set of blocks. 37 | /// 38 | /// # Panics 39 | /// 40 | /// If the driver contains no blocks. 41 | pub fn drive_chain(self, driver: &mut D) -> DriveChainResult 42 | where 43 | D: ChainDriver, 44 | Db: DatabaseCommit, 45 | { 46 | let block_count = driver.blocks().len(); 47 | 48 | let mut trevm = self 49 | .drive_block(&mut driver.blocks()[0]) 50 | .map_err(EvmErrored::err_into::<>::Error>)?; 51 | 52 | if let Err(e) = driver.interblock(&trevm, 0) { 53 | return Err(trevm.errored(e)); 54 | } 55 | 56 | for i in 1..block_count { 57 | trevm = { 58 | let trevm = trevm 59 | .drive_block(&mut driver.blocks()[i]) 60 | .map_err(EvmErrored::err_into::<>::Error>)?; 61 | if let Err(e) = driver.interblock(&trevm, i) { 62 | return Err(trevm.errored(e)); 63 | } 64 | trevm 65 | }; 66 | } 67 | Ok(trevm) 68 | } 69 | 70 | /// Fill a block and return the EVM ready for a transaction. 71 | /// 72 | /// This does not perform any pre- or post-block logic. To manage block 73 | /// lifecycles, use [`Self::drive_block`] or [`Self::drive_chain`] instead. 74 | pub fn fill_block(mut self, filler: &B) -> EvmNeedsTx { 75 | filler.fill_block(self.inner_mut_unchecked()); 76 | // SAFETY: Same size and repr. Only phantomdata type changes 77 | unsafe { core::mem::transmute(self) } 78 | } 79 | } 80 | 81 | impl EvmNeedsBlock 82 | where 83 | Db: Database + StateAcc, 84 | Insp: Inspector>, 85 | { 86 | /// Finish execution and return the outputs. 87 | /// 88 | /// If the State has not been built with 89 | /// [revm::database::StateBuilder::with_bundle_update] then the returned 90 | /// [`BundleState`] will be meaningless. 91 | /// 92 | /// See [`State::merge_transitions`] and [`State::take_bundle`]. 93 | /// 94 | /// [`State::merge_transitions`]: revm::database::State::merge_transitions 95 | /// [`State::take_bundle`]: revm::database::State::take_bundle 96 | pub fn finish(self) -> BundleState { 97 | let Self { inner: mut evm, .. } = self; 98 | evm.db_mut().merge_transitions(BundleRetention::Reverts); 99 | let bundle = evm.db_mut().take_bundle(); 100 | 101 | bundle 102 | } 103 | } 104 | 105 | impl EvmNeedsBlock 106 | where 107 | Db: Database + TryStateAcc, 108 | Insp: Inspector>, 109 | { 110 | /// Fallibly finish execution and return the outputs. This function is 111 | /// intended to be used by shared states, where mutable access may fail, e. 112 | /// g. an `Arc`. Prefer [`Self::finish`] when available. 113 | /// 114 | /// If the State has not been built with 115 | /// [revm::database::StateBuilder::with_bundle_update] then the returned 116 | /// [`BundleState`] will be meaningless. 117 | /// 118 | /// See [`State::merge_transitions`] and [`State::take_bundle`]. 119 | /// 120 | /// [`State::merge_transitions`]: revm::database::State::merge_transitions 121 | /// [`State::take_bundle`]: revm::database::State::take_bundle 122 | pub fn try_finish( 123 | mut self, 124 | ) -> Result::Error>> { 125 | let db = self.inner.db_mut(); 126 | 127 | trevm_try!(db.try_merge_transitions(BundleRetention::Reverts), self); 128 | 129 | let bundle = trevm_try!(db.try_take_bundle(), self); 130 | 131 | Ok(bundle) 132 | } 133 | } 134 | 135 | // Some code above and documentation is adapted from the revm crate, and is 136 | // reproduced here under the terms of the MIT license. 137 | // 138 | // MIT License 139 | // 140 | // Copyright (c) 2021-2024 draganrakita 141 | // 142 | // Permission is hereby granted, free of charge, to any person obtaining a copy 143 | // of this software and associated documentation files (the "Software"), to deal 144 | // in the Software without restriction, including without limitation the rights 145 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 146 | // copies of the Software, and to permit persons to whom the Software is 147 | // furnished to do so, subject to the following conditions: 148 | // 149 | // The above copyright notice and this permission notice shall be included in all 150 | // copies or substantial portions of the Software. 151 | // 152 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 153 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 154 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 155 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 156 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 157 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 158 | // SOFTWARE. 159 | 160 | // Some code above is reproduced from `reth`. It is reused here under the MIT 161 | // license. 162 | // 163 | // The MIT License (MIT) 164 | // 165 | // Copyright (c) 2022-2024 Reth Contributors 166 | // 167 | // Permission is hereby granted, free of charge, to any person obtaining a copy 168 | // of this software and associated documentation files (the "Software"), to deal 169 | // in the Software without restriction, including without limitation the rights 170 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 171 | // copies of the Software, and to permit persons to whom the Software is 172 | // furnished to do so, subject to the following conditions: 173 | // 174 | // The above copyright notice and this permission notice shall be included in 175 | // all copies or substantial portions of the Software. 176 | // 177 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 178 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 179 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 180 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 181 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 182 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 183 | // THE SOFTWARE. 184 | -------------------------------------------------------------------------------- /src/inspectors/spanning.rs: -------------------------------------------------------------------------------- 1 | use alloy::{consensus::constants::SELECTOR_LEN, hex}; 2 | use revm::{ 3 | context::{ContextTr, LocalContextTr}, 4 | interpreter::{ 5 | CallInput, CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, 6 | InterpreterTypes, 7 | }, 8 | Inspector, 9 | }; 10 | use tracing::{ 11 | debug_span, error_span, info_span, span::EnteredSpan, trace_span, warn_span, Level, Span, 12 | }; 13 | 14 | macro_rules! runtime_level_span { 15 | ($level:expr, $($args:tt)*) => {{ 16 | match $level { 17 | Level::TRACE => trace_span!($($args)*), 18 | Level::DEBUG => debug_span!($($args)*), 19 | Level::INFO => info_span!($($args)*), 20 | Level::WARN => warn_span!($($args)*), 21 | Level::ERROR => error_span!($($args)*), 22 | } 23 | }}; 24 | } 25 | 26 | /// Inspector that creates spans for each call and create operation. 27 | /// 28 | /// This inspector is useful for tracing the execution of the EVM and 29 | /// contextualizing information from other tracing inspectors. It uses 30 | /// [`tracing`] to create spans for each call frame, at a specfied [`Level`], 31 | /// and adds interpreter information to the span. 32 | /// 33 | /// Spans are created at the beginning of each call and create operation, 34 | /// and closed at the end of the operation. The spans are named 35 | /// according to the operation type (call or create) and include 36 | /// the call depth, gas remaining, and other relevant information. 37 | /// 38 | /// # Note on functionality 39 | /// 40 | /// We assume that the EVM execution is synchronous, so [`EnteredSpan`]s will 41 | /// not be held across await points. This means we can simply keep a 42 | /// `Vec` to which push and from which we pop. The first span in 43 | /// the vec will always be our root span, and 1 span will be held for each 44 | /// active callframe. 45 | /// 46 | pub struct SpanningInspector { 47 | active: Vec, 48 | level: Level, 49 | } 50 | 51 | impl core::fmt::Debug for SpanningInspector { 52 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 53 | f.debug_struct("SpanningInspector") 54 | .field("active", &self.active.len()) 55 | .field("level", &self.level) 56 | .finish() 57 | } 58 | } 59 | 60 | impl SpanningInspector { 61 | /// Create a new `SpanningInspector` with the given tracing level. 62 | /// Spans will be created at this level. 63 | pub const fn new(level: Level) -> Self { 64 | Self { active: Vec::new(), level } 65 | } 66 | 67 | /// Create a new `SpanningInspector` producing spans at [`Level::TRACE`]. 68 | pub const fn at_trace() -> Self { 69 | Self::new(Level::TRACE) 70 | } 71 | 72 | /// Create a new `SpanningInspector` producing spans at [`Level::DEBUG`]. 73 | pub const fn at_debug() -> Self { 74 | Self::new(Level::DEBUG) 75 | } 76 | 77 | /// Create a new `SpanningInspector` producing spans at [`Level::WARN`]. 78 | pub const fn at_info() -> Self { 79 | Self::new(Level::INFO) 80 | } 81 | 82 | /// Create a root span 83 | fn root_span(&mut self, _interp: &mut Interpreter, _context: &mut Ctx) -> Span 84 | where 85 | Int: InterpreterTypes, 86 | { 87 | runtime_level_span!( 88 | self.level, 89 | parent: None, // this is the root span :) 90 | "evm_execution", 91 | ) 92 | } 93 | 94 | /// Init the inspector by setting the root span. This should be called only in [`Inspector::initialize_interp`]. 95 | fn init(&mut self, interp: &mut Interpreter, context: &mut Ctx) 96 | where 97 | Int: InterpreterTypes, 98 | { 99 | // just in case 100 | self.active.clear(); 101 | let span = self.root_span(interp, context).entered(); 102 | self.active.push(span); 103 | } 104 | 105 | /// Exit the active span. 106 | fn exit_span(&mut self) { 107 | self.active.pop(); 108 | // If there's only 1 left, it's the root span for the trace, and all 109 | // callframes are closed. We are now done with execution. 110 | if self.active.len() == 1 { 111 | self.active.pop(); 112 | } 113 | } 114 | 115 | /// Create a span for a `CALL`-family opcode. 116 | fn span_call(&self, context: &mut Ctx, inputs: &CallInputs) -> Span 117 | where 118 | Ctx: ContextTr, 119 | { 120 | let selector = resolve_selector(inputs, context).map(hex::encode); 121 | 122 | runtime_level_span!( 123 | self.level, 124 | "call", 125 | input_len = inputs.input.len(), 126 | selector, 127 | gas_limit = inputs.gas_limit, 128 | bytecode_address = %inputs.bytecode_address, 129 | target_addrses = %inputs.target_address, 130 | caller = %inputs.caller, 131 | value = %inputs.value.get(), 132 | scheme = ?inputs.scheme, 133 | ) 134 | } 135 | 136 | /// Create, enter, and store a span for a `CALL`-family opcode. 137 | fn enter_call(&mut self, context: &mut Ctx, inputs: &CallInputs) 138 | where 139 | Ctx: ContextTr, 140 | { 141 | self.active.push(self.span_call(context, inputs).entered()) 142 | } 143 | 144 | /// Create a span for a `CREATE`-family opcode. 145 | fn span_create(&self, _context: &Ctx, inputs: &CreateInputs) -> Span { 146 | runtime_level_span!( 147 | self.level, 148 | "create", 149 | caller = %inputs.caller, 150 | value = %inputs.value, 151 | gas_limit = inputs.gas_limit, 152 | scheme = ?inputs.scheme, 153 | ) 154 | } 155 | 156 | /// Create, enter, and store a span for a `CREATE`-family opcode. 157 | fn enter_create(&mut self, context: &Ctx, inputs: &CreateInputs) { 158 | self.active.push(self.span_create(context, inputs).entered()) 159 | } 160 | } 161 | 162 | impl Inspector for SpanningInspector 163 | where 164 | Int: InterpreterTypes, 165 | Ctx: ContextTr, 166 | { 167 | fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut Ctx) { 168 | self.init(interp, context); 169 | } 170 | 171 | fn call(&mut self, context: &mut Ctx, inputs: &mut CallInputs) -> Option { 172 | self.enter_call(context, inputs); 173 | None 174 | } 175 | 176 | fn call_end(&mut self, _context: &mut Ctx, _inputs: &CallInputs, _outcome: &mut CallOutcome) { 177 | self.exit_span(); 178 | } 179 | 180 | fn create(&mut self, context: &mut Ctx, inputs: &mut CreateInputs) -> Option { 181 | self.enter_create(context, inputs); 182 | None 183 | } 184 | 185 | fn create_end( 186 | &mut self, 187 | _context: &mut Ctx, 188 | _inputs: &CreateInputs, 189 | _outcome: &mut CreateOutcome, 190 | ) { 191 | self.exit_span(); 192 | } 193 | } 194 | 195 | /// Resolve a selector from the [CallInputs]. 196 | fn resolve_selector(inputs: &CallInputs, ctx: &mut impl ContextTr) -> Option<[u8; SELECTOR_LEN]> { 197 | match &inputs.input { 198 | CallInput::SharedBuffer(range) => { 199 | let raw = ctx.local().shared_memory_buffer_slice(range.clone()); 200 | 201 | raw?.get(..SELECTOR_LEN).map(TryInto::try_into).and_then(Result::ok) 202 | } 203 | CallInput::Bytes(bytes) => { 204 | bytes.as_ref().get(..SELECTOR_LEN).map(TryInto::try_into).and_then(Result::ok) 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/db/sync/builder.rs: -------------------------------------------------------------------------------- 1 | use crate::db::sync::ConcurrentState; 2 | use revm::{ 3 | database::{ 4 | states::{BundleState, TransitionState}, 5 | DatabaseRef, EmptyDB, 6 | }, 7 | primitives::B256, 8 | }; 9 | use std::collections::BTreeMap; 10 | 11 | use super::{ConcurrentCacheState, ConcurrentStateInfo}; 12 | 13 | /// Allows building of State and initializing it with different options. 14 | #[derive(Clone, Debug)] 15 | pub struct ConcurrentStateBuilder { 16 | /// Database that we use to fetch data from. 17 | database: Db, 18 | /// Enabled state clear flag that is introduced in Spurious Dragon hardfork. 19 | /// Default is true as spurious dragon happened long time ago. 20 | with_state_clear: bool, 21 | /// if there is prestate that we want to use. 22 | /// This would mean that we have additional state layer between evm and disk/database. 23 | with_bundle_prestate: Option, 24 | /// This will initialize cache to this state. 25 | with_cache_prestate: Option, 26 | /// Do we want to create reverts and update bundle state. 27 | /// Default is false. 28 | with_bundle_update: bool, 29 | /// Do we want to merge transitions in background. 30 | /// This will allows evm to continue executing. 31 | /// Default is false. 32 | with_background_transition_merge: bool, 33 | /// If we want to set different block hashes 34 | with_block_hashes: BTreeMap, 35 | } 36 | 37 | impl ConcurrentStateBuilder { 38 | /// Create a new builder with an empty database. 39 | /// 40 | /// If you want to instantiate it with a specific database, use 41 | /// [`new_with_database`](Self::new_with_database). 42 | pub fn new() -> Self { 43 | Self::default() 44 | } 45 | } 46 | 47 | impl Default for ConcurrentStateBuilder { 48 | fn default() -> Self { 49 | Self::new_with_database(Db::default()) 50 | } 51 | } 52 | 53 | impl ConcurrentStateBuilder { 54 | /// Create a new builder with the given database. 55 | pub const fn new_with_database(database: Db) -> Self { 56 | Self { 57 | database, 58 | with_state_clear: true, 59 | with_cache_prestate: None, 60 | with_bundle_prestate: None, 61 | with_bundle_update: false, 62 | with_background_transition_merge: false, 63 | with_block_hashes: BTreeMap::new(), 64 | } 65 | } 66 | 67 | /// Set the database. 68 | pub fn with_database( 69 | self, 70 | database: ODb, 71 | ) -> ConcurrentStateBuilder { 72 | // cast to the different database, 73 | // Note that we return different type depending of the database NewDBError. 74 | ConcurrentStateBuilder { 75 | with_state_clear: self.with_state_clear, 76 | database, 77 | with_cache_prestate: self.with_cache_prestate, 78 | with_bundle_prestate: self.with_bundle_prestate, 79 | with_bundle_update: self.with_bundle_update, 80 | with_background_transition_merge: self.with_background_transition_merge, 81 | with_block_hashes: self.with_block_hashes, 82 | } 83 | } 84 | 85 | /// Alias for [`Self::with_database`], for revm compat reasons. 86 | pub fn with_database_ref( 87 | self, 88 | database: ODb, 89 | ) -> ConcurrentStateBuilder { 90 | self.with_database(database) 91 | } 92 | 93 | /// By default state clear flag is enabled but for initial sync on mainnet 94 | /// we want to disable it so proper consensus changes are in place. 95 | pub fn without_state_clear(self) -> Self { 96 | Self { with_state_clear: false, ..self } 97 | } 98 | 99 | /// Allows setting prestate that is going to be used for execution. 100 | /// This bundle state will act as additional layer of cache. 101 | /// and State after not finding data inside StateCache will try to find it inside BundleState. 102 | /// 103 | /// On update Bundle state will be changed and updated. 104 | pub fn with_bundle_prestate(self, bundle: BundleState) -> Self { 105 | Self { with_bundle_prestate: Some(bundle), ..self } 106 | } 107 | 108 | /// Make transitions and update bundle state. 109 | /// 110 | /// This is needed option if we want to create reverts 111 | /// and getting output of changed states. 112 | pub fn with_bundle_update(self) -> Self { 113 | Self { with_bundle_update: true, ..self } 114 | } 115 | 116 | /// It will use different cache for the state. If set, it will ignore bundle prestate. 117 | /// and will ignore `without_state_clear` flag as cache contains its own state_clear flag. 118 | /// 119 | /// This is useful for testing. 120 | pub fn with_cached_prestate(self, cache: impl Into) -> Self { 121 | Self { with_cache_prestate: Some(cache.into()), ..self } 122 | } 123 | 124 | /// Starts the thread that will take transitions and do merge to the bundle state 125 | /// in the background. 126 | pub fn with_background_transition_merge(self) -> Self { 127 | Self { with_background_transition_merge: true, ..self } 128 | } 129 | 130 | /// Add block hashes to the state. 131 | pub fn with_block_hashes(self, block_hashes: BTreeMap) -> Self { 132 | Self { with_block_hashes: block_hashes, ..self } 133 | } 134 | 135 | /// Build the concurrent state. 136 | pub fn build(mut self) -> ConcurrentState { 137 | let use_preloaded_bundle = if self.with_cache_prestate.is_some() { 138 | self.with_bundle_prestate = None; 139 | false 140 | } else { 141 | self.with_bundle_prestate.is_some() 142 | }; 143 | ConcurrentState::new( 144 | self.database, 145 | ConcurrentStateInfo { 146 | cache: self 147 | .with_cache_prestate 148 | .unwrap_or_else(|| ConcurrentCacheState::new(self.with_state_clear)), 149 | transition_state: self.with_bundle_update.then(TransitionState::default), 150 | bundle_state: self.with_bundle_prestate.unwrap_or_default(), 151 | use_preloaded_bundle, 152 | block_hashes: self.with_block_hashes.into(), 153 | }, 154 | ) 155 | } 156 | } 157 | 158 | // Some code above and documentation is adapted from the revm crate, and is 159 | // reproduced here under the terms of the MIT license. 160 | // 161 | // MIT License 162 | // 163 | // Copyright (c) 2021-2024 draganrakita 164 | // 165 | // Permission is hereby granted, free of charge, to any person obtaining a copy 166 | // of this software and associated documentation files (the "Software"), to deal 167 | // in the Software without restriction, including without limitation the rights 168 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 169 | // copies of the Software, and to permit persons to whom the Software is 170 | // furnished to do so, subject to the following conditions: 171 | // 172 | // The above copyright notice and this permission notice shall be included in all 173 | // copies or substantial portions of the Software. 174 | // 175 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 176 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 177 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 178 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 179 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 180 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 181 | // SOFTWARE. 182 | -------------------------------------------------------------------------------- /src/db/sync/cache.rs: -------------------------------------------------------------------------------- 1 | //! The majority of this code has been reproduced from revm. 2 | 3 | use alloy::primitives::{Address, B256}; 4 | use dashmap::DashMap; 5 | use revm::{ 6 | database::{ 7 | states::{plain_account::PlainStorage, CacheAccount}, 8 | CacheState, TransitionAccount, 9 | }, 10 | state::{Account, AccountInfo, Bytecode, EvmState}, 11 | }; 12 | 13 | /// A concurrent version of [`revm::database::CacheState`]. 14 | /// 15 | /// Most of the code for this has been reproduced from revm. 16 | #[derive(Debug, Clone)] 17 | pub struct ConcurrentCacheState { 18 | /// Block state account with account state. 19 | pub accounts: DashMap, 20 | /// Created contracts. 21 | // TODO add bytecode counter for number of bytecodes added/removed. 22 | pub contracts: DashMap, 23 | /// Has EIP-161 state clear enabled (Spurious Dragon hardfork). 24 | pub has_state_clear: bool, 25 | } 26 | 27 | impl From for ConcurrentCacheState { 28 | fn from(other: CacheState) -> Self { 29 | Self { 30 | accounts: other.accounts.into_iter().collect(), 31 | contracts: other.contracts.into_iter().collect(), 32 | has_state_clear: other.has_state_clear, 33 | } 34 | } 35 | } 36 | 37 | impl Default for ConcurrentCacheState { 38 | fn default() -> Self { 39 | Self::new(true) 40 | } 41 | } 42 | 43 | impl ConcurrentCacheState { 44 | /// New default state. 45 | pub fn new(has_state_clear: bool) -> Self { 46 | Self { accounts: DashMap::default(), contracts: DashMap::default(), has_state_clear } 47 | } 48 | 49 | /// Absorb other into self, overwriting any existing values. 50 | pub fn absorb(&self, other: Self) { 51 | // NB: the `Extend` trait takes self by `&mut self`, so we have inlined 52 | // it here 53 | for pair in other.accounts.into_iter() { 54 | self.accounts.insert(pair.0, pair.1); 55 | } 56 | for pair in other.contracts.into_iter() { 57 | self.contracts.insert(pair.0, pair.1); 58 | } 59 | } 60 | 61 | /// Set state clear flag. EIP-161. 62 | pub const fn set_state_clear_flag(&mut self, has_state_clear: bool) { 63 | self.has_state_clear = has_state_clear; 64 | } 65 | 66 | /// Insert not existing account. 67 | pub fn insert_not_existing(&self, address: Address) { 68 | self.accounts.insert(address, CacheAccount::new_loaded_not_existing()); 69 | } 70 | 71 | /// Insert Loaded (Or LoadedEmptyEip161 if account is empty) account. 72 | pub fn insert_account(&self, address: Address, info: AccountInfo) { 73 | let account = if !info.is_empty() { 74 | CacheAccount::new_loaded(info, PlainStorage::default()) 75 | } else { 76 | CacheAccount::new_loaded_empty_eip161(PlainStorage::default()) 77 | }; 78 | self.accounts.insert(address, account); 79 | } 80 | 81 | /// Similar to `insert_account` but with storage. 82 | pub fn insert_account_with_storage( 83 | &self, 84 | address: Address, 85 | info: AccountInfo, 86 | storage: PlainStorage, 87 | ) { 88 | let account = if !info.is_empty() { 89 | CacheAccount::new_loaded(info, storage) 90 | } else { 91 | CacheAccount::new_loaded_empty_eip161(storage) 92 | }; 93 | self.accounts.insert(address, account); 94 | } 95 | 96 | /// Apply output of revm execution and create account transitions that are used to build BundleState. 97 | pub fn apply_evm_state(&self, evm_state: EvmState) -> Vec<(Address, TransitionAccount)> { 98 | let mut transitions = Vec::with_capacity(evm_state.len()); 99 | for (address, account) in evm_state { 100 | if let Some(transition) = self.apply_account_state(address, account) { 101 | transitions.push((address, transition)); 102 | } 103 | } 104 | transitions 105 | } 106 | 107 | /// Apply updated account state to the cached account. 108 | /// Returns account transition if applicable. 109 | fn apply_account_state(&self, address: Address, account: Account) -> Option { 110 | // not touched account are never changed. 111 | if !account.is_touched() { 112 | return None; 113 | } 114 | 115 | let mut this_account = 116 | self.accounts.get_mut(&address).expect("All accounts should be present inside cache"); 117 | 118 | // If it is marked as selfdestructed inside revm 119 | // we need to changed state to destroyed. 120 | if account.is_selfdestructed() { 121 | return this_account.selfdestruct(); 122 | } 123 | 124 | let is_created = account.is_created(); 125 | let is_empty = account.is_empty(); 126 | 127 | // transform evm storage to storage with previous value. 128 | let changed_storage = account 129 | .storage 130 | .into_iter() 131 | .filter(|(_, slot)| slot.is_changed()) 132 | .map(|(key, slot)| (key, slot.into())) 133 | .collect(); 134 | 135 | // Note: it can happen that created contract get selfdestructed in same block 136 | // that is why is_created is checked after selfdestructed 137 | // 138 | // Note: Create2 opcode (Petersburg) was after state clear EIP (Spurious Dragon) 139 | // 140 | // Note: It is possibility to create KECCAK_EMPTY contract with some storage 141 | // by just setting storage inside CRATE constructor. Overlap of those contracts 142 | // is not possible because CREATE2 is introduced later. 143 | if is_created { 144 | return Some(this_account.newly_created(account.info, changed_storage)); 145 | } 146 | 147 | // Account is touched, but not selfdestructed or newly created. 148 | // Account can be touched and not changed. 149 | // And when empty account is touched it needs to be removed from database. 150 | // EIP-161 state clear 151 | if is_empty { 152 | if self.has_state_clear { 153 | // touch empty account. 154 | this_account.touch_empty_eip161() 155 | } else { 156 | // if account is empty and state clear is not enabled we should save 157 | // empty account. 158 | this_account.touch_create_pre_eip161(changed_storage) 159 | } 160 | } else { 161 | Some(this_account.change(account.info, changed_storage)) 162 | } 163 | } 164 | } 165 | 166 | // Some code above and documentation is adapted from the revm crate, and is 167 | // reproduced here under the terms of the MIT license. 168 | // 169 | // MIT License 170 | // 171 | // Copyright (c) 2021-2024 draganrakita 172 | // 173 | // Permission is hereby granted, free of charge, to any person obtaining a copy 174 | // of this software and associated documentation files (the "Software"), to deal 175 | // in the Software without restriction, including without limitation the rights 176 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 177 | // copies of the Software, and to permit persons to whom the Software is 178 | // furnished to do so, subject to the following conditions: 179 | // 180 | // The above copyright notice and this permission notice shall be included in all 181 | // copies or substantial portions of the Software. 182 | // 183 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 184 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 185 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 186 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 187 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 188 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 189 | // SOFTWARE. 190 | -------------------------------------------------------------------------------- /src/test_utils.rs: -------------------------------------------------------------------------------- 1 | use crate::{helpers::Ctx, EvmNeedsCfg, Trevm}; 2 | use alloy::{ 3 | primitives::{Address, U256}, 4 | signers::{k256::ecdsa::SigningKey, local::PrivateKeySigner}, 5 | }; 6 | use revm::{ 7 | bytecode::Bytecode, 8 | database::{CacheDB, EmptyDB, InMemoryDB, State}, 9 | inspector::{inspectors::TracerEip3155, NoOpInspector}, 10 | interpreter::{ 11 | CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterTypes, 12 | }, 13 | primitives::{bytes, hardfork::SpecId, Log}, 14 | state::AccountInfo, 15 | Context, Inspector, MainBuilder, 16 | }; 17 | use std::sync::LazyLock; 18 | 19 | /// LogContract bytecode 20 | /// This is the runtime bytecode. This should be set directly with ``set_bytecode_unchecked`` 21 | /// ```ignore 22 | /// contract LogContract { 23 | /// event Hello(); 24 | /// event World(); 25 | /// 26 | /// function emitHello() public { 27 | /// emit Hello(); 28 | /// } 29 | /// 30 | /// function emitWorld() public { 31 | /// emit World(); 32 | /// } 33 | /// } 34 | /// ``` 35 | pub const LOG_DEPLOYED_BYTECODE: alloy::primitives::Bytes = bytes!("6080604052348015600e575f80fd5b50600436106030575f3560e01c80637b3ab2d01460345780639ee1a44014603c575b5f80fd5b603a6044565b005b60426072565b005b7fbcdfe0d5b27dd186282e187525415c57ea3077c34efb39148111e4d342e7ab0e60405160405180910390a1565b7f2d67bb91f17bca05af6764ab411e86f4ddf757adb89fcec59a7d21c525d4171260405160405180910390a156fea2646970667358221220144b313f421e29c7119666392827595d05f3dc33d0ccb0e75314cc9180e4fb1f64736f6c634300081a0033"); 36 | 37 | /// Alice testing signer 38 | pub static ALICE: LazyLock = 39 | LazyLock::new(|| PrivateKeySigner::from(SigningKey::from_slice(&[0x11; 32]).unwrap())); 40 | /// Bob testing signer 41 | pub static BOB: LazyLock = 42 | LazyLock::new(|| PrivateKeySigner::from(SigningKey::from_slice(&[0x22; 32]).unwrap())); 43 | 44 | impl Trevm 45 | where 46 | Insp: Inspector>, 47 | { 48 | /// Modify an account with the provide closure. Returns the original 49 | /// account info 50 | pub fn test_modify_account(&mut self, address: Address, f: F) -> AccountInfo 51 | where 52 | F: FnOnce(&mut AccountInfo), 53 | { 54 | self.modify_account_unchecked(address, f) 55 | } 56 | 57 | /// Set the nonce of an account, returning the previous nonce. 58 | pub fn test_set_nonce(&mut self, address: Address, nonce: u64) -> u64 { 59 | self.set_nonce_unchecked(address, nonce) 60 | } 61 | 62 | /// Increment the nonce of an account, returning the previous nonce. 63 | /// 64 | /// If this would cause the nonce to overflow, the nonce will be set to the 65 | /// maximum value. 66 | pub fn test_increment_nonce(&mut self, address: Address) -> u64 { 67 | self.increment_nonce_unchecked(address) 68 | } 69 | 70 | /// Decrement the nonce of an account, returning the previous nonce. 71 | /// 72 | /// If this would cause the nonce to underflow, the nonce will be set to 73 | /// 0. 74 | pub fn test_decrement_nonce(&mut self, address: Address) -> u64 { 75 | self.decrement_nonce_unchecked(address) 76 | } 77 | 78 | /// Set the EVM storage at a slot. 79 | pub fn test_set_storage(&mut self, address: Address, slot: U256, value: U256) -> U256 { 80 | self.set_storage_unchecked(address, slot, value) 81 | } 82 | 83 | /// Set the bytecode at a specific address, returning the previous bytecode 84 | /// at that address. 85 | pub fn test_set_bytecode(&mut self, address: Address, bytecode: Bytecode) -> Option { 86 | self.set_bytecode_unchecked(address, bytecode) 87 | } 88 | 89 | /// Increase the balance of an account. Returns the previous balance. 90 | /// 91 | /// If this would cause the balance to overflow, the balance will be set 92 | /// to `U256::MAX`. 93 | pub fn test_increase_balance(&mut self, address: Address, amount: U256) -> U256 { 94 | self.increase_balance_unchecked(address, amount) 95 | } 96 | 97 | /// Decrease the balance of an account. Returns the previous balance. 98 | /// 99 | /// If this would cause the balance to underflow, the balance will be set 100 | /// to `U256::ZERO`. 101 | pub fn test_decrease_balance(&mut self, address: Address, amount: U256) -> U256 { 102 | self.decrease_balance_unchecked(address, amount) 103 | } 104 | 105 | /// Set the balance of an account. Returns the previous balance. 106 | pub fn test_set_balance(&mut self, address: Address, amount: U256) -> U256 { 107 | self.set_balance_unchecked(address, amount) 108 | } 109 | } 110 | 111 | /// Make a new [`Trevm`], funding the provided accounts with the given 112 | /// amounts. 113 | pub fn test_trevm_with_funds<'b, I>(i: I) -> EvmNeedsCfg 114 | where 115 | I: IntoIterator, 116 | { 117 | let mut trevm = test_trevm(); 118 | 119 | for (address, amount) in i { 120 | trevm.test_set_balance(*address, *amount); 121 | } 122 | 123 | trevm 124 | } 125 | 126 | /// Make a new [`Trevm`] with an in-memory database. 127 | pub fn test_trevm() -> EvmNeedsCfg { 128 | Trevm::from( 129 | Context::new(CacheDB::new(EmptyDB::default()), SpecId::PRAGUE) 130 | .build_mainnet_with_inspector(NoOpInspector), 131 | ) 132 | } 133 | 134 | /// Make a new [`Trevm`] with an in-memory database wrapped in a [`State`]. 135 | pub fn test_state_trevm() -> EvmNeedsCfg, NoOpInspector> { 136 | Trevm::from( 137 | Context::new(State::builder().with_bundle_update().build(), SpecId::PRAGUE) 138 | .build_mainnet_with_inspector(NoOpInspector), 139 | ) 140 | } 141 | 142 | /// Make a new [`Trevm`] with an in-memory database and a tracer inspector. 143 | /// The tracer will print all EVM instructions to stdout. 144 | pub fn test_trevm_tracing() -> EvmNeedsCfg { 145 | Trevm::from( 146 | Context::new(CacheDB::new(EmptyDB::default()), SpecId::PRAGUE) 147 | .build_mainnet_with_inspector(TracerEip3155::new(Box::new(std::io::stdout()))), 148 | ) 149 | } 150 | 151 | /// Inspector that tracks whether the various inspector methods were 152 | /// called. This is useful for testing the inspector stack. 153 | /// 154 | /// It is not a real inspector, and does not do anything with the 155 | /// information it collects. 156 | #[derive(Default, Debug, Clone, Copy)] 157 | pub struct TestInspector { 158 | /// Whether initialize_interp was called. 159 | pub init_interp: bool, 160 | /// Whether step was called. 161 | pub step: bool, 162 | /// Whether step_end was called. 163 | pub step_end: bool, 164 | /// Whether log was called. 165 | pub log: bool, 166 | /// Whether call was called. 167 | pub call: bool, 168 | /// Whether call_end was called. 169 | pub call_end: bool, 170 | /// Whether create was called. 171 | pub create: bool, 172 | /// Whether create_end was called. 173 | pub create_end: bool, 174 | /// Whether eofcreate was called. 175 | pub eofcreate: bool, 176 | /// Whether eofcreate_end was called. 177 | pub eofcreate_end: bool, 178 | /// Whether selfdestruct was called. 179 | pub selfdestruct: bool, 180 | } 181 | 182 | impl TestInspector { 183 | /// Reset the inspector to its default state. 184 | pub fn reset(&mut self) { 185 | *self = Self::default(); 186 | } 187 | } 188 | 189 | impl Inspector for TestInspector 190 | where 191 | Int: InterpreterTypes, 192 | { 193 | fn initialize_interp(&mut self, _interp: &mut Interpreter, _context: &mut Ctx) { 194 | tracing::info!("initialize_interp"); 195 | self.init_interp = true; 196 | } 197 | 198 | fn step(&mut self, _interp: &mut Interpreter, _context: &mut Ctx) { 199 | tracing::info!("step"); 200 | self.step = true; 201 | } 202 | 203 | fn step_end(&mut self, _interp: &mut Interpreter, _context: &mut Ctx) { 204 | tracing::info!("step_end"); 205 | self.step_end = true; 206 | } 207 | 208 | fn log(&mut self, _interp: &mut Interpreter, _context: &mut Ctx, log: Log) { 209 | tracing::info!(?log, "log"); 210 | self.log = true; 211 | } 212 | 213 | fn call(&mut self, _context: &mut Ctx, inputs: &mut CallInputs) -> Option { 214 | tracing::info!(?inputs, "call"); 215 | self.call = true; 216 | None 217 | } 218 | 219 | fn call_end(&mut self, _context: &mut Ctx, inputs: &CallInputs, outcome: &mut CallOutcome) { 220 | tracing::info!(?inputs, ?outcome, "call_end"); 221 | self.call_end = true; 222 | } 223 | 224 | fn create(&mut self, _context: &mut Ctx, inputs: &mut CreateInputs) -> Option { 225 | tracing::info!(?inputs, "create"); 226 | self.create = true; 227 | None 228 | } 229 | 230 | fn create_end( 231 | &mut self, 232 | _context: &mut Ctx, 233 | _inputs: &CreateInputs, 234 | _outcome: &mut CreateOutcome, 235 | ) { 236 | tracing::info!("create_end"); 237 | self.create_end = true; 238 | } 239 | 240 | fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { 241 | tracing::info!(?contract, ?target, ?value, "selfdestruct"); 242 | self.selfdestruct = true; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/evm/need_tx.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | db::{StateAcc, TryStateAcc}, 3 | helpers::Ctx, 4 | Block, BundleDriver, DriveBundleResult, EvmErrored, EvmNeedsBlock, EvmNeedsTx, EvmReady, 5 | EvmTransacted, Tx, 6 | }; 7 | use alloy::rpc::types::BlockOverrides; 8 | use revm::{ 9 | context::{result::ExecutionResult, ContextTr}, 10 | Database, DatabaseCommit, Inspector, 11 | }; 12 | 13 | impl EvmNeedsTx 14 | where 15 | Db: Database, 16 | Insp: Inspector>, 17 | { 18 | /// Close the current block, returning the EVM ready for the next block. 19 | pub fn close_block(self) -> EvmNeedsBlock { 20 | // SAFETY: Same size and repr. Only phantomdata type changes 21 | unsafe { core::mem::transmute(self) } 22 | } 23 | 24 | /// Drive a bundle to completion, apply some post-bundle logic, and return the 25 | /// EVM ready for the next bundle or tx. 26 | pub fn drive_bundle(self, driver: &mut D) -> DriveBundleResult 27 | where 28 | D: BundleDriver, 29 | Db: DatabaseCommit, 30 | { 31 | let trevm = driver.run_bundle(self)?; 32 | 33 | match driver.post_bundle(&trevm) { 34 | Ok(_) => Ok(trevm), 35 | Err(e) => Err(trevm.errored(e)), 36 | } 37 | } 38 | 39 | /// Fill the transaction environment. 40 | pub fn fill_tx(mut self, filler: &T) -> EvmReady { 41 | filler.fill_tx(&mut self.inner); 42 | // SAFETY: Same size and repr. Only phantomdata type changes 43 | unsafe { core::mem::transmute(self) } 44 | } 45 | 46 | /// Execute a transaction. Shortcut for `fill_tx(tx).run()`. 47 | pub fn run_tx( 48 | self, 49 | filler: &T, 50 | ) -> Result, EvmErrored> { 51 | self.fill_tx(filler).run() 52 | } 53 | 54 | /// Simulate the transaction, and return the [`ExecutionResult`]. The 55 | /// following modifications are made to the environment while simulating. 56 | /// 57 | /// - [EIP-3607] is disabled. 58 | /// - Base fee checks are disabled. 59 | /// - Nonce checks are disabled. 60 | /// 61 | /// [EIP-3607]: https://eips.ethereum.org/EIPS/eip-3607 62 | #[cfg(feature = "call")] 63 | pub fn call_tx( 64 | self, 65 | filler: &T, 66 | ) -> Result<(ExecutionResult, Self), EvmErrored> { 67 | self.fill_tx(filler).call() 68 | } 69 | 70 | /// Estimate the gas cost of a transaction. Shortcut for `fill_tx(tx). 71 | /// estimate()`. Returns an [`EstimationResult`] and the EVM populated with 72 | /// the transaction. 73 | /// 74 | /// [`EstimationResult`]: crate::EstimationResult 75 | #[cfg(feature = "estimate_gas")] 76 | pub fn estimate_tx_gas( 77 | self, 78 | filler: &T, 79 | ) -> Result<(crate::EstimationResult, EvmReady), EvmErrored> { 80 | self.fill_tx(filler).estimate_gas() 81 | } 82 | } 83 | 84 | impl EvmNeedsTx 85 | where 86 | Db: Database + StateAcc, 87 | Insp: Inspector>, 88 | { 89 | /// Apply block overrides to the current block. 90 | /// 91 | /// Note that this is NOT reversible. The overrides are applied directly to 92 | /// the underlying state and these changes cannot be removed. If it is 93 | /// important that you have access to the pre-change state, you should wrap 94 | /// the existing DB in a new [`State`] and apply the overrides to that. 95 | /// 96 | /// [`State`]: revm::database::State 97 | pub fn apply_block_overrides(mut self, overrides: &BlockOverrides) -> Self { 98 | overrides.fill_block(&mut self.inner); 99 | 100 | if let Some(hashes) = overrides.block_hash.as_ref() { 101 | self.inner.db_mut().set_block_hashes(hashes) 102 | } 103 | 104 | self 105 | } 106 | 107 | /// Apply block overrides to the current block, if they are provided. 108 | /// 109 | /// Note that this is NOT reversible. The overrides are applied directly to 110 | /// the underlying state and these changes cannot be removed. If it is 111 | /// important that you have access to the pre-change state, you should wrap 112 | /// the existing DB in a new [`State`] and apply the overrides to that. 113 | /// 114 | /// [`State`]: revm::database::State 115 | pub fn maybe_apply_block_overrides(self, overrides: Option<&BlockOverrides>) -> Self { 116 | if let Some(overrides) = overrides { 117 | self.apply_block_overrides(overrides) 118 | } else { 119 | self 120 | } 121 | } 122 | } 123 | 124 | impl EvmNeedsTx 125 | where 126 | Db: Database + TryStateAcc, 127 | Insp: Inspector>, 128 | { 129 | /// Apply block overrides to the current block. This function is 130 | /// intended to be used by shared states, where mutable access may fail, e. 131 | /// g. an `Arc`. Prefer [`Self::apply_block_overrides`] when 132 | /// available. 133 | /// 134 | /// Note that this is NOT reversible. The overrides are applied directly to 135 | /// the underlying state and these changes cannot be removed. If it is 136 | /// important that you have access to the pre-change state, you should wrap 137 | /// the existing DB in a new [`State`] and apply the overrides to that. 138 | /// 139 | /// [`State`]: revm::database::State 140 | pub fn try_apply_block_overrides( 141 | mut self, 142 | overrides: &BlockOverrides, 143 | ) -> Result::Error>> { 144 | overrides.fill_block(&mut self.inner); 145 | 146 | if let Some(hashes) = overrides.block_hash.as_ref() { 147 | trevm_try!(self.inner.db_mut().try_set_block_hashes(hashes), self); 148 | } 149 | 150 | Ok(self) 151 | } 152 | 153 | /// Apply block overrides to the current block, if they are provided. This 154 | /// function is intended to be used by shared states, where mutable access 155 | /// may fail, e.g. an `Arc`.Prefer 156 | /// [`Self::maybe_apply_block_overrides`] when available. 157 | /// 158 | /// Note that this is NOT reversible. The overrides are applied directly to 159 | /// the underlying state and these changes cannot be removed. If it is 160 | /// important that you have access to the pre-change state, you should wrap 161 | /// the existing DB in a new [`State`] and apply the overrides to that. 162 | /// 163 | /// [`State`]: revm::database::State 164 | pub fn try_maybe_apply_block_overrides( 165 | self, 166 | overrides: Option<&BlockOverrides>, 167 | ) -> Result::Error>> { 168 | if let Some(overrides) = overrides { 169 | self.try_apply_block_overrides(overrides) 170 | } else { 171 | Ok(self) 172 | } 173 | } 174 | } 175 | 176 | // Some code above and documentation is adapted from the revm crate, and is 177 | // reproduced here under the terms of the MIT license. 178 | // 179 | // MIT License 180 | // 181 | // Copyright (c) 2021-2024 draganrakita 182 | // 183 | // Permission is hereby granted, free of charge, to any person obtaining a copy 184 | // of this software and associated documentation files (the "Software"), to deal 185 | // in the Software without restriction, including without limitation the rights 186 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 187 | // copies of the Software, and to permit persons to whom the Software is 188 | // furnished to do so, subject to the following conditions: 189 | // 190 | // The above copyright notice and this permission notice shall be included in all 191 | // copies or substantial portions of the Software. 192 | // 193 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 194 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 195 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 196 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 197 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 198 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 199 | // SOFTWARE. 200 | 201 | // Some code above is reproduced from `reth`. It is reused here under the MIT 202 | // license. 203 | // 204 | // The MIT License (MIT) 205 | // 206 | // Copyright (c) 2022-2024 Reth Contributors 207 | // 208 | // Permission is hereby granted, free of charge, to any person obtaining a copy 209 | // of this software and associated documentation files (the "Software"), to deal 210 | // in the Software without restriction, including without limitation the rights 211 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 212 | // copies of the Software, and to permit persons to whom the Software is 213 | // furnished to do so, subject to the following conditions: 214 | // 215 | // The above copyright notice and this permission notice shall be included in 216 | // all copies or substantial portions of the Software. 217 | // 218 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 219 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 220 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 221 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 222 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 223 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 224 | // THE SOFTWARE. 225 | -------------------------------------------------------------------------------- /src/fill/traits.rs: -------------------------------------------------------------------------------- 1 | use crate::helpers::Ctx; 2 | use revm::{ 3 | context::{BlockEnv, CfgEnv, Evm, TxEnv}, 4 | Database, 5 | }; 6 | use std::sync::Arc; 7 | 8 | /// Types that can fill the EVM transaction environment [`TxEnv`]. 9 | pub trait Tx: Send + Sync { 10 | /// Fill the transaction environment. 11 | /// 12 | /// ## Note: 13 | /// 14 | /// It is generally expected that the filler will fill (or at least 15 | /// inspect) ALL fields of the transaction environment. This is because 16 | /// the EVM will NOT clear the transaction environment between 17 | /// transactions. If the filler does not fill a field, it will be left 18 | /// unchanged from the previous transaction. 19 | fn fill_tx_env(&self, tx_env: &mut TxEnv); 20 | 21 | /// Fill the transaction environment on the EVM. 22 | fn fill_tx( 23 | &self, 24 | evm: &mut Evm, Insp, Inst, Prec, Frame>, 25 | ) where 26 | Self: Sized, 27 | { 28 | evm.ctx.modify_tx(|tx_env| self.fill_tx_env(tx_env)); 29 | } 30 | 31 | /// Create a new [`TxEnv`] filled by this filler. 32 | fn to_tx_env(&self) -> TxEnv { 33 | let mut tx_env = TxEnv::default(); 34 | self.fill_tx_env(&mut tx_env); 35 | tx_env 36 | } 37 | } 38 | 39 | impl Tx for TxEnv { 40 | fn fill_tx_env(&self, tx_env: &mut TxEnv) { 41 | *tx_env = self.clone(); 42 | } 43 | } 44 | 45 | impl Tx for Arc { 46 | fn fill_tx_env(&self, tx_env: &mut TxEnv) { 47 | self.as_ref().fill_tx_env(tx_env); 48 | } 49 | } 50 | 51 | impl Tx for Box { 52 | fn fill_tx_env(&self, tx_env: &mut TxEnv) { 53 | self.as_ref().fill_tx_env(tx_env); 54 | } 55 | } 56 | 57 | impl Tx for T 58 | where 59 | T: Fn(&mut TxEnv) + Send + Sync, 60 | { 61 | fn fill_tx_env(&self, tx_env: &mut TxEnv) { 62 | self(tx_env); 63 | } 64 | } 65 | 66 | /// Types that can fill the EVM block environment [`BlockEnv`]. 67 | pub trait Block: Send + Sync { 68 | /// Fill the block environment. 69 | /// 70 | /// ## Note: 71 | /// 72 | /// It is generally expected that the filler will fill (or at least 73 | /// inspect) ALL fields of the block environment. This is because the EVM 74 | /// will NOT clear the block environment between blocks. If the filler does 75 | /// not fill a field, it will be left unchanged from the previous block. 76 | fn fill_block_env(&self, block_env: &mut BlockEnv); 77 | 78 | /// Fill the block environment on the EVM. 79 | fn fill_block( 80 | &self, 81 | evm: &mut Evm, Insp, Inst, Prec, Frame>, 82 | ) where 83 | Self: Sized, 84 | { 85 | evm.ctx.modify_block(|block_env| self.fill_block_env(block_env)); 86 | } 87 | 88 | /// Get the transaction count hint from the filler. This can be used for 89 | /// memory pre-allocation during block execution. 90 | fn tx_count_hint(&self) -> Option { 91 | None 92 | } 93 | 94 | /// Create a new [`BlockEnv`] filled by this filler. 95 | fn to_block_env(&self) -> BlockEnv { 96 | let mut block_env = BlockEnv::default(); 97 | self.fill_block_env(&mut block_env); 98 | block_env 99 | } 100 | } 101 | 102 | impl Block for T 103 | where 104 | T: Fn(&mut BlockEnv) + Send + Sync, 105 | { 106 | fn fill_block_env(&self, block_env: &mut BlockEnv) { 107 | self(block_env); 108 | } 109 | } 110 | 111 | impl Block for BlockEnv { 112 | fn fill_block_env(&self, block_env: &mut BlockEnv) { 113 | *block_env = self.clone(); 114 | } 115 | } 116 | 117 | impl Block for Arc { 118 | fn fill_block_env(&self, block_env: &mut BlockEnv) { 119 | self.as_ref().fill_block_env(block_env); 120 | } 121 | } 122 | 123 | impl Block for Box { 124 | fn fill_block_env(&self, block_env: &mut BlockEnv) { 125 | self.as_ref().fill_block_env(block_env); 126 | } 127 | } 128 | 129 | /// Types that can fill the EVM configuration environment [`CfgEnv`]. 130 | /// 131 | /// Because the CFG env has quite a few conditionally compiled properties, it 132 | /// is recommended to use the default implementation of `fill_cfg_env` to ensure 133 | /// that fields are filled only when appropriate. 134 | /// 135 | /// Note that the following properties on [`CfgEnv`] are feature-gated: 136 | /// - `kzg_settings` - gated by `c-kzg` 137 | /// - `memory_limit` - gated by `memory_limit` 138 | /// - `disable_balance_check` - gated by `optional_balance_check` 139 | /// - `disable_block_gas_limit` - gated by `optional_block_gas_limit` 140 | /// - `disable_eip3607` - gated by `optional_eip3607` 141 | /// - `disable_gas_refund` - gated by `optional_gas_refund` 142 | /// - `disable_base_fee` - gated by `optional_no_base_fee` 143 | /// - `disable_beneficiary_reward` - gated by `optional_beneficiary_reward` 144 | /// 145 | /// Cfg fillers should consider these feature gates when implementing the trait. 146 | pub trait Cfg: Send + Sync { 147 | /// Fill the configuration environment. 148 | fn fill_cfg_env(&self, cfg_env: &mut CfgEnv); 149 | 150 | /// Fill the configuration environment on the EVM. 151 | fn fill_cfg( 152 | &self, 153 | evm: &mut Evm, Insp, Inst, Prec, Frame>, 154 | ) where 155 | Self: Sized, 156 | { 157 | evm.ctx.modify_cfg(|cfg_env| self.fill_cfg_env(cfg_env)); 158 | } 159 | 160 | /// Create a new [`CfgEnv`] filled by this filler. 161 | fn to_cfg_env(&self) -> CfgEnv { 162 | let mut cfg_env = CfgEnv::default(); 163 | self.fill_cfg_env(&mut cfg_env); 164 | cfg_env 165 | } 166 | } 167 | 168 | impl Cfg for Arc { 169 | fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) { 170 | self.as_ref().fill_cfg_env(cfg_env); 171 | } 172 | } 173 | 174 | impl Cfg for CfgEnv { 175 | fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) { 176 | *cfg_env = self.clone(); 177 | } 178 | } 179 | 180 | impl Cfg for Box { 181 | fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) { 182 | self.as_ref().fill_cfg_env(cfg_env); 183 | } 184 | } 185 | 186 | impl Cfg for T 187 | where 188 | T: Fn(&mut CfgEnv) + Send + Sync, 189 | { 190 | fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) { 191 | self(cfg_env); 192 | } 193 | } 194 | 195 | #[cfg(test)] 196 | mod test { 197 | use alloy::{ 198 | consensus::constants::GWEI_TO_WEI, 199 | primitives::{B256, U256}, 200 | }; 201 | use revm::{ 202 | context::{BlockEnv, CfgEnv}, 203 | context_interface::block::BlobExcessGasAndPrice, 204 | }; 205 | 206 | use super::*; 207 | 208 | #[allow(dead_code)] 209 | fn object_safety(cfg: Box, block: Box, tx: Box) { 210 | crate::test_utils::test_trevm().fill_cfg(&cfg).fill_block(&block).fill_tx(&tx); 211 | 212 | unimplemented!("compilation check only") 213 | } 214 | 215 | impl Block for () { 216 | fn fill_block_env(&self, block_env: &mut BlockEnv) { 217 | let BlockEnv { 218 | number, 219 | beneficiary, 220 | timestamp, 221 | gas_limit, 222 | basefee, 223 | difficulty, 224 | prevrandao, 225 | blob_excess_gas_and_price, 226 | } = block_env; 227 | *number = U256::ONE; 228 | *beneficiary = Default::default(); 229 | *timestamp = U256::from(1720450148u64); // Time when I was writing the test code 230 | *gas_limit = 30_000_000; 231 | *basefee = 5 * GWEI_TO_WEI; 232 | 233 | let diff = B256::repeat_byte(0xab); 234 | *prevrandao = Some(diff); 235 | *difficulty = U256::from_be_bytes(diff.into()); 236 | 237 | *blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new( 238 | 1_000_000, 239 | revm::primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE, 240 | )); 241 | } 242 | 243 | fn tx_count_hint(&self) -> Option { 244 | None 245 | } 246 | } 247 | 248 | impl Cfg for () { 249 | fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) { 250 | let CfgEnv { chain_id, .. } = cfg_env; 251 | *chain_id = 1; 252 | } 253 | } 254 | 255 | // This test exists to ensure that if fields are added to BlockEnv or TxEnv, 256 | // the compiler will emit an error to remind us to update the fillers to 257 | // handle the new fields. The change canary is not present for [`CfgEnv`] 258 | // because it is marked `#[non_exhaustive]`, which prevents compiler errors 259 | // when fields are added. This is moderately annoying. 260 | #[allow(unused_variables)] 261 | fn _change_canary(block: &BlockEnv, tx: &TxEnv) { 262 | let BlockEnv { 263 | number, 264 | beneficiary, 265 | timestamp, 266 | gas_limit, 267 | basefee, 268 | difficulty, 269 | prevrandao, 270 | blob_excess_gas_and_price, 271 | } = block; 272 | 273 | let TxEnv { 274 | tx_type, 275 | caller, 276 | gas_limit, 277 | gas_price, 278 | kind, 279 | value, 280 | data, 281 | nonce, 282 | chain_id, 283 | access_list, 284 | gas_priority_fee, 285 | blob_hashes, 286 | max_fee_per_blob_gas, 287 | authorization_list, 288 | } = tx; 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /src/evm/states.rs: -------------------------------------------------------------------------------- 1 | use crate::{driver::BundleDriver, BlockDriver, ChainDriver, Trevm}; 2 | use revm::{context::result::EVMError, inspector::NoOpInspector, Database}; 3 | use sealed::*; 4 | 5 | /// A [`Trevm`] that requires a [`Cfg`]. 6 | /// 7 | /// Expected continuations include: 8 | /// - [`EvmNeedsCfg::fill_cfg`] 9 | /// 10 | /// [`Cfg`]: crate::Cfg 11 | pub type EvmNeedsCfg = Trevm; 12 | 13 | /// A [`Trevm`] that requires a [`Block`] and contains no 14 | /// outputs. This EVM has not yet executed any transactions or state changes. 15 | /// 16 | /// Expected continuations include: 17 | /// - [`EvmNeedsBlock::fill_block`] 18 | /// - [`EvmNeedsBlock::drive_block`] 19 | /// - [`EvmNeedsBlock::drive_chain`] 20 | /// 21 | /// [`Block`]: crate::Block 22 | pub type EvmNeedsBlock = Trevm; 23 | 24 | /// A [`Trevm`] that requires a [`Tx`]. 25 | /// 26 | /// Expected continuations include: 27 | /// - [`EvmNeedsTx::fill_tx`] 28 | /// - [`EvmNeedsTx::run_tx`] 29 | /// - [`EvmNeedsTx::finish`] 30 | /// 31 | /// [`Tx`]: crate::Tx 32 | pub type EvmNeedsTx = Trevm; 33 | 34 | /// A [`Trevm`] that is ready to execute a transaction. 35 | /// 36 | /// The transaction may be executed with [`EvmReady::run`] or cleared 37 | /// with [`EvmReady::clear_tx`] 38 | pub type EvmReady = Trevm; 39 | 40 | /// A [`Trevm`] that run a transaction, and contains the resulting execution 41 | /// details and state. 42 | /// 43 | /// Expected continuations include: 44 | /// - [`EvmTransacted::reject`] 45 | /// - [`EvmTransacted::accept`] 46 | pub type EvmTransacted = Trevm; 47 | 48 | /// A [`Trevm`] that encountered an error during transaction execution. 49 | /// 50 | /// Expected continuations include: 51 | /// - [`EvmErrored::discard_error`] 52 | /// - [`EvmErrored::into_error`] 53 | pub type EvmErrored::Error>> = 54 | Trevm>; 55 | 56 | /// A [`Trevm`] that encountered an error during [`BlockDriver`] execution. 57 | /// 58 | /// This is an [`EvmErrored`] parameterized with the driver's error type. 59 | pub type EvmBlockDriverErrored = 60 | EvmErrored>::Error>; 61 | 62 | /// A [`Trevm`] that encountered an error during [`ChainDriver`] execution. 63 | /// 64 | /// This is an [`EvmErrored`] parameterized with the driver's error type. 65 | pub type EvmChainDriverErrored = 66 | EvmErrored>::Error>; 67 | 68 | /// A [`Trevm`] that encountered an error during [`BundleDriver`] execution. 69 | /// 70 | /// This is an [`EvmErrored`] parameterized with the driver's error type. 71 | pub type EvmBundleDriverErrored = 72 | EvmErrored>::Error>; 73 | 74 | #[allow(unnameable_types, dead_code, unreachable_pub)] 75 | pub(crate) mod sealed { 76 | use revm::context::result::ResultAndState; 77 | 78 | macro_rules! states { 79 | ($($name:ident),+) => { 80 | $( 81 | 82 | /// A state for the [`Trevm`]. 83 | /// 84 | /// [`Trevm`]: crate::Trevm 85 | #[derive(Debug, Copy, Clone)] 86 | pub struct $name { _private: () } 87 | 88 | impl $name { 89 | /// Create a new state. 90 | pub(crate) const fn new() -> Self { 91 | Self { _private: () } 92 | } 93 | } 94 | 95 | )* 96 | }; 97 | } 98 | 99 | states!(NeedsCfg, NeedsBlock, NeedsTx, Ready); 100 | 101 | /// A state for the [`Trevm`]. 102 | /// 103 | /// [`Trevm`]: crate::Trevm 104 | #[derive(Debug, Clone)] 105 | pub struct TransactedState { 106 | /// The transaction result. 107 | pub result: ResultAndState, 108 | } 109 | 110 | /// A state for the [`Trevm`]. 111 | /// 112 | /// [`Trevm`]: crate::Trevm 113 | #[derive(Debug, Copy, Clone)] 114 | pub struct ErroredState { 115 | /// The transaction error. 116 | pub error: E, 117 | } 118 | 119 | pub struct Seal; 120 | 121 | pub trait HasCfg {} 122 | impl HasCfg for NeedsBlock {} 123 | impl HasCfg for NeedsTx {} 124 | impl HasCfg for TransactedState {} 125 | impl HasCfg for ErroredState {} 126 | impl HasCfg for Ready {} 127 | 128 | pub trait HasBlock: HasCfg {} 129 | impl HasBlock for NeedsTx {} 130 | impl HasBlock for TransactedState {} 131 | impl HasBlock for ErroredState {} 132 | impl HasBlock for Ready {} 133 | 134 | pub trait HasTx: HasBlock + HasCfg {} 135 | impl HasTx for TransactedState {} 136 | impl HasTx for ErroredState {} 137 | impl HasTx for Ready {} 138 | } 139 | 140 | #[macro_export] 141 | /// Declare type aliases for trevm with a concrete `Ext` and `Db` type. 142 | /// 143 | /// This will create aliases with your concrete types for the following types: 144 | /// - [`EvmNeedsCfg`] 145 | /// - [`EvmNeedsBlock`] 146 | /// - [`EvmNeedsTx`] 147 | /// - [`EvmReady`] 148 | /// - [`EvmTransacted`] 149 | /// - [`EvmErrored`] 150 | /// - [`EvmBlockDriverErrored`] 151 | /// - [`EvmChainDriverErrored`] 152 | /// - [`EvmBundleDriverErrored`] 153 | /// 154 | /// ## Basic usage: 155 | /// 156 | /// Invoking with just a DB type will use `()` for the ext 157 | /// 158 | /// ``` 159 | /// use trevm::trevm_aliases; 160 | /// use revm::database::in_memory_db::InMemoryDB; 161 | /// 162 | /// // produces types that look like this: 163 | /// // type EvmNeedsCfg = trevm::EvmNeedsCfg 164 | /// trevm_aliases!(revm::database::in_memory_db::InMemoryDB); 165 | /// ``` 166 | /// 167 | /// Invoking with an ext and DB type will use the provided ext type and the 168 | /// static lifetime: 169 | /// 170 | /// ``` 171 | /// # mod t { 172 | /// # use trevm::trevm_aliases; 173 | /// # use revm::database::in_memory_db::InMemoryDB; 174 | /// # pub struct SomeExtType; 175 | /// // produces types that look like this: 176 | /// // type EvmNeedsCfg = trevm::EvmNeedsCfg 177 | /// trevm_aliases!(revm::database::in_memory_db::InMemoryDB, SomeExtType); 178 | /// # } 179 | /// ``` 180 | macro_rules! trevm_aliases { 181 | ($db:ty) => { 182 | trevm_aliases!((), $db); 183 | }; 184 | 185 | ($db:ty, $insp:ty) => { 186 | #[allow(unused_imports, unreachable_pub, dead_code)] 187 | pub use __aliases::*; 188 | 189 | #[allow(unused_imports, unreachable_pub, dead_code)] 190 | mod __aliases { 191 | use super::*; 192 | // bring these in scope so that doclinks work in generated docs 193 | use $crate::{Block, BlockDriver, ChainDriver, Trevm, Tx}; 194 | 195 | /// A [`Trevm`] that requires a [`Cfg`]. 196 | /// 197 | /// Expected continuations include: 198 | /// - [`Trevm::fill_cfg`] 199 | /// 200 | /// [`Cfg`]: crate::Cfg 201 | /// [`Trevm`]: crate::Trevm 202 | pub type EvmNeedsCfg = $crate::EvmNeedsCfg<$db, $insp>; 203 | 204 | /// A [`Trevm`] that requires a [`Block`]. 205 | /// 206 | /// Expected continuations include: 207 | /// - [`EvmNeedsBlock::open_block`] 208 | /// 209 | /// [`Block`]: crate::Block 210 | pub type EvmNeedsBlock = $crate::EvmNeedsBlock<$db, $insp>; 211 | 212 | /// A [`Trevm`] that requires a [`Tx`]. 213 | // 214 | /// Expected continuations include: 215 | /// - [`EvmNeedsTx::fill_tx`] 216 | /// - [`EvmNeedsTx::execute_tx`] 217 | /// - [`EvmNeedsTx::apply_tx`] 218 | /// - [`EvmNeedsTx::finish`] 219 | /// 220 | /// [`Tx`]: crate::Tx 221 | pub type EvmNeedsTx = $crate::EvmNeedsTx<$db, $insp>; 222 | 223 | /// A [`Trevm`] that is ready to execute a transaction. 224 | /// 225 | /// The transaction may be executed with [`Trevm::execute_tx`] or 226 | /// cleared with [`Trevm::clear_tx`] 227 | pub type EvmReady = $crate::EvmReady<$db, $insp>; 228 | 229 | /// A [`Trevm`] that encountered an error during transaction execution. 230 | /// 231 | /// Expected continuations include: 232 | /// - [`EvmTransacted::reject`] 233 | /// - [`EvmTransacted::accept`] 234 | pub type EvmTransacted = $crate::EvmTransacted<$db, $insp>; 235 | 236 | /// A [`Trevm`] that encountered an error during transaction execution. 237 | /// 238 | /// Expected continuations include: 239 | /// - [`EvmErrored::discard_error`] 240 | /// - [`EvmErrored::into_error`] 241 | pub type EvmErrored = $crate::EvmErrored<$db, $insp, E>; 242 | 243 | /// A [`Trevm`] that encountered an error during [`BlockDriver`] execution. 244 | /// 245 | /// This is an [`EvmErrored`] parameterized with the driver's error type. 246 | pub type EvmBlockDriverErrored = $crate::EvmBlockDriverErrored<$db, $insp, T>; 247 | 248 | /// A [`Trevm`] that encountered an error during [`ChainDriver`] execution. 249 | /// 250 | /// This is an [`EvmErrored`] parameterized with the driver's error type. 251 | pub type EvmChainDriverErrored = $crate::EvmChainDriverErrored<$db, $insp, T>; 252 | 253 | /// A [`Trevm`] that encountered an error during [`BundleDriver`] execution. 254 | /// 255 | /// This is an [`EvmErrored`] parameterized with the driver's error type. 256 | pub type EvmBundleDriverErrored = $crate::EvmBundleDriverErrored<$db, $insp, T>; 257 | } 258 | }; 259 | } 260 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /src/journal/index.rs: -------------------------------------------------------------------------------- 1 | use alloy::primitives::{Address, Sign, B256, I256, U256}; 2 | use revm::{ 3 | bytecode::Bytecode, 4 | database::{states::StorageSlot, AccountStatus, BundleAccount, BundleState}, 5 | primitives::HashMap, 6 | state::AccountInfo, 7 | }; 8 | use std::{borrow::Cow, collections::BTreeMap}; 9 | 10 | /// Outcome of an account info after block execution. 11 | /// 12 | /// Post-6780, accounts cannot be destroyed, only created or modified. In 13 | /// either case, the new and old states are contained in this object. 14 | /// 15 | /// In general, this should not be instantiated directly. Instead, use the 16 | /// [`BundleStateIndex`] to index a [`BundleState`]. 17 | #[derive(Debug, Clone, PartialEq, Eq)] 18 | pub enum InfoOutcome<'a> { 19 | /// Account was created after block execution. 20 | /// 21 | /// Reverting this means deleting the account. 22 | Created(Cow<'a, AccountInfo>), 23 | /// Account was modified after block execution. This object contains the 24 | /// new and previous states. 25 | /// 26 | /// Reverting this means restoring the previous state. 27 | Diff { 28 | /// The original account info before block execution. 29 | old: Cow<'a, AccountInfo>, 30 | /// The updated account info after block execution. 31 | new: Cow<'a, AccountInfo>, 32 | }, 33 | /// Account was destroyed after block execution. Restoring this state means 34 | /// restoring the account. 35 | Destroyed(Cow<'a, AccountInfo>), 36 | } 37 | 38 | impl InfoOutcome<'_> { 39 | /// Get the original account info. This is `None` if the account was 40 | /// created. 41 | pub const fn original(&self) -> Option> { 42 | match self { 43 | Self::Created(_) => None, 44 | Self::Diff { old: Cow::Owned(old), .. } => Some(Cow::Borrowed(old)), 45 | Self::Diff { old: Cow::Borrowed(old), .. } => Some(Cow::Borrowed(*old)), 46 | Self::Destroyed(Cow::Owned(old)) => Some(Cow::Borrowed(old)), 47 | Self::Destroyed(Cow::Borrowed(old)) => Some(Cow::Borrowed(*old)), 48 | } 49 | } 50 | 51 | /// Get the updated account info. This is the account info at the end of 52 | /// block execution. 53 | pub fn updated(&self) -> Cow<'_, AccountInfo> { 54 | match self { 55 | Self::Created(info) => Cow::Borrowed(info), 56 | Self::Diff { new, .. } => Cow::Borrowed(new), 57 | Self::Destroyed(_) => Cow::Owned(Default::default()), 58 | } 59 | } 60 | } 61 | 62 | impl<'a> From<&'a BundleAccount> for InfoOutcome<'a> { 63 | fn from(value: &'a BundleAccount) -> Self { 64 | match (&value.original_info, &value.info) { 65 | (None, Some(new)) => Self::Created(Cow::Borrowed(new)), 66 | (Some(old), Some(new)) => { 67 | Self::Diff { old: Cow::Borrowed(old), new: Cow::Borrowed(new) } 68 | } 69 | (Some(old), None) => { 70 | Self::Destroyed(Cow::Borrowed(old)) 71 | } 72 | _ => unreachable!("revm will never output a bundle account that went from not-existing to not-existing"), 73 | } 74 | } 75 | } 76 | 77 | /// Contains the diff of an account after block execution. 78 | /// 79 | /// This includes the account info and the storage diff. This type ensures that 80 | /// the storage updates are sorted by slot. 81 | /// 82 | /// Reverting this means: 83 | /// - Write the original value for the account info (deleting the account if it 84 | /// was created) 85 | /// - Write the original value for each storage slot 86 | /// 87 | /// In general, this should not be instantiated directly. Instead, use the 88 | /// [`BundleStateIndex`] to index a [`BundleState`]. 89 | #[derive(Debug, Clone, PartialEq, Eq)] 90 | pub struct AcctDiff<'a> { 91 | /// Outcome of an account info after block execution. 92 | pub outcome: InfoOutcome<'a>, 93 | /// The storage diff for the account. This is a map of storage slot to the 94 | /// old and new values 95 | pub storage_diff: BTreeMap>, 96 | } 97 | 98 | impl AcctDiff<'_> { 99 | /// Get the original account info. This is `None` if the account was 100 | /// created. 101 | pub const fn original(&self) -> Option> { 102 | self.outcome.original() 103 | } 104 | 105 | /// Get the updated account info. This is the account info at the end of 106 | /// block execution. 107 | pub fn updated(&self) -> Cow<'_, AccountInfo> { 108 | self.outcome.updated() 109 | } 110 | 111 | /// Get the change in balance for the account. 112 | pub fn balance_change(&self) -> I256 { 113 | let old = self.original().map(|info| info.balance).unwrap_or_default(); 114 | let new = self.updated().balance; 115 | 116 | let abs = core::cmp::max(new, old) - core::cmp::min(new, old); 117 | let sign = if new > old { Sign::Positive } else { Sign::Negative }; 118 | I256::checked_from_sign_and_abs(sign, abs).expect("balance diff overflow") 119 | } 120 | } 121 | 122 | impl<'a> From<&'a BundleAccount> for AcctDiff<'a> { 123 | fn from(value: &'a BundleAccount) -> Self { 124 | let outcome = InfoOutcome::from(value); 125 | let storage_diff = value 126 | .storage 127 | .iter() 128 | .filter(|(_, v)| v.is_changed()) 129 | .map(|(k, v)| (*k, Cow::Borrowed(v))) 130 | .collect(); 131 | AcctDiff { outcome, storage_diff } 132 | } 133 | } 134 | 135 | impl From> for BundleAccount { 136 | fn from(value: AcctDiff<'_>) -> Self { 137 | let original_info = value.outcome.original().map(|info| info.into_owned()); 138 | let info = Some(value.outcome.updated().into_owned()); 139 | let storage = value.storage_diff.into_iter().map(|(k, v)| (k, v.into_owned())).collect(); 140 | 141 | Self { original_info, info, storage, status: AccountStatus::Changed } 142 | } 143 | } 144 | 145 | /// A state index contains the diffs for a single block. The primary purpose of 146 | /// this type is to iterate over the information in a [`BundleState`], making a 147 | /// [`BTreeMap`] containing the changed addresses. This ensures that the 148 | /// state updates are sorted by address, and the bytecodes are sorted by 149 | /// contract address. 150 | /// 151 | /// Reverting this type means reverting 152 | /// - Reverting each account state 153 | /// - Deleting each new contract 154 | /// 155 | /// # Example 156 | /// 157 | /// ``` 158 | /// # use revm::database::BundleState; 159 | /// # use trevm::journal::{BundleStateIndex, JournalEncode, JournalDecode, JournalDecodeError}; 160 | /// # fn make_index(bundle_state: &BundleState) -> Result<(), JournalDecodeError> { 161 | /// let index = BundleStateIndex::from(bundle_state); 162 | /// let serialized_index = index.encoded(); 163 | /// let decoded = BundleStateIndex::decode(&mut serialized_index.as_ref())?; 164 | /// assert_eq!(index, decoded); 165 | /// # Ok(()) 166 | /// # } 167 | /// ``` 168 | #[derive(Debug, Clone, Default, PartialEq, Eq)] 169 | pub struct BundleStateIndex<'a> { 170 | /// The state index contains the account and storage diffs for a single 171 | /// block. 172 | pub state: BTreeMap>, 173 | /// The new contracts created in this block. 174 | pub new_contracts: BTreeMap>, 175 | } 176 | 177 | impl<'a> From<&'a BundleState> for BundleStateIndex<'a> { 178 | fn from(value: &'a BundleState) -> Self { 179 | let state = value 180 | .state 181 | .iter() 182 | .map(|(address, account)| (*address, AcctDiff::from(account))) 183 | .collect(); 184 | 185 | let new_contracts = value.contracts.iter().map(|(k, v)| (*k, Cow::Borrowed(v))).collect(); 186 | BundleStateIndex { state, new_contracts } 187 | } 188 | } 189 | 190 | impl From> for BundleState { 191 | // much of this implementation adapted from revm: 192 | // revm/src/db/states/bundle_state.rs 193 | fn from(value: BundleStateIndex<'_>) -> Self { 194 | let mut state_size = 0; 195 | let state: HashMap<_, _, _> = value 196 | .state 197 | .into_iter() 198 | .map(|(address, info)| { 199 | let original = info.original().map(Cow::into_owned); 200 | let present = Some(info.updated().into_owned()); 201 | 202 | let storage = 203 | info.storage_diff.into_iter().map(|(k, v)| (k, v.into_owned())).collect(); 204 | 205 | let account: BundleAccount = 206 | BundleAccount::new(original, present, storage, AccountStatus::Changed); 207 | 208 | state_size += account.size_hint(); 209 | (address, account) 210 | }) 211 | .collect(); 212 | 213 | let contracts = value.new_contracts.into_iter().map(|(a, c)| (a, c.into_owned())).collect(); 214 | 215 | Self { state, reverts: Default::default(), contracts, state_size, reverts_size: 0 } 216 | } 217 | } 218 | 219 | // Some code above and documentation is adapted from the revm crate, and is 220 | // reproduced here under the terms of the MIT license. 221 | // 222 | // MIT License 223 | // 224 | // Copyright (c) 2021-2024 draganrakita 225 | // 226 | // Permission is hereby granted, free of charge, to any person obtaining a copy 227 | // of this software and associated documentation files (the "Software"), to deal 228 | // in the Software without restriction, including without limitation the rights 229 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 230 | // copies of the Software, and to permit persons to whom the Software is 231 | // furnished to do so, subject to the following conditions: 232 | // 233 | // The above copyright notice and this permission notice shall be included in all 234 | // copies or substantial portions of the Software. 235 | // 236 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 237 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 238 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 239 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 240 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 241 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 242 | // SOFTWARE. 243 | --------------------------------------------------------------------------------