From 0df519f694b3442d43c03249d68e6f4ec54cb33e Mon Sep 17 00:00:00 2001 From: dylan Date: Sun, 16 Mar 2025 19:14:45 -0600 Subject: [PATCH 1/5] adds simulation factory to builder - adds initial implementation of simulator task to the builder --- Cargo.toml | 7 + src/tasks/block.rs | 5 + src/tasks/mod.rs | 3 + src/tasks/simulator.rs | 333 ++++++++++++++++++++++++++++++++++++++++ tests/simulator_test.rs | 97 ++++++++++++ 5 files changed, 445 insertions(+) create mode 100644 src/tasks/simulator.rs create mode 100644 tests/simulator_test.rs diff --git a/Cargo.toml b/Cargo.toml index b9231f6..1b5bdcf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,13 @@ alloy = { version = "0.12.6", features = [ "serde", ] } +trevm = { version = "0.19.12", features = [ "concurrent-db" ]} +revm = { version = "19.5.0", features = [ "alloydb" ]} + +# HACK: Update these to use main alloy package +alloy-provider = { version = "0.7.3" } +alloy-eips = { version = "0.7.3" } + aws-config = "1.1.7" aws-sdk-kms = "1.15.0" diff --git a/src/tasks/block.rs b/src/tasks/block.rs index a0c9c87..4e26d90 100644 --- a/src/tasks/block.rs +++ b/src/tasks/block.rs @@ -43,6 +43,11 @@ impl InProgressBlock { self.transactions.is_empty() } + /// Returns the current list of transactions included in this block + pub fn transactions(&self) -> Vec { + self.transactions.clone() + } + /// Unseal the block fn unseal(&mut self) { self.raw_encoding.take(); diff --git a/src/tasks/mod.rs b/src/tasks/mod.rs index e6983af..9e8cd00 100644 --- a/src/tasks/mod.rs +++ b/src/tasks/mod.rs @@ -15,3 +15,6 @@ pub mod submit; /// Tx polling task pub mod tx_poller; + +/// Tx and bundle simulation task +pub mod simulator; diff --git a/src/tasks/simulator.rs b/src/tasks/simulator.rs new file mode 100644 index 0000000..1062419 --- /dev/null +++ b/src/tasks/simulator.rs @@ -0,0 +1,333 @@ +use crate::tasks::block::InProgressBlock; +use alloy::consensus::TxEnvelope; +use alloy::primitives::U256; +use eyre::Result; +use revm::{db::CacheDB, primitives::{address, Account, CfgEnv, ExecutionResult}, DatabaseRef}; +use std::{convert::Infallible, sync::Arc}; +use tokio::{select, sync::mpsc::UnboundedReceiver, task::JoinSet}; +use trevm::{ + db::sync::{ConcurrentState, ConcurrentStateInfo}, + revm::{ + primitives::{EVMError, ResultAndState}, + Database, DatabaseCommit, EvmBuilder, + }, + BlockDriver, Cfg, DbConnect, EvmFactory, NoopBlock, TrevmBuilder, Tx, +}; + +/// Tracks the EVM state, score, and result of an EVM execution. +/// Scores are assigned by the evaluation function, and are Ord +/// or PartialOrd to allow for sorting. +#[derive(Debug, Clone)] +pub struct Best { + /// The transaction being executed. + pub tx: Arc, + /// The result and state of the execution. + pub result: ResultAndState, + /// The score calculated by the evaluation function. + pub score: S, +} + +/// Binds a database and an extension together. +#[derive(Debug, Clone)] +pub struct SimulatorFactory { + /// The database state the execution is carried out on. + pub db: Db, + /// The extension, if any, provided to the trevm instance. + pub ext: Ext, +} + +/// SimResult is an [`Option`] type that holds a tuple of a transaction and its associated +/// state as a [`Db`] type updates if it was successfully executed. +type SimResult = Option<(Best, ConcurrentState>>)>; + +impl SimulatorFactory +where + Ext: Send + Sync + Clone + 'static, + Db: Database + DatabaseRef + DatabaseCommit + Send + Sync + Clone + 'static, +{ + /// Creates a new Simulator factory out of the database and extension. + pub const fn new(db: Db, ext: Ext) -> Self { + Self { db, ext } + } + + /// Spawns a trevm simulator that runs until `deadline` is hit. + /// * Spawn does not guarantee that a thread is finished before the deadline. + /// * This is intentional, so that it can maximize simulation time before the deadline. + /// * This function always returns whatever the latest finished in progress block is. + pub fn spawn( + self, + mut inbound_tx: UnboundedReceiver, + evaluator: Arc, + deadline: tokio::time::Instant, + ) -> tokio::task::JoinHandle + where + T: Tx, + F: Fn(&ResultAndState) -> U256 + Send + Sync + 'static, + { + tokio::spawn(async move { + // Spawn a join set to track all simulation threads + let mut join_set = JoinSet::new(); + + let mut best: Option> = None; + + let mut block = InProgressBlock::new(); + + let sleep = tokio::time::sleep_until(deadline); + tokio::pin!(sleep); + + loop { + select! { + _ = &mut sleep => break, + // Handle incoming + tx = inbound_tx.recv() => { + if let Some(inbound_tx) = tx { + // Setup the simulation environment + let sim = self.clone(); + let eval = evaluator.clone(); + let mut parent_db = Arc::new(sim.connect().unwrap()); + + // Kick off the work in a new thread + join_set.spawn(async move { + let result = sim.simulate_tx(inbound_tx, eval, parent_db.child()); + + if let Some((best, db)) = result { + if let Ok(()) = parent_db.merge_child(db) { + tracing::debug!("merging updated simulation state"); + return Some(best) + } + tracing::error!("failed to update simulation state"); + None + } else { + None + } + }); + } + } + Some(result) = join_set.join_next() => { + match result { + Ok(Some(candidate)) => { + tracing::info!(tx_hash = ?candidate.tx.tx_hash(), "ingesting transaction"); + block.ingest_tx(candidate.tx.as_ref()); + + if candidate.score > best.as_ref().map(|b| b.score).unwrap_or_default() { + tracing::info!(score = ?candidate.score, "new best candidate found"); + best = Some(candidate); + } + } + Ok(None) => { + tracing::debug!("simulation returned no result"); + } + Err(e) => { + tracing::error!("simulation task failed: {}", e); + } + } + } + else => break, + } + } + + block + }) + } + + /// Simulates an inbound tx and applies its state if it's successfully simualted + pub fn simulate_tx( + self, + tx: TxEnvelope, + evaluator: Arc, + db: ConcurrentState>>, + ) -> SimResult + where + F: Fn(&ResultAndState) -> U256 + Send + Sync + 'static, + Db: Database + DatabaseRef + DatabaseCommit + Send + Sync + Clone + 'static, + { + let trevm_instance = EvmBuilder::default().with_db(db).build_trevm(); + + let result = trevm_instance + .fill_cfg(&PecorinoCfg) + .fill_block(&NoopBlock) + .fill_tx(&tx) // Use as_ref() to get &SimTxEnvelope from Arc + .run(); + + match result { + Ok(t) => { + // log and evaluate simulation results + tracing::info!(tx_hash = ?tx.clone().tx_hash(), "transaction simulated"); + let result = t.result_and_state().clone(); + tracing::debug!(gas_used = &result.result.gas_used(), "gas consumed"); + let score = evaluator(&result); + tracing::debug!(score = ?score, "transaction evaluated"); + + // accept results + let t = t.accept(); + let db = t.1.into_db(); + + // return the updated db with the candidate applied to its state + Some((Best { tx: Arc::new(tx), result, score }, db)) + } + Err(e) => { + // if this transaction fails to run, log the error and return None + tracing::error!(err = ?e.as_transaction_error(), "failed to simulate tx"); + None + } + } + } + + /// Simulates an inbound bundle and applies its state if it's successfully simulated + pub fn simulate_bundle( + &self, + _bundle: Arc>, + _evaluator: Arc, + _trevm_instance: trevm::EvmNeedsCfg<'_, (), ConcurrentState>>>, + ) -> Option>> + where + T: Tx + Send + Sync + 'static, + F: Fn(&ResultAndState) -> U256 + Send + Sync + 'static, + { + todo!("implement bundle handling") + } +} + +/// Wraps a Db into an EvmFactory compatible [`Database`] +impl<'a, Db, Ext> DbConnect<'a> for SimulatorFactory +where + Db: Database + DatabaseRef + DatabaseCommit + Sync + Send + Clone + 'static, + Ext: Sync + Clone, +{ + type Database = ConcurrentState; + type Error = Infallible; + + fn connect(&'a self) -> Result { + let inner = ConcurrentState::new(self.db.clone(), ConcurrentStateInfo::default()); + Ok(inner) + } +} + +/// Makes a SimulatorFactory capable of creating and configuring trevm instances +impl<'a, Db, Ext> EvmFactory<'a> for SimulatorFactory +where + Db: Database + DatabaseRef + DatabaseCommit + Sync + Send + Clone + 'static, + Ext: Sync + Clone, +{ + type Ext = (); + + /// Create makes a [`ConcurrentState`] database by calling connect + fn create(&'a self) -> Result, Self::Error> { + let db = self.connect()?; + let trevm = trevm::revm::EvmBuilder::default().with_db(db).build_trevm(); + Ok(trevm) + } +} + +/// A trait for extracting transactions from +pub trait BlockExtractor: Send + Sync + 'static { + /// BlockDriver runs the transactions over the provided trevm instance. + type Driver: BlockDriver: core::error::Error>; + + /// Instantiate an configure a new [`trevm`] instance. + fn trevm(&self, db: Db) -> trevm::EvmNeedsBlock<'static, Ext, Db>; + + /// Extracts transactions from the source. + /// + /// Extraction is infallible. Worst case it should return a no-op driver. + fn extract(&mut self, bytes: &[u8]) -> Self::Driver; +} + +impl BlockDriver for InProgressBlock { + type Block = NoopBlock; + + type Error = Error; + + fn block(&self) -> &Self::Block { + &NoopBlock + } + + /// Loops through the transactions in the block and runs them, accepting the state at the end + /// if it was successful and returning and erroring out otherwise. + fn run_txns<'a, Db: Database + DatabaseCommit>( + &mut self, + mut trevm: trevm::EvmNeedsTx<'a, Ext, Db>, + ) -> trevm::RunTxResult<'a, Ext, Db, Self> { + for tx in self.transactions().iter() { + if tx.recover_signer().is_ok() { + let sender = tx.recover_signer().unwrap(); + tracing::info!(sender = ?sender, tx_hash = ?tx.tx_hash(), "simulating transaction"); + + let t = match trevm.run_tx(tx) { + Ok(t) => t, + Err(e) => { + if e.is_transaction_error() { + return Ok(e.discard_error()); + } else { + return Err(e.err_into()); + } + } + }; + + (_, trevm) = t.accept(); + } + } + Ok(trevm) + } + + fn post_block( + &mut self, + _trevm: &trevm::EvmNeedsBlock<'_, Ext, Db>, + ) -> Result<(), Self::Error> { + Ok(()) + } +} + +/// Defines the CfgEnv for Pecorino Network +#[derive(Debug, Clone, Copy)] +pub struct PecorinoCfg; + +impl Cfg for PecorinoCfg { + fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) { + cfg_env.chain_id = 17003; + } +} + +/// Wrap the EVM error in a database error type +pub struct Error(EVMError); + +impl From> for Error +where + Db: Database, +{ + fn from(e: EVMError) -> Self { + Self(e) + } +} + +impl core::error::Error for Error {} + +impl core::fmt::Debug for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Error") + } +} + +impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Error") + } +} + +/// A simple evaluation function as a sane default. +pub fn eval_fn(state: &ResultAndState) -> U256 { + // log the transaction results + match &state.result { + ExecutionResult::Success { .. } => println!("Execution was successful."), + ExecutionResult::Revert { .. } => println!("Execution reverted."), + ExecutionResult::Halt { .. } => println!("Execution halted."), + } + + // return the target account balance + let target_addr = address!("0x0000000000000000000000000000000000000000"); + let default_account = Account::default(); + let target_account = state.state.get(&target_addr).unwrap_or(&default_account); + tracing::info!(balance = ?target_account.info.balance, "target account balance"); + + target_account.info.balance +} \ No newline at end of file diff --git a/tests/simulator_test.rs b/tests/simulator_test.rs new file mode 100644 index 0000000..fd12e76 --- /dev/null +++ b/tests/simulator_test.rs @@ -0,0 +1,97 @@ +use alloy::consensus::{SignableTransaction as _, TxEip1559, TxEnvelope}; +use alloy::network::Ethereum; +use alloy::primitives::U256; +use alloy::signers::local::PrivateKeySigner; +use alloy::signers::SignerSync as _; +use alloy::transports::http::{Client, Http}; +use builder::config::WalletlessProvider; +use builder::tasks::simulator::SimulatorFactory; +use revm::db::{AlloyDB, CacheDB}; +use revm::primitives::{address, TxKind}; +use std::sync::Arc; +use tokio::sync::mpsc; +use tokio::time::{Duration, Instant}; +use trevm::revm::primitives::{Account, ExecutionResult, ResultAndState}; +use alloy::eips::BlockId; +use alloy::providers::{Provider, ProviderBuilder}; + +#[tokio::test(flavor = "multi_thread")] +async fn test_spawn() { + // Setup transaction pipeline plumbing + let (tx_sender, tx_receiver) = mpsc::unbounded_channel::(); + let (_bundle_sender, bundle_receiver) = mpsc::unbounded_channel::>(); + let deadline = Instant::now() + Duration::from_secs(2); + + // Create a new anvil instance + let anvil = + alloy::node_bindings::Anvil::new().block_time(1).chain_id(17003).try_spawn().unwrap(); + + // Create a test wallet from the anvil keys + let keys = anvil.keys(); + let test_wallet = &PrivateKeySigner::from(keys[0].clone()); + + // Create a root provider on that anvil instance + let root_provider = ProviderBuilder::new().on_http(anvil.endpoint_url()); + let latest = root_provider.get_block_number().await.unwrap(); + + // Create an alloyDB from the provider at the latest height + let alloy_db: AlloyDB, Ethereum, Arc> = + AlloyDB::new(Arc::new(root_provider.clone()), BlockId::Number(latest.into())).unwrap(); + let db = CacheDB::new(Arc::new(alloy_db)); + + // Define trevm extension, if any + let ext = (); + + // Define the evaluator function + let evaluator = Arc::new(test_evaluator); + + // Create a simulation factory with the provided DB + let sim_factory = SimulatorFactory::new(db, ext); + let handle = + sim_factory.spawn::(tx_receiver, evaluator, deadline); + + // Send some transactions + for count in 0..2 { + let test_tx = new_test_tx(test_wallet, count).unwrap(); + tx_sender.send(test_tx).unwrap(); + } + + // Wait for simulation to complete + let best = handle.await.unwrap(); + + // Assert on the block + assert_eq!(best.len(), 1); +} + +/// An example of a simple evaluator function for use in testing +fn test_evaluator(state: &ResultAndState) -> U256 { + // log the transaction results + match &state.result { + ExecutionResult::Success { .. } => println!("Execution was successful."), + ExecutionResult::Revert { .. } => println!("Execution reverted."), + ExecutionResult::Halt { .. } => println!("Execution halted."), + } + + // return the target account balance + let target_addr = address!("0x0000000000000000000000000000000000000000"); + let default_account = Account::default(); + let target_account = state.state.get(&target_addr).unwrap_or(&default_account); + tracing::info!(balance = ?target_account.info.balance, "target account balance"); + + target_account.info.balance +} + +// Returns a new signed test transaction with default values +fn new_test_tx(wallet: &PrivateKeySigner, nonce: u64) -> eyre::Result { + let tx = TxEip1559 { + chain_id: 17003, + gas_limit: 50000, + nonce, + to: TxKind::Call(address!("0x0000000000000000000000000000000000000000")), + value: U256::from(1), + input: alloy::primitives::bytes!(""), + ..Default::default() + }; + let signature = wallet.sign_hash_sync(&tx.signature_hash())?; + Ok(TxEnvelope::Eip1559(tx.into_signed(signature))) +} From b85d40909af394a1fc0016da93d6b5681186e54c Mon Sep 17 00:00:00 2001 From: dylan Date: Mon, 31 Mar 2025 11:08:31 -0600 Subject: [PATCH 2/5] wip: updating to trevm @ 0.20 --- Cargo.toml | 9 +-- src/tasks/simulator.rs | 131 +++++++++++++++++++--------------------- tests/simulator_test.rs | 4 +- 3 files changed, 67 insertions(+), 77 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1b5bdcf..b447aa6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,8 @@ signet-bundle = { git = "https://github.com/init4tech/signet-sdk", branch = "mai signet-types = { git = "https://github.com/init4tech/signet-sdk", branch = "main" } signet-zenith = { git = "https://github.com/init4tech/signet-sdk", branch = "main" } +trevm = { version = "0.20.4", features = [ "concurrent-db" ]} + alloy = { version = "0.12.6", features = [ "full", "json-rpc", @@ -38,13 +40,6 @@ alloy = { version = "0.12.6", features = [ "serde", ] } -trevm = { version = "0.19.12", features = [ "concurrent-db" ]} -revm = { version = "19.5.0", features = [ "alloydb" ]} - -# HACK: Update these to use main alloy package -alloy-provider = { version = "0.7.3" } -alloy-eips = { version = "0.7.3" } - aws-config = "1.1.7" aws-sdk-kms = "1.15.0" diff --git a/src/tasks/simulator.rs b/src/tasks/simulator.rs index 1062419..3556d81 100644 --- a/src/tasks/simulator.rs +++ b/src/tasks/simulator.rs @@ -2,16 +2,14 @@ use crate::tasks::block::InProgressBlock; use alloy::consensus::TxEnvelope; use alloy::primitives::U256; use eyre::Result; -use revm::{db::CacheDB, primitives::{address, Account, CfgEnv, ExecutionResult}, DatabaseRef}; -use std::{convert::Infallible, sync::Arc}; +use std::sync::Arc; use tokio::{select, sync::mpsc::UnboundedReceiver, task::JoinSet}; use trevm::{ - db::sync::{ConcurrentState, ConcurrentStateInfo}, - revm::{ - primitives::{EVMError, ResultAndState}, - Database, DatabaseCommit, EvmBuilder, - }, - BlockDriver, Cfg, DbConnect, EvmFactory, NoopBlock, TrevmBuilder, Tx, + db::sync::{ConcurrentState, ConcurrentStateInfo}, helpers::Ctx, revm::{ + context::{ + result::{EVMError, ExecutionResult, ResultAndState}, CfgEnv + }, inspector::inspectors::GasInspector, primitives::address, state::Account, Database, DatabaseCommit, DatabaseRef, Inspector + }, BlockDriver, Cfg, DbConnect, EvmFactory, NoopBlock, Trevm, TrevmBuilder, TrevmBuilderError, Tx }; /// Tracks the EVM state, score, and result of an EVM execution. @@ -29,47 +27,43 @@ pub struct Best { /// Binds a database and an extension together. #[derive(Debug, Clone)] -pub struct SimulatorFactory { +pub struct SimulatorFactory { /// The database state the execution is carried out on. pub db: Db, - /// The extension, if any, provided to the trevm instance. - pub ext: Ext, + /// The inspector + pub inspector: Insp, } /// SimResult is an [`Option`] type that holds a tuple of a transaction and its associated /// state as a [`Db`] type updates if it was successfully executed. -type SimResult = Option<(Best, ConcurrentState>>)>; +type SimResult = Result, ConcurrentState>>)>>; -impl SimulatorFactory +impl SimulatorFactory where - Ext: Send + Sync + Clone + 'static, + Insp: Inspector>> + Send + Sync + Clone + 'static, Db: Database + DatabaseRef + DatabaseCommit + Send + Sync + Clone + 'static, { /// Creates a new Simulator factory out of the database and extension. - pub const fn new(db: Db, ext: Ext) -> Self { - Self { db, ext } + pub const fn new(db: Db, inspector: Insp) -> Self { + Self { db, inspector } } /// Spawns a trevm simulator that runs until `deadline` is hit. /// * Spawn does not guarantee that a thread is finished before the deadline. /// * This is intentional, so that it can maximize simulation time before the deadline. /// * This function always returns whatever the latest finished in progress block is. - pub fn spawn( + pub fn spawn( self, mut inbound_tx: UnboundedReceiver, evaluator: Arc, deadline: tokio::time::Instant, ) -> tokio::task::JoinHandle where - T: Tx, F: Fn(&ResultAndState) -> U256 + Send + Sync + 'static, { tokio::spawn(async move { - // Spawn a join set to track all simulation threads let mut join_set = JoinSet::new(); - let mut best: Option> = None; - let mut block = InProgressBlock::new(); let sleep = tokio::time::sleep_until(deadline); @@ -84,13 +78,13 @@ where // Setup the simulation environment let sim = self.clone(); let eval = evaluator.clone(); - let mut parent_db = Arc::new(sim.connect().unwrap()); + let db = self.connect().expect("must connect db"); + let mut parent_db = Arc::new(db); - // Kick off the work in a new thread join_set.spawn(async move { let result = sim.simulate_tx(inbound_tx, eval, parent_db.child()); - if let Some((best, db)) = result { + if let Ok(Some((best, db))) = result { if let Ok(()) = parent_db.merge_child(db) { tracing::debug!("merging updated simulation state"); return Some(best) @@ -141,34 +135,22 @@ where F: Fn(&ResultAndState) -> U256 + Send + Sync + 'static, Db: Database + DatabaseRef + DatabaseCommit + Send + Sync + Clone + 'static, { - let trevm_instance = EvmBuilder::default().with_db(db).build_trevm(); + let t = TrevmBuilder::new().with_db(db).with_insp(self.inspector.clone()).build_trevm()?; - let result = trevm_instance - .fill_cfg(&PecorinoCfg) - .fill_block(&NoopBlock) - .fill_tx(&tx) // Use as_ref() to get &SimTxEnvelope from Arc - .run(); + let result = t.fill_cfg(&PecorinoCfg).fill_block(&NoopBlock).fill_tx(&tx).run(); match result { Ok(t) => { - // log and evaluate simulation results - tracing::info!(tx_hash = ?tx.clone().tx_hash(), "transaction simulated"); let result = t.result_and_state().clone(); - tracing::debug!(gas_used = &result.result.gas_used(), "gas consumed"); + let db = t.into_db(); let score = evaluator(&result); - tracing::debug!(score = ?score, "transaction evaluated"); + let best = Best { tx: Arc::new(tx), result, score }; - // accept results - let t = t.accept(); - let db = t.1.into_db(); - - // return the updated db with the candidate applied to its state - Some((Best { tx: Arc::new(tx), result, score }, db)) + Ok(Some((best, db))) } - Err(e) => { - // if this transaction fails to run, log the error and return None - tracing::error!(err = ?e.as_transaction_error(), "failed to simulate tx"); - None + Err(terr) => { + tracing::error!(err = ?terr.error(), "transaction simulation error"); + Ok(None) } } } @@ -178,7 +160,7 @@ where &self, _bundle: Arc>, _evaluator: Arc, - _trevm_instance: trevm::EvmNeedsCfg<'_, (), ConcurrentState>>>, + _db: ConcurrentState>>, ) -> Option>> where T: Tx + Send + Sync + 'static, @@ -188,44 +170,51 @@ where } } -/// Wraps a Db into an EvmFactory compatible [`Database`] -impl<'a, Db, Ext> DbConnect<'a> for SimulatorFactory +impl DbConnect for SimulatorFactory where Db: Database + DatabaseRef + DatabaseCommit + Sync + Send + Clone + 'static, - Ext: Sync + Clone, + Insp: Inspector>> + Sync + Send + Clone, { type Database = ConcurrentState; - type Error = Infallible; + type Error = TrevmBuilderError; - fn connect(&'a self) -> Result { + fn connect(&self) -> Result { let inner = ConcurrentState::new(self.db.clone(), ConcurrentStateInfo::default()); Ok(inner) } } /// Makes a SimulatorFactory capable of creating and configuring trevm instances -impl<'a, Db, Ext> EvmFactory<'a> for SimulatorFactory +impl EvmFactory for SimulatorFactory where Db: Database + DatabaseRef + DatabaseCommit + Sync + Send + Clone + 'static, - Ext: Sync + Clone, + Insp: Inspector>> + Sync + Send + Clone, { - type Ext = (); + type Insp = Insp; - /// Create makes a [`ConcurrentState`] database by calling connect - fn create(&'a self) -> Result, Self::Error> { + fn create( + &self, + ) -> std::result::Result, Self::Error> { let db = self.connect()?; - let trevm = trevm::revm::EvmBuilder::default().with_db(db).build_trevm(); - Ok(trevm) + let result = + TrevmBuilder::new().with_db(db).with_insp(self.inspector.clone()).build_trevm(); + match result { + Ok(t) => Ok(t), + Err(e) => Err(e.into()), + } } } -/// A trait for extracting transactions from -pub trait BlockExtractor: Send + Sync + 'static { +pub trait BlockExtractor +where + Db: Database + DatabaseCommit, + Insp: Inspector>, +{ /// BlockDriver runs the transactions over the provided trevm instance. - type Driver: BlockDriver: core::error::Error>; + type Driver: BlockDriver: core::error::Error>; /// Instantiate an configure a new [`trevm`] instance. - fn trevm(&self, db: Db) -> trevm::EvmNeedsBlock<'static, Ext, Db>; + fn trevm(&self, db: Db) -> trevm::EvmNeedsBlock; /// Extracts transactions from the source. /// @@ -233,7 +222,7 @@ pub trait BlockExtractor: Send + Sync + 'sta fn extract(&mut self, bytes: &[u8]) -> Self::Driver; } -impl BlockDriver for InProgressBlock { +impl BlockDriver for InProgressBlock { type Block = NoopBlock; type Error = Error; @@ -244,10 +233,13 @@ impl BlockDriver for InProgressBlock { /// Loops through the transactions in the block and runs them, accepting the state at the end /// if it was successful and returning and erroring out otherwise. - fn run_txns<'a, Db: Database + DatabaseCommit>( + fn run_txns( &mut self, - mut trevm: trevm::EvmNeedsTx<'a, Ext, Db>, - ) -> trevm::RunTxResult<'a, Ext, Db, Self> { + mut trevm: trevm::EvmNeedsTx, + ) -> trevm::RunTxResult + where + Insp: Inspector>, + { for tx in self.transactions().iter() { if tx.recover_signer().is_ok() { let sender = tx.recover_signer().unwrap(); @@ -272,8 +264,11 @@ impl BlockDriver for InProgressBlock { fn post_block( &mut self, - _trevm: &trevm::EvmNeedsBlock<'_, Ext, Db>, - ) -> Result<(), Self::Error> { + _trevm: &trevm::EvmNeedsBlock, + ) -> Result<(), Self::Error> + where + Insp: Inspector>, + { Ok(()) } } @@ -330,4 +325,4 @@ pub fn eval_fn(state: &ResultAndState) -> U256 { tracing::info!(balance = ?target_account.info.balance, "target account balance"); target_account.info.balance -} \ No newline at end of file +} diff --git a/tests/simulator_test.rs b/tests/simulator_test.rs index fd12e76..86e2f77 100644 --- a/tests/simulator_test.rs +++ b/tests/simulator_test.rs @@ -6,8 +6,8 @@ use alloy::signers::SignerSync as _; use alloy::transports::http::{Client, Http}; use builder::config::WalletlessProvider; use builder::tasks::simulator::SimulatorFactory; -use revm::db::{AlloyDB, CacheDB}; -use revm::primitives::{address, TxKind}; +use trevm::revm::database::{AlloyDB, CacheDB}; +use trevm::revm::primitives::{address, TxKind}; use std::sync::Arc; use tokio::sync::mpsc; use tokio::time::{Duration, Instant}; From 24742efe7be505cb79118c171018da333a3f1775 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 3 Apr 2025 09:45:07 -0600 Subject: [PATCH 3/5] wip: figuring out database trait bounds --- Cargo.toml | 2 +- src/tasks/simulator.rs | 17 +++++++---- tests/simulator_test.rs | 64 +++++++++++++++++++++++++---------------- 3 files changed, 51 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b447aa6..68ef17f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ signet-bundle = { git = "https://github.com/init4tech/signet-sdk", branch = "mai signet-types = { git = "https://github.com/init4tech/signet-sdk", branch = "main" } signet-zenith = { git = "https://github.com/init4tech/signet-sdk", branch = "main" } -trevm = { version = "0.20.4", features = [ "concurrent-db" ]} +trevm = { version = "0.20.4", features = [ "concurrent-db", "test-utils" ]} alloy = { version = "0.12.6", features = [ "full", diff --git a/src/tasks/simulator.rs b/src/tasks/simulator.rs index 3556d81..1b0d457 100644 --- a/src/tasks/simulator.rs +++ b/src/tasks/simulator.rs @@ -5,11 +5,11 @@ use eyre::Result; use std::sync::Arc; use tokio::{select, sync::mpsc::UnboundedReceiver, task::JoinSet}; use trevm::{ - db::sync::{ConcurrentState, ConcurrentStateInfo}, helpers::Ctx, revm::{ + db::{cow::CacheOnWrite, sync::{ConcurrentState, ConcurrentStateInfo}}, helpers::Ctx, revm::{ context::{ result::{EVMError, ExecutionResult, ResultAndState}, CfgEnv - }, inspector::inspectors::GasInspector, primitives::address, state::Account, Database, DatabaseCommit, DatabaseRef, Inspector - }, BlockDriver, Cfg, DbConnect, EvmFactory, NoopBlock, Trevm, TrevmBuilder, TrevmBuilderError, Tx + }, primitives::address, state::Account, Database, DatabaseCommit, DatabaseRef, Inspector + }, BlockDriver, Cfg, DbConnect, EvmFactory, NoopBlock, TrevmBuilder, TrevmBuilderError, Tx }; /// Tracks the EVM state, score, and result of an EVM execution. @@ -25,7 +25,7 @@ pub struct Best { pub score: S, } -/// Binds a database and an extension together. +/// Binds a database and an inspector together for simulation. #[derive(Debug, Clone)] pub struct SimulatorFactory { /// The database state the execution is carried out on. @@ -40,10 +40,15 @@ type SimResult = Result, ConcurrentState SimulatorFactory where - Insp: Inspector>> + Send + Sync + Clone + 'static, + Insp: Inspector>> + + Inspector>>>> + + Send + + Sync + + Clone + + 'static, Db: Database + DatabaseRef + DatabaseCommit + Send + Sync + Clone + 'static, { - /// Creates a new Simulator factory out of the database and extension. + /// Creates a new Simulator factory from the provided database and inspector. pub const fn new(db: Db, inspector: Insp) -> Self { Self { db, inspector } } diff --git a/tests/simulator_test.rs b/tests/simulator_test.rs index 86e2f77..5478914 100644 --- a/tests/simulator_test.rs +++ b/tests/simulator_test.rs @@ -1,19 +1,34 @@ -use alloy::consensus::{SignableTransaction as _, TxEip1559, TxEnvelope}; -use alloy::network::Ethereum; -use alloy::primitives::U256; -use alloy::signers::local::PrivateKeySigner; -use alloy::signers::SignerSync as _; -use alloy::transports::http::{Client, Http}; -use builder::config::WalletlessProvider; -use builder::tasks::simulator::SimulatorFactory; -use trevm::revm::database::{AlloyDB, CacheDB}; -use trevm::revm::primitives::{address, TxKind}; +use alloy::{ + consensus::{SignableTransaction as _, TxEip1559, TxEnvelope}, + eips::BlockId, + network::Ethereum, + primitives::U256, + providers::{Provider, ProviderBuilder}, + signers::SignerSync as _, + signers::local::PrivateKeySigner, +}; +use builder::{config::WalletlessProvider, tasks::simulator::SimulatorFactory}; use std::sync::Arc; -use tokio::sync::mpsc; -use tokio::time::{Duration, Instant}; -use trevm::revm::primitives::{Account, ExecutionResult, ResultAndState}; -use alloy::eips::BlockId; -use alloy::providers::{Provider, ProviderBuilder}; +use tokio::{ + sync::mpsc, + time::{Duration, Instant}, +}; +use trevm::{ + db::{ + cow::CacheOnWrite, + sync::{ConcurrentState, ConcurrentStateInfo}, + }, + revm::{ + context::result::{ExecutionResult, ResultAndState}, + database::{CacheDB, Database, DatabaseCommit, DatabaseRef, AlloyDB, WrapDatabaseAsync}, + primitives::{TxKind, address}, + inspector::NoOpInspector, + state::Account, + }, +}; + +// Define a type alias for the database used with SimulatorFactory. +type Db = WrapDatabaseAsync>; #[tokio::test(flavor = "multi_thread")] async fn test_spawn() { @@ -24,7 +39,7 @@ async fn test_spawn() { // Create a new anvil instance let anvil = - alloy::node_bindings::Anvil::new().block_time(1).chain_id(17003).try_spawn().unwrap(); + alloy::node_bindings::Anvil::new().block_time(1).chain_id(14174).try_spawn().unwrap(); // Create a test wallet from the anvil keys let keys = anvil.keys(); @@ -35,20 +50,19 @@ async fn test_spawn() { let latest = root_provider.get_block_number().await.unwrap(); // Create an alloyDB from the provider at the latest height - let alloy_db: AlloyDB, Ethereum, Arc> = - AlloyDB::new(Arc::new(root_provider.clone()), BlockId::Number(latest.into())).unwrap(); - let db = CacheDB::new(Arc::new(alloy_db)); - - // Define trevm extension, if any - let ext = (); + let alloy_db: AlloyDB = + AlloyDB::new(root_provider.clone(), BlockId::from(latest)); + let wrapped_db = WrapDatabaseAsync::new(alloy_db).unwrap(); + let concurrent_db = ConcurrentState::new(wrapped_db, ConcurrentStateInfo::default()); + // Define the evaluator function let evaluator = Arc::new(test_evaluator); // Create a simulation factory with the provided DB - let sim_factory = SimulatorFactory::new(db, ext); - let handle = - sim_factory.spawn::(tx_receiver, evaluator, deadline); + let sim_factory = SimulatorFactory::new(concurrent_db, NoOpInspector); + + let handle = sim_factory.spawn(tx_receiver, evaluator, deadline); // Send some transactions for count in 0..2 { From 3fac60cb1db5d60da8f1388115d9048e33172adb Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 3 Apr 2025 13:06:26 -0600 Subject: [PATCH 4/5] patched with in memory database as workaround for now --- Cargo.toml | 1 + src/tasks/simulator.rs | 208 +++++++++++++++------------------------- tests/simulator_test.rs | 57 ++++------- 3 files changed, 95 insertions(+), 171 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 68ef17f..f5a842a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ signet-types = { git = "https://github.com/init4tech/signet-sdk", branch = "main signet-zenith = { git = "https://github.com/init4tech/signet-sdk", branch = "main" } trevm = { version = "0.20.4", features = [ "concurrent-db", "test-utils" ]} +# trevm = { path = "../trevm", features = [ "concurrent-db", "test-utils" ]} alloy = { version = "0.12.6", features = [ "full", diff --git a/src/tasks/simulator.rs b/src/tasks/simulator.rs index 1b0d457..f68413e 100644 --- a/src/tasks/simulator.rs +++ b/src/tasks/simulator.rs @@ -5,11 +5,21 @@ use eyre::Result; use std::sync::Arc; use tokio::{select, sync::mpsc::UnboundedReceiver, task::JoinSet}; use trevm::{ - db::{cow::CacheOnWrite, sync::{ConcurrentState, ConcurrentStateInfo}}, helpers::Ctx, revm::{ + Cfg, DbConnect, NoopBlock, TrevmBuilder, TrevmBuilderError, Tx, + db::{ + cow::CacheOnWrite, + sync::{ConcurrentState, ConcurrentStateInfo}, + }, + helpers::Ctx, + revm::{ + Database, DatabaseCommit, DatabaseRef, Inspector, context::{ - result::{EVMError, ExecutionResult, ResultAndState}, CfgEnv - }, primitives::address, state::Account, Database, DatabaseCommit, DatabaseRef, Inspector - }, BlockDriver, Cfg, DbConnect, EvmFactory, NoopBlock, TrevmBuilder, TrevmBuilderError, Tx + CfgEnv, + result::{EVMError, ExecutionResult, ResultAndState}, + }, + primitives::address, + state::Account, + }, }; /// Tracks the EVM state, score, and result of an EVM execution. @@ -28,29 +38,33 @@ pub struct Best { /// Binds a database and an inspector together for simulation. #[derive(Debug, Clone)] pub struct SimulatorFactory { - /// The database state the execution is carried out on. - pub db: Db, /// The inspector pub inspector: Insp, + /// A CacheOnWrite that is cloneable + pub cow: MakeCow, } /// SimResult is an [`Option`] type that holds a tuple of a transaction and its associated /// state as a [`Db`] type updates if it was successfully executed. -type SimResult = Result, ConcurrentState>>)>>; +type SimResult = Result, CacheOnWrite>>)>>; impl SimulatorFactory where - Insp: Inspector>> - + Inspector>>>> + Insp: Inspector>>>>> + Send + Sync + Clone + 'static, Db: Database + DatabaseRef + DatabaseCommit + Send + Sync + Clone + 'static, + MakeCow: DbConnect>>>, { /// Creates a new Simulator factory from the provided database and inspector. - pub const fn new(db: Db, inspector: Insp) -> Self { - Self { db, inspector } + pub fn new(db: Db, inspector: Insp) -> Self { + let cdb = ConcurrentState::new(db, ConcurrentStateInfo::default()); + let cdb = Arc::new(cdb); + let cow = MakeCow::new(cdb); + + Self { inspector, cow } } /// Spawns a trevm simulator that runs until `deadline` is hit. @@ -68,7 +82,6 @@ where { tokio::spawn(async move { let mut join_set = JoinSet::new(); - let mut best: Option> = None; let mut block = InProgressBlock::new(); let sleep = tokio::time::sleep_until(deadline); @@ -76,52 +89,55 @@ where loop { select! { - _ = &mut sleep => break, - // Handle incoming + _ = &mut sleep => { + tracing::debug!("deadline reached, stopping simulation"); + break; + }, tx = inbound_tx.recv() => { + tracing::debug!("#### received transaction"); if let Some(inbound_tx) = tx { - // Setup the simulation environment - let sim = self.clone(); let eval = evaluator.clone(); - let db = self.connect().expect("must connect db"); - let mut parent_db = Arc::new(db); + let sim = self.clone(); + let db = self.cow.connect().unwrap(); join_set.spawn(async move { - let result = sim.simulate_tx(inbound_tx, eval, parent_db.child()); - - if let Ok(Some((best, db))) = result { - if let Ok(()) = parent_db.merge_child(db) { - tracing::debug!("merging updated simulation state"); - return Some(best) + let result = sim.simulate_tx(inbound_tx, eval, db.nest()); + match result { + Ok(Some((best, new_db))) => { + tracing::debug!("simulation completed, attempting to update state"); + // TODO: call cow.flatten on the nest instead + tracing::debug!("successfully merged simulation state"); + return Some(best); + } + Ok(None) => { + tracing::debug!("simulation returned no result"); + return None; + } + Err(e) => { + tracing::error!(e = ?e, "failed to simulate transaction"); + return None; } - tracing::error!("failed to update simulation state"); - None - } else { - None } }); } } Some(result) = join_set.join_next() => { + println!("join_set result"); match result { - Ok(Some(candidate)) => { - tracing::info!(tx_hash = ?candidate.tx.tx_hash(), "ingesting transaction"); - block.ingest_tx(candidate.tx.as_ref()); - - if candidate.score > best.as_ref().map(|b| b.score).unwrap_or_default() { - tracing::info!(score = ?candidate.score, "new best candidate found"); - best = Some(candidate); - } - } + Ok(Some(best)) => { + println!("simulation completed"); + block.ingest_tx(best.tx.as_ref()); + }, Ok(None) => { + println!("simulation returned no result"); tracing::debug!("simulation returned no result"); } Err(e) => { - tracing::error!("simulation task failed: {}", e); + println!("simulation returned an error: {}", e); + tracing::error!(e = ?e, "failed to simulate transaction"); } } } - else => break, } } @@ -131,10 +147,10 @@ where /// Simulates an inbound tx and applies its state if it's successfully simualted pub fn simulate_tx( - self, + &self, tx: TxEnvelope, evaluator: Arc, - db: ConcurrentState>>, + db: CacheOnWrite>>>, ) -> SimResult where F: Fn(&ResultAndState) -> U256 + Send + Sync + 'static, @@ -151,6 +167,9 @@ where let score = evaluator(&result); let best = Best { tx: Arc::new(tx), result, score }; + // Flatten to save the result to the parent and return it + let db = db.flatten(); + Ok(Some((best, db))) } Err(terr) => { @@ -175,106 +194,31 @@ where } } -impl DbConnect for SimulatorFactory -where - Db: Database + DatabaseRef + DatabaseCommit + Sync + Send + Clone + 'static, - Insp: Inspector>> + Sync + Send + Clone, -{ - type Database = ConcurrentState; - type Error = TrevmBuilderError; - - fn connect(&self) -> Result { - let inner = ConcurrentState::new(self.db.clone(), ConcurrentStateInfo::default()); - Ok(inner) - } -} +/// MakeCow wraps a ConcurrentState database in an Arc to allow for cloning. +#[derive(Debug, Clone)] +pub struct MakeCow(Arc>); -/// Makes a SimulatorFactory capable of creating and configuring trevm instances -impl EvmFactory for SimulatorFactory +impl MakeCow where - Db: Database + DatabaseRef + DatabaseCommit + Sync + Send + Clone + 'static, - Insp: Inspector>> + Sync + Send + Clone, + Db: Database + DatabaseRef + DatabaseCommit + Send + Sync + 'static, { - type Insp = Insp; - - fn create( - &self, - ) -> std::result::Result, Self::Error> { - let db = self.connect()?; - let result = - TrevmBuilder::new().with_db(db).with_insp(self.inspector.clone()).build_trevm(); - match result { - Ok(t) => Ok(t), - Err(e) => Err(e.into()), - } + /// Returns a new CoW Db that implements Clone for use in DbConnect + pub fn new(db: Arc>) -> Self { + Self(db) } } -pub trait BlockExtractor +impl DbConnect for MakeCow where - Db: Database + DatabaseCommit, - Insp: Inspector>, + Db: Database + DatabaseRef + DatabaseCommit + Sync + Send + Clone + 'static, { - /// BlockDriver runs the transactions over the provided trevm instance. - type Driver: BlockDriver: core::error::Error>; - - /// Instantiate an configure a new [`trevm`] instance. - fn trevm(&self, db: Db) -> trevm::EvmNeedsBlock; - - /// Extracts transactions from the source. - /// - /// Extraction is infallible. Worst case it should return a no-op driver. - fn extract(&mut self, bytes: &[u8]) -> Self::Driver; -} - -impl BlockDriver for InProgressBlock { - type Block = NoopBlock; - - type Error = Error; - - fn block(&self) -> &Self::Block { - &NoopBlock - } - - /// Loops through the transactions in the block and runs them, accepting the state at the end - /// if it was successful and returning and erroring out otherwise. - fn run_txns( - &mut self, - mut trevm: trevm::EvmNeedsTx, - ) -> trevm::RunTxResult - where - Insp: Inspector>, - { - for tx in self.transactions().iter() { - if tx.recover_signer().is_ok() { - let sender = tx.recover_signer().unwrap(); - tracing::info!(sender = ?sender, tx_hash = ?tx.tx_hash(), "simulating transaction"); - - let t = match trevm.run_tx(tx) { - Ok(t) => t, - Err(e) => { - if e.is_transaction_error() { - return Ok(e.discard_error()); - } else { - return Err(e.err_into()); - } - } - }; - - (_, trevm) = t.accept(); - } - } - Ok(trevm) - } + type Database = CacheOnWrite>>; + type Error = TrevmBuilderError; - fn post_block( - &mut self, - _trevm: &trevm::EvmNeedsBlock, - ) -> Result<(), Self::Error> - where - Insp: Inspector>, - { - Ok(()) + /// Connects to the database and returns a CacheOnWrite instance + fn connect(&self) -> Result { + let db: CacheOnWrite>> = CacheOnWrite::new(self.0.clone()); + Ok(db) } } diff --git a/tests/simulator_test.rs b/tests/simulator_test.rs index 5478914..5f57f46 100644 --- a/tests/simulator_test.rs +++ b/tests/simulator_test.rs @@ -1,70 +1,49 @@ use alloy::{ consensus::{SignableTransaction as _, TxEip1559, TxEnvelope}, - eips::BlockId, - network::Ethereum, primitives::U256, - providers::{Provider, ProviderBuilder}, signers::SignerSync as _, signers::local::PrivateKeySigner, }; -use builder::{config::WalletlessProvider, tasks::simulator::SimulatorFactory}; +use builder::tasks::simulator::SimulatorFactory; use std::sync::Arc; use tokio::{ sync::mpsc, time::{Duration, Instant}, }; -use trevm::{ - db::{ - cow::CacheOnWrite, - sync::{ConcurrentState, ConcurrentStateInfo}, - }, - revm::{ - context::result::{ExecutionResult, ResultAndState}, - database::{CacheDB, Database, DatabaseCommit, DatabaseRef, AlloyDB, WrapDatabaseAsync}, - primitives::{TxKind, address}, - inspector::NoOpInspector, - state::Account, - }, +use trevm::revm::{ + context::result::{ExecutionResult, ResultAndState}, + database::{CacheDB, InMemoryDB}, + inspector::NoOpInspector, + primitives::{TxKind, address}, + state::Account, }; -// Define a type alias for the database used with SimulatorFactory. -type Db = WrapDatabaseAsync>; - #[tokio::test(flavor = "multi_thread")] async fn test_spawn() { // Setup transaction pipeline plumbing let (tx_sender, tx_receiver) = mpsc::unbounded_channel::(); - let (_bundle_sender, bundle_receiver) = mpsc::unbounded_channel::>(); - let deadline = Instant::now() + Duration::from_secs(2); + let (_bundle_sender, _bundle_receiver) = mpsc::unbounded_channel::>(); + let deadline = Instant::now() + Duration::from_secs(5); - // Create a new anvil instance + // Create a new anvil instance and test wallets let anvil = alloy::node_bindings::Anvil::new().block_time(1).chain_id(14174).try_spawn().unwrap(); - - // Create a test wallet from the anvil keys let keys = anvil.keys(); let test_wallet = &PrivateKeySigner::from(keys[0].clone()); - // Create a root provider on that anvil instance - let root_provider = ProviderBuilder::new().on_http(anvil.endpoint_url()); - let latest = root_provider.get_block_number().await.unwrap(); - - // Create an alloyDB from the provider at the latest height - let alloy_db: AlloyDB = - AlloyDB::new(root_provider.clone(), BlockId::from(latest)); - - let wrapped_db = WrapDatabaseAsync::new(alloy_db).unwrap(); - let concurrent_db = ConcurrentState::new(wrapped_db, ConcurrentStateInfo::default()); - - // Define the evaluator function + // Create a evaluator let evaluator = Arc::new(test_evaluator); - // Create a simulation factory with the provided DB - let sim_factory = SimulatorFactory::new(concurrent_db, NoOpInspector); + // Make a CoW database + let db = CacheDB::new(InMemoryDB::default()); + + // Create a new simulator factory with the given database and inspector + let sim_factory = SimulatorFactory::new(db, NoOpInspector); + // Spawn the simulator actor let handle = sim_factory.spawn(tx_receiver, evaluator, deadline); - // Send some transactions + // Send transactions to the simulator for count in 0..2 { let test_tx = new_test_tx(test_wallet, count).unwrap(); tx_sender.send(test_tx).unwrap(); From e984e63f91f0040a6e339632da31efdc699de0df Mon Sep 17 00:00:00 2001 From: dylan Date: Tue, 8 Apr 2025 16:36:32 -0600 Subject: [PATCH 5/5] seed database for unit tests - adds a seed_database function for seeding the cache db during testing - gets tests passing to validate sim logic --- Cargo.toml | 1 - src/tasks/bundler.rs | 2 +- src/tasks/simulator.rs | 2 +- tests/simulator_test.rs | 24 ++++++++++++++++++------ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f5a842a..68ef17f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,6 @@ signet-types = { git = "https://github.com/init4tech/signet-sdk", branch = "main signet-zenith = { git = "https://github.com/init4tech/signet-sdk", branch = "main" } trevm = { version = "0.20.4", features = [ "concurrent-db", "test-utils" ]} -# trevm = { path = "../trevm", features = [ "concurrent-db", "test-utils" ]} alloy = { version = "0.12.6", features = [ "full", diff --git a/src/tasks/bundler.rs b/src/tasks/bundler.rs index d3ec34e..d1b1220 100644 --- a/src/tasks/bundler.rs +++ b/src/tasks/bundler.rs @@ -4,11 +4,11 @@ use crate::tasks::oauth::Authenticator; use oauth2::TokenResponse; use reqwest::Url; use serde::{Deserialize, Serialize}; +use signet_bundle::SignetEthBundle; use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel}; use tokio::task::JoinHandle; use tokio::time; use tracing::{Instrument, debug, trace}; -use signet_bundle::SignetEthBundle; /// Holds a bundle from the cache with a unique ID and a Zenith bundle. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Bundle { diff --git a/src/tasks/simulator.rs b/src/tasks/simulator.rs index f68413e..2e485e7 100644 --- a/src/tasks/simulator.rs +++ b/src/tasks/simulator.rs @@ -94,7 +94,7 @@ where break; }, tx = inbound_tx.recv() => { - tracing::debug!("#### received transaction"); + tracing::debug!(tx = ?tx, "received transaction"); if let Some(inbound_tx) = tx { let eval = evaluator.clone(); let sim = self.clone(); diff --git a/tests/simulator_test.rs b/tests/simulator_test.rs index 5f57f46..41d5a42 100644 --- a/tests/simulator_test.rs +++ b/tests/simulator_test.rs @@ -1,8 +1,10 @@ use alloy::{ consensus::{SignableTransaction as _, TxEip1559, TxEnvelope}, primitives::U256, - signers::SignerSync as _, - signers::local::PrivateKeySigner, + signers::{ + SignerSync as _, + local::{LocalSigner, PrivateKeySigner}, + }, }; use builder::tasks::simulator::SimulatorFactory; use std::sync::Arc; @@ -15,13 +17,14 @@ use trevm::revm::{ database::{CacheDB, InMemoryDB}, inspector::NoOpInspector, primitives::{TxKind, address}, - state::Account, + state::{Account, AccountInfo}, }; #[tokio::test(flavor = "multi_thread")] async fn test_spawn() { // Setup transaction pipeline plumbing - let (tx_sender, tx_receiver) = mpsc::unbounded_channel::(); + let unbounded_channel = mpsc::unbounded_channel::(); + let (tx_sender, tx_receiver) = unbounded_channel; let (_bundle_sender, _bundle_receiver) = mpsc::unbounded_channel::>(); let deadline = Instant::now() + Duration::from_secs(5); @@ -34,8 +37,8 @@ async fn test_spawn() { // Create a evaluator let evaluator = Arc::new(test_evaluator); - // Make a CoW database - let db = CacheDB::new(InMemoryDB::default()); + // Make a database and seed it with some starting account state + let db = seed_database(CacheDB::new(InMemoryDB::default()), test_wallet); // Create a new simulator factory with the given database and inspector let sim_factory = SimulatorFactory::new(db, NoOpInspector); @@ -88,3 +91,12 @@ fn new_test_tx(wallet: &PrivateKeySigner, nonce: u64) -> eyre::Result, wallet: &PrivateKeySigner) -> CacheDB { + let mut info = AccountInfo::default(); + info.balance = U256::from(10000); + db.insert_account_info(wallet.address(), info); + + db +}