-
Notifications
You must be signed in to change notification settings - Fork 174
Description
Description
In process_sequence_of_orders, when a sequence has a cached sub-sequence in simulation_cache, the code fetches a CachedSimulationState (including a bundle_state) but does not restore it into the new BlockState. Instead, a fresh BlockState is created. This causes the remaining (non-cached) orders of the sequence to be simulated on top of the wrong state.
Additionally, I think the simulation_cache should persist and restore counters for gas_used and blob_gas_used. On a cache hit, PartialBlock should be initialised using these cached values. Otherwise the remaining orders are simulated as if more resources were available than the cached orders actually left. For example, if the blob gas limit was already exhausted, remaining blob transactions should revert. Without restoring the counters, certain transactions can appear to succeed, producing incorrect results.
Current flow (cache hit)
get_cached_state(&full_sequence_of_orders)returns(Some(cached_state), cached_up_to_index > 0).initialize_block_state(state_provider)is called:- It uses
BlockState::new_arc(state_provider), which always sets a freshBundleState::default().
- It uses
pre_block_callis invoked unconditionally.
Result: the cached sequence is not restored and the simulation effectively starts from an empty state again.
Expected
- If a cached sequence exists:
- Restore
cached_state.bundle_stateintoBlockState(usingwith_bundle_state). - Skip
pre_block_call - Initialise
PartialBlockcounters (gas, blob gas) from the cached values.
- Restore
Minimal fix
Pass the cached state into the initialiser and use it when present:
fn initialize_block_state(
&self,
cached_state_option: &Option<Arc<CachedSimulationState>>,
state_provider: Arc<dyn StateProvider>,
) -> BlockState {
let initial_state = BlockState::new_arc(state_provider);
if let Some(cached_state) = cached_state_option {
initial_state.with_bundle_state(cached_state.bundle_state.clone())
} else {
initial_state
}
}Update the call site in process_sequence_of_orders:
let (cached_state_option, cached_up_to_index) =
self.simulation_cache.get_cached_state(&full_sequence_of_orders);
let mut partial_block = PartialBlock::new(true);
let mut state = self.initialize_block_state(&cached_state_option, state_provider);
if cached_up_to_index == 0 {
partial_block.pre_block_call(&self.ctx, &mut local_ctx, &mut state)?;
} else if let Some(cached) = &cached_state_option {
partial_block.gas_used = cached.cumulative_gas_used;
partial_block.blob_gas_used = cached.cumulative_blob_gas_used;
}Note that updates to CachedSimulationStateand store_simulation_state would also be needed to include the gas counters.
I'm happy to open a PR with this change. If there's intended behaviour I'm missing (e.g., a different place where the cached BundleState is supposed to be re-applied), please let me know. Thanks!