1use crate::{
4 error::{api::FromEthApiError, FromEvmError, ToRpcError},
5 EthApiError,
6};
7use alloy_chains::Chain;
8use alloy_consensus::{transaction::TxHashRef, BlockHeader, Transaction as _};
9use alloy_eips::eip2718::WithEncoded;
10use alloy_network::{NetworkTransactionBuilder, TransactionBuilder};
11use alloy_primitives::{keccak256, map::AddressMap, Address, Bytes, TxKind, U256};
12use alloy_rpc_types_eth::{
13 simulate::{SimBlock, SimCallResult, SimulateError, SimulatedBlock},
14 state::StateOverride,
15 BlockId, BlockOverrides, BlockTransactionsKind,
16};
17use evm2::{interpreter::InstrStop, precompiles::MovePrecompileError, EvmFeatures, TxResult};
18use evm2_inspectors::transfer::TransferInspector;
19use jsonrpsee_types::{error::INTERNAL_ERROR_CODE, ErrorObject};
20use reth_errors::ProviderResult;
21use reth_evm::{
22 execute::{BlockBuilder, BlockBuilderOutcome, BlockTransactionResult},
23 BlockExecutionError, BundleState, Database, DynDatabase, Evm as RethEvm, EvmEnv,
24};
25use reth_primitives_traits::{
26 BlockBody as _, BlockTy, NodePrimitives, Recovered, RecoveredBlock, SealedHeader,
27};
28use reth_rpc_convert::{RpcBlock, RpcConvert, RpcTxReq};
29use reth_rpc_server_types::result::rpc_err;
30use reth_storage_api::{noop::NoopProvider, HashedPostStateProvider, StateProvider};
31use reth_tasks::cancel::is_cancelled;
32use reth_trie_common::{HashedPostState, KeccakKeyHasher};
33use std::mem;
34
35const SIMULATE_FALLBACK_TIMESTAMP_INCREMENT: u64 = 12;
38
39pub const SIMULATE_REVERT_CODE: i32 = 3;
45
46pub const SIMULATE_VM_ERROR_CODE: i32 = -32015;
50
51#[derive(Debug, thiserror::Error)]
53pub enum EthSimulateError {
54 #[error("Block gas limit exceeded by the block's transactions")]
56 BlockGasLimitExceeded,
57 #[error("too many blocks")]
59 TooManyBlocks,
60 #[error("Client adjustable limit reached")]
62 GasLimitReached,
63 #[error("block not found: {block}")]
65 BlockNotFound {
66 block: BlockId,
68 },
69 #[error("block numbers must be in order: {got} <= {parent}")]
71 BlockNumberInvalid {
72 got: u64,
74 parent: u64,
76 },
77 #[error("block timestamps must be in order: {got} <= {parent}")]
79 BlockTimestampInvalid {
80 got: u64,
82 parent: u64,
84 },
85 #[error("nonce too low: next nonce {state}, tx nonce {tx}")]
87 NonceTooLow {
88 tx: u64,
90 state: u64,
92 },
93 #[error("nonce too high")]
95 NonceTooHigh,
96 #[error("nonce has max value")]
98 NonceMaxValue,
99 #[error("max fee per gas less than block base fee")]
101 BaseFeePerGasTooLow,
102 #[error("intrinsic gas too low")]
104 IntrinsicGasTooLow,
105 #[error("insufficient funds for gas * price + value: have {balance} want {cost}")]
107 InsufficientFunds {
108 cost: U256,
110 balance: U256,
112 },
113 #[error("sender is not an EOA")]
115 SenderNotEOA,
116 #[error("max initcode size exceeded")]
118 MaxInitCodeSizeExceeded,
119 #[error("account {0} is not a precompile")]
121 NotAPrecompile(Address),
122 #[error("cannot move precompile {0} to itself")]
124 MovePrecompileToSelf(Address),
125}
126
127impl EthSimulateError {
128 pub const fn error_code(&self) -> i32 {
130 match self {
131 Self::NonceTooLow { .. } => -38010,
132 Self::NonceTooHigh => -38011,
133 Self::NonceMaxValue => INTERNAL_ERROR_CODE,
134 Self::BaseFeePerGasTooLow => -38012,
135 Self::IntrinsicGasTooLow => -38013,
136 Self::InsufficientFunds { .. } => -38014,
137 Self::BlockGasLimitExceeded => -38015,
138 Self::BlockNumberInvalid { .. } => -38020,
139 Self::BlockTimestampInvalid { .. } => -38021,
140 Self::SenderNotEOA => -38024,
141 Self::MaxInitCodeSizeExceeded => -38025,
142 Self::TooManyBlocks | Self::GasLimitReached => -38026,
143 Self::MovePrecompileToSelf(_) => -38022,
144 Self::BlockNotFound { .. } | Self::NotAPrecompile(_) => -32000,
145 }
146 }
147}
148
149impl ToRpcError for EthSimulateError {
150 fn to_rpc_error(&self) -> ErrorObject<'static> {
151 rpc_err(self.error_code(), self.to_string(), None)
152 }
153}
154
155pub fn sanitize_chain<TxReq, H>(
172 blocks: Vec<SimBlock<TxReq>>,
173 parent: &SealedHeader<H>,
174 chain_id: u64,
175 max_simulate_blocks: u64,
176) -> Result<Vec<SimBlock<TxReq>>, EthApiError>
177where
178 H: BlockHeader,
179{
180 let timestamp_increment = Chain::from(chain_id)
181 .average_blocktime_hint()
182 .map(|d| d.as_secs().saturating_add(u64::from(d.subsec_nanos() > 0)))
183 .filter(|&s| s > 0)
184 .unwrap_or(SIMULATE_FALLBACK_TIMESTAMP_INCREMENT);
185
186 let mut out = Vec::with_capacity(blocks.len());
187 let base_number = parent.number();
188 let mut prev_number = base_number;
189 let mut prev_timestamp = parent.timestamp();
190
191 for mut block in blocks {
192 let overrides = block.block_overrides.get_or_insert_with(BlockOverrides::default);
193
194 let target_number = if let Some(n) = overrides.number {
196 u64::try_from(n).unwrap_or(u64::MAX)
197 } else {
198 let n = prev_number.saturating_add(1);
199 overrides.number = Some(U256::from(n));
200 n
201 };
202
203 if target_number <= prev_number {
204 return Err(EthApiError::other(EthSimulateError::BlockNumberInvalid {
205 got: target_number,
206 parent: prev_number,
207 }));
208 }
209
210 if target_number.saturating_sub(base_number) > max_simulate_blocks {
211 return Err(EthApiError::other(EthSimulateError::TooManyBlocks));
212 }
213
214 let gap = target_number - prev_number;
216 if gap > 1 {
217 for i in 1..gap {
218 let filler_number = prev_number + i;
219 let filler_time =
220 prev_timestamp.checked_add(timestamp_increment).ok_or_else(|| {
221 EthApiError::other(EthSimulateError::BlockTimestampInvalid {
222 got: prev_timestamp,
223 parent: prev_timestamp,
224 })
225 })?;
226 out.push(SimBlock {
227 block_overrides: Some(BlockOverrides {
228 number: Some(U256::from(filler_number)),
229 time: Some(filler_time),
230 ..Default::default()
231 }),
232 state_overrides: None,
233 calls: Vec::new(),
234 });
235 prev_timestamp = filler_time;
236 }
237 }
238
239 prev_number = target_number;
240 let block_time = if let Some(t) = overrides.time {
242 if t <= prev_timestamp {
243 return Err(EthApiError::other(EthSimulateError::BlockTimestampInvalid {
244 got: t,
245 parent: prev_timestamp,
246 }));
247 }
248 t
249 } else {
250 let t = prev_timestamp.checked_add(timestamp_increment).ok_or_else(|| {
251 EthApiError::other(EthSimulateError::BlockTimestampInvalid {
252 got: prev_timestamp,
253 parent: prev_timestamp,
254 })
255 })?;
256 overrides.time = Some(t);
257 t
258 };
259 prev_timestamp = block_time;
260
261 out.push(block);
262 }
263
264 Ok(out)
265}
266
267pub fn apply_precompile_overrides(
273 state_overrides: &StateOverride,
274 evm: &mut impl RethEvm,
275) -> Result<(), EthSimulateError> {
276 let moves: Vec<_> = state_overrides
277 .iter()
278 .filter_map(|(source, account_override)| {
279 account_override.move_precompile_to.map(|dest| (*source, dest))
280 })
281 .collect();
282 if moves.is_empty() {
283 return Ok(())
284 }
285
286 for (source, dest) in &moves {
287 if source == dest {
288 if !evm.has_precompile(source) {
289 return Err(EthSimulateError::NotAPrecompile(*source))
290 }
291 return Err(EthSimulateError::MovePrecompileToSelf(*source))
292 }
293 }
294
295 evm.move_precompiles(moves).map_err(|MovePrecompileError::NotAPrecompile(addr)| {
296 EthSimulateError::NotAPrecompile(addr)
297 })?;
298
299 Ok(())
300}
301
302#[expect(clippy::type_complexity, clippy::too_many_arguments)]
317pub fn execute_transactions<S, T, EvmTypes>(
318 mut builder: S,
319 state_provider: impl StateProvider,
320 calls: Vec<RpcTxReq<T::Network>>,
321 remaining_call_gas_limit: &mut Option<u64>,
322 chain_id: u64,
323 simulation_state: Option<&mut SimulatedState>,
324 converter: &T,
325 mut call_nonce: impl FnMut(
326 &RpcTxReq<T::Network>,
327 &mut dyn DynDatabase,
328 ) -> Result<Option<u64>, EthApiError>,
329) -> Result<(BlockBuilderOutcome<S::Primitives>, Vec<TxResult<EvmTypes>>), EthApiError>
330where
331 S: BlockBuilder<Executor: reth_evm::BlockExecutor<Evm: RethEvm<EvmTypes = EvmTypes>>>,
332 T: RpcConvert<Primitives = S::Primitives>,
333 EvmTypes: evm2::EvmTypes,
334{
335 if is_cancelled() {
336 return Err(EthApiError::InternalEthError)
337 }
338 builder.apply_pre_execution_changes()?;
339
340 let mut results = Vec::with_capacity(calls.len());
341 let mut cumulative_tx_gas_used: u64 = 0;
342 let mut block_regular_gas_used: u64 = 0;
343 let mut block_state_gas_used: u64 = 0;
344 let block_gas_limit = builder.evm_env().block_env().gas_limit.to::<u64>();
345 let is_amsterdam = builder.evm_env().version().feature(EvmFeatures::EIP8037);
346 let tx_gas_limit_cap = builder.evm_env().version().tx_gas_limit_cap;
347 for mut call in calls {
348 if is_cancelled() {
349 return Err(EthApiError::InternalEthError)
350 }
351 let block_gas_remaining = if is_amsterdam {
352 let regular_available_gas = block_gas_limit.saturating_sub(block_regular_gas_used);
353 let state_available_gas = block_gas_limit.saturating_sub(block_state_gas_used);
354 if regular_available_gas >= tx_gas_limit_cap {
356 state_available_gas
357 } else {
358 regular_available_gas.min(state_available_gas)
359 }
360 } else {
361 block_gas_limit.saturating_sub(cumulative_tx_gas_used)
362 };
363 let mut default_gas_limit = block_gas_remaining;
364
365 if let Some(gas_limit) = call.as_ref().gas_limit() &&
366 gas_limit > block_gas_remaining
367 {
368 return Err(EthApiError::other(EthSimulateError::BlockGasLimitExceeded))
369 }
370
371 if let Some(remaining_call_gas_limit) = *remaining_call_gas_limit {
372 if let Some(gas_limit) = call.as_ref().gas_limit() {
373 if gas_limit > remaining_call_gas_limit {
374 call.as_mut().set_gas_limit(remaining_call_gas_limit);
375 }
376 } else {
377 default_gas_limit = default_gas_limit.min(remaining_call_gas_limit);
378 }
379 }
380
381 if call.as_ref().nonce().is_none() &&
382 let Some(nonce) = call_nonce(&call, builder.evm_mut().state_db_mut())?
383 {
384 call.as_mut().set_nonce(nonce);
385 }
386
387 let tx = resolve_transaction_with_evm(
390 call,
391 default_gas_limit,
392 builder.evm_env().block_base_fee(),
393 chain_id,
394 !builder.evm_env().version().feature(EvmFeatures::NONCE_CHECK),
395 builder.evm_mut(),
396 converter,
397 )?;
398 let tx = WithEncoded::new(Default::default(), tx);
401
402 let mut tx_regular_gas_used = 0;
403 let gas_output = builder.execute_transaction_with_result_closure(tx, |output| {
404 let output = &output.result().result;
405 tx_regular_gas_used = output.execution_gas_spent();
406 results.push(output.clone())
407 })?;
408
409 if let Some(logs) = unsafe {
413 builder
414 .evm_mut()
415 .with_inspector(|inspector: &mut TransferInspector| inspector.take_logs())
416 } && let Some(result) = results.last_mut()
417 {
418 result.logs = logs;
419 }
420
421 let gas_used = gas_output.tx_gas_used();
422 if let Some(remaining_call_gas_limit) = remaining_call_gas_limit.as_mut() {
423 if gas_used > *remaining_call_gas_limit {
424 return Err(EthApiError::other(EthSimulateError::GasLimitReached))
425 }
426 *remaining_call_gas_limit -= gas_used;
427 }
428
429 cumulative_tx_gas_used = cumulative_tx_gas_used.saturating_add(gas_used);
430 block_regular_gas_used = block_regular_gas_used.saturating_add(tx_regular_gas_used);
431 block_state_gas_used = block_state_gas_used.saturating_add(gas_output.state_gas_used());
432 }
433
434 let result = if let Some(simulation_state) = simulation_state {
435 builder.finish_with_state_root(&state_provider, |output| {
436 simulation_state
437 .extend(&state_provider, &output.state)
438 .map_err(BlockExecutionError::other)?;
439 state_provider
440 .state_root_with_updates(simulation_state.hashed_state().clone())
441 .map(Some)
442 .map_err(BlockExecutionError::other)
443 })?
444 } else {
445 builder.finish(NoopProvider::default(), None)?
446 };
447
448 Ok((result, results))
449}
450
451pub fn resolve_transaction<DB: Database, Tx, T>(
458 tx: RpcTxReq<T::Network>,
459 default_gas_limit: u64,
460 block_base_fee_per_gas: u64,
461 chain_id: u64,
462 disable_nonce_check: bool,
463 db: &mut DB,
464 converter: &T,
465) -> Result<Recovered<Tx>, EthApiError>
466where
467 DB::Error: Into<EthApiError>,
468 T: RpcConvert<Primitives: NodePrimitives<SignedTx = Tx>>,
469{
470 resolve_transaction_with_account(
471 tx,
472 default_gas_limit,
473 block_base_fee_per_gas,
474 chain_id,
475 disable_nonce_check,
476 |address| db.get_account(address).map_err(Into::into),
477 converter,
478 )
479}
480
481fn resolve_transaction_with_evm<Tx, T, E>(
482 tx: RpcTxReq<T::Network>,
483 default_gas_limit: u64,
484 block_base_fee_per_gas: u64,
485 chain_id: u64,
486 disable_nonce_check: bool,
487 evm: &mut E,
488 converter: &T,
489) -> Result<Recovered<Tx>, EthApiError>
490where
491 T: RpcConvert<Primitives: NodePrimitives<SignedTx = Tx>>,
492 E: RethEvm,
493{
494 resolve_transaction_with_account(
495 tx,
496 default_gas_limit,
497 block_base_fee_per_gas,
498 chain_id,
499 disable_nonce_check,
500 |address| evm.account_info(address).map_err(Into::into),
501 converter,
502 )
503}
504
505fn resolve_transaction_with_account<Tx, T, F>(
506 mut tx: RpcTxReq<T::Network>,
507 default_gas_limit: u64,
508 block_base_fee_per_gas: u64,
509 chain_id: u64,
510 disable_nonce_check: bool,
511 mut account: F,
512 converter: &T,
513) -> Result<Recovered<Tx>, EthApiError>
514where
515 T: RpcConvert<Primitives: NodePrimitives<SignedTx = Tx>>,
516 F: FnMut(&Address) -> Result<Option<evm2::evm::AccountInfo>, EthApiError>,
517{
518 let tx_type = tx.as_ref().output_tx_type();
521
522 let from = tx.as_ref().from().unwrap_or_else(|| {
523 tx.as_mut().set_from(Address::ZERO);
524 Address::ZERO
525 });
526
527 let state_nonce = account(&from)?.map(|acc| acc.nonce).unwrap_or_default();
528 if tx.as_ref().nonce().is_none() {
529 tx.as_mut().set_nonce(state_nonce);
530 }
531 if state_nonce == u64::MAX && (disable_nonce_check || tx.as_ref().nonce() == Some(u64::MAX)) {
533 return Err(EthApiError::other(EthSimulateError::NonceMaxValue))
534 }
535 if disable_nonce_check && tx.as_ref().nonce() == Some(u64::MAX) {
538 tx.as_mut().set_nonce(0);
539 }
540
541 if tx.as_ref().gas_limit().is_none() {
542 tx.as_mut().set_gas_limit(default_gas_limit);
543 }
544
545 if tx.as_ref().chain_id().is_none() {
546 tx.as_mut().set_chain_id(chain_id);
547 }
548
549 if tx.as_ref().kind().is_none() {
550 tx.as_mut().set_kind(TxKind::Create);
551 }
552
553 let _ = block_base_fee_per_gas;
560 if tx.as_ref().output_tx_type_checked().is_none() {
561 if tx_type.is_legacy() || tx_type.is_eip2930() {
562 if tx.as_ref().gas_price().is_none() {
563 tx.as_mut().set_gas_price(0);
564 }
565 } else {
566 if tx.as_ref().max_fee_per_gas().is_none() {
567 tx.as_mut().set_max_fee_per_gas(0);
568 }
569 if tx.as_ref().max_priority_fee_per_gas().is_none() {
570 tx.as_mut().set_max_priority_fee_per_gas(0);
571 }
572 }
573 }
574
575 let tx =
576 converter.build_simulate_v1_transaction(tx).map_err(|e| EthApiError::other(e.into()))?;
577
578 Ok(Recovered::new_unchecked(tx, from))
579}
580
581pub fn build_simulated_block<Err, T, EvmTypes>(
583 block: RecoveredBlock<BlockTy<T::Primitives>>,
584 results: Vec<TxResult<EvmTypes>>,
585 txs_kind: BlockTransactionsKind,
586 converter: &T,
587) -> Result<SimulatedBlock<RpcBlock<T::Network>>, Err>
588where
589 Err: std::error::Error
590 + FromEthApiError
591 + FromEvmError<T::Evm>
592 + From<T::Error>
593 + Into<jsonrpsee_types::ErrorObject<'static>>,
594 T: RpcConvert,
595 EvmTypes: evm2::EvmTypes,
596{
597 let mut calls: Vec<SimCallResult> = Vec::with_capacity(results.len());
598
599 let mut log_index = 0;
600 for (index, (result, tx)) in results.into_iter().zip(block.body().transactions()).enumerate() {
601 let gas_used = result.tx_gas_used();
602 let max_used_gas = Some(result.total_gas_spent.max(result.floor_gas));
603 let call = if result.status {
604 SimCallResult {
605 return_data: result.output,
606 error: None,
607 gas_used,
608 max_used_gas,
609 logs: result
610 .logs
611 .into_iter()
612 .map(|log| {
613 log_index += 1;
614 alloy_rpc_types_eth::Log {
615 inner: log,
616 log_index: Some(log_index - 1),
617 transaction_index: Some(index as u64),
618 transaction_hash: Some(*tx.tx_hash()),
619 block_hash: Some(block.hash()),
620 block_number: Some(block.header().number()),
621 block_timestamp: Some(block.header().timestamp()),
622 ..Default::default()
623 }
624 })
625 .collect(),
626 status: true,
627 }
628 } else if result.stop == InstrStop::Revert {
629 let error = Err::from_revert(result.output.clone());
630 SimCallResult {
631 return_data: Bytes::new(),
632 error: Some(SimulateError {
633 message: error.to_string(),
634 code: SIMULATE_REVERT_CODE,
635 data: Some(result.output),
636 }),
637 gas_used,
638 max_used_gas,
639 status: false,
640 logs: Vec::new(),
641 }
642 } else {
643 let error = Err::from_evm_halt(result.stop, tx.gas_limit());
644 SimCallResult {
645 return_data: Bytes::new(),
646 error: Some(SimulateError {
647 message: error.to_string(),
648 code: SIMULATE_VM_ERROR_CODE,
649 ..SimulateError::invalid_params()
650 }),
651 gas_used,
652 max_used_gas,
653 logs: Vec::new(),
654 status: false,
655 }
656 };
657
658 calls.push(call);
659 }
660
661 let block = block.into_rpc_block(
662 txs_kind,
663 |tx, tx_info| converter.fill(tx, tx_info),
664 |header, block_size| converter.convert_header(header, Some(block_size)),
665 )?;
666 Ok(SimulatedBlock { inner: block, calls })
667}
668
669#[derive(Debug, Default)]
675pub struct SimulatedState {
676 hashed: HashedPostState,
677 parent_storage_unzeroed: AddressMap<bool>,
680}
681
682impl SimulatedState {
683 pub const fn hashed_state(&self) -> &HashedPostState {
685 &self.hashed
686 }
687
688 pub fn extend(
693 &mut self,
694 provider: &impl HashedPostStateProvider,
695 changes: &BundleState,
696 ) -> ProviderResult<()> {
697 let mut parent_storage = BundleState::default();
698 for (address, account) in &changes.state {
699 let unzeroed = self
701 .parent_storage_unzeroed
702 .entry(*address)
703 .or_insert_with(|| account.original_info.is_some());
704 if !account.was_destroyed() {
705 continue
706 }
707 if let Some(storage) = self.hashed.storages.get_mut(&keccak256(address)) {
708 storage.storage.values_mut().for_each(|value| *value = U256::ZERO);
709 }
710 if mem::take(unzeroed) {
711 let mut account = account.clone();
712 account.storage.clear();
713 parent_storage.state.insert(*address, account);
714 }
715 }
716
717 if !parent_storage.state.is_empty() {
718 self.hashed.extend(provider.hashed_post_state(&parent_storage)?);
719 }
720
721 self.hashed.extend(HashedPostState::from_bundle_state::<KeccakKeyHasher>(&changes.state));
722 Ok(())
723 }
724}
725
726#[cfg(test)]
727mod tests {
728 use super::{
729 apply_precompile_overrides, sanitize_chain, EthSimulateError, ProviderResult,
730 SimulatedState, INTERNAL_ERROR_CODE,
731 };
732 use crate::{cache::db::apply_state_overrides_with_state, error::ToRpcError, EthApiError};
733 use alloy_chains::Chain;
734 use alloy_consensus::Header;
735 use alloy_primitives::{
736 address, keccak256,
737 map::{AddressMap, B256Map},
738 Address, B256, U256,
739 };
740 use alloy_rpc_types_eth::{
741 simulate::SimBlock,
742 state::{AccountOverride, StateOverride},
743 BlockOverrides, TransactionRequest,
744 };
745 use evm2::{
746 ethereum::ethereum_tx_registry,
747 evm::{AccountInfo, CacheDB, EmptyDB, InMemoryDB},
748 BaseEvmTypes, Evm, Precompiles, SpecId,
749 };
750 use reth_evm::BundleState;
751 use reth_primitives_traits::SealedHeader;
752 use reth_storage_api::HashedPostStateProvider;
753 use reth_trie_common::{HashedPostState, KeccakKeyHasher};
754 use std::{
755 collections::BTreeMap,
756 sync::atomic::{AtomicUsize, Ordering},
757 };
758
759 fn test_evm() -> Evm<'static, BaseEvmTypes> {
760 Evm::new(
761 SpecId::PRAGUE,
762 Default::default(),
763 ethereum_tx_registry(SpecId::PRAGUE),
764 EmptyDB::default(),
765 Precompiles::base(SpecId::PRAGUE),
766 )
767 }
768
769 #[derive(Default)]
771 struct CountingProvider {
772 storage: AddressMap<B256Map<U256>>,
773 scans: AtomicUsize,
774 reads: AtomicUsize,
775 }
776
777 impl CountingProvider {
778 fn resulting_storage(&self, hashed: &HashedPostState) -> BTreeMap<(B256, B256), U256> {
780 let parent = self.storage.iter().flat_map(|(address, slots)| {
781 slots.iter().map(move |(slot, value)| ((keccak256(address), *slot), *value))
782 });
783 let changes = hashed.storages.iter().flat_map(|(address, storage)| {
784 storage.storage.iter().map(|(slot, value)| ((*address, *slot), *value))
785 });
786 parent
787 .chain(changes)
788 .collect::<BTreeMap<_, _>>()
789 .into_iter()
790 .filter(|(_, v)| !v.is_zero())
791 .collect()
792 }
793 }
794
795 impl HashedPostStateProvider for CountingProvider {
796 fn hashed_post_state(&self, bundle_state: &BundleState) -> ProviderResult<HashedPostState> {
797 let mut hashed =
798 HashedPostState::from_bundle_state::<KeccakKeyHasher>(&bundle_state.state);
799 for (address, account) in &bundle_state.state {
800 if account.was_destroyed() && account.original_info.is_some() {
801 self.scans.fetch_add(1, Ordering::Relaxed);
802 let storage = self.storage.get(address).into_iter().flatten();
803 let slots = &mut hashed.storages.entry(keccak256(address)).or_default().storage;
804 for (slot, _) in storage {
805 self.reads.fetch_add(1, Ordering::Relaxed);
806 slots.entry(*slot).or_insert(U256::ZERO);
807 }
808 }
809 }
810 Ok(hashed)
811 }
812 }
813
814 #[test]
815 fn nonce_max_value_error_uses_internal_error_code() {
816 let err = EthSimulateError::NonceMaxValue.to_rpc_error();
817
818 assert_eq!(err.code(), INTERNAL_ERROR_CODE);
819 assert_eq!(err.message(), "nonce has max value");
820 }
821
822 #[test]
823 fn block_not_found_error_uses_simulate_code() {
824 let err = EthSimulateError::BlockNotFound { block: 100000.into() }.to_rpc_error();
825
826 assert_eq!(err.code(), -32000);
827 assert_eq!(err.message(), "block not found: 0x186a0");
828 }
829
830 fn parent_at(number: u64, timestamp: u64) -> SealedHeader<Header> {
831 SealedHeader::seal_slow(Header { number, timestamp, ..Default::default() })
832 }
833
834 fn block_with_number(number: u64) -> SimBlock<TransactionRequest> {
835 SimBlock {
836 block_overrides: Some(BlockOverrides {
837 number: Some(U256::from(number)),
838 ..Default::default()
839 }),
840 ..Default::default()
841 }
842 }
843
844 #[test]
845 fn precompile_self_move_requires_existing_precompile() {
846 let address = address!("c100000000000000000000000000000000000000");
847 let mut state_overrides = StateOverride::default();
848 state_overrides.insert(
849 address,
850 AccountOverride { move_precompile_to: Some(address), ..Default::default() },
851 );
852 let mut evm = test_evm();
853
854 let err = apply_precompile_overrides(&state_overrides, &mut evm).unwrap_err();
855
856 assert!(matches!(err, EthSimulateError::NotAPrecompile(addr) if addr == address));
857 }
858
859 #[test]
860 fn precompile_self_move_errors_for_existing_precompile() {
861 let address = Address::with_last_byte(1);
862 let mut state_overrides = StateOverride::default();
863 state_overrides.insert(
864 address,
865 AccountOverride { move_precompile_to: Some(address), ..Default::default() },
866 );
867 let mut evm = test_evm();
868
869 let err = apply_precompile_overrides(&state_overrides, &mut evm).unwrap_err();
870
871 assert!(matches!(err, EthSimulateError::MovePrecompileToSelf(addr) if addr == address));
872 }
873
874 #[test]
875 fn moved_precompile_is_callable() {
876 let source = Address::with_last_byte(1);
877 let dest = address!("0000000000000000000000000000000000123456");
878 let mut state_overrides = StateOverride::default();
879 state_overrides.insert(
880 source,
881 AccountOverride { move_precompile_to: Some(dest), ..Default::default() },
882 );
883 let mut evm = test_evm();
884
885 apply_precompile_overrides(&state_overrides, &mut evm).unwrap();
886
887 let precompiles = evm.precompiles_as::<Precompiles>().unwrap().as_map();
888 assert!(!precompiles.contains(&source));
889 assert!(precompiles.contains(&dest));
890 }
891
892 #[test]
893 fn sanitize_chain_fills_gaps_with_empty_blocks() {
894 let parent = parent_at(5, 100);
897 let blocks = vec![block_with_number(8)];
898
899 let out = sanitize_chain(blocks, &parent, Chain::mainnet().id(), 256).unwrap();
900 assert_eq!(out.len(), 3);
901
902 let numbers: Vec<u64> = out
903 .iter()
904 .map(|b| b.block_overrides.as_ref().unwrap().number.unwrap().try_into().unwrap())
905 .collect();
906 assert_eq!(numbers, vec![6, 7, 8]);
907
908 assert!(out[0].calls.is_empty());
909 assert!(out[1].calls.is_empty());
910
911 let times: Vec<u64> =
913 out.iter().map(|b| b.block_overrides.as_ref().unwrap().time.unwrap()).collect();
914 assert_eq!(times, vec![112, 124, 136]);
915 }
916
917 #[test]
918 fn sanitize_chain_defaults_missing_number_and_time() {
919 let parent = parent_at(10, 1000);
920 let blocks: Vec<SimBlock<TransactionRequest>> =
921 vec![SimBlock::default(), SimBlock::default()];
922
923 let out = sanitize_chain(blocks, &parent, Chain::mainnet().id(), 256).unwrap();
924 assert_eq!(out.len(), 2);
925
926 let overrides = out[0].block_overrides.as_ref().unwrap();
927 assert_eq!(overrides.number.unwrap(), U256::from(11));
928 assert_eq!(overrides.time, Some(1012));
929
930 let overrides = out[1].block_overrides.as_ref().unwrap();
931 assert_eq!(overrides.number.unwrap(), U256::from(12));
932 assert_eq!(overrides.time, Some(1024));
933 }
934
935 #[test]
936 fn sanitize_chain_uses_chain_blocktime_hint() {
937 let parent = parent_at(0, 0);
939 let blocks = vec![block_with_number(3)];
940
941 let out = sanitize_chain(blocks, &parent, Chain::optimism_mainnet().id(), 256).unwrap();
942 let times: Vec<u64> =
943 out.iter().map(|b| b.block_overrides.as_ref().unwrap().time.unwrap()).collect();
944 assert_eq!(times, vec![2, 4, 6]);
945 }
946
947 #[test]
948 fn sanitize_chain_rounds_subsecond_blocktime_hint_up() {
949 let parent = parent_at(0, 0);
952 let blocks = vec![block_with_number(3)];
953
954 let out = sanitize_chain(blocks, &parent, Chain::arbitrum_mainnet().id(), 256).unwrap();
955 let times: Vec<u64> =
956 out.iter().map(|b| b.block_overrides.as_ref().unwrap().time.unwrap()).collect();
957 assert_eq!(times, vec![1, 2, 3]);
958 }
959
960 #[test]
961 fn sanitize_chain_falls_back_when_chain_has_no_hint() {
962 let parent = parent_at(0, 0);
964 let blocks = vec![block_with_number(2)];
965
966 let out = sanitize_chain(blocks, &parent, Chain::from_id(123_456_789).id(), 256).unwrap();
967 let times: Vec<u64> =
968 out.iter().map(|b| b.block_overrides.as_ref().unwrap().time.unwrap()).collect();
969 assert_eq!(times, vec![12, 24]);
970 }
971
972 #[test]
973 fn sanitize_chain_rejects_non_increasing_number() {
974 let parent = parent_at(10, 100);
975 let err = sanitize_chain(vec![block_with_number(10)], &parent, Chain::mainnet().id(), 256)
976 .unwrap_err();
977 assert!(matches!(err, EthApiError::Other(_)));
978 }
979
980 #[test]
981 fn sanitize_chain_rejects_timestamp_overflow() {
982 let parent = parent_at(0, 0);
985 let blocks: Vec<SimBlock<TransactionRequest>> = vec![
986 SimBlock {
987 block_overrides: Some(BlockOverrides {
988 time: Some(u64::MAX),
989 ..Default::default()
990 }),
991 ..Default::default()
992 },
993 SimBlock::default(),
994 ];
995
996 let err = sanitize_chain(blocks, &parent, Chain::mainnet().id(), 256).unwrap_err();
997 assert!(matches!(err, EthApiError::Other(_)));
998 }
999
1000 #[test]
1001 fn sanitize_chain_rejects_filler_timestamp_overflow() {
1002 let parent = parent_at(0, 0);
1004 let blocks = vec![
1005 SimBlock {
1006 block_overrides: Some(BlockOverrides {
1007 number: Some(U256::ONE),
1008 time: Some(u64::MAX),
1009 ..Default::default()
1010 }),
1011 ..Default::default()
1012 },
1013 block_with_number(4),
1014 ];
1015
1016 let err = sanitize_chain(blocks, &parent, Chain::mainnet().id(), 256).unwrap_err();
1017 assert!(matches!(err, EthApiError::Other(_)));
1018 }
1019
1020 #[test]
1021 fn sanitize_chain_enforces_max_blocks() {
1022 let parent = parent_at(0, 0);
1023 let err = sanitize_chain(vec![block_with_number(257)], &parent, Chain::mainnet().id(), 256)
1024 .unwrap_err();
1025 assert!(matches!(err, EthApiError::Other(_)));
1026 }
1027
1028 #[test]
1031 fn simulated_state_reads_parent_storage_once() {
1032 let wiped = Address::with_last_byte(1);
1033 let emptied = Address::with_last_byte(2);
1034 let created = Address::with_last_byte(3);
1035 let slot = |n: u64| B256::from(U256::from(n));
1036 let slots = |range: std::ops::Range<u64>| {
1037 range.map(|n| (slot(n), slot(n + 1000))).collect::<Vec<_>>()
1038 };
1039
1040 let mut db = InMemoryDB::default();
1041 let mut provider = CountingProvider::default();
1042 for (address, count) in [(wiped, 100), (emptied, 10)] {
1043 db.insert_account_info(
1044 &address,
1045 AccountInfo { balance: U256::from(5), ..Default::default() },
1046 );
1047 for (key, value) in slots(0..count) {
1048 db.insert_account_storage(
1049 &address,
1050 &U256::from_be_bytes(key.0),
1051 &U256::from_be_bytes(value.0),
1052 );
1053 provider
1054 .storage
1055 .entry(address)
1056 .or_default()
1057 .insert(keccak256(key), U256::from_be_bytes(value.0));
1058 }
1059 }
1060 let mut db = CacheDB::new(db);
1061
1062 let blocks = [
1063 StateOverride::from_iter([
1066 (
1067 wiped,
1068 AccountOverride::default()
1069 .with_state([(slot(1), slot(7)), (slot(500), slot(8))]),
1070 ),
1071 (emptied, AccountOverride::default().with_balance(U256::ZERO)),
1072 (created, AccountOverride::default().with_balance(U256::from(3))),
1073 ]),
1074 StateOverride::from_iter([
1077 (wiped, AccountOverride::default().with_state_diff([(slot(2), slot(9))])),
1078 (created, AccountOverride::default().with_state([(slot(1), slot(4))])),
1079 ]),
1080 StateOverride::from_iter([(
1081 wiped,
1082 AccountOverride::default().with_balance(U256::from(6)),
1083 )]),
1084 StateOverride::from_iter([(
1086 wiped,
1087 AccountOverride::default().with_state([(slot(500), slot(10))]),
1088 )]),
1089 StateOverride::from_iter([(
1090 wiped,
1091 AccountOverride::default().with_state_diff([(slot(3), slot(11))]),
1092 )]),
1093 ];
1094
1095 let mut simulated = SimulatedState::default();
1096 let mut cumulative = BundleState::default();
1097 for (index, state_overrides) in blocks.into_iter().enumerate() {
1098 let mut changes = BundleState::default();
1099 apply_state_overrides_with_state(state_overrides, &mut db, Some(&mut changes)).unwrap();
1100
1101 simulated.extend(&provider, &changes).unwrap();
1102 let reads = provider.reads.swap(0, Ordering::Relaxed);
1105 assert_eq!(reads, [110, 0, 0, 0, 0][index], "block {index}");
1106 let scans = provider.scans.swap(0, Ordering::Relaxed);
1107 assert_eq!(scans, [2, 0, 0, 0, 0][index], "block {index}");
1108
1109 cumulative.extend(changes);
1110 let expected = provider.hashed_post_state(&cumulative).unwrap();
1111 provider.reads.store(0, Ordering::Relaxed);
1112 provider.scans.store(0, Ordering::Relaxed);
1113 assert_eq!(simulated.hashed_state().accounts, expected.accounts, "block {index}");
1114 assert_eq!(
1115 provider.resulting_storage(simulated.hashed_state()),
1116 provider.resulting_storage(&expected),
1117 "block {index}"
1118 );
1119 }
1120 }
1121}