diff --git a/crates/sim/src/env/host.rs b/crates/sim/src/env/host.rs new file mode 100644 index 0000000..110939a --- /dev/null +++ b/crates/sim/src/env/host.rs @@ -0,0 +1,69 @@ +use crate::{InnerDb, SimDb, TimeLimited}; +use std::{marker::PhantomData, sync::Arc, time::Instant}; +use trevm::{ + helpers::Ctx, + inspectors::{Layered, TimeLimit}, + revm::{ + database::CacheDB, + inspector::{Inspector, NoOpInspector}, + DatabaseRef, + }, + TrevmBuilder, +}; + +/// A host simulation environment. +#[derive(Debug)] +pub struct HostEnv { + db: InnerDb, + _pd: PhantomData Insp>, +} + +impl Clone for HostEnv { + fn clone(&self) -> Self { + Self { db: self.db.clone(), _pd: PhantomData } + } +} + +impl From for HostEnv +where + Db: DatabaseRef + Send + Sync, +{ + fn from(db: Db) -> Self { + Self::new(db) + } +} + +impl HostEnv { + /// Create a new host environment. + pub fn new(db: Db) -> Self { + Self { db: Arc::new(CacheDB::new(db)), _pd: PhantomData } + } + + /// Get a mutable reference to the inner database. + pub const fn db_mut(&mut self) -> &mut InnerDb { + &mut self.db + } +} + +impl HostEnv +where + Db: DatabaseRef + Send + Sync, + Insp: Inspector>> + Default + Sync, +{ + /// Connect a fresh database for the simulation. + pub fn sim_db(&self) -> crate::SimDb { + crate::SimDb::new(self.db.clone()) + } + + /// Create a new EVM for the host environment that will finish by the + /// given instant. + pub fn create_evm( + &self, + finish_by: Instant, + ) -> trevm::EvmNeedsCfg, TimeLimited> { + let db = self.sim_db(); + let inspector = Layered::new(TimeLimit::new(finish_by - Instant::now()), Insp::default()); + + TrevmBuilder::new().with_insp(inspector).with_db(db).build_trevm().unwrap() + } +} diff --git a/crates/sim/src/env/mod.rs b/crates/sim/src/env/mod.rs new file mode 100644 index 0000000..5d4d2d8 --- /dev/null +++ b/crates/sim/src/env/mod.rs @@ -0,0 +1,11 @@ +mod host; +pub use host::HostEnv; + +mod rollup; +pub use rollup::RollupEnv; + +mod shared; +pub use shared::SharedSimEnv; + +mod sim_env; +pub use sim_env::SimEnv; diff --git a/crates/sim/src/env/rollup.rs b/crates/sim/src/env/rollup.rs new file mode 100644 index 0000000..02f69cb --- /dev/null +++ b/crates/sim/src/env/rollup.rs @@ -0,0 +1,88 @@ +use crate::{InnerDb, SimDb, TimeLimited}; +use signet_evm::EvmNeedsCfg; +use signet_types::constants::SignetSystemConstants; +use std::{marker::PhantomData, sync::Arc, time::Instant}; +use trevm::{ + db::{cow::CacheOnWrite, TryCachingDb}, + helpers::Ctx, + inspectors::{Layered, TimeLimit}, + revm::{ + database::{Cache, CacheDB}, + inspector::NoOpInspector, + DatabaseRef, Inspector, + }, +}; + +/// A rollup simulation environment. +#[derive(Debug)] +pub struct RollupEnv { + db: InnerDb, + constants: SignetSystemConstants, + _pd: PhantomData Insp>, +} + +impl Clone for RollupEnv { + fn clone(&self) -> Self { + Self { db: self.db.clone(), constants: self.constants.clone(), _pd: PhantomData } + } +} + +impl RollupEnv { + /// Create a new rollup environment. + pub fn new(db: Db, constants: SignetSystemConstants) -> Self { + Self { db: Arc::new(CacheDB::new(db)), constants, _pd: PhantomData } + } + + /// Get a mutable reference to the inner database. + pub const fn db_mut(&mut self) -> &mut InnerDb { + &mut self.db + } + + /// Get the constants for this environment. + pub const fn constants(&self) -> &SignetSystemConstants { + &self.constants + } +} + +impl RollupEnv +where + Db: DatabaseRef + Send + Sync, + Insp: Inspector>> + Default + Sync, +{ + /// Connect a fresh database for the simulation. + pub fn sim_db(&self) -> SimDb { + CacheOnWrite::new(self.db.clone()) + } + + /// Create a new EVM for the rollup environment that will finish by the + /// given instant. + pub fn create_evm(&self, finish_by: Instant) -> EvmNeedsCfg, TimeLimited> { + let db = self.sim_db(); + + let inspector = Layered::new(TimeLimit::new(finish_by - Instant::now()), Insp::default()); + + signet_evm::signet_evm_with_inspector(db, inspector, self.constants.clone()) + } +} + +impl RollupEnv +where + Db: DatabaseRef, + Insp: Inspector>> + Default + Sync, +{ + /// Accepts a cache from the simulation and extends the database with it. + pub fn accept_cache( + &mut self, + cache: Cache, + ) -> Result<(), as TryCachingDb>::Error> { + self.db_mut().try_extend(cache) + } + + /// Accepts a cache from the simulation and extends the database with it. + pub fn accept_cache_ref( + &mut self, + cache: &Cache, + ) -> Result<(), as TryCachingDb>::Error> { + self.db_mut().try_extend_ref(cache) + } +} diff --git a/crates/sim/src/env/shared.rs b/crates/sim/src/env/shared.rs new file mode 100644 index 0000000..8ead9b6 --- /dev/null +++ b/crates/sim/src/env/shared.rs @@ -0,0 +1,103 @@ +use crate::{env::RollupEnv, outcome::SimulatedItem, SimCache, SimDb, SimEnv}; +use core::fmt; +use std::{ops::Deref, sync::Arc}; +use tokio::{select, sync::watch}; +use tracing::{instrument, trace}; +use trevm::{ + helpers::Ctx, + revm::{inspector::NoOpInspector, DatabaseRef, Inspector}, + Block, Cfg, +}; + +/// A simulation environment. +/// +/// Contains enough information to run a simulation. +pub struct SharedSimEnv { + inner: Arc>, +} + +impl fmt::Debug for SharedSimEnv { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SharedSimEnv") + .field("finish_by", &self.inner.finish_by()) + .field("concurrency_limit", &self.inner.concurrency_limit()) + .finish_non_exhaustive() + } +} + +impl Deref for SharedSimEnv { + type Target = SimEnv; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From> for SharedSimEnv +where + Db: DatabaseRef + Send + Sync + 'static, + Insp: Inspector>> + Default + Sync + 'static, +{ + fn from(inner: SimEnv) -> Self { + Self { inner: Arc::new(inner) } + } +} + +impl SharedSimEnv +where + Db: DatabaseRef + Send + Sync + 'static, + Insp: Inspector>> + Default + Sync + 'static, +{ + /// Creates a new `SimEnv` instance. + pub fn new( + rollup: RollupEnv, + cfg: C, + block: B, + finish_by: std::time::Instant, + concurrency_limit: usize, + sim_items: SimCache, + ) -> Self + where + C: Cfg, + B: Block, + { + SimEnv::new(rollup, cfg, block, finish_by, concurrency_limit, sim_items).into() + } + + /// Run a simulation round, returning the best item. + #[instrument(skip(self))] + pub async fn sim_round(&mut self, max_gas: u64) -> Option { + let (best_tx, mut best_watcher) = watch::channel(None); + + let this = self.inner.clone(); + + // Spawn a blocking task to run the simulations. + let sim_task = tokio::task::spawn_blocking(move || this.sim_round(max_gas, best_tx)); + + // Either simulation is done, or we time out + select! { + _ = tokio::time::sleep_until(self.finish_by().into()) => { + trace!("Sim round timed out"); + }, + _ = sim_task => { + trace!("Sim round done"); + }, + } + + // Check what the current best outcome is. + let best = best_watcher.borrow_and_update(); + trace!(score = %best.as_ref().map(|candidate| candidate.score).unwrap_or_default(), "Read outcome from channel"); + let outcome = best.as_ref()?; + + // Remove the item from the cache. + let item = self.sim_items().remove(outcome.cache_rank)?; + // Accept the cache from the simulation. + Arc::get_mut(&mut self.inner) + .expect("sims dropped already") + .rollup_mut() + .accept_cache_ref(&outcome.cache) + .ok()?; + + Some(SimulatedItem { gas_used: outcome.gas_used, score: outcome.score, item }) + } +} diff --git a/crates/sim/src/env.rs b/crates/sim/src/env/sim_env.rs similarity index 60% rename from crates/sim/src/env.rs rename to crates/sim/src/env/sim_env.rs index fc62116..e5c6b7c 100644 --- a/crates/sim/src/env.rs +++ b/crates/sim/src/env/sim_env.rs @@ -1,136 +1,31 @@ -use crate::{outcome::SimulatedItem, InnerDb, SimCache, SimDb, SimItem, SimOutcomeWithCache}; +use crate::{env::RollupEnv, SimCache, SimDb, SimItem, SimOutcomeWithCache, TimeLimited}; use alloy::{consensus::TxEnvelope, hex}; use core::fmt; use signet_bundle::{SignetEthBundle, SignetEthBundleDriver, SignetEthBundleError}; -use signet_evm::SignetLayered; use signet_types::constants::SignetSystemConstants; -use std::{convert::Infallible, marker::PhantomData, ops::Deref, sync::Arc, time::Instant}; -use tokio::{ - select, - sync::{mpsc, watch}, -}; +use std::sync::Arc; +use tokio::sync::{mpsc, watch}; use tracing::{instrument, trace, trace_span}; use trevm::{ - db::{cow::CacheOnWrite, TryCachingDb}, helpers::Ctx, - inspectors::{Layered, TimeLimit}, revm::{ context::{ result::{EVMError, ExecutionResult}, BlockEnv, CfgEnv, }, - database::{Cache, CacheDB}, inspector::NoOpInspector, DatabaseRef, Inspector, }, - Block, BundleDriver, Cfg, DbConnect, EvmFactory, + Block, BundleDriver, Cfg, }; /// A simulation environment. -/// -/// Contains enough information to run a simulation. -pub struct SharedSimEnv { - inner: Arc>, -} - -impl fmt::Debug for SharedSimEnv { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SimEnv") - .field("finish_by", &self.inner.finish_by) - .field("concurrency_limit", &self.inner.concurrency_limit) - .finish_non_exhaustive() - } -} - -impl Deref for SharedSimEnv { - type Target = SimEnv; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl From> for SharedSimEnv -where - Db: DatabaseRef + Send + Sync + 'static, - Insp: Inspector>> + Default + Sync + 'static, -{ - fn from(inner: SimEnv) -> Self { - Self { inner: Arc::new(inner) } - } -} - -impl SharedSimEnv -where - Db: DatabaseRef + Send + Sync + 'static, - Insp: Inspector>> + Default + Sync + 'static, -{ - /// Creates a new `SimEnv` instance. - pub fn new( - db: Db, - constants: SignetSystemConstants, - cfg: C, - block: B, - finish_by: std::time::Instant, - concurrency_limit: usize, - sim_items: SimCache, - ) -> Self - where - C: Cfg, - B: Block, - { - SimEnv::new(db, constants, cfg, block, finish_by, concurrency_limit, sim_items).into() - } - - /// Run a simulation round, returning the best item. - #[instrument(skip(self))] - pub async fn sim_round(&mut self, max_gas: u64) -> Option { - let (best_tx, mut best_watcher) = watch::channel(None); - - let this = self.inner.clone(); - - // Spawn a blocking task to run the simulations. - let sim_task = tokio::task::spawn_blocking(move || this.sim_round(max_gas, best_tx)); - - // Either simulation is done, or we time out - select! { - _ = tokio::time::sleep_until(self.finish_by.into()) => { - trace!("Sim round timed out"); - }, - _ = sim_task => { - trace!("Sim round done"); - }, - } - - // Check what the current best outcome is. - let best = best_watcher.borrow_and_update(); - trace!(score = %best.as_ref().map(|candidate| candidate.score).unwrap_or_default(), "Read outcome from channel"); - let outcome = best.as_ref()?; - - // Remove the item from the cache. - let item = self.sim_items.remove(outcome.cache_rank)?; - // Accept the cache from the simulation. - Arc::get_mut(&mut self.inner) - .expect("sims dropped already") - .accept_cache_ref(&outcome.cache) - .ok()?; - - Some(SimulatedItem { gas_used: outcome.gas_used, score: outcome.score, item }) - } -} - -/// A simulation environment. -pub struct SimEnv { - /// The database to use for the simulation. This database will be wrapped - /// in [`CacheOnWrite`] databases for each simulation. - db: InnerDb, +pub struct SimEnv { + rollup: RollupEnv, /// The cache of items to simulate. sim_items: SimCache, - /// The system constants for the Signet network. - constants: SignetSystemConstants, - /// Chain cfg to use for the simulation. cfg: CfgEnv, @@ -142,25 +37,21 @@ pub struct SimEnv { /// The maximum number of concurrent simulations to run. concurrency_limit: usize, - - /// Spooky ghost inspector. - _pd: PhantomData Insp>, } -impl fmt::Debug for SimEnv { +impl fmt::Debug for SimEnv { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SimEnvInner") + f.debug_struct("SimEnv") .field("finish_by", &self.finish_by) .field("concurrency_limit", &self.concurrency_limit) .finish_non_exhaustive() } } -impl SimEnv { +impl SimEnv { /// Creates a new `SimFactory` instance. pub fn new( - db: Db, - constants: SignetSystemConstants, + rollup: RollupEnv, cfg_ref: C, block_ref: B, finish_by: std::time::Instant, @@ -176,26 +67,17 @@ impl SimEnv { let mut block = BlockEnv::default(); block_ref.fill_block_env(&mut block); - Self { - db: Arc::new(CacheDB::new(db)), - constants, - cfg, - block, - finish_by, - concurrency_limit, - sim_items, - _pd: PhantomData, - } + Self { rollup, cfg, block, finish_by, concurrency_limit, sim_items } } /// Get a reference to the database. - pub const fn db_mut(&mut self) -> &mut InnerDb { - &mut self.db + pub const fn rollup_mut(&mut self) -> &mut RollupEnv { + &mut self.rollup } /// Get a reference to the system constants. pub const fn constants(&self) -> &SignetSystemConstants { - &self.constants + self.rollup.constants() } /// Get a reference to the cache of items to simulate. @@ -222,44 +104,22 @@ impl SimEnv { pub const fn set_finish_by(&mut self, timeout: std::time::Instant) { self.finish_by = timeout; } -} - -impl DbConnect for SimEnv -where - Db: DatabaseRef + Send + Sync, - Insp: Sync, -{ - type Database = SimDb; - type Error = Infallible; - - fn connect(&self) -> Result { - Ok(CacheOnWrite::new(self.db.clone())) + /// Get the concurrency limit. + pub const fn concurrency_limit(&self) -> usize { + self.concurrency_limit } } -impl EvmFactory for SimEnv +impl SimEnv where - Db: DatabaseRef + Send + Sync, - Insp: Inspector>> + Default + Sync, + RuDb: DatabaseRef + Send + Sync, + RuInsp: Inspector>> + Default + Sync, { - type Insp = SignetLayered>; - - fn create(&self) -> Result, Self::Error> { - let db = self.connect().unwrap(); - - let inspector = - Layered::new(TimeLimit::new(self.finish_by - Instant::now()), Insp::default()); - - Ok(signet_evm::signet_evm_with_inspector(db, inspector, self.constants.clone())) + fn rollup_evm(&self) -> signet_evm::EvmNeedsTx, TimeLimited> { + self.rollup.create_evm(self.finish_by).fill_cfg(&self.cfg).fill_block(&self.block) } -} -impl SimEnv -where - Db: DatabaseRef + Send + Sync, - Insp: Inspector>> + Default + Sync, -{ /// Simulates a transaction in the context of a block. /// /// This function runs the simulation in a separate thread and waits for @@ -269,8 +129,8 @@ where &self, cache_rank: u128, transaction: &TxEnvelope, - ) -> Result>> { - let trevm = self.create_with_block(&self.cfg, &self.block).unwrap(); + ) -> Result>> { + let trevm = self.rollup_evm(); // Get the initial beneficiary balance let beneficiary = trevm.beneficiary(); @@ -326,13 +186,13 @@ where &self, cache_rank: u128, bundle: &SignetEthBundle, - ) -> Result>> + ) -> Result>> where - Insp: Inspector>> + Default + Sync, + RuInsp: Inspector>> + Default + Sync, { let mut driver = - SignetEthBundleDriver::new(bundle, self.constants.host_chain_id(), self.finish_by); - let trevm = self.create_with_block(&self.cfg, &self.block).unwrap(); + SignetEthBundleDriver::new(bundle, self.constants().host_chain_id(), self.finish_by); + let trevm = self.rollup_evm(); // Run the bundle let trevm = match driver.run_bundle(trevm) { @@ -361,7 +221,7 @@ where &self, cache_rank: u128, item: &SimItem, - ) -> Result>> { + ) -> Result>> { match item { SimItem::Bundle(bundle) => self.simulate_bundle(cache_rank, bundle), SimItem::Tx(tx) => self.simulate_tx(cache_rank, tx), @@ -369,7 +229,7 @@ where } #[instrument(skip_all)] - fn sim_round( + pub(crate) fn sim_round( self: Arc, max_gas: u64, best_tx: watch::Sender>, @@ -444,25 +304,3 @@ where }); } } - -impl SimEnv -where - Db: DatabaseRef, - Insp: Inspector>> + Default + Sync, -{ - /// Accepts a cache from the simulation and extends the database with it. - pub fn accept_cache( - &mut self, - cache: Cache, - ) -> Result<(), as TryCachingDb>::Error> { - self.db_mut().try_extend(cache) - } - - /// Accepts a cache from the simulation and extends the database with it. - pub fn accept_cache_ref( - &mut self, - cache: &Cache, - ) -> Result<(), as TryCachingDb>::Error> { - self.db_mut().try_extend_ref(cache) - } -} diff --git a/crates/sim/src/lib.rs b/crates/sim/src/lib.rs index 85c6783..e59a6d8 100644 --- a/crates/sim/src/lib.rs +++ b/crates/sim/src/lib.rs @@ -21,7 +21,7 @@ mod cache; pub use cache::SimCache; mod env; -pub use env::{SharedSimEnv, SimEnv}; +pub use env::{HostEnv, RollupEnv, SharedSimEnv, SimEnv}; mod error; pub use error::CacheError; @@ -40,3 +40,6 @@ pub type InnerDb = std::sync::Arc>; /// A type alias for the database used in the simulation. pub type SimDb = trevm::db::cow::CacheOnWrite>; + +/// A time-limited layered inspector. +pub type TimeLimited = trevm::inspectors::Layered; diff --git a/crates/sim/src/task.rs b/crates/sim/src/task.rs index 48b36c2..3a2c1c0 100644 --- a/crates/sim/src/task.rs +++ b/crates/sim/src/task.rs @@ -1,4 +1,4 @@ -use crate::{env::SimEnv, BuiltBlock, SharedSimEnv, SimCache, SimDb}; +use crate::{env::SimEnv, BuiltBlock, RollupEnv, SharedSimEnv, SimCache, SimDb}; use signet_types::constants::SignetSystemConstants; use std::time::Duration; use tokio::{select, time::Instant}; @@ -50,8 +50,7 @@ where B: Block, { let env = SimEnv::::new( - db, - constants, + RollupEnv::new(db, constants), cfg, block, finish_by,