diff --git a/Cargo.toml b/Cargo.toml index d7da10a..8a23a36 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trevm" -version = "0.1.1" +version = "0.2.0" rust-version = "1.79.0" edition = "2021" authors = ["init4"] @@ -30,8 +30,10 @@ redundant-clone = "warn" alloy-consensus = { version = "0.2", features = ["k256"] } alloy-eips = "0.2.0" alloy-primitives = "0.7.6" +alloy-rpc-types-eth = "0.2.0" alloy-sol-types = "0.7.7" revm = { version = "12.0.0", default-features = false, features = ["std"] } +thiserror = "1.0.63" zenith-types = "0.2.2" [dev-dependencies] diff --git a/assets/states.png b/assets/states.png index a5278d7..fc4bcc8 100644 Binary files a/assets/states.png and b/assets/states.png differ diff --git a/src/driver/alloy.rs b/src/driver/alloy.rs new file mode 100644 index 0000000..f5fbf66 --- /dev/null +++ b/src/driver/alloy.rs @@ -0,0 +1,101 @@ +use crate::{driver::RunTxResult, Block, BlockDriver, EvmNeedsTx, Shanghai}; +use alloy_consensus::TxEnvelope; +use alloy_primitives::U256; +use revm::{ + primitives::{BlobExcessGasAndPrice, BlockEnv, EVMError}, + Database, DatabaseCommit, +}; + +impl<'a> From<&'a alloy_rpc_types_eth::Block> for Shanghai<'a> { + fn from(block: &'a alloy_rpc_types_eth::Block) -> Self { + block.withdrawals.as_ref().map(|s| Shanghai::new(s)).unwrap_or_default() + } +} + +impl Block for alloy_rpc_types_eth::Header { + fn fill_block_env(&self, block_env: &mut revm::primitives::BlockEnv) { + let BlockEnv { + number, + coinbase, + timestamp, + gas_limit, + basefee, + difficulty, + prevrandao, + blob_excess_gas_and_price, + } = block_env; + *number = U256::from(self.number.unwrap_or_default()); + *coinbase = self.miner; + *timestamp = U256::from(self.timestamp); + *gas_limit = U256::from(self.gas_limit); + *basefee = U256::from(self.base_fee_per_gas.unwrap_or_default()); + *difficulty = U256::from(self.difficulty); + *prevrandao = self.mix_hash; + *blob_excess_gas_and_price = + self.blob_gas_used.map(|ebg| BlobExcessGasAndPrice::new(ebg as u64)); + } +} + +/// Error during Ethereum consensus checks. +#[derive(thiserror::Error)] +pub enum AlloyBlockError { + /// An error occurred in the EVM. + #[error("EVM error")] + EvmError(revm::primitives::EVMError), + + /// Block contained only tx hashes, without transactions + #[error("Block contained only tx hashes, without transactions")] + MissingTransactions, +} + +impl std::fmt::Debug for AlloyBlockError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::EvmError(_) => f.debug_struct("EvmError").finish_non_exhaustive(), + Self::MissingTransactions => f.write_str("MissingTransactions"), + } + } +} + +impl From> for AlloyBlockError { + fn from(e: EVMError) -> Self { + Self::EvmError(e) + } +} + +impl<'b> BlockDriver<'b, Shanghai<'b>> for alloy_rpc_types_eth::Block { + type Block = alloy_rpc_types_eth::Header; + + // TODO: Implement this + type Error = AlloyBlockError; + + fn block(&self) -> &Self::Block { + &self.header + } + + fn context(&'b self) -> Shanghai<'b> { + self.withdrawals.as_ref().map(|w| Shanghai::new(w.as_slice())).unwrap_or_default() + } + + fn run_txns<'a, Ext, Db: Database + DatabaseCommit>( + &self, + mut trevm: EvmNeedsTx<'a, Ext, Db, Shanghai<'b>>, + ) -> RunTxResult<'a, 'b, Ext, Db, Shanghai<'b>, Self> { + if !self.transactions.is_full() { + return Err(trevm.errored(AlloyBlockError::MissingTransactions)); + } + + for transaction in self.transactions.txns() { + trevm = trevm.run_tx(transaction).map_err(|e| e.err_into())?.accept(); + } + Ok(trevm) + } + + fn post_block_checks( + &self, + _trevm: &crate::EvmBlockComplete<'_, Ext, Db, Shanghai<'b>>, + ) -> Result<(), Self::Error> { + // TODO + Ok(()) + } +} diff --git a/src/driver/block.rs b/src/driver/block.rs new file mode 100644 index 0000000..7da684e --- /dev/null +++ b/src/driver/block.rs @@ -0,0 +1,44 @@ +use crate::{Block, BlockContext, EvmBlockComplete, EvmBlockDriverErrored, EvmNeedsTx}; +use revm::{primitives::EVMError, Database, DatabaseCommit}; + +/// The result of running transactions for a block driver. +pub type RunTxResult<'a, 'b, Ext, Db, C, T> = + Result, EvmBlockDriverErrored<'a, 'b, Ext, Db, C, T>>; + +/// The result of driving a block to completion. +pub type DriveBlockResult<'a, 'b, Ext, Db, C, T> = + Result, EvmBlockDriverErrored<'a, 'b, Ext, Db, C, T>>; + +/// Driver for a single trevm block. This trait allows a type to specify the +/// entire lifecycle of a trevm block, from opening the block to driving the +/// trevm to completion. +pub trait BlockDriver<'b, C: BlockContext> +where + Self: 'b, +{ + /// The [`Block`] filler for this driver. + type Block: Block; + + /// An error type for this driver. + type Error: std::error::Error + + From> + + From<::Error>; + + /// Get a reference to the block filler for this driver. + fn block(&self) -> &Self::Block; + + /// Get the context for this block. + fn context(&'b self) -> C; + + /// Run the transactions for the block. + fn run_txns<'a, Ext, Db: Database + DatabaseCommit>( + &self, + trevm: EvmNeedsTx<'a, Ext, Db, C>, + ) -> RunTxResult<'a, 'b, Ext, Db, C, Self>; + + /// Run post + fn post_block_checks( + &self, + trevm: &EvmBlockComplete<'_, Ext, Db, C>, + ) -> Result<(), Self::Error>; +} diff --git a/src/driver/chain.rs b/src/driver/chain.rs new file mode 100644 index 0000000..ac4374f --- /dev/null +++ b/src/driver/chain.rs @@ -0,0 +1,38 @@ +use crate::{BlockContext, BlockDriver, EvmBlockComplete, EvmChainDriverErrored, EvmNeedsBlock}; +use revm::{ + primitives::{EVMError, SpecId}, + Database, DatabaseCommit, +}; + +/// The result of driving a chain to completion. +pub type DriveChainResult<'a, 'b, Ext, Db, C, D> = + Result<(Vec, EvmNeedsBlock<'a, Ext, Db>), EvmChainDriverErrored<'a, 'b, Ext, Db, C, D>>; + +/// Driver for a chain of blocks. +pub trait ChainDriver<'b, C: BlockContext> { + /// The block driver for this chain. + type BlockDriver: BlockDriver<'b, C>; + + /// An error type for this driver. + type Error: std::error::Error + + From> + + From<::Error> + + From<>::Error>; + + /// Get the spec id for a block. + fn spec_id_for(&self, block: &>::Block) -> SpecId; + + /// Get the blocks in this chain. The blocks should be in order, and this + /// function MUST NOT return an empty slice. + fn blocks(&self) -> &[Self::BlockDriver]; + + /// Checks that run between blocks, e.g. 1559 base fee calculation, + /// or parent-child relationships. + /// + /// The `idx` parameter is the index of the block in the chain. + fn check_interblock( + &self, + trevm: &EvmBlockComplete<'_, Ext, Db, C>, + idx: usize, + ) -> Result<(), Self::Error>; +} diff --git a/src/driver/mod.rs b/src/driver/mod.rs new file mode 100644 index 0000000..8f466e8 --- /dev/null +++ b/src/driver/mod.rs @@ -0,0 +1,8 @@ +mod alloy; +pub use alloy::AlloyBlockError; + +mod block; +pub use block::{BlockDriver, DriveBlockResult, RunTxResult}; + +mod chain; +pub use chain::{ChainDriver, DriveChainResult}; diff --git a/src/evm.rs b/src/evm.rs index 0816f8d..b6d480f 100644 --- a/src/evm.rs +++ b/src/evm.rs @@ -1,8 +1,8 @@ use crate::{ - states::EvmBlockComplete, BasicContext, Block, BlockComplete, BlockContext, Cfg, ErroredState, - EvmErrored, EvmExtUnchecked, EvmNeedsCfg, EvmNeedsFirstBlock, EvmNeedsNextBlock, EvmNeedsTx, - EvmReady, EvmTransacted, HasBlock, HasCfg, HasContext, HasOutputs, HasTx, NeedsBlock, NeedsCfg, - NeedsNextBlock, NeedsTx, Ready, TransactedState, Tx, + driver::DriveBlockResult, BasicContext, Block, BlockComplete, BlockContext, BlockDriver, Cfg, + ChainDriver, DriveChainResult, ErroredState, EvmBlockComplete, EvmErrored, EvmExtUnchecked, + EvmNeedsBlock, EvmNeedsCfg, EvmNeedsTx, EvmReady, EvmTransacted, HasBlock, HasCfg, HasContext, + HasTx, NeedsBlock, NeedsCfg, NeedsTx, Ready, TransactedState, Tx, }; use alloy_primitives::{Address, Bytes, U256}; use revm::{ @@ -43,6 +43,8 @@ impl<'a, Ext, Db: Database + DatabaseCommit> From> for EvmNeeds } } +// --- ALL STATES + impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, Db, TrevmState> { /// Get a reference to the current [`Evm`]. pub fn inner(&self) -> &Evm<'a, Ext, Db> { @@ -67,6 +69,30 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, Db, Trev self.inner.spec_id() } + /// Set the [SpecId], modifying the EVM handlers accordingly. This function + /// should be called at hardfork boundaries when running multi-block trevm + /// flows. + pub fn set_spec_id(&mut self, spec_id: SpecId) { + self.inner.modify_spec_id(spec_id) + } + + /// Run a closure with a different [SpecId], then restore the previous + /// setting. + pub fn with_spec_id( + mut self, + spec_id: SpecId, + f: F, + ) -> Trevm<'a, Ext, Db, NewState> + where + F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>, + { + let old = self.spec_id(); + self.set_spec_id(spec_id); + let mut this = f(self); + this.set_spec_id(old); + this + } + /// Get the current account info for a specific address. /// /// Note: due to revm's DB model, this requires a mutable pointer. @@ -411,7 +437,9 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> } } -impl<'a, Ext, Db: Database + DatabaseCommit, EvmState> Trevm<'a, Ext, State, EvmState> { +// --- ALL STATES, WITH STATE + +impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, State, TrevmState> { /// Set the [EIP-161] state clear flag, activated in the Spurious Dragon /// hardfork. pub fn set_state_clear_flag(&mut self, flag: bool) { @@ -423,7 +451,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, EvmState> Trevm<'a, Ext, State, impl<'a, Ext, Db: Database + DatabaseCommit> EvmNeedsCfg<'a, Ext, Db> { /// Fill the configuration environment. - pub fn fill_cfg(mut self, filler: &T) -> EvmNeedsFirstBlock<'a, Ext, Db> { + pub fn fill_cfg(mut self, filler: &T) -> EvmNeedsBlock<'a, Ext, Db> { filler.fill_cfg(&mut self.inner); // SAFETY: Same size and repr. Only phantomdata type changes unsafe { std::mem::transmute(self) } @@ -715,9 +743,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: HasCfg> Trevm<'a, Ext, // --- NEEDS BLOCK -impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: NeedsBlock> - Trevm<'a, Ext, Db, TrevmState> -{ +impl<'a, Ext, Db: Database + DatabaseCommit> EvmNeedsBlock<'a, Ext, Db> { /// Open a block, apply some logic, and return the EVM ready for the next /// transaction. /// @@ -733,7 +759,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: NeedsBlock> ) -> Result, EvmErrored<'a, Ext, Db, C>> where B: Block, - C: BlockContext, + C: BlockContext, Db: Database + DatabaseCommit, { let res = context.open_block(self.inner_mut_unchecked(), filler); @@ -744,6 +770,67 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: NeedsBlock> } } + /// Open a block, apply some logic, and return the EVM ready for the next + /// block. + pub fn drive_block<'b, B, C>(self, driver: &'b B) -> DriveBlockResult<'a, 'b, Ext, Db, C, B> + where + C: BlockContext, + B: BlockDriver<'b, C>, + { + let trevm = self + .open_block(driver.block(), driver.context()) + .map_err(EvmErrored::err_into::<>::Error>)?; + let trevm = driver.run_txns(trevm)?; + + let trevm = trevm.close_block().map_err(EvmErrored::err_into)?; + + match driver.post_block_checks(&trevm) { + Ok(_) => Ok(trevm), + Err(e) => Err(trevm.errored(e)), + } + } + + /// Drive trevm through a set of blocks, returning the block contexts and + /// the EVM ready for the next block. + /// + /// # Panics + /// + /// If the driver contains no blocks. + pub fn drive_chain<'b, D, C>(self, driver: &'b mut D) -> DriveChainResult<'a, 'b, Ext, Db, C, D> + where + D: ChainDriver<'b, C>, + C: BlockContext, + { + let block_count = driver.blocks().len(); + let mut contexts = Vec::with_capacity(block_count); + + let trevm = self + .drive_block(&driver.blocks()[0]) + .map_err(EvmErrored::err_into::<>::Error>)?; + + if let Err(e) = driver.check_interblock(&trevm, 0) { + return Err(trevm.errored(e)); + } + + let (context, mut trevm) = trevm.take_context(); + contexts.push(context); + + for i in 1..block_count { + let context; + (context, trevm) = { + let trevm = trevm + .drive_block(&driver.blocks()[i]) + .map_err(EvmErrored::err_into::<>::Error>)?; + if let Err(e) = driver.check_interblock(&trevm, i) { + return Err(trevm.errored(e)); + } + trevm.take_context() + }; + contexts.push(context); + } + Ok((contexts, trevm)) + } + /// Open a block and return the EVM ready for the next transaction. This is /// a convenience API and uses [`BasicContext`] for the block context. /// @@ -776,11 +863,9 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: HasBlock> Trevm<'a, Ext } } -// --- HAS OUTPUTS +// --- Needs Block with State -impl<'a, Ext, Db: Database + DatabaseCommit, Missing: HasOutputs> - Trevm<'a, Ext, State, Missing> -{ +impl<'a, Ext, Db: Database + DatabaseCommit> EvmNeedsBlock<'a, Ext, State> { /// Finish execution and return the outputs. /// /// ## Panics @@ -800,17 +885,18 @@ impl<'a, Ext, Db: Database + DatabaseCommit, Missing: HasOutputs> bundle } } + // --- BLOCK COMPLETE impl<'a, Ext, Db: Database + DatabaseCommit, C> EvmBlockComplete<'a, Ext, Db, C> { /// Destructure the EVM and return the block context and the EVM ready for /// the next block. - pub fn take_context(self) -> (C, EvmNeedsNextBlock<'a, Ext, Db>) { - (self.state.0, EvmNeedsNextBlock { inner: self.inner, state: NeedsNextBlock::new() }) + pub fn take_context(self) -> (C, EvmNeedsBlock<'a, Ext, Db>) { + (self.state.0, EvmNeedsBlock { inner: self.inner, state: NeedsBlock::new() }) } /// Discard the block context and return the EVM ready for the next block. - pub fn discard_context(self) -> EvmNeedsNextBlock<'a, Ext, Db> { + pub fn discard_context(self) -> EvmNeedsBlock<'a, Ext, Db> { self.take_context().1 } } @@ -843,11 +929,25 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: HasContext> f(&mut state, &mut inner); Trevm { inner, state } } + + /// Convert self into [`EvmErrored`] by supplying an error + pub fn errored( + self, + error: E, + ) -> EvmErrored<'a, Ext, Db, ::Context, E> + where + ::Context: BlockContext, + { + EvmErrored { + inner: self.inner, + state: ErroredState { context: self.state.take_context(), error }, + } + } } // --- NEEDS TX -impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> EvmNeedsTx<'a, Ext, Db, C> { +impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> EvmNeedsTx<'a, Ext, Db, C> { /// Close the current block, applying some logic, and returning the EVM /// ready for the next block. /// @@ -915,7 +1015,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: HasTx> Trevm<'a, Ext, D // --- READY -impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> EvmReady<'a, Ext, Db, C> { +impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> EvmReady<'a, Ext, Db, C> { /// Clear the current transaction environment. pub fn clear_tx(self) -> EvmNeedsTx<'a, Ext, Db, C> { // NB: we do not clear the tx env here, as we may read it during `BlockContext::post_tx` @@ -947,9 +1047,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> EvmReady< // --- ERRORED -impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext, E> - EvmErrored<'a, Ext, Db, C, E> -{ +impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext, E> EvmErrored<'a, Ext, Db, C, E> { /// Get a reference to the error. pub const fn error(&self) -> &E { &self.state.error @@ -979,9 +1077,25 @@ impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext, E> let Trevm { inner, state: ErroredState { context, error } } = self; (Trevm { inner, state: NeedsTx(context) }, error) } + + /// Transform the error into a new error type. + pub fn err_into>(self) -> EvmErrored<'a, Ext, Db, C, NewErr> { + self.map_err(Into::into) + } + + /// Map the error to a new error type. + pub fn map_err(self, f: F) -> EvmErrored<'a, Ext, Db, C, NewErr> + where + F: FnOnce(E) -> NewErr, + { + Trevm { + inner: self.inner, + state: ErroredState { context: self.state.context, error: f(self.state.error) }, + } + } } -impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> +impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> EvmErrored<'a, Ext, Db, C, EVMError> { /// Check if the error is a transaction error. This is provided as a @@ -1012,7 +1126,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> // --- TRANSACTED -impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> AsRef +impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> AsRef for EvmTransacted<'a, Ext, Db, C> { fn as_ref(&self) -> &ResultAndState { @@ -1020,7 +1134,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> AsRef> AsRef +impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> AsRef for EvmTransacted<'a, Ext, Db, C> { fn as_ref(&self) -> &ExecutionResult { @@ -1028,9 +1142,7 @@ impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> AsRef> - EvmTransacted<'a, Ext, Db, C> -{ +impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> EvmTransacted<'a, Ext, Db, C> { /// Get a reference to the result. pub fn result(&self) -> &ExecutionResult { self.as_ref() diff --git a/src/lib.rs b/src/lib.rs index 926c9b8..9b55fdf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,8 +10,7 @@ //! [`Trevm`] models the EVM as a state machine with the following states: //! //! - [`EvmNeedsCfg`]: The EVM needs to be configured. -//! - [`EvmNeedsFirstBlock`]: The EVM is configured and needs a block -//! environment. +//! - [`EvmNeedsBlock`]: The EVM is configured and needs a block environment. //! - [`EvmNeedsTx`]: The EVM is configured and has a block environment, and //! now needs a transaction to execute. //! - [`EvmReady`]: The EVM has a transaction loaded and is ready to execute it. @@ -19,10 +18,7 @@ //! error. //! - [`EvmTransacted`]: The EVM has executed a transaction successfully. //! - [`EvmBlockComplete`]: The EVM has executed and closed a block and contains -//! some a populated [`BlockContext`] object -//! - [`EvmNeedsNextBlock`]: The EVM has executed a transaction (or several -//! transactions) successfully and the block has been closed. The EVM is now -//! ready to open the next block, or to yield its outputs. +//! some populated [`BlockContext`] object //! //! ## Quickstart //! @@ -281,7 +277,7 @@ //! # use revm::{EvmBuilder, db::{InMemoryDB, BundleState}, State, //! # StateBuilder}; //! # use trevm::{TrevmBuilder, EvmErrored, Cfg, Block, Tx, BlockOutput, -//! # EvmNeedsCfg, EvmNeedsFirstBlock, EvmNeedsTx, EvmReady, EvmNeedsNextBlock, +//! # EvmNeedsCfg, EvmNeedsBlock, EvmNeedsTx, EvmReady, //! # EvmBlockComplete, Shanghai, EvmTransacted}; //! # fn t(cfg: &C, block: &B, tx: &T) //! # -> Result<(), Box> { @@ -292,8 +288,8 @@ //! .with_db(state) //! .build_trevm(); //! -//! // Once the cfg is filled, we move to `EvmNeedsFirstBlock`. -//! let trevm: EvmNeedsFirstBlock<'_, _, _> = trevm.fill_cfg(cfg); +//! // Once the cfg is filled, we move to `EvmNeedsBlock`. +//! let trevm: EvmNeedsBlock<'_, _, _> = trevm.fill_cfg(cfg); //! //! // Filling the block gets us to `EvmNeedsTx`. `open_block` takes a //! // context object that will apply pre- and post-block logic, accumulate @@ -325,7 +321,7 @@ //! .map_err(EvmErrored::into_error)?;; //! //! // During block execution, a context object -//! let (context, trevm): (Shanghai<'_>, EvmNeedsNextBlock<'_, _, _>) = trevm +//! let (context, trevm): (Shanghai<'_>, EvmNeedsBlock<'_, _, _>) = trevm //! .take_context(); //! //! // Finishing the EVM gets us the final changes and a list of block outputs @@ -344,20 +340,20 @@ //! +-----+ +-----------+ //! |Start| ---> |EvmNeedsCfg| //! +-----+ +-----------+ -//! | +--------------------+ -//! +-- fill_cfg() -------> | EvmNeedsFirstBlock | -//! +--------------------+ -//! | -//! +----------------+ | -//! +-- |EvmBlockComplete|--take_context()-+ | -//! | +----------------+ or discard | | -//! | ^ V | -//! | | +-----------------+ | -//! finish() | |EvmNeedsNextBlock|------+ -//! | close_block() +-----------------+ | -//! V | | -//! +------+ +----------+ | -//! |Finish| |EvmNeedsTx| <------ open_block() -----------+ +//! | +----------------+ +//! +-- fill_cfg() ----------+--> | EvmNeedsBliock | +//! | +----------------+ +//! | | +//! +----------------+ | | +//! +-- |EvmBlockComplete|--take_context()-+ | +//! | +----------------+ or discard | +//! | ^ | +//! | | | +//! finish() | | +//! | close_block() | +//! V | | +//! +------+ +----------+ | +//! |Finish| |EvmNeedsTx| <------ open_block()---------+ //! +------+ +----------+ //! ^ | +--------+ //! | +------- fill_tx() -------> |EvmReady|--+ @@ -399,6 +395,11 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +mod driver; +pub use driver::{ + AlloyBlockError, BlockDriver, ChainDriver, DriveBlockResult, DriveChainResult, RunTxResult, +}; + mod evm; pub use evm::Trevm; @@ -416,8 +417,8 @@ pub use lifecycle::{ mod states; pub(crate) use states::sealed::*; pub use states::{ - EvmBlockComplete, EvmErrored, EvmNeedsCfg, EvmNeedsFirstBlock, EvmNeedsNextBlock, EvmNeedsTx, - EvmReady, EvmTransacted, + EvmBlockComplete, EvmBlockDriverErrored, EvmChainDriverErrored, EvmErrored, EvmNeedsBlock, + EvmNeedsCfg, EvmNeedsTx, EvmReady, EvmTransacted, }; pub mod syscall; diff --git a/src/lifecycle/contexts.rs b/src/lifecycle/contexts.rs index b6514dc..d97da53 100644 --- a/src/lifecycle/contexts.rs +++ b/src/lifecycle/contexts.rs @@ -29,23 +29,30 @@ use std::{collections::HashMap, sync::OnceLock}; #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] pub struct BasicContext; -impl BlockContext for BasicContext { - type Error = EVMError; +impl BlockContext for BasicContext { + type Error = EVMError; - fn open_block( + fn open_block( &mut self, evm: &mut Evm<'_, Ext, Db>, b: &B, - ) -> Result<(), Self::Error> { + ) -> Result<(), Self::Error> { b.fill_block(evm); Ok(()) } - fn after_tx(&mut self, evm: &mut Evm<'_, Ext, Db>, result: revm::primitives::ResultAndState) { + fn after_tx( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + result: revm::primitives::ResultAndState, + ) { evm.db_mut().commit(result.state); } - fn close_block(&mut self, _evm: &mut Evm<'_, Ext, Db>) -> Result<(), Self::Error> { + fn close_block( + &mut self, + _evm: &mut Evm<'_, Ext, Db>, + ) -> Result<(), Self::Error> { Ok(()) } } @@ -68,14 +75,14 @@ impl<'a> From<&'a [Withdrawal]> for Shanghai<'a> { } } -impl BlockContext for Shanghai<'_> { - type Error = EVMError; +impl BlockContext for Shanghai<'_> { + type Error = EVMError; - fn open_block( + fn open_block( &mut self, evm: &mut Evm<'_, Ext, Db>, b: &B, - ) -> Result<(), Self::Error> { + ) -> Result<(), Self::Error> { if let Some(hint) = b.tx_count_hint() { self.outputs.reserve(hint); } @@ -83,7 +90,11 @@ impl BlockContext for Shanghai<'_> Ok(()) } - fn after_tx(&mut self, evm: &mut Evm<'_, Ext, Db>, result: ResultAndState) { + fn after_tx( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + result: ResultAndState, + ) { let sender = evm.tx().caller; let receipt = self.make_receipt(result.result).into(); @@ -105,7 +116,10 @@ impl BlockContext for Shanghai<'_> self.outputs.push_result(receipt, sender); } - fn close_block(&mut self, evm: &mut Evm<'_, Ext, Db>) -> Result<(), Self::Error> { + fn close_block( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + ) -> Result<(), Self::Error> { self.apply_withdrawals(evm)?; Ok(()) } @@ -145,7 +159,7 @@ impl Shanghai<'_> { pub fn apply_withdrawals( &mut self, evm: &mut Evm<'_, Ext, Db>, - ) -> Result<(), >::Error> + ) -> Result<(), ::Error> where Db: Database + DatabaseCommit, { @@ -231,23 +245,30 @@ impl<'a> std::ops::DerefMut for Cancun<'a> { } } -impl BlockContext for Cancun<'_> { - type Error = EVMError; +impl BlockContext for Cancun<'_> { + type Error = EVMError; - fn open_block( + fn open_block( &mut self, evm: &mut Evm<'_, Ext, Db>, b: &B, - ) -> Result<(), Self::Error> { + ) -> Result<(), Self::Error> { self.shanghai.open_block(evm, b)?; self.apply_eip4788(evm) } - fn after_tx(&mut self, evm: &mut Evm<'_, Ext, Db>, result: ResultAndState) { + fn after_tx( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + result: ResultAndState, + ) { self.shanghai.after_tx(evm, result) } - fn close_block(&mut self, evm: &mut Evm<'_, Ext, Db>) -> Result<(), Self::Error> { + fn close_block( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + ) -> Result<(), Self::Error> { self.shanghai.close_block(evm) } } @@ -268,7 +289,7 @@ impl Cancun<'_> { pub fn apply_eip4788( &mut self, evm: &mut Evm<'_, Ext, Db>, - ) -> Result<(), >::Error> + ) -> Result<(), ::Error> where Db: Database + DatabaseCommit, { @@ -316,29 +337,33 @@ impl<'a> From> for Prague<'a> { } } -impl BlockContext for Prague<'_> -where - Db: Database + DatabaseCommit, -{ - type Error = EVMError; +impl BlockContext for Prague<'_> { + type Error = EVMError; - fn open_block<'a, B: Block>( + fn open_block<'a, B: Block, Ext, Db: Database + DatabaseCommit>( &mut self, evm: &mut Evm<'_, Ext, Db>, b: &B, - ) -> Result<(), Self::Error> { + ) -> Result<(), Self::Error> { self.cancun.open_block(evm, b)?; Self::apply_eip2935(evm) } - fn after_tx<'a>(&mut self, evm: &mut Evm<'_, Ext, Db>, result: ResultAndState) { + fn after_tx<'a, Ext, Db: Database + DatabaseCommit>( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + result: ResultAndState, + ) { // ordering is important here as the `find_deposit_logs` method relies // on the receipt produced by the inner `after_tx` call. self.cancun.after_tx(evm, result); self.find_deposit_logs(); } - fn close_block<'a>(&mut self, evm: &mut Evm<'_, Ext, Db>) -> Result<(), Self::Error> { + fn close_block<'a, Ext, Db: Database + DatabaseCommit>( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + ) -> Result<(), Self::Error> { self.apply_eip7002(evm)?; self.apply_eip7251(evm)?; self.cancun.close_block(evm) @@ -390,7 +415,7 @@ impl Prague<'_> { /// [EIP-2935]: https://eips.ethereum.org/EIPS/eip-2935 pub fn apply_eip2935( evm: &mut Evm<'_, Ext, Db>, - ) -> Result<(), >::Error> + ) -> Result<(), ::Error> where Db: Database + DatabaseCommit, { @@ -446,7 +471,7 @@ impl Prague<'_> { &mut self, evm: &mut Evm<'_, Ext, Db>, - ) -> Result<(), >::Error> + ) -> Result<(), ::Error> where Db: Database + DatabaseCommit, { @@ -486,7 +511,7 @@ impl Prague<'_> { pub fn apply_eip7251( &mut self, evm: &mut Evm<'_, Ext, Db>, - ) -> Result<(), >::Error> + ) -> Result<(), ::Error> where Db: Database + DatabaseCommit, { diff --git a/src/lifecycle/trait.rs b/src/lifecycle/trait.rs index 9cee4f8..8f96a03 100644 --- a/src/lifecycle/trait.rs +++ b/src/lifecycle/trait.rs @@ -40,17 +40,17 @@ use revm::{ /// [`CfgEnv`]: revm::primitives::CfgEnv /// [`SpecId`]: revm::primitives::SpecId /// [`Trevm`]: crate::Trevm -pub trait BlockContext { +pub trait BlockContext { /// The error type for the context. This captures logic errors that occur /// during the lifecycle. - type Error: From>; + type Error: From>; /// Apply pre-block logic, and prep the EVM for the first user transaction. - fn open_block( + fn open_block( &mut self, evm: &mut Evm<'_, Ext, Db>, b: &B, - ) -> Result<(), Self::Error>; + ) -> Result<(), Self::Error>; /// Apply post-transaction logic and then commit the transaction to the evm /// state. This will be called by [`Trevm`] for each transaction in a block. @@ -73,17 +73,15 @@ pub trait BlockContext { /// # /// # impl MyContext { fn make_receipt(&self, result: &ResultAndState) {} } /// # - /// impl BlockContext for MyContext - /// where - /// Db: Database + DatabaseCommit + /// impl BlockContext for MyContext /// { - /// # type Error = EVMError; - /// # fn open_block( + /// # type Error = EVMError; + /// # fn open_block( /// # &mut self, /// # _evm: &mut Evm<'_, Ext, Db>, /// # _b: &B - /// # ) -> Result<(), Self::Error> { Ok(()) } - /// fn after_tx( + /// # ) -> Result<(), Self::Error> { Ok(()) } + /// fn after_tx( /// &mut self, /// evm: &mut Evm<'_, Ext, Db>, /// result: ResultAndState @@ -93,18 +91,25 @@ pub trait BlockContext { /// evm.db_mut().commit(result.state); /// } /// # - /// # fn close_block( + /// # fn close_block( /// # &mut self, /// # _evm: &mut Evm<'_, Ext, Db> - /// # ) -> Result<(), Self::Error> { + /// # ) -> Result<(), Self::Error> { /// # Ok(()) /// # } /// } /// ``` /// /// [`Trevm`]: crate::Trevm - fn after_tx(&mut self, evm: &mut Evm<'_, Ext, Db>, result: ResultAndState); + fn after_tx( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + result: ResultAndState, + ); /// Apply post-block logic and close the block. - fn close_block(&mut self, evm: &mut Evm<'_, Ext, Db>) -> Result<(), Self::Error>; + fn close_block( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + ) -> Result<(), Self::Error>; } diff --git a/src/states.rs b/src/states.rs index 8b7afa5..fedb9dd 100644 --- a/src/states.rs +++ b/src/states.rs @@ -1,4 +1,4 @@ -use crate::{BlockContext, Trevm}; +use crate::{BlockContext, BlockDriver, ChainDriver, Trevm}; use sealed::*; /// A [`Trevm`] that requires a [`Cfg`]. @@ -13,10 +13,12 @@ pub type EvmNeedsCfg<'a, Ext, Db> = Trevm<'a, Ext, Db, NeedsCfg>; /// outputs. This EVM has not yet executed any transactions or state changes. /// /// Expected continuations include: -/// - [`EvmNeedsFirstBlock::open_block`] +/// - [`EvmNeedsBlock::open_block`] +/// - [`EvmNeedsBlock::drive_block`] +/// - [`EvmNeedsBlock::drive_chain`] /// /// [`Block`]: crate::Block -pub type EvmNeedsFirstBlock<'a, Ext, Db> = Trevm<'a, Ext, Db, NeedsFirstBlock>; +pub type EvmNeedsBlock<'a, Ext, Db> = Trevm<'a, Ext, Db, NeedsBlock>; /// A [`Trevm`] that has completed a block and contains the block's populated /// lifecycle object. @@ -26,14 +28,6 @@ pub type EvmNeedsFirstBlock<'a, Ext, Db> = Trevm<'a, Ext, Db, NeedsFirstBlock>; /// - [`EvmBlockComplete::discard_context`] pub type EvmBlockComplete<'a, Ext, Db, T> = Trevm<'a, Ext, Db, BlockComplete>; -/// A [`Trevm`] that requires a [`Block`]. -/// -/// Expected continuations include: -/// - [`EvmNeedsFirstBlock::open_block`] -/// -/// [`Block`]: crate::Block -pub type EvmNeedsNextBlock<'a, Ext, Db> = Trevm<'a, Ext, Db, NeedsNextBlock>; - /// A [`Trevm`] that requires a [`Tx`]. /// /// Expected continuations include: @@ -62,9 +56,21 @@ pub type EvmTransacted<'a, Ext, Db, C> = Trevm<'a, Ext, Db, TransactedState>; /// Expected continuations include: /// - [`EvmErrored::discard_error`] /// - [`EvmErrored::into_error`] -pub type EvmErrored<'a, Ext, Db, C, E = >::Error> = +pub type EvmErrored<'a, Ext, Db, C, E = ::Error> = Trevm<'a, Ext, Db, ErroredState>; +/// A [`Trevm`] that encountered an error during [`BlockDriver`] execution. +/// +/// This is an [`EvmErrored`] parameterized with the driver's error type. +pub type EvmBlockDriverErrored<'a, 'b, Ext, Db, C, T> = + EvmErrored<'a, Ext, Db, C, >::Error>; + +/// A [`Trevm`] that encountered an error during [`ChainDriver`] execution. +/// +/// This is an [`EvmErrored`] parameterized with the driver's error type. +pub type EvmChainDriverErrored<'a, 'b, Ext, Db, C, T> = + EvmErrored<'a, Ext, Db, C, >::Error>; + #[allow(unnameable_types, dead_code, unreachable_pub)] pub(crate) mod sealed { use revm::primitives::ResultAndState; @@ -90,7 +96,7 @@ pub(crate) mod sealed { }; } - states!(NeedsCfg, NeedsFirstBlock, NeedsNextBlock); + states!(NeedsCfg, NeedsBlock); /// A state for the [`Trevm`]. /// @@ -132,22 +138,10 @@ pub(crate) mod sealed { pub error: E, } - /// Trait for states where block execution can be started. - pub trait NeedsBlock {} - impl NeedsBlock for NeedsFirstBlock {} - impl NeedsBlock for NeedsNextBlock {} - - /// Trait for states where thcare outputs vec is non-empty. - pub trait HasOutputs {} - impl HasOutputs for NeedsNextBlock {} - impl HasOutputs for NeedsTx {} - impl HasOutputs for TransactedState {} - impl HasOutputs for ErroredState {} - impl HasOutputs for Ready {} + pub struct Seal; pub trait HasCfg {} - impl HasCfg for NeedsFirstBlock {} - impl HasCfg for NeedsNextBlock {} + impl HasCfg for NeedsBlock {} impl HasCfg for NeedsTx {} impl HasCfg for TransactedState {} impl HasCfg for ErroredState {} @@ -170,6 +164,10 @@ pub(crate) mod sealed { fn context(&self) -> &Self::Context; fn context_mut(&mut self) -> &mut Self::Context; + + fn take_context(self) -> Self::Context + where + Self: Sized; } impl HasContext for NeedsTx { @@ -182,6 +180,10 @@ pub(crate) mod sealed { fn context_mut(&mut self) -> &mut Self::Context { &mut self.0 } + + fn take_context(self) -> Self::Context { + self.0 + } } impl HasContext for BlockComplete { @@ -194,6 +196,10 @@ pub(crate) mod sealed { fn context_mut(&mut self) -> &mut Self::Context { &mut self.0 } + + fn take_context(self) -> Self::Context { + self.0 + } } impl HasContext for TransactedState { @@ -206,6 +212,10 @@ pub(crate) mod sealed { fn context_mut(&mut self) -> &mut Self::Context { &mut self.context } + + fn take_context(self) -> Self::Context { + self.context + } } impl HasContext for ErroredState { @@ -218,6 +228,10 @@ pub(crate) mod sealed { fn context_mut(&mut self) -> &mut Self::Context { &mut self.context } + + fn take_context(self) -> Self::Context { + self.context + } } impl HasContext for Ready { @@ -230,6 +244,10 @@ pub(crate) mod sealed { fn context_mut(&mut self) -> &mut Self::Context { &mut self.0 } + + fn take_context(self) -> Self::Context { + self.0 + } } } @@ -238,9 +256,8 @@ pub(crate) mod sealed { /// /// This will create aliases with your concrete types for the following types: /// - [`EvmNeedsCfg`] -/// - [`EvmNeedsFirstBlock`] +/// - [`EvmNeedsBlock`] /// - [`EvmBlockComplete`] -/// - [`EvmNeedsNextBlock`] /// - [`EvmNeedsTx`] /// - [`EvmReady`] /// @@ -296,10 +313,7 @@ macro_rules! trevm_aliases { mod __aliases { use super::*; // bring these in scope so that doclinks work in generated docs - use $crate::Block; - use $crate::Cfg; - use $crate::Trevm; - use $crate::Tx; + use $crate::{Block, BlockDriver, Cfg, ChainDriver, Trevm, Tx}; /// A [`Trevm`] that requires a [`Cfg`]. /// @@ -314,10 +328,12 @@ macro_rules! trevm_aliases { /// outputs. This EVM has not yet executed any transactions or state changes. /// /// Expected continuations include: - /// - [`EvmNeedsFirstBlock::open_block`] + /// - [`EvmNeedsBlock::open_block`] + /// - [`EvmNeedsBlock::drive_block`] + /// - [`EvmNeedsBlock::drive_chain`] /// /// [`Block`]: crate::Block - pub type EvmNeedsFirstBlock<'a> = $crate::EvmNeedsFirstBlock<'a, $ext, $db>; + pub type EvmNeedsBlock<'a> = $crate::EvmNeedsBlock<'a, $ext, $db>; /// A [`Trevm`] that has completed a block and contains the block's populated /// lifecycle object. @@ -327,14 +343,6 @@ macro_rules! trevm_aliases { /// - [`EvmBlockComplete::discard_context`] pub type EvmBlockComplete<'a, C> = $crate::EvmBlockComplete<'a, $ext, $db, C>; - /// A [`Trevm`] that requires a [`Block`]. - /// - /// Expected continuations include: - /// - [`EvmNeedsFirstBlock::open_block`] - /// - /// [`Block`]: crate::Block - pub type EvmNeedsNextBlock<'a> = $crate::EvmNeedsNextBlock<'a, $ext, $db>; - /// A [`Trevm`] that requires a [`Tx`]. /// /// Expected continuations include: @@ -364,8 +372,20 @@ macro_rules! trevm_aliases { /// Expected continuations include: /// - [`EvmErrored::discard_error`] /// - [`EvmErrored::into_error`] - pub type EvmErrored<'a, C, E = >::Error> = + pub type EvmErrored<'a, C, E = ::Error<$db>> = $crate::EvmErrored<'a, $ext, $db, C, E>; + + /// A [`Trevm`] that encountered an error during [`BlockDriver`] execution. + /// + /// This is an [`EvmErrored`] parameterized with the driver's error type. + pub type EvmBlockDriverErrored<'a, 'b, C, T> = + $crate::EvmBlockDriverErrored<'a, 'b, $ext, $db, C, T>; + + /// A [`Trevm`] that encountered an error during [`ChainDriver`] execution. + /// + /// This is an [`EvmErrored`] parameterized with the driver's error type. + pub type EvmChainDriverErrored<'a, 'b, C, T> = + $crate::EvmChainDriverErrored<'a, 'b, $ext, $db, C, T>; } }; @@ -377,10 +397,7 @@ macro_rules! trevm_aliases { mod __aliases { use super::*; // bring these in scope so that doclinks work in generated docs - use $crate::Block; - use $crate::Cfg; - use $crate::Trevm; - use $crate::Tx; + use $crate::{Block, BlockDriver, Cfg, ChainDriver, Trevm, Tx}; /// A [`Trevm`] that requires a [`Cfg`]. /// @@ -391,14 +408,13 @@ macro_rules! trevm_aliases { /// [`Trevm`]: crate::Trevm pub type EvmNeedsCfg = $crate::EvmNeedsCfg<'static, $ext, $db>; - /// A [`Trevm`] that requires a [`Block`] and contains no - /// outputs. This EVM has not yet executed any transactions or state changes. + /// A [`Trevm`] that requires a [`Block`]. /// /// Expected continuations include: - /// - [`EvmNeedsFirstBlock::open_block`] + /// - [`EvmNeedsBlock::open_block`] /// /// [`Block`]: crate::Block - pub type EvmNeedsFirstBlock = $crate::EvmNeedsFirstBlock<'static, $ext, $db>; + pub type EvmNeedsBlock = $crate::EvmNeedsBlock<'static, $ext, $db>; /// A [`Trevm`] that has completed a block and contains the block's populated /// lifecycle object. @@ -408,14 +424,6 @@ macro_rules! trevm_aliases { /// - [`EvmBlockComplete::discard_context`] pub type EvmBlockComplete = $crate::EvmBlockComplete<'static, $ext, $db, C>; - /// A [`Trevm`] that requires a [`Block`]. - /// - /// Expected continuations include: - /// - [`EvmNeedsFirstBlock::open_block`] - /// - /// [`Block`]: crate::Block - pub type EvmNeedsNextBlock = $crate::EvmNeedsNextBlock<'static, $ext, $db>; - /// A [`Trevm`] that requires a [`Tx`]. // /// Expected continuations include: @@ -445,8 +453,20 @@ macro_rules! trevm_aliases { /// Expected continuations include: /// - [`EvmErrored::discard_error`] /// - [`EvmErrored::into_error`] - pub type EvmErrored>::Error> = + pub type EvmErrored::Error<$db>> = $crate::EvmErrored<'static, $ext, $db, C, E>; + + /// A [`Trevm`] that encountered an error during [`BlockDriver`] execution. + /// + /// This is an [`EvmErrored`] parameterized with the driver's error type. + pub type EvmBlockDriverErrored<'a, C, T> = + $crate::EvmBlockDriverErrored<'static, 'a, $ext, $db, C, T>; + + /// A [`Trevm`] that encountered an error during [`ChainDriver`] execution. + /// + /// This is an [`EvmErrored`] parameterized with the driver's error type. + pub type EvmChainDriverErrored<'a, C, T> = + $crate::EvmChainDriverErrored<'static, 'a, $ext, $db, C, T>; } }; }