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