From 33fe87b966b366f2e8bc219312b795307b9f8487 Mon Sep 17 00:00:00 2001 From: Fraser Hutchison <190532+Fraser999@users.noreply.github.com> Date: Wed, 4 Mar 2026 23:42:26 +0000 Subject: [PATCH 1/4] fix: use async db in sim_round --- Cargo.toml | 5 +- crates/sim/src/cache/item.rs | 22 +++---- crates/sim/src/cache/mod.rs | 2 +- crates/sim/src/cache/state.rs | 45 ++++++++------ crates/sim/src/cache/store.rs | 108 +++++++++++----------------------- crates/sim/src/env/shared.rs | 85 ++++++++++++++++++++++++-- crates/sim/src/env/sim_env.rs | 16 +---- crates/sim/src/lib.rs | 4 +- crates/sim/src/task.rs | 58 +++++++++++++++--- crates/test-utils/src/evm.rs | 43 ++++++++++++-- 10 files changed, 253 insertions(+), 135 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7fc4a64d..706e8f87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ signet-zenith = { version = "0.16.0-rc.12", path = "crates/zenith" } signet-test-utils = { version = "0.16.0-rc.12", path = "crates/test-utils" } # trevm -trevm = { version = "0.34.0", features = ["full_env_cfg"] } +trevm = { version = "0.34.0", features = ["full_env_cfg", "asyncdb"] } # Alloy periphery crates alloy-core = "1.4" @@ -82,3 +82,6 @@ uuid = "1.16.0" # Test Utils alloy-rlp = "0.3.11" + +[patch.crates-io] +trevm = { git = "https://github.com/init4tech/trevm.git", branch = "fraser/eng-1944/fix-hang" } diff --git a/crates/sim/src/cache/item.rs b/crates/sim/src/cache/item.rs index c462d7c0..446c32ed 100644 --- a/crates/sim/src/cache/item.rs +++ b/crates/sim/src/cache/item.rs @@ -113,12 +113,11 @@ impl SimItem { } } - fn check_tx(&self, source: &S) -> Result> + async fn check_tx(&self, source: &S) -> Result> where S: StateSource, { let item = self.as_tx().expect("SimItem is not a Tx"); - let total = U256::from(item.max_fee_per_gas() * item.gas_limit() as u128) + item.value(); source @@ -139,10 +138,11 @@ impl SimItem { // nonce is equal and balance is sufficient SimItemValidity::Now }) + .await .map_err(Into::into) } - fn check_bundle_tx_list( + async fn check_bundle_tx_list( items: impl Iterator, source: &S, ) -> Result @@ -160,7 +160,7 @@ impl SimItem { // Peek to perform the balance check for the first tx if let Some(first) = items.peek() { - let info = source.account_details(&first.signer)?; + let info = source.account_details(&first.signer).await?; // check balance for the first tx is sufficient if first.balance > info.balance { @@ -175,7 +175,7 @@ impl SimItem { let state_nonce = match nonce_cache.get(&requirement.signer) { Some(cached_nonce) => *cached_nonce, None => { - let nonce = source.nonce(&requirement.signer)?; + let nonce = source.nonce(&requirement.signer).await?; nonce_cache.insert(requirement.signer, nonce); nonce } @@ -198,7 +198,7 @@ impl SimItem { Ok(SimItemValidity::Now) } - fn check_bundle( + async fn check_bundle( &self, source: &S, host_source: &S2, @@ -209,8 +209,8 @@ impl SimItem { { let item = self.as_bundle().expect("SimItem is not a Bundle"); - let ru_tx = Self::check_bundle_tx_list(item.tx_reqs(), source)?; - let host_tx = Self::check_bundle_tx_list(item.host_tx_reqs(), host_source)?; + let ru_tx = Self::check_bundle_tx_list(item.tx_reqs(), source).await?; + let host_tx = Self::check_bundle_tx_list(item.host_tx_reqs(), host_source).await?; // Check both the regular txs and the host txs. Ok(ru_tx.min(host_tx)) @@ -220,7 +220,7 @@ impl SimItem { /// /// This will check that nonces and balances are sufficient for the item to /// be included on the current state. - pub fn check( + pub async fn check( &self, source: &S, host_source: &S2, @@ -230,8 +230,8 @@ impl SimItem { S2: StateSource, { match self { - SimItem::Bundle(_) => self.check_bundle(source, host_source), - SimItem::Tx(_) => self.check_tx(source), + SimItem::Bundle(_) => self.check_bundle(source, host_source).await, + SimItem::Tx(_) => self.check_tx(source).await, } } } diff --git a/crates/sim/src/cache/mod.rs b/crates/sim/src/cache/mod.rs index 4ec67e82..c21f9234 100644 --- a/crates/sim/src/cache/mod.rs +++ b/crates/sim/src/cache/mod.rs @@ -5,7 +5,7 @@ mod item; pub use item::{SimIdentifier, SimItem}; mod state; -pub use state::StateSource; +pub use state::{AcctInfo, StateSource}; mod store; pub use store::SimCache; diff --git a/crates/sim/src/cache/state.rs b/crates/sim/src/cache/state.rs index d308675e..f913e2c6 100644 --- a/crates/sim/src/cache/state.rs +++ b/crates/sim/src/cache/state.rs @@ -1,4 +1,6 @@ use alloy::primitives::{Address, U256}; +use core::future::Future; +use trevm::revm::database_interface::async_db::DatabaseAsyncRef; /// Account information including nonce and balance. This is partially modeled /// after [`revm::AccountInfo`], but only includes the fields we care about. @@ -6,48 +8,57 @@ use alloy::primitives::{Address, U256}; /// [`revm::AccountInfo`]: trevm::revm::state::AccountInfo #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AcctInfo { + /// The account nonce. pub nonce: u64, + /// The account balance. pub balance: U256, + /// Whether the account has deployed code. pub has_code: bool, } /// A source for nonce and balance information. Exists to simplify type bounds /// in various places. -pub trait StateSource { - /// The error type for state lookups, usually a database error. - type Error: core::error::Error + 'static; +pub trait StateSource: Send + Sync { + /// The error type for state lookups. + type Error: core::error::Error + Send + 'static; /// Get account details for an address. - fn account_details(&self, address: &Address) -> Result; + fn account_details( + &self, + address: &Address, + ) -> impl Future> + Send; - /// Get the nonce for an address. This should return the NEXT EXPECTED - /// nonce. I.e. `0` for an address that has never sent a transaction, 1 for an address that has sent exactly one transaction, etc. - fn nonce(&self, address: &Address) -> Result { - self.account_details(address).map(|info| info.nonce) + /// Get the nonce for an address. Returns the NEXT EXPECTED nonce, i.e. `0` for an address that + /// has never sent a transaction, 1 for an address that has sent exactly one transaction, etc. + fn nonce(&self, address: &Address) -> impl Future> + Send { + async { self.account_details(address).await.map(|info| info.nonce) } } /// Get the balance for an address. - fn balance(&self, address: &Address) -> Result { - self.account_details(address).map(|info| info.balance) + fn balance(&self, address: &Address) -> impl Future> + Send { + async { self.account_details(address).await.map(|info| info.balance) } } /// Run an arbitrary check on the account details for an address. - fn map T>(&self, address: &Address, f: F) -> Result { - self.account_details(address).map(|info| f(&info)) + fn map T + Send>( + &self, + address: &Address, + f: F, + ) -> impl Future> + Send { + async { self.account_details(address).await.map(|info| f(&info)) } } } impl StateSource for Db where - Db: trevm::revm::DatabaseRef, + Db: DatabaseAsyncRef + Send + Sync, + Db::Error: Send + 'static, { type Error = Db::Error; - fn account_details(&self, address: &Address) -> Result { - let info = self.basic_ref(*address)?.unwrap_or_default(); - + async fn account_details(&self, address: &Address) -> Result { + let info = self.basic_async_ref(*address).await?.unwrap_or_default(); let has_code = info.code_hash() != trevm::revm::primitives::KECCAK_EMPTY; - Ok(AcctInfo { nonce: info.nonce, balance: info.balance, has_code }) } } diff --git a/crates/sim/src/cache/store.rs b/crates/sim/src/cache/store.rs index d0d0770f..ff07d5c6 100644 --- a/crates/sim/src/cache/store.rs +++ b/crates/sim/src/cache/store.rs @@ -64,73 +64,6 @@ impl SimCache { vec } - /// Iter over the best items in the cache, writing only those that pass - /// preflight validity checks (nonce and initial fee) to the buffer. - /// - /// The state sources are used to validate the items against the current - /// nonce and balance, to prevent simulating invalid items. - /// - /// This will additionally remove items that can _never_ be valid from the - /// cache. - /// - /// When an error is encountered, the process stops and the error is - /// returned. At this point, the buffer may be partially written. - pub fn write_best_valid_to( - &self, - buf: &mut [MaybeUninit<(u128, SimItem)>], - source: &S, - host_source: &S2, - ) -> Result> - where - S: StateSource, - S2: StateSource, - { - let mut cache = self.inner.upgradable_read(); - let mut slots = buf.iter_mut(); - let start = slots.len(); - - let mut never = Vec::new(); - - // Traverse the cache in reverse order (best items first), checking - // each item. - // - // Errors are shortcut by `try_for_each`. Passes are written to the - // buffer, consuming slots. Once no slots are left, the try_for_each - // returns early. - let res = cache - .items - .iter() - .rev() - .map(|(rank, item)| { - item.check(source, host_source).map(|validity| (validity, rank, item)) - }) - .try_for_each(|result| { - if slots.len() == 0 { - return Ok(()); - } - let (validity, rank, item) = result?; - - if validity.is_valid_now() { - slots.next().expect("checked by len").write((*rank, item.clone())); - } - if validity.is_never_valid() { - never.push(*rank); - } - - Ok(()) - }) - .map(|_| start - slots.len()); - - cache.with_upgraded(|cache| { - // Remove never valid items from the cache - never.iter().for_each(|rank| { - cache.remove_and_disallow(*rank); - }); - }); - - res - } - /// Get up to the `n` best items in the cache that pass preflight validity /// checks (nonce and initial fee). The returned vector may be smaller than /// `n` if not enough valid items are found. @@ -140,7 +73,7 @@ impl SimCache { /// /// The state sources are used to validate the items against the current /// nonce and balance, to prevent simulating invalid items. - pub fn read_best_valid( + pub async fn read_best_valid( &self, n: usize, source: &S, @@ -150,11 +83,40 @@ impl SimCache { S: StateSource, S2: StateSource, { - let mut vec = Vec::with_capacity(n); - let n = self.write_best_valid_to(vec.spare_capacity_mut(), source, host_source)?; - // SAFETY: We just wrote n items. - unsafe { vec.set_len(n) }; - Ok(vec) + // Snapshot the top items under a short-lived read lock. + let candidates: Vec<(u128, SimItem)> = { + let cache = self.inner.read(); + // Traverse the cache in reverse order (best items first). + cache.items.iter().rev().take(n).map(|(rank, item)| (*rank, item.clone())).collect() + }; + + let mut valid = Vec::with_capacity(n); + let mut never = Vec::new(); + + for (rank, item) in &candidates { + if valid.len() >= n { + break; + } + + let validity = item.check(source, host_source).await?; + + if validity.is_valid_now() { + valid.push((*rank, item.clone())); + } + if validity.is_never_valid() { + never.push(*rank); + } + } + + // Remove never-valid items under a write lock. + if !never.is_empty() { + let mut cache = self.inner.write(); + for rank in never { + cache.remove_and_disallow(rank); + } + } + + Ok(valid) } /// Get the number of items in the cache. diff --git a/crates/sim/src/env/shared.rs b/crates/sim/src/env/shared.rs index 69e6dc6e..86290524 100644 --- a/crates/sim/src/env/shared.rs +++ b/crates/sim/src/env/shared.rs @@ -1,13 +1,44 @@ -use crate::{env::RollupEnv, outcome::SimulatedItem, HostEnv, SimCache, SimDb, SimEnv}; +use crate::{ + cache::StateSource, env::RollupEnv, outcome::SimulatedItem, AcctInfo, HostEnv, SimCache, SimDb, + SimEnv, +}; +use alloy::primitives::Address; use core::fmt; use std::{ops::Deref, sync::Arc}; use tokio::{select, sync::watch}; -use tracing::{debug, trace, trace_span}; +use tracing::{debug, trace, trace_span, warn}; use trevm::{ + db::TryCachingDb, helpers::Ctx, - revm::{inspector::NoOpInspector, DatabaseRef, Inspector}, + revm::{database::Cache, inspector::NoOpInspector, DatabaseRef, Inspector}, }; +/// Composite async source that overlays the sim env's committed cache +/// on top of a fallback [`StateSource`]. +/// +/// Accounts whose state was modified by prior sim rounds (present in +/// the cache) are returned directly, avoiding async I/O. Accounts not +/// in the cache fall through to the asynchronous source. +struct CachedAsyncSource<'a, S> { + cache: &'a Cache, + fallback: &'a S, +} + +impl StateSource for CachedAsyncSource<'_, S> { + type Error = S::Error; + + async fn account_details(&self, address: &Address) -> Result { + if let Some(acct) = self.cache.accounts.get(address) { + return Ok(AcctInfo { + nonce: acct.info.nonce, + balance: acct.info.balance, + has_code: acct.info.code_hash() != trevm::revm::primitives::KECCAK_EMPTY, + }); + } + self.fallback.account_details(address).await + } +} + /// A simulation environment. /// /// Contains enough information to run a simulation. @@ -89,9 +120,53 @@ where } /// Run a simulation round, returning the best item. - pub async fn sim_round(&mut self, max_gas: u64, max_host_gas: u64) -> Option { + /// + /// Preflight validity checks (nonce/balance) are performed asynchronously + /// using the provided [`StateSource`]s. This avoids the tokio I/O + /// driver starvation deadlock that occurs when sync `DatabaseRef` calls + /// go through `block_in_place` + `Handle::block_on`. + pub async fn sim_round( + &mut self, + max_gas: u64, + max_host_gas: u64, + async_ru_source: &AS, + async_host_source: &AH, + ) -> Option + where + AS: StateSource, + AH: StateSource, + { let span = trace_span!("sim_round", max_gas, max_host_gas).or_current(); + // Overlay the sim env's committed cache so that accounts touched + // by prior rounds (e.g. nonce bumps) are visible to the preflight + // validity check without requiring async I/O. + let ru_source = CachedAsyncSource { + cache: self.inner.rollup_env().db().cache(), + fallback: async_ru_source, + }; + let host_source = CachedAsyncSource { + cache: self.inner.host_env().db().cache(), + fallback: async_host_source, + }; + + let active_sim = match self + .inner + .sim_items() + .read_best_valid(self.inner.concurrency_limit(), &ru_source, &host_source) + .await + { + Ok(items) => items, + Err(error) => { + warn!(%error, "preflight validity check failed"); + return None; + } + }; + + if active_sim.is_empty() { + return None; + } + // These will be moved into the blocking task. let scope_span = span.clone(); let this = self.inner.clone(); @@ -99,7 +174,7 @@ where // Spawn a blocking task to run the simulations. let sim_task = tokio::task::spawn_blocking(move || { - scope_span.in_scope(|| this.sim_round(max_gas, max_host_gas, best_tx)) + scope_span.in_scope(|| this.sim_round(max_gas, max_host_gas, best_tx, active_sim)) }); // Either simulation is done, or we time out diff --git a/crates/sim/src/env/sim_env.rs b/crates/sim/src/env/sim_env.rs index f4e1bf9d..c28bdb9e 100644 --- a/crates/sim/src/env/sim_env.rs +++ b/crates/sim/src/env/sim_env.rs @@ -6,7 +6,7 @@ use signet_evm::SignetInspector; use signet_types::constants::SignetSystemConstants; use std::{borrow::Cow, sync::Arc}; use tokio::sync::{mpsc, watch}; -use tracing::{instrument, trace, trace_span, warn}; +use tracing::{instrument, trace, trace_span}; use trevm::{ helpers::Ctx, revm::{ @@ -259,20 +259,8 @@ where max_gas: u64, max_host_gas: u64, best_tx: watch::Sender>, + active_sim: Vec<(u128, SimItem)>, ) { - // Pull the `n` best items from the cache. - let active_sim = match self.sim_items.read_best_valid( - self.concurrency_limit, - &self.rollup_env().db(), - &self.host_env().db(), - ) { - Ok(items) => items, - Err(error) => { - warn!(%error, "State access error during sim round preflight"); - return; - } - }; - // Create a channel to send the results back. let (candidates, mut candidates_rx) = mpsc::channel(self.concurrency_limit); diff --git a/crates/sim/src/lib.rs b/crates/sim/src/lib.rs index d841454e..784aea1b 100644 --- a/crates/sim/src/lib.rs +++ b/crates/sim/src/lib.rs @@ -19,7 +19,9 @@ mod built; pub use built::BuiltBlock; mod cache; -pub use cache::{CacheError, SimCache, SimIdentifier, SimItem, SimItemValidity, StateSource}; +pub use cache::{ + AcctInfo, CacheError, SimCache, SimIdentifier, SimItem, SimItemValidity, StateSource, +}; mod env; pub use env::{HostEnv, RollupEnv, SharedSimEnv, SimEnv}; diff --git a/crates/sim/src/task.rs b/crates/sim/src/task.rs index 0583a052..a4fbc124 100644 --- a/crates/sim/src/task.rs +++ b/crates/sim/src/task.rs @@ -1,4 +1,6 @@ -use crate::{env::SimEnv, BuiltBlock, HostEnv, RollupEnv, SharedSimEnv, SimCache, SimDb}; +use crate::{ + cache::StateSource, env::SimEnv, BuiltBlock, HostEnv, RollupEnv, SharedSimEnv, SimCache, SimDb, +}; use std::time::Duration; use tokio::select; use tracing::{debug, trace}; @@ -12,10 +14,23 @@ pub(crate) const SIM_SLEEP_MS: u64 = 50; /// Builds a single block by repeatedly invoking [`SimEnv`]. #[derive(Debug)] -pub struct BlockBuild { +pub struct BlockBuild< + RuDb, + HostDb, + RuAsync, + HostAsync, + RuInsp = NoOpInspector, + HostInsp = NoOpInspector, +> { /// The simulation environment. env: SharedSimEnv, + /// Async state source for the rollup chain, used for preflight validity checks. + ru_async_source: RuAsync, + + /// Async state source for the host chain, used for preflight validity checks. + host_async_source: HostAsync, + /// The block being built. block: BuiltBlock, @@ -30,14 +45,18 @@ pub struct BlockBuild BlockBuild +impl + BlockBuild where RuDb: DatabaseRef + Send + Sync + 'static, RuInsp: Inspector>> + Default + Sync + 'static, HostDb: DatabaseRef + Send + Sync + 'static, HostInsp: Inspector>> + Default + Sync + 'static, + RuAsync: StateSource, + HostAsync: StateSource, { /// Create a new block building process. + #[expect(clippy::too_many_arguments, reason = "should be refactored to avoid this warning")] pub fn new( rollup: RollupEnv, host: HostEnv, @@ -46,6 +65,8 @@ where sim_items: SimCache, max_gas: u64, max_host_gas: u64, + ru_async_source: RuAsync, + host_async_source: HostAsync, ) -> Self { let number = rollup.block().number; @@ -59,6 +80,8 @@ where let finish_by = env.finish_by(); Self { env: env.into(), + ru_async_source, + host_async_source, block: BuiltBlock::new(number.to()), finish_by, max_gas, @@ -114,7 +137,16 @@ where let gas_allowed = self.max_gas - self.block.gas_used(); let host_gas_allowed = self.max_host_gas - self.block.host_gas_used(); - if let Some(simulated) = self.env.sim_round(gas_allowed, host_gas_allowed).await { + if let Some(simulated) = self + .env + .sim_round( + gas_allowed, + host_gas_allowed, + &self.ru_async_source, + &self.host_async_source, + ) + .await + { debug!( score = %simulated.score, gas_used = simulated.gas_used, @@ -163,6 +195,7 @@ where } // If there are items to simulate, we run a simulation round. + let prev_tx_count = self.block.transactions.len(); let fut = self.round(); select! { @@ -179,6 +212,13 @@ where trace!(remaining_items, "Round completed"); } } + + // If the round didn't produce any new transactions (e.g. all + // cached items have Future validity), sleep to avoid spinning + // and allow new items to arrive. + if self.block.transactions.len() == prev_tx_count { + tokio::time::sleep_until(next_round_time).await; + } } debug!(rounds = i, transactions = self.block.transactions.len(), "Building completed",); @@ -194,19 +234,21 @@ where #[cfg(test)] mod test { - use std::future::Future; - use super::*; + use crate::cache::StateSource; + use std::future::Future; /// Compile-time check to ensure that the block building process is /// `Send`. - fn _build_fut_is_send( - b: BlockBuild, + fn _build_fut_is_send( + b: BlockBuild, ) where RuDb: DatabaseRef + Send + Sync + 'static, RuInsp: Inspector>> + Default + Sync + 'static, HostDb: DatabaseRef + Send + Sync + 'static, HostInsp: Inspector>> + Default + Sync + 'static, + RuAsync: StateSource, + HostAsync: StateSource, { let _: Box + Send> = Box::new(b.build()); } diff --git a/crates/test-utils/src/evm.rs b/crates/test-utils/src/evm.rs index d9c1e4b3..e282b75d 100644 --- a/crates/test-utils/src/evm.rs +++ b/crates/test-utils/src/evm.rs @@ -16,7 +16,7 @@ use alloy::{ primitives::{Address, Bytes, KECCAK256_EMPTY, U256}, }; use signet_constants::test_utils::*; -use signet_sim::{BlockBuild, HostEnv, RollupEnv}; +use signet_sim::{AcctInfo, BlockBuild, HostEnv, RollupEnv, StateSource}; use trevm::{ helpers::Ctx, revm::{ @@ -112,12 +112,47 @@ pub fn host_sim_env() -> HostEnv, NoOpInspector> { HostEnv::new(host_db, TEST_SYS, &HostTestCfg, &NoopBlock) } +/// Async state source adapter for sync in-memory databases. +/// +/// Wraps an `Arc` and implements [`StateSource`] by delegating to sync +/// `DatabaseRef` methods. Suitable for tests where no real I/O occurs. +#[derive(Clone)] +pub struct SyncAsyncSource(pub Arc); + +impl StateSource for SyncAsyncSource { + type Error = ::Error; + + async fn account_details(&self, address: &Address) -> Result { + use trevm::revm::DatabaseRef; + let info = self.0.basic_ref(*address)?.unwrap_or_default(); + let has_code = info.code_hash() != trevm::revm::primitives::KECCAK_EMPTY; + Ok(AcctInfo { nonce: info.nonce, balance: info.balance, has_code }) + } +} + /// Create a [`BlockBuild`] simulator environment for testing. pub fn test_sim_env( deadline: tokio::time::Instant, -) -> BlockBuild, Arc> { - let (ru_evm, host_evm) = (rollup_sim_env(), host_sim_env()); - BlockBuild::new(ru_evm, host_evm, deadline, 10, Default::default(), 50_000_000, 50_000_000) +) -> BlockBuild, Arc, SyncAsyncSource, SyncAsyncSource> { + let ru_evm = rollup_sim_env(); + let host_evm = host_sim_env(); + + let mut ru_async_db = InMemoryDB::default(); + setup_rollup_db(&mut ru_async_db).unwrap(); + let mut host_async_db = InMemoryDB::default(); + setup_host_db(&mut host_async_db).unwrap(); + + BlockBuild::new( + ru_evm, + host_evm, + deadline, + 10, + Default::default(), + 50_000_000, + 50_000_000, + SyncAsyncSource(Arc::new(ru_async_db)), + SyncAsyncSource(Arc::new(host_async_db)), + ) } fn modify_account(db: &mut Db, addr: Address, f: F) -> Result From 4f84c04811a29711de22d509073bc37575cbcd4a Mon Sep 17 00:00:00 2001 From: Fraser Hutchison <190532+Fraser999@users.noreply.github.com> Date: Thu, 5 Mar 2026 10:58:17 +0000 Subject: [PATCH 2/4] remove patch section --- Cargo.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 706e8f87..79ae6ad2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ signet-zenith = { version = "0.16.0-rc.12", path = "crates/zenith" } signet-test-utils = { version = "0.16.0-rc.12", path = "crates/test-utils" } # trevm -trevm = { version = "0.34.0", features = ["full_env_cfg", "asyncdb"] } +trevm = { version = "0.34.1", features = ["full_env_cfg", "asyncdb"] } # Alloy periphery crates alloy-core = "1.4" @@ -82,6 +82,3 @@ uuid = "1.16.0" # Test Utils alloy-rlp = "0.3.11" - -[patch.crates-io] -trevm = { git = "https://github.com/init4tech/trevm.git", branch = "fraser/eng-1944/fix-hang" } From 3661ea129ac5d54585f6994f3574c0b740b44dfc Mon Sep 17 00:00:00 2001 From: Fraser Hutchison <190532+Fraser999@users.noreply.github.com> Date: Fri, 6 Mar 2026 14:15:49 +0000 Subject: [PATCH 3/4] snapshot the entire cache to match previous behaviour --- crates/sim/src/cache/store.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/sim/src/cache/store.rs b/crates/sim/src/cache/store.rs index ff07d5c6..b608a59b 100644 --- a/crates/sim/src/cache/store.rs +++ b/crates/sim/src/cache/store.rs @@ -83,11 +83,12 @@ impl SimCache { S: StateSource, S2: StateSource, { - // Snapshot the top items under a short-lived read lock. + // Snapshot the entire cache under a short-lived read lock so that + // filtering out invalid items doesn't reduce the result set below `n`. let candidates: Vec<(u128, SimItem)> = { let cache = self.inner.read(); // Traverse the cache in reverse order (best items first). - cache.items.iter().rev().take(n).map(|(rank, item)| (*rank, item.clone())).collect() + cache.items.iter().rev().map(|(rank, item)| (*rank, item.clone())).collect() }; let mut valid = Vec::with_capacity(n); From a964f5b5e35c8923a0de48cae21cd7e1ed807294 Mon Sep 17 00:00:00 2001 From: Fraser Hutchison <190532+Fraser999@users.noreply.github.com> Date: Fri, 6 Mar 2026 14:21:37 +0000 Subject: [PATCH 4/4] fix new clippy warnings --- crates/evm/src/sys/native.rs | 2 +- crates/test-utils/tests/orders_filler.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/evm/src/sys/native.rs b/crates/evm/src/sys/native.rs index 58685eb4..ce6b1566 100644 --- a/crates/evm/src/sys/native.rs +++ b/crates/evm/src/sys/native.rs @@ -177,7 +177,7 @@ fn adjust_decimals(amount: U256, decimals: u8, target_decimals: u8) -> U256 { let divisor = U256::from(10u64).pow(U256::from(divisor_exp)); amount / divisor } else { - let multiplier_exp = target_decimals.checked_sub(decimals).unwrap_or_default(); + let multiplier_exp = target_decimals.saturating_sub(decimals); let multiplier = U256::from(10u64).pow(U256::from(multiplier_exp)); amount * multiplier } diff --git a/crates/test-utils/tests/orders_filler.rs b/crates/test-utils/tests/orders_filler.rs index 7c4dd894..f6a44203 100644 --- a/crates/test-utils/tests/orders_filler.rs +++ b/crates/test-utils/tests/orders_filler.rs @@ -1,4 +1,7 @@ //! Integration tests for [`signet_orders::Filler`]. + +#![recursion_limit = "256"] + use alloy::{ consensus::{Transaction, TxEnvelope}, eips::eip2718::Decodable2718,