pub struct BlockSimHandle<'a> { /* private fields */ }Expand description
Owns a per-block shared EVM (one CacheDB + revm Context, state overrides
applied once) for the in-process sim fan-out (Tier 1).
Built ONCE per block by BlockSimHandle::build (the strategy’s
dispatch_profitable_results calls it), then each candidate path is
simulated SERIALLY by the strategy’s simulate_path_on_evm over the shared
&mut evm exposed by BlockSimHandle::evm_mut. Per-path isolation is
revm’s finalize() (clears the journal between paths — execute()’s
SSTOREs live in the per-path State returned by finalize, never
committed to the shared CacheDB; see the module-level note above). The
CacheDB + EVM drop naturally at end of block — no cross-block caching in
Tier 1, so memory is bounded by one block’s working set.
Serial (not buffer_unordered) because a shared &mut evm can’t be held
across 'static+Send futures. The benchmark’s config B (serial-warm,
~1 µs p50) beats config A (parallel-cold, ~590 µs p50) by ~60× — losing
concurrency is a NET WIN because the per-path RPC cold-miss dwarfs the
per-path EVM execution.
The state overrides — owner ETH funding, executor code injection, warmup
slots, WETH balance — are all per-block-invariant, so applying them once at
BlockSimHandle::build is semantically identical to applying them
per-path.
Implementations§
Source§impl<'a> BlockSimHandle<'a>
impl<'a> BlockSimHandle<'a>
Sourcepub fn build(
provider: &AlloyProvider,
base_fee_next: u128,
current_block: u64,
block_timestamp: u64,
override_params: &SimulationOverrideParams,
oracle: &'a dyn SimAnchorOracle,
warm_cache: &Arc<RwLock<WarmCodeCacheInner>>,
storage_memo: Option<&Arc<StorageMemo>>,
verify_divergence: bool,
) -> Option<Self>
pub fn build( provider: &AlloyProvider, base_fee_next: u128, current_block: u64, block_timestamp: u64, override_params: &SimulationOverrideParams, oracle: &'a dyn SimAnchorOracle, warm_cache: &Arc<RwLock<WarmCodeCacheInner>>, storage_memo: Option<&Arc<StorageMemo>>, verify_divergence: bool, ) -> Option<Self>
Build the per-block shared EVM: the layered DB (AlloyDB →
WrapDatabaseAsync → BotStateDb → WarmCodeCache → CacheDB), the
state overrides applied ONCE (every override is per-block-invariant),
and the revm Context pinned to the block env. Returns None on a
build failure (no ambient multi-threaded runtime for
WrapDatabaseAsync, or an override-application error); the caller
tallies rpc-failed for every candidate in that case.
The block-env primitives (provider, base_fee_next, current_block,
block_timestamp) + the projected SimulationOverrideParams are
handed in directly — the engine never names the strategy’s
SimulateContext (ADR-019 D7, decision R — strategy/engine separation).
warm_cache is the cross-block persistent bytecode + account-existence
layer (super::WarmCodeCache); it persists across blocks via the
engine owner’s Arc<RwLock<WarmCodeCacheInner>> (cloned into the
per-block WarmCodeCache wrapper value here — only the inner map
survives across blocks). The per-block CacheDB layered above still
drops at end of block (overrides + mutable storage are per-block).
WrapDatabaseAsync::new returns None if there is no runtime or the
runtime is CurrentThread. The pump drives the dispatch through
pyo3_async_runtimes (Builder::new_multi_thread()), so the None
path is unreachable in production — None means the dispatch host lost
its runtime.
Sourcepub fn evm_mut(&mut self) -> &mut BlockEvm<'a>
pub fn evm_mut(&mut self) -> &mut BlockEvm<'a>
Borrow the shared per-block EVM mutably — the strategy’s
simulate_path_on_evm drives this &mut evm per candidate path. The
fan-out is serial, so the borrow is never aliased. Per-path isolation
is revm’s finalize() (the strategy clears the journal between paths
via the finalize() calls inside simulate_path_on_evm —
execute()’s SSTOREs stay in the per-path State, never committed to
the shared CacheDB).
Sourcepub fn scratch_evm(&mut self) -> Option<&mut ScratchEvm<ScratchDb<'a>>>
pub fn scratch_evm(&mut self) -> Option<&mut ScratchEvm<ScratchDb<'a>>>
The frame-replay seam (super::frame_replay): a scratch engine over
a FRESHLY STACKED instance of the layered DB (same layers, new
instances — the shared EVM’s journal and per-block CacheDB are never
aliased; the cross-block WarmCodeCacheInner owner arc IS shared, so
warm bytecode/account caches carry over). State overrides are NOT
applied — a foreign frame runs against chain state, not the strategy’s
executor funding. Built lazily ONCE and kept for the handle’s life, so
the scratch’s warmed read-caches serve every later frame; only the
scratch’s per-frame DB layer is discarded. The cold path is counted
(the soak-budget rpc_reads).
Returns None when no ambient multi-threaded runtime exists for the
fresh WrapDatabaseAsync (same failure surface as Self::build).
Auto Trait Implementations§
impl<'a> !Freeze for BlockSimHandle<'a>
impl<'a> !RefUnwindSafe for BlockSimHandle<'a>
impl<'a> !Send for BlockSimHandle<'a>
impl<'a> !Sync for BlockSimHandle<'a>
impl<'a> !UnwindSafe for BlockSimHandle<'a>
impl<'a> Unpin for BlockSimHandle<'a>
impl<'a> UnsafeUnpin for BlockSimHandle<'a>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.