1use self::{in_memory_db::StateRootDb, state::trie_storage};
3use crate::{
4 ForkChoice, NodeConfig, PrecompileFactory,
5 config::{ForkTransactionReplay, PruneStateHistoryConfig, source_hardfork},
6 eth::{
7 backend::{
8 cheats::{CheatEcrecover, CheatsManager, NextBlockOverrides},
9 db::{
10 AnvilCacheDB, BLOCKHASH_HISTORY, Db, EmptyAsAbsentDb, MaybeFullDatabase,
11 SerializableState, StateDb,
12 },
13 executor::{
14 AnvilBlockExecutor, BlockExecutionKind, EthereumBlockTransitions,
15 ExecutedPoolTransactions, FoundryReceiptBuilder, PoolTxGasConfig,
16 apply_ethereum_post_execution_changes, apply_ethereum_pre_execution_changes,
17 block_blob_gas_limit, build_tx_env_for_pending, execute_pool_transaction,
18 execute_pool_transactions,
19 },
20 fork::{ClientFork, ForkEndpointIdentity},
21 genesis::GenesisConfig,
22 mem::{
23 state::{
24 StateRootCache, state_root, state_trie_witness, storage_root, trie_accounts,
25 },
26 storage::MinedTransactionReceipt,
27 },
28 notifications::{ChainNotification, ChainNotifications, NewBlockNotification},
29 replay::{
30 ExecutedHistoricalReplay, HistoricalReplayTransaction,
31 PreparedForkTransactionReplay, execute_historical_replay,
32 prepare_fork_transaction_replay,
33 },
34 tempo::AnvilStorageProvider,
35 time::{PendingBlockTimestamp, TimeManager, TimeSnapshot, utc_from_secs},
36 validate::TransactionValidator,
37 },
38 error::{BlockchainError, ErrDetail, InvalidTransactionError},
39 fees::{FeeDetails, FeeManager, FeeSnapshot, MIN_SUGGESTED_PRIORITY_FEE},
40 macros::node_info,
41 pool::transactions::PoolTransaction,
42 preserve_simulation_request_fields,
43 },
44 mem::{
45 inspector::{AnvilInspector, InspectorTxConfig},
46 storage::{BlockchainStorage, InMemoryBlockStates, MinedBlockOutcome},
47 },
48};
49use alloy_chains::NamedChain;
50use alloy_consensus::{
51 Blob, BlockBody, BlockHeader, EnvKzgSettings, Header, Signed, Transaction as TransactionTrait,
52 TransactionEnvelope, TrieAccount, TxEip4844Variant, TxEnvelope, TxReceipt, Typed2718,
53 constants::EMPTY_WITHDRAWALS,
54 proofs::{calculate_receipt_root, calculate_transaction_root},
55 transaction::Recovered,
56};
57use alloy_eips::{
58 BlockNumHash, Encodable2718, eip2935, eip4788,
59 eip4844::{DATA_GAS_PER_BLOB, kzg_to_versioned_hash},
60 eip6110::MAINNET_DEPOSIT_CONTRACT_ADDRESS,
61 eip7002, eip7251,
62 eip7685::EMPTY_REQUESTS_HASH,
63 eip7840::BlobParams,
64 eip7910::SystemContract,
65 eip7928::{BlockAccessList, EMPTY_BLOCK_ACCESS_LIST_HASH, compute_block_access_list_hash},
66};
67use alloy_evm::{
68 Database, EthEvmFactory, Evm, EvmEnv, EvmFactory, FromTxWithEncoded,
69 block::{BalIndexedDatabase, BlockExecutionResult, BlockExecutor, StateDB},
70 eth::{EthEvm, EthEvmContext},
71 overrides::{OverrideBlockHashes, apply_state_overrides},
72 precompiles::{DynPrecompile, MovePrecompileError, Precompile, PrecompilesMap},
73};
74use alloy_genesis::Genesis;
75use alloy_network::{
76 AnyHeader, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope, AnyTxType,
77 BlockResponse, Network, NetworkTransactionBuilder, ReceiptResponse, UnknownTxEnvelope,
78 UnknownTypedTransaction,
79};
80use alloy_primitives::{
81 Address, B256, Bloom, Bytes, Signature, TxKind, U256, address, hex, keccak256,
82 map::{AddressMap, B256Set, HashMap, HashSet},
83};
84use alloy_rlp::{Decodable, Encodable};
85use alloy_rpc_types::{
86 AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, BlockOverrides,
87 BlockTransactions, EIP1186AccountProofResponse as AccountProof,
88 EIP1186StorageProof as StorageProof, Filter, Header as AlloyHeader, Index, Log, Transaction,
89 TransactionReceipt,
90 anvil::Forking,
91 debug::ExecutionWitness,
92 request::TransactionRequest,
93 serde_helpers::JsonStorageKey,
94 simulate::{
95 MAX_SIMULATE_BLOCKS, SimBlock, SimCallResult, SimulateError, SimulatePayload,
96 SimulatedBlock,
97 },
98 state::{EvmOverrides, StateOverride},
99 trace::{
100 filter::TraceFilter,
101 geth::{
102 CallConfig, FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerConfig,
103 GethDebugTracerType, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace,
104 NoopFrame, TraceResult,
105 },
106 opcode::{BlockOpcodeGas, TransactionOpcodeGas},
107 parity::{
108 LocalizedTransactionTrace, TraceResults, TraceResultsWithTransactionHash, TraceType,
109 },
110 },
111};
112use alloy_rpc_types_eth::{AccountInfo as RpcAccountInfo, Bundle, EthCallResponse};
113use alloy_rpc_types_mev::{EthCallBundle, EthCallBundleResponse, EthCallBundleTransactionResult};
114use alloy_serde::{OtherFields, WithOtherFields};
115use alloy_sol_types::SolCall;
116use alloy_trie::{HashBuilder, Nibbles, proof::ProofRetainer, root::state_root_ref_unhashed};
117use anvil_core::eth::{
118 block::{Block, BlockInfo, canonical_block, create_block},
119 transaction::{MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo},
120};
121use anvil_rpc::error::{ErrorCode, RpcError};
122use chrono::Datelike;
123use eyre::{Context, Result};
124use flate2::{Compression, read::GzDecoder, write::GzEncoder};
125use foundry_evm::{
126 backend::{BlockchainDb, DatabaseError, DatabaseResult, RevertStateSnapshotAction},
127 constants::{DEFAULT_CREATE2_DEPLOYER, DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE},
128 core::precompiles::EC_RECOVER,
129 decode::RevertDecoder,
130 hardfork::{EthereumHardfork, FoundryHardfork},
131 inspectors::AccessListInspector,
132 traces::{
133 CallTraceDecoder, FourByteInspector, GethTraceBuilder, TracingInspector,
134 TracingInspectorConfig,
135 },
136 utils::{
137 apply_chain_specific_tx_replay_env_changes_for_chain, block_env_from_header,
138 get_blob_base_fee_update_fraction, get_blob_base_fee_update_fraction_by_spec_id,
139 get_blob_params_by_hardfork,
140 },
141};
142use foundry_evm_networks::{NetworkConfigs, apply_bsc_p256_precompile, arbitrum};
143use foundry_primitives::{
144 FoundryHeader, FoundryNetwork, FoundryReceiptEnvelope, FoundryTransactionRequest,
145 FoundryTxEnvelope, FoundryTxReceipt, TempoTransactionRequest,
146};
147use futures::channel::mpsc::{UnboundedSender, unbounded};
148use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
149use revm::{
150 Database as RevmDatabase, DatabaseCommit, Inspector,
151 context::{Block as RevmBlock, BlockEnv, Cfg, CfgEnv, ContextSetters, ContextTr, TxEnv},
152 context_interface::{
153 JournalTr, Transaction as _,
154 block::BlobExcessGasAndPrice,
155 result::{
156 EVMError, ExecutionResult, HaltReason, InvalidTransaction, Output, ResultAndState,
157 },
158 transaction::TransactionType,
159 },
160 database::{
161 AccountState, CacheDB, DbAccount, WrapDatabaseRef,
162 bal::{BalDatabase, BalState},
163 },
164 handler::{
165 EthFrame, EvmTr, EvmTrError, FrameResult, FrameTr, Handler as EvmHandler, validation,
166 },
167 inspector::{InspectorEvmTr, InspectorHandler},
168 interpreter::{InstructionResult, interpreter::EthInterpreter, interpreter_action::FrameInit},
169 precompile::{PrecompileSpecId, Precompiles},
170 primitives::{eip2780, hardfork::SpecId},
171 state::{Account, AccountInfo, EvmState, EvmStorageSlot, TransactionId},
172};
173use revm_inspectors::opcode::OpcodeGasInspector;
174use std::{
175 collections::BTreeMap,
176 fmt::{self, Debug},
177 io::{Read, Write},
178 marker::PhantomData,
179 ops::Mul,
180 path::{Path, PathBuf},
181 sync::{
182 Arc,
183 atomic::{AtomicBool, Ordering},
184 },
185 time::Duration,
186};
187use storage::{Blockchain, DEFAULT_HISTORY_LIMIT, MinedTransaction};
188use tempo_evm::{TempoPoolValidationEvm, evm::TempoEvmFactory};
189use tempo_hardfork::TempoHardfork;
190use tempo_precompiles::{
191 NONCE_PRECOMPILE_ADDRESS, TIP_FEE_MANAGER_ADDRESS, extend_tempo_precompiles,
192 nonce::NonceManager,
193 storage::{Handler, StorageActions, StorageCtx},
194 tip_fee_manager::{IFeeManager, TipFeeManager},
195 tip20::{ITIP20, TIP20Token},
196 tip20_factory::TIP20Factory,
197};
198use tempo_primitives::{
199 AASigned, SignatureType, TEMPO_TX_TYPE_ID, TempoSignature,
200 transaction::{
201 Call, KeychainSignature, PrimitiveSignature, RecoveredTempoAuthorization,
202 tt_signature::{P256SignatureWithPreHash, WebAuthnSignature},
203 },
204};
205use tempo_revm::{
206 ExecutionContext, TempoBatchCallEnv, TempoBlockEnv, TempoInvalidTransaction, TempoTxEnv,
207 evm::TempoContext, gas_params::tempo_gas_params,
208};
209#[cfg(test)]
210use tokio::sync::Notify;
211use tokio::{sync::RwLock as AsyncRwLock, task::JoinSet};
212
213#[cfg(any(feature = "base", feature = "optimism"))]
214use foundry_primitives::get_deposit_tx_parts;
215#[cfg(any(feature = "base", feature = "optimism"))]
216use op_alloy_consensus::DEPOSIT_TX_TYPE_ID;
217#[cfg(any(feature = "base", feature = "optimism"))]
218use op_revm::transaction::deposit::DepositTransactionParts;
219
220#[cfg(feature = "base")]
221use alloy_hardforks::ForkCondition;
222#[cfg(feature = "base")]
223use base_common_chains::{ChainConfig, ChainUpgrades};
224#[cfg(feature = "base")]
225use base_common_consensus::{
226 BaseTransactionInfo, BaseTxEnvelope, EIP8130_REJECTION_MSG, Eip8130Constants, Predeploys,
227};
228#[cfg(feature = "base")]
229use base_common_evm::{
230 BaseContext, BaseEvmFactory, BaseSpecId, BaseTime, BaseTransaction, DEPOSIT_TRANSACTION_TYPE,
231 DepositTransactionParts as BaseDepositTransactionParts, EIP8130_TRANSACTION_TYPE,
232 Eip8130PhaseStatuses, L1BlockInfo, ensure_create2_deployer, ensure_eip8130_system_accounts,
233};
234#[cfg(feature = "base")]
235use base_common_genesis::RollupConfig;
236#[cfg(feature = "base")]
237use base_common_precompiles::NonceManagerStorage;
238#[cfg(feature = "base")]
239use base_common_rpc_types::{
240 EIP8130_PRE_ZENITH_RPC_ERROR, Eip8130Nonce, Eip8130ReceiptFields,
241 Transaction as BaseRpcTransaction,
242};
243#[cfg(feature = "base")]
244use base_execution_eip8130::{FeeCheck, IntrinsicGas, IntrinsicGasInput};
245#[cfg(feature = "base")]
246use foundry_evm::{
247 core::{constants::SYSTEM_PRECOMPILE_STUB, evm::base_code_sentinel_addresses},
248 hardfork::BaseUpgrade,
249};
250#[cfg(feature = "base")]
251use revm::inspector::NoOpInspector;
252
253#[cfg(feature = "optimism")]
254use alloy_op_evm::{OpEvmContext, OpEvmFactory, OpTx};
255#[cfg(feature = "optimism")]
256use foundry_evm::hardfork::OpHardfork;
257#[cfg(feature = "optimism")]
258use foundry_evm_networks::NetworkVariant;
259#[cfg(feature = "optimism")]
260use op_alloy_consensus::POST_EXEC_TX_TYPE_ID;
261#[cfg(feature = "optimism")]
262use op_revm::OpTransaction;
263
264#[derive(Default, Clone, Debug)]
266struct CallTransactionInfo {
267 #[cfg(any(feature = "base", feature = "optimism"))]
269 deposit: DepositTransactionParts,
270}
271
272pub(crate) struct StagedForkReset {
274 node_config: NodeConfig,
275 db: Box<dyn Db>,
276 fees: FeeManager,
277 evm_env: EvmEnv,
278 fork: ClientFork,
279 timestamp: u64,
280 discard_old_cached_state: bool,
281 invalidated_cache_namespaces: Vec<ForkCacheNamespace>,
282 flush_old_cache: bool,
283 cache_lease: StagedForkCacheLease,
284}
285
286pub(crate) struct StagedMemoryReset<N: Network> {
288 node_config: NodeConfig,
289 db: Box<dyn Db>,
290 fees: FeeManager,
291 evm_env: EvmEnv,
292 hardfork: FoundryHardfork,
293 storage: BlockchainStorage<N>,
294 timestamp: u64,
295 flush_old_cache: bool,
296}
297
298#[derive(Clone, Debug, PartialEq, Eq)]
300struct ForkCacheSource {
301 rpc_url: String,
302 endpoint_identity: ForkEndpointIdentity,
303}
304
305impl ForkCacheSource {
306 fn from_fork(fork: &ClientFork) -> Option<Self> {
307 let config = fork.config.read();
308 Some(Self {
309 rpc_url: config.eth_rpc_url()?.to_string(),
310 endpoint_identity: config.endpoint_identity,
311 })
312 }
313
314 fn authoritative_identity_changed_at_same_url(
315 &self,
316 rpc_url: &str,
317 endpoint_identity: ForkEndpointIdentity,
318 ) -> bool {
319 let authoritative =
323 self.endpoint_identity.is_authoritative() || endpoint_identity.is_authoritative();
324 self.rpc_url == rpc_url && authoritative && self.endpoint_identity != endpoint_identity
325 }
326}
327
328#[derive(Clone, Debug, PartialEq, Eq)]
330struct ForkCacheNamespace {
331 chain_cache_dir: PathBuf,
332 file_name: String,
333}
334
335impl ForkCacheNamespace {
336 fn new(source_chain_id: u64, rpc_url: &str) -> Option<Self> {
337 Some(Self {
338 chain_cache_dir: foundry_config::Config::foundry_chain_cache_dir(source_chain_id)?,
339 file_name: format!("storage-{:x}.json", keccak256(rpc_url)),
340 })
341 }
342
343 fn invalidate(&self) -> Result<(), BlockchainError> {
344 let entries = match std::fs::read_dir(&self.chain_cache_dir) {
345 Ok(entries) => entries,
346 Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(()),
347 Err(err) => {
348 return Err(BlockchainError::Internal(format!(
349 "failed to inspect fork cache at {}: {err}",
350 self.chain_cache_dir.display()
351 )));
352 }
353 };
354
355 for entry in entries {
356 let entry = entry.map_err(|err| {
357 BlockchainError::Internal(format!(
358 "failed to inspect fork cache at {}: {err}",
359 self.chain_cache_dir.display()
360 ))
361 })?;
362 let file_type = entry.file_type().map_err(|err| {
363 BlockchainError::Internal(format!(
364 "failed to inspect fork cache entry at {}: {err}",
365 entry.path().display()
366 ))
367 })?;
368 if !file_type.is_dir() {
369 continue;
370 }
371 let cache_path = entry.path().join(&self.file_name);
372 if let Err(err) = std::fs::remove_file(&cache_path)
373 && err.kind() != std::io::ErrorKind::NotFound
374 {
375 return Err(BlockchainError::Internal(format!(
376 "failed to invalidate fork cache at {}: {err}",
377 cache_path.display()
378 )));
379 }
380 }
381 Ok(())
382 }
383}
384
385#[derive(Clone, Debug, Default)]
387struct StagedForkCacheLease(Option<Arc<StagedForkCacheLeaseInner>>);
388
389#[derive(Debug)]
390struct StagedForkCacheLeaseInner {
391 db: BlockchainDb,
392 cache_path: PathBuf,
393 armed: AtomicBool,
394}
395
396impl StagedForkCacheLease {
397 fn new(db: BlockchainDb, cache_path: Option<PathBuf>) -> Self {
398 Self(cache_path.map(|cache_path| {
399 Arc::new(StagedForkCacheLeaseInner { db, cache_path, armed: AtomicBool::new(true) })
400 }))
401 }
402
403 fn for_db(db: &BlockchainDb) -> Self {
404 Self::new(db.clone(), db.cache().cache_path().map(Path::to_path_buf))
405 }
406
407 fn disarm(&self) {
408 if let Some(inner) = &self.0 {
409 inner.armed.store(false, Ordering::Release);
410 }
411 }
412
413 fn rollback(&self) -> Result<(), BlockchainError> {
414 let Some(inner) = &self.0 else { return Ok(()) };
415 if Arc::strong_count(inner) == 1 && inner.armed.load(Ordering::Acquire) {
418 inner.invalidate()?;
419 inner.armed.store(false, Ordering::Release);
420 }
421 Ok(())
422 }
423}
424
425impl StagedForkCacheLeaseInner {
426 fn invalidate(&self) -> Result<(), BlockchainError> {
427 self.db.db().clear();
428 self.db.cache().flush();
429 if let Err(err) = std::fs::remove_file(&self.cache_path)
430 && err.kind() != std::io::ErrorKind::NotFound
431 {
432 return Err(BlockchainError::Internal(format!(
433 "failed to invalidate fork cache at {}: {err}",
434 self.cache_path.display()
435 )));
436 }
437 Ok(())
438 }
439}
440
441impl Drop for StagedForkCacheLeaseInner {
442 fn drop(&mut self) {
443 if self.armed.swap(false, Ordering::AcqRel)
444 && let Err(err) = self.invalidate()
445 {
446 warn!(target: "backend", %err, "failed to roll back staged fork cache");
447 }
448 }
449}
450
451struct StagedForkDbUser<D> {
456 db: Option<Arc<AsyncRwLock<D>>>,
457 cache_lease: StagedForkCacheLease,
458}
459
460impl<D> Clone for StagedForkDbUser<D> {
461 fn clone(&self) -> Self {
462 Self { db: self.db.clone(), cache_lease: self.cache_lease.clone() }
463 }
464}
465
466impl<D> StagedForkDbUser<D> {
467 const fn db(&self) -> &Arc<AsyncRwLock<D>> {
468 self.db.as_ref().expect("staged fork database must be present until drop")
469 }
470}
471
472impl<D> Drop for StagedForkDbUser<D> {
473 fn drop(&mut self) {
474 drop(self.db.take());
476 }
477}
478
479#[cfg(feature = "monad")]
480pub(crate) type MonadReplayContext = monad_revm::MonadChainContext;
481#[cfg(not(feature = "monad"))]
483#[derive(Clone)]
484pub(crate) struct MonadReplayContext;
485
486#[cfg(feature = "monad")]
487enum MonadExecutionContext<'a> {
488 Exact(Box<MonadReplayContext>),
489 Next(&'a mut MonadReplayContext),
490}
491
492#[cfg(not(feature = "monad"))]
493struct MonadExecutionContext<'a> {
494 _marker: std::marker::PhantomData<&'a mut MonadReplayContext>,
495}
496
497#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
498enum EnvelopeExecutionKind {
499 #[default]
500 Transaction,
501 Replay,
502}
503
504#[cfg_attr(not(feature = "monad"), allow(dead_code))]
505struct EnvelopeExecution<'a> {
506 monad_context: Option<MonadExecutionContext<'a>>,
507 kind: EnvelopeExecutionKind,
508 hardfork: FoundryHardfork,
509}
510
511impl<'a> EnvelopeExecution<'a> {
512 const fn transaction(
513 monad_context: Option<MonadExecutionContext<'a>>,
514 hardfork: FoundryHardfork,
515 ) -> Self {
516 Self { monad_context, kind: EnvelopeExecutionKind::Transaction, hardfork }
517 }
518
519 const fn replay(
520 monad_context: Option<MonadExecutionContext<'a>>,
521 hardfork: FoundryHardfork,
522 ) -> Self {
523 Self { monad_context, kind: EnvelopeExecutionKind::Replay, hardfork }
524 }
525}
526
527#[cfg(feature = "monad")]
528fn monad_execution_context_at(
529 context: Option<&MonadReplayContext>,
530 current_tx_index: usize,
531) -> Option<MonadExecutionContext<'static>> {
532 context.map(|context| {
533 let mut context = context.clone();
534 context.current_tx_index = current_tx_index;
535 MonadExecutionContext::Exact(Box::new(context))
536 })
537}
538
539#[cfg(not(feature = "monad"))]
540const fn monad_execution_context_at(
541 _context: Option<&MonadReplayContext>,
542 _current_tx_index: usize,
543) -> Option<MonadExecutionContext<'static>> {
544 None
545}
546
547#[cfg(feature = "monad")]
548const fn next_monad_context(context: &mut MonadReplayContext) -> MonadExecutionContext<'_> {
549 MonadExecutionContext::Next(context)
550}
551
552#[cfg(not(feature = "monad"))]
553const fn next_monad_context(_context: &mut MonadReplayContext) -> MonadExecutionContext<'_> {
554 MonadExecutionContext { _marker: std::marker::PhantomData }
555}
556
557const SIMULATE_GAS_CAP: u64 = 50_000_000;
559const SEPOLIA_DEPOSIT_CONTRACT_ADDRESS: Address =
560 address!("7f02c3e3c98b133055b8b348b2ac625669ed295d");
561const HOLESKY_DEPOSIT_CONTRACT_ADDRESS: Address = Address::repeat_byte(0x42);
562
563const TEMPO_RPC_SIMULATION_CONTEXT: B256 = B256::new(*b"TEMPO_RPC_SIMULATION_MPP_CONTEXT");
565
566struct SimulationHandler<EVM, ERROR, FRAME> {
568 _phantom: PhantomData<(EVM, ERROR, FRAME)>,
569}
570
571impl<EVM, ERROR, FRAME> Default for SimulationHandler<EVM, ERROR, FRAME> {
572 fn default() -> Self {
573 Self { _phantom: PhantomData }
574 }
575}
576
577impl<EVM, ERROR, FRAME> EvmHandler for SimulationHandler<EVM, ERROR, FRAME>
578where
579 EVM: EvmTr<
580 Context: ContextTr<
581 Block = BlockEnv,
582 Tx = TxEnv,
583 Journal: JournalTr<State = EvmState>,
584 > + ContextSetters,
585 Frame = FRAME,
586 >,
587 ERROR: EvmTrError<EVM>,
588 FRAME: FrameTr<FrameResult = FrameResult, FrameInit = FrameInit>,
589{
590 type Evm = EVM;
591 type Error = ERROR;
592 type HaltReason = HaltReason;
593
594 fn validate_env(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> {
595 let skip_blob_fee_check = evm.ctx_ref().cfg().is_base_fee_check_disabled()
596 && evm.ctx_ref().tx().tx_type == 3
597 && evm.ctx_ref().tx().max_fee_per_blob_gas == 0;
598 if !skip_blob_fee_check {
599 return validation::validate_env(evm.ctx());
600 }
601
602 let block = evm.ctx_ref().block().clone();
603 let mut validation_block = block.clone();
604 if let Some(blob_gas_and_price) = &mut validation_block.blob_excess_gas_and_price {
605 blob_gas_and_price.blob_gasprice = 0;
606 }
607 evm.ctx().set_block(validation_block);
608 let result = validation::validate_env(evm.ctx());
609 evm.ctx().set_block(block);
610 result
611 }
612}
613
614impl<EVM, ERROR> InspectorHandler for SimulationHandler<EVM, ERROR, EthFrame<EthInterpreter>>
615where
616 EVM: InspectorEvmTr<
617 Context: ContextTr<
618 Block = BlockEnv,
619 Tx = TxEnv,
620 Journal: JournalTr<State = EvmState>,
621 > + ContextSetters,
622 Frame = EthFrame<EthInterpreter>,
623 Inspector: Inspector<<EVM as EvmTr>::Context, EthInterpreter>,
624 >,
625 ERROR: EvmTrError<EVM>,
626{
627 type IT = EthInterpreter;
628}
629
630#[derive(Clone)]
631enum CallTxEnv {
632 Eth(TxEnv),
633 #[cfg(feature = "base")]
634 Base(Box<BaseTransaction<TxEnv>>),
635 #[cfg(feature = "monad")]
636 Monad(TxEnv),
637 #[cfg(feature = "optimism")]
638 Op(OpTransaction<TxEnv>),
639 Tempo(TempoTxEnv),
640}
641
642impl CallTxEnv {
643 #[cfg_attr(not(feature = "js-tracer"), allow(dead_code))]
644 const fn base(&self) -> &TxEnv {
645 match self {
646 Self::Eth(tx) => tx,
647 #[cfg(feature = "base")]
648 Self::Base(tx) => &tx.base,
649 #[cfg(feature = "monad")]
650 Self::Monad(tx) => tx,
651 #[cfg(feature = "optimism")]
652 Self::Op(tx) => &tx.base,
653 Self::Tempo(tx) => &tx.inner,
654 }
655 }
656
657 const fn base_mut(&mut self) -> &mut TxEnv {
658 match self {
659 Self::Eth(tx) => tx,
660 #[cfg(feature = "base")]
661 Self::Base(tx) => &mut tx.base,
662 #[cfg(feature = "monad")]
663 Self::Monad(tx) => tx,
664 #[cfg(feature = "optimism")]
665 Self::Op(tx) => &mut tx.base,
666 Self::Tempo(tx) => &mut tx.inner,
667 }
668 }
669
670 fn into_base(self) -> TxEnv {
671 match self {
672 Self::Eth(tx) => tx,
673 #[cfg(feature = "base")]
674 Self::Base(tx) => tx.base,
675 #[cfg(feature = "monad")]
676 Self::Monad(tx) => tx,
677 #[cfg(feature = "optimism")]
678 Self::Op(tx) => tx.base,
679 Self::Tempo(tx) => tx.inner,
680 }
681 }
682
683 fn uses_protocol_call_nonce(&self) -> bool {
684 match self {
685 Self::Eth(tx) => tx.kind.is_call(),
686 #[cfg(feature = "base")]
687 Self::Base(tx) => {
688 tx.eip8130.is_none()
689 && tx.base.tx_type != DEPOSIT_TRANSACTION_TYPE
690 && tx.base.kind.is_call()
691 }
692 #[cfg(feature = "monad")]
693 Self::Monad(tx) => tx.kind.is_call(),
694 #[cfg(feature = "optimism")]
695 Self::Op(tx) => tx.base.kind.is_call(),
696 Self::Tempo(tx) => tx.tempo_tx_env.as_ref().map_or_else(
697 || tx.inner.kind.is_call(),
698 |aa| {
699 aa.nonce_key.is_zero()
700 && aa.aa_calls.first().is_some_and(|call| call.to.is_call())
701 },
702 ),
703 }
704 }
705}
706
707fn apply_tempo_envelope_identity(tx_env: &mut CallTxEnv, simulated_tx: Option<&AASigned>) {
708 if let (CallTxEnv::Tempo(tx_env), Some(simulated_tx)) = (tx_env, simulated_tx) {
709 tx_env.unique_tx_identifier = Some(simulated_tx.expiring_nonce_hash(tx_env.inner.caller));
710 if let Some(batch) = &mut tx_env.tempo_tx_env {
711 batch.tx_hash = *simulated_tx.hash();
712 }
713 }
714}
715
716struct PreparedCall {
717 evm_env: EvmEnv,
718 tx_env: CallTxEnv,
719 simulated_tempo_tx: Option<AASigned>,
720 #[cfg(feature = "base")]
721 simulated_envelope: Option<FoundryTxEnvelope>,
722}
723
724#[derive(Default)]
725struct TypedCallOverrides {
726 gas_limit: Option<u64>,
727 access_list: Option<AccessList>,
728 disable_fee_charge: bool,
729}
730
731pub(crate) struct GasEstimateCallOptions {
732 gas_limit: u64,
733 disable_fee_charge: bool,
734 monad_context: Option<MonadReplayContext>,
735}
736
737impl GasEstimateCallOptions {
738 pub(crate) const fn new(
739 gas_limit: u64,
740 disable_fee_charge: bool,
741 monad_context: Option<MonadReplayContext>,
742 ) -> Self {
743 Self { gas_limit, disable_fee_charge, monad_context }
744 }
745}
746
747#[cfg(all(feature = "base", feature = "optimism", feature = "monad"))]
751pub trait BackendInspector<DB: Database>:
752 Inspector<EthEvmContext<DB>>
753 + Inspector<BaseContext<DB>>
754 + Inspector<OpEvmContext<DB>>
755 + Inspector<TempoContext<DB>>
756 + Inspector<alloy_monad_evm::MonadContext<DB>>
757{
758}
759#[cfg(all(feature = "base", feature = "optimism", feature = "monad"))]
760impl<DB: Database, T> BackendInspector<DB> for T where
761 T: Inspector<EthEvmContext<DB>>
762 + Inspector<BaseContext<DB>>
763 + Inspector<OpEvmContext<DB>>
764 + Inspector<TempoContext<DB>>
765 + Inspector<alloy_monad_evm::MonadContext<DB>>
766{
767}
768#[cfg(all(feature = "base", feature = "optimism", not(feature = "monad")))]
769pub trait BackendInspector<DB: Database>:
770 Inspector<EthEvmContext<DB>>
771 + Inspector<BaseContext<DB>>
772 + Inspector<OpEvmContext<DB>>
773 + Inspector<TempoContext<DB>>
774{
775}
776#[cfg(all(feature = "base", feature = "optimism", not(feature = "monad")))]
777impl<DB: Database, T> BackendInspector<DB> for T where
778 T: Inspector<EthEvmContext<DB>>
779 + Inspector<BaseContext<DB>>
780 + Inspector<OpEvmContext<DB>>
781 + Inspector<TempoContext<DB>>
782{
783}
784#[cfg(all(feature = "base", not(feature = "optimism"), feature = "monad"))]
785pub trait BackendInspector<DB: Database>:
786 Inspector<EthEvmContext<DB>>
787 + Inspector<BaseContext<DB>>
788 + Inspector<TempoContext<DB>>
789 + Inspector<alloy_monad_evm::MonadContext<DB>>
790{
791}
792#[cfg(all(feature = "base", not(feature = "optimism"), feature = "monad"))]
793impl<DB: Database, T> BackendInspector<DB> for T where
794 T: Inspector<EthEvmContext<DB>>
795 + Inspector<BaseContext<DB>>
796 + Inspector<TempoContext<DB>>
797 + Inspector<alloy_monad_evm::MonadContext<DB>>
798{
799}
800#[cfg(all(feature = "base", not(feature = "optimism"), not(feature = "monad")))]
801pub trait BackendInspector<DB: Database>:
802 Inspector<EthEvmContext<DB>> + Inspector<BaseContext<DB>> + Inspector<TempoContext<DB>>
803{
804}
805#[cfg(all(feature = "base", not(feature = "optimism"), not(feature = "monad")))]
806impl<DB: Database, T> BackendInspector<DB> for T where
807 T: Inspector<EthEvmContext<DB>> + Inspector<BaseContext<DB>> + Inspector<TempoContext<DB>>
808{
809}
810#[cfg(all(not(feature = "base"), feature = "optimism", feature = "monad"))]
811pub trait BackendInspector<DB: Database>:
812 Inspector<EthEvmContext<DB>>
813 + Inspector<OpEvmContext<DB>>
814 + Inspector<TempoContext<DB>>
815 + Inspector<alloy_monad_evm::MonadContext<DB>>
816{
817}
818#[cfg(all(not(feature = "base"), feature = "optimism", feature = "monad"))]
819impl<DB: Database, T> BackendInspector<DB> for T where
820 T: Inspector<EthEvmContext<DB>>
821 + Inspector<OpEvmContext<DB>>
822 + Inspector<TempoContext<DB>>
823 + Inspector<alloy_monad_evm::MonadContext<DB>>
824{
825}
826#[cfg(all(not(feature = "base"), feature = "optimism", not(feature = "monad")))]
827pub trait BackendInspector<DB: Database>:
828 Inspector<EthEvmContext<DB>> + Inspector<OpEvmContext<DB>> + Inspector<TempoContext<DB>>
829{
830}
831#[cfg(all(not(feature = "base"), feature = "optimism", not(feature = "monad")))]
832impl<DB: Database, T> BackendInspector<DB> for T where
833 T: Inspector<EthEvmContext<DB>> + Inspector<OpEvmContext<DB>> + Inspector<TempoContext<DB>>
834{
835}
836#[cfg(all(not(feature = "base"), not(feature = "optimism"), feature = "monad"))]
837pub trait BackendInspector<DB: Database>:
838 Inspector<EthEvmContext<DB>>
839 + Inspector<TempoContext<DB>>
840 + Inspector<alloy_monad_evm::MonadContext<DB>>
841{
842}
843#[cfg(all(not(feature = "base"), not(feature = "optimism"), feature = "monad"))]
844impl<DB: Database, T> BackendInspector<DB> for T where
845 T: Inspector<EthEvmContext<DB>>
846 + Inspector<TempoContext<DB>>
847 + Inspector<alloy_monad_evm::MonadContext<DB>>
848{
849}
850#[cfg(all(not(feature = "base"), not(feature = "optimism"), not(feature = "monad")))]
851pub trait BackendInspector<DB: Database>:
852 Inspector<EthEvmContext<DB>> + Inspector<TempoContext<DB>>
853{
854}
855#[cfg(all(not(feature = "base"), not(feature = "optimism"), not(feature = "monad")))]
856impl<DB: Database, T> BackendInspector<DB> for T where
857 T: Inspector<EthEvmContext<DB>> + Inspector<TempoContext<DB>>
858{
859}
860#[cfg(feature = "base")]
861mod base;
862pub mod cache;
863pub mod fork_db;
864pub mod in_memory_db;
865pub mod inspector;
866#[cfg(feature = "monad")]
867mod monad;
868#[cfg(feature = "optimism")]
869pub mod optimism;
870pub mod state;
871pub mod storage;
872
873pub trait DatabaseRef: revm::DatabaseRef<Error = DatabaseError> + Debug {}
877impl<T> DatabaseRef for T where T: revm::DatabaseRef<Error = DatabaseError> + Debug {}
878impl DatabaseRef for dyn crate::eth::backend::db::Db {}
879
880pub const MIN_TRANSACTION_GAS: u128 = 21000;
882pub const MIN_CREATE_GAS: u128 = 53000;
884#[cfg(feature = "base")]
886const DEFAULT_BASE_L1_BASE_FEE: u64 = 1_000_000_000;
887#[cfg(feature = "base")]
889const DEFAULT_BASE_L1_FEE_SCALAR: u32 = 1_000_000;
890
891#[cfg(feature = "base")]
896fn ensure_base_time_predeploy(
897 mut db: &mut dyn crate::eth::backend::db::Db,
898 reset_admin: bool,
899) -> Result<(), DatabaseError> {
900 base::ensure_l1_block_predeploy(db)?;
901 let mut proxy = db.basic(Predeploys::BASE_TIME)?.unwrap_or_default();
902 let missing_code = proxy.is_empty_code_hash();
903 if missing_code {
904 let code = revm::state::Bytecode::new_raw(BaseTime::proxy_bytecode());
905 proxy.code_hash = code.hash_slow();
906 proxy.code = Some(code);
907 db.insert_account(Predeploys::BASE_TIME, proxy);
908 }
909 if reset_admin || missing_code {
910 db.set_storage_at(
911 Predeploys::BASE_TIME,
912 B256::from(BaseTime::ADMIN_SLOT.to_be_bytes::<32>()),
913 B256::from(U256::from_be_slice(Predeploys::PROXY_ADMIN.as_slice()).to_be_bytes::<32>()),
914 )?;
915 }
916 let upgrades = ChainUpgrades::new([(BaseUpgrade::Denim, ForkCondition::Timestamp(1))]);
917 BaseTime::ensure_predeploy(upgrades, 1, &mut db)
918 .map_err(|err| DatabaseError::AnyRequest(Arc::new(eyre::eyre!(err))))
919}
920
921fn tempo_nonce(
922 state: &dyn DatabaseRef,
923 caller: Address,
924 nonce_key: U256,
925) -> Result<u64, BlockchainError> {
926 if nonce_key.is_zero() {
927 return Ok(state.basic_ref(caller)?.map(|account| account.nonce).unwrap_or_default());
928 }
929 if nonce_key == U256::MAX {
930 return Ok(0);
931 }
932 let slot = NonceManager::new().nonces[caller][nonce_key].slot();
933 Ok(state.storage_ref(NONCE_PRECOMPILE_ADDRESS, slot)?.saturating_to())
934}
935
936fn mock_tempo_signature(
937 key_type: SignatureType,
938 key_data: Option<Bytes>,
939 key_id: Option<Address>,
940 caller: Address,
941 is_t1c: bool,
942) -> TempoSignature {
943 let signature = match key_type {
944 SignatureType::Secp256k1 => {
945 PrimitiveSignature::Secp256k1(Signature::new(U256::ZERO, U256::ZERO, false))
946 }
947 SignatureType::P256 => PrimitiveSignature::P256(P256SignatureWithPreHash {
948 r: B256::ZERO,
949 s: B256::ZERO,
950 pub_key_x: B256::ZERO,
951 pub_key_y: B256::ZERO,
952 pre_hash: false,
953 }),
954 SignatureType::WebAuthn => {
955 const CLIENT_JSON: &str = r#"{"type":"webauthn.get","challenge":"","origin":""}"#;
956 const AUTH_DATA_SIZE: usize = 37;
957 const MIN_SIZE: usize = AUTH_DATA_SIZE + CLIENT_JSON.len();
958 const DEFAULT_SIZE: usize = 800;
959 const MAX_SIZE: usize = 8192;
960
961 let size = key_data
962 .as_deref()
963 .and_then(|data| match data.len() {
964 1 => Some(data[0] as usize),
965 2 => Some(u16::from_be_bytes([data[0], data[1]]) as usize),
966 4 => Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]]) as usize),
967 _ => None,
968 })
969 .unwrap_or(DEFAULT_SIZE)
970 .clamp(MIN_SIZE, MAX_SIZE);
971 let mut webauthn_data = vec![0u8; AUTH_DATA_SIZE];
972 webauthn_data[32] = 0x01;
973 let padding = "x".repeat(size - MIN_SIZE);
974 webauthn_data.extend_from_slice(
975 format!(r#"{{"type":"webauthn.get","challenge":"","origin":"{padding}"}}"#)
976 .as_bytes(),
977 );
978 PrimitiveSignature::WebAuthn(WebAuthnSignature {
979 webauthn_data: webauthn_data.into(),
980 r: B256::ZERO,
981 s: B256::ZERO,
982 pub_key_x: B256::ZERO,
983 pub_key_y: B256::ZERO,
984 })
985 }
986 };
987
988 if key_id.is_some() {
989 let signature = if is_t1c {
990 KeychainSignature::new(caller, signature)
991 } else {
992 KeychainSignature::new_v1(caller, signature)
993 };
994 TempoSignature::Keychain(signature)
995 } else {
996 TempoSignature::Primitive(signature)
997 }
998}
999
1000fn call_config_from_tracer_config(
1001 tracer_config: GethDebugTracerConfig,
1002) -> Result<CallConfig, serde_json::Error> {
1003 let mut tracer_config = tracer_config.into_json();
1004 if let Some(config) = tracer_config.as_object_mut()
1005 && !config.contains_key("onlyTopCall")
1006 && let Some(only_top_level_call) = config.remove("onlyTopLevelCall")
1007 {
1008 config.insert("onlyTopCall".to_string(), only_top_level_call);
1009 }
1010
1011 GethDebugTracerConfig(tracer_config).into_call_config()
1012}
1013
1014fn parity_trace_results(
1019 inspector: TracingInspector,
1020 result: &ResultAndState<HaltReason>,
1021 trace_types: &HashSet<TraceType>,
1022 db: impl revm::DatabaseRef<Error = DatabaseError>,
1023) -> Result<TraceResults, BlockchainError> {
1024 inspector
1025 .into_parity_builder()
1026 .into_trace_results_with_state(result, trace_types, EmptyAsAbsentDb(db))
1027 .map_err(Into::into)
1028}
1029
1030fn ensure_mined_trace_block(block: BlockNumber) -> Result<(), BlockchainError> {
1034 if block.is_pending() {
1035 return Err(BlockchainError::RpcError(RpcError::invalid_params(
1036 "pending block traces are not supported",
1037 )));
1038 }
1039 Ok(())
1040}
1041
1042pub type State = foundry_evm::utils::StateChangeset;
1043
1044#[derive(Clone, Debug, Default)]
1045struct SimulationPrecompileOverrides {
1046 moves: Vec<(Address, Address)>,
1047}
1048
1049pub enum BlockRequest<T> {
1051 Pending(Vec<Arc<PoolTransaction<T>>>),
1052 Number(u64),
1053}
1054
1055impl<T> fmt::Debug for BlockRequest<T> {
1056 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1057 match self {
1058 Self::Pending(txs) => f.debug_tuple("Pending").field(&txs.len()).finish(),
1059 Self::Number(n) => f.debug_tuple("Number").field(n).finish(),
1060 }
1061 }
1062}
1063
1064impl<T> BlockRequest<T> {
1065 pub const fn block_number(&self) -> BlockNumber {
1066 match *self {
1067 Self::Pending(_) => BlockNumber::Pending,
1068 Self::Number(n) => BlockNumber::Number(n),
1069 }
1070 }
1071}
1072
1073struct StateSnapshot {
1074 block_number: u64,
1075 block_hash: B256,
1076 fees: FeeSnapshot,
1077 time: TimeSnapshot,
1078 next_block: NextBlockOverrides,
1079}
1080
1081#[cfg(test)]
1082#[derive(Clone)]
1083struct MiningCommitHook {
1084 reached: Arc<Notify>,
1085 resume: Arc<Notify>,
1086}
1087
1088pub struct Backend<N: Network> {
1090 db: Arc<AsyncRwLock<Box<dyn Db>>>,
1110 blockchain: Blockchain<N>,
1112 states: Arc<RwLock<InMemoryBlockStates>>,
1114 evm_env: Arc<RwLock<EvmEnv>>,
1116 networks: NetworkConfigs,
1118 #[cfg(feature = "base")]
1120 base_activation_admin: Option<Address>,
1121 hardfork: Arc<RwLock<FoundryHardfork>>,
1123 fork: Arc<RwLock<Option<ClientFork>>>,
1125 last_fork_cache_source: Arc<RwLock<Option<ForkCacheSource>>>,
1127 time: TimeManager,
1129 cheats: CheatsManager,
1131 fees: FeeManager,
1133 genesis: GenesisConfig,
1135 new_block_listeners: Arc<Mutex<Vec<UnboundedSender<ChainNotification>>>>,
1138 active_state_snapshots: Arc<Mutex<HashMap<U256, StateSnapshot>>>,
1140 enable_steps_tracing: bool,
1141 print_logs: bool,
1142 print_traces: bool,
1143 call_trace_decoder: Arc<RwLock<Arc<CallTraceDecoder>>>,
1145 prune_state_history_config: PruneStateHistoryConfig,
1147 transaction_block_keeper: Option<usize>,
1149 pub(crate) node_config: Arc<AsyncRwLock<NodeConfig>>,
1150 slots_in_an_epoch: u64,
1152 precompile_factory: Option<Arc<dyn PrecompileFactory>>,
1154 mining: Arc<tokio::sync::Mutex<()>>,
1156 #[cfg(test)]
1158 mining_commit_hook: Arc<Mutex<Option<MiningCommitHook>>>,
1159 disable_pool_balance_checks: bool,
1161 startup_fork_cache_user: StagedForkDbUser<Box<dyn Db>>,
1166}
1167
1168impl<N: Network> Clone for Backend<N> {
1169 fn clone(&self) -> Self {
1170 Self {
1171 db: self.db.clone(),
1172 blockchain: self.blockchain.clone(),
1173 states: self.states.clone(),
1174 evm_env: self.evm_env.clone(),
1175 networks: self.networks,
1176 #[cfg(feature = "base")]
1177 base_activation_admin: self.base_activation_admin,
1178 hardfork: self.hardfork.clone(),
1179 fork: self.fork.clone(),
1180 last_fork_cache_source: self.last_fork_cache_source.clone(),
1181 time: self.time.clone(),
1182 cheats: self.cheats.clone(),
1183 fees: self.fees.clone(),
1184 genesis: self.genesis.clone(),
1185 new_block_listeners: self.new_block_listeners.clone(),
1186 active_state_snapshots: self.active_state_snapshots.clone(),
1187 enable_steps_tracing: self.enable_steps_tracing,
1188 print_logs: self.print_logs,
1189 print_traces: self.print_traces,
1190 call_trace_decoder: self.call_trace_decoder.clone(),
1191 prune_state_history_config: self.prune_state_history_config,
1192 transaction_block_keeper: self.transaction_block_keeper,
1193 node_config: self.node_config.clone(),
1194 slots_in_an_epoch: self.slots_in_an_epoch,
1195 precompile_factory: self.precompile_factory.clone(),
1196 mining: self.mining.clone(),
1197 #[cfg(test)]
1198 mining_commit_hook: self.mining_commit_hook.clone(),
1199 disable_pool_balance_checks: self.disable_pool_balance_checks,
1200 startup_fork_cache_user: self.startup_fork_cache_user.clone(),
1201 }
1202 }
1203}
1204
1205impl<N: Network> fmt::Debug for Backend<N> {
1206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1207 f.debug_struct("Backend").finish_non_exhaustive()
1208 }
1209}
1210
1211impl<N: Network> Backend<N> {
1213 pub fn impersonate(&self, addr: Address) -> bool {
1217 if self.cheats.impersonated_accounts().contains(&addr) {
1218 return true;
1219 }
1220 self.evm_env.write().cfg_env.disable_eip3607 = true;
1222 self.cheats.impersonate(addr)
1223 }
1224
1225 pub fn stop_impersonating(&self, addr: Address) {
1229 self.cheats.stop_impersonating(&addr);
1230 }
1231
1232 pub fn auto_impersonate_account(&self, enabled: bool) {
1234 self.cheats.set_auto_impersonate_account(enabled);
1235 }
1236
1237 pub fn get_fork(&self) -> Option<ClientFork> {
1239 self.fork.read().clone()
1240 }
1241
1242 pub(crate) fn commit_startup_fork_cache(&self) {
1244 self.startup_fork_cache_user.cache_lease.disarm();
1245 }
1246
1247 pub fn get_db(&self) -> &Arc<AsyncRwLock<Box<dyn Db>>> {
1249 &self.db
1250 }
1251
1252 pub(crate) async fn lock_mining(&self) -> tokio::sync::MutexGuard<'_, ()> {
1254 self.mining.lock().await
1255 }
1256
1257 pub(crate) async fn lock_mining_owned(self: &Arc<Self>) -> tokio::sync::OwnedMutexGuard<()> {
1259 self.mining.clone().lock_owned().await
1260 }
1261
1262 pub async fn get_account(&self, address: Address) -> DatabaseResult<AccountInfo> {
1264 Ok(self.db.read().await.basic_ref(address)?.unwrap_or_default())
1265 }
1266
1267 pub fn is_fork(&self) -> bool {
1269 self.fork.read().is_some()
1270 }
1271
1272 pub async fn set_create2_deployer(&self, address: Address) -> DatabaseResult<()> {
1274 self.set_code(address, Bytes::from_static(DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE)).await?;
1275 Ok(())
1276 }
1277
1278 pub(crate) fn update_interval_mine_block_time(&self, block_time: Duration) {
1280 self.states.write().update_interval_mine_block_time(block_time)
1281 }
1282
1283 pub const fn time(&self) -> &TimeManager {
1285 &self.time
1286 }
1287
1288 pub const fn cheats(&self) -> &CheatsManager {
1290 &self.cheats
1291 }
1292
1293 pub fn skip_blob_validation(&self, impersonator: Option<Address>) -> bool {
1295 self.cheats().auto_impersonate_accounts()
1296 || impersonator
1297 .is_some_and(|addr| self.cheats().impersonated_accounts().contains(&addr))
1298 }
1299
1300 pub const fn fees(&self) -> &FeeManager {
1302 &self.fees
1303 }
1304
1305 pub const fn evm_env(&self) -> &Arc<RwLock<EvmEnv>> {
1307 &self.evm_env
1308 }
1309
1310 pub fn best_hash(&self) -> B256 {
1312 self.blockchain.storage.read().best_hash
1313 }
1314
1315 pub fn best_number(&self) -> u64 {
1317 self.blockchain.storage.read().best_number
1318 }
1319
1320 pub fn set_block_number(&self, number: u64) {
1322 self.evm_env.write().block_env.number = U256::from(number);
1323 }
1324
1325 pub fn coinbase(&self) -> Address {
1327 self.evm_env.read().block_env.beneficiary
1328 }
1329
1330 pub fn chain_id(&self) -> U256 {
1332 U256::from(self.evm_env.read().cfg_env.chain_id)
1333 }
1334
1335 fn protocol_chain_id(&self) -> u64 {
1337 self.get_fork().map_or_else(|| self.evm_env.read().cfg_env.chain_id, |fork| fork.chain_id())
1338 }
1339
1340 pub fn set_chain_id(&self, chain_id: u64) {
1341 self.evm_env.write().cfg_env.chain_id = chain_id;
1342 }
1343
1344 pub const fn genesis_time(&self) -> u64 {
1346 self.genesis.timestamp
1347 }
1348
1349 pub const fn genesis_number(&self) -> u64 {
1351 self.genesis.number
1352 }
1353
1354 pub async fn current_balance(&self, address: Address) -> DatabaseResult<U256> {
1356 Ok(self.get_account(address).await?.balance)
1357 }
1358
1359 pub async fn current_nonce(&self, address: Address) -> DatabaseResult<u64> {
1361 Ok(self.get_account(address).await?.nonce)
1362 }
1363
1364 pub fn set_coinbase(&self, address: Address) {
1366 self.evm_env.write().block_env.beneficiary = address;
1367 }
1368
1369 pub fn set_next_block_prevrandao(&self, prevrandao: B256) {
1374 self.cheats.set_next_block_prevrandao(prevrandao);
1375 }
1376
1377 pub fn set_next_block_parent_beacon_block_root(&self, root: B256) {
1382 self.cheats.set_next_block_parent_beacon_block_root(root);
1383 }
1384
1385 pub async fn set_nonce(&self, address: Address, nonce: U256) -> DatabaseResult<()> {
1387 self.db.write().await.set_nonce(address, nonce.try_into().unwrap_or(u64::MAX))
1388 }
1389
1390 pub async fn set_balance(&self, address: Address, balance: U256) -> DatabaseResult<()> {
1392 self.db.write().await.set_balance(address, balance)
1393 }
1394
1395 pub(crate) async fn add_balance(&self, address: Address, balance: U256) -> DatabaseResult<()> {
1397 let mut db = self.db.write().await;
1398 let current_balance = db.basic(address)?.unwrap_or_default().balance;
1399 db.set_balance(address, current_balance.saturating_add(balance))
1400 }
1401
1402 pub async fn set_code(&self, address: Address, code: Bytes) -> DatabaseResult<()> {
1404 self.db.write().await.set_code(address, code)
1405 }
1406
1407 pub async fn set_storage_at(
1409 &self,
1410 address: Address,
1411 slot: U256,
1412 val: B256,
1413 ) -> DatabaseResult<()> {
1414 self.db.write().await.set_storage_at(address, slot.into(), val)
1415 }
1416
1417 pub fn spec_id(&self) -> SpecId {
1419 *self.evm_env.read().spec_id()
1420 }
1421
1422 pub fn is_eip1559(&self) -> bool {
1424 (self.spec_id() as u8) >= (SpecId::LONDON as u8)
1425 }
1426
1427 pub fn is_eip3675(&self) -> bool {
1429 (self.spec_id() as u8) >= (SpecId::MERGE as u8)
1430 }
1431
1432 pub fn is_eip2930(&self) -> bool {
1434 (self.spec_id() as u8) >= (SpecId::BERLIN as u8)
1435 }
1436
1437 pub fn is_eip4844(&self) -> bool {
1439 (self.spec_id() as u8) >= (SpecId::CANCUN as u8)
1440 }
1441
1442 pub fn is_eip7702(&self) -> bool {
1444 (self.spec_id() as u8) >= (SpecId::PRAGUE as u8)
1445 }
1446
1447 pub const fn is_optimism(&self) -> bool {
1451 self.networks.is_optimism()
1452 }
1453
1454 pub const fn is_tempo(&self) -> bool {
1456 self.networks.is_tempo()
1457 }
1458
1459 pub const fn is_celo(&self) -> bool {
1461 self.networks.is_celo()
1462 }
1463
1464 pub const fn is_monad(&self) -> bool {
1466 self.networks.is_monad()
1467 }
1468
1469 pub const fn execution_profile_name(&self) -> &'static str {
1471 self.networks.execution_profile_name()
1472 }
1473
1474 fn pending_mined_transaction(
1476 &self,
1477 transaction: MaybeImpersonatedTransaction<FoundryTxEnvelope>,
1478 ) -> Result<PendingTransaction<FoundryTxEnvelope>, BlockchainError> {
1479 #[cfg(feature = "monad")]
1480 if self.is_monad() {
1481 return Self::monad_pending_mined_transaction_from_storage(
1482 &self.blockchain.storage.read(),
1483 transaction,
1484 );
1485 }
1486 Ok(PendingTransaction::from_maybe_impersonated(transaction)?)
1487 }
1488
1489 #[cfg(not(feature = "monad"))]
1490 fn active_monad_context_for_mined_block(
1491 &self,
1492 _block: &Block,
1493 ) -> Result<Option<MonadReplayContext>, BlockchainError> {
1494 Ok(None)
1495 }
1496
1497 #[cfg(not(feature = "monad"))]
1498 fn active_monad_context_before_mined_transaction(
1499 &self,
1500 _block: &Block,
1501 _current_tx_index: usize,
1502 ) -> Result<Option<MonadReplayContext>, BlockchainError> {
1503 Ok(None)
1504 }
1505
1506 #[cfg(feature = "base")]
1508 pub const fn is_base(&self) -> bool {
1509 self.networks.is_base()
1510 }
1511
1512 pub fn hardfork(&self) -> FoundryHardfork {
1514 if let Some(hardfork) =
1515 self.fork.read().as_ref().and_then(|fork| fork.config.read().hardfork)
1516 {
1517 return hardfork;
1518 }
1519 *self.hardfork.read()
1520 }
1521
1522 fn ethereum_block_transitions(
1524 &self,
1525 hardfork: FoundryHardfork,
1526 parent_beacon_block_root: Option<B256>,
1527 execution_kind: BlockExecutionKind,
1528 ) -> Option<EthereumBlockTransitions> {
1529 if self.is_optimism() || self.is_tempo() {
1530 return None;
1531 }
1532 let FoundryHardfork::Ethereum(hardfork) = hardfork else { return None };
1533 Some(EthereumBlockTransitions {
1534 hardfork,
1535 deposit_contract_address: self.ethereum_deposit_contract_address(),
1536 parent_beacon_block_root,
1537 execution_kind,
1538 })
1539 }
1540
1541 fn ethereum_deposit_contract_address(&self) -> Address {
1543 if let Some(address) = self
1544 .genesis
1545 .genesis_init
1546 .as_ref()
1547 .and_then(|genesis| genesis.config.deposit_contract_address)
1548 {
1549 return address;
1550 }
1551
1552 match NamedChain::try_from(self.evm_env.read().cfg_env.chain_id) {
1553 Ok(NamedChain::Sepolia) => SEPOLIA_DEPOSIT_CONTRACT_ADDRESS,
1554 Ok(NamedChain::Holesky) => HOLESKY_DEPOSIT_CONTRACT_ADDRESS,
1555 _ => MAINNET_DEPOSIT_CONTRACT_ADDRESS,
1557 }
1558 }
1559
1560 pub fn tempo_hardfork(&self) -> TempoHardfork {
1562 TempoHardfork::from(self.hardfork())
1563 }
1564
1565 #[cfg(feature = "monad")]
1567 pub fn monad_hardfork(&self) -> monad_revm::MonadHardfork {
1568 monad_revm::MonadHardfork::from(self.hardfork())
1569 }
1570
1571 #[cfg(feature = "base")]
1573 pub fn base_upgrade(&self) -> BaseUpgrade {
1574 match self.hardfork() {
1575 FoundryHardfork::Base(upgrade) => upgrade,
1576 _ => BaseUpgrade::Azul,
1577 }
1578 }
1579
1580 #[cfg(feature = "base")]
1582 pub const fn base_activation_admin(&self) -> Option<Address> {
1583 self.base_activation_admin
1584 }
1585
1586 #[cfg(feature = "base")]
1588 pub fn base_upgrade_at_timestamp(&self, timestamp: u64) -> BaseUpgrade {
1589 if self.is_fork() {
1590 BaseUpgrade::from_chain_and_timestamp(self.chain_id().to(), timestamp)
1591 .unwrap_or_else(|| self.base_upgrade())
1592 } else {
1593 self.base_upgrade()
1594 }
1595 }
1596
1597 fn prepare_block_timestamp(
1599 &self,
1600 db: &dyn revm::DatabaseRef<Error = DatabaseError>,
1601 parent_timestamp: u64,
1602 ) -> Result<PendingBlockTimestamp, DatabaseError> {
1603 #[cfg(feature = "base")]
1604 if self.is_base() && self.base_upgrade() >= BaseUpgrade::Denim {
1605 return base::prepare_block_timestamp(db, &self.time, parent_timestamp);
1606 }
1607 let _ = (db, parent_timestamp);
1608 Ok(self.time.prepare_next_timestamp())
1609 }
1610
1611 #[cfg(feature = "base")]
1613 fn base_system_transactions(
1614 &self,
1615 db: &dyn revm::DatabaseRef<Error = DatabaseError>,
1616 block_number: u64,
1617 timestamp: u64,
1618 parent_hash: B256,
1619 ) -> Result<Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>, DatabaseError> {
1620 if self.is_base() && self.base_upgrade() >= BaseUpgrade::Denim {
1621 base::system_transactions(db, block_number, timestamp, parent_hash, self.is_fork())
1622 } else {
1623 Ok(Vec::new())
1624 }
1625 }
1626
1627 #[cfg(feature = "base")]
1629 pub fn eip8130_pool_timestamp(&self) -> u64 {
1630 self.evm_env.read().block_env.timestamp.saturating_to()
1631 }
1632
1633 #[cfg(feature = "base")]
1639 pub fn eip8130_pool_timestamp_ms(&self) -> u64 {
1640 self.eip8130_pool_timestamp().saturating_mul(1_000)
1641 }
1642
1643 pub fn is_tempo_hardfork_active(&self, hardfork: TempoHardfork) -> bool {
1645 self.is_tempo() && self.tempo_hardfork() >= hardfork
1646 }
1647
1648 pub fn precompiles(&self) -> BTreeMap<String, Address> {
1650 let spec_id = self.spec_id();
1651 let mut precompiles =
1652 PrecompilesMap::from_static(Precompiles::new(PrecompileSpecId::from_spec_id(spec_id)));
1653 let chain_id = self.protocol_chain_id();
1654 let timestamp = self.evm_env.read().block_env.timestamp.saturating_to();
1655 apply_bsc_p256_precompile(&mut precompiles, chain_id, timestamp);
1656
1657 let mut precompiles_map = BTreeMap::<String, Address>::default();
1658 for address in precompiles.addresses() {
1659 let precompile = precompiles.get(address).expect("precompile address must resolve");
1660 precompiles_map.insert(precompile.precompile_id().name().to_string(), *address);
1661 }
1662
1663 precompiles_map.extend(self.networks.precompiles(Some(self.hardfork())));
1665
1666 if let Some(factory) = &self.precompile_factory {
1667 for (address, precompile) in factory.precompiles() {
1668 precompiles_map.insert(precompile.precompile_id().to_string(), address);
1669 }
1670 }
1671
1672 precompiles_map
1673 }
1674
1675 pub fn is_precompile(&self, address: &Address, block_env: &BlockEnv) -> bool {
1677 let mut evm_env = self.evm_env.read().clone();
1678 evm_env.block_env = block_env.clone();
1679 let mut precompiles = PrecompilesMap::from_static(Precompiles::new(
1680 PrecompileSpecId::from_spec_id(self.spec_id()),
1681 ));
1682 self.inject_precompiles(&mut precompiles, &evm_env);
1683 precompiles.get(address).is_some()
1684 }
1685
1686 pub fn system_contracts(&self) -> BTreeMap<SystemContract, Address> {
1688 let mut system_contracts = BTreeMap::<SystemContract, Address>::default();
1689
1690 let spec_id = self.spec_id();
1691
1692 if spec_id >= SpecId::CANCUN {
1693 system_contracts.extend(SystemContract::cancun());
1694 }
1695
1696 if spec_id >= SpecId::PRAGUE {
1697 system_contracts.extend(SystemContract::prague(None));
1698 }
1699
1700 system_contracts
1701 }
1702
1703 pub fn blob_params(&self) -> BlobParams {
1705 self.fees.blob_params()
1706 }
1707
1708 fn simulation_blob_params_at_timestamp(&self, timestamp: u64) -> BlobParams {
1709 let configured_hardfork = self.hardfork();
1710 if let FoundryHardfork::Ethereum(
1711 configured
1712 @ (EthereumHardfork::Osaka | EthereumHardfork::Bpo1 | EthereumHardfork::Bpo2),
1713 ) = configured_hardfork
1714 && let Some(hardfork) = FoundryHardfork::from_chain_and_timestamp(
1715 self.evm_env.read().cfg_env.chain_id,
1716 timestamp,
1717 )
1718 && let FoundryHardfork::Ethereum(
1719 scheduled @ (EthereumHardfork::Osaka
1720 | EthereumHardfork::Bpo1
1721 | EthereumHardfork::Bpo2),
1722 ) = hardfork
1723 {
1724 let hardfork = match (configured, scheduled) {
1725 (EthereumHardfork::Bpo2, _) | (_, EthereumHardfork::Bpo2) => EthereumHardfork::Bpo2,
1726 (EthereumHardfork::Bpo1, _) | (_, EthereumHardfork::Bpo1) => EthereumHardfork::Bpo1,
1727 _ => EthereumHardfork::Osaka,
1728 };
1729 return get_blob_params_by_hardfork(hardfork.into());
1730 }
1731 get_blob_params_by_hardfork(configured_hardfork)
1732 }
1733
1734 #[cfg(feature = "optimism")]
1735 fn is_optimism_jovian_at_header<H: BlockHeader>(
1736 &self,
1737 header: &H,
1738 decoded: Option<bool>,
1739 ) -> bool {
1740 if !self.is_optimism() {
1741 return false;
1742 }
1743 if let Some(jovian) = decoded {
1744 return jovian;
1745 }
1746 if !header.extra_data().is_empty() {
1747 return false;
1748 }
1749 let hardfork = if self.get_fork().is_some() {
1750 NetworkVariant::Optimism
1751 .historical_hardfork(self.protocol_chain_id(), header.timestamp())
1752 .unwrap_or_else(|| self.hardfork())
1753 } else {
1754 self.hardfork()
1755 };
1756 OpHardfork::from(hardfork) >= OpHardfork::Jovian
1757 }
1758
1759 #[cfg(not(feature = "optimism"))]
1760 const fn is_optimism_jovian_at_header<H: BlockHeader>(
1761 &self,
1762 _header: &H,
1763 _decoded: Option<bool>,
1764 ) -> bool {
1765 false
1766 }
1767
1768 pub fn ensure_eip1559_active(&self) -> Result<(), BlockchainError> {
1770 if self.is_eip1559() {
1771 return Ok(());
1772 }
1773 Err(BlockchainError::EIP1559TransactionUnsupportedAtHardfork)
1774 }
1775
1776 pub fn ensure_eip2930_active(&self) -> Result<(), BlockchainError> {
1778 if self.is_eip2930() {
1779 return Ok(());
1780 }
1781 Err(BlockchainError::EIP2930TransactionUnsupportedAtHardfork)
1782 }
1783
1784 pub fn ensure_eip4844_active(&self) -> Result<(), BlockchainError> {
1785 if self.is_eip4844() {
1786 return Ok(());
1787 }
1788 Err(BlockchainError::EIP4844TransactionUnsupportedAtHardfork)
1789 }
1790
1791 pub fn ensure_eip7702_active(&self) -> Result<(), BlockchainError> {
1792 if self.is_eip7702() {
1793 return Ok(());
1794 }
1795 Err(BlockchainError::EIP7702TransactionUnsupportedAtHardfork)
1796 }
1797
1798 #[cfg(any(feature = "base", feature = "optimism"))]
1800 pub const fn ensure_deposits_active(&self) -> Result<(), BlockchainError> {
1801 #[cfg(feature = "base")]
1802 if self.is_base() {
1803 return Ok(());
1804 }
1805 #[cfg(feature = "optimism")]
1806 if self.is_optimism() {
1807 return Ok(());
1808 }
1809 Err(BlockchainError::DepositTransactionUnsupported)
1810 }
1811
1812 #[cfg(feature = "base")]
1814 pub fn ensure_base_eip8130_active(&self) -> Result<(), BlockchainError> {
1815 let timestamp = self.evm_env.read().block_env.timestamp.saturating_to();
1816 self.ensure_base_eip8130_active_at(timestamp)
1817 }
1818
1819 #[cfg(feature = "base")]
1821 pub fn ensure_base_eip8130_active_at(&self, timestamp: u64) -> Result<(), BlockchainError> {
1822 let upgrade = self.base_upgrade_at_timestamp(timestamp);
1823 if self.is_base() && upgrade >= BaseUpgrade::Zenith {
1824 return Ok(());
1825 }
1826 Err(BlockchainError::InvalidTransactionRequest(EIP8130_PRE_ZENITH_RPC_ERROR.to_string()))
1827 }
1828
1829 #[cfg(feature = "base")]
1831 pub fn ensure_base_eip8130_submission_active(&self) -> Result<(), BlockchainError> {
1832 if self.is_base() && self.base_upgrade() >= BaseUpgrade::Zenith {
1833 return Ok(());
1834 }
1835 Err(BlockchainError::Eip8130TransactionRejected(EIP8130_REJECTION_MSG.to_string()))
1836 }
1837
1838 pub const fn ensure_tempo_active(&self) -> Result<(), BlockchainError> {
1840 if self.is_tempo() {
1841 return Ok(());
1842 }
1843 Err(BlockchainError::TempoTransactionUnsupported)
1844 }
1845
1846 pub fn ensure_cip64_active(&self) -> Result<(), BlockchainError> {
1848 if !self.is_celo() {
1849 return Err(BlockchainError::InvalidTransactionRequest(
1850 "CIP-64 transactions require Celo mode (--celo)".into(),
1851 ));
1852 }
1853 self.ensure_eip1559_active()
1854 }
1855
1856 fn inspector_tx_config(&self) -> InspectorTxConfig {
1858 InspectorTxConfig {
1859 print_traces: self.print_traces,
1860 print_logs: self.print_logs,
1861 call_trace_decoder: self.call_trace_decoder(),
1862 }
1863 }
1864
1865 fn call_trace_decoder(&self) -> Arc<CallTraceDecoder> {
1867 let hardfork = Some(self.networks.executed_hardfork(self.hardfork()));
1868 let decoder = self.call_trace_decoder.read();
1869 if decoder.hardfork() == hardfork {
1870 return Arc::clone(&decoder);
1871 }
1872 drop(decoder);
1873
1874 let mut decoder = self.call_trace_decoder.write();
1875 let mut updated = decoder.as_ref().clone();
1876 updated.set_hardfork(hardfork);
1877 *decoder = Arc::new(updated);
1878 Arc::clone(&decoder)
1879 }
1880
1881 fn pool_tx_gas_config(&self, evm_env: &EvmEnv) -> PoolTxGasConfig {
1883 let spec_id = *evm_env.spec_id();
1884 let is_cancun = spec_id >= SpecId::CANCUN;
1885 let blob_params = self.blob_params();
1886 PoolTxGasConfig {
1887 disable_block_gas_limit: evm_env.cfg_env.disable_block_gas_limit,
1888 enforced_tx_gas_limit_cap: evm_env
1889 .cfg_env
1890 .tx_gas_limit_cap
1891 .is_none()
1892 .then(|| self.tx_gas_limit_cap(evm_env)),
1893 max_blob_gas_per_block: blob_params.max_blob_gas_per_block(),
1894 is_cancun,
1895 }
1896 }
1897
1898 #[cfg(feature = "monad")]
1899 fn monad_cfg_env(&self, evm_env: &EvmEnv) -> Option<monad_revm::MonadCfgEnv> {
1900 if !self.is_monad() {
1901 return None;
1902 }
1903
1904 let hardfork = monad_revm::MonadHardfork::from(self.hardfork());
1905 Some(monad_revm::MonadCfgEnv::from(evm_env.cfg_env.clone().with_spec_and_gas_params(
1906 hardfork,
1907 monad_revm::instructions::monad_gas_params(hardfork),
1908 )))
1909 }
1910
1911 fn tx_gas_limit_cap(&self, evm_env: &EvmEnv) -> u64 {
1912 #[cfg(feature = "monad")]
1913 if let Some(cfg) = self.monad_cfg_env(evm_env) {
1914 return cfg.tx_gas_limit_cap();
1915 }
1916 evm_env.cfg_env.tx_gas_limit_cap()
1917 }
1918
1919 pub(crate) fn fallback_tx_gas_limit(&self, evm_env: &EvmEnv) -> u64 {
1920 let block_gas_limit = evm_env.block_env.gas_limit;
1921 if evm_env.cfg_env.tx_gas_limit_cap.is_none() {
1922 block_gas_limit.min(self.tx_gas_limit_cap(evm_env))
1923 } else {
1924 block_gas_limit
1925 }
1926 }
1927
1928 fn max_initcode_size(&self, evm_env: &EvmEnv) -> usize {
1929 #[cfg(feature = "monad")]
1930 if let Some(cfg) = self.monad_cfg_env(evm_env) {
1931 return cfg.max_initcode_size();
1932 }
1933 evm_env.cfg_env.max_initcode_size()
1934 }
1935
1936 pub fn gas_limit(&self) -> u64 {
1938 self.evm_env.read().block_env.gas_limit
1939 }
1940
1941 pub fn set_gas_limit(&self, gas_limit: u64) {
1943 self.evm_env.write().block_env.gas_limit = gas_limit;
1944 }
1945
1946 pub fn base_fee(&self) -> u64 {
1948 self.fees.base_fee()
1949 }
1950
1951 pub const fn is_min_priority_fee_enforced(&self) -> bool {
1953 self.fees.is_min_priority_fee_enforced()
1954 }
1955
1956 pub fn excess_blob_gas_and_price(&self) -> Option<BlobExcessGasAndPrice> {
1957 self.fees.excess_blob_gas_and_price()
1958 }
1959
1960 pub fn set_base_fee(&self, basefee: u64) {
1962 self.fees.set_base_fee(basefee)
1963 }
1964
1965 pub fn set_gas_price(&self, price: u128) {
1967 self.fees.set_gas_price(price)
1968 }
1969
1970 pub fn elasticity(&self) -> f64 {
1971 self.fees.elasticity()
1972 }
1973
1974 pub fn total_difficulty(&self) -> U256 {
1979 self.blockchain.storage.read().total_difficulty
1980 }
1981
1982 pub async fn create_state_snapshot(&self) -> U256 {
1986 let num = self.best_number();
1987 let hash = self.best_hash();
1988 let id = self.db.write().await.snapshot_state();
1989 trace!(target: "backend", "creating snapshot {} at {}", id, num);
1990 self.active_state_snapshots.lock().insert(
1991 id,
1992 StateSnapshot {
1993 block_number: num,
1994 block_hash: hash,
1995 fees: self.fees.snapshot(),
1996 time: self.time.snapshot(),
1997 next_block: self.cheats.next_block_overrides(),
1998 },
1999 );
2000 id
2001 }
2002
2003 pub fn list_state_snapshots(&self) -> BTreeMap<U256, (u64, B256)> {
2004 self.active_state_snapshots
2005 .lock()
2006 .iter()
2007 .map(|(&id, snapshot)| (id, (snapshot.block_number, snapshot.block_hash)))
2008 .collect()
2009 }
2010
2011 fn evm_block_number(&self, rpc_number: u64) -> U256 {
2013 self.get_fork()
2014 .map_or_else(|| U256::from(rpc_number), |fork| fork.evm_block_number(rpc_number))
2015 }
2016
2017 fn block_env_from_header(&self, header: &impl BlockHeader) -> BlockEnv {
2019 let mut block = block_env_from_header::<BlockEnv>(header);
2020 block.number = self.evm_block_number(header.number());
2021 if let Some(excess_blob_gas) = header.excess_blob_gas() {
2022 block.set_blob_excess_gas_and_price(
2023 excess_blob_gas,
2024 self.blob_params().update_fraction as u64,
2025 );
2026 }
2027 block
2028 }
2029
2030 fn next_evm_env(
2032 &self,
2033 db: &dyn revm::DatabaseRef<Error = DatabaseError>,
2034 ) -> Result<EvmEnv, DatabaseError> {
2035 let mut evm_env = self.evm_env.read().clone();
2036 evm_env.block_env.number = evm_env.block_env.number.saturating_add(U256::ONE);
2038 evm_env.block_env.basefee = self.base_fee();
2039 evm_env.block_env.blob_excess_gas_and_price = self.excess_blob_gas_and_price();
2040 let pending =
2041 self.prepare_block_timestamp(db, evm_env.block_env.timestamp.saturating_to())?;
2042 evm_env.block_env.timestamp = U256::from(pending.timestamp);
2043 Ok(evm_env)
2044 }
2045
2046 fn tx_replay_evm_env(&self, block: &Block) -> (EvmEnv, FoundryHardfork) {
2048 let mut evm_env = self.evm_env.read().clone();
2049 evm_env.block_env = self.block_env_from_header(&block.header);
2050 let hardfork = self.hardfork();
2051 #[cfg(feature = "monad")]
2052 let hardfork = if self.is_monad() {
2053 let block_hash = block.header.hash_slow();
2054 let fallback = crate::eth::backend::db::MonadBlockReplayProfile {
2055 execution_chain_id: evm_env.cfg_env.chain_id,
2056 hardfork: self.monad_hardfork(),
2057 };
2058 let profile = self
2059 .blockchain
2060 .storage
2061 .read()
2062 .monad_block_replay_profiles
2063 .get(&block_hash)
2064 .copied()
2065 .unwrap_or(fallback);
2066 evm_env.cfg_env.chain_id = profile.execution_chain_id;
2067 evm_env.cfg_env.spec = profile.hardfork.into();
2068 profile.hardfork.into()
2069 } else {
2070 hardfork
2071 };
2072 apply_chain_specific_tx_replay_env_changes_for_chain(
2073 &mut evm_env,
2074 self.protocol_chain_id(),
2075 );
2076 (evm_env, hardfork)
2077 }
2078
2079 fn prepare_block_replay<'a>(
2085 &self,
2086 block: &Block,
2087 parent_state: &'a StateDb,
2088 ) -> Result<(CacheDB<&'a StateDb>, EvmEnv, FoundryHardfork), BlockchainError> {
2089 self.prepare_block_replay_with_db(block, parent_state)
2090 }
2091
2092 fn prepare_block_replay_with_db<DB>(
2094 &self,
2095 block: &Block,
2096 db: DB,
2097 ) -> Result<(CacheDB<DB>, EvmEnv, FoundryHardfork), BlockchainError>
2098 where
2099 DB: DatabaseRef<Error = DatabaseError> + Debug,
2100 {
2101 let (evm_env, hardfork) = self.tx_replay_evm_env(block);
2102 let mut cache_db = AnvilCacheDB::new(db, *evm_env.spec_id());
2103 let inspector_tx_config = self.inspector_tx_config();
2104 let gas_config = self.pool_tx_gas_config(&evm_env);
2105
2106 self.execute_with_block_executor(
2107 &mut cache_db,
2108 &evm_env,
2109 block.header.parent_hash,
2110 hardfork,
2111 block.header.parent_beacon_block_root,
2112 BlockExecutionKind::TransactionPrefix,
2113 &[],
2114 &gas_config,
2115 &inspector_tx_config,
2116 &|_, _| Ok(()),
2117 )?;
2118
2119 Ok((cache_db.0, evm_env, hardfork))
2120 }
2121
2122 fn replay_mined_transaction_prefix<DB>(
2124 &self,
2125 cache_db: &mut CacheDB<DB>,
2126 evm_env: &EvmEnv,
2127 hardfork: FoundryHardfork,
2128 block: &Block,
2129 end: usize,
2130 ) -> Result<(), BlockchainError>
2131 where
2132 DB: DatabaseRef<Error = DatabaseError> + Debug,
2133 {
2134 let monad_context = self.active_monad_context_for_mined_block(block)?;
2135 for (index, transaction) in block.body.transactions[..end].iter().enumerate() {
2136 let pending = self.pending_mined_transaction(transaction.clone())?;
2137 let mut inspector = AnvilInspector::default();
2138 let transaction_context = monad_execution_context_at(monad_context.as_ref(), index);
2139 let (result, _) = self.replay_envelope_with_inspector_ref_and_context(
2140 cache_db,
2141 evm_env,
2142 &mut inspector,
2143 &pending,
2144 EnvelopeExecution::replay(transaction_context, hardfork),
2145 )?;
2146 cache_db.commit(result.state);
2147 }
2148 Ok(())
2149 }
2150
2151 fn build_inspector(&self) -> AnvilInspector {
2153 let mut inspector = AnvilInspector::default();
2154
2155 if self.print_logs {
2156 inspector = inspector.with_log_collector();
2157 }
2158 if self.print_traces {
2159 inspector = inspector.with_trace_printer();
2160 }
2161
2162 inspector
2163 }
2164
2165 fn build_mining_inspector(&self) -> AnvilInspector {
2167 let mut inspector = AnvilInspector::default().with_tracing();
2168 if self.print_logs {
2169 inspector = inspector.with_log_collector();
2170 }
2171 if self.print_traces {
2172 inspector = inspector.with_trace_printer();
2173 }
2174 inspector
2175 }
2176
2177 pub fn new_block_notifications(&self) -> ChainNotifications {
2180 let (tx, rx) = unbounded();
2181 self.new_block_listeners.lock().push(tx);
2182 trace!(target: "backed", "added new block listener");
2183 rx
2184 }
2185
2186 pub fn new_block_listeners_count(&self) -> usize {
2189 self.new_block_listeners.lock().len()
2190 }
2191
2192 fn notify_on_new_block(&self, header: Header, hash: B256) {
2194 self.new_block_listeners.lock().retain(|tx| !tx.is_closed());
2197
2198 let notification =
2199 ChainNotification::Block(NewBlockNotification { hash, header: Arc::new(header) });
2200
2201 self.new_block_listeners
2202 .lock()
2203 .retain(|tx| tx.unbounded_send(notification.clone()).is_ok());
2204 }
2205
2206 fn notify_on_removed_logs(&self, logs: Vec<Log>) {
2209 self.new_block_listeners.lock().retain(|tx| !tx.is_closed());
2212
2213 let notification = ChainNotification::RemovedLogs(Arc::new(logs));
2214
2215 self.new_block_listeners
2216 .lock()
2217 .retain(|tx| tx.unbounded_send(notification.clone()).is_ok());
2218 }
2219
2220 pub fn convert_block_number(&self, block: Option<BlockNumber>) -> u64 {
2222 let current = self.best_number();
2223 let genesis = self.genesis_number();
2226 match block.unwrap_or(BlockNumber::Latest) {
2227 BlockNumber::Latest | BlockNumber::Pending => current,
2228 BlockNumber::Earliest => genesis,
2229 BlockNumber::Number(num) => num,
2230 BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch).max(genesis),
2231 BlockNumber::Finalized => {
2232 current.saturating_sub(self.slots_in_an_epoch * 2).max(genesis)
2233 }
2234 }
2235 }
2236
2237 pub(crate) fn block_hash_by_number(&self, number: u64) -> Option<B256> {
2239 self.blockchain.hash(BlockId::number(number), self.slots_in_an_epoch)
2240 }
2241
2242 pub(crate) fn get_block_with_hash(&self, id: impl Into<BlockId>) -> Option<(Block, B256)> {
2244 let hash = self.blockchain.hash(id.into(), self.slots_in_an_epoch)?;
2245 let block = self.get_block_by_hash(hash)?;
2246 Some((block, hash))
2247 }
2248
2249 pub fn get_block(&self, id: impl Into<BlockId>) -> Option<Block> {
2250 self.get_block_with_hash(id).map(|(block, _)| block)
2251 }
2252
2253 pub fn get_block_by_hash(&self, hash: B256) -> Option<Block> {
2254 self.blockchain.get_block_by_hash(&hash)
2255 }
2256
2257 pub(crate) async fn fee_history_next_fees(&self, highest: u64) -> Option<(u128, u128)> {
2263 let _mining_guard = self.mining.lock().await;
2264 let next_number = highest.checked_add(1)?;
2265 if let Some(block) = self.get_block(next_number) {
2266 Some((
2267 block.header.base_fee_per_gas.unwrap_or_default() as u128,
2268 block.header.blob_fee(self.blob_params()).unwrap_or_default(),
2269 ))
2270 } else if highest == self.best_number() {
2271 Some((self.fees().base_fee() as u128, self.fees().base_fee_per_blob_gas()))
2272 } else {
2273 None
2274 }
2275 }
2276
2277 pub(crate) fn mined_parity_trace_transaction(
2279 &self,
2280 hash: B256,
2281 ) -> Option<Vec<LocalizedTransactionTrace>> {
2282 self.blockchain.storage.read().transactions.get(&hash).map(|tx| tx.parity_traces())
2283 }
2284
2285 pub(crate) fn mined_parity_trace_block(
2287 &self,
2288 block: u64,
2289 ) -> Option<Vec<LocalizedTransactionTrace>> {
2290 let block = self.get_block(block)?;
2291 let mut traces = vec![];
2292 let storage = self.blockchain.storage.read();
2293 for tx in block.body.transactions {
2294 if let Some(mined_tx) = storage.transactions.get(&tx.hash()) {
2295 traces.extend(mined_tx.parity_traces());
2296 }
2297 }
2298 Some(traces)
2299 }
2300
2301 pub(crate) fn mined_transaction(&self, hash: B256) -> Option<MinedTransaction<N>> {
2303 self.blockchain.storage.read().transactions.get(&hash).cloned()
2304 }
2305
2306 pub async fn impersonate_signature(
2308 &self,
2309 signature: Bytes,
2310 address: Address,
2311 ) -> Result<(), BlockchainError> {
2312 self.cheats.add_recover_override(signature, address);
2313 Ok(())
2314 }
2315
2316 pub async fn debug_code_by_hash(
2318 &self,
2319 code_hash: B256,
2320 block_id: Option<BlockId>,
2321 ) -> Result<Option<Bytes>, BlockchainError> {
2322 if let Ok(code) = self.db.read().await.code_by_hash_ref(code_hash) {
2323 return Ok(Some(code.original_bytes()));
2324 }
2325 if let Some(fork) = self.get_fork() {
2326 return Ok(fork.debug_code_by_hash(code_hash, block_id).await?);
2327 }
2328
2329 Ok(None)
2330 }
2331
2332 pub async fn debug_db_get(&self, key: String) -> Result<Option<Bytes>, BlockchainError> {
2340 let key_bytes = if key.starts_with("0x") {
2341 hex::decode(&key)
2342 .map_err(|_| BlockchainError::Message("Invalid hex key".to_string()))?
2343 } else {
2344 key.into_bytes()
2345 };
2346
2347 if key_bytes.len() != 33 {
2349 return Err(BlockchainError::Message(format!(
2350 "Invalid key length: expected 33 bytes, got {}",
2351 key_bytes.len()
2352 )));
2353 }
2354
2355 if key_bytes[0] != 0x63 {
2357 return Err(BlockchainError::Message(
2358 "Key prefix must be 0x63 for code hash lookups".to_string(),
2359 ));
2360 }
2361
2362 let code_hash = B256::from_slice(&key_bytes[1..33]);
2363
2364 self.debug_code_by_hash(code_hash, None).await
2366 }
2367
2368 fn mined_block_by_hash(&self, hash: B256) -> Option<AnyRpcBlock> {
2369 let block = self.blockchain.get_block_by_hash(&hash)?;
2370 Some(self.convert_block_with_hash(block, Some(hash)))
2371 }
2372
2373 pub(crate) async fn mined_transactions_by_block_number(
2374 &self,
2375 number: BlockNumber,
2376 ) -> Option<Vec<AnyRpcTransaction>> {
2377 if let Some(block) = self.get_block(number) {
2378 return self.mined_transactions_in_block(&block);
2379 }
2380 None
2381 }
2382
2383 pub(crate) fn mined_transactions_in_block(
2385 &self,
2386 block: &Block,
2387 ) -> Option<Vec<AnyRpcTransaction>> {
2388 let mut transactions = Vec::with_capacity(block.body.transactions.len());
2389 let base_fee = block.header.base_fee_per_gas();
2390 let storage = self.blockchain.storage.read();
2391 for hash in block.body.transactions.iter().map(|tx| tx.hash()) {
2392 let info = storage.transactions.get(&hash)?.info.clone();
2393 let tx = block.body.transactions.get(info.transaction_index as usize)?.clone();
2394
2395 let tx = transaction_build(Some(hash), tx, Some(block), Some(info), base_fee);
2396 transactions.push(tx);
2397 }
2398 Some(transactions)
2399 }
2400
2401 pub fn mined_block_by_number(&self, number: BlockNumber) -> Option<AnyRpcBlock> {
2402 let (block, hash) = self.get_block_with_hash(number)?;
2403 let mut block = self.convert_block_with_hash(block, Some(hash));
2404 block.transactions.convert_to_hashes();
2405 Some(block)
2406 }
2407
2408 pub fn get_full_block(&self, id: impl Into<BlockId>) -> Option<AnyRpcBlock> {
2409 let (block, hash) = self.get_block_with_hash(id)?;
2410 let transactions = self.mined_transactions_in_block(&block)?;
2411 let mut block = self.convert_block_with_hash(block, Some(hash));
2412 block.inner.transactions = BlockTransactions::Full(transactions);
2413 Some(block)
2414 }
2415
2416 pub fn convert_block(&self, block: Block) -> AnyRpcBlock {
2418 self.convert_block_with_hash(block, None)
2419 }
2420
2421 pub fn convert_block_with_hash(&self, block: Block, known_hash: Option<B256>) -> AnyRpcBlock {
2424 let transactions = block.body.transactions.iter().map(|tx| tx.hash()).collect();
2425 let block = canonical_block(block);
2426 let size = U256::from(block.length() as u32);
2427 let header = block.header;
2428
2429 let hash = known_hash.unwrap_or_else(|| header.hash_slow());
2430 let number = header.number();
2431 let withdrawals_root = header.withdrawals_root();
2432 let tempo_fields = header
2433 .as_tempo()
2434 .map(|header| {
2435 (
2436 header.timestamp_millis(),
2437 header.general_gas_limit,
2438 header.shared_gas_limit,
2439 header.timestamp_millis_part,
2440 )
2441 })
2442 .or_else(|| {
2443 self.is_tempo()
2444 .then(|| (header.timestamp().saturating_mul(1000), header.gas_limit(), 0, 0))
2445 });
2446
2447 let block = AlloyBlock {
2448 header: AlloyHeader {
2449 inner: AnyHeader::from(header.into_inner()),
2450 hash,
2451 total_difficulty: Some(self.total_difficulty()),
2452 size: Some(size),
2453 },
2454 transactions: alloy_rpc_types::BlockTransactions::Hashes(transactions),
2455 uncles: vec![],
2456 withdrawals: withdrawals_root.map(|_| Default::default()),
2457 };
2458
2459 let mut block = WithOtherFields::new(block);
2460
2461 if is_arbitrum(self.protocol_chain_id()) {
2463 block.other.insert(
2465 "l1BlockNumber".to_string(),
2466 serde_json::json!(self.evm_block_number(number)),
2467 );
2468 }
2469
2470 if let Some((
2471 timestamp_millis,
2472 general_gas_limit,
2473 shared_gas_limit,
2474 timestamp_millis_part,
2475 )) = tempo_fields
2476 {
2477 block.other.insert(
2478 "timestampMillis".to_string(),
2479 serde_json::Value::String(format!("0x{timestamp_millis:x}")),
2480 );
2481 block.other.insert(
2482 "mainBlockGeneralGasLimit".to_string(),
2483 serde_json::Value::String(format!("0x{general_gas_limit:x}")),
2484 );
2485 block.other.insert(
2486 "sharedGasLimit".to_string(),
2487 serde_json::Value::String(format!("0x{shared_gas_limit:x}")),
2488 );
2489 block.other.insert(
2490 "timestampMillisPart".to_string(),
2491 serde_json::Value::String(format!("0x{timestamp_millis_part:x}")),
2492 );
2493 }
2494
2495 AnyRpcBlock::from(block)
2496 }
2497
2498 pub async fn block_by_hash(&self, hash: B256) -> Result<Option<AnyRpcBlock>, BlockchainError> {
2499 trace!(target: "backend", "get block by hash {:?}", hash);
2500 if let tx @ Some(_) = self.mined_block_by_hash(hash) {
2501 return Ok(tx);
2502 }
2503
2504 if let Some(fork) = self.get_fork() {
2505 return Ok(fork.block_by_hash(hash).await?);
2506 }
2507
2508 Ok(None)
2509 }
2510
2511 pub async fn block_by_hash_full(
2512 &self,
2513 hash: B256,
2514 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
2515 trace!(target: "backend", "get block by hash {:?}", hash);
2516 if let tx @ Some(_) = self.get_full_block(hash) {
2517 return Ok(tx);
2518 }
2519
2520 if let Some(fork) = self.get_fork() {
2521 return Ok(fork.block_by_hash_full(hash).await?);
2522 }
2523
2524 Ok(None)
2525 }
2526
2527 pub async fn block_by_number(
2528 &self,
2529 number: BlockNumber,
2530 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
2531 trace!(target: "backend", "get block by number {:?}", number);
2532 if let tx @ Some(_) = self.mined_block_by_number(number) {
2533 return Ok(tx);
2534 }
2535
2536 if let Some(fork) = self.get_fork() {
2537 let number = self.convert_block_number(Some(number));
2538 if fork.predates_fork_inclusive(number) {
2539 return Ok(fork.block_by_number(number).await?);
2540 }
2541 }
2542
2543 Ok(None)
2544 }
2545
2546 pub async fn block_by_number_full(
2547 &self,
2548 number: BlockNumber,
2549 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
2550 trace!(target: "backend", "get block by number {:?}", number);
2551 if let tx @ Some(_) = self.get_full_block(number) {
2552 return Ok(tx);
2553 }
2554
2555 if let Some(fork) = self.get_fork() {
2556 let number = self.convert_block_number(Some(number));
2557 if fork.predates_fork_inclusive(number) {
2558 return Ok(fork.block_by_number_full(number).await?);
2559 }
2560 }
2561
2562 Ok(None)
2563 }
2564
2565 pub async fn ensure_block_number<T: Into<BlockId>>(
2571 &self,
2572 block_id: Option<T>,
2573 ) -> Result<u64, BlockchainError> {
2574 let current = self.best_number();
2575 let requested = match block_id.map(Into::into).unwrap_or(BlockId::latest()) {
2576 BlockId::Hash(hash) => {
2577 self.block_by_hash(hash.block_hash)
2578 .await?
2579 .ok_or(BlockchainError::BlockNotFound)?
2580 .header
2581 .number
2582 }
2583 BlockId::Number(num) => self.convert_block_number(Some(num)),
2584 };
2585
2586 if requested > current {
2587 Err(BlockchainError::BlockOutOfRange(current, requested))
2588 } else {
2589 Ok(requested)
2590 }
2591 }
2592
2593 #[cfg(feature = "base")]
2595 pub async fn block_request_timestamp(
2596 &self,
2597 block_request: &BlockRequest<FoundryTxEnvelope>,
2598 ) -> Result<u64, BlockchainError> {
2599 match block_request {
2600 BlockRequest::Pending(_) => Ok(self.time.current_call_timestamp()),
2601 BlockRequest::Number(number) => self
2602 .block_by_number(BlockNumber::Number(*number))
2603 .await?
2604 .map(|block| block.header.timestamp())
2605 .ok_or(BlockchainError::BlockNotFound),
2606 }
2607 }
2608
2609 fn inject_precompiles(&self, precompiles: &mut PrecompilesMap, evm_env: &EvmEnv) {
2618 self.inject_configured_precompiles(precompiles, evm_env);
2619
2620 if let Some(block_number) = self.arbitrum_block_number(evm_env) {
2621 self.inject_arbitrum_precompile_at_block(precompiles, block_number);
2622 }
2623 }
2624
2625 fn inject_configured_precompiles(&self, precompiles: &mut PrecompilesMap, evm_env: &EvmEnv) {
2626 self.networks.inject_precompiles(precompiles);
2627 apply_bsc_p256_precompile(
2628 precompiles,
2629 self.protocol_chain_id(),
2630 evm_env.block_env.timestamp.saturating_to(),
2631 );
2632
2633 if let Some(factory) = &self.precompile_factory {
2634 factory.install(precompiles);
2635 }
2636
2637 let cheats = Arc::new(self.cheats.clone());
2638 if cheats.has_recover_overrides() {
2639 let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats));
2640 precompiles.apply_precompile(&EC_RECOVER, move |_| {
2641 Some(DynPrecompile::new_stateful(
2642 cheat_ecrecover.precompile_id().clone(),
2643 move |input| cheat_ecrecover.call(input),
2644 ))
2645 });
2646 }
2647 }
2648
2649 fn inject_arbitrum_precompile_at_block(
2650 &self,
2651 precompiles: &mut PrecompilesMap,
2652 block_number: u64,
2653 ) {
2654 precompiles.apply_precompile(&arbitrum::ARB_SYS_ADDRESS, move |_| {
2655 Some(arbitrum::arb_sys_precompile(block_number))
2656 });
2657 }
2658
2659 fn simulation_precompile_overrides(
2660 &self,
2661 state_overrides: Option<&StateOverride>,
2662 evm_env: &EvmEnv,
2663 ) -> Result<SimulationPrecompileOverrides, BlockchainError> {
2664 let mut moves = state_overrides
2665 .into_iter()
2666 .flatten()
2667 .filter_map(|(source, account)| {
2668 account.move_precompile_to.map(|destination| (*source, destination))
2669 })
2670 .collect::<Vec<_>>();
2671 moves.sort_unstable();
2672 if moves.is_empty() {
2673 return Ok(SimulationPrecompileOverrides::default());
2674 }
2675 #[cfg(feature = "base")]
2676 if self.is_base() {
2677 return Err(simulate_rpc_error(
2678 -32000,
2679 "precompile moves are not supported on this network",
2680 ));
2681 }
2682 if self.is_optimism() || self.is_tempo() || self.is_monad() {
2683 return Err(simulate_rpc_error(
2684 -32000,
2685 "precompile moves are not supported on this network",
2686 ));
2687 }
2688
2689 let mut precompiles = PrecompilesMap::from_static(Precompiles::new(
2690 PrecompileSpecId::from_spec_id(*evm_env.spec_id()),
2691 ));
2692 self.inject_precompiles(&mut precompiles, evm_env);
2693 let precompile_addresses = precompiles.addresses().copied().collect::<HashSet<_>>();
2694
2695 for (source, _) in &moves {
2698 if !precompile_addresses.contains(source) {
2699 return Err(simulate_rpc_error(
2700 -32000,
2701 format!("account {source} is not a precompile"),
2702 ));
2703 }
2704 }
2705 for (source, destination) in &moves {
2706 if source == destination {
2707 return Err(simulate_rpc_error(
2708 -38022,
2709 format!("cannot move precompile {source} to itself"),
2710 ));
2711 }
2712 }
2713 let mut destinations = Vec::with_capacity(moves.len());
2714 for (_, destination) in &moves {
2715 if destinations.contains(destination) {
2716 return Err(simulate_rpc_error(
2717 -38023,
2718 format!("multiple precompiles moved to {destination}"),
2719 ));
2720 }
2721 destinations.push(*destination);
2722 }
2723
2724 Ok(SimulationPrecompileOverrides { moves })
2725 }
2726
2727 fn apply_simulation_precompile_overrides(
2728 &self,
2729 precompiles: &mut PrecompilesMap,
2730 overrides: &SimulationPrecompileOverrides,
2731 ) -> Result<alloy_primitives::map::AddressSet, BlockchainError> {
2732 let warm_addresses = precompiles.addresses().copied().collect();
2733 precompiles.move_precompiles(overrides.moves.iter().copied()).map_err(
2734 |MovePrecompileError::NotAPrecompile(address)| {
2735 simulate_rpc_error(-32000, format!("account {address} is not a precompile"))
2736 },
2737 )?;
2738
2739 let moved_sources =
2741 Arc::new(overrides.moves.iter().map(|(source, _)| *source).collect::<HashSet<_>>());
2742 precompiles.map_precompile_lookup(move |address, previous| {
2743 if moved_sources.contains(address) {
2744 None
2745 } else {
2746 previous.and_then(|lookup| lookup.lookup(address))
2747 }
2748 });
2749 Ok(warm_addresses)
2750 }
2751
2752 fn inject_tempo_precompiles<DB, I>(
2753 &self,
2754 evm: &mut tempo_evm::evm::TempoEvm<DB, I>,
2755 evm_env: &EvmEnv,
2756 ) where
2757 DB: Database,
2758 I: Inspector<TempoContext<DB>>,
2759 {
2760 self.inject_configured_precompiles(evm.precompiles_mut(), evm_env);
2761 let cfg = evm.ctx().cfg.clone();
2763 let non_creditable_slots = evm.non_creditable_slots();
2764 extend_tempo_precompiles(
2765 evm.precompiles_mut(),
2766 &cfg,
2767 StorageActions::disabled(),
2768 non_creditable_slots,
2769 );
2770 }
2771
2772 fn transact_eth_with_inspector_ref<'db, I, DB>(
2777 &self,
2778 db: &'db DB,
2779 evm_env: &EvmEnv,
2780 inspector: &mut I,
2781 tx_env: TxEnv,
2782 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
2783 where
2784 DB: DatabaseRef + ?Sized,
2785 I: Inspector<EthEvmContext<WrapDatabaseRef<&'db DB>>>,
2786 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
2787 {
2788 self.transact_eth_with_inspector_ref_and_precompile_overrides(
2789 db,
2790 evm_env,
2791 inspector,
2792 tx_env,
2793 &SimulationPrecompileOverrides::default(),
2794 )
2795 }
2796
2797 fn transact_eth_with_inspector_ref_and_precompile_overrides<'db, I, DB>(
2798 &self,
2799 db: &'db DB,
2800 evm_env: &EvmEnv,
2801 inspector: &mut I,
2802 tx_env: TxEnv,
2803 overrides: &SimulationPrecompileOverrides,
2804 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
2805 where
2806 DB: DatabaseRef + ?Sized,
2807 I: Inspector<EthEvmContext<WrapDatabaseRef<&'db DB>>>,
2808 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
2809 {
2810 let mut evm = self.prepare_eth_evm(db, evm_env, inspector, overrides)?;
2811 Ok(evm.transact(tx_env)?)
2812 }
2813
2814 fn transact_eth_simulation_with_inspector_ref<'db, I, DB>(
2815 &self,
2816 db: &'db DB,
2817 evm_env: &EvmEnv,
2818 inspector: &mut I,
2819 tx_env: TxEnv,
2820 overrides: &SimulationPrecompileOverrides,
2821 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
2822 where
2823 DB: DatabaseRef + ?Sized,
2824 I: Inspector<EthEvmContext<WrapDatabaseRef<&'db DB>>>,
2825 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
2826 {
2827 let evm = self.prepare_eth_evm(db, evm_env, inspector, overrides)?;
2828 let mut evm = evm.into_inner();
2829 ContextSetters::set_tx(evm.ctx_mut(), tx_env);
2830 let mut handler = SimulationHandler::<
2831 _,
2832 revm::context::result::EVMError<DatabaseError>,
2833 EthFrame<EthInterpreter>,
2834 >::default();
2835 let result = handler.inspect_run(&mut evm)?;
2836 let state = evm.ctx_mut().journal_mut().finalize();
2837 Ok(ResultAndState { result, state })
2838 }
2839
2840 fn prepare_eth_evm<'db, 'inspector, I, DB>(
2842 &self,
2843 db: &'db DB,
2844 evm_env: &EvmEnv,
2845 inspector: &'inspector mut I,
2846 overrides: &SimulationPrecompileOverrides,
2847 ) -> Result<EthEvm<WrapDatabaseRef<&'db DB>, &'inspector mut I, PrecompilesMap>, BlockchainError>
2848 where
2849 DB: DatabaseRef + ?Sized,
2850 I: Inspector<EthEvmContext<WrapDatabaseRef<&'db DB>>>,
2851 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
2852 {
2853 let mut evm = EthEvmFactory::default().create_evm_with_inspector(
2854 WrapDatabaseRef(db),
2855 evm_env.clone(),
2856 inspector,
2857 );
2858 self.inject_precompiles(evm.precompiles_mut(), evm_env);
2859 if !overrides.moves.is_empty() {
2860 let warm_addresses =
2861 self.apply_simulation_precompile_overrides(evm.precompiles_mut(), overrides)?;
2862 evm.ctx_mut().journal_mut().warm_precompiles(&warm_addresses);
2864 }
2865 Ok(evm)
2866 }
2867
2868 fn transact_envelope_with_inspector_ref_and_context<'db, I, DB>(
2872 &self,
2873 db: &'db DB,
2874 evm_env: &EvmEnv,
2875 inspector: &mut I,
2876 pending: &PendingTransaction<FoundryTxEnvelope>,
2877 #[cfg_attr(not(feature = "monad"), allow(unused_variables))] monad_context: Option<
2878 MonadExecutionContext<'_>,
2879 >,
2880 ) -> Result<(ResultAndState<HaltReason>, TxEnv), BlockchainError>
2881 where
2882 DB: DatabaseRef + ?Sized,
2883 I: BackendInspector<WrapDatabaseRef<&'db DB>>,
2884 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
2885 {
2886 self.transact_envelope_with_inspector_ref_and_context_kind(
2887 db,
2888 evm_env,
2889 inspector,
2890 pending,
2891 EnvelopeExecution::transaction(monad_context, self.hardfork()),
2892 )
2893 }
2894
2895 fn replay_envelope_with_inspector_ref_and_context<'db, I, DB>(
2897 &self,
2898 db: &'db DB,
2899 evm_env: &EvmEnv,
2900 inspector: &mut I,
2901 pending: &PendingTransaction<FoundryTxEnvelope>,
2902 execution: EnvelopeExecution<'_>,
2903 ) -> Result<(ResultAndState<HaltReason>, TxEnv), BlockchainError>
2904 where
2905 DB: DatabaseRef + ?Sized,
2906 I: BackendInspector<WrapDatabaseRef<&'db DB>>,
2907 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
2908 {
2909 self.transact_envelope_with_inspector_ref_and_context_kind(
2910 db, evm_env, inspector, pending, execution,
2911 )
2912 }
2913
2914 fn transact_envelope_with_inspector_ref_and_context_kind<'db, I, DB>(
2915 &self,
2916 db: &'db DB,
2917 evm_env: &EvmEnv,
2918 inspector: &mut I,
2919 pending: &PendingTransaction<FoundryTxEnvelope>,
2920 #[cfg_attr(not(feature = "monad"), allow(unused_variables))] execution: EnvelopeExecution<
2921 '_,
2922 >,
2923 ) -> Result<(ResultAndState<HaltReason>, TxEnv), BlockchainError>
2924 where
2925 DB: DatabaseRef + ?Sized,
2926 I: BackendInspector<WrapDatabaseRef<&'db DB>>,
2927 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
2928 {
2929 #[cfg(any(feature = "base", feature = "optimism"))]
2930 let (tx, sender) = (pending.transaction.as_ref(), *pending.sender());
2931 #[cfg(feature = "base")]
2932 if self.is_base() {
2933 let base_tx: BaseTransaction<TxEnv> =
2934 FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into());
2935 let base = base_tx.base.clone();
2936 let result = self.transact_base_with_inspector_ref(db, evm_env, inspector, base_tx)?;
2937 return Ok((result, base));
2938 }
2939 if self.is_tempo() {
2940 let tx_env: TempoTxEnv = build_tx_env_for_pending(pending, self.cheats());
2941 let base = tx_env.inner.clone();
2942 let result = self.transact_tempo_with_inspector_ref(db, evm_env, inspector, tx_env)?;
2943 return Ok((result, base));
2944 }
2945 #[cfg(feature = "optimism")]
2946 if self.is_optimism() {
2947 let op_tx: OpTransaction<TxEnv> =
2948 FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into());
2949 let base = op_tx.base.clone();
2950 let spec = self.hardfork().into();
2951 let result =
2952 self.transact_op_with_inspector_ref(db, evm_env, inspector, op_tx, spec)?;
2953 return Ok((result, base));
2954 }
2955 let tx_env: TxEnv = build_tx_env_for_pending(pending, self.cheats());
2956 let base = tx_env.clone();
2957 #[cfg(feature = "monad")]
2958 let result = if self.is_monad() {
2959 let context = monad::resolve_execution_context(execution.monad_context, &tx_env);
2960 self.transact_monad_with_inspector_ref(
2961 db,
2962 evm_env,
2963 inspector,
2964 tx_env,
2965 monad::PreparedExecution {
2966 context,
2967 kind: execution.kind,
2968 hardfork: monad_revm::MonadHardfork::from(execution.hardfork),
2969 },
2970 )?
2971 } else {
2972 self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env)?
2973 };
2974 #[cfg(not(feature = "monad"))]
2975 let result = self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env)?;
2976 Ok((result, base))
2977 }
2978
2979 fn build_tempo_evm_env(
2982 evm_env: &EvmEnv,
2983 hardfork: TempoHardfork,
2984 ) -> EvmEnv<TempoHardfork, TempoBlockEnv> {
2985 EvmEnv::new(
2986 evm_env.cfg_env.clone().with_spec_and_gas_params(hardfork, tempo_gas_params(hardfork)),
2987 TempoBlockEnv {
2988 inner: evm_env.block_env.clone(),
2989 timestamp_millis_part: 0,
2990 ..Default::default()
2991 },
2992 )
2993 }
2994
2995 fn transact_tempo_with_inspector_ref<'db, I, DB>(
2997 &self,
2998 db: &'db DB,
2999 evm_env: &EvmEnv,
3000 inspector: &mut I,
3001 tx_env: TempoTxEnv,
3002 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
3003 where
3004 DB: DatabaseRef + ?Sized,
3005 I: Inspector<TempoContext<WrapDatabaseRef<&'db DB>>>,
3006 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
3007 {
3008 let tempo_env = Self::build_tempo_evm_env(evm_env, self.tempo_hardfork());
3009 let mut evm = TempoEvmFactory::default().create_evm_with_inspector(
3010 WrapDatabaseRef(db),
3011 tempo_env,
3012 inspector,
3013 );
3014 self.inject_tempo_precompiles(&mut evm, evm_env);
3015 Ok(evm.transact(tx_env)?)
3016 }
3017
3018 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
3021 fn execute_with_block_executor<DB>(
3022 &self,
3023 db: DB,
3024 evm_env: &EvmEnv,
3025 parent_hash: B256,
3026 hardfork: FoundryHardfork,
3027 parent_beacon_block_root: Option<B256>,
3028 execution_kind: BlockExecutionKind,
3029 pool_transactions: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
3030 gas_config: &PoolTxGasConfig,
3031 inspector_tx_config: &InspectorTxConfig,
3032 validator: &dyn Fn(
3033 &PoolTransaction<FoundryTxEnvelope>,
3034 &AccountInfo,
3035 ) -> Result<(), InvalidTransactionError>,
3036 ) -> Result<
3037 (ExecutedPoolTransactions<FoundryTxEnvelope>, BlockExecutionResult<FoundryReceiptEnvelope>),
3038 BlockchainError,
3039 >
3040 where
3041 DB: StateDB<Error = DatabaseError> + BalIndexedDatabase,
3042 {
3043 let spec_id = *evm_env.spec_id();
3044 #[cfg(feature = "monad")]
3045 if self.is_monad() {
3046 return self.execute_with_monad_block_executor(
3047 db,
3048 evm_env,
3049 parent_hash,
3050 spec_id,
3051 hardfork.into(),
3052 pool_transactions,
3053 gas_config,
3054 inspector_tx_config,
3055 validator,
3056 );
3057 }
3058
3059 let inspector = self.build_mining_inspector();
3060 let transitions =
3061 self.ethereum_block_transitions(hardfork, parent_beacon_block_root, execution_kind);
3062
3063 macro_rules! execute {
3064 ($executor:expr) => {{
3065 let mut executor = $executor;
3066 executor
3067 .apply_pre_execution_changes()
3068 .map_err(|err| BlockchainError::Internal(err.to_string()))?;
3069 let pool_result = execute_pool_transactions(
3070 &mut executor,
3071 pool_transactions,
3072 gas_config,
3073 inspector_tx_config,
3074 self.cheats(),
3075 validator,
3076 &mut execute_pool_transaction,
3077 );
3078 let (_, block_result) =
3079 executor.finish().map_err(|err| BlockchainError::Internal(err.to_string()))?;
3080 Ok((pool_result, block_result))
3081 }};
3082 }
3083
3084 #[cfg(feature = "base")]
3085 if self.is_base() {
3086 let configured = match hardfork {
3087 FoundryHardfork::Base(upgrade) => upgrade,
3088 _ => BaseUpgrade::Azul,
3089 };
3090 let upgrade = if self.is_fork() {
3091 BaseUpgrade::from_chain_and_timestamp(
3092 evm_env.cfg_env.chain_id,
3093 evm_env.block_env.timestamp.saturating_to(),
3094 )
3095 .unwrap_or(configured)
3096 } else {
3097 configured
3098 };
3099 let cfg =
3100 evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(BaseSpecId::new(upgrade));
3101 let activation_admin = self.base_activation_admin().or_else(|| {
3102 ChainConfig::activation_admin_address_for_upgrade_by_chain_id(cfg.chain_id, upgrade)
3103 });
3104 let base_env = EvmEnv::new(cfg, evm_env.block_env.clone());
3105 let mut evm = BaseEvmFactory::new(activation_admin)
3106 .create_evm_with_inspector(db, base_env, inspector);
3107 evm.ctx_mut().cfg.tx_chain_id_check = true;
3108 self.inject_precompiles(evm.precompiles_mut(), evm_env);
3109 let mut executor = AnvilBlockExecutor::new(evm, parent_hash, spec_id, transitions)
3110 .with_max_blob_gas_per_block(gas_config.max_blob_gas_per_block);
3111 executor.set_base_upgrade(upgrade);
3112 return execute!(executor);
3113 }
3114
3115 #[cfg(feature = "optimism")]
3116 if self.is_optimism() {
3117 let op_env = EvmEnv::new(
3118 evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(hardfork.into()),
3119 evm_env.block_env.clone(),
3120 );
3121 let mut evm =
3122 OpEvmFactory::<OpTx>::default().create_evm_with_inspector(db, op_env, inspector);
3123 self.inject_precompiles(evm.precompiles_mut(), evm_env);
3124 let mut executor = AnvilBlockExecutor::new(evm, parent_hash, spec_id, transitions)
3125 .with_max_blob_gas_per_block(gas_config.max_blob_gas_per_block);
3126 executor.set_optimism_hardfork(hardfork);
3127 return execute!(executor);
3128 }
3129
3130 if self.is_tempo() {
3131 let tempo_env = Self::build_tempo_evm_env(evm_env, self.tempo_hardfork());
3132 let mut evm =
3133 TempoEvmFactory::default().create_evm_with_inspector(db, tempo_env, inspector);
3134 self.inject_precompiles(evm.precompiles_mut(), evm_env);
3135 let executor = AnvilBlockExecutor::new(evm, parent_hash, spec_id, transitions)
3136 .with_max_blob_gas_per_block(gas_config.max_blob_gas_per_block);
3137 return execute!(executor);
3138 }
3139 let mut evm =
3140 EthEvmFactory::default().create_evm_with_inspector(db, evm_env.clone(), inspector);
3141 self.inject_precompiles(evm.precompiles_mut(), evm_env);
3142 let executor = AnvilBlockExecutor::new(evm, parent_hash, spec_id, transitions)
3143 .with_max_blob_gas_per_block(gas_config.max_blob_gas_per_block);
3144 execute!(executor)
3145 }
3146
3147 fn apply_simulation_pre_execution_changes<DB>(
3149 &self,
3150 db: DB,
3151 evm_env: &EvmEnv,
3152 parent_hash: B256,
3153 transitions: EthereumBlockTransitions,
3154 ) -> Result<(), BlockchainError>
3155 where
3156 DB: StateDB,
3157 {
3158 let inspector = self.build_mining_inspector();
3159 let mut evm =
3160 EthEvmFactory::default().create_evm_with_inspector(db, evm_env.clone(), inspector);
3161 self.inject_precompiles(evm.precompiles_mut(), evm_env);
3162 apply_ethereum_pre_execution_changes(&mut evm, parent_hash, transitions)
3163 .map_err(|err| BlockchainError::Internal(err.to_string()))
3164 }
3165
3166 fn apply_simulation_post_execution_changes<DB>(
3168 &self,
3169 db: DB,
3170 evm_env: &EvmEnv,
3171 transitions: EthereumBlockTransitions,
3172 receipts: &[FoundryReceiptEnvelope],
3173 ) -> Result<alloy_eips::eip7685::Requests, BlockchainError>
3174 where
3175 DB: StateDB,
3176 {
3177 let inspector = self.build_mining_inspector();
3178 let mut evm =
3179 EthEvmFactory::default().create_evm_with_inspector(db, evm_env.clone(), inspector);
3180 self.inject_precompiles(evm.precompiles_mut(), evm_env);
3181 apply_ethereum_post_execution_changes(&mut evm, transitions, receipts)
3182 .map_err(|err| BlockchainError::Internal(err.to_string()))
3183 }
3184
3185 fn build_call_env_with_base(
3196 &self,
3197 request: WithOtherFields<TransactionRequest>,
3198 fee_details: FeeDetails,
3199 block_env: BlockEnv,
3200 base_evm_env: Option<&EvmEnv>,
3201 ) -> (EvmEnv, TxEnv, CallTransactionInfo) {
3202 let tx_type = request.minimal_tx_type() as u8;
3203 #[cfg(any(feature = "base", feature = "optimism"))]
3204 let mut transaction_info = CallTransactionInfo::default();
3205 #[cfg(not(any(feature = "base", feature = "optimism")))]
3206 let transaction_info = CallTransactionInfo::default();
3207
3208 let WithOtherFields::<TransactionRequest> {
3209 inner:
3210 TransactionRequest {
3211 from,
3212 to,
3213 gas,
3214 value,
3215 input,
3216 access_list,
3217 blob_versioned_hashes,
3218 authorization_list,
3219 nonce,
3220 sidecar: _,
3221 chain_id,
3222 .. },
3224 other,
3225 } = request;
3226
3227 let FeeDetails {
3228 gas_price,
3229 max_fee_per_gas,
3230 max_priority_fee_per_gas,
3231 max_fee_per_blob_gas,
3232 } = fee_details;
3233
3234 let gas_limit = gas.unwrap_or(block_env.gas_limit);
3235 let mut evm_env = base_evm_env.cloned().unwrap_or_else(|| self.evm_env.read().clone());
3236 evm_env.block_env = block_env;
3237 evm_env.cfg_env.disable_block_gas_limit = true;
3240 evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
3241
3242 evm_env.cfg_env.disable_nonce_check = true;
3244
3245 let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| {
3246 self.fees().raw_gas_price().saturating_add(MIN_SUGGESTED_PRIORITY_FEE)
3247 });
3248 if gas_price == 0 {
3252 evm_env.block_env.basefee = 0;
3253 }
3254 evm_env.cfg_env.disable_base_fee =
3257 evm_env.block_env.basefee == 0 || evm_env.cfg_env.spec < SpecId::LONDON;
3258 let caller = from.unwrap_or_default();
3259 let to = to.as_ref().and_then(TxKind::to);
3260 let blob_hashes = blob_versioned_hashes.unwrap_or_default();
3261 let is_blob_call = !blob_hashes.is_empty() || max_fee_per_blob_gas.is_some();
3264 let max_fee_per_blob_gas = max_fee_per_blob_gas.unwrap_or_default();
3265 if is_blob_call
3266 && max_fee_per_blob_gas == 0
3267 && let Some(blob) = evm_env.block_env.blob_excess_gas_and_price.as_mut()
3268 {
3269 blob.blob_gasprice = 0;
3270 }
3271 let mut tx_env = TxEnv {
3272 caller,
3273 gas_limit,
3274 gas_price,
3275 gas_priority_fee: max_priority_fee_per_gas,
3276 max_fee_per_blob_gas,
3277 kind: match to {
3278 Some(addr) => TxKind::Call(*addr),
3279 None => TxKind::Create,
3280 },
3281 tx_type,
3282 value: value.unwrap_or_default(),
3283 data: input.into_input().unwrap_or_default(),
3284 chain_id: Some(chain_id.unwrap_or(evm_env.cfg_env.chain_id)),
3285 access_list: access_list.unwrap_or_default(),
3286 blob_hashes,
3287 ..Default::default()
3288 };
3289 tx_env.set_signed_authorization(authorization_list.unwrap_or_default());
3290
3291 if let Some(nonce) = nonce {
3292 tx_env.nonce = nonce;
3293 }
3294
3295 #[cfg(any(feature = "base", feature = "optimism"))]
3297 {
3298 transaction_info.deposit = if self.ensure_deposits_active().is_ok()
3299 && let Ok(deposit) = get_deposit_tx_parts(&other)
3300 {
3301 deposit
3302 } else {
3303 DepositTransactionParts::default()
3304 };
3305 }
3306 #[cfg(not(any(feature = "base", feature = "optimism")))]
3307 {
3308 let _ = &other;
3310 }
3311
3312 (evm_env, tx_env, transaction_info)
3313 }
3314
3315 fn prepare_call_env(
3316 &self,
3317 state: &dyn DatabaseRef,
3318 request: WithOtherFields<TransactionRequest>,
3319 fee_details: FeeDetails,
3320 block_env: BlockEnv,
3321 ) -> Result<PreparedCall, BlockchainError> {
3322 self.prepare_call_env_from_base(state, request, fee_details, block_env, None)
3323 }
3324
3325 fn prepare_call_env_from_base(
3326 &self,
3327 state: &dyn DatabaseRef,
3328 request: WithOtherFields<TransactionRequest>,
3329 fee_details: FeeDetails,
3330 block_env: BlockEnv,
3331 base_evm_env: Option<&EvmEnv>,
3332 ) -> Result<PreparedCall, BlockchainError> {
3333 let gas_omitted = request.gas.is_none();
3334 let request = self.parse_transaction_request(request)?;
3335 let mut prepared = self.prepare_typed_call_env_with_base(
3336 state,
3337 request,
3338 fee_details,
3339 block_env,
3340 base_evm_env,
3341 )?;
3342 let cap_by_balance = match &prepared.tx_env {
3346 CallTxEnv::Tempo(_) => false,
3347 #[cfg(feature = "optimism")]
3348 CallTxEnv::Op(tx) => tx.tx_type() != DEPOSIT_TX_TYPE_ID,
3349 _ => true,
3350 };
3351 let min_gas = if prepared.evm_env.cfg_env.enable_amsterdam_eip2780 {
3352 eip2780::TX_BASE_COST
3353 } else {
3354 MIN_TRANSACTION_GAS as u64
3355 };
3356 let tx_env = prepared.tx_env.base_mut();
3357 if gas_omitted && cap_by_balance && tx_env.gas_price > 0 {
3358 let balance =
3359 state.basic_ref(tx_env.caller)?.map(|info| info.balance).unwrap_or_default();
3360 let upfront = tx_env.value.saturating_add(tx_env.calc_max_data_fee());
3361 if let Some(allowance) = balance
3364 .checked_sub(upfront)
3365 .map(|available| available / U256::from(tx_env.gas_price))
3366 && allowance >= U256::from(min_gas)
3367 {
3368 tx_env.gas_limit = tx_env.gas_limit.min(allowance.saturating_to());
3369 }
3370 }
3371 Ok(prepared)
3372 }
3373
3374 const fn base_call_tx_env(&self, tx_env: TxEnv) -> CallTxEnv {
3375 #[cfg(feature = "monad")]
3376 if self.is_monad() {
3377 return CallTxEnv::Monad(tx_env);
3378 }
3379 CallTxEnv::Eth(tx_env)
3380 }
3381
3382 fn prepare_base_call_env_with_base(
3383 &self,
3384 request: WithOtherFields<TransactionRequest>,
3385 fee_details: FeeDetails,
3386 block_env: BlockEnv,
3387 base_evm_env: Option<&EvmEnv>,
3388 ) -> Result<PreparedCall, BlockchainError> {
3389 let (evm_env, tx_env, transaction_info) =
3390 self.build_call_env_with_base(request, fee_details, block_env, base_evm_env);
3391
3392 #[cfg(feature = "base")]
3393 if self.is_base() {
3394 let mut base_tx = BaseTransaction::new(tx_env);
3395 if base_tx.base.tx_type == DEPOSIT_TRANSACTION_TYPE {
3396 let deposit = &transaction_info.deposit;
3397 base_tx.deposit = BaseDepositTransactionParts::new(
3398 deposit.source_hash,
3399 deposit.mint,
3400 deposit.is_system_transaction,
3401 );
3402 } else {
3403 base_tx.enveloped_tx = Some(Bytes::new());
3404 }
3405 return Ok(PreparedCall {
3406 evm_env,
3407 tx_env: CallTxEnv::Base(Box::new(base_tx)),
3408 simulated_tempo_tx: None,
3409 simulated_envelope: None,
3410 });
3411 }
3412
3413 #[cfg(feature = "optimism")]
3414 let tx_env = if self.is_optimism() {
3415 CallTxEnv::Op(OpTransaction {
3416 base: tx_env,
3417 deposit: transaction_info.deposit,
3418 enveloped_tx: Some(Bytes::new()),
3419 })
3420 } else if self.is_tempo() {
3421 CallTxEnv::Tempo(TempoTxEnv::from(tx_env))
3422 } else {
3423 self.base_call_tx_env(tx_env)
3424 };
3425 #[cfg(not(feature = "optimism"))]
3426 let tx_env = {
3427 let _ = transaction_info;
3428 if self.is_tempo() {
3429 CallTxEnv::Tempo(TempoTxEnv::from(tx_env))
3430 } else {
3431 self.base_call_tx_env(tx_env)
3432 }
3433 };
3434 Ok(PreparedCall {
3435 evm_env,
3436 tx_env,
3437 simulated_tempo_tx: None,
3438 #[cfg(feature = "base")]
3439 simulated_envelope: None,
3440 })
3441 }
3442
3443 pub(crate) fn parse_transaction_request(
3445 &self,
3446 request: WithOtherFields<TransactionRequest>,
3447 ) -> Result<FoundryTransactionRequest, BlockchainError> {
3448 request
3449 .input
3450 .unique_input()
3451 .map_err(|err| BlockchainError::RpcError(RpcError::invalid_params(err.to_string())))?;
3452 if request.gas_price.is_some()
3454 && (request.max_fee_per_gas.is_some() || request.max_priority_fee_per_gas.is_some())
3455 {
3456 return Err(BlockchainError::ConflictingFeeFields);
3457 }
3458 let transaction_type = request.transaction_type;
3459 if transaction_type == Some(foundry_primitives::CIP64_TX_TYPE)
3460 || request.other.get("feeCurrency").is_some_and(|value| !value.is_null())
3461 {
3462 self.ensure_cip64_active()?;
3463 return FoundryTransactionRequest::new(request)
3464 .map_err(|err| BlockchainError::InvalidTransactionRequest(err.to_string()));
3465 }
3466 #[cfg(feature = "base")]
3467 if self.is_base() {
3468 let parsed = FoundryTransactionRequest::try_from(request.clone()).map_err(
3469 |err: serde_json::Error| {
3470 BlockchainError::InvalidTransactionRequest(err.to_string())
3471 },
3472 )?;
3473 if parsed.is_base() {
3474 return Ok(parsed);
3475 }
3476 if transaction_type == Some(DEPOSIT_TX_TYPE_ID)
3477 || get_deposit_tx_parts(&request.other).is_ok()
3478 {
3479 return Ok(FoundryTransactionRequest::Op(request));
3480 }
3481 return Ok(FoundryTransactionRequest::Ethereum(request.into_inner()));
3482 }
3483 #[cfg(feature = "base")]
3484 if transaction_type == Some(foundry_primitives::FoundryTxType::Eip8130.into())
3485 || matches!(
3486 FoundryTransactionRequest::try_from(request.clone()),
3487 Ok(FoundryTransactionRequest::Base(_))
3488 )
3489 {
3490 return Err(BlockchainError::BaseTransactionUnsupported);
3491 }
3492 if !self.is_tempo() && transaction_type != Some(TEMPO_TX_TYPE_ID) {
3493 #[cfg(feature = "optimism")]
3494 if transaction_type == Some(DEPOSIT_TX_TYPE_ID)
3495 || transaction_type == Some(POST_EXEC_TX_TYPE_ID)
3496 || get_deposit_tx_parts(&request.other).is_ok()
3497 {
3498 return Ok(FoundryTransactionRequest::Op(request));
3499 }
3500 return Ok(FoundryTransactionRequest::Ethereum(request.into_inner()));
3501 }
3502
3503 let parsed: FoundryTransactionRequest =
3504 FoundryTransactionRequest::new(request).map_err(|err: serde_json::Error| {
3505 BlockchainError::InvalidTransactionRequest(err.to_string())
3506 })?;
3507 if parsed.is_tempo() {
3508 self.ensure_tempo_active()?;
3509 }
3510 if parsed.is_tempo()
3511 && self.is_tempo()
3512 && transaction_type.is_some_and(|ty| ty != TEMPO_TX_TYPE_ID)
3513 {
3514 return Err(BlockchainError::FailedToDecodeTransaction);
3515 }
3516 Ok(parsed)
3517 }
3518
3519 fn build_tempo_request_env(
3520 request: TempoTransactionRequest,
3521 mut base: TxEnv,
3522 hardfork: TempoHardfork,
3523 ) -> Result<(TempoTxEnv, AASigned), BlockchainError> {
3524 let fee_payer = request.fee_payer_signature.map(|_| {
3525 request.clone().build_aa().ok().and_then(|tx| tx.recover_fee_payer(base.caller).ok())
3526 });
3527
3528 let mut response_request = request.clone();
3531 response_request.inner.from = Some(base.caller);
3532 response_request.inner.gas = Some(base.gas_limit);
3533 response_request.inner.nonce = Some(base.nonce);
3534 response_request.inner.chain_id = base.chain_id;
3535 response_request.inner.max_fee_per_gas = Some(base.gas_price);
3536 response_request.inner.max_priority_fee_per_gas =
3537 Some(base.gas_priority_fee.unwrap_or_default());
3538 response_request.inner.access_list = Some(base.access_list.clone());
3539 if response_request.calls.is_empty()
3540 && response_request.inner.to.is_none()
3541 && !base.data.is_empty()
3542 {
3543 response_request.inner.to = Some(base.kind);
3544 }
3545 let response_tx = response_request
3546 .build_aa()
3547 .map_err(|err| BlockchainError::InvalidTransactionRequest(err.to_string()))?;
3548 let response_tx = response_tx.into_signed(TempoSignature::default());
3549 let key_type = request.key_type.unwrap_or(SignatureType::Secp256k1);
3550 let key_data = request.key_data.clone();
3551 let key_id = request.key_id;
3552 let signature =
3553 mock_tempo_signature(key_type, key_data, key_id, base.caller, hardfork.is_t1c());
3554 let mut calls = request.calls;
3555 if let Some(to) = request.inner.to {
3556 calls.push(Call {
3557 to,
3558 value: request.inner.value.unwrap_or_default(),
3559 input: request.inner.input.into_input().unwrap_or_default(),
3560 });
3561 } else if calls.is_empty() && !base.data.is_empty() {
3562 calls.push(Call { to: base.kind, value: base.value, input: base.data.clone() });
3565 }
3566 if let Some(first_call) = calls.first() {
3567 base.kind = first_call.to;
3568 base.value = first_call.value;
3569 base.data = first_call.input.clone();
3570 }
3571 let tx_env = TempoTxEnv {
3572 fee_token: request.fee_token,
3573 is_system_tx: false,
3574 execution_context: ExecutionContext::Simulation,
3575 unique_tx_identifier: Some(TEMPO_RPC_SIMULATION_CONTEXT),
3576 fee_payer,
3577 tempo_tx_env: Some(Box::new(TempoBatchCallEnv {
3578 aa_calls: calls,
3579 signature,
3580 tempo_authorization_list: request
3581 .tempo_authorization_list
3582 .into_iter()
3583 .map(RecoveredTempoAuthorization::new)
3584 .collect(),
3585 nonce_key: request.nonce_key.unwrap_or_default(),
3586 key_authorization: request.key_authorization,
3587 signature_hash: B256::ZERO,
3588 tx_hash: B256::ZERO,
3589 valid_before: request.valid_before.map(|value| value.get()),
3590 valid_after: request.valid_after.map(|value| value.get()),
3591 override_key_id: key_id,
3592 expiring_nonce_idx: None,
3593 })),
3594 inner: base,
3595 };
3596 Ok((tx_env, response_tx))
3597 }
3598
3599 fn prepare_typed_call_env(
3600 &self,
3601 state: &dyn DatabaseRef,
3602 request: FoundryTransactionRequest,
3603 fee_details: FeeDetails,
3604 block_env: BlockEnv,
3605 ) -> Result<PreparedCall, BlockchainError> {
3606 self.prepare_typed_call_env_with_base(state, request, fee_details, block_env, None)
3607 }
3608
3609 fn prepare_typed_call_env_with_base(
3610 &self,
3611 state: &dyn DatabaseRef,
3612 request: FoundryTransactionRequest,
3613 fee_details: FeeDetails,
3614 block_env: BlockEnv,
3615 base_evm_env: Option<&EvmEnv>,
3616 ) -> Result<PreparedCall, BlockchainError> {
3617 match request {
3618 FoundryTransactionRequest::Tempo(tempo_request) => {
3619 self.ensure_tempo_active()?;
3620 let mut tempo_request = *tempo_request;
3621 if tempo_request.inner.nonce.is_none() {
3622 let caller = tempo_request.inner.from.unwrap_or_default();
3623 tempo_request.inner.nonce = Some(tempo_nonce(
3624 state,
3625 caller,
3626 tempo_request.nonce_key.unwrap_or_default(),
3627 )?);
3628 }
3629 let inner = WithOtherFields::new(tempo_request.inner.clone());
3630 let (evm_env, base, _) =
3631 self.build_call_env_with_base(inner, fee_details, block_env, base_evm_env);
3632 let (tx_env, simulated_tempo_tx) =
3633 Self::build_tempo_request_env(tempo_request, base, self.tempo_hardfork())?;
3634 Ok(PreparedCall {
3635 evm_env,
3636 tx_env: CallTxEnv::Tempo(tx_env),
3637 simulated_tempo_tx: Some(simulated_tempo_tx),
3638 #[cfg(feature = "base")]
3639 simulated_envelope: None,
3640 })
3641 }
3642 #[cfg(feature = "base")]
3643 FoundryTransactionRequest::Base(request) => {
3644 self.ensure_base_eip8130_active_at(block_env.timestamp.saturating_to())?;
3645 let gas_limit = block_env.gas_limit;
3646 let (evm_env, _, _) = self.build_call_env_with_base(
3647 WithOtherFields::new(request.as_ref().clone()),
3648 fee_details,
3649 block_env,
3650 base_evm_env,
3651 );
3652 let tx = request
3653 .to_eip8130_simulation_tx(self.chain_id().to(), gas_limit)
3654 .ok_or_else(|| {
3655 BlockchainError::InvalidTransactionRequest(
3656 "invalid EIP-8130 simulation request: missing or conflicting sender, \
3657 oversized authentication data, or invalid payer authenticator"
3658 .to_string(),
3659 )
3660 })?;
3661 let simulated_envelope = tx
3662 .eip8130
3663 .as_ref()
3664 .map(|parts| FoundryTxEnvelope::Eip8130(parts.signed.clone()));
3665 Ok(PreparedCall {
3666 evm_env,
3667 tx_env: CallTxEnv::Base(Box::new(tx)),
3668 simulated_tempo_tx: None,
3669 simulated_envelope,
3670 })
3671 }
3672 FoundryTransactionRequest::Ethereum(mut request)
3673 | FoundryTransactionRequest::Celo(foundry_primitives::Cip64TransactionRequest {
3674 inner: mut request,
3675 ..
3676 }) => {
3677 if self.is_tempo() && request.nonce.is_none() {
3680 request.nonce =
3681 Some(tempo_nonce(state, request.from.unwrap_or_default(), U256::ZERO)?);
3682 }
3683 self.prepare_base_call_env_with_base(
3684 WithOtherFields::new(request),
3685 fee_details,
3686 block_env,
3687 base_evm_env,
3688 )
3689 }
3690 #[cfg(any(feature = "base", feature = "optimism"))]
3691 FoundryTransactionRequest::Op(request) => {
3692 self.prepare_base_call_env_with_base(request, fee_details, block_env, base_evm_env)
3693 }
3694 }
3695 }
3696
3697 fn transact_call_with_inspector_ref<'db, I, DB>(
3698 &self,
3699 db: &'db DB,
3700 evm_env: &EvmEnv,
3701 inspector: &mut I,
3702 tx_env: CallTxEnv,
3703 #[cfg_attr(not(feature = "monad"), allow(unused_variables))] monad_context: Option<
3704 MonadExecutionContext<'_>,
3705 >,
3706 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
3707 where
3708 DB: DatabaseRef + ?Sized,
3709 I: BackendInspector<WrapDatabaseRef<&'db DB>>,
3710 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
3711 {
3712 self.transact_call_with_inspector_ref_at_hardfork(
3713 db,
3714 evm_env,
3715 inspector,
3716 tx_env,
3717 monad_context,
3718 self.hardfork(),
3719 )
3720 }
3721
3722 fn transact_call_with_inspector_ref_at_hardfork<'db, I, DB>(
3723 &self,
3724 db: &'db DB,
3725 evm_env: &EvmEnv,
3726 inspector: &mut I,
3727 tx_env: CallTxEnv,
3728 #[cfg_attr(not(feature = "monad"), allow(unused_variables))] monad_context: Option<
3729 MonadExecutionContext<'_>,
3730 >,
3731 #[cfg_attr(not(feature = "monad"), allow(unused_variables))] hardfork: FoundryHardfork,
3732 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
3733 where
3734 DB: DatabaseRef + ?Sized,
3735 I: BackendInspector<WrapDatabaseRef<&'db DB>>,
3736 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
3737 {
3738 match tx_env {
3739 CallTxEnv::Eth(tx_env) => {
3740 self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env)
3741 }
3742 #[cfg(feature = "base")]
3743 CallTxEnv::Base(tx_env) => {
3744 self.transact_base_with_inspector_ref(db, evm_env, inspector, *tx_env)
3745 }
3746 #[cfg(feature = "monad")]
3747 CallTxEnv::Monad(tx_env) => {
3748 let context = monad::resolve_execution_context(monad_context, &tx_env);
3749 self.transact_monad_with_inspector_ref(
3750 db,
3751 evm_env,
3752 inspector,
3753 tx_env,
3754 monad::PreparedExecution {
3755 context,
3756 kind: EnvelopeExecutionKind::Transaction,
3757 hardfork: monad_revm::MonadHardfork::from(hardfork),
3758 },
3759 )
3760 }
3761 #[cfg(feature = "optimism")]
3762 CallTxEnv::Op(tx_env) => {
3763 let spec = self.hardfork().into();
3764 self.transact_op_with_inspector_ref(db, evm_env, inspector, tx_env, spec)
3765 }
3766 CallTxEnv::Tempo(tx_env) => {
3767 self.transact_tempo_with_inspector_ref(db, evm_env, inspector, tx_env)
3768 }
3769 }
3770 }
3771
3772 pub fn call_with_state(
3773 &self,
3774 state: &dyn DatabaseRef,
3775 request: WithOtherFields<TransactionRequest>,
3776 fee_details: FeeDetails,
3777 block_env: BlockEnv,
3778 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
3779 self.call_with_state_and_context(state, request, fee_details, block_env, None)
3780 }
3781
3782 pub(crate) fn call_with_state_and_context(
3783 &self,
3784 state: &dyn DatabaseRef,
3785 request: WithOtherFields<TransactionRequest>,
3786 fee_details: FeeDetails,
3787 block_env: BlockEnv,
3788 mut monad_context: Option<MonadReplayContext>,
3789 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
3790 let mut inspector = self.build_inspector();
3791 let PreparedCall { evm_env, tx_env, .. } =
3792 self.prepare_call_env(state, request, fee_details, block_env)?;
3793 let ResultAndState { result, state } = self.transact_call_with_inspector_ref(
3794 state,
3795 &evm_env,
3796 &mut inspector,
3797 tx_env,
3798 monad_context.as_mut().map(next_monad_context),
3799 )?;
3800
3801 let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
3802 inspector.print_logs();
3803
3804 if self.print_traces {
3805 inspector.into_print_traces(self.call_trace_decoder());
3806 }
3807
3808 Ok((exit_reason, out, gas_used as u128, state))
3809 }
3810
3811 pub(crate) fn call_with_state_typed_gas_limit(
3812 &self,
3813 state: &dyn DatabaseRef,
3814 request: FoundryTransactionRequest,
3815 fee_details: FeeDetails,
3816 block_env: BlockEnv,
3817 options: GasEstimateCallOptions,
3818 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
3819 let GasEstimateCallOptions { gas_limit, disable_fee_charge, monad_context } = options;
3820 self.call_with_state_typed_inner(
3821 state,
3822 request,
3823 fee_details,
3824 block_env,
3825 TypedCallOverrides {
3826 gas_limit: Some(gas_limit),
3827 disable_fee_charge,
3828 ..Default::default()
3829 },
3830 monad_context,
3831 )
3832 }
3833
3834 pub(crate) fn call_with_state_typed_access_list(
3835 &self,
3836 state: &dyn DatabaseRef,
3837 request: FoundryTransactionRequest,
3838 fee_details: FeeDetails,
3839 block_env: BlockEnv,
3840 access_list: AccessList,
3841 monad_context: Option<MonadReplayContext>,
3842 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
3843 self.call_with_state_typed_inner(
3844 state,
3845 request,
3846 fee_details,
3847 block_env,
3848 TypedCallOverrides { access_list: Some(access_list), ..Default::default() },
3849 monad_context,
3850 )
3851 }
3852
3853 fn call_with_state_typed_inner(
3854 &self,
3855 state: &dyn DatabaseRef,
3856 request: FoundryTransactionRequest,
3857 fee_details: FeeDetails,
3858 block_env: BlockEnv,
3859 overrides: TypedCallOverrides,
3860 mut monad_context: Option<MonadReplayContext>,
3861 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
3862 let mut inspector = AnvilInspector::default();
3864 if self.print_logs {
3865 inspector = inspector.with_log_collector();
3866 }
3867 let block_fees = (block_env.basefee, block_env.blob_excess_gas_and_price);
3868 let PreparedCall { mut evm_env, mut tx_env, .. } =
3869 self.prepare_typed_call_env(state, request, fee_details, block_env)?;
3870 evm_env.cfg_env.disable_fee_charge = overrides.disable_fee_charge;
3871 if let Some(gas_limit) = overrides.gas_limit {
3872 tx_env.base_mut().gas_limit = gas_limit;
3873 }
3874 if let Some(access_list) = overrides.access_list {
3875 let tx_env = tx_env.base_mut();
3876 Self::use_block_fees(&mut evm_env, tx_env, block_fees);
3877 tx_env.access_list = access_list;
3878 if tx_env.tx_type == TransactionType::Legacy as u8 {
3879 tx_env.tx_type = TransactionType::Eip2930 as u8;
3880 }
3881 }
3882 let ResultAndState { result, state } = self.transact_call_with_inspector_ref(
3883 state,
3884 &evm_env,
3885 &mut inspector,
3886 tx_env,
3887 monad_context.as_mut().map(next_monad_context),
3888 )?;
3889 let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
3890 inspector.print_logs();
3891 Ok((exit_reason, out, gas_used as u128, state))
3892 }
3893
3894 pub fn build_access_list_with_state(
3895 &self,
3896 state: &dyn DatabaseRef,
3897 request: WithOtherFields<TransactionRequest>,
3898 fee_details: FeeDetails,
3899 block_env: BlockEnv,
3900 ) -> Result<(InstructionResult, Option<Output>, u64, AccessList), BlockchainError> {
3901 self.build_access_list_with_state_and_context(state, request, fee_details, block_env, None)
3902 }
3903
3904 pub(crate) fn build_access_list_with_state_and_context(
3905 &self,
3906 state: &dyn DatabaseRef,
3907 request: WithOtherFields<TransactionRequest>,
3908 fee_details: FeeDetails,
3909 block_env: BlockEnv,
3910 mut monad_context: Option<MonadReplayContext>,
3911 ) -> Result<(InstructionResult, Option<Output>, u64, AccessList), BlockchainError> {
3912 let mut inspector =
3913 AccessListInspector::new(request.access_list.clone().unwrap_or_default());
3914
3915 let block_fees = (block_env.basefee, block_env.blob_excess_gas_and_price);
3916 let PreparedCall { mut evm_env, mut tx_env, .. } =
3917 self.prepare_call_env(state, request, fee_details, block_env)?;
3918 Self::use_block_fees(&mut evm_env, tx_env.base_mut(), block_fees);
3919 let ResultAndState { result, state: _ } = self.transact_call_with_inspector_ref(
3920 state,
3921 &evm_env,
3922 &mut inspector,
3923 tx_env,
3924 monad_context.as_mut().map(next_monad_context),
3925 )?;
3926 let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
3927 let access_list = inspector.access_list();
3928 #[cfg(feature = "monad")]
3929 let access_list = if self.is_monad() {
3930 monad::normalize_access_list(access_list, self.monad_hardfork())
3931 } else {
3932 access_list
3933 };
3934 Ok((exit_reason, out, gas_used, access_list))
3935 }
3936
3937 fn use_block_fees(
3940 evm_env: &mut EvmEnv,
3941 tx_env: &mut TxEnv,
3942 (basefee, blob_excess_gas_and_price): (u64, Option<BlobExcessGasAndPrice>),
3943 ) {
3944 evm_env.block_env.basefee = basefee;
3945 evm_env.block_env.blob_excess_gas_and_price = blob_excess_gas_and_price;
3946 if !tx_env.blob_hashes.is_empty() && tx_env.max_fee_per_blob_gas == 0 {
3947 tx_env.max_fee_per_blob_gas = evm_env.block_env.blob_gasprice().unwrap_or_default();
3948 }
3949 }
3950
3951 fn arbitrum_block_number(&self, evm_env: &EvmEnv) -> Option<u64> {
3952 if !arbitrum::is_arbitrum_chain(self.protocol_chain_id()) {
3953 return None;
3954 }
3955
3956 let env_block = evm_env.block_env.number;
3957 Some(
3958 self.get_fork()
3959 .map_or_else(|| env_block.saturating_to(), |fork| fork.rpc_block_number(env_block)),
3960 )
3961 }
3962
3963 pub fn get_code_with_state(
3964 &self,
3965 state: &dyn DatabaseRef,
3966 address: Address,
3967 ) -> Result<Bytes, BlockchainError> {
3968 trace!(target: "backend", "get code for {:?}", address);
3969 let account = state.basic_ref(address)?.unwrap_or_default();
3970 if account.is_empty_code_hash() {
3971 return Ok(Default::default());
3973 }
3974 let code = if let Some(code) = account.code {
3975 code
3976 } else {
3977 state.code_by_hash_ref(account.code_hash())?
3978 };
3979 Ok(code.original_bytes())
3980 }
3981
3982 pub fn get_balance_with_state<D>(
3983 &self,
3984 state: D,
3985 address: Address,
3986 ) -> Result<U256, BlockchainError>
3987 where
3988 D: DatabaseRef,
3989 {
3990 trace!(target: "backend", "get balance for {:?}", address);
3991 Ok(state.basic_ref(address)?.unwrap_or_default().balance)
3992 }
3993
3994 pub async fn transaction_by_block_number_and_index(
3995 &self,
3996 number: BlockNumber,
3997 index: Index,
3998 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
3999 if let Some(block) = self.mined_block_by_number(number) {
4000 return Ok(self.mined_transaction_by_block_hash_and_index(block.header.hash, index));
4001 }
4002
4003 if let Some(fork) = self.get_fork() {
4004 let number = self.convert_block_number(Some(number));
4005 if fork.predates_fork(number) {
4006 return Ok(fork
4007 .transaction_by_block_number_and_index(number, index.into())
4008 .await?);
4009 }
4010 }
4011
4012 Ok(None)
4013 }
4014
4015 pub async fn transaction_by_block_hash_and_index(
4016 &self,
4017 hash: B256,
4018 index: Index,
4019 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
4020 if let tx @ Some(_) = self.mined_transaction_by_block_hash_and_index(hash, index) {
4021 return Ok(tx);
4022 }
4023
4024 if let Some(fork) = self.get_fork() {
4025 return Ok(fork.transaction_by_block_hash_and_index(hash, index.into()).await?);
4026 }
4027
4028 Ok(None)
4029 }
4030
4031 pub fn mined_transaction_by_block_hash_and_index(
4032 &self,
4033 block_hash: B256,
4034 index: Index,
4035 ) -> Option<AnyRpcTransaction> {
4036 let (info, block, tx) = {
4037 let storage = self.blockchain.storage.read();
4038 let block = storage.blocks.get(&block_hash).cloned()?;
4039 let index: usize = index.into();
4040 let tx = block.body.transactions.get(index)?.clone();
4041 let info = storage.transactions.get(&tx.hash())?.info.clone();
4042 (info, block, tx)
4043 };
4044
4045 Some(transaction_build(
4046 Some(info.transaction_hash),
4047 tx,
4048 Some(&block),
4049 Some(info),
4050 block.header.base_fee_per_gas(),
4051 ))
4052 }
4053
4054 pub async fn transaction_by_hash(
4055 &self,
4056 hash: B256,
4057 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
4058 trace!(target: "backend", "transaction_by_hash={:?}", hash);
4059 if let tx @ Some(_) = self.mined_transaction_by_hash(hash) {
4060 return Ok(tx);
4061 }
4062
4063 if let Some(fork) = self.get_fork() {
4064 return fork
4065 .transaction_by_hash(hash)
4066 .await
4067 .map_err(BlockchainError::AlloyForkProvider);
4068 }
4069
4070 Ok(None)
4071 }
4072
4073 pub fn mined_transaction_by_hash(&self, hash: B256) -> Option<AnyRpcTransaction> {
4074 let (info, block) = {
4075 let storage = self.blockchain.storage.read();
4076 let MinedTransaction { info, block_hash, .. } =
4077 storage.transactions.get(&hash)?.clone();
4078 let block = storage.blocks.get(&block_hash).cloned()?;
4079 (info, block)
4080 };
4081 let tx = block.body.transactions.get(info.transaction_index as usize)?.clone();
4082
4083 Some(transaction_build(
4084 Some(info.transaction_hash),
4085 tx,
4086 Some(&block),
4087 Some(info),
4088 block.header.base_fee_per_gas(),
4089 ))
4090 }
4091
4092 pub async fn trace_transaction(
4094 &self,
4095 hash: B256,
4096 ) -> Result<Option<Vec<LocalizedTransactionTrace>>, BlockchainError> {
4097 if let Some(traces) = self.mined_parity_trace_transaction(hash) {
4098 return Ok(Some(traces));
4099 }
4100
4101 if let Some(fork) = self.get_fork() {
4102 return Ok(fork.trace_transaction(hash).await?);
4103 }
4104
4105 Ok(None)
4106 }
4107
4108 pub async fn trace_get(
4110 &self,
4111 hash: B256,
4112 indices: Vec<Index>,
4113 ) -> Result<Option<LocalizedTransactionTrace>, BlockchainError> {
4114 if let Some(traces) = self.mined_parity_trace_transaction(hash) {
4115 let trace_address = indices.iter().copied().map(usize::from).collect::<Vec<_>>();
4116 return Ok(traces.into_iter().find(|trace| trace.trace.trace_address == trace_address));
4117 }
4118
4119 if let Some(fork) = self.get_fork() {
4120 return Ok(fork.trace_get(hash, indices).await?);
4121 }
4122
4123 Ok(None)
4124 }
4125
4126 pub async fn trace_block(
4128 &self,
4129 block: BlockNumber,
4130 ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
4131 ensure_mined_trace_block(block)?;
4132 let number = self.convert_block_number(Some(block));
4133 if let Some(traces) = self.mined_parity_trace_block(number) {
4134 return Ok(traces);
4135 }
4136
4137 if let Some(fork) = self.get_fork()
4138 && fork.predates_fork_inclusive(number)
4139 {
4140 return Ok(fork.trace_block(number).await?);
4141 }
4142
4143 Err(BlockchainError::BlockNotFound)
4144 }
4145
4146 pub async fn trace_call(
4148 &self,
4149 request: WithOtherFields<TransactionRequest>,
4150 fee_details: FeeDetails,
4151 trace_types: HashSet<TraceType>,
4152 block_request: BlockRequest<FoundryTxEnvelope>,
4153 block_id: BlockId,
4154 ) -> Result<TraceResults, BlockchainError>
4155 where
4156 Self: TransactionValidator<FoundryTxEnvelope>,
4157 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
4158 {
4159 if let BlockRequest::Number(number) = &block_request
4160 && let Some(fork) = self.get_fork()
4161 && fork.predates_fork(*number)
4162 {
4163 let resolved = match block_id {
4167 BlockId::Hash(_) => block_id,
4168 _ => BlockId::number(*number),
4169 };
4170 return Ok(fork.trace_call(request, trace_types, resolved).await?);
4171 }
4172
4173 self.with_database_at_and_context(Some(block_request), |state, block, mut monad_context| {
4174 let cache_db = CacheDB::new(state);
4175 let mut inspector =
4176 TracingInspector::new(TracingInspectorConfig::from_parity_config(&trace_types));
4177 let PreparedCall { evm_env, tx_env, .. } =
4178 self.prepare_call_env(&cache_db, request, fee_details, block)?;
4179 let result = self.transact_call_with_inspector_ref(
4180 &cache_db,
4181 &evm_env,
4182 &mut inspector,
4183 tx_env,
4184 monad_context.as_mut().map(next_monad_context),
4185 )?;
4186
4187 parity_trace_results(inspector, &result, &trace_types, &cache_db)
4188 })
4189 .await
4190 }
4191
4192 pub async fn trace_replay_block_transactions(
4195 &self,
4196 block: BlockNumber,
4197 trace_types: HashSet<TraceType>,
4198 ) -> Result<Option<Vec<TraceResultsWithTransactionHash>>, BlockchainError> {
4199 ensure_mined_trace_block(block)?;
4200 let block_number = self.convert_block_number(Some(block));
4201
4202 if let Some(results) =
4204 self.mined_parity_trace_replay_block_transactions(block_number, &trace_types)?
4205 {
4206 return Ok(Some(results));
4207 }
4208
4209 if let Some(fork) = self.get_fork()
4211 && fork.predates_fork_inclusive(block_number)
4212 {
4213 return Ok(Some(
4214 fork.trace_replay_block_transactions(block_number, trace_types).await?,
4215 ));
4216 }
4217
4218 Ok(None)
4219 }
4220
4221 pub async fn trace_replay_transaction(
4224 &self,
4225 hash: B256,
4226 trace_types: HashSet<TraceType>,
4227 ) -> Result<Option<TraceResultsWithTransactionHash>, BlockchainError> {
4228 let mined = self.blockchain.storage.read().transactions.contains_key(&hash);
4229
4230 let full_trace = if mined {
4234 let inspector =
4235 TracingInspector::new(TracingInspectorConfig::from_parity_config(&trace_types));
4236 self.replay_tx_with_inspector(hash, inspector, |result, cache_db, inspector, _, _| {
4237 parity_trace_results(inspector, &result, &trace_types, &cache_db)
4238 })??
4239 } else if let Some(fork) = self.get_fork() {
4240 let Some(full_trace) = fork.trace_replay_transaction(hash, trace_types).await? else {
4242 return Ok(None);
4243 };
4244 full_trace
4245 } else {
4246 return Ok(None);
4247 };
4248
4249 Ok(Some(TraceResultsWithTransactionHash { transaction_hash: hash, full_trace }))
4250 }
4251
4252 pub async fn trace_raw_transaction(
4254 &self,
4255 pending_transaction: PendingTransaction<FoundryTxEnvelope>,
4256 trace_types: HashSet<TraceType>,
4257 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4258 ) -> Result<TraceResults, BlockchainError>
4259 where
4260 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
4261 {
4262 let trace_config = TracingInspectorConfig::from_parity_config(&trace_types);
4263
4264 self.with_database_at_and_context(block_request, |state, block_env, mut monad_context| {
4265 let cache_db = CacheDB::new(state);
4266 let mut evm_env = self.evm_env.read().clone();
4267 evm_env.block_env = block_env;
4268 evm_env.cfg_env.disable_eip3607 = false;
4271
4272 let mut inspector = TracingInspector::new(trace_config);
4273 let (result, _) = self.transact_envelope_with_inspector_ref_and_context(
4274 &cache_db,
4275 &evm_env,
4276 &mut inspector,
4277 &pending_transaction,
4278 monad_context.as_mut().map(next_monad_context),
4279 )?;
4280
4281 parity_trace_results(inspector, &result, &trace_types, &cache_db)
4282 })
4283 .await
4284 }
4285
4286 pub async fn trace_call_many(
4288 &self,
4289 calls: Vec<(WithOtherFields<TransactionRequest>, HashSet<TraceType>)>,
4290 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4291 ) -> Result<Vec<TraceResults>, BlockchainError>
4292 where
4293 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
4294 {
4295 self.with_database_at_and_context(block_request, |state, block_env, mut monad_context| {
4296 let mut cache_db = CacheDB::new(state);
4297 let mut results = Vec::with_capacity(calls.len());
4298 let mut calls = calls.into_iter().peekable();
4299
4300 while let Some((request, trace_types)) = calls.next() {
4301 let fee_details = FeeDetails::new(
4302 request.gas_price,
4303 request.max_fee_per_gas,
4304 request.max_priority_fee_per_gas,
4305 request.max_fee_per_blob_gas,
4306 )?
4307 .or_zero_fees();
4308 let PreparedCall { evm_env, mut tx_env, simulated_tempo_tx, .. } =
4309 self.prepare_call_env(&cache_db, request, fee_details, block_env.clone())?;
4310 apply_tempo_envelope_identity(&mut tx_env, simulated_tempo_tx.as_ref());
4311
4312 let trace_config = TracingInspectorConfig::from_parity_config(&trace_types);
4313 let mut inspector = TracingInspector::new(trace_config);
4314 let result = self.transact_call_with_inspector_ref(
4315 &cache_db,
4316 &evm_env,
4317 &mut inspector,
4318 tx_env,
4319 monad_context.as_mut().map(next_monad_context),
4320 )?;
4321
4322 let trace_result =
4323 parity_trace_results(inspector, &result, &trace_types, &cache_db)?;
4324 results.push(trace_result);
4325
4326 if calls.peek().is_some() {
4327 cache_db.commit(result.state);
4328 }
4329 }
4330
4331 Ok(results)
4332 })
4333 .await
4334 }
4335
4336 fn mined_parity_trace_replay_block_transactions(
4338 &self,
4339 block_number: u64,
4340 trace_types: &HashSet<TraceType>,
4341 ) -> Result<Option<Vec<TraceResultsWithTransactionHash>>, BlockchainError> {
4342 let Some(block) = self.get_block(block_number) else { return Ok(None) };
4343
4344 if block.body.transactions.is_empty() {
4346 return Ok(Some(Vec::new()));
4347 }
4348
4349 let parent_hash = block.header.parent_hash;
4351 let trace_config = TracingInspectorConfig::from_parity_config(trace_types);
4352
4353 let read_guard = self.states.upgradable_read();
4354 if let Some(state) = read_guard.get_state(&parent_hash) {
4355 self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types)
4356 .map(Some)
4357 } else {
4358 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
4359 let Some(state) = write_guard.get_on_disk_state(&parent_hash) else {
4360 return Err(BlockchainError::HistoricalStateUnavailable(block.header.number()));
4361 };
4362 self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types)
4363 .map(Some)
4364 }
4365 }
4366
4367 fn replay_block_transactions_with_inspector(
4369 &self,
4370 block: &Block,
4371 parent_state: &StateDb,
4372 trace_config: TracingInspectorConfig,
4373 trace_types: &HashSet<TraceType>,
4374 ) -> Result<Vec<TraceResultsWithTransactionHash>, BlockchainError> {
4375 let (mut cache_db, evm_env, hardfork) = self.prepare_block_replay(block, parent_state)?;
4376 let mut results = Vec::new();
4377 let monad_context = self.active_monad_context_for_mined_block(block)?;
4378
4379 for tx_envelope in &block.body.transactions {
4381 let tx_hash = tx_envelope.hash();
4382
4383 let mut inspector = TracingInspector::new(trace_config);
4385
4386 let pending_tx = self.pending_mined_transaction(tx_envelope.clone())?;
4388 let transaction_context =
4389 monad_execution_context_at(monad_context.as_ref(), results.len());
4390 let (result, _) = self.replay_envelope_with_inspector_ref_and_context(
4391 &cache_db,
4392 &evm_env,
4393 &mut inspector,
4394 &pending_tx,
4395 EnvelopeExecution::replay(transaction_context, hardfork),
4396 )?;
4397
4398 let full_trace = parity_trace_results(inspector, &result, trace_types, &cache_db)?;
4400
4401 results.push(TraceResultsWithTransactionHash { transaction_hash: tx_hash, full_trace });
4402
4403 cache_db.commit(result.state);
4405 }
4406
4407 Ok(results)
4408 }
4409
4410 fn replay_tx_with_inspector<I, F, T>(
4411 &self,
4412 hash: B256,
4413 mut inspector: I,
4414 f: F,
4415 ) -> Result<T, BlockchainError>
4416 where
4417 for<'a> I: BackendInspector<WrapDatabaseRef<&'a CacheDB<Box<&'a StateDb>>>> + 'a,
4418 for<'a> F:
4419 FnOnce(ResultAndState<HaltReason>, CacheDB<Box<&'a StateDb>>, I, TxEnv, EvmEnv) -> T,
4420 {
4421 let block = {
4422 let storage = self.blockchain.storage.read();
4423 let MinedTransaction { block_hash, .. } =
4424 storage.transactions.get(&hash).ok_or(BlockchainError::TransactionNotFound)?;
4425
4426 storage.blocks.get(block_hash).cloned().ok_or(BlockchainError::BlockNotFound)?
4427 };
4428
4429 let index = block
4430 .body
4431 .transactions
4432 .iter()
4433 .position(|tx| tx.hash() == hash)
4434 .expect("transaction not found in block");
4435
4436 let trace = |parent_state: &StateDb| -> Result<T, BlockchainError> {
4437 let (mut cache_db, evm_env, hardfork) =
4438 self.prepare_block_replay_with_db(&block, Box::new(parent_state))?;
4439 self.replay_mined_transaction_prefix(&mut cache_db, &evm_env, hardfork, &block, index)?;
4440
4441 let target_tx = block.body.transactions[index].clone();
4442 let target_tx = self.pending_mined_transaction(target_tx)?;
4443 let monad_context = self.active_monad_context_for_mined_block(&block)?;
4444 let transaction_context = monad_execution_context_at(monad_context.as_ref(), index);
4445 let (result, base_tx_env) = self.replay_envelope_with_inspector_ref_and_context(
4446 &cache_db,
4447 &evm_env,
4448 &mut inspector,
4449 &target_tx,
4450 EnvelopeExecution::replay(transaction_context, hardfork),
4451 )?;
4452
4453 Ok(f(result, cache_db, inspector, base_tx_env, evm_env))
4454 };
4455
4456 let read_guard = self.states.upgradable_read();
4457 if let Some(state) = read_guard.get_state(&block.header.parent_hash) {
4458 trace(state)
4459 } else {
4460 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
4461 let state = write_guard
4462 .get_on_disk_state(&block.header.parent_hash)
4463 .ok_or(BlockchainError::HistoricalStateUnavailable(block.header.number()))?;
4464 trace(state)
4465 }
4466 }
4467
4468 pub async fn trace_filter(
4470 &self,
4471 filter: TraceFilter,
4472 ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
4473 let matcher = filter.matcher();
4474 let best_number = self.best_number();
4475 let start = filter.from_block.unwrap_or(best_number);
4476 let end = filter.to_block.unwrap_or(best_number);
4477
4478 if start > best_number || end > best_number {
4479 return Err(BlockchainError::BlockNotFound);
4480 }
4481 if start > end {
4482 return Err(BlockchainError::RpcError(RpcError::invalid_params(
4483 "invalid block range, ensure that to block is greater than from block".to_string(),
4484 )));
4485 }
4486
4487 let dist = end - start;
4488 if dist > 300 {
4489 return Err(BlockchainError::RpcError(RpcError::invalid_params(
4490 "block range too large, currently limited to 300".to_string(),
4491 )));
4492 }
4493
4494 let mut trace_tasks = vec![];
4496 for num in start..=end {
4497 trace_tasks.push(self.trace_block(num.into()));
4498 }
4499
4500 let traces = futures::future::try_join_all(trace_tasks).await?;
4502 let filtered_traces =
4503 traces.into_iter().flatten().filter(|trace| matcher.matches(&trace.trace));
4504
4505 let filtered_traces: Vec<_> = if let Some(after) = filter.after {
4507 filtered_traces.skip(after as usize).collect()
4508 } else {
4509 filtered_traces.collect()
4510 };
4511
4512 let filtered_traces: Vec<_> = if let Some(count) = filter.count {
4513 filtered_traces.into_iter().take(count as usize).collect()
4514 } else {
4515 filtered_traces
4516 };
4517
4518 Ok(filtered_traces)
4519 }
4520
4521 pub fn get_blobs_by_block_id(
4522 &self,
4523 id: impl Into<BlockId>,
4524 versioned_hashes: Vec<B256>,
4525 ) -> Result<Option<Vec<alloy_consensus::Blob>>> {
4526 Ok(self.get_block(id).map(|block| {
4527 block
4528 .body
4529 .transactions
4530 .iter()
4531 .filter_map(|tx| tx.as_ref().sidecar())
4532 .flat_map(|sidecar| {
4533 sidecar.sidecar.blobs().iter().zip(sidecar.sidecar.commitments().iter())
4534 })
4535 .filter(|(_, commitment)| {
4536 versioned_hashes.is_empty()
4538 || versioned_hashes.contains(&kzg_to_versioned_hash(commitment.as_slice()))
4539 })
4540 .map(|(blob, _)| *blob)
4541 .collect()
4542 }))
4543 }
4544
4545 #[allow(clippy::large_stack_frames)]
4546 pub fn get_blob_by_versioned_hash(&self, hash: B256) -> Result<Option<Blob>> {
4547 let storage = self.blockchain.storage.read();
4548 for block in storage.blocks.values() {
4549 for tx in &block.body.transactions {
4550 let typed_tx = tx.as_ref();
4551 if let Some(sidecar) = typed_tx.sidecar() {
4552 for versioned_hash in sidecar.sidecar.versioned_hashes() {
4553 if versioned_hash == hash
4554 && let Some(index) =
4555 sidecar.sidecar.commitments().iter().position(|commitment| {
4556 kzg_to_versioned_hash(commitment.as_slice()) == *hash
4557 })
4558 && let Some(blob) = sidecar.sidecar.blobs().get(index)
4559 {
4560 return Ok(Some(*blob));
4561 }
4562 }
4563 }
4564 }
4565 }
4566 Ok(None)
4567 }
4568
4569 #[expect(clippy::too_many_arguments)]
4571 pub async fn with_genesis(
4572 db: Arc<AsyncRwLock<Box<dyn Db>>>,
4573 env: Arc<RwLock<EvmEnv>>,
4574 networks: NetworkConfigs,
4575 genesis: GenesisConfig,
4576 fees: FeeManager,
4577 fork: Arc<RwLock<Option<ClientFork>>>,
4578 enable_steps_tracing: bool,
4579 print_logs: bool,
4580 print_traces: bool,
4581 call_trace_decoder: Arc<CallTraceDecoder>,
4582 prune_state_history_config: PruneStateHistoryConfig,
4583 max_persisted_states: Option<usize>,
4584 transaction_block_keeper: Option<usize>,
4585 automine_block_time: Option<Duration>,
4586 cache_path: Option<PathBuf>,
4587 node_config: Arc<AsyncRwLock<NodeConfig>>,
4588 ) -> Result<Self> {
4589 let last_fork_cache_source = fork.read().as_ref().and_then(ForkCacheSource::from_fork);
4590 let blockchain = if let Some(fork) = fork.read().as_ref() {
4592 trace!(target: "backend", "using forked blockchain at {}", fork.block_number());
4593 Blockchain::forked(fork.block_number(), fork.block_hash(), fork.total_difficulty())
4594 } else {
4595 let header = if let Some(genesis) = genesis.genesis_init.as_ref() {
4596 genesis_json_header(genesis)
4597 } else {
4598 genesis_header(
4599 &env.read(),
4600 fees.is_eip1559().then(|| fees.base_fee()),
4601 genesis.timestamp,
4602 genesis.number,
4603 )
4604 };
4605 Blockchain::new(foundry_header(&networks, header))
4606 };
4607
4608 if fork.read().is_none() {
4611 env.write().block_env.number = U256::from(genesis.number);
4612
4613 if fees.tempo_hardfork().is_some() {
4617 let env = env.read();
4618 let next_base_fee = fees.get_next_block_base_fee_per_gas(
4619 0,
4620 env.block_env.gas_limit,
4621 env.block_env.basefee,
4622 );
4623 drop(env);
4624 fees.set_base_fee(next_base_fee);
4625 }
4626 }
4627
4628 let start_timestamp = if let Some(fork) = fork.read().as_ref() {
4629 fork.timestamp()
4630 } else {
4631 genesis.timestamp
4632 };
4633
4634 let mut states = if prune_state_history_config.is_config_enabled() {
4635 prune_state_history_config
4637 .max_memory_history
4638 .map(|limit| InMemoryBlockStates::new(limit, 0))
4639 .unwrap_or_default()
4640 .memory_only()
4641 } else if max_persisted_states.is_some() {
4642 max_persisted_states
4643 .map(|limit| InMemoryBlockStates::new(DEFAULT_HISTORY_LIMIT, limit))
4644 .unwrap_or_default()
4645 } else {
4646 Default::default()
4647 };
4648
4649 if let Some(cache_path) = cache_path {
4650 states = states.disk_path(cache_path);
4651 }
4652
4653 let (slots_in_an_epoch, precompile_factory, disable_pool_balance_checks, hardfork) = {
4654 let cfg = node_config.read().await;
4655 (
4656 cfg.slots_in_an_epoch,
4657 cfg.precompile_factory.clone(),
4658 cfg.disable_pool_balance_checks,
4659 cfg.get_hardfork(),
4660 )
4661 };
4662 let startup_cache_lease = if fork.read().is_some() {
4663 let db = db.read().await;
4664 let inner = db
4665 .maybe_inner()
4666 .map_err(|err| eyre::eyre!("fork database is missing its cache backend: {err}"))?;
4667 StagedForkCacheLease::for_db(inner)
4668 } else {
4669 StagedForkCacheLease::default()
4670 };
4671 let startup_fork_cache_user =
4672 StagedForkDbUser { db: Some(Arc::clone(&db)), cache_lease: startup_cache_lease };
4673 #[cfg(feature = "base")]
4674 let base_activation_admin = node_config.read().await.base_activation_admin;
4675
4676 let backend = Self {
4677 db,
4678 blockchain,
4679 states: Arc::new(RwLock::new(states)),
4680 evm_env: env,
4681 networks,
4682 #[cfg(feature = "base")]
4683 base_activation_admin,
4684 hardfork: Arc::new(RwLock::new(hardfork)),
4685 fork,
4686 last_fork_cache_source: Arc::new(RwLock::new(last_fork_cache_source)),
4687 time: TimeManager::new(start_timestamp),
4688 cheats: Default::default(),
4689 new_block_listeners: Default::default(),
4690 fees,
4691 genesis,
4692 active_state_snapshots: Arc::new(Mutex::new(Default::default())),
4693 enable_steps_tracing,
4694 print_logs,
4695 print_traces,
4696 call_trace_decoder: Arc::new(RwLock::new(call_trace_decoder)),
4697 prune_state_history_config,
4698 transaction_block_keeper,
4699 node_config,
4700 slots_in_an_epoch,
4701 precompile_factory,
4702 mining: Arc::new(tokio::sync::Mutex::new(())),
4703 #[cfg(test)]
4704 mining_commit_hook: Default::default(),
4705 disable_pool_balance_checks,
4706 startup_fork_cache_user,
4707 };
4708
4709 #[cfg(feature = "monad")]
4710 let monad_fork =
4711 if backend.networks.is_monad() { backend.fork.read().clone() } else { None };
4712 #[cfg(feature = "monad")]
4713 if let Some(fork) = monad_fork {
4714 monad::cache_fork_context(&fork).await?;
4715 }
4716
4717 if let Some(interval_block_time) = automine_block_time {
4718 backend.update_interval_mine_block_time(interval_block_time);
4719 }
4720
4721 backend.apply_genesis().await.wrap_err("failed to create genesis")?;
4723 Ok(backend)
4724 }
4725
4726 async fn apply_genesis(&self) -> Result<(), DatabaseError> {
4730 trace!(target: "backend", "setting genesis balances");
4731
4732 if self.fork.read().is_some() {
4733 let parent_env = self.evm_env.read().clone();
4734 return self
4735 .apply_fork_genesis(
4736 Arc::clone(&self.db),
4737 self.startup_fork_cache_user.cache_lease.clone(),
4738 &parent_env,
4739 self.best_hash(),
4740 self.hardfork(),
4741 )
4742 .await;
4743 }
4744
4745 let mut db = self.db.write().await;
4746 for (account, info) in self.genesis.account_infos() {
4747 db.insert_account(account, info);
4748 }
4749
4750 db.insert_block_hash(U256::from(self.best_number()), self.best_hash());
4753
4754 if let Some(transitions) =
4755 self.ethereum_block_transitions(self.hardfork(), None, BlockExecutionKind::Complete)
4756 {
4757 if transitions.hardfork >= EthereumHardfork::Cancun {
4758 db.set_code(eip4788::BEACON_ROOTS_ADDRESS, eip4788::BEACON_ROOTS_CODE.clone())?;
4759 }
4760 if transitions.hardfork >= EthereumHardfork::Prague {
4761 db.set_code(
4762 eip2935::HISTORY_STORAGE_ADDRESS,
4763 eip2935::HISTORY_STORAGE_CODE.clone(),
4764 )?;
4765 db.set_code(
4766 eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
4767 eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_CODE.clone(),
4768 )?;
4769 db.set_code(
4770 eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
4771 eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_CODE.clone(),
4772 )?;
4773 }
4774 }
4775 self.genesis.apply_genesis_json_alloc(&mut **db)?;
4777 drop(db);
4778 self.apply_funded_accounts(&self.db).await?;
4779
4780 #[cfg(feature = "base")]
4782 if self.is_base() && !self.is_fork() {
4783 let upgrade = self.base_upgrade();
4784 Self::apply_base_genesis(&mut **self.db.write().await, upgrade)?;
4785 }
4786
4787 if self.networks.is_tempo() && !self.is_fork() {
4790 let chain_id = self.evm_env.read().cfg_env.chain_id;
4791 let timestamp = self.genesis.timestamp;
4792 let test_accounts: Vec<Address> = self.genesis.accounts.clone();
4793 let hardfork = self.tempo_hardfork();
4794 let mut db = self.db.write().await;
4795 crate::eth::backend::tempo::initialize_tempo_precompiles(
4796 &mut **db,
4797 chain_id,
4798 timestamp,
4799 &test_accounts,
4800 hardfork,
4801 )
4802 .map_err(|e| {
4803 tracing::error!(target: "backend", "failed to initialize Tempo precompiles: {e}");
4804 DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}")))
4805 })?;
4806 trace!(target: "backend", "initialized Tempo precompiles and fee tokens for {} accounts", test_accounts.len());
4807 }
4808
4809 trace!(target: "backend", "set genesis balances");
4810
4811 Ok(())
4812 }
4813
4814 async fn apply_fork_genesis(
4816 &self,
4817 db: Arc<AsyncRwLock<Box<dyn Db>>>,
4818 cache_lease: StagedForkCacheLease,
4819 parent_env: &EvmEnv,
4820 parent_hash: B256,
4821 hardfork: FoundryHardfork,
4822 ) -> Result<(), DatabaseError> {
4823 let user = StagedForkDbUser { db: Some(db), cache_lease };
4824 let mut genesis_accounts = JoinSet::new();
4825 for address in self.genesis.accounts.iter().copied() {
4826 let task_user = StagedForkDbUser {
4827 db: Some(Arc::clone(user.db())),
4828 cache_lease: user.cache_lease.clone(),
4829 };
4830
4831 genesis_accounts.spawn(async move {
4833 let db = task_user.db().read().await;
4834 let info = db.basic_ref(address)?.unwrap_or_default();
4835 Ok::<_, DatabaseError>((address, info))
4836 });
4837 }
4838
4839 let mut account_infos = Vec::with_capacity(self.genesis.accounts.len());
4840 while let Some(result) = genesis_accounts.join_next().await {
4841 match result {
4842 Ok(Ok(account)) => account_infos.push(account),
4843 Ok(Err(err)) => {
4844 genesis_accounts.shutdown().await;
4845 return Err(err);
4846 }
4847 Err(err) => {
4848 genesis_accounts.shutdown().await;
4849 return Err(DatabaseError::AnyRequest(Arc::new(eyre::eyre!(
4850 "fork genesis account task failed: {err}"
4851 ))));
4852 }
4853 }
4854 }
4855 let mut db_guard = user.db().write().await;
4856 for (address, mut info) in account_infos {
4857 info.balance = self.genesis.balance;
4858 db_guard.insert_account(address, info);
4859 }
4860 self.genesis.apply_genesis_json_alloc(&mut **db_guard)?;
4861 #[cfg(feature = "base")]
4865 if matches!(hardfork, FoundryHardfork::Base(upgrade) if upgrade >= BaseUpgrade::Denim) {
4866 ensure_base_time_predeploy(&mut **db_guard, false)?;
4867 self.ensure_fork_accepts_system_transactions(
4868 &**db_guard,
4869 parent_env,
4870 parent_hash,
4871 hardfork,
4872 )?;
4873 }
4874 #[cfg(not(feature = "base"))]
4875 let _ = (parent_env, parent_hash, hardfork);
4876 drop(db_guard);
4877 self.apply_funded_accounts(user.db()).await
4878 }
4879
4880 async fn apply_funded_accounts(
4882 &self,
4883 db: &Arc<AsyncRwLock<Box<dyn Db>>>,
4884 ) -> Result<(), DatabaseError> {
4885 let funded_accounts = self.node_config.read().await.funded_accounts.clone();
4886 let mut accounts = Vec::with_capacity(funded_accounts.len());
4887 for (address, balance) in funded_accounts {
4888 let mut info = db.read().await.basic_ref(address)?.unwrap_or_default();
4889 info.balance = balance;
4890 accounts.push((address, info));
4891 }
4892 let mut db = db.write().await;
4893 for (address, info) in accounts {
4894 db.insert_account(address, info);
4895 }
4896 Ok(())
4897 }
4898
4899 #[cfg(feature = "base")]
4901 fn apply_base_genesis(mut db: &mut dyn Db, upgrade: BaseUpgrade) -> Result<(), DatabaseError> {
4902 for address in [
4903 Predeploys::L1_BLOCK_INFO,
4904 Predeploys::SEQUENCER_FEE_VAULT,
4905 Predeploys::BASE_FEE_VAULT,
4906 Predeploys::L1_FEE_VAULT,
4907 Predeploys::OPERATOR_FEE_VAULT,
4908 Predeploys::BASE_TIME,
4909 ] {
4910 if db.basic(address)?.is_none() {
4911 db.insert_account(address, AccountInfo::default());
4912 }
4913 }
4914
4915 db.set_storage_at(
4916 Predeploys::L1_BLOCK_INFO,
4917 L1BlockInfo::L1_BASE_FEE_SLOT.into(),
4918 U256::from(DEFAULT_BASE_L1_BASE_FEE).into(),
4919 )?;
4920 db.set_storage_at(
4921 Predeploys::L1_BLOCK_INFO,
4922 L1BlockInfo::ECOTONE_L1_BLOB_BASE_FEE_SLOT.into(),
4923 B256::with_last_byte(1),
4924 )?;
4925 let mut l1_fee_scalars = [0u8; 32];
4926 l1_fee_scalars
4927 [L1BlockInfo::BASE_FEE_SCALAR_OFFSET..L1BlockInfo::BASE_FEE_SCALAR_OFFSET + 4]
4928 .copy_from_slice(&DEFAULT_BASE_L1_FEE_SCALAR.to_be_bytes());
4929 db.set_storage_at(
4930 Predeploys::L1_BLOCK_INFO,
4931 L1BlockInfo::ECOTONE_L1_FEE_SCALARS_SLOT.into(),
4932 B256::from(l1_fee_scalars),
4933 )?;
4934
4935 if upgrade >= BaseUpgrade::Canyon {
4936 let upgrades = ChainUpgrades::new([(BaseUpgrade::Canyon, ForkCondition::Timestamp(1))]);
4937 ensure_create2_deployer(upgrades, 1, &mut db)?;
4938 }
4939 if upgrade >= BaseUpgrade::Denim {
4940 ensure_base_time_predeploy(&mut *db, true)?;
4941 }
4942 if upgrade >= BaseUpgrade::Zenith {
4943 let mut upgrades = RollupConfig::default();
4945 upgrades.set_upgrade_activation_timestamp(BaseUpgrade::Zenith, 0);
4946 ensure_eip8130_system_accounts(upgrades, 1, &mut db)?;
4947 }
4948
4949 for address in base_code_sentinel_addresses(upgrade) {
4953 let mut account = db.basic(address)?.unwrap_or_default();
4954 if account.code.as_ref().is_none_or(|code| code.is_empty()) {
4955 let code =
4956 revm::state::Bytecode::new_legacy(Bytes::from_static(SYSTEM_PRECOMPILE_STUB));
4957 account.set_code(code);
4958 db.insert_account(address, account);
4959 }
4960 }
4961
4962 Ok(())
4963 }
4964
4965 #[allow(clippy::too_many_arguments)]
4967 fn populate_memory_db(
4968 db: &mut dyn Db,
4969 genesis: &GenesisConfig,
4970 funded_accounts: &HashMap<Address, U256>,
4971 hardfork: FoundryHardfork,
4972 chain_id: u64,
4973 is_tempo: bool,
4974 tempo_hardfork: Option<TempoHardfork>,
4975 genesis_hash: B256,
4976 install_create2_deployer: bool,
4977 ) -> Result<(), DatabaseError> {
4978 for (account, info) in genesis.account_infos() {
4979 db.insert_account(account, info);
4980 }
4981 db.insert_block_hash(U256::from(genesis.number), genesis_hash);
4982
4983 if let FoundryHardfork::Ethereum(hardfork) = hardfork {
4984 if hardfork >= EthereumHardfork::Cancun {
4985 db.set_code(eip4788::BEACON_ROOTS_ADDRESS, eip4788::BEACON_ROOTS_CODE.clone())?;
4986 }
4987 if hardfork >= EthereumHardfork::Prague {
4988 db.set_code(
4989 eip2935::HISTORY_STORAGE_ADDRESS,
4990 eip2935::HISTORY_STORAGE_CODE.clone(),
4991 )?;
4992 db.set_code(
4993 eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
4994 eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_CODE.clone(),
4995 )?;
4996 db.set_code(
4997 eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
4998 eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_CODE.clone(),
4999 )?;
5000 }
5001 }
5002
5003 genesis.apply_genesis_json_alloc(db)?;
5004 for (&address, &balance) in funded_accounts {
5005 let mut info = db.basic_ref(address)?.unwrap_or_default();
5006 info.balance = balance;
5007 db.insert_account(address, info);
5008 }
5009
5010 if is_tempo {
5011 let hardfork = tempo_hardfork.ok_or_else(|| {
5012 DatabaseError::AnyRequest(Arc::new(eyre::eyre!(
5013 "missing Tempo hardfork during memory reset"
5014 )))
5015 })?;
5016 crate::eth::backend::tempo::initialize_tempo_precompiles(
5017 db,
5018 chain_id,
5019 genesis.timestamp,
5020 &genesis.accounts,
5021 hardfork,
5022 )
5023 .map_err(|err| DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{err}"))))?;
5024 }
5025
5026 if install_create2_deployer {
5027 db.set_code(
5028 DEFAULT_CREATE2_DEPLOYER,
5029 Bytes::from_static(DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE),
5030 )?;
5031 }
5032
5033 Ok(())
5034 }
5035
5036 pub(crate) async fn prepare_fork_reset(
5038 &self,
5039 forking: Forking,
5040 serving_instance_id: B256,
5041 ) -> Result<StagedForkReset, BlockchainError> {
5042 let previous_fork = self.get_fork();
5043 let previous_source = self
5044 .last_fork_cache_source
5045 .read()
5046 .clone()
5047 .or_else(|| previous_fork.as_ref().and_then(ForkCacheSource::from_fork));
5048 let configured_rpc_urls = self.node_config.read().await.fork_urls.clone();
5049 let rpc_url_was_provided = forking.json_rpc_url.is_some();
5050 let target_rpc_urls = if let Some(url) = forking.json_rpc_url {
5051 vec![url]
5052 } else if !configured_rpc_urls.is_empty() {
5053 configured_rpc_urls
5054 } else {
5055 previous_fork
5056 .as_ref()
5057 .map(|fork| fork.config.read().fork_urls.clone())
5058 .filter(|urls| !urls.is_empty())
5059 .ok_or_else(|| {
5060 RpcError::invalid_params(
5061 "Forking not enabled and RPC URL not provided to start forking",
5062 )
5063 })?
5064 };
5065 let flush_old_cache = previous_fork.is_some();
5066 if flush_old_cache {
5067 self.db.write().await.maybe_flush_cache().map_err(BlockchainError::Internal)?;
5070 }
5071
5072 for _ in 0..3 {
5073 if let Some(staged) = self
5074 .stage_fork_reset(
5075 &target_rpc_urls,
5076 forking.block_number,
5077 serving_instance_id,
5078 previous_source.clone(),
5079 flush_old_cache,
5080 rpc_url_was_provided,
5081 )
5082 .await?
5083 {
5084 return Ok(staged);
5085 }
5086 }
5087 Err(BlockchainError::Internal(
5088 "fork endpoint changed while the replacement was being staged".to_string(),
5089 ))
5090 }
5091
5092 async fn stage_fork_reset(
5094 &self,
5095 target_rpc_urls: &[String],
5096 block_number: Option<u64>,
5097 serving_instance_id: B256,
5098 previous_source: Option<ForkCacheSource>,
5099 flush_old_cache: bool,
5100 rpc_url_was_provided: bool,
5101 ) -> Result<Option<StagedForkReset>, BlockchainError> {
5102 let target_rpc_url = target_rpc_urls.first().ok_or_else(|| {
5103 BlockchainError::Internal("at least one fork URL is required".to_string())
5104 })?;
5105 let mut staged_config = self.node_config.read().await.clone();
5106 if rpc_url_was_provided {
5107 staged_config.fork_chain_id = None;
5108 }
5109 let configured_endpoint_is_anvil = staged_config.fork_endpoint_is_anvil
5110 && staged_config.fork_urls.contains(target_rpc_url);
5111 let cached_endpoint_is_anvil = previous_source.as_ref().is_some_and(|source| {
5112 source.rpc_url == *target_rpc_url && source.endpoint_identity.is_authoritative()
5113 });
5114 staged_config.fork_endpoint_is_anvil =
5115 configured_endpoint_is_anvil || cached_endpoint_is_anvil;
5116 staged_config.fork_urls = target_rpc_urls.to_vec();
5117 staged_config.fork_choice = block_number.map(|number| ForkChoice::Block(number as i128));
5118 let mut staged_env = self.evm_env.read().clone();
5119 staged_config.apply_tempo_fork_beneficiary_default(&mut staged_env);
5120 let staged_fees = self.fees.detached();
5121 let (mut staged_db, staged_client_config) = staged_config
5122 .setup_fork_db_config_for_reset(
5123 target_rpc_url.clone(),
5124 &mut staged_env,
5125 &staged_fees,
5126 self.networks,
5127 )
5128 .await?;
5129 let cache_lease = StagedForkCacheLease::for_db(staged_db.inner());
5130 let cache_identity_changed = previous_source.as_ref().is_some_and(|source| {
5131 source.authoritative_identity_changed_at_same_url(
5132 target_rpc_url,
5133 staged_client_config.endpoint_identity,
5134 )
5135 });
5136 if cache_identity_changed {
5137 staged_db.clear_into_state_snapshot();
5138 staged_db.insert_block_hash(
5139 U256::from(staged_client_config.block_number),
5140 staged_client_config.block_hash,
5141 );
5142 }
5143 if !staged_config.no_bal && !staged_config.no_fork_node_info {
5144 staged_client_config.prefill_cache(staged_db.inner()).await;
5145 }
5146 let mut invalidated_cache_namespaces = Vec::new();
5147 if cache_identity_changed && !staged_config.no_storage_caching {
5148 if let Some(source) = &previous_source
5149 && let Some(namespace) = ForkCacheNamespace::new(
5150 source.endpoint_identity.source_chain_id,
5151 target_rpc_url,
5152 )
5153 {
5154 invalidated_cache_namespaces.push(namespace);
5155 }
5156 if let Some(namespace) =
5157 ForkCacheNamespace::new(staged_client_config.chain_id, target_rpc_url)
5158 && !invalidated_cache_namespaces.contains(&namespace)
5159 {
5160 invalidated_cache_namespaces.push(namespace);
5161 }
5162 }
5163 let discard_old_cached_state =
5164 cache_identity_changed && flush_old_cache && !invalidated_cache_namespaces.is_empty();
5165 let staged_db: Arc<AsyncRwLock<Box<dyn Db>>> =
5166 Arc::new(AsyncRwLock::new(Box::new(staged_db)));
5167 let staged_fork = ClientFork::new(staged_client_config.clone(), Arc::clone(&staged_db));
5168 let attempt = async {
5169 let target_networks =
5170 staged_client_config.endpoint_identity.network_profile.unwrap_or_default();
5171 if !staged_config.has_explicit_network_selection()
5172 && !self.networks.supports_fork_source(&target_networks)
5173 {
5174 return Err(RpcError::invalid_params(format!(
5175 "cannot reset Anvil across network families ({} -> {}); start a new \
5176 instance with matching network configuration",
5177 self.execution_profile_name(),
5178 target_networks.execution_profile_name()
5179 ))
5180 .into());
5181 }
5182 if staged_client_config.endpoint_identity.instance_id == Some(serving_instance_id) {
5183 return Err(
5184 RpcError::invalid_params("cannot reset Anvil to its own RPC endpoint").into()
5185 );
5186 }
5187 let fork_block = staged_fork
5188 .block_by_number(staged_fork.block_number())
5189 .await?
5190 .ok_or(BlockchainError::BlockNotFound)?;
5191 if fork_block.header.hash != staged_client_config.block_hash {
5192 return Ok(None);
5193 }
5194 self.apply_fork_genesis(
5195 Arc::clone(&staged_db),
5196 cache_lease.clone(),
5197 &staged_env,
5198 staged_client_config.block_hash,
5199 staged_client_config.hardfork.unwrap_or_else(|| staged_config.get_hardfork()),
5200 )
5201 .await?;
5202
5203 #[cfg(feature = "monad")]
5204 if self.is_monad() {
5205 monad::cache_fork_context(&staged_fork).await?;
5206 }
5207
5208 if !staged_config
5209 .fork_urls_match_context(
5210 target_rpc_urls,
5211 staged_client_config.endpoint_identity,
5212 staged_client_config.block_number,
5213 staged_client_config.block_hash,
5214 )
5215 .await?
5216 {
5217 return Ok(None);
5218 }
5219
5220 Ok(Some((fork_block, staged_fork.storage.read().clone())))
5221 }
5222 .await;
5223 drop(staged_fork);
5224 let (fork_block, staged_storage) = match attempt {
5225 Ok(Some(staged)) => staged,
5226 Ok(None) => {
5227 drop(staged_db);
5228 self.rollback_staged_fork_cache(cache_lease, flush_old_cache).await?;
5229 return Ok(None);
5230 }
5231 Err(err) => {
5232 drop(staged_db);
5233 self.rollback_staged_fork_cache(cache_lease, flush_old_cache).await?;
5234 return Err(err);
5235 }
5236 };
5237 let staged_db = match Arc::try_unwrap(staged_db) {
5238 Ok(staged_db) => staged_db.into_inner(),
5239 Err(staged_db) => {
5240 drop(staged_db);
5241 self.rollback_staged_fork_cache(cache_lease, flush_old_cache).await?;
5242 return Err(BlockchainError::Internal(
5243 "staged fork database still has active references".to_string(),
5244 ));
5245 }
5246 };
5247
5248 let fork = ClientFork::new(staged_client_config, Arc::clone(&self.db));
5249 *fork.storage.write() = staged_storage;
5250 Ok(Some(StagedForkReset {
5251 node_config: staged_config,
5252 db: staged_db,
5253 fees: staged_fees,
5254 evm_env: staged_env,
5255 fork,
5256 timestamp: fork_block.header.timestamp(),
5257 discard_old_cached_state,
5258 invalidated_cache_namespaces,
5259 flush_old_cache,
5260 cache_lease,
5261 }))
5262 }
5263
5264 async fn rollback_staged_fork_cache(
5265 &self,
5266 cache_lease: StagedForkCacheLease,
5267 restore_live_cache: bool,
5268 ) -> Result<(), BlockchainError> {
5269 let rollback_err = cache_lease.rollback().err();
5270 drop(cache_lease);
5273 let restore_err =
5274 if restore_live_cache { self.db.read().await.maybe_flush_cache().err() } else { None };
5275 match (rollback_err, restore_err) {
5276 (None, None) => Ok(()),
5277 (Some(err), None) => Err(err),
5278 (None, Some(err)) => Err(BlockchainError::Internal(format!(
5279 "failed to restore the live fork cache after staged reset rollback: {err}"
5280 ))),
5281 (Some(rollback), Some(restore)) => Err(BlockchainError::Internal(format!(
5282 "{rollback}; restoring the live fork cache also failed: {restore}"
5283 ))),
5284 }
5285 }
5286
5287 pub(crate) async fn commit_fork_reset(
5289 &self,
5290 staged: StagedForkReset,
5291 ) -> Result<(), BlockchainError> {
5292 let fork_block_number = staged.fork.block_number();
5293 let fork_block_hash = staged.fork.block_hash();
5294 let fork_total_difficulty = staged.fork.total_difficulty();
5295 let fork_cache_source = ForkCacheSource::from_fork(&staged.fork);
5296
5297 let mut node_config = self.node_config.write().await;
5300 let mut db = self.db.write().await;
5301 if staged.flush_old_cache {
5302 db.maybe_flush_cache().map_err(BlockchainError::Internal)?;
5303 }
5304 if let Err(err) =
5305 staged.invalidated_cache_namespaces.iter().try_for_each(ForkCacheNamespace::invalidate)
5306 {
5307 let StagedForkReset { db: staged_db, cache_lease, flush_old_cache, .. } = staged;
5310 drop(staged_db);
5311 let rollback_err = cache_lease.rollback().err();
5312 drop(cache_lease);
5313 let restore_err = if flush_old_cache { db.maybe_flush_cache().err() } else { None };
5314 let mut message = err.to_string();
5315 if let Some(err) = rollback_err {
5316 message.push_str(&format!("; staged cache rollback also failed: {err}"));
5317 }
5318 if let Some(err) = restore_err {
5319 message.push_str(&format!("; restoring the live fork cache also failed: {err}"));
5320 }
5321 return Err(BlockchainError::Internal(message));
5322 }
5323 if staged.discard_old_cached_state {
5324 db.clear_into_state_snapshot();
5325 }
5326 staged.cache_lease.disarm();
5327 let StagedForkReset {
5328 node_config: staged_node_config,
5329 db: staged_db,
5330 fees,
5331 evm_env,
5332 fork,
5333 timestamp,
5334 ..
5335 } = staged;
5336 *node_config = staged_node_config;
5337 *db = staged_db;
5338 self.fees.replace_from(&fees);
5339 *self.evm_env.write() = evm_env;
5340 *self.fork.write() = Some(fork);
5341 *self.last_fork_cache_source.write() = fork_cache_source;
5342 *self.blockchain.storage.write() =
5343 BlockchainStorage::forked(fork_block_number, fork_block_hash, fork_total_difficulty);
5344 self.states.write().clear();
5345 self.active_state_snapshots.lock().clear();
5346 self.time.reset(timestamp);
5347 self.cheats.clear_next_block_overrides();
5348
5349 trace!(target: "backend", "reset fork");
5350 Ok(())
5351 }
5352
5353 pub(crate) async fn prepare_memory_reset(
5355 &self,
5356 ) -> Result<StagedMemoryReset<N>, BlockchainError> {
5357 let reset_from_fork = self.is_fork();
5358 let genesis_timestamp = self.genesis.timestamp;
5359 let genesis_number = self.genesis.number;
5360 let mut staged_config = self.node_config.read().await.clone();
5361 staged_config.fork_source_chain_id = None;
5362 staged_config.fork_execution_chain_id = None;
5363 staged_config.fork_endpoint_is_anvil = false;
5364 staged_config.restore_fork_overrides();
5365 let local_chain_id = staged_config.get_chain_id();
5366 staged_config.update_wallet_chain_id(local_chain_id);
5367 let (
5368 local_gas_limit,
5369 local_base_fee,
5370 local_base_fee_is_explicit,
5371 local_gas_price,
5372 local_blob_params,
5373 local_blob_excess_gas_and_price,
5374 local_beneficiary,
5375 install_create2_deployer,
5376 ) = {
5377 (
5378 staged_config.gas_limit(),
5379 staged_config.get_base_fee(),
5380 staged_config.base_fee.is_some()
5381 || staged_config
5382 .genesis
5383 .as_ref()
5384 .is_some_and(|genesis| genesis.base_fee_per_gas.is_some()),
5385 staged_config.get_gas_price(),
5386 staged_config.get_blob_params(),
5387 staged_config.get_blob_excess_gas_and_price(),
5388 staged_config.genesis.as_ref().map(|genesis| genesis.coinbase).unwrap_or_default(),
5389 !staged_config.disable_default_create2_deployer,
5390 )
5391 };
5392
5393 let local_hardfork = staged_config.get_hardfork();
5394 let local_spec = SpecId::from(local_hardfork);
5395 let local_tempo_hardfork =
5396 self.networks.is_tempo().then(|| TempoHardfork::from(local_hardfork));
5397 let staged_fees = self.fees.detached();
5398 staged_fees.set_execution_rules(
5399 local_spec,
5400 self.networks.base_fee_params(genesis_timestamp),
5401 local_tempo_hardfork,
5402 );
5403 #[cfg(feature = "optimism")]
5404 if self.networks.is_optimism() {
5405 staged_fees.set_optimism_hardfork(local_hardfork.into());
5406 }
5407 staged_fees.set_blob_params(local_blob_params);
5408 staged_fees.set_blob_excess_gas_and_price(local_blob_excess_gas_and_price);
5409
5410 let preserve_live_base_fee =
5416 !reset_from_fork && !local_base_fee_is_explicit && local_tempo_hardfork.is_none();
5417 let genesis_base_fee =
5418 if preserve_live_base_fee { staged_fees.base_fee() } else { local_base_fee };
5419
5420 let mut staged_cfg = CfgEnv::new();
5421 staged_cfg.set_spec_and_mainnet_gas_params(local_spec);
5422 staged_cfg.chain_id = local_chain_id;
5423 staged_cfg.limit_contract_code_size = staged_config.code_size_limit;
5424 staged_cfg.disable_eip3607 = true;
5425 staged_cfg.disable_block_gas_limit = staged_config.disable_block_gas_limit;
5426 if !staged_config.enable_tx_gas_limit {
5427 staged_cfg.tx_gas_limit_cap = Some(u64::MAX);
5428 }
5429 if let Some(memory_limit) = staged_config.memory_limit {
5430 staged_cfg.memory_limit = memory_limit;
5431 }
5432 let staged_env = EvmEnv::new(
5433 staged_cfg,
5434 BlockEnv {
5435 number: U256::from(genesis_number),
5436 beneficiary: local_beneficiary,
5437 timestamp: U256::from(genesis_timestamp),
5438 gas_limit: local_gas_limit,
5439 basefee: genesis_base_fee,
5440 prevrandao: Some(B256::ZERO),
5441 ..Default::default()
5442 },
5443 );
5444
5445 let base_fee = staged_fees.is_eip1559().then_some(genesis_base_fee);
5446 let header = genesis_header(&staged_env, base_fee, genesis_timestamp, genesis_number);
5447 let staged_storage = BlockchainStorage::new(foundry_header(&self.networks, header));
5448
5449 staged_fees.set_base_fee(genesis_base_fee);
5453 if staged_fees.is_eip1559() {
5454 let next_base_fee = if staged_fees.tempo_hardfork().is_some() {
5455 staged_fees.get_next_block_base_fee_per_gas(
5456 0,
5457 staged_env.block_env.gas_limit,
5458 genesis_base_fee,
5459 )
5460 } else if preserve_live_base_fee {
5461 crate::eth::fees::INITIAL_BASE_FEE
5462 } else {
5463 genesis_base_fee
5464 };
5465 staged_fees.set_base_fee(next_base_fee);
5466 }
5467 staged_fees.set_gas_price(local_gas_price);
5468
5469 let mut staged_db: Box<dyn Db> = Box::new(StateRootDb::new(
5470 self.prune_state_history_config.is_state_history_supported(),
5471 ));
5472 Self::populate_memory_db(
5473 &mut *staged_db,
5474 &self.genesis,
5475 &staged_config.funded_accounts,
5476 local_hardfork,
5477 local_chain_id,
5478 self.networks.is_tempo(),
5479 local_tempo_hardfork,
5480 staged_storage.genesis_hash,
5481 install_create2_deployer,
5482 )?;
5483 #[cfg(feature = "base")]
5484 if self.is_base() {
5485 let upgrade = match local_hardfork {
5486 FoundryHardfork::Base(upgrade) => upgrade,
5487 _ => BaseUpgrade::Azul,
5488 };
5489 Self::apply_base_genesis(&mut *staged_db, upgrade)?;
5490 }
5491
5492 Ok(StagedMemoryReset {
5493 node_config: staged_config,
5494 db: staged_db,
5495 fees: staged_fees,
5496 evm_env: staged_env,
5497 hardfork: local_hardfork,
5498 storage: staged_storage,
5499 timestamp: genesis_timestamp,
5500 flush_old_cache: reset_from_fork,
5501 })
5502 }
5503
5504 pub(crate) async fn commit_memory_reset(
5506 &self,
5507 staged: StagedMemoryReset<N>,
5508 ) -> Result<(), BlockchainError> {
5509 let StagedMemoryReset {
5510 node_config,
5511 db,
5512 fees,
5513 evm_env,
5514 hardfork,
5515 storage,
5516 timestamp,
5517 flush_old_cache,
5518 } = staged;
5519 let mut live_config = self.node_config.write().await;
5520 let mut live_db = self.db.write().await;
5521 if flush_old_cache {
5522 live_db.maybe_flush_cache().map_err(BlockchainError::Internal)?;
5523 }
5524
5525 *live_config = node_config;
5526 *live_db = db;
5527 self.fees.replace_from(&fees);
5528 *self.evm_env.write() = evm_env;
5529 *self.hardfork.write() = hardfork;
5530 *self.fork.write() = None;
5531 *self.blockchain.storage.write() = storage;
5532 self.states.write().clear();
5533 self.active_state_snapshots.lock().clear();
5534 self.time.reset(timestamp);
5535 self.cheats.clear_next_block_overrides();
5536 trace!(target: "backend", "reset to fresh in-memory state");
5537 Ok(())
5538 }
5539
5540 pub async fn revert_state_snapshot(&self, id: U256) -> Result<bool, BlockchainError>
5542 where
5543 N::ReceiptEnvelope: TxReceipt<Log = alloy_primitives::Log>,
5544 {
5545 let Some((num, hash, fees, time, next_block)) =
5546 self.active_state_snapshots.lock().get(&id).map(|snapshot| {
5547 (
5548 snapshot.block_number,
5549 snapshot.block_hash,
5550 snapshot.fees,
5551 snapshot.time,
5552 snapshot.next_block,
5553 )
5554 })
5555 else {
5556 return Ok(false);
5557 };
5558 let block = self.block_by_hash(hash).await?.ok_or(BlockchainError::BlockNotFound)?;
5559 let removed_logs = self.removed_logs_since(num);
5560 if !self.db.write().await.revert_state(id, RevertStateSnapshotAction::RevertRemove) {
5561 return Ok(false);
5562 }
5563 {
5564 let mut snapshots = self.active_state_snapshots.lock();
5565 snapshots.remove(&id);
5566 snapshots.retain(|snapshot_id, _| *snapshot_id < id);
5567 }
5568 let removed_blocks = self.blockchain.storage.write().unwind_to(num, hash);
5570 let removed_hashes: Vec<_> = removed_blocks.iter().map(|b| b.header.hash_slow()).collect();
5571 self.states.write().remove_block_states(&removed_hashes);
5572 if !removed_logs.is_empty() {
5573 self.notify_on_removed_logs(removed_logs);
5574 }
5575
5576 self.time.restore(time);
5577 self.cheats.restore_next_block_overrides(&next_block);
5578
5579 {
5580 let mut env = self.evm_env.write();
5581 env.block_env = BlockEnv {
5582 number: self.evm_block_number(num),
5583 timestamp: U256::from(block.header.timestamp()),
5584 difficulty: block.header.difficulty(),
5585 prevrandao: Some(block.header.mix_hash().unwrap_or_default()),
5587 gas_limit: block.header.gas_limit(),
5588 beneficiary: env.block_env.beneficiary,
5590 basefee: env.block_env.basefee,
5591 ..Default::default()
5592 };
5593 }
5594 self.fees.restore(fees);
5595 Ok(true)
5596 }
5597
5598 pub async fn inspect_tx(
5600 &self,
5601 tx: Arc<PoolTransaction<FoundryTxEnvelope>>,
5602 ) -> Result<
5603 (InstructionResult, Option<Output>, u64, State, Vec<revm::primitives::Log>),
5604 BlockchainError,
5605 > {
5606 let db = self.db.read().await;
5607 let evm_env = self.next_evm_env(&**db)?;
5608 let mut inspector = self.build_inspector();
5609 #[cfg(feature = "monad")]
5610 let mut monad_context = self
5611 .is_monad()
5612 .then(|| self.monad_context_for_child_of(self.best_hash()))
5613 .transpose()?;
5614 #[cfg(not(feature = "monad"))]
5615 let mut monad_context = None;
5616 let (ResultAndState { result, state }, _) = self
5617 .transact_envelope_with_inspector_ref_and_context(
5618 &**db,
5619 &evm_env,
5620 &mut inspector,
5621 &tx.pending_transaction,
5622 monad_context.as_mut().map(next_monad_context),
5623 )?;
5624 let (exit_reason, gas_used, out, logs) = unpack_execution_result(result);
5625
5626 inspector.print_logs();
5627
5628 if self.print_traces {
5629 inspector.print_traces(self.call_trace_decoder());
5630 }
5631
5632 Ok((exit_reason, out, gas_used, state, logs))
5633 }
5634}
5635
5636impl<N: Network> Backend<N>
5637where
5638 N::ReceiptEnvelope: TxReceipt<Log = alloy_primitives::Log>,
5639{
5640 fn mined_logs_for_block(&self, filter: Filter, block: Block, block_hash: B256) -> Vec<Log> {
5642 let mut all_logs = Vec::new();
5643 let mut block_log_index = 0u32;
5644
5645 let storage = self.blockchain.storage.read();
5646
5647 for tx in block.body.transactions {
5648 let Some(tx) = storage.transactions.get(&tx.hash()) else {
5649 continue;
5650 };
5651
5652 let logs = tx.receipt.logs();
5653 let transaction_hash = tx.info.transaction_hash;
5654
5655 for log in logs {
5656 if filter.matches(log) {
5657 all_logs.push(Log {
5658 inner: log.clone(),
5659 block_hash: Some(block_hash),
5660 block_number: Some(block.header.number()),
5661 block_timestamp: Some(block.header.timestamp()),
5662 transaction_hash: Some(transaction_hash),
5663 transaction_index: Some(tx.info.transaction_index),
5664 log_index: Some(block_log_index as u64),
5665 removed: false,
5666 });
5667 }
5668 block_log_index += 1;
5669 }
5670 }
5671 all_logs
5672 }
5673
5674 fn removed_logs_since(&self, block_number: u64) -> Vec<Log> {
5681 let storage = self.blockchain.storage.read();
5682 let mut all_logs = Vec::new();
5683
5684 for num in (block_number + 1)..=storage.best_number {
5685 if let Some(hash) = storage.hashes.get(&num)
5686 && let Some(block) = storage.blocks.get(hash)
5687 {
5688 let mut block_log_index = 0u64;
5689 for tx in &block.body.transactions {
5690 if let Some(tx) = storage.transactions.get(&tx.hash()) {
5691 for log in tx.receipt.logs() {
5692 all_logs.push(Log {
5693 inner: log.clone(),
5694 block_hash: Some(*hash),
5695 block_number: Some(num),
5696 block_timestamp: Some(block.header.timestamp()),
5697 transaction_hash: Some(tx.info.transaction_hash),
5698 transaction_index: Some(tx.info.transaction_index),
5699 log_index: Some(block_log_index),
5700 removed: true,
5701 });
5702 block_log_index += 1;
5703 }
5704 }
5705 }
5706 }
5707 }
5708
5709 all_logs
5710 }
5711
5712 async fn logs_for_block(
5714 &self,
5715 filter: Filter,
5716 hash: B256,
5717 ) -> Result<Vec<Log>, BlockchainError> {
5718 if let Some(block) = self.blockchain.get_block_by_hash(&hash) {
5719 return Ok(self.mined_logs_for_block(filter, block, hash));
5720 }
5721
5722 if let Some(fork) = self.get_fork() {
5723 return Ok(fork.logs(&filter).await?);
5724 }
5725
5726 Err(BlockchainError::UnknownBlock)
5727 }
5728
5729 async fn logs_for_range(
5731 &self,
5732 filter: &Filter,
5733 mut from: u64,
5734 to: u64,
5735 ) -> Result<Vec<Log>, BlockchainError> {
5736 let mut all_logs = Vec::new();
5737
5738 if let Some(fork) = self.get_fork() {
5740 let to_on_fork = if fork.predates_fork(to) {
5741 to
5742 } else {
5743 fork.block_number()
5745 };
5746
5747 if fork.predates_fork_inclusive(from) {
5748 let filter = filter.clone().from_block(from).to_block(to_on_fork);
5750 all_logs = fork.logs(&filter).await?;
5751
5752 from = fork.block_number() + 1;
5754 }
5755 }
5756
5757 for number in from..=to {
5758 if let Some((block, hash)) = self.get_block_with_hash(number) {
5759 all_logs.extend(self.mined_logs_for_block(filter.clone(), block, hash));
5760 }
5761 }
5762
5763 Ok(all_logs)
5764 }
5765
5766 pub async fn logs(&self, filter: Filter) -> Result<Vec<Log>, BlockchainError> {
5768 trace!(target: "backend", "get logs [{:?}]", filter);
5769 if let Some(hash) = filter.get_block_hash() {
5770 self.logs_for_block(filter, hash).await
5771 } else {
5772 let best = self.best_number();
5773 let to_block =
5774 self.convert_block_number(filter.block_option.get_to_block().copied()).min(best);
5775 let from_block =
5776 self.convert_block_number(filter.block_option.get_from_block().copied());
5777 if from_block > best {
5778 return Err(BlockchainError::BlockOutOfRange(best, from_block));
5779 }
5780
5781 self.logs_for_range(&filter, from_block, to_block).await
5782 }
5783 }
5784
5785 pub fn mined_receipts(&self, hash: B256) -> Option<Vec<N::ReceiptEnvelope>> {
5787 let storage = self.blockchain.storage.read();
5788 let block = storage.blocks.get(&hash)?;
5789 block
5790 .body
5791 .transactions
5792 .iter()
5793 .map(|transaction| {
5794 storage.transactions.get(&transaction.hash()).map(|tx| tx.receipt.clone())
5795 })
5796 .collect()
5797 }
5798
5799 pub fn block_access_list_by_hash(&self, hash: B256) -> Option<BlockAccessList> {
5801 self.blockchain.storage.read().block_access_lists.get(&hash).cloned()
5802 }
5803
5804 pub fn block_access_list_by_number(&self, number: u64) -> Option<BlockAccessList> {
5806 let storage = self.blockchain.storage.read();
5807 storage.hashes.get(&number).and_then(|hash| storage.block_access_lists.get(hash)).cloned()
5808 }
5809}
5810
5811impl<N: Network> Backend<N>
5813where
5814 Self: TransactionValidator<FoundryTxEnvelope>,
5815 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
5816{
5817 pub async fn mine_block(
5822 &self,
5823 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
5824 ) -> Result<MinedBlockOutcome<FoundryTxEnvelope>, BlockchainError> {
5825 self.do_mine_block(pool_transactions).await
5826 }
5827
5828 pub(crate) async fn mine_block_locked(
5830 &self,
5831 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
5832 ) -> Result<MinedBlockOutcome<FoundryTxEnvelope>, BlockchainError> {
5833 self.do_mine_block_locked(pool_transactions).await
5834 }
5835
5836 pub(crate) async fn apply_fork_transaction_replay(
5838 &self,
5839 replay: ForkTransactionReplay,
5840 ) -> Result<()> {
5841 let source_chain_id = self.protocol_chain_id();
5842 let arbitrum_block_numbers = is_arbitrum(source_chain_id).then(|| {
5843 (
5844 replay.source_block.header().number(),
5845 arbitrum_replay_block_number(&replay.source_block),
5846 )
5847 });
5848 let prepared = prepare_fork_transaction_replay(replay, self.is_monad(), self.is_celo())?;
5849 let fallback_execution_chain_id = self
5850 .get_fork()
5851 .map(|fork| fork.execution_chain_id())
5852 .unwrap_or_else(|| self.chain_id().to());
5853 let execution_chain_id = prepared.execution_chain_id(fallback_execution_chain_id)?;
5854 let PreparedForkTransactionReplay { transactions, timestamp, parent_beacon_block_root } =
5855 prepared;
5856 eyre::ensure!(!transactions.is_empty(), "fork transaction replay prefix is empty");
5857 let next_timestamp = timestamp.checked_add(1).ok_or_else(|| {
5858 eyre::eyre!("fork transaction replay timestamp cannot be incremented")
5859 })?;
5860
5861 let _mining_guard = self.mining.lock().await;
5862 let current_base_fee = self.base_fee();
5863 let current_excess_blob_gas_and_price = self.excess_blob_gas_and_price();
5864 let mut evm_env = self.evm_env.read().clone();
5865 if evm_env.block_env.basefee == 0 {
5866 evm_env.cfg_env.disable_base_fee = true;
5867 }
5868
5869 let best_number = self.blockchain.storage.read().best_number;
5870 let block_number = best_number.saturating_add(1);
5871 evm_env.block_env.number = evm_env.block_env.number.saturating_add(U256::ONE);
5872 evm_env.block_env.basefee = current_base_fee;
5873 evm_env.block_env.blob_excess_gas_and_price = current_excess_blob_gas_and_price;
5874 evm_env.block_env.timestamp = U256::from(timestamp);
5875
5876 let best_hash = self.blockchain.storage.read().best_hash;
5877 let mut prevrandao_input = [0u8; 40];
5878 prevrandao_input[..32].copy_from_slice(best_hash.as_slice());
5879 prevrandao_input[32..].copy_from_slice(&block_number.to_le_bytes());
5880 evm_env.block_env.prevrandao = Some(
5881 self.cheats.take_next_block_prevrandao().unwrap_or_else(|| keccak256(prevrandao_input)),
5882 );
5883
5884 let mut replay_env = evm_env.clone();
5885 let arbitrum_rpc_block_number = arbitrum_block_numbers.map(|(rpc, evm)| {
5886 replay_env.block_env.number = evm;
5887 rpc
5888 });
5889 replay_env.cfg_env.chain_id = execution_chain_id;
5890 apply_chain_specific_tx_replay_env_changes_for_chain(&mut replay_env, source_chain_id);
5891 let inspector_tx_config = self.inspector_tx_config();
5892
5893 let scheduled_hardfork =
5894 source_hardfork(self.networks.execution_network(), source_chain_id, timestamp);
5895 #[cfg(feature = "monad")]
5896 let mut monad_replay = self
5897 .prepare_monad_fork_replay(
5898 source_chain_id,
5899 execution_chain_id,
5900 timestamp,
5901 best_hash,
5902 &transactions,
5903 )
5904 .await?;
5905 #[cfg(feature = "monad")]
5906 let hardfork = monad_replay
5907 .as_ref()
5908 .map(monad::ForkReplay::hardfork)
5909 .unwrap_or_else(|| scheduled_hardfork.unwrap_or_else(|| self.hardfork()));
5910 #[cfg(not(feature = "monad"))]
5911 let hardfork = scheduled_hardfork.unwrap_or_else(|| self.hardfork());
5912 if !self.is_optimism() && !self.is_tempo() {
5913 replay_env.cfg_env.spec = SpecId::from(hardfork);
5914 }
5915 let replay_spec = SpecId::from(hardfork);
5916 if !self.is_tempo()
5919 && replay_spec >= SpecId::CANCUN
5920 && replay_env.block_env.blob_excess_gas_and_price.is_none()
5921 {
5922 replay_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new(
5923 0,
5924 get_blob_base_fee_update_fraction_by_spec_id(replay_spec),
5925 ));
5926 }
5927
5928 #[cfg(feature = "monad")]
5929 let monad_context = monad_replay.as_mut().and_then(monad::ForkReplay::take_context);
5930 #[cfg(not(feature = "monad"))]
5931 let monad_context = None;
5932
5933 let (block_info, state_changes, block_hash) = {
5934 let db = self.db.read().await;
5935 let mut overlay = AnvilCacheDB::new(&**db, *replay_env.spec_id());
5936 let ExecutedHistoricalReplay {
5937 block_result,
5938 transactions,
5939 transaction_infos,
5940 state_changes,
5941 } = self.execute_with_replay_block_executor(
5942 &mut overlay,
5943 &replay_env,
5944 best_hash,
5945 arbitrum_rpc_block_number,
5946 hardfork,
5947 parent_beacon_block_root,
5948 &transactions,
5949 &inspector_tx_config,
5950 monad_context,
5951 )?;
5952 let state_root = overlay.maybe_state_root().unwrap_or_default();
5953 let block_info = self.build_block_info(
5954 &replay_env,
5955 best_hash,
5956 block_number,
5957 state_root,
5958 block_result,
5959 transactions,
5960 transaction_infos,
5961 parent_beacon_block_root,
5962 None,
5963 );
5964 let block_hash = block_info.block.header.hash_slow();
5965 (block_info, state_changes, block_hash)
5966 };
5967
5968 if self.prune_state_history_config.is_state_history_supported() {
5969 let state = self.db.read().await.current_state();
5970 self.states.write().insert(best_hash, state);
5971 }
5972
5973 {
5974 let mut db = self.db.write().await;
5975 for state in state_changes {
5976 db.commit(state);
5977 }
5978 db.insert_block_hash(U256::from(block_number), block_hash);
5979 }
5980
5981 let BlockInfo { block, transactions, receipts } = block_info;
5982 let header = block.header.clone();
5983 {
5984 let mut storage = self.blockchain.storage.write();
5985 storage.best_number = block_number;
5986 storage.best_hash = block_hash;
5987 if !self.is_eip3675() {
5988 storage.total_difficulty =
5989 storage.total_difficulty.saturating_add(header.difficulty);
5990 }
5991 storage.blocks.insert(block_hash, block);
5992 storage.hashes.insert(block_number, block_hash);
5993 #[cfg(feature = "monad")]
5994 if let Some(replay) = &mut monad_replay {
5995 replay.store_metadata(&mut storage, block_hash);
5996 }
5997 for (info, receipt) in transactions.into_iter().zip(receipts) {
5998 let mined_tx = MinedTransaction { info, receipt, block_hash, block_number };
5999 storage.transactions.insert(mined_tx.info.transaction_hash, mined_tx);
6000 }
6001
6002 if let Some(transaction_block_keeper) = self.transaction_block_keeper
6003 && storage.blocks.len() > transaction_block_keeper
6004 {
6005 let to_clear = block_number
6006 .saturating_sub(transaction_block_keeper.try_into().unwrap_or(u64::MAX));
6007 storage.remove_block_transactions_by_number(to_clear)
6008 }
6009 }
6010
6011 #[cfg(feature = "monad")]
6012 if let Some(replay) = &monad_replay {
6013 self.finalize_monad_fork_replay(replay, &mut evm_env);
6014 }
6015
6016 evm_env.block_env.difficulty = U256::ZERO;
6017 *self.evm_env.write() = evm_env;
6018 self.time.reset(timestamp);
6019 self.time.set_next_block_timestamp(next_timestamp)?;
6020
6021 #[cfg(feature = "optimism")]
6022 if self.is_optimism() {
6023 self.fees.set_optimism_base_fee_rules(header.extra_data());
6024 }
6025 let next_block_base_fee = self.fees.get_next_block_base_fee_from_header(&header);
6026 let next_block_excess_blob_gas = self.networks.next_block_blob_excess_gas(
6027 self.fees.blob_params(),
6028 header.excess_blob_gas.unwrap_or_default(),
6029 header.blob_gas_used.unwrap_or_default(),
6030 header.base_fee_per_gas.unwrap_or_default(),
6031 );
6032 self.fees.set_base_fee(next_block_base_fee);
6033 self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
6034 next_block_excess_blob_gas,
6035 get_blob_base_fee_update_fraction_by_spec_id(*self.evm_env.read().spec_id()),
6036 ));
6037 self.notify_on_new_block(header.into_inner(), block_hash);
6038
6039 Ok(())
6040 }
6041
6042 #[allow(clippy::too_many_arguments)]
6043 fn execute_with_replay_block_executor<DB>(
6044 &self,
6045 db: DB,
6046 evm_env: &EvmEnv,
6047 parent_hash: B256,
6048 arbitrum_rpc_block_number: Option<u64>,
6049 hardfork: FoundryHardfork,
6050 parent_beacon_block_root: Option<B256>,
6051 transactions: &[HistoricalReplayTransaction],
6052 inspector_tx_config: &InspectorTxConfig,
6053 #[cfg_attr(not(feature = "monad"), allow(unused_variables))] monad_context: Option<
6054 MonadReplayContext,
6055 >,
6056 ) -> Result<ExecutedHistoricalReplay>
6057 where
6058 DB: StateDB<Error = DatabaseError> + BalIndexedDatabase,
6059 {
6060 #[cfg(feature = "monad")]
6061 if self.is_monad() {
6062 return self.execute_with_monad_replay_block_executor(
6063 db,
6064 evm_env,
6065 parent_hash,
6066 hardfork.into(),
6067 transactions,
6068 inspector_tx_config,
6069 monad_context
6070 .ok_or_else(|| eyre::eyre!("Monad replay ancestor context is unavailable"))?,
6071 );
6072 }
6073
6074 let inspector = self.build_mining_inspector();
6075 let spec_id = *evm_env.spec_id();
6076 let transitions = self.ethereum_block_transitions(
6077 hardfork,
6078 parent_beacon_block_root,
6079 BlockExecutionKind::TransactionPrefix,
6080 );
6081
6082 macro_rules! execute {
6083 ($executor:expr) => {{
6084 let mut executor = $executor;
6085 executor
6086 .apply_pre_execution_changes()
6087 .wrap_err("failed to apply replay block-start transitions")?;
6088 let (stored_transactions, transaction_infos) =
6089 execute_historical_replay(&mut executor, transactions, inspector_tx_config)?;
6090 let state_changes = executor.take_state_changes();
6091 let (_, block_result) =
6092 executor.finish().wrap_err("failed to finish replay block execution")?;
6093 Ok(ExecutedHistoricalReplay {
6094 block_result,
6095 transactions: stored_transactions,
6096 transaction_infos,
6097 state_changes,
6098 })
6099 }};
6100 }
6101
6102 #[cfg(feature = "base")]
6103 if self.is_base() {
6104 let upgrade =
6105 self.base_upgrade_at_timestamp(evm_env.block_env.timestamp.saturating_to());
6106 let cfg =
6107 evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(BaseSpecId::new(upgrade));
6108 let activation_admin = self.base_activation_admin().or_else(|| {
6109 ChainConfig::activation_admin_address_for_upgrade_by_chain_id(cfg.chain_id, upgrade)
6110 });
6111 let base_env = EvmEnv::new(cfg, evm_env.block_env.clone());
6112 let mut evm = BaseEvmFactory::new(activation_admin)
6113 .create_evm_with_inspector(db, base_env, inspector);
6114 evm.ctx_mut().cfg.tx_chain_id_check = true;
6115 self.inject_precompiles(evm.precompiles_mut(), evm_env);
6116 if let Some(block_number) = arbitrum_rpc_block_number {
6117 self.inject_arbitrum_precompile_at_block(evm.precompiles_mut(), block_number);
6118 }
6119 let mut executor = AnvilBlockExecutor::new(evm, parent_hash, spec_id, transitions)
6120 .with_state_changes();
6121 executor.set_base_upgrade(upgrade);
6122 return execute!(executor);
6123 }
6124
6125 #[cfg(feature = "optimism")]
6126 if self.is_optimism() {
6127 let op_env = EvmEnv::new(
6128 evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(hardfork.into()),
6129 evm_env.block_env.clone(),
6130 );
6131 let mut evm =
6132 OpEvmFactory::<OpTx>::default().create_evm_with_inspector(db, op_env, inspector);
6133 self.inject_precompiles(evm.precompiles_mut(), evm_env);
6134 if let Some(block_number) = arbitrum_rpc_block_number {
6135 self.inject_arbitrum_precompile_at_block(evm.precompiles_mut(), block_number);
6136 }
6137 let mut executor = AnvilBlockExecutor::new(evm, parent_hash, spec_id, transitions)
6139 .with_state_changes();
6140 executor.set_optimism_hardfork(hardfork);
6141 return execute!(executor);
6142 }
6143
6144 if self.is_tempo() {
6145 let tempo_env = Self::build_tempo_evm_env(evm_env, self.tempo_hardfork());
6146 let mut evm =
6147 TempoEvmFactory::default().create_evm_with_inspector(db, tempo_env, inspector);
6148 self.inject_precompiles(evm.precompiles_mut(), evm_env);
6149 if let Some(block_number) = arbitrum_rpc_block_number {
6150 self.inject_arbitrum_precompile_at_block(evm.precompiles_mut(), block_number);
6151 }
6152 let executor = AnvilBlockExecutor::new(evm, parent_hash, spec_id, transitions)
6153 .with_state_changes();
6154 return execute!(executor);
6155 }
6156
6157 let mut evm =
6158 EthEvmFactory::default().create_evm_with_inspector(db, evm_env.clone(), inspector);
6159 self.inject_precompiles(evm.precompiles_mut(), evm_env);
6160 if let Some(block_number) = arbitrum_rpc_block_number {
6161 self.inject_arbitrum_precompile_at_block(evm.precompiles_mut(), block_number);
6162 }
6163 let executor =
6164 AnvilBlockExecutor::new(evm, parent_hash, spec_id, transitions).with_state_changes();
6165 execute!(executor)
6166 }
6167
6168 #[allow(clippy::too_many_arguments)]
6170 fn build_block_info(
6171 &self,
6172 evm_env: &EvmEnv,
6173 parent_hash: B256,
6174 number: u64,
6175 state_root: B256,
6176 block_result: BlockExecutionResult<FoundryReceiptEnvelope>,
6177 transactions: Vec<MaybeImpersonatedTransaction<FoundryTxEnvelope>>,
6178 transaction_infos: Vec<TransactionInfo>,
6179 parent_beacon_block_root: Option<B256>,
6180 block_access_list: Option<&BlockAccessList>,
6181 ) -> BlockInfo<N> {
6182 let spec_id = *evm_env.spec_id();
6183 let is_shanghai = spec_id >= SpecId::SHANGHAI;
6184 let is_cancun = spec_id >= SpecId::CANCUN;
6185 let is_prague = spec_id >= SpecId::PRAGUE;
6186
6187 let receipts_root = calculate_receipt_root(&block_result.receipts);
6188 let cumulative_blob_gas_used = is_cancun.then_some(block_result.blob_gas_used);
6189 let bloom = block_result.receipts.iter().fold(Bloom::default(), |mut b, r| {
6190 b.accrue_bloom(r.logs_bloom());
6191 b
6192 });
6193
6194 let header = Header {
6195 parent_hash,
6196 ommers_hash: Default::default(),
6197 beneficiary: evm_env.block_env.beneficiary,
6198 state_root,
6199 transactions_root: Default::default(),
6200 receipts_root,
6201 logs_bloom: bloom,
6202 difficulty: evm_env.block_env.difficulty,
6203 number,
6204 gas_limit: evm_env.block_env.gas_limit,
6205 gas_used: block_result.gas_used,
6206 timestamp: evm_env.block_env.timestamp.saturating_to(),
6207 extra_data: self.fees.base_fee_extra_data(),
6208 mix_hash: evm_env.block_env.prevrandao.unwrap_or_default(),
6209 nonce: Default::default(),
6210 base_fee_per_gas: (spec_id >= SpecId::LONDON).then_some(evm_env.block_env.basefee),
6211 parent_beacon_block_root: is_cancun
6212 .then(|| parent_beacon_block_root.unwrap_or_default()),
6213 blob_gas_used: cumulative_blob_gas_used,
6214 excess_blob_gas: if is_cancun { evm_env.block_env.blob_excess_gas() } else { None },
6215 withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
6216 requests_hash: is_prague.then(|| block_result.requests.requests_hash()),
6217 block_access_list_hash: block_access_list
6218 .map(|bal| compute_block_access_list_hash(bal.as_slice())),
6219 slot_number: None,
6220 };
6221
6222 let block = create_block(foundry_header(&self.networks, header), transactions);
6223 BlockInfo { block, transactions: transaction_infos, receipts: block_result.receipts }
6224 }
6225
6226 async fn do_mine_block(
6227 &self,
6228 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
6229 ) -> Result<MinedBlockOutcome<FoundryTxEnvelope>, BlockchainError> {
6230 let _mining_guard = self.mining.lock().await;
6231 self.do_mine_block_locked(pool_transactions).await
6232 }
6233
6234 async fn do_mine_block_locked(
6236 &self,
6237 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
6238 ) -> Result<MinedBlockOutcome<FoundryTxEnvelope>, BlockchainError> {
6239 trace!(target: "backend", "creating new block with {} transactions", pool_transactions.len());
6240
6241 let (outcome, header, block_hash) = {
6242 let current_base_fee = self.base_fee();
6243 let current_excess_blob_gas_and_price = self.excess_blob_gas_and_price();
6244
6245 let mut evm_env = self.evm_env.read().clone();
6246 #[cfg(feature = "monad")]
6247 let execution_chain_id = evm_env.cfg_env.chain_id;
6248 let hardfork = self.hardfork();
6249
6250 if evm_env.block_env.basefee == 0 {
6251 evm_env.cfg_env.disable_base_fee = true;
6254 }
6255
6256 let block_number = self.blockchain.storage.read().best_number.saturating_add(1);
6257
6258 evm_env.block_env.number = evm_env.block_env.number.saturating_add(U256::ONE);
6261
6262 evm_env.block_env.basefee = current_base_fee;
6263 evm_env.block_env.blob_excess_gas_and_price = current_excess_blob_gas_and_price;
6264
6265 let best_hash = self.blockchain.storage.read().best_hash;
6266
6267 let mut input = [0u8; 40];
6268 input[..32].copy_from_slice(best_hash.as_slice());
6269 input[32..].copy_from_slice(&block_number.to_le_bytes());
6270 let next_block = self.cheats.next_block_overrides();
6275 evm_env.block_env.prevrandao =
6276 Some(next_block.prevrandao.value.unwrap_or_else(|| keccak256(input)));
6277 let parent_beacon_block_root =
6278 Some(next_block.parent_beacon_block_root.value.unwrap_or_default());
6279
6280 let (
6281 db_guard,
6282 block_info,
6283 included,
6284 stale,
6285 invalid,
6286 not_yet_valid,
6287 block_hash,
6288 parent_state,
6289 next_block_base_fee,
6290 next_block_blob_fees,
6291 block_access_list,
6292 ) = {
6293 let mut db = self.db.write().await;
6294
6295 let pending_timestamp = self
6299 .prepare_block_timestamp(&**db, evm_env.block_env.timestamp.saturating_to())?;
6300 let block_timestamp = pending_timestamp.timestamp;
6301 evm_env.block_env.timestamp = U256::from(block_timestamp);
6302
6303 let mut mining_evm_env = evm_env.clone();
6306 if pool_transactions.iter().any(|tx| tx.is_replay) {
6307 apply_chain_specific_tx_replay_env_changes_for_chain(
6308 &mut mining_evm_env,
6309 self.protocol_chain_id(),
6310 );
6311 }
6312
6313 #[cfg(feature = "base")]
6314 let (protocol_transactions, system_transaction_count) = {
6315 let mut transactions = self.base_system_transactions(
6316 &**db,
6317 block_number,
6318 block_timestamp,
6319 best_hash,
6320 )?;
6321 let system_transaction_count = transactions.len();
6322 transactions.extend(pool_transactions);
6323 (transactions, system_transaction_count)
6324 };
6325 #[cfg(not(feature = "base"))]
6326 let protocol_transactions = pool_transactions;
6327
6328 let inspector_tx_config = self.inspector_tx_config();
6329 let gas_config = self.pool_tx_gas_config(&mining_evm_env);
6330
6331 let mut candidate_db = AnvilCacheDB::new(&**db, *mining_evm_env.spec_id());
6332 if matches!(
6333 hardfork,
6334 FoundryHardfork::Ethereum(hardfork) if hardfork >= EthereumHardfork::Amsterdam
6335 ) {
6336 candidate_db.enable_bal_recording();
6337 }
6338 let (pool_result, block_result) = self.execute_with_block_executor(
6339 &mut candidate_db,
6340 &mining_evm_env,
6341 best_hash,
6342 hardfork,
6343 parent_beacon_block_root,
6344 BlockExecutionKind::Complete,
6345 &protocol_transactions,
6346 &gas_config,
6347 &inspector_tx_config,
6348 &|pool_tx, account| {
6349 let validation_env =
6350 if pool_tx.is_replay { &mining_evm_env } else { &evm_env };
6351 self.validate_mining_pool_transaction_for(pool_tx, account, validation_env)
6352 },
6353 )?;
6354 let block_access_list = candidate_db.take_block_access_list();
6355
6356 #[cfg(feature = "base")]
6357 base::validate_system_transactions(
6358 &protocol_transactions[..system_transaction_count],
6359 &pool_result,
6360 )?;
6361
6362 #[cfg_attr(not(feature = "base"), allow(unused_mut))]
6363 let mut included = pool_result.included;
6364 #[cfg(feature = "base")]
6367 included.drain(..system_transaction_count);
6368 let stale = pool_result.stale;
6369 let invalid = pool_result.invalid;
6370 let not_yet_valid = pool_result.not_yet_valid;
6371
6372 let CacheDB { cache, db: _ } = candidate_db.0;
6373 let parent_state = self
6374 .prune_state_history_config
6375 .is_state_history_supported()
6376 .then(|| db.current_state());
6377 commit_cache(&mut **db, cache)?;
6378 let state_root = db.maybe_state_root().unwrap_or_default();
6379 let block_info = self.build_block_info(
6380 &mining_evm_env,
6381 best_hash,
6382 block_number,
6383 state_root,
6384 block_result,
6385 pool_result.txs,
6386 pool_result.tx_info,
6387 parent_beacon_block_root,
6388 block_access_list.as_ref(),
6389 );
6390
6391 let block_hash = block_info.block.header.hash_slow();
6393 db.insert_block_hash(U256::from(block_info.block.header.number()), block_hash);
6394 self.time.commit_next_timestamp(pending_timestamp);
6395 self.cheats.consume_next_block_overrides(&next_block);
6396
6397 let header = &block_info.block.header;
6398 let next_block_base_fee = self.fees.get_next_block_base_fee_from_header(header);
6399 let next_block_excess_blob_gas = self.networks.next_block_blob_excess_gas(
6400 self.fees.blob_params(),
6401 header.excess_blob_gas.unwrap_or_default(),
6402 header.blob_gas_used.unwrap_or_default(),
6403 header.base_fee_per_gas.unwrap_or_default(),
6404 );
6405 let next_block_blob_fees = BlobExcessGasAndPrice::new(
6406 next_block_excess_blob_gas,
6407 self.fees.blob_params().update_fraction as u64,
6408 );
6409
6410 (
6411 db,
6412 block_info,
6413 included,
6414 stale,
6415 invalid,
6416 not_yet_valid,
6417 block_hash,
6418 parent_state,
6419 next_block_base_fee,
6420 next_block_blob_fees,
6421 block_access_list,
6422 )
6423 };
6424
6425 let BlockInfo { block, transactions, receipts } = block_info;
6427
6428 let header = block.header.clone();
6429 #[cfg(feature = "monad")]
6430 let monad_participants = self.is_monad().then(|| {
6431 let tx_envs = included
6432 .iter()
6433 .map(|pool_tx| {
6434 build_tx_env_for_pending::<FoundryTxEnvelope, TxEnv>(
6435 &pool_tx.pending_transaction,
6436 self.cheats(),
6437 )
6438 })
6439 .collect::<Vec<_>>();
6440 foundry_evm::core::evm::monad_block_participants(&tx_envs)
6441 });
6442
6443 if let Some(parent_state) = parent_state {
6444 self.states.write().insert(best_hash, parent_state);
6445 }
6446
6447 trace!(
6448 target: "backend",
6449 "Mined block {} with {} tx {:?}",
6450 block_number,
6451 transactions.len(),
6452 transactions.iter().map(|tx| tx.transaction_hash).collect::<Vec<_>>()
6453 );
6454 #[cfg(test)]
6455 let mining_commit_hook = { self.mining_commit_hook.lock().clone() };
6456 #[cfg(test)]
6457 if let Some(hook) = mining_commit_hook {
6458 hook.reached.notify_one();
6459 hook.resume.notified().await;
6460 }
6461 let mut storage = self.blockchain.storage.write();
6462 storage.best_number = block_number;
6464 storage.best_hash = block_hash;
6465 if !self.is_eip3675() {
6468 storage.total_difficulty =
6469 storage.total_difficulty.saturating_add(header.difficulty);
6470 }
6471
6472 storage.blocks.insert(block_hash, block);
6473 storage.hashes.insert(block_number, block_hash);
6474 if let Some(block_access_list) = block_access_list {
6475 storage.block_access_lists.insert(block_hash, block_access_list);
6476 }
6477 #[cfg(feature = "monad")]
6478 if let Some(participants) = monad_participants {
6479 monad::store_block_metadata(
6480 &mut storage,
6481 block_hash,
6482 participants,
6483 execution_chain_id,
6484 hardfork,
6485 );
6486 }
6487
6488 node_info!("");
6489 for (info, receipt) in transactions.into_iter().zip(receipts) {
6491 node_info!(" Transaction: {:?}", info.transaction_hash);
6493 if let Some(contract) = &info.contract_address {
6494 node_info!(" Contract created: {contract}");
6495 }
6496 node_info!(" Gas used: {}", info.gas_used);
6497 if !info.exit.is_ok() {
6498 let r = RevertDecoder::new().decode(
6499 info.out.as_ref().map(|b| &b[..]).unwrap_or_default(),
6500 Some(info.exit),
6501 );
6502 node_info!(" Error: reverted with: {r}");
6503 }
6504 node_info!("");
6505
6506 let mined_tx = MinedTransaction { info, receipt, block_hash, block_number };
6507 storage.transactions.insert(mined_tx.info.transaction_hash, mined_tx);
6508 }
6509
6510 if let Some(transaction_block_keeper) = self.transaction_block_keeper
6512 && storage.blocks.len() > transaction_block_keeper
6513 {
6514 let to_clear = block_number
6515 .saturating_sub(transaction_block_keeper.try_into().unwrap_or(u64::MAX));
6516 storage.remove_block_transactions_by_number(to_clear)
6517 }
6518
6519 evm_env.block_env.difficulty = U256::ZERO;
6526 *self.evm_env.write() = evm_env;
6527 self.fees.set_next_block_fees(next_block_base_fee, next_block_blob_fees);
6528 drop(storage);
6529 drop(db_guard);
6530
6531 self.time.mark_block_created();
6532
6533 let timestamp = utc_from_secs(header.timestamp);
6534
6535 node_info!(" Block Number: {}", block_number);
6536 node_info!(" Block Hash: {:?}", block_hash);
6537 if timestamp.year() > 9999 {
6538 node_info!(" Block Time: {:?}\n", timestamp.to_rfc3339());
6540 } else {
6541 node_info!(" Block Time: {:?}\n", timestamp.to_rfc2822());
6542 }
6543
6544 let outcome =
6545 MinedBlockOutcome { block_number, included, stale, invalid, not_yet_valid };
6546
6547 (outcome, header, block_hash)
6548 };
6549 self.notify_on_new_block(header.into_inner(), block_hash);
6551
6552 Ok(outcome)
6553 }
6554
6555 pub async fn reorg(
6559 &self,
6560 depth: u64,
6561 tx_pairs: HashMap<u64, Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>>,
6562 _mining_guard: &tokio::sync::MutexGuard<'_, ()>,
6563 ) -> Result<(), BlockchainError> {
6564 for i in 0..depth {
6566 let to_be_mined = tx_pairs.get(&i).cloned().unwrap_or_else(Vec::new);
6567 let outcome = self.do_mine_block_locked(to_be_mined).await?;
6568 node_info!(
6569 " Mined reorg block number {}. With {} valid txs and with invalid {} txs",
6570 outcome.block_number,
6571 outcome.included.len(),
6572 outcome.invalid.len()
6573 );
6574 }
6575
6576 Ok(())
6577 }
6578
6579 pub async fn pending_block(
6583 &self,
6584 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
6585 ) -> Result<BlockInfo<N>, BlockchainError> {
6586 self.with_pending_block(pool_transactions, |_, block| block).await
6587 }
6588
6589 pub async fn with_pending_block<F, T>(
6593 &self,
6594 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
6595 f: F,
6596 ) -> Result<T, BlockchainError>
6597 where
6598 F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockInfo<N>) -> T,
6599 {
6600 let db = self.db.read().await;
6601 let evm_env = self.next_evm_env(&**db)?;
6602
6603 let mut cache_db = AnvilCacheDB::new(&*db, *evm_env.spec_id());
6604
6605 let parent_hash = self.blockchain.storage.read().best_hash;
6606 let block_number = self.best_number().saturating_add(1);
6607
6608 #[cfg(feature = "base")]
6609 let (pool_transactions, system_transaction_count) = {
6610 let mut transactions = self.base_system_transactions(
6611 &**db,
6612 block_number,
6613 evm_env.block_env.timestamp.saturating_to(),
6614 parent_hash,
6615 )?;
6616 let system_transaction_count = transactions.len();
6617 transactions.extend(pool_transactions);
6618 (transactions, system_transaction_count)
6619 };
6620 let parent_beacon_block_root = Some(
6623 self.cheats.next_block_overrides().parent_beacon_block_root.value.unwrap_or_default(),
6624 );
6625
6626 let inspector_tx_config = self.inspector_tx_config();
6627 let gas_config = self.pool_tx_gas_config(&evm_env);
6628
6629 let (pool_result, block_result) = self.execute_with_block_executor(
6630 &mut cache_db,
6631 &evm_env,
6632 parent_hash,
6633 self.hardfork(),
6634 parent_beacon_block_root,
6635 BlockExecutionKind::Complete,
6636 &pool_transactions,
6637 &gas_config,
6638 &inspector_tx_config,
6639 &|pool_tx, account| {
6640 self.validate_pool_transaction_for(&pool_tx.pending_transaction, account, &evm_env)
6641 },
6642 )?;
6643
6644 #[cfg(feature = "base")]
6645 base::validate_system_transactions(
6646 &pool_transactions[..system_transaction_count],
6647 &pool_result,
6648 )?;
6649
6650 let cache_db = cache_db.0;
6652
6653 let state_root = cache_db.maybe_state_root().unwrap_or_default();
6654 let block_info = self.build_block_info(
6655 &evm_env,
6656 parent_hash,
6657 block_number,
6658 state_root,
6659 block_result,
6660 pool_result.txs,
6661 pool_result.tx_info,
6662 parent_beacon_block_root,
6663 None,
6664 );
6665
6666 Ok(f(Box::new(cache_db), block_info))
6667 }
6668
6669 pub async fn get_fee_token_balance(
6674 &self,
6675 token: Address,
6676 account: Address,
6677 ) -> Result<U256, BlockchainError> {
6678 let mut calldata = vec![0x70, 0xa0, 0x82, 0x31];
6680 calldata.extend_from_slice(&[0u8; 12]);
6682 calldata.extend_from_slice(account.as_slice());
6683
6684 let request = WithOtherFields::new(TransactionRequest {
6685 from: Some(Address::ZERO),
6686 to: Some(TxKind::Call(token)),
6687 input: calldata.into(),
6688 ..Default::default()
6689 });
6690
6691 let fee_details = FeeDetails::zero();
6692 let (exit, out, _, _) = self.call(request, fee_details, None, Default::default()).await?;
6693
6694 if exit != InstructionResult::Return && exit != InstructionResult::Stop {
6696 return Ok(U256::ZERO);
6698 }
6699
6700 match out {
6702 Some(Output::Call(data)) if data.len() >= 32 => Ok(U256::from_be_slice(&data[..32])),
6703 _ => Ok(U256::ZERO),
6704 }
6705 }
6706
6707 pub async fn tempo_fee_payer(&self) -> Option<Address> {
6711 if !self.is_tempo() {
6712 return None;
6713 }
6714 self.node_config.read().await.tempo_fee_payer_address()
6715 }
6716
6717 pub async fn tempo_user_fee_token(&self, account: Address) -> Result<Address, BlockchainError> {
6721 let calldata = IFeeManager::userTokensCall { user: account }.abi_encode();
6722
6723 let request = WithOtherFields::new(TransactionRequest {
6724 from: Some(Address::ZERO),
6725 to: Some(TxKind::Call(TIP_FEE_MANAGER_ADDRESS)),
6726 input: calldata.into(),
6727 ..Default::default()
6728 });
6729
6730 let (exit, out, _, _) =
6731 self.call(request, FeeDetails::zero(), None, Default::default()).await?;
6732
6733 let token = if exit == InstructionResult::Return
6734 && let Some(Output::Call(data)) = out
6735 {
6736 IFeeManager::userTokensCall::abi_decode_returns(&data).unwrap_or(Address::ZERO)
6737 } else {
6738 Address::ZERO
6739 };
6740
6741 if token.is_zero() {
6742 return Ok(foundry_evm::core::tempo::PATH_USD_ADDRESS);
6743 }
6744 Ok(token)
6745 }
6746
6747 pub async fn call(
6753 &self,
6754 request: WithOtherFields<TransactionRequest>,
6755 fee_details: FeeDetails,
6756 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
6757 overrides: EvmOverrides,
6758 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
6759 self.with_database_at_and_context(block_request, |state, mut block, monad_context| {
6760 let block_number = block.number;
6761 let (exit, out, gas, state) = {
6762 let mut cache_db = CacheDB::new(state);
6763 if let Some(state_overrides) = overrides.state {
6764 apply_state_overrides(state_overrides.into_iter().collect(), &mut cache_db)?;
6765 }
6766 if let Some(block_overrides) = overrides.block {
6767 cache_db.apply_block_overrides(*block_overrides, &mut block);
6768 }
6769 self.call_with_state_and_context(
6770 &cache_db,
6771 request,
6772 fee_details,
6773 block,
6774 monad_context,
6775 )
6776 }?;
6777 trace!(target: "backend", "call return {:?} out: {:?} gas {} on block {}", exit, out, gas, block_number);
6778 Ok((exit, out, gas, state))
6779 })
6780 .await
6781 }
6782
6783 pub async fn call_with_tracing(
6784 &self,
6785 request: WithOtherFields<TransactionRequest>,
6786 fee_details: FeeDetails,
6787 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
6788 opts: GethDebugTracingCallOptions,
6789 ) -> Result<GethTrace, BlockchainError> {
6790 let GethDebugTracingCallOptions {
6791 tracing_options,
6792 block_overrides,
6793 state_overrides,
6794 tx_index,
6795 } = opts;
6796
6797 if let Some(tx_index) = tx_index {
6798 return self
6799 .call_with_tracing_at_tx_index(
6800 request,
6801 fee_details,
6802 block_request,
6803 tx_index,
6804 tracing_options,
6805 state_overrides,
6806 block_overrides,
6807 )
6808 .await;
6809 }
6810
6811 self.with_database_at_and_context(block_request, |state, block, monad_context| {
6812 let cache_db = CacheDB::new(state);
6813 self.trace_call_with_state(
6814 request,
6815 fee_details,
6816 block,
6817 cache_db,
6818 tracing_options,
6819 state_overrides,
6820 block_overrides,
6821 monad_context,
6822 None,
6823 )
6824 })
6825 .await
6826 }
6827
6828 #[allow(clippy::too_many_arguments)]
6829 async fn call_with_tracing_at_tx_index(
6830 &self,
6831 request: WithOtherFields<TransactionRequest>,
6832 fee_details: FeeDetails,
6833 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
6834 tx_index: u64,
6835 tracing_options: GethDebugTracingOptions,
6836 state_overrides: Option<StateOverride>,
6837 block_overrides: Option<BlockOverrides>,
6838 ) -> Result<GethTrace, BlockchainError> {
6839 let tx_index = usize::try_from(tx_index).map_err(|_| {
6840 BlockchainError::RpcError(RpcError::invalid_params(format!(
6841 "tx_index {tx_index} does not fit in usize"
6842 )))
6843 })?;
6844 let block_number = match block_request {
6845 Some(BlockRequest::Pending(_)) => {
6846 return Err(BlockchainError::RpcError(RpcError::invalid_params(
6847 "tx_index is not supported for pending blocks".to_string(),
6848 )));
6849 }
6850 Some(BlockRequest::Number(number)) => number,
6851 None => self.best_number(),
6852 };
6853 let block_id = BlockId::number(block_number);
6854
6855 if let Some(block) = self.get_block(block_id) {
6856 return self.mined_trace_call_at_tx_index(
6857 request,
6858 fee_details,
6859 &block,
6860 tx_index,
6861 tracing_options,
6862 state_overrides,
6863 block_overrides,
6864 );
6865 }
6866
6867 if let Some(fork) = self.get_fork()
6868 && fork.predates_fork_inclusive(block_number)
6869 {
6870 let opts = GethDebugTracingCallOptions {
6871 tracing_options,
6872 state_overrides,
6873 block_overrides,
6874 tx_index: Some(tx_index as u64),
6875 };
6876 return Ok(fork.debug_trace_call(request, block_id, opts).await?);
6877 }
6878
6879 Err(BlockchainError::BlockNotFound)
6880 }
6881
6882 #[allow(clippy::too_many_arguments)]
6883 fn mined_trace_call_at_tx_index(
6884 &self,
6885 request: WithOtherFields<TransactionRequest>,
6886 fee_details: FeeDetails,
6887 block: &Block,
6888 tx_index: usize,
6889 tracing_options: GethDebugTracingOptions,
6890 state_overrides: Option<StateOverride>,
6891 block_overrides: Option<BlockOverrides>,
6892 ) -> Result<GethTrace, BlockchainError> {
6893 let transaction_count = block.body.transactions.len();
6894 if tx_index >= transaction_count {
6895 return Err(BlockchainError::RpcError(RpcError::invalid_params(format!(
6896 "tx_index {tx_index} out of bounds for block with {transaction_count} transactions"
6897 ))));
6898 }
6899
6900 let trace = |parent_state: &StateDb| -> Result<GethTrace, BlockchainError> {
6901 let db = Box::new(parent_state) as Box<dyn MaybeFullDatabase + '_>;
6902 let (mut cache_db, evm_env, hardfork) = self.prepare_block_replay_with_db(block, db)?;
6903 self.replay_mined_transaction_prefix(
6904 &mut cache_db,
6905 &evm_env,
6906 hardfork,
6907 block,
6908 tx_index,
6909 )?;
6910 self.trace_call_with_state(
6911 request,
6912 fee_details,
6913 evm_env.block_env.clone(),
6914 cache_db,
6915 tracing_options,
6916 state_overrides,
6917 block_overrides,
6918 self.active_monad_context_before_mined_transaction(block, tx_index)?,
6919 Some((evm_env, hardfork)),
6920 )
6921 };
6922
6923 let read_guard = self.states.upgradable_read();
6924 if let Some(state) = read_guard.get_state(&block.header.parent_hash) {
6925 trace(state)
6926 } else {
6927 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
6928 let state = write_guard
6929 .get_on_disk_state(&block.header.parent_hash)
6930 .ok_or(BlockchainError::HistoricalStateUnavailable(block.header.number()))?;
6931 trace(state)
6932 }
6933 }
6934
6935 #[allow(clippy::too_many_arguments)]
6936 fn trace_call_with_state(
6937 &self,
6938 request: WithOtherFields<TransactionRequest>,
6939 fee_details: FeeDetails,
6940 mut block: BlockEnv,
6941 mut cache_db: CacheDB<Box<dyn MaybeFullDatabase + '_>>,
6942 tracing_options: GethDebugTracingOptions,
6943 state_overrides: Option<StateOverride>,
6944 block_overrides: Option<BlockOverrides>,
6945 mut monad_context: Option<MonadReplayContext>,
6946 historical_execution: Option<(EvmEnv, FoundryHardfork)>,
6947 ) -> Result<GethTrace, BlockchainError> {
6948 let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options;
6949 let block_number = block.number;
6950 let base_evm_env = historical_execution.as_ref().map(|(evm_env, _)| evm_env);
6951 let hardfork = historical_execution
6952 .as_ref()
6953 .map(|(_, hardfork)| *hardfork)
6954 .unwrap_or_else(|| self.hardfork());
6955
6956 if let Some(state_overrides) = state_overrides {
6957 apply_state_overrides(state_overrides, &mut cache_db)?;
6958 }
6959 if let Some(block_overrides) = block_overrides {
6960 cache_db.apply_block_overrides(block_overrides, &mut block);
6961 }
6962
6963 if let Some(tracer) = tracer {
6964 return match tracer {
6965 GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
6966 GethDebugBuiltInTracerType::CallTracer => {
6967 let call_config = call_config_from_tracer_config(tracer_config)
6968 .map_err(|e| RpcError::invalid_params(e.to_string()))?;
6969
6970 let mut inspector = self.build_inspector().with_tracing_config(
6971 TracingInspectorConfig::from_geth_call_config(&call_config),
6972 );
6973
6974 let PreparedCall { evm_env, tx_env, .. } = self
6975 .prepare_call_env_from_base(
6976 &cache_db,
6977 request,
6978 fee_details,
6979 block,
6980 base_evm_env,
6981 )?;
6982 let ResultAndState { result, state: _ } = self
6983 .transact_call_with_inspector_ref_at_hardfork(
6984 &cache_db,
6985 &evm_env,
6986 &mut inspector,
6987 tx_env,
6988 monad_context.as_mut().map(next_monad_context),
6989 hardfork,
6990 )?;
6991
6992 inspector.print_logs();
6993 if self.print_traces {
6994 inspector.print_traces(self.call_trace_decoder());
6995 }
6996
6997 let tracing_inspector = inspector.tracer.expect("tracer disappeared");
6998
6999 Ok(tracing_inspector
7000 .into_geth_builder()
7001 .geth_call_traces(call_config, result.tx_gas_used())
7002 .into())
7003 }
7004 GethDebugBuiltInTracerType::PreStateTracer => {
7005 let pre_state_config = tracer_config
7006 .into_pre_state_config()
7007 .map_err(|e| RpcError::invalid_params(e.to_string()))?;
7008
7009 let mut inspector = TracingInspector::new(
7010 TracingInspectorConfig::from_geth_prestate_config(&pre_state_config),
7011 );
7012
7013 let PreparedCall { evm_env, tx_env, .. } = self
7014 .prepare_call_env_from_base(
7015 &cache_db,
7016 request,
7017 fee_details,
7018 block,
7019 base_evm_env,
7020 )?;
7021 let result = self.transact_call_with_inspector_ref_at_hardfork(
7022 &cache_db,
7023 &evm_env,
7024 &mut inspector,
7025 tx_env,
7026 monad_context.as_mut().map(next_monad_context),
7027 hardfork,
7028 )?;
7029
7030 Ok(inspector
7031 .into_geth_builder()
7032 .geth_prestate_traces(&result, &pre_state_config, cache_db)?
7033 .into())
7034 }
7035 GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()),
7036 GethDebugBuiltInTracerType::FourByteTracer
7037 | GethDebugBuiltInTracerType::MuxTracer
7038 | GethDebugBuiltInTracerType::FlatCallTracer
7039 | GethDebugBuiltInTracerType::Erc7562Tracer
7040 | GethDebugBuiltInTracerType::StateGasTracer => {
7041 Err(RpcError::invalid_params("unsupported tracer type").into())
7042 }
7043 },
7044 #[cfg(not(feature = "js-tracer"))]
7045 GethDebugTracerType::JsTracer(_) => {
7046 Err(RpcError::invalid_params("unsupported tracer type").into())
7047 }
7048 #[cfg(feature = "js-tracer")]
7049 GethDebugTracerType::JsTracer(code) => {
7050 let config = tracer_config.into_json();
7051 let mut inspector =
7052 revm_inspectors::tracing::js::JsInspector::new(code, config)
7053 .map_err(|err| BlockchainError::Message(err.to_string()))?;
7054
7055 let PreparedCall { evm_env, tx_env, .. } = self.prepare_call_env_from_base(
7056 &cache_db,
7057 request,
7058 fee_details,
7059 block.clone(),
7060 base_evm_env,
7061 )?;
7062 let result = self.transact_call_with_inspector_ref_at_hardfork(
7063 &cache_db,
7064 &evm_env,
7065 &mut inspector,
7066 tx_env.clone(),
7067 monad_context.as_mut().map(next_monad_context),
7068 hardfork,
7069 )?;
7070 let res = inspector
7071 .json_result(result, tx_env.base(), &block, &cache_db)
7072 .map_err(|err| BlockchainError::Message(err.to_string()))?;
7073
7074 Ok(GethTrace::JS(res))
7075 }
7076 };
7077 }
7078
7079 let mut inspector = self
7081 .build_inspector()
7082 .with_tracing_config(TracingInspectorConfig::from_geth_config(&config));
7083
7084 let PreparedCall { evm_env, tx_env, .. } =
7085 self.prepare_call_env_from_base(&cache_db, request, fee_details, block, base_evm_env)?;
7086 let ResultAndState { result, state: _ } = self
7087 .transact_call_with_inspector_ref_at_hardfork(
7088 &cache_db,
7089 &evm_env,
7090 &mut inspector,
7091 tx_env,
7092 monad_context.as_mut().map(next_monad_context),
7093 hardfork,
7094 )?;
7095
7096 let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
7097
7098 let tracing_inspector = inspector.tracer.expect("tracer disappeared");
7099 let return_value = out.as_ref().map(|o| o.data()).cloned().unwrap_or_default();
7100
7101 trace!(target: "backend", ?exit_reason, ?out, %gas_used, %block_number, "trace call");
7102
7103 let res = tracing_inspector
7104 .into_geth_builder()
7105 .geth_traces(gas_used, return_value, config)
7106 .into();
7107
7108 Ok(res)
7109 }
7110
7111 pub async fn with_database_at<F, T>(
7113 &self,
7114 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
7115 f: F,
7116 ) -> Result<T, BlockchainError>
7117 where
7118 F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockEnv) -> T,
7119 {
7120 let block_number = match block_request {
7121 Some(BlockRequest::Pending(pool_transactions)) => {
7122 let result = self
7123 .with_pending_block(pool_transactions, |state, block| {
7124 let block = block.block;
7125 f(state, self.block_env_from_header(&block.header))
7126 })
7127 .await?;
7128 return Ok(result);
7129 }
7130 Some(BlockRequest::Number(bn)) => Some(BlockNumber::Number(bn)),
7131 None => None,
7132 };
7133 let block_number = self.convert_block_number(block_number);
7134 let current_number = self.best_number();
7135
7136 if block_number > current_number {
7138 return Err(BlockchainError::BlockOutOfRange(current_number, block_number));
7139 }
7140
7141 let _head_guard = if block_number == current_number {
7145 let db = self.db.read().await;
7146 if self.best_number() == block_number
7147 || !self.prune_state_history_config.is_state_history_supported()
7148 {
7149 let block = self.evm_env.read().block_env.clone();
7150 return Ok(f(Box::new(&**db), block));
7151 }
7152 Some(db)
7153 } else {
7154 None
7155 };
7156
7157 if let Some((block_hash, block)) = self
7158 .block_by_number(BlockNumber::Number(block_number))
7159 .await?
7160 .map(|block| (block.header.hash, block))
7161 {
7162 let read_guard = self.states.upgradable_read();
7163 if let Some(state_db) = read_guard.get_state(&block_hash) {
7164 return Ok(f(Box::new(state_db), self.block_env_from_header(&block.header)));
7165 }
7166
7167 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
7168 if let Some(state) = write_guard.get_on_disk_state(&block_hash) {
7169 return Ok(f(Box::new(state), self.block_env_from_header(&block.header)));
7170 }
7171 }
7172
7173 warn!(target: "backend", "Not historic state found for block={}", block_number);
7174 Err(BlockchainError::BlockOutOfRange(self.best_number(), block_number))
7175 }
7176
7177 pub(crate) async fn with_database_at_and_context<F, T>(
7179 &self,
7180 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
7181 f: F,
7182 ) -> Result<T, BlockchainError>
7183 where
7184 F: FnOnce(
7185 Box<dyn MaybeFullDatabase + '_>,
7186 BlockEnv,
7187 Option<MonadReplayContext>,
7188 ) -> Result<T, BlockchainError>,
7189 {
7190 let block_number = match block_request {
7191 Some(BlockRequest::Pending(pool_transactions)) => {
7192 return self
7193 .with_pending_block(pool_transactions, |state, block_info| {
7194 let context = self.active_monad_context_before_mined_transaction(
7195 &block_info.block,
7196 block_info.block.body.transactions.len(),
7197 )?;
7198 let block_env = self.block_env_from_header(&block_info.block.header);
7199 f(state, block_env, context)
7200 })
7201 .await?;
7202 }
7203 Some(BlockRequest::Number(number)) => Some(BlockNumber::Number(number)),
7204 None => None,
7205 };
7206 let block_number = self.convert_block_number(block_number);
7207 let current_number = self.best_number();
7208
7209 if block_number > current_number {
7210 return Err(BlockchainError::BlockOutOfRange(current_number, block_number));
7211 }
7212
7213 #[cfg(feature = "monad")]
7214 let context = if self.is_monad() {
7215 Some(self.monad_context_for_child_of_block_number(block_number).await?)
7216 } else {
7217 None
7218 };
7219 #[cfg(not(feature = "monad"))]
7220 let context = None;
7221
7222 let _head_guard = if block_number == current_number {
7224 let db = self.db.read().await;
7225 if self.best_number() == block_number
7226 || !self.prune_state_history_config.is_state_history_supported()
7227 {
7228 let block = self.evm_env.read().block_env.clone();
7229 return f(Box::new(&**db), block, context);
7230 }
7231 Some(db)
7232 } else {
7233 None
7234 };
7235
7236 if let Some((block_hash, block)) = self
7237 .block_by_number(BlockNumber::Number(block_number))
7238 .await?
7239 .map(|block| (block.header.hash, block))
7240 {
7241 let read_guard = self.states.upgradable_read();
7242 if let Some(state_db) = read_guard.get_state(&block_hash) {
7243 return f(Box::new(state_db), self.block_env_from_header(&block.header), context);
7244 }
7245
7246 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
7247 if let Some(state) = write_guard.get_on_disk_state(&block_hash) {
7248 return f(Box::new(state), self.block_env_from_header(&block.header), context);
7249 }
7250 }
7251
7252 warn!(target: "backend", "Not historic state found for block={}", block_number);
7253 Err(BlockchainError::BlockOutOfRange(self.best_number(), block_number))
7254 }
7255
7256 pub async fn storage_at(
7257 &self,
7258 address: Address,
7259 index: U256,
7260 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
7261 ) -> Result<B256, BlockchainError> {
7262 self.with_database_at(block_request, |db, _| {
7263 trace!(target: "backend", "get storage for {:?} at {:?}", address, index);
7264 let val = db.storage_ref(address, index)?;
7265 Ok(val.into())
7266 })
7267 .await?
7268 }
7269
7270 pub async fn tempo_nonce(
7271 &self,
7272 caller: Address,
7273 nonce_key: U256,
7274 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
7275 ) -> Result<u64, BlockchainError> {
7276 self.with_database_at(block_request, |state, _| tempo_nonce(&state, caller, nonce_key))
7277 .await?
7278 }
7279
7280 pub async fn storage_values(
7282 &self,
7283 requests: HashMap<Address, Vec<B256>>,
7284 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
7285 ) -> Result<HashMap<Address, Vec<B256>>, BlockchainError> {
7286 self.with_database_at(block_request, |db, _| {
7287 trace!(target: "backend", "get storage values for {} addresses", requests.len());
7288 let mut result: HashMap<Address, Vec<B256>> = HashMap::default();
7289 for (address, slots) in &requests {
7290 let mut values = Vec::with_capacity(slots.len());
7291 for slot in slots {
7292 let val = db.storage_ref(*address, (*slot).into())?;
7293 values.push(val.into());
7294 }
7295 result.insert(*address, values);
7296 }
7297 Ok(result)
7298 })
7299 .await?
7300 }
7301
7302 pub async fn get_code(
7307 &self,
7308 address: Address,
7309 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
7310 ) -> Result<Bytes, BlockchainError> {
7311 self.with_database_at(block_request, |db, _| self.get_code_with_state(&db, address)).await?
7312 }
7313
7314 pub async fn get_balance(
7318 &self,
7319 address: Address,
7320 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
7321 ) -> Result<U256, BlockchainError> {
7322 self.with_database_at(block_request, |db, _| self.get_balance_with_state(db, address))
7323 .await?
7324 }
7325
7326 pub async fn get_account_at_block(
7327 &self,
7328 address: Address,
7329 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
7330 ) -> Result<TrieAccount, BlockchainError> {
7331 self.with_database_at(block_request, |block_db, _| {
7332 let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
7333 let account = db.get(&address).cloned().unwrap_or_default();
7334 let storage_root = storage_root(&account.storage);
7335 let code_hash = account.info.code_hash();
7336 let balance = account.info.balance;
7337 let nonce = account.info.nonce;
7338 Ok(TrieAccount { balance, nonce, code_hash, storage_root })
7339 })
7340 .await?
7341 }
7342
7343 pub async fn get_account_info_at_block(
7344 &self,
7345 address: Address,
7346 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
7347 ) -> Result<RpcAccountInfo, BlockchainError> {
7348 self.with_database_at(block_request, |db, _| {
7349 let account = db.basic_ref(address)?.unwrap_or_default();
7350 let code = self.get_code_with_state(&db, address)?;
7351 Ok(RpcAccountInfo { balance: account.balance, nonce: account.nonce, code })
7352 })
7353 .await?
7354 }
7355
7356 pub async fn get_nonce(
7360 &self,
7361 address: Address,
7362 block_request: BlockRequest<FoundryTxEnvelope>,
7363 ) -> Result<u64, BlockchainError> {
7364 if let BlockRequest::Pending(pool_transactions) = &block_request
7365 && let Some(value) = get_pool_transactions_nonce(pool_transactions, address)
7366 {
7367 return Ok(value);
7368 }
7369 let final_block_request = match block_request {
7370 BlockRequest::Pending(_) => BlockRequest::Number(self.best_number()),
7371 BlockRequest::Number(bn) => BlockRequest::Number(bn),
7372 };
7373
7374 self.with_database_at(Some(final_block_request), |db, _| {
7375 trace!(target: "backend", "get nonce for {:?}", address);
7376 Ok(db.basic_ref(address)?.unwrap_or_default().nonce)
7377 })
7378 .await?
7379 }
7380
7381 #[cfg(feature = "js-tracer")]
7383 pub async fn trace_tx_with_js_tracer(
7384 &self,
7385 hash: B256,
7386 code: String,
7387 opts: GethDebugTracingOptions,
7388 ) -> Result<GethTrace, BlockchainError> {
7389 let GethDebugTracingOptions { tracer_config, .. } = opts;
7390 let config = tracer_config.into_json();
7391 let inspector = revm_inspectors::tracing::js::JsInspector::new(code, config)
7392 .map_err(|err| BlockchainError::Message(err.to_string()))?;
7393 let trace = self.replay_tx_with_inspector(
7394 hash,
7395 inspector,
7396 |result, cache_db, mut inspector, tx_env, evm_env| {
7397 inspector
7398 .json_result(
7399 result,
7400 &alloy_evm::IntoTxEnv::into_tx_env(tx_env),
7401 evm_env.block_env(),
7402 &cache_db,
7403 )
7404 .map_err(|e| BlockchainError::Message(e.to_string()))
7405 },
7406 )??;
7407 Ok(GethTrace::JS(trace))
7408 }
7409
7410 pub async fn prove_account_at(
7414 &self,
7415 address: Address,
7416 keys: Vec<B256>,
7417 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
7418 ) -> Result<AccountProof, BlockchainError> {
7419 let block_number = block_request.as_ref().map(|r| r.block_number());
7420
7421 self.with_database_at(block_request, |block_db, _| {
7422 trace!(target: "backend", "get proof for {:?} at {:?}", address, block_number);
7423 let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
7424 let account = db.get(&address).cloned().unwrap_or_default();
7425
7426 let mut builder = HashBuilder::default()
7427 .with_proof_retainer(ProofRetainer::new(vec![Nibbles::unpack(keccak256(address))]));
7428
7429 for (key, account) in trie_accounts(db) {
7430 builder.add_leaf(key, &account);
7431 }
7432
7433 let _ = builder.root();
7434
7435 let proof = builder
7436 .take_proof_nodes()
7437 .into_nodes_sorted()
7438 .into_iter()
7439 .map(|(_, v)| v)
7440 .collect();
7441 let (storage_hash, storage_proofs) = prove_storage(&account.storage, &keys);
7442
7443 let account_proof = AccountProof {
7444 address,
7445 balance: account.info.balance,
7446 nonce: account.info.nonce,
7447 code_hash: account.info.code_hash(),
7448 storage_hash,
7449 account_proof: proof,
7450 storage_proof: keys
7451 .into_iter()
7452 .zip(storage_proofs)
7453 .map(|(key, proof)| {
7454 let storage_key: U256 = key.into();
7455 let value = account.storage.get(&storage_key).copied().unwrap_or_default();
7456 StorageProof { key: JsonStorageKey::Hash(key), value, proof }
7457 })
7458 .collect(),
7459 };
7460
7461 Ok(account_proof)
7462 })
7463 .await?
7464 }
7465}
7466
7467impl<N: Network> Backend<N>
7468where
7469 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
7470{
7471 pub async fn trace_transaction_opcode_gas(
7473 &self,
7474 hash: B256,
7475 ) -> Result<Option<TransactionOpcodeGas>, BlockchainError> {
7476 match self.replay_tx_with_inspector(
7477 hash,
7478 OpcodeGasInspector::new(),
7479 move |_, _, inspector, _, _| TransactionOpcodeGas {
7480 transaction_hash: hash,
7481 opcode_gas: inspector.opcode_gas_iter().collect(),
7482 },
7483 ) {
7484 Ok(trace) => Ok(Some(trace)),
7485 Err(BlockchainError::TransactionNotFound) => {
7486 if let Some(fork) = self.get_fork() {
7487 return Ok(fork.trace_transaction_opcode_gas(hash).await?);
7488 }
7489
7490 Ok(None)
7491 }
7492 Err(err) => Err(err),
7493 }
7494 }
7495
7496 pub async fn trace_block_opcode_gas(
7498 &self,
7499 block_id: BlockId,
7500 ) -> Result<Option<BlockOpcodeGas>, BlockchainError> {
7501 if let Some((block, block_hash)) = self.get_block_with_hash(block_id) {
7502 return self.mined_block_opcode_gas(&block, block_hash).map(Some);
7503 }
7504
7505 if let Some(fork) = self.get_fork() {
7506 let number = self.ensure_block_number(Some(block_id)).await?;
7507 if fork.predates_fork_inclusive(number) {
7508 let resolved = match block_id {
7512 BlockId::Hash(_) => block_id,
7513 _ => BlockId::number(number),
7514 };
7515 return Ok(fork.trace_block_opcode_gas(resolved).await?);
7516 }
7517 }
7518
7519 Err(BlockchainError::BlockNotFound)
7520 }
7521
7522 fn mined_block_opcode_gas(
7523 &self,
7524 block: &Block,
7525 block_hash: B256,
7526 ) -> Result<BlockOpcodeGas, BlockchainError> {
7527 if block.header.number() == self.genesis_number() {
7529 return Ok(BlockOpcodeGas {
7530 block_hash,
7531 block_number: block.header.number(),
7532 transactions: Vec::new(),
7533 });
7534 }
7535
7536 let parent_hash = block.header.parent_hash;
7537
7538 let trace = |parent_state: &StateDb| -> Result<Vec<TransactionOpcodeGas>, BlockchainError> {
7539 let (mut cache_db, evm_env, hardfork) =
7540 self.prepare_block_replay(block, parent_state)?;
7541 let mut transactions = Vec::with_capacity(block.body.transactions.len());
7542 let monad_context = self.active_monad_context_for_mined_block(block)?;
7543
7544 for tx_envelope in &block.body.transactions {
7545 let mut inspector = OpcodeGasInspector::new();
7546 let pending_tx = self.pending_mined_transaction(tx_envelope.clone())?;
7547 let transaction_context =
7548 monad_execution_context_at(monad_context.as_ref(), transactions.len());
7549 let (result, _) = self.replay_envelope_with_inspector_ref_and_context(
7550 &cache_db,
7551 &evm_env,
7552 &mut inspector,
7553 &pending_tx,
7554 EnvelopeExecution::replay(transaction_context, hardfork),
7555 )?;
7556
7557 transactions.push(TransactionOpcodeGas {
7558 transaction_hash: tx_envelope.hash(),
7559 opcode_gas: inspector.opcode_gas_iter().collect(),
7560 });
7561
7562 cache_db.commit(result.state);
7563 }
7564
7565 Ok(transactions)
7566 };
7567
7568 let read_guard = self.states.upgradable_read();
7569 let transactions = if let Some(state) = read_guard.get_state(&parent_hash) {
7570 trace(state)?
7571 } else {
7572 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
7573 let state = write_guard
7574 .get_on_disk_state(&parent_hash)
7575 .ok_or(BlockchainError::HistoricalStateUnavailable(block.header.number()))?;
7576 trace(state)?
7577 };
7578
7579 Ok(BlockOpcodeGas { block_hash, block_number: block.header.number(), transactions })
7580 }
7581
7582 pub async fn debug_execution_witness(
7601 &self,
7602 block: BlockNumber,
7603 ) -> Result<ExecutionWitness, BlockchainError> {
7604 let number = self.convert_block_number(Some(block));
7605 let best = self.best_number();
7606 if number > best {
7607 return Err(BlockchainError::BlockOutOfRange(best, number));
7608 }
7609 let Some(parent) = number.checked_sub(1) else {
7610 return Err(BlockchainError::Message(
7611 "genesis block has no parent state to build a witness from".to_string(),
7612 ));
7613 };
7614
7615 let mut headers = Vec::new();
7616 for ancestor in (number.saturating_sub(BLOCKHASH_HISTORY)..number).rev() {
7617 let Some(block) = self.get_block(ancestor) else { break };
7618 headers.push(alloy_rlp::encode(&block.header).into());
7619 }
7620
7621 self.with_database_at(Some(BlockRequest::Number(parent)), |state, _| {
7622 let Some(accounts) = state.maybe_full_db() else {
7623 return Err(BlockchainError::Message(
7624 "debug_executionWitness is not supported while forking".to_string(),
7625 ));
7626 };
7627
7628 let (_, nodes) = state_trie_witness(&accounts);
7629 let mut codes = Vec::new();
7630 let mut seen_codes = B256Set::default();
7631 let mut keys = Vec::new();
7632 for (address, account) in &accounts {
7633 keys.push(Bytes::copy_from_slice(address.as_slice()));
7634 for slot in account.storage.keys() {
7635 keys.push(Bytes::from(slot.to_be_bytes::<32>()));
7636 }
7637 if !account.info.is_empty_code_hash() && seen_codes.insert(account.info.code_hash())
7638 {
7639 let code = match &account.info.code {
7640 Some(code) => code.original_bytes(),
7641 None => state.code_by_hash_ref(account.info.code_hash())?.original_bytes(),
7642 };
7643 codes.push(code);
7644 }
7645 }
7646 keys.sort_unstable();
7647 keys.dedup();
7648
7649 Ok(ExecutionWitness { state: nodes, codes, keys, headers })
7650 })
7651 .await?
7652 }
7653
7654 pub async fn debug_account_info_at(
7656 &self,
7657 block_id: BlockId,
7658 tx_index: Index,
7659 address: Address,
7660 ) -> Result<Option<RpcAccountInfo>, BlockchainError> {
7661 if let Some((block, _)) = self.get_block_with_hash(block_id) {
7662 return self.mined_debug_account_info_at(&block, tx_index, address).map(Some);
7663 }
7664
7665 if let Some(fork) = self.get_fork() {
7666 let number = self.ensure_block_number(Some(block_id)).await?;
7667 if fork.predates_fork_inclusive(number) {
7668 let resolved = match block_id {
7672 BlockId::Hash(_) => block_id,
7673 _ => BlockId::number(number),
7674 };
7675 return Ok(fork.debug_account_info_at(resolved, tx_index, address).await?);
7676 }
7677 }
7678
7679 Err(BlockchainError::BlockNotFound)
7680 }
7681
7682 fn mined_debug_account_info_at(
7683 &self,
7684 block: &Block,
7685 tx_index: Index,
7686 address: Address,
7687 ) -> Result<RpcAccountInfo, BlockchainError> {
7688 let tx_index = tx_index.0;
7689 let transaction_count = block.body.transactions.len();
7690 if tx_index >= transaction_count {
7691 return Err(BlockchainError::RpcError(RpcError::invalid_params(format!(
7692 "tx_index {tx_index} out of bounds for block with {transaction_count} transactions"
7693 ))));
7694 }
7695
7696 let trace = |parent_state: &StateDb| -> Result<RpcAccountInfo, BlockchainError> {
7697 let (mut cache_db, evm_env, hardfork) =
7698 self.prepare_block_replay_with_db(block, Box::new(parent_state))?;
7699 self.replay_mined_transaction_prefix(
7700 &mut cache_db,
7701 &evm_env,
7702 hardfork,
7703 block,
7704 tx_index + 1,
7705 )?;
7706 let account = revm::DatabaseRef::basic_ref(&cache_db, address)?.unwrap_or_default();
7707 let code = self.get_code_with_state(&cache_db, address)?;
7708 Ok(RpcAccountInfo { balance: account.balance, nonce: account.nonce, code })
7709 };
7710
7711 let read_guard = self.states.upgradable_read();
7712 if let Some(state) = read_guard.get_state(&block.header.parent_hash) {
7713 trace(state)
7714 } else {
7715 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
7716 let state = write_guard
7717 .get_on_disk_state(&block.header.parent_hash)
7718 .ok_or(BlockchainError::HistoricalStateUnavailable(block.header.number()))?;
7719 trace(state)
7720 }
7721 }
7722
7723 pub async fn rollback(
7728 &self,
7729 common_block: Block,
7730 _mining_guard: &tokio::sync::MutexGuard<'_, ()>,
7731 ) -> Result<(), BlockchainError> {
7732 let hash = common_block.header.hash_slow();
7733
7734 let (common_state, replace_accounts) = {
7736 let return_state_or_throw_err =
7737 |db: Option<&StateDb>| -> Result<AddressMap<DbAccount>, BlockchainError> {
7738 let state_db = db.ok_or(BlockchainError::DataUnavailable)?;
7739 let db_full =
7740 state_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
7741 Ok(db_full.clone())
7742 };
7743
7744 let read_guard = self.states.upgradable_read();
7745 if let Some(db) = read_guard.get_state(&hash) {
7746 (return_state_or_throw_err(Some(db))?, true)
7747 } else {
7748 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
7749 (return_state_or_throw_err(write_guard.get_on_disk_state(&hash))?, false)
7750 }
7751 };
7752
7753 let mut db = self.db.write().await;
7755 {
7756 let removed_logs = self.removed_logs_since(common_block.header.number());
7759
7760 let removed_blocks =
7762 self.blockchain.storage.write().unwind_to(common_block.header.number(), hash);
7763
7764 let removed_hashes: Vec<_> =
7766 removed_blocks.iter().map(|b| b.header.hash_slow()).collect();
7767 self.states.write().remove_block_states(&removed_hashes);
7768
7769 if !removed_logs.is_empty() {
7772 self.notify_on_removed_logs(removed_logs);
7773 }
7774
7775 let mut env = self.evm_env.write();
7777 env.block_env.number = self.evm_block_number(common_block.header.number());
7778 env.block_env.timestamp = U256::from(common_block.header.timestamp());
7779 env.block_env.gas_limit = common_block.header.gas_limit();
7780 env.block_env.difficulty = common_block.header.difficulty();
7781 env.block_env.prevrandao = common_block.header.mix_hash();
7782
7783 self.time.reset(env.block_env.timestamp.saturating_to());
7784 self.cheats.clear_next_block_overrides();
7786 }
7787
7788 let block_hashes: Vec<_> = {
7790 let storage = self.blockchain.storage.read();
7791 let min_block = common_block.header.number().saturating_sub(256);
7792 storage
7793 .hashes
7794 .iter()
7795 .filter(|(num, _)| **num >= min_block)
7796 .map(|(&num, &hash)| (num, hash))
7797 .collect()
7798 };
7799
7800 db.clear();
7801
7802 if replace_accounts && let Some(accounts) = db.maybe_as_full_db_mut() {
7803 *accounts = common_state;
7804 } else {
7805 for (address, acc) in common_state {
7807 db.insert_account(address, acc.info);
7808 for (key, value) in acc.storage {
7809 db.set_storage_at(address, key.into(), value.into())?;
7810 }
7811 }
7812 }
7813
7814 for (block_num, hash) in block_hashes {
7816 db.insert_block_hash(U256::from(block_num), hash);
7817 }
7818
7819 Ok(())
7820 }
7821
7822 pub async fn debug_trace_transaction(
7824 &self,
7825 hash: B256,
7826 opts: GethDebugTracingOptions,
7827 ) -> Result<GethTrace, BlockchainError> {
7828 #[cfg(feature = "js-tracer")]
7829 if let Some(tracer_type) = opts.tracer.as_ref()
7830 && tracer_type.is_js()
7831 {
7832 return self
7833 .trace_tx_with_js_tracer(hash, tracer_type.as_str().to_string(), opts.clone())
7834 .await;
7835 }
7836
7837 if let Some(trace) = self.mined_geth_trace_transaction(hash, opts.clone()).await {
7838 return trace;
7839 }
7840
7841 if let Some(fork) = self.get_fork() {
7842 return Ok(fork.debug_trace_transaction(hash, opts).await?);
7843 }
7844
7845 Err(BlockchainError::TransactionNotFound)
7846 }
7847
7848 pub async fn debug_trace_block(
7850 &self,
7851 rlp_block: Bytes,
7852 opts: GethDebugTracingOptions,
7853 ) -> Result<Vec<TraceResult>, BlockchainError> {
7854 let mut rlp = rlp_block.as_ref();
7855 let block = Block::<FoundryTxEnvelope>::decode(&mut rlp).map_err(|err| {
7856 BlockchainError::RpcError(RpcError::invalid_params(format!(
7857 "failed to decode block: {err}"
7858 )))
7859 })?;
7860 if !rlp.is_empty() {
7861 return Err(BlockchainError::RpcError(RpcError::invalid_params(
7862 "failed to decode block: trailing bytes".to_string(),
7863 )));
7864 }
7865
7866 self.debug_trace_block_by_hash(block.header.hash_slow(), opts).await
7867 }
7868
7869 pub async fn debug_trace_block_by_hash(
7871 &self,
7872 block_hash: B256,
7873 opts: GethDebugTracingOptions,
7874 ) -> Result<Vec<TraceResult>, BlockchainError> {
7875 if let Some(block) = self.blockchain.get_block_by_hash(&block_hash) {
7876 return Ok(self.debug_trace_mined_block(&block, opts).await);
7877 }
7878
7879 if let Some(fork) = self.get_fork() {
7880 return Ok(fork.debug_trace_block_by_hash(block_hash, opts).await?);
7881 }
7882
7883 Err(BlockchainError::BlockNotFound)
7884 }
7885
7886 pub async fn debug_trace_block_by_number(
7888 &self,
7889 block_number: BlockNumber,
7890 opts: GethDebugTracingOptions,
7891 ) -> Result<Vec<TraceResult>, BlockchainError> {
7892 let number = self.convert_block_number(Some(block_number));
7893
7894 if let Some(block) = self.get_block(BlockId::number(number)) {
7895 return Ok(self.debug_trace_mined_block(&block, opts).await);
7896 }
7897
7898 if let Some(fork) = self.get_fork() {
7899 return Ok(fork.debug_trace_block_by_number(number, opts).await?);
7900 }
7901
7902 Err(BlockchainError::BlockNotFound)
7903 }
7904
7905 async fn debug_trace_mined_block(
7906 &self,
7907 block: &Block,
7908 opts: GethDebugTracingOptions,
7909 ) -> Vec<TraceResult> {
7910 if block.body.transactions.is_empty() {
7911 return Vec::new();
7912 }
7913
7914 if block.body.transactions.len() > 1 {
7916 let traces = match &opts.tracer {
7917 Some(GethDebugTracerType::BuiltInTracer(
7918 GethDebugBuiltInTracerType::CallTracer,
7919 )) => {
7920 call_config_from_tracer_config(opts.tracer_config.clone()).ok().map(|config| {
7921 self.replay_block_geth_traces(
7922 block,
7923 TracingInspectorConfig::from_geth_call_config(&config),
7924 |inspector, gas_used, _| {
7925 inspector.geth_builder().geth_call_traces(config, gas_used).into()
7926 },
7927 )
7928 })
7929 }
7930 None if self.enable_steps_tracing => Some(self.replay_block_geth_traces(
7932 block,
7933 TracingInspectorConfig::from_geth_config(&opts.config),
7934 |inspector, gas_used, output| {
7935 inspector.geth_builder().geth_traces(gas_used, output, opts.config).into()
7936 },
7937 )),
7938 _ => None,
7939 };
7940 if let Some(Ok(traces)) = traces {
7941 return traces;
7942 }
7943 }
7944
7945 let mut traces = Vec::with_capacity(block.body.transactions.len());
7947 for tx in &block.body.transactions {
7948 let tx_hash = tx.hash();
7949 traces.push(match self.debug_trace_transaction(tx_hash, opts.clone()).await {
7950 Ok(result) => TraceResult::Success { result, tx_hash: Some(tx_hash) },
7951 Err(error) => {
7952 TraceResult::Error { error: error.to_string(), tx_hash: Some(tx_hash) }
7953 }
7954 });
7955 }
7956 traces
7957 }
7958
7959 fn replay_block_geth_traces(
7961 &self,
7962 block: &Block,
7963 tracing_config: TracingInspectorConfig,
7964 build_trace: impl Fn(&TracingInspector, u64, Bytes) -> GethTrace,
7965 ) -> Result<Vec<TraceResult>, BlockchainError> {
7966 let outcomes = {
7967 let storage = self.blockchain.storage.read();
7968 let block_hash = block.header.hash_slow();
7969 block
7970 .body
7971 .transactions
7972 .iter()
7973 .enumerate()
7974 .map(|(index, tx)| {
7975 let hash = tx.hash();
7976 let mined = storage
7977 .transactions
7978 .get(&hash)
7979 .ok_or(BlockchainError::TransactionNotFound)?;
7980 if mined.block_hash != block_hash
7981 || mined.info.transaction_index as usize != index
7982 || mined.info.transaction_hash != hash
7983 {
7984 return Err(BlockchainError::TransactionNotFound);
7985 }
7986 Ok((mined.info.gas_used, mined.info.out.clone().unwrap_or_default()))
7987 })
7988 .collect::<Result<Vec<_>, BlockchainError>>()?
7989 };
7990
7991 let trace = |parent_state: &StateDb| -> Result<Vec<TraceResult>, BlockchainError> {
7992 let (mut cache_db, evm_env, hardfork) =
7993 self.prepare_block_replay(block, parent_state)?;
7994 let monad_context = self.active_monad_context_for_mined_block(block)?;
7995 let mut traces = Vec::with_capacity(block.body.transactions.len());
7996 for (index, (tx, (gas_used, output))) in
7997 block.body.transactions.iter().zip(&outcomes).enumerate()
7998 {
7999 let mut inspector = TracingInspector::new(tracing_config);
8000 let pending = self.pending_mined_transaction(tx.clone())?;
8001 let transaction_context = monad_execution_context_at(monad_context.as_ref(), index);
8002 let (result, _) = self.replay_envelope_with_inspector_ref_and_context(
8003 &cache_db,
8004 &evm_env,
8005 &mut inspector,
8006 &pending,
8007 EnvelopeExecution::replay(transaction_context, hardfork),
8008 )?;
8009 traces.push(TraceResult::Success {
8010 result: build_trace(&inspector, *gas_used, output.clone()),
8011 tx_hash: Some(tx.hash()),
8012 });
8013 cache_db.commit(result.state);
8014 }
8015 Ok(traces)
8016 };
8017
8018 let read_guard = self.states.upgradable_read();
8019 if let Some(state) = read_guard.get_state(&block.header.parent_hash) {
8020 trace(state)
8021 } else {
8022 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
8023 let state = write_guard
8024 .get_on_disk_state(&block.header.parent_hash)
8025 .ok_or(BlockchainError::HistoricalStateUnavailable(block.header.number()))?;
8026 trace(state)
8027 }
8028 }
8029
8030 fn geth_trace(
8031 &self,
8032 tx: &MinedTransaction<N>,
8033 opts: GethDebugTracingOptions,
8034 ) -> Result<GethTrace, BlockchainError> {
8035 let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts;
8036
8037 if let Some(tracer) = tracer {
8038 match tracer {
8039 GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
8040 GethDebugBuiltInTracerType::FourByteTracer => {
8041 let inspector = FourByteInspector::default();
8042 let res = self.replay_tx_with_inspector(
8043 tx.info.transaction_hash,
8044 inspector,
8045 |_, _, inspector, _, _| FourByteFrame::from(inspector).into(),
8046 )?;
8047 return Ok(res);
8048 }
8049 GethDebugBuiltInTracerType::CallTracer => {
8050 return match call_config_from_tracer_config(tracer_config) {
8051 Ok(call_config) => {
8052 let inspector = TracingInspector::new(
8053 TracingInspectorConfig::from_geth_call_config(&call_config),
8054 );
8055 let frame = self.replay_tx_with_inspector(
8056 tx.info.transaction_hash,
8057 inspector,
8058 |_, _, inspector, _, _| {
8059 inspector
8060 .geth_builder()
8061 .geth_call_traces(call_config, tx.info.gas_used)
8062 .into()
8063 },
8064 )?;
8065 Ok(frame)
8066 }
8067 Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
8068 };
8069 }
8070 GethDebugBuiltInTracerType::PreStateTracer => {
8071 return match tracer_config.into_pre_state_config() {
8072 Ok(pre_state_config) => {
8073 let inspector = TracingInspector::new(
8074 TracingInspectorConfig::from_geth_prestate_config(
8075 &pre_state_config,
8076 ),
8077 );
8078 let frame = self.replay_tx_with_inspector(
8079 tx.info.transaction_hash,
8080 inspector,
8081 |state, db, inspector, _, _| {
8082 inspector.geth_builder().geth_prestate_traces(
8083 &state,
8084 &pre_state_config,
8085 db,
8086 )
8087 },
8088 )??;
8089 Ok(frame.into())
8090 }
8091 Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
8092 };
8093 }
8094 GethDebugBuiltInTracerType::NoopTracer
8095 | GethDebugBuiltInTracerType::MuxTracer
8096 | GethDebugBuiltInTracerType::Erc7562Tracer
8097 | GethDebugBuiltInTracerType::FlatCallTracer
8098 | GethDebugBuiltInTracerType::StateGasTracer => {}
8099 },
8100 GethDebugTracerType::JsTracer(_code) => {}
8101 }
8102
8103 return Ok(NoopFrame::default().into());
8104 }
8105
8106 let return_value = tx.info.out.clone().unwrap_or_default();
8108
8109 if self.enable_steps_tracing {
8112 let inspector =
8113 TracingInspector::new(TracingInspectorConfig::from_geth_config(&config));
8114 return self.replay_tx_with_inspector(
8115 tx.info.transaction_hash,
8116 inspector,
8117 |_, _, inspector, _, _| {
8118 inspector
8119 .geth_builder()
8120 .geth_traces(tx.info.gas_used, return_value, config)
8121 .into()
8122 },
8123 );
8124 }
8125
8126 Ok(GethTraceBuilder::new_borrowed(&tx.info.traces)
8127 .geth_traces(tx.info.gas_used, return_value, config)
8128 .into())
8129 }
8130
8131 async fn mined_geth_trace_transaction(
8132 &self,
8133 hash: B256,
8134 opts: GethDebugTracingOptions,
8135 ) -> Option<Result<GethTrace, BlockchainError>> {
8136 self.blockchain.storage.read().transactions.get(&hash).map(|tx| self.geth_trace(tx, opts))
8137 }
8138
8139 pub async fn transaction_receipt(
8140 &self,
8141 hash: B256,
8142 ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
8143 if let Some(receipt) = self.mined_transaction_receipt(hash) {
8144 return Ok(Some(receipt.inner));
8145 }
8146
8147 if let Some(fork) = self.get_fork() {
8148 let receipt = fork.transaction_receipt(hash).await?;
8149 let number = self.convert_block_number(
8150 receipt.clone().and_then(|r| r.block_number()).map(BlockNumber::from),
8151 );
8152
8153 if fork.predates_fork_inclusive(number) {
8154 return Ok(receipt);
8155 }
8156 }
8157
8158 Ok(None)
8159 }
8160
8161 pub fn mined_block_receipts(&self, id: impl Into<BlockId>) -> Option<Vec<FoundryTxReceipt>> {
8163 let storage = self.blockchain.storage.read();
8164 let hash = match id.into() {
8165 BlockId::Hash(hash) => hash.block_hash,
8166 BlockId::Number(number) => storage.hash(number, self.slots_in_an_epoch)?,
8167 };
8168 let block = storage.blocks.get(&hash)?.clone();
8169
8170 if block.body.transactions.iter().enumerate().any(|(index, transaction)| {
8171 storage.transactions.get(&transaction.hash()).is_none_or(|transaction| {
8172 transaction.block_hash != hash
8173 || transaction.info.transaction_index as usize != index
8174 })
8175 }) {
8176 drop(storage);
8177 return block
8178 .body
8179 .transactions
8180 .into_iter()
8181 .map(|transaction| {
8182 self.mined_transaction_receipt(transaction.hash()).map(|receipt| receipt.inner)
8183 })
8184 .collect();
8185 }
8186
8187 let mut receipts = Vec::with_capacity(block.body.transactions.len());
8188 let mut next_log_index = 0;
8189
8190 for block_transaction in &block.body.transactions {
8191 let transaction = storage.transactions.get(&block_transaction.hash())?;
8192 let log_count = transaction.receipt.logs().len();
8193 let receipt = self.build_mined_transaction_receipt(
8194 &transaction.info,
8195 transaction.receipt.clone(),
8196 transaction.block_hash,
8197 &block,
8198 next_log_index,
8199 );
8200 receipts.push(receipt.inner);
8201 next_log_index += log_count;
8202 }
8203
8204 Some(receipts)
8205 }
8206
8207 pub(crate) fn mined_transaction_receipt(
8209 &self,
8210 hash: B256,
8211 ) -> Option<MinedTransactionReceipt<FoundryNetwork>> {
8212 let storage = self.blockchain.storage.read();
8213 let transaction = storage.transactions.get(&hash)?;
8214
8215 let index = transaction.info.transaction_index as usize;
8216 let block = storage.blocks.get(&transaction.block_hash)?;
8217 let mut next_log_index = 0;
8218 for block_transaction in &block.body.transactions[..index] {
8219 next_log_index +=
8220 storage.transactions.get(&block_transaction.hash())?.receipt.logs().len();
8221 }
8222
8223 Some(self.build_mined_transaction_receipt(
8224 &transaction.info,
8225 transaction.receipt.clone(),
8226 transaction.block_hash,
8227 block,
8228 next_log_index,
8229 ))
8230 }
8231
8232 fn build_mined_transaction_receipt(
8233 &self,
8234 info: &TransactionInfo,
8235 tx_receipt: FoundryReceiptEnvelope,
8236 block_hash: B256,
8237 block: &Block,
8238 next_log_index: usize,
8239 ) -> MinedTransactionReceipt<FoundryNetwork> {
8240 let transaction = block.body.transactions[info.transaction_index as usize].clone();
8241
8242 let excess_blob_gas = block.header.excess_blob_gas();
8244 let blob_gas_used = transaction.blob_gas_used();
8245 let blob_gas_price = blob_gas_used
8246 .map(|_| alloy_eips::eip4844::calc_blob_gasprice(excess_blob_gas.unwrap_or_default()));
8247
8248 let effective_gas_price = transaction.effective_gas_price(block.header.base_fee_per_gas());
8249
8250 #[cfg(feature = "base")]
8251 let eip8130_phase_statuses = tx_receipt.eip8130_phase_statuses().to_vec();
8252 let tx_receipt = tx_receipt.convert_logs_rpc(
8253 BlockNumHash::new(block.header.number(), block_hash),
8254 block.header.timestamp(),
8255 info.transaction_hash,
8256 info.transaction_index,
8257 next_log_index,
8258 );
8259
8260 let receipt = TransactionReceipt {
8261 inner: tx_receipt,
8262 transaction_hash: info.transaction_hash,
8263 transaction_index: Some(info.transaction_index),
8264 block_number: Some(block.header.number()),
8265 gas_used: info.gas_used,
8266 contract_address: info.contract_address,
8267 effective_gas_price,
8268 block_hash: Some(block_hash),
8269 from: info.from,
8270 to: info.to,
8271 blob_gas_price,
8272 blob_gas_used,
8273 };
8274
8275 let mut inner = FoundryTxReceipt::with_timestamp(receipt, block.header.timestamp());
8277 if let FoundryTxEnvelope::Celo(tx) = &*transaction {
8278 inner
8279 .0
8280 .other
8281 .insert("feeCurrency".into(), serde_json::to_value(tx.tx().fee_currency).unwrap());
8282 }
8283 if self.is_tempo() {
8284 let fee_payer = match &*transaction {
8285 FoundryTxEnvelope::Tempo(tx) => match tx.tx().recover_fee_payer(info.from) {
8286 Ok(fee_payer) => fee_payer,
8287 Err(error) => {
8288 warn!(
8289 target: "backend",
8290 %error,
8291 tx_hash = ?info.transaction_hash,
8292 "failed to recover Tempo fee payer for mined receipt"
8293 );
8294 info.from
8295 }
8296 },
8297 _ => info.from,
8298 };
8299 inner = inner.with_fee_payer(fee_payer);
8300
8301 if inner.effective_gas_price() > 0
8304 && inner.gas_used() > 0
8305 && let Some(fee_token) = inner.0.inner.logs().last().map(|log| log.address())
8306 {
8307 inner = inner.with_fee_token(fee_token);
8308 }
8309 }
8310 #[cfg(feature = "base")]
8311 if self.is_base()
8312 && let FoundryTxEnvelope::Eip8130(tx) = transaction.as_ref()
8313 {
8314 let fields = Eip8130ReceiptFields {
8315 payer: Some(tx.tx().payer.unwrap_or(info.from)),
8316 phase_statuses: eip8130_phase_statuses,
8317 metadata: (!tx.tx().metadata.is_empty()).then(|| tx.tx().metadata.clone()),
8318 };
8319 inner
8320 .0
8321 .other
8322 .extend(OtherFields::try_from(fields).expect("EIP-8130 receipt fields serialize"));
8323 }
8324 MinedTransactionReceipt { inner, out: info.out.clone() }
8325 }
8326
8327 pub async fn pending_block_receipts(
8329 &self,
8330 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
8331 ) -> Result<Vec<FoundryTxReceipt>, BlockchainError> {
8332 let BlockInfo { block, transactions, receipts } =
8333 self.pending_block(pool_transactions).await?;
8334 let block_hash = block.header.hash_slow();
8335 let mut pending_receipts = Vec::with_capacity(receipts.len());
8336 let mut next_log_index = 0;
8337
8338 for (info, receipt) in transactions.iter().zip(receipts) {
8339 let log_count = receipt.logs().len();
8340 let receipt = self.build_mined_transaction_receipt(
8341 info,
8342 receipt,
8343 block_hash,
8344 &block,
8345 next_log_index,
8346 );
8347 pending_receipts.push(receipt.inner);
8348 next_log_index += log_count;
8349 }
8350
8351 Ok(pending_receipts)
8352 }
8353
8354 pub async fn block_receipts(
8356 &self,
8357 number: BlockId,
8358 ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
8359 if let Some(receipts) = self.mined_block_receipts(number) {
8360 return Ok(Some(receipts));
8361 }
8362
8363 if let Some(fork) = self.get_fork() {
8364 let number = match self.ensure_block_number(Some(number)).await {
8365 Err(_) => return Ok(None),
8366 Ok(n) => n,
8367 };
8368
8369 if fork.predates_fork_inclusive(number) {
8370 let receipts = fork.block_receipts(number).await?;
8371
8372 return Ok(receipts);
8373 }
8374 }
8375
8376 Ok(None)
8377 }
8378}
8379
8380impl<N: Network<ReceiptEnvelope = FoundryReceiptEnvelope>> Backend<N> {
8381 pub async fn serialized_state(
8383 &self,
8384 preserve_historical_states: bool,
8385 ) -> Result<SerializableState, BlockchainError> {
8386 let _mining_guard = self.mining.lock().await;
8388 let at = self.evm_env.read().block_env.clone();
8389 #[cfg(feature = "monad")]
8390 let mut monad_block_participants = BTreeMap::new();
8391 #[cfg(feature = "monad")]
8392 let mut monad_block_replay_profiles = BTreeMap::new();
8393 let (best_number, blocks, transactions) = {
8394 let storage = self.blockchain.storage.read();
8395 #[cfg(feature = "monad")]
8396 if self.is_monad() {
8397 monad_block_participants = storage
8398 .monad_block_participants
8399 .iter()
8400 .filter(|(hash, _)| storage.blocks.contains_key(*hash))
8401 .map(|(hash, participants)| (*hash, participants.iter().copied().collect()))
8402 .collect();
8403 monad_block_replay_profiles = storage
8404 .monad_block_replay_profiles
8405 .iter()
8406 .filter(|(hash, _)| storage.blocks.contains_key(*hash))
8407 .map(|(hash, profile)| (*hash, *profile))
8408 .collect();
8409 }
8410 (storage.best_number, storage.serialized_blocks(), storage.serialized_transactions())
8411 };
8412 let historical_states =
8413 preserve_historical_states.then(|| self.states.write().serialized_states());
8414
8415 let state = self
8416 .db
8417 .read()
8418 .await
8419 .dump_state(at, best_number, blocks, transactions, historical_states)?
8420 .ok_or_else(|| {
8421 BlockchainError::RpcError(RpcError::invalid_params(
8422 "Dumping state not supported with the current configuration",
8423 ))
8424 })?;
8425 #[cfg(feature = "monad")]
8426 let state = {
8427 let mut state = state;
8428 state.monad_block_participants = monad_block_participants;
8429 state.monad_block_replay_profiles = monad_block_replay_profiles;
8430 state
8431 };
8432 Ok(state)
8433 }
8434
8435 pub async fn dump_state(
8437 &self,
8438 preserve_historical_states: bool,
8439 ) -> Result<Bytes, BlockchainError> {
8440 let state = self.serialized_state(preserve_historical_states).await?;
8441 let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
8442 encoder
8443 .write_all(&serde_json::to_vec(&state).unwrap_or_default())
8444 .map_err(|_| BlockchainError::DataUnavailable)?;
8445 Ok(encoder.finish().unwrap_or_default().into())
8446 }
8447
8448 pub async fn load_state(&self, mut state: SerializableState) -> Result<bool, BlockchainError> {
8450 let _mining_guard = self.mining.lock().await;
8451 let mut block_env = state.block.take();
8452 let mut fork_block_boundary = None;
8453 let mut selected_head = None;
8454 let mut selected_header = None;
8455 let mut checkpoint = None;
8456 let fork_head = self.get_fork().map(|f| (f.block_number(), f.block_hash(), f.timestamp()));
8457 if let Some(block) = &mut block_env {
8458 if self.is_tempo() && self.is_fork() && block.beneficiary.is_zero() {
8459 block.beneficiary = TIP_FEE_MANAGER_ADDRESS;
8460 }
8461 let best_number = state.best_block_number.unwrap_or(block.number.saturating_to());
8464 let (selected_best_number, selected_best_hash) = if let Some((number, hash, _)) =
8465 fork_head
8466 {
8467 trace!(target: "backend", state_block_number=?best_number, fork_block_number=?number);
8468 if best_number > number {
8472 (best_number, None)
8473 } else {
8474 fork_block_boundary = Some(number);
8477 (number, Some(hash))
8478 }
8479 } else {
8480 (best_number, None)
8481 };
8482
8483 let best_hash = if let Some(hash) = selected_best_hash {
8484 selected_header = state
8485 .blocks
8486 .iter()
8487 .rev()
8488 .find(|block| block.header.hash_slow() == hash)
8489 .map(|block| block.header.clone());
8490 hash
8491 } else if state.blocks.is_empty() {
8492 let spec_id = self.spec_id();
8493 let is_cancun = spec_id >= SpecId::CANCUN;
8494 let parent_hash = selected_best_number
8495 .checked_sub(1)
8496 .and_then(|number| self.blockchain.storage.read().hashes.get(&number).copied())
8497 .unwrap_or_default();
8498 let header = Header {
8499 parent_hash,
8500 beneficiary: block.beneficiary,
8501 difficulty: block.difficulty,
8502 number: selected_best_number,
8503 gas_limit: block.gas_limit,
8504 timestamp: block.timestamp.saturating_to(),
8505 mix_hash: block.prevrandao.unwrap_or_default(),
8506 base_fee_per_gas: (spec_id >= SpecId::LONDON).then_some(block.basefee),
8507 parent_beacon_block_root: is_cancun.then_some(Default::default()),
8508 blob_gas_used: is_cancun.then_some(0),
8509 excess_blob_gas: if is_cancun { block.blob_excess_gas() } else { None },
8510 withdrawals_root: (spec_id >= SpecId::SHANGHAI).then_some(EMPTY_WITHDRAWALS),
8511 requests_hash: (spec_id >= SpecId::PRAGUE).then_some(EMPTY_REQUESTS_HASH),
8512 ..Default::default()
8513 };
8514 let header = foundry_header(&self.networks, header);
8515 let best_hash = header.hash_slow();
8516 selected_header = Some(header.clone());
8517 checkpoint = Some(create_block(
8518 header,
8519 Vec::<MaybeImpersonatedTransaction<FoundryTxEnvelope>>::new(),
8520 ));
8521 warn!(
8522 target: "backend",
8523 block_number = selected_best_number,
8524 "state dump has no block history; created a synthetic checkpoint block"
8525 );
8526 best_hash
8527 } else if let Some(header) = state
8528 .blocks
8529 .iter()
8530 .rev()
8531 .find(|block| block.header.number() == selected_best_number)
8532 .map(|block| block.header.clone())
8533 {
8534 let best_hash = header.hash_slow();
8535 selected_header = Some(header);
8536 best_hash
8537 } else {
8538 return Err(BlockchainError::RpcError(RpcError::internal_error_with(format!(
8539 "Best hash not found for best number {selected_best_number}",
8540 ))));
8541 };
8542
8543 selected_head = Some((selected_best_number, best_hash));
8544 }
8545
8546 let blocks = std::mem::take(&mut state.blocks);
8550 let transactions = std::mem::take(&mut state.transactions);
8551 let mut storage = self.blockchain.storage.read().clone();
8552 storage.load_blocks(blocks, fork_block_boundary);
8553 if !self.is_celo()
8554 && storage.blocks.values().any(|block| {
8555 block
8556 .body
8557 .transactions
8558 .iter()
8559 .any(|tx| matches!(tx.as_ref(), FoundryTxEnvelope::Celo(_)))
8560 })
8561 {
8562 self.ensure_cip64_active()?;
8563 }
8564 storage.load_transactions(transactions, fork_block_boundary);
8565 if let Some(checkpoint) = checkpoint {
8566 storage.insert_block(checkpoint);
8567 }
8568 if let Some((number, hash)) = selected_head {
8569 storage.hashes.insert(number, hash);
8570 storage.best_number = number;
8571 storage.best_hash = hash;
8572 storage.hashes.retain(|block_number, _| *block_number <= number);
8573 }
8574
8575 #[cfg(feature = "monad")]
8576 if self.is_monad() {
8577 for (hash, profile) in &state.monad_block_replay_profiles {
8578 if storage.blocks.contains_key(hash) {
8579 storage.monad_block_replay_profiles.insert(*hash, *profile);
8580 }
8581 }
8582 for (hash, participants) in &state.monad_block_participants {
8583 if storage.blocks.contains_key(hash) {
8584 storage
8585 .monad_block_participants
8586 .insert(*hash, participants.iter().copied().collect());
8587 }
8588 }
8589 self.rebuild_monad_block_participant_cache(&mut storage)?;
8590 self.monad_context_for_child_of_in_storage(&storage, storage.best_hash)?;
8593 }
8594
8595 let canonical_timestamp = match fork_head {
8600 Some((_, fork_hash, fork_timestamp)) if storage.best_hash == fork_hash => {
8601 Some(fork_timestamp)
8602 }
8603 _ => storage.blocks.get(&storage.best_hash).map(|block| block.header.timestamp),
8604 };
8605
8606 if let Some(block) = block_env.as_mut() {
8607 if !is_arbitrum(self.protocol_chain_id())
8610 && let Some((number, _)) = selected_head
8611 {
8612 block.number = U256::from(number);
8613 }
8614 }
8615
8616 let next_fees = selected_header.as_ref().map(|header| {
8617 let parent_fees = self.fees.get_parent_header_fees(header);
8618 let next_block_excess_blob_gas = self.networks.next_block_blob_excess_gas(
8619 self.fees.blob_params(),
8620 header.excess_blob_gas().unwrap_or_default(),
8621 header.blob_gas_used().unwrap_or_default(),
8622 header.base_fee_per_gas().unwrap_or_default(),
8623 );
8624 let blob_excess_gas_and_price = BlobExcessGasAndPrice::new(
8625 next_block_excess_blob_gas,
8626 get_blob_base_fee_update_fraction(
8627 self.evm_env.read().cfg_env.chain_id,
8628 header.timestamp,
8629 ),
8630 );
8631 (parent_fees, blob_excess_gas_and_price)
8632 });
8633
8634 let historical_states = state.historical_states.take();
8635 let mut db = self.db.write().await;
8636 let db_snapshot = db.snapshot_state();
8637 let load_result = (|| -> Result<(), BlockchainError> {
8638 if !db.load_state(state)? {
8639 return Err(RpcError::invalid_params(
8640 "Loading state not supported with the current configuration",
8641 )
8642 .into());
8643 }
8644 #[cfg(feature = "base")]
8647 if self.is_base() && self.base_upgrade() >= BaseUpgrade::Denim {
8648 ensure_base_time_predeploy(&mut **db, false)?;
8649 }
8650 Ok(())
8651 })();
8652 if let Err(err) = load_result {
8653 db.revert_state(db_snapshot, RevertStateSnapshotAction::RevertRemove);
8654 return Err(err);
8655 }
8656 db.delete_state_snapshot(db_snapshot);
8657 drop(db);
8658
8659 let block_hashes = {
8664 let min_block = storage.best_number.saturating_sub(256);
8665 storage
8666 .hashes
8667 .iter()
8668 .filter(|(num, _)| (min_block..=storage.best_number).contains(*num))
8669 .map(|(&num, &hash)| (U256::from(num), hash))
8670 .collect()
8671 };
8672
8673 *self.blockchain.storage.write() = storage;
8674 if let Some(timestamp) = canonical_timestamp {
8675 self.time.reset(timestamp);
8676 }
8677 if let Some(block_env) = block_env {
8678 self.evm_env.write().block_env = block_env;
8679 }
8680 if let Some((parent_fees, blob_excess_gas_and_price)) = next_fees {
8681 #[cfg(feature = "optimism")]
8682 if self.is_optimism() {
8683 self.fees.set_optimism_base_fee_rules(&parent_fees.extra_data);
8684 }
8685 self.fees.set_base_fee(parent_fees.base_fee);
8686 self.fees.set_blob_excess_gas_and_price(blob_excess_gas_and_price);
8687 }
8688
8689 self.db.write().await.set_block_hashes(block_hashes);
8690
8691 if let Some(historical_states) = historical_states {
8692 self.states.write().load_states(historical_states);
8693 }
8694
8695 Ok(true)
8696 }
8697
8698 pub async fn load_state_bytes(&self, buf: Bytes) -> Result<bool, BlockchainError> {
8700 let orig_buf = &buf.0[..];
8701 let mut decoder = GzDecoder::new(orig_buf);
8702 let mut decoded_data = Vec::new();
8703
8704 let state: SerializableState = serde_json::from_slice(if decoder.header().is_some() {
8705 decoder
8706 .read_to_end(decoded_data.as_mut())
8707 .map_err(|_| BlockchainError::FailedToDecodeStateDump)?;
8708 &decoded_data
8709 } else {
8710 &buf.0
8711 })
8712 .map_err(|_| BlockchainError::FailedToDecodeStateDump)?;
8713
8714 self.load_state(state).await
8715 }
8716}
8717
8718impl Backend<FoundryNetwork> {
8719 pub async fn call_bundle(
8721 &self,
8722 bundle: EthCallBundle,
8723 transactions: Vec<PendingTransaction<FoundryTxEnvelope>>,
8724 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
8725 ) -> Result<EthCallBundleResponse, BlockchainError> {
8726 let EthCallBundle {
8727 block_number,
8728 coinbase,
8729 timestamp,
8730 gas_limit,
8731 difficulty,
8732 base_fee,
8733 ..
8734 } = bundle;
8735
8736 let blob_gas_used = transactions
8737 .iter()
8738 .filter_map(|transaction| transaction.transaction.blob_gas_used())
8739 .sum::<u64>();
8740 let max_blob_gas = self.blob_params().max_blob_gas_per_block();
8741 if blob_gas_used > max_blob_gas {
8742 return Err(BlockchainError::RpcError(RpcError::invalid_params(format!(
8743 "blob gas usage exceeds the limit of {max_blob_gas} gas per block."
8744 ))));
8745 }
8746
8747 self.with_database_at_and_context(
8748 block_request,
8749 |state, mut block_env, mut monad_context| {
8750 let state_block_number = block_env.number.to::<u64>();
8751 block_env.number = U256::from(block_number);
8752 block_env.timestamp = timestamp
8753 .map(U256::from)
8754 .unwrap_or_else(|| block_env.timestamp.saturating_add(U256::from(12)));
8755 if let Some(coinbase) = coinbase {
8756 block_env.beneficiary = coinbase;
8757 }
8758 if let Some(gas_limit) = gas_limit {
8759 block_env.gas_limit = gas_limit;
8760 }
8761 if let Some(difficulty) = difficulty {
8762 block_env.difficulty = difficulty;
8763 }
8764 if let Some(base_fee) = base_fee {
8765 block_env.basefee = base_fee.try_into().unwrap_or(u64::MAX);
8766 }
8767
8768 let mut evm_env = self.evm_env.read().clone();
8769 evm_env.block_env = block_env;
8770 let coinbase = evm_env.block_env.beneficiary;
8771 let base_fee = evm_env.block_env.basefee;
8772 let mut cache_db = CacheDB::new(state);
8773 let initial_coinbase = revm::DatabaseRef::basic_ref(&cache_db, coinbase)?
8774 .map(|account| account.balance)
8775 .unwrap_or_default();
8776 let mut coinbase_balance_before_tx = initial_coinbase;
8777 let mut coinbase_balance_after_tx = initial_coinbase;
8778 let mut total_gas_used = 0u64;
8779 let mut total_gas_fees = U256::ZERO;
8780 let mut bundle_hash = alloy_primitives::Keccak256::new();
8781 let mut results = Vec::with_capacity(transactions.len());
8782
8783 for transaction in transactions {
8784 let sender = *transaction.sender();
8785 let tx = transaction.transaction.as_ref();
8786 let tx_hash = tx.hash();
8787 bundle_hash.update(tx_hash);
8788
8789 let mut inspector = self.build_inspector();
8790 let (ResultAndState { result, state }, _) = self
8791 .transact_envelope_with_inspector_ref_and_context(
8792 &cache_db,
8793 &evm_env,
8794 &mut inspector,
8795 &transaction,
8796 monad_context.as_mut().map(next_monad_context),
8797 )?;
8798
8799 let gas_price = tx.effective_tip_per_gas(base_fee).unwrap_or_default();
8800 let gas_used = result.tx_gas_used();
8801 let gas_fees = U256::from(gas_used) * U256::from(gas_price);
8802 total_gas_used += gas_used;
8803 total_gas_fees += gas_fees;
8804
8805 coinbase_balance_after_tx = state
8806 .get(&coinbase)
8807 .map(|account| account.info.balance)
8808 .unwrap_or(coinbase_balance_before_tx);
8809 let coinbase_diff =
8810 coinbase_balance_after_tx.saturating_sub(coinbase_balance_before_tx);
8811 let eth_sent_to_coinbase = coinbase_diff.saturating_sub(gas_fees);
8812 coinbase_balance_before_tx = coinbase_balance_after_tx;
8813
8814 let output = result.output().cloned().unwrap_or_default();
8815 let (value, revert) = if result.is_success() {
8816 (Some(output), None)
8817 } else {
8818 (None, Some(output))
8819 };
8820
8821 results.push(EthCallBundleTransactionResult {
8822 coinbase_diff,
8823 eth_sent_to_coinbase,
8824 from_address: sender,
8825 gas_fees,
8826 gas_price: U256::from(gas_price),
8827 gas_used,
8828 to_address: tx.to(),
8829 tx_hash,
8830 value,
8831 revert,
8832 });
8833 cache_db.commit(state);
8834 }
8835
8836 let coinbase_diff = coinbase_balance_after_tx.saturating_sub(initial_coinbase);
8837 let eth_sent_to_coinbase = coinbase_diff.saturating_sub(total_gas_fees);
8838 let bundle_gas_price =
8839 coinbase_diff.checked_div(U256::from(total_gas_used)).unwrap_or_default();
8840
8841 Ok(EthCallBundleResponse {
8842 bundle_hash: bundle_hash.finalize(),
8843 bundle_gas_price,
8844 coinbase_diff,
8845 eth_sent_to_coinbase,
8846 gas_fees: total_gas_fees,
8847 results,
8848 state_block_number,
8849 total_gas_used,
8850 })
8851 },
8852 )
8853 .await
8854 }
8855
8856 pub async fn call_many(
8858 &self,
8859 bundles: Vec<Bundle<WithOtherFields<TransactionRequest>>>,
8860 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
8861 state_override: Option<alloy_rpc_types::state::StateOverride>,
8862 ) -> Result<Vec<Vec<EthCallResponse>>, BlockchainError> {
8863 if bundles.is_empty() {
8864 return Err(BlockchainError::RpcError(RpcError::invalid_params(
8865 "bundles are empty.".to_string(),
8866 )));
8867 }
8868
8869 self.with_database_at_and_context(
8870 block_request,
8871 |state, mut block_env, mut monad_context| {
8872 let mut cache_db = CacheDB::new(state);
8873 if let Some(state_override) = state_override {
8874 apply_state_overrides(state_override, &mut cache_db)?;
8875 }
8876
8877 let mut results = Vec::with_capacity(bundles.len());
8878 for bundle in bundles {
8879 let Bundle { transactions, block_override } = bundle;
8880 if let Some(block_override) = block_override {
8881 cache_db.apply_block_overrides(block_override, &mut block_env);
8882 }
8883
8884 let mut bundle_results = Vec::with_capacity(transactions.len());
8885 for request in transactions {
8886 let fee_details = FeeDetails::new(
8887 request.gas_price,
8888 request.max_fee_per_gas,
8889 request.max_priority_fee_per_gas,
8890 request.max_fee_per_blob_gas,
8891 )?
8892 .or_zero_fees();
8893 let PreparedCall { evm_env, mut tx_env, simulated_tempo_tx, .. } = self
8894 .prepare_call_env(&cache_db, request, fee_details, block_env.clone())?;
8895 apply_tempo_envelope_identity(&mut tx_env, simulated_tempo_tx.as_ref());
8896
8897 let mut inspector = self.build_inspector();
8898 let ResultAndState { result, state } = self
8899 .transact_call_with_inspector_ref(
8900 &cache_db,
8901 &evm_env,
8902 &mut inspector,
8903 tx_env,
8904 monad_context.as_mut().map(next_monad_context),
8905 )?;
8906
8907 let output = result.output().cloned().unwrap_or_default();
8908 let response = if result.is_success() {
8909 EthCallResponse { value: Some(output), error: None }
8910 } else {
8911 let error = RevertDecoder::new()
8912 .maybe_decode(&output, None)
8913 .unwrap_or_else(|| "execution failed".to_string());
8914 EthCallResponse { value: None, error: Some(error) }
8915 };
8916
8917 cache_db.commit(state);
8918 bundle_results.push(response);
8919 }
8920
8921 results.push(bundle_results);
8922 block_env.number = block_env.number.saturating_add(U256::ONE);
8923 block_env.timestamp = block_env.timestamp.saturating_add(U256::ONE);
8924 #[cfg(feature = "monad")]
8925 self::monad::advance_block_context(&mut monad_context);
8926 }
8927
8928 Ok(results)
8929 },
8930 )
8931 .await
8932 }
8933
8934 pub async fn simulate(
8936 &self,
8937 request: SimulatePayload,
8938 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
8939 block_interval: u64,
8940 ) -> Result<Vec<SimulatedBlock<AnyRpcBlock>>, BlockchainError> {
8941 self.simulate_raw(
8942 preserve_simulation_request_fields(request),
8943 block_request,
8944 block_interval,
8945 )
8946 .await
8947 }
8948
8949 pub(crate) async fn simulate_raw(
8951 &self,
8952 request: SimulatePayload<WithOtherFields<TransactionRequest>>,
8953 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
8954 block_interval: u64,
8955 ) -> Result<Vec<SimulatedBlock<AnyRpcBlock>>, BlockchainError> {
8956 let simulate_at = |state: Box<dyn MaybeFullDatabase + '_>,
8957 base_block_env: BlockEnv,
8958 base_number,
8959 base_timestamp,
8960 base_hash,
8961 base_fee,
8962 mut monad_context: Option<MonadReplayContext>,
8963 base_base_fee_per_gas,
8964 base_excess_blob_gas,
8965 base_blob_gas_used,
8966 base_fee_extra_data: Bytes,
8967 optimism_jovian: bool| {
8968 let SimulatePayload {
8969 block_state_calls,
8970 trace_transfers,
8971 validation,
8972 return_full_transactions,
8973 } = request;
8974 let block_state_calls = sanitize_simulation_blocks(
8975 block_state_calls,
8976 base_number,
8977 base_timestamp,
8978 block_interval,
8979 )?;
8980 let mut cache_db = BalDatabase::new(CacheDB::new(state));
8981 cache_db.cache.block_hashes.insert(U256::from(base_number), base_hash);
8982 let mut block_res = Vec::with_capacity(block_state_calls.len());
8983 let cache_state_roots = block_state_calls.len() > 1;
8985 let mut state_root_cache = None::<(StateRootCache, AddressMap<DbAccount>)>;
8986 let mut parent_hash = base_hash;
8987 let mut next_base_fee = base_fee;
8988 let mut inherited_block_env = base_block_env;
8989 let (is_merge, is_cancun, is_amsterdam, tx_gas_limit_cap) = {
8990 let cfg_env = &self.evm_env.read().cfg_env;
8991 (
8992 cfg_env.spec >= SpecId::MERGE,
8993 cfg_env.spec >= SpecId::CANCUN,
8994 cfg_env.spec >= SpecId::AMSTERDAM,
8995 cfg_env.tx_gas_limit_cap(),
8996 )
8997 };
8998 let mut parent_base_fee_per_gas = base_base_fee_per_gas;
8999 let mut parent_excess_blob_gas = base_excess_blob_gas;
9000 let mut parent_blob_gas_used = base_blob_gas_used;
9001 let mut rpc_gas_budget = SIMULATE_GAS_CAP;
9002
9003 for block in block_state_calls {
9005 let SimBlock { block_overrides, state_overrides, calls } = block;
9006 let mut block_env = inherited_block_env.clone();
9007 let overridden_beacon_root =
9008 block_overrides.as_ref().and_then(|overrides| overrides.beacon_root);
9009 let block_timestamp = block_overrides
9010 .as_ref()
9011 .and_then(|overrides| overrides.time)
9012 .unwrap_or_else(|| block_env.timestamp.saturating_to());
9013 let blob_params = self.simulation_blob_params_at_timestamp(block_timestamp);
9014 if is_cancun {
9015 let excess_blob_gas = self.networks.next_block_blob_excess_gas(
9016 blob_params,
9017 parent_excess_blob_gas,
9018 parent_blob_gas_used,
9019 parent_base_fee_per_gas,
9020 );
9021 block_env.set_blob_excess_gas_and_price(
9022 excess_blob_gas,
9023 blob_params.update_fraction as u64,
9024 );
9025 } else {
9026 block_env.blob_excess_gas_and_price = None;
9027 }
9028 block_env.basefee = if validation { next_base_fee } else { 0 };
9029 block_env.prevrandao = Some(B256::ZERO);
9030 if is_merge && !is_arbitrum(self.protocol_chain_id()) {
9031 block_env.difficulty = U256::ZERO;
9032 }
9033 let mut call_res = Vec::with_capacity(calls.len());
9034 let mut log_index = 0;
9035 let mut cumulative_gas_used = 0;
9036 let mut block_regular_gas_used = 0;
9037 let mut block_state_gas_used = 0;
9038 let mut block_blob_gas_used = 0u64;
9039 let mut transactions = Vec::with_capacity(calls.len());
9040 let mut transaction_envelopes = Vec::with_capacity(calls.len());
9041 let mut receipts = Vec::with_capacity(calls.len());
9042 let overridden_block_hashes = block_overrides
9043 .as_ref()
9044 .and_then(|overrides| overrides.block_hash.as_ref())
9045 .map(|overrides| {
9046 overrides
9047 .keys()
9048 .map(|number| {
9049 let number = U256::from(*number);
9050 (number, cache_db.cache.block_hashes.get(&number).copied())
9051 })
9052 .collect::<Vec<_>>()
9053 })
9054 .unwrap_or_default();
9055
9056 if let Some(block_overrides) = block_overrides {
9057 cache_db.apply_block_overrides(block_overrides, &mut block_env);
9058 }
9059 let rpc_block_number = block_env.number.saturating_to();
9062 block_env.number = self.evm_block_number(rpc_block_number);
9063 let simulation_evm_env =
9064 EvmEnv::new(self.evm_env.read().cfg_env.clone(), block_env.clone());
9065 let spec_id = *simulation_evm_env.spec_id();
9066 let ethereum_transitions = self
9067 .ethereum_block_transitions(self.hardfork(), None, BlockExecutionKind::Complete)
9068 .map(|mut transitions| {
9069 transitions.parent_beacon_block_root = (transitions.hardfork
9070 >= EthereumHardfork::Cancun)
9071 .then_some(overridden_beacon_root.unwrap_or_default());
9072 transitions
9073 });
9074 let precompile_overrides = self.simulation_precompile_overrides(
9075 state_overrides.as_ref(),
9076 &simulation_evm_env,
9077 )?;
9078
9079 if let Some(mut state_overrides) = state_overrides {
9082 state_overrides.retain(|_, account| {
9083 account.balance.is_some()
9084 || account.nonce.is_some()
9085 || account.code.is_some()
9086 || account.state.is_some()
9087 || account
9088 .state_diff
9089 .as_ref()
9090 .is_some_and(|state_diff| !state_diff.is_empty())
9091 });
9092 let previously_deleted = previously_deleted_accounts(
9093 &cache_db.cache.accounts,
9094 state_overrides.keys().copied(),
9095 );
9096 apply_state_overrides(state_overrides, &mut cache_db.db)?;
9097 preserve_deleted_storage(&mut cache_db.cache.accounts, previously_deleted);
9098 }
9099
9100 if ethereum_transitions
9102 .is_some_and(|transitions| transitions.hardfork >= EthereumHardfork::Amsterdam)
9103 {
9104 cache_db.bal_state = BalState::new().with_bal_builder();
9105 }
9106
9107 if let Some(transitions) = ethereum_transitions {
9108 self.apply_simulation_pre_execution_changes(
9109 &mut cache_db,
9110 &simulation_evm_env,
9111 parent_hash,
9112 transitions,
9113 )?;
9114 }
9115
9116 cache_db.bump_bal_index();
9117
9118 for (req_idx, mut request) in calls.into_iter().enumerate() {
9120 #[cfg(feature = "base")]
9121 if request.transaction_type == Some(EIP8130_TRANSACTION_TYPE) {
9122 return Err(BlockchainError::InvalidTransactionRequest(
9123 "eth_simulateV1 does not accept EIP-8130 transaction requests"
9124 .to_string(),
9125 ));
9126 }
9127
9128 let classified_request = self.parse_transaction_request(request.clone())?;
9129 #[cfg(feature = "base")]
9130 if classified_request.is_base() {
9131 return Err(BlockchainError::InvalidTransactionRequest(
9132 "eth_simulateV1 does not accept EIP-8130 transaction requests"
9133 .to_string(),
9134 ));
9135 }
9136 let is_ethereum_request = classified_request.is_ethereum();
9137 let mut parsed_request = self.is_tempo().then_some(classified_request);
9138 if is_ethereum_request {
9139 request.populate_blob_hashes();
9140 let preferred_type = request.preferred_type();
9141 request.transaction_type = Some(preferred_type as u8);
9142 request.trim_conflicting_keys();
9143 request.populate_blob_hashes();
9144 }
9145 let request_blob_gas_used = if is_ethereum_request && !optimism_jovian {
9146 u64::try_from(request.blob_versioned_hashes.as_ref().map_or(0, Vec::len))
9147 .unwrap_or(u64::MAX)
9148 .saturating_mul(DATA_GAS_PER_BLOB)
9149 } else {
9150 0
9151 };
9152 let max_blob_gas = block_blob_gas_limit(
9153 optimism_jovian,
9154 block_env.gas_limit,
9155 blob_params.max_blob_gas_per_block(),
9156 );
9157 if !optimism_jovian
9158 && block_blob_gas_used.saturating_add(request_blob_gas_used) > max_blob_gas
9159 {
9160 return Err(BlockchainError::RpcError(RpcError::invalid_params(format!(
9161 "blob gas usage exceeds the limit of {max_blob_gas} gas per block."
9162 ))));
9163 }
9164 block_blob_gas_used = block_blob_gas_used.saturating_add(request_blob_gas_used);
9165
9166 let inner = request.as_ref();
9167 let remaining_regular_gas =
9168 block_env.gas_limit.saturating_sub(block_regular_gas_used);
9169 let remaining_state_gas =
9170 block_env.gas_limit.saturating_sub(block_state_gas_used);
9171 let remaining_gas = if is_amsterdam {
9172 remaining_regular_gas.min(remaining_state_gas)
9173 } else {
9174 block_env.gas_limit.saturating_sub(cumulative_gas_used)
9175 };
9176 let requested_gas = inner.gas.unwrap_or(remaining_gas);
9177 let exceeds_gas_limit = if is_amsterdam {
9178 let requested_regular_gas = requested_gas.min(tx_gas_limit_cap);
9179 requested_regular_gas > remaining_regular_gas
9180 || requested_gas > remaining_state_gas
9181 } else {
9182 requested_gas > remaining_gas
9183 };
9184 if exceeds_gas_limit {
9185 return Err(BlockchainError::RpcError(RpcError {
9186 code: ErrorCode::ServerError(-38015),
9187 message: format!(
9188 "block gas limit exceeded: remaining {remaining_gas}, requested {requested_gas}"
9189 )
9190 .into(),
9191 data: None,
9192 }));
9193 }
9194 let execution_gas_limit = requested_gas.min(rpc_gas_budget);
9195 let preserve_signed_gas = matches!(
9196 &parsed_request,
9197 Some(FoundryTransactionRequest::Tempo(request))
9198 if request.fee_payer_signature.is_some()
9199 );
9200 request.gas = Some(execution_gas_limit);
9201 if !preserve_signed_gas && let Some(parsed_request) = &mut parsed_request {
9202 parsed_request.as_mut().gas = Some(execution_gas_limit);
9203 }
9204
9205 let caller = request.from.unwrap_or_default();
9206 let caller_nonce = RevmDatabase::basic(&mut cache_db.db, caller)?
9207 .map(|account| account.nonce)
9208 .unwrap_or_default();
9209 let tempo_nonce_key =
9210 parsed_request.as_ref().and_then(|request| match request {
9211 FoundryTransactionRequest::Tempo(request) => request.nonce_key,
9212 _ => None,
9213 });
9214 if request.nonce.is_none() {
9215 let nonce = tempo_nonce_key.map_or(Ok(caller_nonce), |nonce_key| {
9216 tempo_nonce(&cache_db.db, caller, nonce_key)
9217 })?;
9218 request.nonce = Some(nonce);
9219 if let Some(parsed_request) = &mut parsed_request {
9220 parsed_request.as_mut().nonce = Some(nonce);
9221 }
9222 }
9223
9224 if is_ethereum_request {
9225 let mut canonical_request =
9226 FoundryTransactionRequest::Ethereum(request.inner.clone());
9227 canonical_request.prep_for_submission();
9228 request.inner = canonical_request.as_ref().clone();
9229 if let Some(parsed_request) = &mut parsed_request {
9230 *parsed_request = canonical_request;
9231 }
9232 }
9233
9234 let fee_details = FeeDetails::new(
9235 request.gas_price,
9236 request.max_fee_per_gas,
9237 request.max_priority_fee_per_gas,
9238 request.max_fee_per_blob_gas,
9239 )?
9240 .or_zero_fees();
9241
9242 let PreparedCall {
9243 mut evm_env,
9244 mut tx_env,
9245 simulated_tempo_tx,
9246 #[cfg(feature = "base")]
9247 simulated_envelope,
9248 } = if let Some(parsed_request) = parsed_request {
9249 self.prepare_typed_call_env(
9250 &cache_db.db,
9251 parsed_request,
9252 fee_details,
9253 block_env.clone(),
9254 )?
9255 } else {
9256 self.prepare_call_env(
9257 &cache_db.db,
9258 request.clone(),
9259 fee_details,
9260 block_env.clone(),
9261 )?
9262 };
9263 tx_env.base_mut().gas_limit = execution_gas_limit;
9264 apply_tempo_envelope_identity(&mut tx_env, simulated_tempo_tx.as_ref());
9265 if !validation
9266 && tempo_nonce_key.is_none_or(|key| key.is_zero())
9267 && request.nonce == Some(u64::MAX)
9268 {
9269 tx_env.base_mut().nonce = 0;
9270 }
9271 let uses_protocol_call_nonce = tx_env.uses_protocol_call_nonce();
9272 #[cfg(feature = "base")]
9273 let simulated_envelope = simulated_envelope
9274 .or_else(|| simulated_tempo_tx.map(FoundryTxEnvelope::Tempo));
9275 #[cfg(not(feature = "base"))]
9276 let simulated_envelope = simulated_tempo_tx.map(FoundryTxEnvelope::Tempo);
9277
9278 if is_amsterdam {
9279 let spec = evm_env.cfg_env.spec;
9281 evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec);
9282 }
9283
9284 evm_env.cfg_env.disable_eip3607 = true;
9286
9287 evm_env.block_env.basefee = block_env.basefee;
9290 evm_env.block_env.blob_excess_gas_and_price =
9291 block_env.blob_excess_gas_and_price;
9292 evm_env.cfg_env.disable_base_fee = !validation;
9295 if validation {
9296 evm_env.cfg_env.disable_nonce_check = false;
9297 evm_env.cfg_env.disable_block_gas_limit = false;
9298 }
9299
9300 let mut inspector = self.build_inspector();
9301
9302 inspector = inspector.with_simulation_logs(trace_transfers);
9304 trace!(target: "backend", env=?evm_env, spec=?evm_env.spec_id(),"simulate evm env");
9305 let execution_result = match tx_env {
9306 CallTxEnv::Eth(tx_env)
9307 if !validation
9308 && tx_env.tx_type == 3
9309 && tx_env.max_fee_per_blob_gas == 0 =>
9310 {
9311 self.transact_eth_simulation_with_inspector_ref(
9312 &cache_db.db,
9313 &evm_env,
9314 &mut inspector,
9315 tx_env,
9316 &precompile_overrides,
9317 )
9318 }
9319 tx_env if precompile_overrides.moves.is_empty() => self
9320 .transact_call_with_inspector_ref(
9321 &cache_db.db,
9322 &evm_env,
9323 &mut inspector,
9324 tx_env,
9325 monad_context.as_mut().map(next_monad_context),
9326 ),
9327 tx_env => self.transact_eth_with_inspector_ref_and_precompile_overrides(
9328 &cache_db.db,
9329 &evm_env,
9330 &mut inspector,
9331 tx_env.into_base(),
9332 &precompile_overrides,
9333 ),
9334 };
9335 let ResultAndState { result, mut state } = match execution_result {
9336 Err(BlockchainError::InvalidTransaction(error)) => {
9337 return Err(simulate_transaction_error(error));
9338 }
9339 result => result?,
9340 };
9341 if !validation
9342 && caller_nonce == u64::MAX
9343 && uses_protocol_call_nonce
9344 && let Some(account) = state.get_mut(&caller)
9345 {
9346 account.info.nonce = 0;
9347 }
9348 trace!(target: "backend", ?result, ?request, "simulate call");
9349
9350 let canonical_logs = result.clone().into_logs();
9351 let (response_logs, attempted_log_count) = inspector
9352 .take_simulation_logs(&canonical_logs, result.is_success())
9353 .expect("simulation log collector is installed");
9354 inspector.print_logs();
9355 if self.print_traces {
9356 inspector.into_print_traces(self.call_trace_decoder());
9357 }
9358
9359 let previously_deleted = previously_deleted_accounts(
9363 &cache_db.cache.accounts,
9364 state.keys().copied(),
9365 );
9366
9367 rpc_gas_budget = rpc_gas_budget.saturating_sub(result.tx_gas_used());
9368 cumulative_gas_used = cumulative_gas_used.saturating_add(result.tx_gas_used());
9369 block_regular_gas_used = block_regular_gas_used
9370 .saturating_add(result.gas().block_regular_gas_used());
9371 block_state_gas_used =
9372 block_state_gas_used.saturating_add(result.gas().block_state_gas_used());
9373
9374 let from = caller;
9376 request.sidecar = None;
9377 let tx = if let Some(envelope) = simulated_envelope {
9378 MaybeImpersonatedTransaction::impersonated(envelope, from)
9379 } else {
9380 if request.to.is_none() {
9381 request.to = Some(TxKind::Create);
9382 }
9383 let mut request = self.parse_transaction_request(request)?;
9384 request.prep_for_submission();
9385 let typed_tx = request.build_unsigned().map_err(|e| {
9386 BlockchainError::InvalidTransactionRequest(e.to_string())
9387 })?;
9388 MaybeImpersonatedTransaction::impersonated(
9389 typed_tx.into_impersonated(),
9390 from,
9391 )
9392 };
9393 let tx_hash = tx.as_ref().hash();
9394 #[cfg(feature = "optimism")]
9395 if optimism_jovian {
9396 let tx_blob_gas = crate::eth::backend::executor::optimism::blob_gas_used(
9397 &mut cache_db.db,
9398 tx.as_ref(),
9399 true,
9400 )
9401 .map_err(|err| BlockchainError::Internal(err.to_string()))?;
9402 if block_blob_gas_used.saturating_add(tx_blob_gas) > max_blob_gas {
9403 return Err(BlockchainError::RpcError(RpcError::invalid_params(
9404 format!(
9405 "blob gas usage exceeds the limit of {max_blob_gas} gas per block."
9406 ),
9407 )));
9408 }
9409 block_blob_gas_used = block_blob_gas_used.saturating_add(tx_blob_gas);
9410 }
9411
9412 cache_db.commit(state);
9415 cache_db.bump_bal_index();
9416 preserve_deleted_storage(&mut cache_db.cache.accounts, previously_deleted);
9417 #[cfg(any(feature = "base", feature = "optimism"))]
9418 let receipt = if tx.as_ref().is_deposit() {
9419 crate::eth::backend::executor::optimism::build_simulated_deposit_receipt(
9420 self.hardfork(),
9421 caller_nonce,
9422 &result,
9423 canonical_logs.clone(),
9424 cumulative_gas_used,
9425 )
9426 } else {
9427 FoundryReceiptBuilder::build_simulated_receipt(
9428 tx.as_ref().tx_type(),
9429 &result,
9430 canonical_logs.clone(),
9431 cumulative_gas_used,
9432 )
9433 };
9434 #[cfg(not(any(feature = "base", feature = "optimism")))]
9435 let receipt = FoundryReceiptBuilder::build_simulated_receipt(
9436 tx.as_ref().tx_type(),
9437 &result,
9438 canonical_logs.clone(),
9439 cumulative_gas_used,
9440 );
9441 receipts.push(receipt);
9442 transaction_envelopes.push(tx.as_ref().clone());
9443 let rpc_tx =
9444 transaction_build(Some(tx_hash), tx, None, None, Some(block_env.basefee));
9445 transactions.push(rpc_tx);
9446
9447 let return_data = if result.is_success() {
9448 result.output().cloned().unwrap_or_default()
9449 } else {
9450 Bytes::new()
9451 };
9452 let sim_res = SimCallResult {
9453 return_data,
9454 gas_used: result.tx_gas_used(),
9455 max_used_gas: Some(
9456 result.gas().total_gas_spent().max(result.gas().floor_gas()),
9457 ),
9458 status: result.is_success(),
9459 error: match &result {
9460 ExecutionResult::Success { .. } => None,
9461 ExecutionResult::Revert { output, .. } => {
9462 let message = RevertDecoder::new()
9463 .maybe_decode(output, None)
9464 .map(|reason| format!("execution reverted: {reason}"))
9465 .unwrap_or_else(|| "execution reverted".to_string());
9466 Some(SimulateError {
9467 code: SimulateError::EXECUTION_REVERTED_CODE,
9468 message,
9469 data: Some(output.clone()),
9470 })
9471 }
9472 ExecutionResult::Halt { reason, .. } => Some(SimulateError {
9473 code: SimulateError::VM_EXECUTION_ERROR_CODE,
9474 message: if matches!(reason, HaltReason::OutOfGas(_)) {
9475 "out of gas".to_string()
9476 } else {
9477 format!("vm execution error: {reason}")
9478 },
9479 data: None,
9480 }),
9481 },
9482 logs: response_logs
9483 .into_iter()
9484 .map(|(idx, log)| Log {
9485 inner: log,
9486 block_number: Some(rpc_block_number),
9487 block_timestamp: Some(block_env.timestamp.saturating_to()),
9488 transaction_index: Some(req_idx as u64),
9489 log_index: Some(idx + log_index),
9490 removed: false,
9491
9492 block_hash: None,
9493 transaction_hash: Some(tx_hash),
9494 })
9495 .collect(),
9496 };
9497 log_index += attempted_log_count;
9498 call_res.push(sim_res);
9499 }
9500
9501 for (number, hash) in overridden_block_hashes {
9502 if let Some(hash) = hash {
9503 cache_db.cache.block_hashes.insert(number, hash);
9504 } else {
9505 cache_db.cache.block_hashes.remove(&number);
9506 }
9507 }
9508
9509 let gas_used = if is_amsterdam {
9510 block_regular_gas_used.max(block_state_gas_used)
9511 } else {
9512 cumulative_gas_used
9513 };
9514 let requests = if let Some(transitions) = ethereum_transitions {
9515 self.apply_simulation_post_execution_changes(
9516 &mut cache_db,
9517 &simulation_evm_env,
9518 transitions,
9519 &receipts,
9520 )?
9521 } else {
9522 Default::default()
9523 };
9524
9525 let incremental = state_root_cache.as_mut().is_some_and(|(cache, previous)| {
9527 cache.record_overlay(&cache_db.cache.accounts, previous)
9528 });
9529 let state_root = if incremental {
9530 let (cache, previous) = state_root_cache.as_mut().unwrap();
9531 let root = cache.root(&cache_db.cache.accounts);
9532 previous.clone_from(&cache_db.cache.accounts);
9533 root
9534 } else {
9535 cache_db
9536 .maybe_full_db()
9537 .map(|accounts| {
9538 if cache_state_roots
9542 && accounts.values().all(|account| {
9543 account.account_state != AccountState::NotExisting
9544 || account.storage.is_empty()
9545 })
9546 {
9547 let (cache, previous) =
9548 state_root_cache.get_or_insert_with(Default::default);
9549 let root = cache.root(&accounts);
9550 previous.clone_from(&cache_db.cache.accounts);
9551 root
9552 } else {
9553 state_root_cache = None;
9554 state_root(&accounts)
9555 }
9556 })
9557 .unwrap_or_default()
9558 };
9559 let block_access_list_hash = cache_db
9560 .bal_state
9561 .take_built_alloy_bal()
9562 .map(|bal| compute_block_access_list_hash(bal.as_slice()));
9563 let header = Header {
9564 block_access_list_hash,
9565 logs_bloom: receipts.iter().fold(Bloom::ZERO, |mut bloom, receipt| {
9566 bloom.accrue_bloom(receipt.logs_bloom());
9567 bloom
9568 }),
9569 transactions_root: calculate_transaction_root(&transaction_envelopes),
9570 receipts_root: calculate_receipt_root(&receipts),
9571 parent_hash,
9572 beneficiary: block_env.beneficiary,
9573 state_root,
9574 difficulty: block_env.difficulty,
9575 number: rpc_block_number,
9576 gas_limit: block_env.gas_limit,
9577 gas_used,
9578 timestamp: block_env.timestamp.saturating_to(),
9579 extra_data: base_fee_extra_data.clone(),
9580 mix_hash: block_env.prevrandao.unwrap_or_default(),
9581 nonce: Default::default(),
9582 base_fee_per_gas: (spec_id >= SpecId::LONDON).then_some(block_env.basefee),
9583 withdrawals_root: (spec_id >= SpecId::SHANGHAI).then_some(EMPTY_WITHDRAWALS),
9584 blob_gas_used: is_cancun.then_some(block_blob_gas_used),
9585 excess_blob_gas: if is_cancun { block_env.blob_excess_gas() } else { None },
9586 parent_beacon_block_root: ethereum_transitions.and_then(|transitions| {
9587 (transitions.hardfork >= EthereumHardfork::Cancun)
9588 .then_some(transitions.parent_beacon_block_root.unwrap_or_default())
9589 }),
9590 requests_hash: ethereum_transitions.and_then(|transitions| {
9591 (transitions.hardfork >= EthereumHardfork::Prague)
9592 .then(|| requests.requests_hash())
9593 }),
9594 ..Default::default()
9595 };
9596 let block_hash = header.hash_slow();
9597 let withdrawals = (spec_id >= SpecId::SHANGHAI).then_some(Default::default());
9598 let size = U256::from(
9599 BlockBody {
9600 transactions: transaction_envelopes,
9601 ommers: vec![],
9602 withdrawals: withdrawals.clone(),
9603 }
9604 .into_block(header.clone())
9605 .length(),
9606 );
9607 for (transaction_index, transaction) in transactions.iter_mut().enumerate() {
9608 transaction.block_hash = Some(block_hash);
9609 transaction.block_number = Some(header.number);
9610 transaction.transaction_index = Some(transaction_index as u64);
9611 transaction.block_timestamp = Some(header.timestamp);
9612 }
9613 let mut block = alloy_rpc_types::Block {
9614 header: AnyRpcHeader {
9615 hash: block_hash,
9616 inner: header.into(),
9617 total_difficulty: None,
9618 size: Some(size),
9619 },
9620 uncles: vec![],
9621 transactions: BlockTransactions::Full(transactions),
9622 withdrawals,
9623 };
9624
9625 if !return_full_transactions {
9626 block.transactions.convert_to_hashes();
9627 }
9628
9629 for res in &mut call_res {
9630 res.logs.iter_mut().for_each(|log| {
9631 log.block_hash = Some(block.header.hash);
9632 });
9633 }
9634
9635 let mut block = AnyRpcBlock::new(WithOtherFields::new(block));
9636 if is_arbitrum(self.protocol_chain_id()) {
9637 block
9638 .other
9639 .insert("l1BlockNumber".to_string(), serde_json::json!(block_env.number));
9640 }
9641 let simulated_block = SimulatedBlock { inner: block, calls: call_res };
9642
9643 parent_hash = block_hash;
9644 cache_db.cache.block_hashes.insert(U256::from(rpc_block_number), block_hash);
9645 inherited_block_env.beneficiary = block_env.beneficiary;
9646 inherited_block_env.difficulty = block_env.difficulty;
9647 inherited_block_env.gas_limit = block_env.gas_limit;
9648 let header = &simulated_block.inner.header;
9650 next_base_fee = self.fees.calculate_next_block_base_fee_from_header(&header.inner);
9651 parent_base_fee_per_gas = header.base_fee_per_gas().unwrap_or_default();
9652 parent_excess_blob_gas = header.excess_blob_gas().unwrap_or_default();
9653 parent_blob_gas_used = header.blob_gas_used().unwrap_or_default();
9654
9655 block_res.push(simulated_block);
9656 #[cfg(feature = "monad")]
9657 self::monad::advance_block_context(&mut monad_context);
9658 }
9659
9660 Ok(block_res)
9661 };
9662
9663 match block_request {
9664 Some(BlockRequest::Pending(pool_transactions)) => {
9665 self.with_pending_block(pool_transactions, |state, block| {
9666 let header = &block.block.header;
9667 let parent_fees = self.fees.calculate_parent_header_fees(header);
9668 let optimism_jovian =
9669 self.is_optimism_jovian_at_header(header, parent_fees.optimism_jovian);
9670 let monad_context = self.active_monad_context_before_mined_transaction(
9671 &block.block,
9672 block.block.body.transactions.len(),
9673 )?;
9674 #[cfg(feature = "monad")]
9675 let monad_context = {
9676 let mut monad_context = monad_context;
9677 self::monad::advance_block_context(&mut monad_context);
9678 monad_context
9679 };
9680 simulate_at(
9681 state,
9682 self.block_env_from_header(header),
9683 header.number(),
9684 header.timestamp(),
9685 header.hash_slow(),
9686 parent_fees.base_fee,
9687 monad_context,
9688 header.base_fee_per_gas().unwrap_or_default(),
9689 header.excess_blob_gas().unwrap_or_default(),
9690 header.blob_gas_used().unwrap_or_default(),
9691 parent_fees.extra_data,
9692 optimism_jovian,
9693 )
9694 })
9695 .await?
9696 }
9697 block_request => {
9698 let base_block_number = match block_request.as_ref() {
9699 Some(BlockRequest::Number(number)) => BlockNumber::Number(*number),
9700 Some(BlockRequest::Pending(_)) => unreachable!(),
9701 None => BlockNumber::Latest,
9702 };
9703 let base_block = self
9704 .block_by_number(base_block_number)
9705 .await?
9706 .ok_or(BlockchainError::BlockNotFound)?;
9707 let base_number = base_block.header.number();
9708 let base_timestamp = base_block.header.timestamp();
9709 let base_hash = base_block.header.hash;
9710 let parent_fees = self.fees.calculate_parent_header_fees(&base_block.header.inner);
9711 let optimism_jovian = self.is_optimism_jovian_at_header(
9712 &base_block.header.inner,
9713 parent_fees.optimism_jovian,
9714 );
9715
9716 #[cfg(feature = "monad")]
9717 let monad_context = if self.is_monad() {
9718 Some(self.monad_context_for_child_of_block_number(base_number).await?)
9719 } else {
9720 None
9721 };
9722 #[cfg(not(feature = "monad"))]
9723 let monad_context = None;
9724
9725 self.with_database_at(block_request, |state, block_env| {
9726 simulate_at(
9727 state,
9728 block_env,
9729 base_number,
9730 base_timestamp,
9731 base_hash,
9732 parent_fees.base_fee,
9733 monad_context,
9734 base_block.header.base_fee_per_gas().unwrap_or_default(),
9735 base_block.header.excess_blob_gas().unwrap_or_default(),
9736 base_block.header.blob_gas_used().unwrap_or_default(),
9737 parent_fees.extra_data,
9738 optimism_jovian,
9739 )
9740 })
9741 .await?
9742 }
9743 }
9744 }
9745
9746 pub fn get_blob_by_tx_hash(&self, hash: B256) -> Result<Option<Vec<alloy_consensus::Blob>>> {
9747 let storage = self.blockchain.storage.read();
9748 Ok(storage.transactions.get(&hash).and_then(|mined| {
9749 storage
9750 .blocks
9751 .get(&mined.block_hash)?
9752 .body
9753 .transactions
9754 .get(mined.info.transaction_index as usize)?
9755 .as_ref()
9756 .sidecar()
9757 .map(|sidecar| sidecar.sidecar.blobs().to_vec())
9758 }))
9759 }
9760
9761 pub async fn set_fee_token(&self, user: Address, token: Address) -> DatabaseResult<()> {
9763 self.with_tempo_storage(|| {
9764 let mut fee_manager = TipFeeManager::new();
9765 fee_manager
9766 .set_user_token(user, IFeeManager::setUserTokenCall { token })
9767 .map_err(tempo_db_err)
9768 })
9769 .await
9770 }
9771
9772 pub async fn set_validator_fee_token(
9774 &self,
9775 validator: Address,
9776 token: Address,
9777 ) -> DatabaseResult<()> {
9778 self.with_tempo_storage(|| {
9779 let mut fee_manager = TipFeeManager::new();
9780 fee_manager
9782 .set_validator_token(
9783 validator,
9784 IFeeManager::setValidatorTokenCall { token },
9785 Address::ZERO,
9786 )
9787 .map_err(tempo_db_err)
9788 })
9789 .await
9790 }
9791
9792 pub async fn set_fee_amm_liquidity(
9794 &self,
9795 user_token: Address,
9796 validator_token: Address,
9797 amount: U256,
9798 ) -> DatabaseResult<()> {
9799 let admin = Address::repeat_byte(0x11);
9801 self.with_tempo_storage(|| {
9802 for &token_address in &[user_token, validator_token] {
9805 let mut token = TIP20Token::from_address(token_address).map_err(tempo_db_err)?;
9806 token
9807 .grant_role_internal(admin, TIP20Token::issuer_role())
9808 .map_err(tempo_db_err)?;
9809 token.mint(admin, ITIP20::mintCall { to: admin, amount }).map_err(tempo_db_err)?;
9810 }
9811 let mut fee_manager = TipFeeManager::new();
9812 fee_manager
9813 .mint(admin, user_token, validator_token, amount, admin)
9814 .map_err(tempo_db_err)?;
9815 Ok(())
9816 })
9817 .await
9818 }
9819
9820 pub async fn set_tip20_balance(
9822 &self,
9823 address: Address,
9824 token_address: Address,
9825 balance: U256,
9826 ) -> DatabaseResult<()> {
9827 if self.try_set_tip20_balance(address, token_address, balance).await? {
9828 return Ok(());
9829 }
9830
9831 Err(tempo_db_err(format!("address {token_address} is not a deployed TIP-20 token")))
9832 }
9833
9834 pub async fn try_set_tip20_balance(
9836 &self,
9837 address: Address,
9838 token_address: Address,
9839 balance: U256,
9840 ) -> DatabaseResult<bool> {
9841 self.with_tempo_storage(|| {
9842 if !TIP20Factory::new().is_tip20(token_address).map_err(tempo_db_err)? {
9843 return Ok(false);
9844 }
9845
9846 let mut token = TIP20Token::from_address(token_address).map_err(tempo_db_err)?;
9847 token.balances[address].write(balance).map_err(tempo_db_err)?;
9848 Ok(true)
9849 })
9850 .await
9851 }
9852
9853 async fn with_tempo_storage<R>(&self, f: impl FnOnce() -> R) -> R {
9856 let hardfork = self.hardfork();
9857 let (chain_id, timestamp, block_number) = {
9859 let env = self.evm_env.read();
9860 (env.cfg_env.chain_id, env.block_env.timestamp, env.block_env.number.to::<u64>())
9861 };
9862 let mut db = self.db.write().await;
9863 let mut storage = AnvilStorageProvider::new(
9864 &mut **db,
9865 chain_id,
9866 timestamp,
9867 block_number,
9868 hardfork.into(),
9869 );
9870 StorageCtx::enter(&mut storage, f)
9871 }
9872}
9873
9874fn tempo_db_err<E: std::fmt::Display>(e: E) -> DatabaseError {
9876 DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}")))
9877}
9878
9879fn get_pool_transactions_nonce(
9881 pool_transactions: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
9882 address: Address,
9883) -> Option<u64> {
9884 if let Some(highest_nonce) = pool_transactions
9885 .iter()
9886 .filter(|tx| {
9887 *tx.pending_transaction.sender() == address
9888 && !tx.pending_transaction.transaction.as_ref().has_nonzero_tempo_nonce_key()
9889 })
9890 .map(|tx| tx.pending_transaction.nonce())
9891 .max()
9892 {
9893 let tx_count = highest_nonce.saturating_add(1);
9894 return Some(tx_count);
9895 }
9896 None
9897}
9898
9899impl<N: Network> Backend<N>
9900where
9901 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
9902{
9903 fn validate_mining_pool_transaction_for(
9905 &self,
9906 pool_tx: &PoolTransaction<FoundryTxEnvelope>,
9907 account: &AccountInfo,
9908 evm_env: &EvmEnv,
9909 ) -> Result<(), InvalidTransactionError> {
9910 #[cfg(feature = "monad")]
9911 if self.validate_monad_mining_pool_transaction_for(pool_tx, account, evm_env)? {
9912 return Ok(());
9913 }
9914
9915 self.validate_pool_transaction_for(&pool_tx.pending_transaction, account, evm_env)
9916 }
9917
9918 async fn validate_tempo_pool_transaction(
9926 &self,
9927 pending: &PendingTransaction<FoundryTxEnvelope>,
9928 evm_env: &EvmEnv,
9929 ) -> Result<(), BlockchainError> {
9930 let tx = pending.transaction.as_ref();
9931 let tx_env: TempoTxEnv =
9932 FromTxWithEncoded::from_encoded_tx(tx, *pending.sender(), tx.encoded_2718().into());
9933 let evm_env = evm_env.clone();
9934 self.with_database_at(None, move |state, _| {
9935 let cache_db = CacheDB::new(state);
9936 let mut inspector = self.build_inspector();
9937 let mut evm = TempoEvmFactory::default().create_evm_with_inspector(
9938 WrapDatabaseRef(&cache_db),
9939 Self::build_tempo_evm_env(&evm_env, self.tempo_hardfork()),
9940 &mut inspector,
9941 );
9942 self.inject_tempo_precompiles(&mut evm, &evm_env);
9943 evm.configure_for_pool();
9944 let (result, _) = evm.validate_pool_transaction(tx_env);
9945 result.map(drop).map_err(|err| match err {
9946 EVMError::Transaction(TempoInvalidTransaction::EthInvalidTransaction(
9949 InvalidTransaction::LackOfFundForMaxFee { fee, balance },
9950 )) => InvalidTransactionError::TempoInsufficientFeeTokenBalance {
9951 balance: *balance,
9952 required: *fee,
9953 }
9954 .into(),
9955 err => err.into(),
9956 })
9957 })
9958 .await?
9959 }
9960}
9961
9962#[async_trait::async_trait]
9963impl<N: Network> TransactionValidator<FoundryTxEnvelope> for Backend<N>
9964where
9965 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
9966{
9967 async fn validate_pool_transaction(
9968 &self,
9969 tx: &PendingTransaction<FoundryTxEnvelope>,
9970 ) -> Result<(), BlockchainError> {
9971 let address = *tx.sender();
9972 let account = self.get_account(address).await?;
9973 #[cfg_attr(not(feature = "base"), allow(unused_mut))]
9974 let mut evm_env = self.next_evm_env(&**self.db.read().await)?;
9975 #[cfg(feature = "base")]
9976 if tx.transaction.as_ref().is_eip8130() {
9977 evm_env.block_env.timestamp = U256::from(self.eip8130_pool_timestamp());
9978 }
9979
9980 #[cfg(feature = "base")]
9981 if let FoundryTxEnvelope::Eip8130(signed) = tx.transaction.as_ref() {
9982 let body = signed.tx();
9983 let now = self.eip8130_pool_timestamp_ms();
9984 signed
9985 .validate_admission_static(self.chain_id().to())
9986 .map_err(|error| BlockchainError::Eip8130TransactionRejected(error.to_string()))?;
9987 signed
9988 .validate_timestamp(now)
9989 .map_err(|error| BlockchainError::Eip8130TransactionRejected(error.to_string()))?;
9990
9991 let (nonce_key_first_use, ready_for_execution) =
9992 if body.nonce_key == Eip8130Constants::NONCE_KEY_MAX {
9993 (false, true)
9994 } else if body.nonce_key.is_zero() {
9995 (account.nonce == 0, body.nonce_sequence == account.nonce)
9996 } else {
9997 let slot = NonceManagerStorage::nonce_slot(address, body.nonce_key).map_err(
9998 |error| BlockchainError::Eip8130TransactionRejected(error.to_string()),
9999 )?;
10000 let word = self.storage_at(NonceManagerStorage::ADDRESS, slot, None).await?;
10001 let channel_nonce = Eip8130Nonce::decode_channel_nonce(word.into()).to::<u64>();
10002 (channel_nonce == 0, body.nonce_sequence == channel_nonce)
10003 };
10004 let intrinsic = IntrinsicGas::compute(
10005 signed,
10006 &signed.encoded_2718(),
10007 &IntrinsicGasInput::worst_case(
10008 nonce_key_first_use,
10009 IntrinsicGasInput::sender_auto_delegated(&body.account_changes),
10010 body.payer.is_some(),
10011 ),
10012 )
10013 .map_err(|error| BlockchainError::Eip8130TransactionRejected(error.to_string()))?;
10014 if intrinsic.execution_gas_available(body.gas_limit).is_none() {
10015 return Err(BlockchainError::Eip8130TransactionRejected(
10016 "sender intrinsic gas exceeds transaction gas limit".to_string(),
10017 ));
10018 }
10019
10020 let payer = body.payer.unwrap_or(address);
10021 let payer_account =
10022 if payer == address { account.clone() } else { self.get_account(payer).await? };
10023 FeeCheck::validate_balance(
10024 payer_account.balance,
10025 body.gas_limit,
10026 intrinsic.payer_auth,
10027 body.max_fee_per_gas,
10028 )
10029 .map_err(|error| BlockchainError::Eip8130TransactionRejected(error.to_string()))?;
10030
10031 let db = self.db.read().await;
10036 let mut inspector = NoOpInspector;
10037 let validation = self.transact_envelope_with_inspector_ref_and_context(
10038 &**db,
10039 &evm_env,
10040 &mut inspector,
10041 tx,
10042 None,
10043 );
10044 Eip8130PhaseStatuses::clear();
10045 match validation {
10046 Ok(_) => {}
10047 Err(BlockchainError::Eip8130TransactionRejected(reason))
10048 if !ready_for_execution && reason.contains("is ahead of the channel nonce") => {
10049 }
10050 Err(error) => return Err(error),
10051 }
10052 }
10053
10054 if let FoundryTxEnvelope::Tempo(aa_tx) = tx.transaction.as_ref() {
10056 let tempo_tx = aa_tx.tx();
10057 let current_time = evm_env.block_env.timestamp.saturating_to::<u64>();
10058
10059 const AA_VALID_BEFORE_MIN_SECS: u64 = 3;
10061 if let Some(valid_before) = tempo_tx.valid_before.map(|v| v.get()) {
10062 let min_allowed = current_time.saturating_add(AA_VALID_BEFORE_MIN_SECS);
10063 if valid_before <= min_allowed {
10064 return Err(InvalidTransactionError::TempoValidBeforeExpired {
10065 valid_before,
10066 min_allowed,
10067 }
10068 .into());
10069 }
10070
10071 let hardfork = self.tempo_hardfork();
10072 if hardfork.is_t1() && tempo_tx.is_expiring_nonce_tx() {
10073 let max_expiry_secs = hardfork.expiring_nonce_max_expiry_secs();
10074 let max_allowed = current_time.saturating_add(max_expiry_secs);
10075 if valid_before > max_allowed {
10076 return Err(InvalidTransactionError::TempoValidBeforeTooFar {
10077 valid_before,
10078 max_expiry_secs,
10079 max_allowed,
10080 }
10081 .into());
10082 }
10083 }
10084 }
10085
10086 const AA_VALID_AFTER_MAX_SECS: u64 = 120;
10090 if let Some(valid_after) = tempo_tx.valid_after.map(|v| v.get()) {
10091 let max_allowed = current_time.saturating_add(AA_VALID_AFTER_MAX_SECS);
10092 if valid_after > max_allowed {
10093 return Err(InvalidTransactionError::TempoValidAfterTooFar {
10094 valid_after,
10095 max_valid_after_secs: AA_VALID_AFTER_MAX_SECS,
10096 max_allowed,
10097 }
10098 .into());
10099 }
10100 }
10101 }
10102
10103 self.validate_pool_transaction_for(tx, &account, &evm_env)?;
10104
10105 if self.is_tempo() && !self.disable_pool_balance_checks {
10108 self.validate_tempo_pool_transaction(tx, &evm_env).await?;
10109 }
10110
10111 Ok(())
10112 }
10113
10114 fn validate_pool_transaction_for(
10115 &self,
10116 pending: &PendingTransaction<FoundryTxEnvelope>,
10117 account: &AccountInfo,
10118 evm_env: &EvmEnv,
10119 ) -> Result<(), InvalidTransactionError> {
10120 let tx = &pending.transaction;
10121
10122 if let Some(tx_chain_id) = tx.chain_id() {
10123 let chain_id = self.chain_id();
10124 if chain_id.to::<u64>() != tx_chain_id {
10125 if let FoundryTxEnvelope::Legacy(tx) = tx.as_ref() {
10126 if evm_env.cfg_env.spec >= SpecId::SPURIOUS_DRAGON && tx.chain_id().is_none() {
10128 debug!(target: "backend", ?chain_id, ?tx_chain_id, "incompatible EIP155-based V");
10129 return Err(InvalidTransactionError::IncompatibleEIP155);
10130 }
10131 } else {
10132 debug!(target: "backend", ?chain_id, ?tx_chain_id, "invalid chain id");
10133 return Err(InvalidTransactionError::InvalidChainId);
10134 }
10135 }
10136 }
10137
10138 if self.is_tempo() && !tx.value().is_zero() {
10140 warn!(target: "backend", "[{:?}] native value transfer not allowed in Tempo mode", tx.hash());
10141 return Err(InvalidTransactionError::TempoNativeValueTransfer);
10142 }
10143
10144 if self.is_tempo_hardfork_active(TempoHardfork::T5)
10146 && let FoundryTxEnvelope::Tempo(aa_tx) = tx.as_ref()
10147 {
10148 const MAX_TEMPO_AUTHORIZATIONS: usize = 16;
10149 let auth_count = aa_tx.tx().tempo_authorization_list.len();
10150 if auth_count > MAX_TEMPO_AUTHORIZATIONS {
10151 warn!(target: "backend", "[{:?}] Tempo tx has too many authorizations: {}", tx.hash(), auth_count);
10152 return Err(InvalidTransactionError::TempoTooManyAuthorizations {
10153 count: auth_count,
10154 max: MAX_TEMPO_AUTHORIZATIONS,
10155 });
10156 }
10157 }
10158
10159 #[cfg(feature = "base")]
10160 if let FoundryTxEnvelope::Eip8130(signed) = pending.transaction.as_ref() {
10161 signed
10162 .validate_admission_static(evm_env.cfg_env.chain_id)
10163 .map_err(|error| InvalidTransactionError::Eip8130(error.to_string()))?;
10164 signed
10165 .validate_timestamp(
10166 evm_env.block_env.timestamp.saturating_to::<u64>().saturating_mul(1_000),
10167 )
10168 .map_err(|error| InvalidTransactionError::Eip8130(error.to_string()))?;
10169 }
10170
10171 #[cfg(any(feature = "base", feature = "optimism"))]
10173 let is_deposit_tx = pending.transaction.as_ref().is_deposit();
10174 #[cfg(not(any(feature = "base", feature = "optimism")))]
10175 let is_deposit_tx = false;
10176 #[cfg(feature = "base")]
10177 let is_eip8130_tx = pending.transaction.as_ref().is_eip8130();
10178 #[cfg(not(feature = "base"))]
10179 let is_eip8130_tx = false;
10180 let is_tempo_tx = pending.transaction.as_ref().is_tempo();
10181 let nonce = tx.nonce();
10182 if nonce < account.nonce && !is_deposit_tx && !is_eip8130_tx && !is_tempo_tx {
10183 debug!(target: "backend", "[{:?}] nonce too low", tx.hash());
10184 return Err(InvalidTransactionError::NonceTooLow);
10185 }
10186
10187 #[cfg(feature = "monad")]
10188 self.validate_monad_transaction_type(tx)?;
10189
10190 if evm_env.cfg_env.spec >= SpecId::CANCUN && tx.is_eip4844() {
10192 let blob_tx = match tx.as_ref() {
10194 FoundryTxEnvelope::Eip4844(tx) => tx.tx(),
10195 _ => unreachable!(),
10196 };
10197
10198 let blob_count = blob_tx.tx().blob_versioned_hashes.len();
10199
10200 if blob_count == 0 {
10202 return Err(InvalidTransactionError::NoBlobHashes);
10203 }
10204
10205 let max_blobs_per_tx = self.blob_params().max_blobs_per_tx as usize;
10207 if blob_count > max_blobs_per_tx {
10208 return Err(InvalidTransactionError::TooManyBlobs(blob_count, max_blobs_per_tx));
10209 }
10210
10211 if !self.skip_blob_validation(Some(*pending.sender()))
10213 && let Err(err) = blob_tx.validate(EnvKzgSettings::default().get())
10214 {
10215 return Err(InvalidTransactionError::BlobTransactionValidationError(err));
10216 }
10217 }
10218
10219 if evm_env.cfg_env.spec >= SpecId::SHANGHAI && tx.kind().is_create() {
10221 let max_initcode_size = evm_env
10222 .cfg_env
10223 .limit_contract_code_size
10224 .map(|limit| limit.saturating_mul(2))
10225 .unwrap_or_else(|| self.max_initcode_size(evm_env));
10226 if tx.input().len() > max_initcode_size {
10227 return Err(InvalidTransactionError::MaxInitCodeSizeExceeded);
10228 }
10229 }
10230
10231 if !self.disable_pool_balance_checks {
10233 let min_gas = if evm_env.cfg_env.enable_amsterdam_eip2780 {
10236 eip2780::TX_BASE_COST
10237 } else {
10238 MIN_TRANSACTION_GAS as u64
10239 };
10240 if tx.gas_limit() < min_gas {
10241 debug!(target: "backend", "[{:?}] gas too low", tx.hash());
10242 return Err(InvalidTransactionError::GasTooLow);
10243 }
10244
10245 if !evm_env.cfg_env.disable_block_gas_limit
10247 && tx.gas_limit() > evm_env.block_env.gas_limit
10248 {
10249 debug!(target: "backend", "[{:?}] gas too high", tx.hash());
10250 return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
10251 detail: String::from("tx.gas_limit > env.block.gas_limit"),
10252 }));
10253 }
10254
10255 if evm_env.cfg_env.tx_gas_limit_cap.is_none()
10257 && tx.gas_limit() > self.tx_gas_limit_cap(evm_env)
10258 {
10259 debug!(target: "backend", "[{:?}] gas too high", tx.hash());
10260 return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
10261 detail: String::from("tx.gas_limit > resolved tx gas limit cap"),
10262 }));
10263 }
10264
10265 if evm_env.cfg_env.spec >= SpecId::LONDON {
10267 if tx.max_fee_per_gas() < evm_env.block_env.basefee.into() && !is_deposit_tx {
10268 debug!(target: "backend", "max fee per gas={}, too low, block basefee={}", tx.max_fee_per_gas(), evm_env.block_env.basefee);
10269 return Err(InvalidTransactionError::FeeCapTooLow);
10270 }
10271
10272 if !evm_env.cfg_env.disable_priority_fee_check
10273 && let (Some(max_priority_fee_per_gas), max_fee_per_gas) =
10274 (tx.as_ref().max_priority_fee_per_gas(), tx.as_ref().max_fee_per_gas())
10275 && max_priority_fee_per_gas > max_fee_per_gas
10276 {
10277 debug!(target: "backend", "max priority fee per gas={}, too high, max fee per gas={}", max_priority_fee_per_gas, max_fee_per_gas);
10278 return Err(InvalidTransactionError::TipAboveFeeCap);
10279 }
10280 }
10281
10282 if evm_env.cfg_env.spec >= SpecId::CANCUN
10284 && tx.is_eip4844()
10285 && let Some(max_fee_per_blob_gas) = tx.max_fee_per_blob_gas()
10286 && let Some(blob_gas_and_price) = &evm_env.block_env.blob_excess_gas_and_price
10287 && max_fee_per_blob_gas < blob_gas_and_price.blob_gasprice
10288 {
10289 debug!(target: "backend", "max fee per blob gas={}, too low, block blob gas price={}", max_fee_per_blob_gas, blob_gas_and_price.blob_gasprice);
10290 return Err(InvalidTransactionError::BlobFeeCapTooLow(
10291 max_fee_per_blob_gas,
10292 blob_gas_and_price.blob_gasprice,
10293 ));
10294 }
10295
10296 let value = tx.value();
10297 match tx.as_ref() {
10298 #[cfg(any(feature = "base", feature = "optimism"))]
10299 FoundryTxEnvelope::Deposit(deposit_tx) => {
10300 if value > account.balance + U256::from(deposit_tx.mint) {
10306 debug!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance + U256::from(deposit_tx.mint), value, *pending.sender());
10307 return Err(InvalidTransactionError::InsufficientFunds);
10308 }
10309 }
10310 #[cfg(feature = "base")]
10311 FoundryTxEnvelope::Eip8130(_) => {
10312 }
10315 FoundryTxEnvelope::Tempo(_) => {
10316 }
10319 _ if self.is_tempo() => {
10320 }
10324 #[cfg(feature = "monad")]
10325 _ if self.validate_monad_transaction_funds(pending, account, evm_env)? => {}
10326 _ => {
10327 let max_cost = (tx.gas_limit() as u128)
10328 .saturating_mul(tx.max_fee_per_gas())
10329 .saturating_add(
10330 tx.blob_gas_used()
10331 .map(|g| g as u128)
10332 .unwrap_or(0)
10333 .mul(tx.max_fee_per_blob_gas().unwrap_or(0)),
10334 );
10335 let req_funds =
10337 max_cost.checked_add(value.saturating_to()).ok_or_else(|| {
10338 debug!(target: "backend", "[{:?}] cost too high", tx.hash());
10339 InvalidTransactionError::InsufficientFunds
10340 })?;
10341 if account.balance < U256::from(req_funds) {
10342 debug!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender());
10343 return Err(InvalidTransactionError::InsufficientFunds);
10344 }
10345 }
10346 }
10347 }
10348 Ok(())
10349 }
10350
10351 fn validate_for(
10352 &self,
10353 tx: &PendingTransaction<FoundryTxEnvelope>,
10354 account: &AccountInfo,
10355 evm_env: &EvmEnv,
10356 ) -> Result<(), InvalidTransactionError> {
10357 self.validate_pool_transaction_for(tx, account, evm_env)?;
10358 #[cfg(feature = "base")]
10361 if tx.transaction.as_ref().is_eip8130() {
10362 return Ok(());
10363 }
10364 if tx.nonce() > account.nonce {
10365 return Err(InvalidTransactionError::NonceTooHigh);
10366 }
10367 Ok(())
10368 }
10369}
10370
10371fn rehash<T>(signed: Signed<T>, hash: B256) -> Signed<T>
10373where
10374 T: alloy_consensus::transaction::RlpEcdsaEncodableTx,
10375{
10376 let (t, sig, _) = signed.into_parts();
10377 Signed::new_unchecked(t, sig, hash)
10378}
10379
10380fn build_rpc_transaction(
10381 envelope: AnyTxEnvelope,
10382 from: Address,
10383 block: Option<&Block>,
10384 info: Option<&TransactionInfo>,
10385 effective_gas_price: Option<u128>,
10386) -> AnyRpcTransaction {
10387 let tx = Transaction {
10388 inner: Recovered::new_unchecked(envelope, from),
10389 block_hash: block.map(|block| block.header.hash_slow()),
10390 block_number: block.map(|block| block.header.number()),
10391 transaction_index: info.map(|info| info.transaction_index),
10392 effective_gas_price,
10393 block_timestamp: block.map(|block| block.header.timestamp()),
10394 };
10395 AnyRpcTransaction::from(WithOtherFields::new(tx))
10396}
10397
10398pub fn transaction_build(
10400 tx_hash: Option<B256>,
10401 eth_transaction: MaybeImpersonatedTransaction<FoundryTxEnvelope>,
10402 block: Option<&Block>,
10403 info: Option<TransactionInfo>,
10404 base_fee: Option<u64>,
10405) -> AnyRpcTransaction {
10406 let mined_from = info.as_ref().map(|info| info.from);
10407
10408 #[cfg(any(feature = "base", feature = "optimism"))]
10409 if let FoundryTxEnvelope::Deposit(deposit_tx) = eth_transaction.as_ref() {
10410 let dep_tx = deposit_tx;
10411
10412 let ser = serde_json::to_value(dep_tx).expect("could not serialize TxDeposit");
10413 let maybe_deposit_fields = OtherFields::try_from(ser);
10414
10415 match maybe_deposit_fields {
10416 Ok(mut fields) => {
10417 fields.insert("v".to_string(), serde_json::to_value("0x0").unwrap());
10420 fields.insert("r".to_string(), serde_json::to_value(B256::ZERO).unwrap());
10421 fields.insert(String::from("s"), serde_json::to_value(B256::ZERO).unwrap());
10422 fields.insert(String::from("nonce"), serde_json::to_value("0x0").unwrap());
10423
10424 let inner = UnknownTypedTransaction {
10425 ty: AnyTxType(DEPOSIT_TX_TYPE_ID),
10426 fields,
10427 memo: Default::default(),
10428 };
10429
10430 let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
10431 hash: tx_hash.unwrap_or_else(|| eth_transaction.hash()),
10432 inner,
10433 });
10434
10435 return build_rpc_transaction(
10436 envelope,
10437 mined_from.unwrap_or(deposit_tx.from),
10438 block,
10439 info.as_ref(),
10440 None,
10441 );
10442 }
10443 Err(_) => {
10444 error!(target: "backend", "failed to serialize deposit transaction");
10445 }
10446 }
10447 }
10448
10449 #[cfg(feature = "base")]
10450 if let FoundryTxEnvelope::Eip8130(eip8130_tx) = eth_transaction.as_ref() {
10451 let from = mined_from.unwrap_or_else(|| eth_transaction.recover().unwrap_or_default());
10452 let rpc_tx = BaseRpcTransaction::from_transaction(
10453 Recovered::new_unchecked(BaseTxEnvelope::Eip8130(eip8130_tx.clone()), from),
10454 BaseTransactionInfo::default(),
10455 );
10456 let mut fields = OtherFields::try_from(
10457 serde_json::to_value(rpc_tx)
10458 .expect("could not serialize canonical EIP-8130 transaction"),
10459 )
10460 .expect("EIP-8130 transaction must serialize to an object");
10461 for key in
10462 ["hash", "from", "blockHash", "blockNumber", "transactionIndex", "effectiveGasPrice"]
10463 {
10464 fields.remove(key);
10465 }
10466 let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
10467 hash: eth_transaction.hash(),
10468 inner: UnknownTypedTransaction {
10469 ty: AnyTxType(EIP8130_TRANSACTION_TYPE),
10470 fields,
10471 memo: Default::default(),
10472 },
10473 });
10474 return build_rpc_transaction(envelope, from, block, info.as_ref(), None);
10475 }
10476
10477 if let FoundryTxEnvelope::Celo(tx) = eth_transaction.as_ref() {
10478 let mut fields =
10479 OtherFields::try_from(serde_json::to_value(tx).expect("valid CIP-64 transaction"))
10480 .expect("CIP-64 transaction serializes to an object");
10481 fields.remove("hash");
10482 let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
10483 hash: tx_hash.unwrap_or_else(|| eth_transaction.hash()),
10484 inner: UnknownTypedTransaction {
10485 ty: AnyTxType(foundry_primitives::CIP64_TX_TYPE),
10486 fields,
10487 memo: Default::default(),
10488 },
10489 });
10490 let from = mined_from.unwrap_or_else(|| eth_transaction.recover().unwrap_or_default());
10491 let effective_gas_price = block.map(|_| eth_transaction.effective_gas_price(base_fee));
10492 return build_rpc_transaction(envelope, from, block, info.as_ref(), effective_gas_price);
10493 }
10494 if let FoundryTxEnvelope::Tempo(tempo_tx) = eth_transaction.as_ref() {
10495 let from = mined_from.unwrap_or_else(|| eth_transaction.recover().unwrap_or_default());
10496 let ser = serde_json::to_value(tempo_tx).expect("could not serialize Tempo transaction");
10497 let maybe_tempo_fields = OtherFields::try_from(ser);
10498
10499 match maybe_tempo_fields {
10500 Ok(fields) => {
10501 let inner = UnknownTypedTransaction {
10502 ty: AnyTxType(TEMPO_TX_TYPE_ID),
10503 fields,
10504 memo: Default::default(),
10505 };
10506
10507 let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
10508 hash: tx_hash.unwrap_or_else(|| eth_transaction.hash()),
10509 inner,
10510 });
10511
10512 return build_rpc_transaction(envelope, from, block, info.as_ref(), None);
10513 }
10514 Err(_) => {
10515 error!(target: "backend", "failed to serialize tempo transaction");
10516 }
10517 }
10518 }
10519
10520 let from = mined_from.unwrap_or_else(|| eth_transaction.recover().unwrap_or_default());
10521 let effective_gas_price = eth_transaction.effective_gas_price(base_fee);
10522
10523 let hash = tx_hash.unwrap_or_else(|| eth_transaction.hash());
10529
10530 let eth_envelope = FoundryTxEnvelope::from(eth_transaction)
10531 .try_into_eth()
10532 .expect("non-standard transactions are handled above");
10533
10534 let envelope = match eth_envelope {
10535 TxEnvelope::Legacy(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(rehash(s, hash))),
10536 TxEnvelope::Eip1559(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip1559(rehash(s, hash))),
10537 TxEnvelope::Eip2930(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip2930(rehash(s, hash))),
10538 TxEnvelope::Eip4844(s) => {
10539 let s = if block.is_some() { s.map(TxEip4844Variant::drop_sidecar) } else { s };
10540 AnyTxEnvelope::Ethereum(TxEnvelope::Eip4844(rehash(s, hash)))
10541 }
10542 TxEnvelope::Eip7702(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip7702(rehash(s, hash))),
10543 };
10544
10545 build_rpc_transaction(envelope, from, block, info.as_ref(), Some(effective_gas_price))
10546}
10547
10548pub fn prove_storage(
10554 storage: &alloy_primitives::map::U256Map<U256>,
10555 keys: &[B256],
10556) -> (B256, Vec<Vec<Bytes>>) {
10557 let keys: Vec<_> = keys.iter().map(|key| Nibbles::unpack(keccak256(key))).collect();
10558
10559 let mut builder = HashBuilder::default().with_proof_retainer(ProofRetainer::new(keys.clone()));
10560
10561 for (key, value) in trie_storage(storage) {
10562 builder.add_leaf(key, &value);
10563 }
10564
10565 let root = builder.root();
10566
10567 let mut proofs = Vec::new();
10568 let all_proof_nodes = builder.take_proof_nodes();
10569
10570 for proof_key in keys {
10571 let matching_proof_nodes =
10574 all_proof_nodes.matching_nodes_sorted(&proof_key).into_iter().map(|(_, node)| node);
10575 proofs.push(matching_proof_nodes.collect());
10576 }
10577
10578 (root, proofs)
10579}
10580
10581pub fn is_arbitrum(chain_id: u64) -> bool {
10582 if let Ok(chain) = NamedChain::try_from(chain_id) {
10583 return chain.is_arbitrum();
10584 }
10585 false
10586}
10587
10588fn commit_cache(db: &mut dyn Db, cache: revm::database::Cache) -> Result<(), BlockchainError> {
10590 let revm::database::Cache { accounts, contracts, .. } = cache;
10591 let mut changes = EvmState::default();
10592 for (address, db_account) in accounts {
10593 if db_account.account_state == AccountState::None {
10594 continue;
10595 }
10596
10597 let DbAccount { mut info, account_state, storage } = db_account;
10598 if account_state == AccountState::NotExisting && db.basic(address)?.is_none() {
10601 continue;
10602 }
10603 if info.code.is_none() {
10604 info.code = contracts.get(&info.code_hash()).cloned();
10605 }
10606 let mut account = Account::from(info);
10607 account.mark_touch();
10608 match account_state {
10609 AccountState::NotExisting => account.mark_selfdestruct(),
10610 AccountState::StorageCleared => account.mark_created(),
10611 AccountState::Touched => {}
10612 AccountState::None => unreachable!(),
10613 }
10614 for (slot, value) in storage {
10615 let original = if account_state.is_storage_cleared() {
10616 U256::ZERO
10617 } else {
10618 db.storage(address, slot)?
10619 };
10620 account
10621 .storage
10622 .insert(slot, EvmStorageSlot::new_changed(original, value, TransactionId::ZERO));
10623 }
10624 changes.insert(address, account);
10625 }
10626 db.commit(changes);
10627 Ok(())
10628}
10629
10630fn simulate_rpc_error(code: i64, message: impl Into<String>) -> BlockchainError {
10631 BlockchainError::RpcError(RpcError {
10632 code: ErrorCode::from(code),
10633 message: message.into().into(),
10634 data: None,
10635 })
10636}
10637
10638fn previously_deleted_accounts(
10639 accounts: &AddressMap<DbAccount>,
10640 addresses: impl IntoIterator<Item = Address>,
10641) -> Vec<Address> {
10642 addresses
10643 .into_iter()
10644 .filter(|address| {
10645 accounts
10646 .get(address)
10647 .is_some_and(|account| account.account_state == AccountState::NotExisting)
10648 })
10649 .collect()
10650}
10651
10652fn preserve_deleted_storage(
10653 accounts: &mut AddressMap<DbAccount>,
10654 previously_deleted: Vec<Address>,
10655) {
10656 for address in previously_deleted {
10657 if let Some(account) = accounts.get_mut(&address)
10658 && account.account_state != AccountState::NotExisting
10659 {
10660 account.account_state = AccountState::StorageCleared;
10661 }
10662 }
10663}
10664
10665fn simulate_transaction_error(error: InvalidTransactionError) -> BlockchainError {
10666 let code = match &error {
10667 InvalidTransactionError::NonceTooLow => -38010,
10668 InvalidTransactionError::NonceTooHigh => -38011,
10669 InvalidTransactionError::NonceMaxValue => -32603,
10670 InvalidTransactionError::FeeCapTooLow => -38012,
10671 InvalidTransactionError::GasTooLow | InvalidTransactionError::GasTooHigh(_) => -38013,
10672 InvalidTransactionError::InsufficientFunds
10673 | InvalidTransactionError::InsufficientFundsForTransfer => -38014,
10674 _ => return BlockchainError::InvalidTransaction(error),
10675 };
10676
10677 simulate_rpc_error(code, format!("err: {error}"))
10678}
10679
10680pub(in crate::eth) fn sanitize_simulation_blocks<T>(
10681 blocks: Vec<SimBlock<T>>,
10682 base_number: u64,
10683 base_timestamp: u64,
10684 block_interval: u64,
10685) -> Result<Vec<SimBlock<T>>, BlockchainError> {
10686 let block_interval = block_interval.max(1);
10687 let mut sanitized = Vec::with_capacity(blocks.len());
10688 let mut previous_number = base_number;
10689 let mut previous_timestamp = base_timestamp;
10690
10691 for mut block in blocks {
10692 let mut overrides = block.block_overrides.take().unwrap_or_default();
10693 let default_number = previous_number.checked_add(1).ok_or_else(|| {
10694 simulate_rpc_error(-38020, "block number overflow while constructing sequence")
10695 })?;
10696 let number =
10697 overrides.number.map(|number| number.saturating_to()).unwrap_or(default_number);
10698
10699 if number <= previous_number {
10700 return Err(simulate_rpc_error(
10701 -38020,
10702 format!("block numbers must be in order: {number} <= {previous_number}"),
10703 ));
10704 }
10705
10706 let gap = number - previous_number - 1;
10707 let remaining = MAX_SIMULATE_BLOCKS as usize - sanitized.len();
10708 if gap as usize >= remaining {
10709 return Err(simulate_rpc_error(-38026, "too many blocks"));
10710 }
10711
10712 for offset in 0..gap {
10713 let timestamp = previous_timestamp.checked_add(block_interval).ok_or_else(|| {
10714 simulate_rpc_error(-38021, "block timestamp overflow while filling number gap")
10715 })?;
10716 sanitized.push(SimBlock {
10717 block_overrides: Some(BlockOverrides {
10718 number: Some(U256::from(default_number + offset)),
10719 time: Some(timestamp),
10720 ..Default::default()
10721 }),
10722 state_overrides: None,
10723 calls: Vec::new(),
10724 });
10725 previous_timestamp = timestamp;
10726 }
10727
10728 let timestamp = match overrides.time {
10729 Some(timestamp) => timestamp,
10730 None => previous_timestamp.checked_add(block_interval).ok_or_else(|| {
10731 simulate_rpc_error(-38021, "block timestamp overflow while constructing sequence")
10732 })?,
10733 };
10734 if timestamp <= previous_timestamp {
10735 return Err(simulate_rpc_error(
10736 -38021,
10737 format!("block timestamps must be in order: {timestamp} <= {previous_timestamp}"),
10738 ));
10739 }
10740
10741 overrides.number = Some(U256::from(number));
10742 overrides.time = Some(timestamp);
10743 block.block_overrides = Some(overrides);
10744 sanitized.push(block);
10745 previous_number = number;
10746 previous_timestamp = timestamp;
10747 }
10748
10749 Ok(sanitized)
10750}
10751
10752fn unpack_execution_result<H: IntoInstructionResult>(
10754 result: ExecutionResult<H>,
10755) -> (InstructionResult, u64, Option<Output>, Vec<revm::primitives::Log>) {
10756 match result {
10757 ExecutionResult::Success { reason, gas, output, logs, .. } => {
10758 (reason.into(), gas.tx_gas_used(), Some(output), logs)
10759 }
10760 ExecutionResult::Revert { gas, output, logs, .. } => {
10761 (InstructionResult::Revert, gas.tx_gas_used(), Some(Output::Call(output)), logs)
10762 }
10763 ExecutionResult::Halt { reason, gas, logs, .. } => {
10764 (reason.into_instruction_result(), gas.tx_gas_used(), None, logs)
10765 }
10766 }
10767}
10768
10769fn arbitrum_replay_block_number(block: &AnyRpcBlock) -> U256 {
10770 block
10771 .other
10772 .get("l1BlockNumber")
10773 .cloned()
10774 .and_then(|number| serde_json::from_value(number).ok())
10775 .unwrap_or_else(|| U256::from(block.header().number()))
10776}
10777
10778pub use foundry_evm::core::evm::IntoInstructionResult;
10783
10784fn genesis_header(
10786 evm_env: &EvmEnv,
10787 base_fee: Option<u64>,
10788 timestamp: u64,
10789 genesis_number: u64,
10790) -> Header {
10791 let spec_id = *evm_env.spec_id();
10792 Header {
10793 timestamp,
10794 base_fee_per_gas: base_fee,
10795 gas_limit: evm_env.block_env.gas_limit,
10796 beneficiary: evm_env.block_env.beneficiary,
10797 difficulty: evm_env.block_env.difficulty,
10798 blob_gas_used: evm_env.block_env.blob_excess_gas_and_price.as_ref().map(|_| 0),
10799 excess_blob_gas: evm_env.block_env.blob_excess_gas(),
10800 number: genesis_number,
10801 parent_beacon_block_root: (spec_id >= SpecId::CANCUN).then_some(Default::default()),
10802 withdrawals_root: (spec_id >= SpecId::SHANGHAI).then_some(EMPTY_WITHDRAWALS),
10803 requests_hash: (spec_id >= SpecId::PRAGUE).then_some(EMPTY_REQUESTS_HASH),
10804 ..Default::default()
10805 }
10806}
10807
10808fn genesis_json_header(genesis: &Genesis) -> Header {
10810 let number = genesis.number.unwrap_or_default();
10811 let timestamp = genesis.timestamp;
10812 let is_london = genesis.config.is_london_active_at_block(number);
10813 let is_shanghai = genesis.config.is_shanghai_active_at_block_and_timestamp(number, timestamp);
10814 let is_cancun = genesis.config.is_cancun_active_at_block_and_timestamp(number, timestamp);
10815 let is_prague = genesis.config.prague_time.is_some_and(|fork| fork <= timestamp);
10816 let is_amsterdam = genesis.config.amsterdam_time.is_some_and(|fork| fork <= timestamp);
10817
10818 Header {
10819 number,
10820 parent_hash: genesis.parent_hash.unwrap_or_default(),
10821 gas_limit: genesis.gas_limit,
10822 difficulty: genesis.difficulty,
10823 nonce: genesis.nonce.into(),
10824 extra_data: genesis.extra_data.clone(),
10825 state_root: state_root_ref_unhashed(&genesis.alloc),
10826 timestamp,
10827 mix_hash: genesis.mix_hash,
10828 beneficiary: genesis.coinbase,
10829 base_fee_per_gas: is_london.then(|| {
10830 genesis
10831 .base_fee_per_gas
10832 .map(|fee| fee as u64)
10833 .unwrap_or(crate::eth::fees::INITIAL_BASE_FEE)
10834 }),
10835 withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
10836 parent_beacon_block_root: is_cancun.then_some(B256::ZERO),
10837 blob_gas_used: is_cancun.then_some(genesis.blob_gas_used.unwrap_or_default()),
10838 excess_blob_gas: is_cancun.then_some(genesis.excess_blob_gas.unwrap_or_default()),
10839 requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH),
10840 block_access_list_hash: is_amsterdam.then_some(EMPTY_BLOCK_ACCESS_LIST_HASH),
10841 slot_number: is_amsterdam.then_some(genesis.slot_number.unwrap_or_default()),
10842 ..Default::default()
10843 }
10844}
10845
10846fn foundry_header(networks: &NetworkConfigs, header: Header) -> FoundryHeader {
10848 if networks.is_tempo() { FoundryHeader::tempo(header) } else { header.into() }
10849}
10850
10851#[cfg(test)]
10852mod tests {
10853 use super::{
10854 BlockRequest, FeeDetails, ForkCacheNamespace, ForkCacheSource, InstructionResult,
10855 MiningCommitHook, Output, StagedForkCacheLease, StagedForkDbUser,
10856 arbitrum_replay_block_number,
10857 };
10858 use crate::{NodeConfig, config::ForkTransactionReplay, spawn};
10859 use alloy_network::{AnyHeader, AnyRpcBlock, AnyRpcHeader, TransactionBuilder};
10860 use alloy_primitives::{Address, B256, Bytes, U256};
10861 use alloy_provider::Provider;
10862 use alloy_rpc_types::{Block, BlockTransactions, TransactionRequest, state::EvmOverrides};
10863 use alloy_serde::WithOtherFields;
10864 use foundry_config::NamedChain;
10865 use foundry_evm::{
10866 backend::{BlockchainDb, BlockchainDbMeta},
10867 hardfork::{EthereumHardfork, FoundryHardfork},
10868 };
10869 use foundry_evm_networks::arbitrum;
10870 use std::sync::Arc;
10871 use tempfile::tempdir;
10872 use tokio::sync::Notify;
10873
10874 #[cfg(feature = "base")]
10875 use base_common_precompiles::{ActivationRegistryStorage, B20FactoryStorage};
10876 #[cfg(feature = "base")]
10877 use foundry_evm::{hardforks::BaseUpgrade, traces::CallTraceDecoderBuilder};
10878
10879 #[cfg(all(feature = "base", feature = "optimism"))]
10880 use alloy_consensus::Header;
10881 #[cfg(all(feature = "base", feature = "optimism"))]
10882 use foundry_evm::hardfork::OpHardfork;
10883 #[cfg(all(feature = "base", feature = "optimism"))]
10884 use foundry_evm_networks::NetworkConfigs;
10885
10886 fn test_cache_db(cache_path: std::path::PathBuf) -> BlockchainDb {
10887 let db = BlockchainDb::new(BlockchainDbMeta::default(), Some(cache_path));
10888 db.block_hashes().write().insert(U256::ZERO, B256::repeat_byte(0x11));
10889 db.cache().flush();
10890 db
10891 }
10892
10893 #[test]
10894 fn arbitrum_transaction_replay_uses_l1_block_number() {
10895 let header = AnyHeader { number: 75_219_831, ..Default::default() };
10896 let mut block = AnyRpcBlock::new(
10897 Block::new(
10898 AnyRpcHeader::from_sealed(header.seal(B256::ZERO)),
10899 BlockTransactions::Full(Vec::new()),
10900 )
10901 .into(),
10902 );
10903 block.other.insert("l1BlockNumber".to_string(), serde_json::json!("0x10276d3"));
10904
10905 assert_eq!(arbitrum_replay_block_number(&block), U256::from(16_938_707));
10906 }
10907
10908 #[cfg(all(feature = "base", feature = "optimism"))]
10909 #[tokio::test(flavor = "multi_thread")]
10910 async fn optimism_fork_uses_optimism_schedule_for_base_headers() {
10911 let timestamp = 1_710_374_401;
10912 let (_, origin) = spawn(
10913 NodeConfig::test_base()
10914 .with_hardfork(Some(BaseUpgrade::Ecotone.into()))
10915 .with_genesis_timestamp(Some(timestamp)),
10916 )
10917 .await;
10918 let (api, _) = spawn(
10919 NodeConfig::test()
10920 .with_networks(NetworkConfigs::with_optimism())
10921 .with_eth_rpc_url(Some(origin.http_endpoint())),
10922 )
10923 .await;
10924
10925 assert!(api.backend.is_optimism());
10926 assert_eq!(api.backend.hardfork(), OpHardfork::Ecotone.into());
10927 assert!(
10928 !api.backend
10929 .is_optimism_jovian_at_header(&Header { timestamp, ..Default::default() }, None)
10930 );
10931 }
10932
10933 #[tokio::test]
10934 async fn fork_arbitrum_transaction_replay_preserves_rpc_block_number() {
10935 const GENESIS_BLOCK: u64 = 101;
10936 const L1_BLOCK: u64 = 10;
10937 const REPLAY_BLOCK: u64 = GENESIS_BLOCK + 1;
10938
10939 let config = || {
10940 NodeConfig::test()
10941 .with_chain_id(Some(NamedChain::Arbitrum as u64))
10942 .with_genesis_block_number(Some(GENESIS_BLOCK))
10943 };
10944 let (_source_api, source_handle) = spawn(config()).await;
10945 let source_provider = source_handle.http_provider();
10946 let sender = source_provider.get_accounts().await.unwrap()[0];
10947 let receipt = source_provider
10948 .send_transaction(WithOtherFields::new(
10949 TransactionRequest::default()
10950 .with_from(sender)
10951 .with_to(arbitrum::ARB_SYS_ADDRESS)
10952 .with_input(Bytes::from(arbitrum::ARB_BLOCK_NUMBER_SELECTOR)),
10953 ))
10954 .await
10955 .unwrap()
10956 .get_receipt()
10957 .await
10958 .unwrap();
10959 let mut source_block = source_provider
10960 .get_block_by_hash(receipt.block_hash.unwrap())
10961 .full()
10962 .await
10963 .unwrap()
10964 .unwrap();
10965 source_block
10966 .other
10967 .insert("l1BlockNumber".to_string(), serde_json::json!(format!("0x{L1_BLOCK:x}")));
10968
10969 let (replay_api, _replay_handle) = spawn(config()).await;
10970 replay_api
10971 .backend
10972 .apply_fork_transaction_replay(ForkTransactionReplay { source_block, target_index: 0 })
10973 .await
10974 .unwrap();
10975
10976 let expected = arbitrum::arb_block_number_output(REPLAY_BLOCK);
10977 let replayed =
10978 replay_api.backend.mined_transaction_receipt(receipt.transaction_hash).unwrap();
10979 assert_eq!(replayed.out.unwrap(), expected);
10980 assert_eq!(replay_api.block_number().unwrap(), U256::from(REPLAY_BLOCK));
10981
10982 let output = replay_api
10983 .call(
10984 WithOtherFields::new(
10985 TransactionRequest::default()
10986 .with_to(arbitrum::ARB_SYS_ADDRESS)
10987 .with_input(Bytes::from(arbitrum::ARB_BLOCK_NUMBER_SELECTOR)),
10988 ),
10989 None,
10990 EvmOverrides::default(),
10991 )
10992 .await
10993 .unwrap();
10994 assert_eq!(output, expected);
10995 }
10996
10997 fn test_endpoint_identity(
10998 hardfork: Option<FoundryHardfork>,
10999 instance_id: Option<B256>,
11000 ) -> super::ForkEndpointIdentity {
11001 super::ForkEndpointIdentity {
11002 execution_chain_id: 1,
11003 source_chain_id: 1,
11004 network: None,
11005 network_profile: None,
11006 hardfork,
11007 instance_id,
11008 source_fork_block_number: None,
11009 source_fork_block_hash: None,
11010 }
11011 }
11012
11013 #[tokio::test]
11014 async fn missing_snapshot_block_does_not_change_head() {
11015 let (api, _handle) = spawn(NodeConfig::test()).await;
11016 let snapshot_hash = api.backend.best_hash();
11017 let snapshot = api.backend.create_state_snapshot().await;
11018 api.mine_one().await.unwrap();
11019 let best_number = api.backend.best_number();
11020 let best_hash = api.backend.best_hash();
11021 let head_wall_time = api.backend.time().last_block_wall_time();
11022
11023 api.backend.blockchain.storage.write().blocks.remove(&snapshot_hash);
11024 let err = api.backend.revert_state_snapshot(snapshot).await.unwrap_err();
11025
11026 assert!(matches!(err, super::BlockchainError::BlockNotFound));
11027 assert_eq!(api.backend.best_number(), best_number);
11028 assert_eq!(api.backend.best_hash(), best_hash);
11029 assert_eq!(api.backend.time().last_block_wall_time(), head_wall_time);
11030 }
11031
11032 #[tokio::test(flavor = "multi_thread")]
11033 async fn live_execution_snapshot_is_coherent_during_mining() {
11034 let (api, handle) = spawn(NodeConfig::test().with_no_mining(true)).await;
11035 let sender = handle.dev_wallets().next().unwrap().address();
11036 let recipient = Address::repeat_byte(0x11);
11037 let contract = Address::repeat_byte(0x22);
11038
11039 let mut code = vec![0x73];
11042 code.extend_from_slice(recipient.as_slice());
11043 code.extend_from_slice(&[
11044 0x31, 0x60, 0x00, 0x52, 0x43, 0x60, 0x20, 0x52, 0x60, 0x40, 0x60, 0x00, 0xf3,
11045 ]);
11046 api.anvil_set_code(contract, code.into()).await.unwrap();
11047 api.send_transaction(WithOtherFields::new(
11048 TransactionRequest::default().from(sender).to(recipient).value(U256::ONE),
11049 ))
11050 .await
11051 .unwrap();
11052
11053 let resolved_head = api.backend.best_number();
11054 let db_guard = api.backend.db.write().await;
11055
11056 let mining_api = api.clone();
11059 let mut mining = Box::pin(async move { mining_api.mine_one().await });
11060 assert!(futures::poll!(mining.as_mut()).is_pending());
11061
11062 let request = WithOtherFields::new(TransactionRequest::default().to(contract));
11063 let mut call = Box::pin(api.backend.call(
11064 request,
11065 FeeDetails::zero(),
11066 Some(BlockRequest::Number(resolved_head)),
11067 EvmOverrides::default(),
11068 ));
11069 assert!(futures::poll!(call.as_mut()).is_pending());
11070
11071 let mut pending_block = Box::pin(api.backend.pending_block(Vec::new()));
11072 assert!(futures::poll!(pending_block.as_mut()).is_pending());
11073
11074 let mut state = Box::pin(api.serialized_state(false));
11075 assert!(futures::poll!(state.as_mut()).is_pending());
11076
11077 let hook =
11080 MiningCommitHook { reached: Arc::new(Notify::new()), resume: Arc::new(Notify::new()) };
11081 *api.backend.mining_commit_hook.lock() = Some(hook.clone());
11082 drop(db_guard);
11083 let mining = tokio::spawn(mining);
11084 hook.reached.notified().await;
11085
11086 assert!(futures::poll!(call.as_mut()).is_pending());
11089 assert!(futures::poll!(pending_block.as_mut()).is_pending());
11090 assert!(futures::poll!(state.as_mut()).is_pending());
11091
11092 hook.resume.notify_one();
11093 mining.await.unwrap().unwrap();
11094
11095 let (exit, output, _, _) = call.await.unwrap();
11096 let pending_block = pending_block.await.unwrap();
11097 let state = state.await.unwrap();
11098
11099 assert_eq!(state.accounts[&recipient].balance, U256::ONE);
11100 assert_eq!(state.block.unwrap().number, U256::ONE);
11101 assert_eq!(state.best_block_number, Some(1));
11102
11103 assert_eq!(exit, InstructionResult::Return);
11104 let Some(Output::Call(output)) = output else { panic!("call did not return data") };
11105 assert_eq!(output.len(), 64);
11106 let balance = U256::from_be_slice(&output[..32]);
11107 let block_number = U256::from_be_slice(&output[32..]);
11108 assert_eq!((balance, block_number), (U256::ZERO, U256::ZERO));
11110
11111 assert_eq!(pending_block.block.header.number, api.backend.best_number() + 1);
11112 assert_eq!(pending_block.block.header.parent_hash, api.backend.best_hash());
11113 assert_eq!(pending_block.block.header.base_fee_per_gas, Some(875_175_000));
11114 }
11115
11116 async fn read_head_balance_while_mining(config: NodeConfig) -> U256 {
11118 let (api, handle) = spawn(config.with_no_mining(true)).await;
11119 let sender = handle.dev_wallets().next().unwrap().address();
11120 let recipient = Address::repeat_byte(0x11);
11121 api.send_transaction(WithOtherFields::new(
11122 TransactionRequest::default().from(sender).to(recipient).value(U256::from(1)),
11123 ))
11124 .await
11125 .unwrap();
11126
11127 let hook =
11130 MiningCommitHook { reached: Arc::new(Notify::new()), resume: Arc::new(Notify::new()) };
11131 *api.backend.mining_commit_hook.lock() = Some(hook.clone());
11132 let mining_api = api.clone();
11133 let mining = tokio::spawn(async move { mining_api.mine_one().await });
11134 hook.reached.notified().await;
11135
11136 let head = api.backend.best_number();
11138 assert_eq!(head, 0);
11139 let mut balance =
11140 Box::pin(api.backend.get_balance(recipient, Some(BlockRequest::Number(head))));
11141 assert!(futures::poll!(balance.as_mut()).is_pending());
11142
11143 hook.resume.notify_one();
11144 mining.await.unwrap().unwrap();
11145 assert_eq!(api.backend.best_number(), 1);
11146
11147 balance.await.unwrap()
11148 }
11149
11150 #[tokio::test(flavor = "multi_thread")]
11151 async fn head_number_read_waiting_on_mining_keeps_requested_state() {
11152 assert_eq!(read_head_balance_while_mining(NodeConfig::test()).await, U256::ZERO);
11154 }
11155
11156 #[tokio::test(flavor = "multi_thread")]
11157 async fn head_number_read_waiting_on_mining_without_history_reads_live_state() {
11158 let config = NodeConfig::test().set_pruned_history(Some(None));
11161 assert_eq!(read_head_balance_while_mining(config).await, U256::from(1));
11162 }
11163
11164 struct CacheFlushingDb(BlockchainDb);
11165
11166 impl Drop for CacheFlushingDb {
11167 fn drop(&mut self) {
11168 self.0.block_hashes().write().insert(U256::ONE, B256::repeat_byte(0x22));
11169 self.0.cache().flush();
11170 }
11171 }
11172
11173 #[test]
11174 fn staged_fork_cache_lease_waits_for_last_owner() {
11175 let root = tempdir().unwrap();
11176 let block_cache_dir = root.path().join("1");
11177 let cache_path = block_cache_dir.join("storage.json");
11178 let sibling_cache_path = block_cache_dir.join("storage-sibling.json");
11179 let db = test_cache_db(cache_path.clone());
11180 std::fs::write(&sibling_cache_path, b"sibling").unwrap();
11181 let lease = StagedForkCacheLease::new(db.clone(), Some(cache_path.clone()));
11182 let last_user = lease.clone();
11183
11184 lease.rollback().unwrap();
11185 assert!(!db.block_hashes().read().is_empty());
11186 drop(lease);
11187 assert!(!db.block_hashes().read().is_empty());
11188
11189 drop(last_user);
11190 assert!(db.block_hashes().read().is_empty());
11191 assert!(!cache_path.exists());
11192 assert!(sibling_cache_path.exists());
11193 }
11194
11195 #[tokio::test]
11196 async fn staged_fork_cache_lease_survives_task_cancellation() {
11197 let root = tempdir().unwrap();
11198 let block_cache_dir = root.path().join("1");
11199 let cache_path = block_cache_dir.join("storage.json");
11200 let cache_db = test_cache_db(cache_path.clone());
11201 let lease = StagedForkCacheLease::new(cache_db.clone(), Some(cache_path.clone()));
11202 let db = Arc::new(tokio::sync::RwLock::new(CacheFlushingDb(cache_db.clone())));
11203 let task_user = StagedForkDbUser { db: Some(Arc::clone(&db)), cache_lease: lease.clone() };
11204 drop(db);
11205 let task = tokio::spawn(async move {
11206 let _db = task_user.db().read().await;
11207 std::future::pending::<()>().await;
11208 });
11209 tokio::task::yield_now().await;
11210
11211 assert_eq!(Arc::strong_count(lease.0.as_ref().unwrap()), 2);
11213 lease.rollback().unwrap();
11214 drop(lease);
11215
11216 task.abort();
11217 assert!(task.await.unwrap_err().is_cancelled());
11218
11219 assert!(cache_db.block_hashes().read().is_empty());
11220 assert!(!cache_path.exists());
11221 }
11222
11223 #[test]
11224 fn staged_fork_cache_lease_disarm_preserves_committed_cache() {
11225 let root = tempdir().unwrap();
11226 let block_cache_dir = root.path().join("1");
11227 let cache_path = block_cache_dir.join("storage.json");
11228 let db = test_cache_db(cache_path.clone());
11229 let lease = StagedForkCacheLease::new(db.clone(), Some(cache_path.clone()));
11230
11231 lease.disarm();
11232 drop(lease);
11233
11234 assert!(!db.block_hashes().read().is_empty());
11235 assert!(cache_path.exists());
11236 }
11237
11238 #[test]
11239 fn fork_cache_namespace_preserves_sibling_endpoints() {
11240 let root = tempdir().unwrap();
11241 let target_file = "storage-target.json";
11242 let sibling_file = "storage-sibling.json";
11243 let regular_cache_entry = root.path().join("latest.json");
11244 std::fs::write(®ular_cache_entry, b"ordinary RPC cache entry").unwrap();
11245 for block in ["1", "2"] {
11246 let block_dir = root.path().join(block);
11247 std::fs::create_dir_all(&block_dir).unwrap();
11248 std::fs::write(block_dir.join(target_file), b"target").unwrap();
11249 std::fs::write(block_dir.join(sibling_file), b"sibling").unwrap();
11250 }
11251 let namespace = ForkCacheNamespace {
11252 chain_cache_dir: root.path().to_path_buf(),
11253 file_name: target_file.to_string(),
11254 };
11255
11256 namespace.invalidate().unwrap();
11257
11258 for block in ["1", "2"] {
11259 let block_dir = root.path().join(block);
11260 assert!(!block_dir.join(target_file).exists());
11261 assert!(block_dir.join(sibling_file).exists());
11262 }
11263 assert!(regular_cache_entry.exists());
11264 }
11265
11266 #[test]
11267 fn startup_fork_cache_user_rolls_back_after_database_flush() {
11268 let root = tempdir().unwrap();
11269 let block_cache_dir = root.path().join("1");
11270 let cache_path = block_cache_dir.join("storage.json");
11271 let cache_db = test_cache_db(cache_path.clone());
11272 let user = StagedForkDbUser {
11273 db: Some(Arc::new(tokio::sync::RwLock::new(CacheFlushingDb(cache_db.clone())))),
11274 cache_lease: StagedForkCacheLease::new(cache_db.clone(), Some(cache_path.clone())),
11275 };
11276
11277 drop(user);
11278
11279 assert!(cache_db.block_hashes().read().is_empty());
11280 assert!(!cache_path.exists());
11281 }
11282
11283 #[test]
11284 fn startup_fork_cache_user_preserves_cache_after_commit() {
11285 let root = tempdir().unwrap();
11286 let block_cache_dir = root.path().join("1");
11287 let cache_path = block_cache_dir.join("storage.json");
11288 let cache_db = test_cache_db(cache_path.clone());
11289 let user = StagedForkDbUser {
11290 db: Some(Arc::new(tokio::sync::RwLock::new(CacheFlushingDb(cache_db.clone())))),
11291 cache_lease: StagedForkCacheLease::new(cache_db.clone(), Some(cache_path.clone())),
11292 };
11293 user.cache_lease.disarm();
11294
11295 drop(user);
11296
11297 assert_eq!(cache_db.block_hashes().read().get(&U256::ONE), Some(&B256::repeat_byte(0x22)));
11298 assert!(cache_path.exists());
11299 }
11300
11301 #[test]
11302 fn fork_cache_source_invalidates_only_same_url_authoritative_replacements() {
11303 let anonymous = test_endpoint_identity(None, None);
11304 let anonymous_source = ForkCacheSource {
11305 rpc_url: "http://localhost".to_string(),
11306 endpoint_identity: anonymous,
11307 };
11308 let mut changed_anonymous = anonymous;
11309 changed_anonymous.source_chain_id = 2;
11310
11311 assert!(
11312 !anonymous_source
11313 .authoritative_identity_changed_at_same_url("http://localhost", changed_anonymous)
11314 );
11315 assert!(
11316 !anonymous_source
11317 .authoritative_identity_changed_at_same_url("http://mirror", anonymous)
11318 );
11319
11320 let hardfork = Some(FoundryHardfork::Ethereum(EthereumHardfork::Prague));
11321 let authoritative = test_endpoint_identity(hardfork, Some(B256::repeat_byte(0x22)));
11322 let authoritative_source = ForkCacheSource {
11323 rpc_url: "http://localhost".to_string(),
11324 endpoint_identity: authoritative,
11325 };
11326 let replacement = test_endpoint_identity(hardfork, Some(B256::repeat_byte(0x33)));
11327
11328 assert!(
11329 !authoritative_source
11330 .authoritative_identity_changed_at_same_url("http://localhost", authoritative)
11331 );
11332 assert!(
11333 authoritative_source
11334 .authoritative_identity_changed_at_same_url("http://localhost", replacement)
11335 );
11336 assert!(
11337 anonymous_source
11338 .authoritative_identity_changed_at_same_url("http://localhost", authoritative)
11339 );
11340 assert!(
11341 authoritative_source
11342 .authoritative_identity_changed_at_same_url("http://localhost", anonymous)
11343 );
11344 }
11345
11346 #[tokio::test]
11347 async fn test_deterministic_block_mining() {
11348 let genesis_timestamp = 1743944919u64;
11350
11351 let config_a = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
11353 let config_b = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
11354
11355 let (api_a, _handle_a) = spawn(config_a).await;
11356 let (api_b, _handle_b) = spawn(config_b).await;
11357
11358 let outcome_a_1 = api_a.backend.mine_block(vec![]).await.unwrap();
11360 let outcome_b_1 = api_b.backend.mine_block(vec![]).await.unwrap();
11361
11362 assert_eq!(outcome_a_1.block_number, outcome_b_1.block_number);
11364
11365 let block_a_1 =
11367 api_a.block_by_number(outcome_a_1.block_number.into()).await.unwrap().unwrap();
11368 let block_b_1 =
11369 api_b.block_by_number(outcome_b_1.block_number.into()).await.unwrap().unwrap();
11370
11371 assert_eq!(
11373 block_a_1.header.hash, block_b_1.header.hash,
11374 "Block hashes should be deterministic. Got {} vs {}",
11375 block_a_1.header.hash, block_b_1.header.hash
11376 );
11377
11378 let outcome_a_2 = api_a.backend.mine_block(vec![]).await.unwrap();
11380 let outcome_b_2 = api_b.backend.mine_block(vec![]).await.unwrap();
11381
11382 let block_a_2 =
11383 api_a.block_by_number(outcome_a_2.block_number.into()).await.unwrap().unwrap();
11384 let block_b_2 =
11385 api_b.block_by_number(outcome_b_2.block_number.into()).await.unwrap().unwrap();
11386
11387 assert_eq!(
11388 block_a_2.header.hash, block_b_2.header.hash,
11389 "Second block hashes should also be deterministic. Got {} vs {}",
11390 block_a_2.header.hash, block_b_2.header.hash
11391 );
11392
11393 assert_ne!(
11395 block_a_1.header.hash, block_a_2.header.hash,
11396 "Different blocks should have different hashes"
11397 );
11398 }
11399
11400 #[cfg(feature = "monad")]
11401 #[tokio::test]
11402 async fn monad_load_state_rebuilds_participant_cache() {
11403 use alloy_network::TransactionBuilder as _;
11404 use alloy_provider::Provider as _;
11405
11406 let config = || {
11407 NodeConfig::test_monad()
11408 .with_hardfork(Some(foundry_evm::hardfork::MonadHardfork::MonadNine.into()))
11409 };
11410 let (api, handle) = spawn(config()).await;
11411 let provider = handle.http_provider();
11412 let accounts = provider.get_accounts().await.unwrap();
11413 let sender = accounts[0];
11414
11415 let receipt = provider
11416 .send_transaction(
11417 alloy_rpc_types::TransactionRequest::default()
11418 .with_from(sender)
11419 .with_to(accounts[1])
11420 .with_value(U256::ONE)
11421 .into(),
11422 )
11423 .await
11424 .unwrap()
11425 .get_receipt()
11426 .await
11427 .unwrap();
11428 let block_hash = receipt.block_hash.unwrap();
11429 let state = api.serialized_state(false).await.unwrap();
11430
11431 let (loaded_api, _handle) = spawn(config()).await;
11432 loaded_api.backend.load_state(state).await.unwrap();
11433
11434 let storage = loaded_api.backend.blockchain.storage.read();
11435 let participants = storage.monad_block_participants.get(&block_hash).unwrap();
11436 assert!(participants.contains(&sender));
11437 }
11438
11439 #[cfg(feature = "monad")]
11440 #[tokio::test]
11441 async fn monad_load_state_restores_pruned_participant_cache() {
11442 use alloy_consensus::BlockHeader as _;
11443 use alloy_network::TransactionBuilder as _;
11444 use alloy_provider::Provider as _;
11445
11446 let config = || {
11447 NodeConfig::test_monad()
11448 .with_hardfork(Some(foundry_evm::hardfork::MonadHardfork::MonadNine.into()))
11449 .with_transaction_block_keeper(Some(1usize))
11450 };
11451 let (api, handle) = spawn(config()).await;
11452 let provider = handle.http_provider();
11453 let accounts = provider.get_accounts().await.unwrap();
11454 let sender = accounts[0];
11455
11456 let first_receipt = provider
11457 .send_transaction(
11458 alloy_rpc_types::TransactionRequest::default()
11459 .with_from(sender)
11460 .with_to(accounts[1])
11461 .with_value(U256::ONE)
11462 .into(),
11463 )
11464 .await
11465 .unwrap()
11466 .get_receipt()
11467 .await
11468 .unwrap();
11469 let second_receipt = provider
11470 .send_transaction(
11471 alloy_rpc_types::TransactionRequest::default()
11472 .with_from(sender)
11473 .with_to(accounts[1])
11474 .with_value(U256::ONE)
11475 .into(),
11476 )
11477 .await
11478 .unwrap()
11479 .get_receipt()
11480 .await
11481 .unwrap();
11482 let first_block_hash = first_receipt.block_hash.unwrap();
11483 let second_block_hash = second_receipt.block_hash.unwrap();
11484
11485 {
11486 let storage = api.backend.blockchain.storage.read();
11487 let first_block = storage.blocks.get(&first_block_hash).unwrap();
11488 assert!(first_block.body.transactions.is_empty());
11489 assert_ne!(
11490 first_block.header.transactions_root(),
11491 alloy_consensus::constants::EMPTY_ROOT_HASH
11492 );
11493 assert!(storage.monad_block_participants[&first_block_hash].contains(&sender));
11494 assert!(!storage.blocks[&second_block_hash].body.transactions.is_empty());
11495 }
11496
11497 let state = api.serialized_state(false).await.unwrap();
11498 assert!(state.monad_block_participants[&first_block_hash].contains(&sender));
11499 assert_eq!(state.monad_block_participants.len(), 2);
11500 assert!(state.monad_block_participants[&second_block_hash].contains(&sender));
11501
11502 let (loaded_api, _handle) = spawn(config()).await;
11503 loaded_api.backend.load_state(state).await.unwrap();
11504
11505 {
11506 let storage = loaded_api.backend.blockchain.storage.read();
11507 assert!(storage.monad_block_participants[&first_block_hash].contains(&sender));
11508 assert!(storage.monad_block_participants[&second_block_hash].contains(&sender));
11509 }
11510
11511 let outcome = loaded_api.backend.mine_block(vec![]).await.unwrap();
11512 assert_eq!(outcome.block_number, 3);
11513 }
11514
11515 #[cfg(feature = "monad")]
11516 #[tokio::test]
11517 async fn monad_load_state_rejection_is_atomic() {
11518 use alloy_consensus::BlockHeader as _;
11519 use alloy_network::TransactionBuilder as _;
11520 use alloy_provider::Provider as _;
11521
11522 let config = || {
11523 NodeConfig::test_monad()
11524 .with_hardfork(Some(foundry_evm::hardfork::MonadHardfork::MonadNine.into()))
11525 .with_transaction_block_keeper(Some(1usize))
11526 };
11527 let (source_api, source_handle) = spawn(config()).await;
11528 let source_provider = source_handle.http_provider();
11529 let accounts = source_provider.get_accounts().await.unwrap();
11530 let sentinel = alloy_primitives::Address::repeat_byte(0x77);
11531
11532 source_api.anvil_set_balance(sentinel, U256::from(123)).await.unwrap();
11533 for nonce in 0..2 {
11534 source_provider
11535 .send_transaction(
11536 alloy_rpc_types::TransactionRequest::default()
11537 .with_from(accounts[0])
11538 .with_to(accounts[1])
11539 .with_nonce(nonce)
11540 .with_value(U256::ONE)
11541 .into(),
11542 )
11543 .await
11544 .unwrap()
11545 .get_receipt()
11546 .await
11547 .unwrap();
11548 }
11549
11550 let mut invalid_state = source_api.serialized_state(false).await.unwrap();
11551 let pruned_block_hash = invalid_state
11552 .blocks
11553 .iter()
11554 .find(|block| {
11555 block.transactions.is_empty()
11556 && block.header.transactions_root()
11557 != alloy_consensus::constants::EMPTY_ROOT_HASH
11558 })
11559 .unwrap()
11560 .header
11561 .hash_slow();
11562 assert!(invalid_state.monad_block_participants.remove(&pruned_block_hash).is_some());
11563
11564 let (target_api, _handle) = spawn(config()).await;
11565 target_api.anvil_set_balance(sentinel, U256::from(7)).await.unwrap();
11566 target_api.mine_one().await.unwrap();
11567 let original_best_hash = target_api.backend.best_hash();
11568 let original_best_number = target_api.backend.best_number();
11569 let original_block_env = target_api.backend.evm_env.read().block_env.clone();
11570 let original_balance = target_api.backend.current_balance(sentinel).await.unwrap();
11571
11572 let err = target_api.backend.load_state(invalid_state).await.unwrap_err();
11573 assert!(matches!(err, super::BlockchainError::DataUnavailable));
11574 assert_eq!(target_api.backend.best_hash(), original_best_hash);
11575 assert_eq!(target_api.backend.best_number(), original_best_number);
11576 assert_eq!(target_api.backend.evm_env.read().block_env, original_block_env);
11577 assert_eq!(target_api.backend.current_balance(sentinel).await.unwrap(), original_balance);
11578
11579 let outcome = target_api.backend.mine_block(vec![]).await.unwrap();
11580 assert_eq!(outcome.block_number, original_best_number + 1);
11581 }
11582
11583 #[tokio::test]
11584 async fn trace_decoder_follows_executed_hardfork_for_cross_namespace_override() {
11585 let (api, _) = spawn(
11586 NodeConfig::test_tempo()
11587 .with_hardfork(Some(FoundryHardfork::Ethereum(EthereumHardfork::Prague))),
11588 )
11589 .await;
11590
11591 let decoder = api.backend.call_trace_decoder();
11592 assert_eq!(decoder.hardfork(), Some(FoundryHardfork::Tempo(api.backend.tempo_hardfork())));
11593 assert!(Arc::ptr_eq(&decoder, &api.backend.call_trace_decoder()));
11595 }
11596
11597 #[cfg(feature = "monad")]
11598 #[tokio::test]
11599 async fn monad_trace_decoder_follows_resolved_hardfork() {
11600 let (monad_eight, _) = spawn(
11601 NodeConfig::test_monad()
11602 .with_hardfork(Some(foundry_evm::hardfork::MonadHardfork::MonadEight.into())),
11603 )
11604 .await;
11605 let stale_monad_nine = foundry_evm::traces::CallTraceDecoderBuilder::new()
11606 .with_hardfork(Some(foundry_evm::hardfork::MonadHardfork::MonadNine.into()))
11607 .build();
11608 *monad_eight.backend.call_trace_decoder.write() = Arc::new(stale_monad_nine);
11609
11610 let decoder = monad_eight.backend.call_trace_decoder();
11611 assert_eq!(
11612 decoder.hardfork(),
11613 Some(foundry_evm::hardfork::MonadHardfork::MonadEight.into())
11614 );
11615 assert!(
11616 !decoder
11617 .labels
11618 .contains_key(&monad_revm::reserve_balance::abi::RESERVE_BALANCE_ADDRESS)
11619 );
11620
11621 let (monad_nine, _) = spawn(
11622 NodeConfig::test_monad()
11623 .with_hardfork(Some(foundry_evm::hardfork::MonadHardfork::MonadNine.into())),
11624 )
11625 .await;
11626 let stale_monad_eight = foundry_evm::traces::CallTraceDecoderBuilder::new()
11627 .with_hardfork(Some(foundry_evm::hardfork::MonadHardfork::MonadEight.into()))
11628 .build();
11629 *monad_nine.backend.call_trace_decoder.write() = Arc::new(stale_monad_eight);
11630
11631 let decoder = monad_nine.backend.call_trace_decoder();
11632 assert_eq!(
11633 decoder.hardfork(),
11634 Some(foundry_evm::hardfork::MonadHardfork::MonadNine.into())
11635 );
11636 assert_eq!(
11637 decoder
11638 .labels
11639 .get(&monad_revm::reserve_balance::abi::RESERVE_BALANCE_ADDRESS)
11640 .map(String::as_str),
11641 Some("ReserveBalance")
11642 );
11643 }
11644
11645 #[cfg(feature = "base")]
11646 #[tokio::test]
11647 async fn base_trace_decoder_follows_resolved_upgrade() {
11648 let (azul, _) =
11649 spawn(NodeConfig::test_base().with_hardfork(Some(BaseUpgrade::Azul.into()))).await;
11650 let stale_beryl =
11651 CallTraceDecoderBuilder::new().with_hardfork(Some(BaseUpgrade::Beryl.into())).build();
11652 *azul.backend.call_trace_decoder.write() = Arc::new(stale_beryl);
11653
11654 let decoder = azul.backend.call_trace_decoder();
11655 assert_eq!(decoder.hardfork(), Some(BaseUpgrade::Azul.into()));
11656 assert!(!decoder.labels.contains_key(&B20FactoryStorage::ADDRESS));
11657 assert!(!decoder.labels.contains_key(&ActivationRegistryStorage::ADDRESS));
11658
11659 let (beryl, _) =
11660 spawn(NodeConfig::test_base().with_hardfork(Some(BaseUpgrade::Beryl.into()))).await;
11661 let stale_azul =
11662 CallTraceDecoderBuilder::new().with_hardfork(Some(BaseUpgrade::Azul.into())).build();
11663 *beryl.backend.call_trace_decoder.write() = Arc::new(stale_azul);
11664
11665 let decoder = beryl.backend.call_trace_decoder();
11666 assert_eq!(decoder.hardfork(), Some(BaseUpgrade::Beryl.into()));
11667 assert_eq!(
11668 decoder.labels.get(&B20FactoryStorage::ADDRESS).map(String::as_str),
11669 Some("B20Factory")
11670 );
11671 assert_eq!(
11672 decoder.labels.get(&ActivationRegistryStorage::ADDRESS).map(String::as_str),
11673 Some("ActivationRegistry")
11674 );
11675 }
11676}