1use self::state::trie_storage;
3
4use crate::{
5 ForkChoice, NodeConfig, PrecompileFactory,
6 config::{ForkTransactionReplay, PruneStateHistoryConfig},
7 eth::{
8 backend::{
9 cheats::{CheatEcrecover, CheatsManager},
10 db::{AnvilCacheDB, Db, MaybeFullDatabase, SerializableState, StateDb},
11 executor::{
12 AnvilBlockExecutor, BlockExecutionKind, EthereumBlockTransitions,
13 ExecutedPoolTransactions, FoundryReceiptBuilder, PoolTxGasConfig,
14 apply_ethereum_post_execution_changes, apply_ethereum_pre_execution_changes,
15 execute_pool_transactions,
16 },
17 fork::ClientFork,
18 genesis::GenesisConfig,
19 mem::{
20 state::{state_root, storage_root, trie_accounts},
21 storage::MinedTransactionReceipt,
22 },
23 notifications::{ChainNotification, ChainNotifications, NewBlockNotification},
24 replay::{
25 ExecutedHistoricalReplay, HistoricalReplayTransaction,
26 PreparedForkTransactionReplay, execute_historical_replay,
27 prepare_fork_transaction_replay,
28 },
29 tempo::AnvilStorageProvider,
30 time::{TimeManager, utc_from_secs},
31 validate::TransactionValidator,
32 },
33 error::{BlockchainError, ErrDetail, InvalidTransactionError},
34 fees::{FeeDetails, FeeManager, MIN_SUGGESTED_PRIORITY_FEE},
35 macros::node_info,
36 pool::transactions::PoolTransaction,
37 preserve_simulation_request_fields,
38 },
39 mem::{
40 inspector::{AnvilInspector, InspectorTxConfig},
41 storage::{BlockchainStorage, InMemoryBlockStates, MinedBlockOutcome},
42 },
43};
44use alloy_chains::NamedChain;
45use alloy_consensus::{
46 Blob, BlockHeader, EnvKzgSettings, Header, Signed, Transaction as TransactionTrait,
47 TransactionEnvelope, TrieAccount, TxEip4844Variant, TxEnvelope, TxReceipt, Typed2718,
48 constants::EMPTY_WITHDRAWALS,
49 proofs::{calculate_receipt_root, calculate_transaction_root},
50 transaction::Recovered,
51};
52use alloy_eips::{
53 BlockNumHash, Encodable2718, eip2935, eip4788,
54 eip4844::{DATA_GAS_PER_BLOB, kzg_to_versioned_hash},
55 eip6110::MAINNET_DEPOSIT_CONTRACT_ADDRESS,
56 eip7002, eip7251,
57 eip7685::EMPTY_REQUESTS_HASH,
58 eip7840::BlobParams,
59 eip7910::SystemContract,
60};
61use alloy_evm::{
62 Database, EthEvmFactory, Evm, EvmEnv, EvmFactory, FromTxWithEncoded,
63 block::{BlockExecutionResult, BlockExecutor, StateDB},
64 eth::EthEvmContext,
65 overrides::{OverrideBlockHashes, apply_state_overrides},
66 precompiles::{DynPrecompile, MovePrecompileError, Precompile, PrecompilesMap},
67};
68use alloy_network::{
69 AnyHeader, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope, AnyTxType, Network,
70 NetworkTransactionBuilder, ReceiptResponse, UnknownTxEnvelope, UnknownTypedTransaction,
71};
72#[cfg(feature = "optimism")]
73use alloy_op_evm::{OpEvmContext, OpEvmFactory, OpTx};
74use alloy_primitives::{
75 Address, B256, Bloom, Bytes, Signature, TxHash, TxKind, U64, U256, address, hex, keccak256,
76 map::{AddressMap, HashMap, HashSet},
77};
78use alloy_rlp::Decodable;
79use alloy_rpc_types::{
80 AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, BlockOverrides,
81 BlockTransactions, EIP1186AccountProofResponse as AccountProof,
82 EIP1186StorageProof as StorageProof, Filter, Header as AlloyHeader, Index, Log, Transaction,
83 TransactionReceipt,
84 anvil::Forking,
85 request::TransactionRequest,
86 serde_helpers::JsonStorageKey,
87 simulate::{
88 MAX_SIMULATE_BLOCKS, SimBlock, SimCallResult, SimulateError, SimulatePayload,
89 SimulatedBlock,
90 },
91 state::{EvmOverrides, StateOverride},
92 trace::{
93 filter::TraceFilter,
94 geth::{
95 CallConfig, FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerConfig,
96 GethDebugTracerType, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace,
97 NoopFrame, TraceResult,
98 },
99 opcode::{BlockOpcodeGas, TransactionOpcodeGas},
100 parity::{
101 LocalizedTransactionTrace, TraceResults, TraceResultsWithTransactionHash, TraceType,
102 },
103 },
104};
105use alloy_rpc_types_eth::{AccountInfo as RpcAccountInfo, Bundle, EthCallResponse};
106use alloy_rpc_types_mev::{EthCallBundle, EthCallBundleResponse, EthCallBundleTransactionResult};
107use alloy_serde::{OtherFields, WithOtherFields};
108use alloy_trie::{HashBuilder, Nibbles, proof::ProofRetainer};
109use anvil_core::eth::{
110 block::{Block, BlockInfo, canonical_block, create_block},
111 transaction::{MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo},
112};
113use anvil_rpc::error::{ErrorCode, RpcError};
114use chrono::Datelike;
115use eyre::{Context, Result};
116use flate2::{Compression, read::GzDecoder, write::GzEncoder};
117#[cfg(feature = "optimism")]
118use foundry_evm::hardfork::OpHardfork;
119use foundry_evm::{
120 backend::{DatabaseError, DatabaseResult, RevertStateSnapshotAction},
121 constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE,
122 core::{
123 evm::{EvmEnvFor, TempoEvmNetwork},
124 precompiles::EC_RECOVER,
125 },
126 decode::RevertDecoder,
127 hardfork::{EthereumHardfork, FoundryHardfork},
128 inspectors::AccessListInspector,
129 traces::{
130 CallTraceDecoder, FourByteInspector, GethTraceBuilder, TracingInspector,
131 TracingInspectorConfig,
132 },
133 utils::{
134 apply_chain_specific_tx_replay_env_changes, block_env_from_header,
135 get_blob_base_fee_update_fraction, get_blob_base_fee_update_fraction_by_spec_id,
136 get_blob_params_by_spec_id,
137 },
138};
139use foundry_evm_networks::{NetworkConfigs, arbitrum};
140#[cfg(feature = "optimism")]
141use foundry_primitives::get_deposit_tx_parts;
142use foundry_primitives::{
143 FoundryHeader, FoundryNetwork, FoundryReceiptEnvelope, FoundryTransactionRequest,
144 FoundryTxEnvelope, FoundryTxReceipt, TempoTransactionRequest,
145};
146use futures::channel::mpsc::{UnboundedSender, unbounded};
147#[cfg(feature = "optimism")]
148use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, POST_EXEC_TX_TYPE_ID};
149#[cfg(feature = "optimism")]
150use op_revm::{OpTransaction, transaction::deposit::DepositTransactionParts};
151
152#[cfg(feature = "optimism")]
159type OpCallDepositInfo = DepositTransactionParts;
160#[cfg(not(feature = "optimism"))]
161#[derive(Default, Clone, Debug)]
162struct OpCallDepositInfo;
163
164const SIMULATE_GAS_CAP: u64 = 50_000_000;
166const SEPOLIA_DEPOSIT_CONTRACT_ADDRESS: Address =
167 address!("7f02c3e3c98b133055b8b348b2ac625669ed295d");
168const HOLESKY_DEPOSIT_CONTRACT_ADDRESS: Address =
169 address!("4242424242424242424242424242424242424242");
170
171const TEMPO_RPC_SIMULATION_CONTEXT: B256 = B256::new(*b"TEMPO_RPC_SIMULATION_MPP_CONTEXT");
173
174struct SimulationHandler<EVM, ERROR, FRAME> {
176 _phantom: PhantomData<(EVM, ERROR, FRAME)>,
177}
178
179impl<EVM, ERROR, FRAME> Default for SimulationHandler<EVM, ERROR, FRAME> {
180 fn default() -> Self {
181 Self { _phantom: PhantomData }
182 }
183}
184
185impl<EVM, ERROR, FRAME> EvmHandler for SimulationHandler<EVM, ERROR, FRAME>
186where
187 EVM: EvmTr<
188 Context: ContextTr<
189 Block = BlockEnv,
190 Tx = TxEnv,
191 Journal: JournalTr<State = EvmState>,
192 > + ContextSetters,
193 Frame = FRAME,
194 >,
195 ERROR: EvmTrError<EVM>,
196 FRAME: FrameTr<FrameResult = FrameResult, FrameInit = FrameInit>,
197{
198 type Evm = EVM;
199 type Error = ERROR;
200 type HaltReason = HaltReason;
201
202 fn validate_env(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> {
203 let skip_blob_fee_check = evm.ctx_ref().cfg().is_base_fee_check_disabled()
204 && evm.ctx_ref().tx().tx_type == 3
205 && evm.ctx_ref().tx().max_fee_per_blob_gas == 0;
206 if !skip_blob_fee_check {
207 return validation::validate_env(evm.ctx());
208 }
209
210 let block = evm.ctx_ref().block().clone();
211 let mut validation_block = block.clone();
212 if let Some(blob_gas_and_price) = &mut validation_block.blob_excess_gas_and_price {
213 blob_gas_and_price.blob_gasprice = 0;
214 }
215 evm.ctx().set_block(validation_block);
216 let result = validation::validate_env(evm.ctx());
217 evm.ctx().set_block(block);
218 result
219 }
220}
221
222impl<EVM, ERROR> InspectorHandler for SimulationHandler<EVM, ERROR, EthFrame<EthInterpreter>>
223where
224 EVM: InspectorEvmTr<
225 Context: ContextTr<
226 Block = BlockEnv,
227 Tx = TxEnv,
228 Journal: JournalTr<State = EvmState>,
229 > + ContextSetters,
230 Frame = EthFrame<EthInterpreter>,
231 Inspector: Inspector<<EVM as EvmTr>::Context, EthInterpreter>,
232 >,
233 ERROR: EvmTrError<EVM>,
234{
235 type IT = EthInterpreter;
236}
237
238#[derive(Clone)]
239enum CallTxEnv {
240 Eth(TxEnv),
241 #[cfg(feature = "optimism")]
242 Op(OpTransaction<TxEnv>),
243 Tempo(TempoTxEnv),
244}
245
246impl CallTxEnv {
247 #[cfg_attr(not(feature = "js-tracer"), allow(dead_code))]
248 const fn base(&self) -> &TxEnv {
249 match self {
250 Self::Eth(tx) => tx,
251 #[cfg(feature = "optimism")]
252 Self::Op(tx) => &tx.base,
253 Self::Tempo(tx) => &tx.inner,
254 }
255 }
256
257 const fn base_mut(&mut self) -> &mut TxEnv {
258 match self {
259 Self::Eth(tx) => tx,
260 #[cfg(feature = "optimism")]
261 Self::Op(tx) => &mut tx.base,
262 Self::Tempo(tx) => &mut tx.inner,
263 }
264 }
265
266 fn into_base(self) -> TxEnv {
267 match self {
268 Self::Eth(tx) => tx,
269 #[cfg(feature = "optimism")]
270 Self::Op(tx) => tx.base,
271 Self::Tempo(tx) => tx.inner,
272 }
273 }
274
275 fn uses_protocol_call_nonce(&self) -> bool {
276 match self {
277 Self::Eth(tx) => matches!(tx.kind, TxKind::Call(_)),
278 #[cfg(feature = "optimism")]
279 Self::Op(tx) => matches!(tx.base.kind, TxKind::Call(_)),
280 Self::Tempo(tx) => tx.tempo_tx_env.as_ref().map_or_else(
281 || matches!(tx.inner.kind, TxKind::Call(_)),
282 |aa| {
283 aa.nonce_key.is_zero()
284 && aa
285 .aa_calls
286 .first()
287 .is_some_and(|call| matches!(call.to, TxKind::Call(_)))
288 },
289 ),
290 }
291 }
292}
293
294fn apply_tempo_envelope_identity(tx_env: &mut CallTxEnv, simulated_tx: Option<&AASigned>) {
295 if let (CallTxEnv::Tempo(tx_env), Some(simulated_tx)) = (tx_env, simulated_tx) {
296 tx_env.unique_tx_identifier = Some(simulated_tx.expiring_nonce_hash(tx_env.inner.caller));
297 if let Some(batch) = &mut tx_env.tempo_tx_env {
298 batch.tx_hash = *simulated_tx.hash();
299 }
300 }
301}
302
303struct PreparedCall {
304 evm_env: EvmEnv,
305 tx_env: CallTxEnv,
306 simulated_tempo_tx: Option<AASigned>,
307}
308
309#[derive(Default)]
310struct TypedCallOverrides {
311 gas_limit: Option<u64>,
312 access_list: Option<AccessList>,
313 disable_fee_charge: bool,
314}
315
316#[cfg(feature = "optimism")]
320pub trait BackendInspector<DB: Database>:
321 Inspector<EthEvmContext<DB>> + Inspector<OpEvmContext<DB>> + Inspector<TempoContext<DB>>
322{
323}
324#[cfg(feature = "optimism")]
325impl<DB: Database, T> BackendInspector<DB> for T where
326 T: Inspector<EthEvmContext<DB>> + Inspector<OpEvmContext<DB>> + Inspector<TempoContext<DB>>
327{
328}
329#[cfg(not(feature = "optimism"))]
330pub trait BackendInspector<DB: Database>:
331 Inspector<EthEvmContext<DB>> + Inspector<TempoContext<DB>>
332{
333}
334#[cfg(not(feature = "optimism"))]
335impl<DB: Database, T> BackendInspector<DB> for T where
336 T: Inspector<EthEvmContext<DB>> + Inspector<TempoContext<DB>>
337{
338}
339use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
340use revm::{
341 Database as RevmDatabase, DatabaseCommit, Inspector,
342 context::{Block as RevmBlock, BlockEnv, Cfg, ContextSetters, ContextTr, TxEnv},
343 context_interface::{
344 JournalTr,
345 block::BlobExcessGasAndPrice,
346 result::{ExecutionResult, HaltReason, Output, ResultAndState},
347 },
348 database::{AccountState, CacheDB, DbAccount, WrapDatabaseRef},
349 handler::{
350 EthFrame, EvmTr, EvmTrError, FrameResult, FrameTr, Handler as EvmHandler, validation,
351 },
352 inspector::{InspectorEvmTr, InspectorHandler},
353 interpreter::{InstructionResult, interpreter::EthInterpreter, interpreter_action::FrameInit},
354 precompile::{PrecompileSpecId, Precompiles},
355 primitives::{KECCAK_EMPTY, hardfork::SpecId},
356 state::{Account, AccountInfo, EvmState, EvmStorageSlot, TransactionId},
357};
358use revm_inspectors::opcode::OpcodeGasInspector;
359use std::{
360 collections::BTreeMap,
361 fmt::{self, Debug},
362 io::{Read, Write},
363 marker::PhantomData,
364 ops::Mul,
365 path::PathBuf,
366 sync::Arc,
367 time::Duration,
368};
369use storage::{Blockchain, DEFAULT_HISTORY_LIMIT, MinedTransaction};
370use tempo_evm::evm::TempoEvmFactory;
371use tempo_hardfork::TempoHardfork;
372use tempo_precompiles::{
373 NONCE_PRECOMPILE_ADDRESS, TIP_FEE_MANAGER_ADDRESS, extend_tempo_precompiles,
374 nonce::NonceManager,
375 storage::{Handler, StorageActions, StorageCtx},
376 tip_fee_manager::{IFeeManager, TipFeeManager},
377 tip20::{ISSUER_ROLE, ITIP20, TIP20Token},
378 tip20_factory::TIP20Factory,
379};
380use tempo_primitives::{
381 AASigned, SignatureType, TEMPO_TX_TYPE_ID, TempoSignature,
382 transaction::{
383 Call, KeychainSignature, PrimitiveSignature, RecoveredTempoAuthorization,
384 tt_signature::{P256SignatureWithPreHash, WebAuthnSignature},
385 },
386};
387use tempo_revm::{
388 TempoBatchCallEnv, TempoBlockEnv, TempoHaltReason, TempoTxEnv, evm::TempoContext,
389 gas_params::tempo_gas_params,
390};
391use tokio::sync::RwLock as AsyncRwLock;
392
393pub mod cache;
394pub mod fork_db;
395pub mod in_memory_db;
396pub mod inspector;
397#[cfg(feature = "optimism")]
398pub mod optimism;
399pub mod state;
400pub mod storage;
401
402pub trait DatabaseRef: revm::DatabaseRef<Error = DatabaseError> + Debug {}
406impl<T> DatabaseRef for T where T: revm::DatabaseRef<Error = DatabaseError> + Debug {}
407impl DatabaseRef for dyn crate::eth::backend::db::Db {}
408
409pub const MIN_TRANSACTION_GAS: u128 = 21000;
411pub const MIN_CREATE_GAS: u128 = 53000;
413
414fn tempo_nonce(
415 state: &dyn DatabaseRef,
416 caller: Address,
417 nonce_key: U256,
418) -> Result<u64, BlockchainError> {
419 if nonce_key.is_zero() {
420 return Ok(state.basic_ref(caller)?.map(|account| account.nonce).unwrap_or_default());
421 }
422 if nonce_key == U256::MAX {
423 return Ok(0);
424 }
425 let slot = NonceManager::new().nonces[caller][nonce_key].slot();
426 Ok(state.storage_ref(NONCE_PRECOMPILE_ADDRESS, slot)?.saturating_to())
427}
428
429fn mock_tempo_signature(
430 key_type: SignatureType,
431 key_data: Option<Bytes>,
432 key_id: Option<Address>,
433 caller: Address,
434 is_t1c: bool,
435) -> TempoSignature {
436 let signature = match key_type {
437 SignatureType::Secp256k1 => {
438 PrimitiveSignature::Secp256k1(Signature::new(U256::ZERO, U256::ZERO, false))
439 }
440 SignatureType::P256 => PrimitiveSignature::P256(P256SignatureWithPreHash {
441 r: B256::ZERO,
442 s: B256::ZERO,
443 pub_key_x: B256::ZERO,
444 pub_key_y: B256::ZERO,
445 pre_hash: false,
446 }),
447 SignatureType::WebAuthn => {
448 const CLIENT_JSON: &str = r#"{"type":"webauthn.get","challenge":"","origin":""}"#;
449 const AUTH_DATA_SIZE: usize = 37;
450 const MIN_SIZE: usize = AUTH_DATA_SIZE + CLIENT_JSON.len();
451 const DEFAULT_SIZE: usize = 800;
452 const MAX_SIZE: usize = 8192;
453
454 let size = key_data
455 .as_deref()
456 .and_then(|data| match data.len() {
457 1 => Some(data[0] as usize),
458 2 => Some(u16::from_be_bytes([data[0], data[1]]) as usize),
459 4 => Some(u32::from_be_bytes([data[0], data[1], data[2], data[3]]) as usize),
460 _ => None,
461 })
462 .unwrap_or(DEFAULT_SIZE)
463 .clamp(MIN_SIZE, MAX_SIZE);
464 let mut webauthn_data = vec![0u8; AUTH_DATA_SIZE];
465 webauthn_data[32] = 0x01;
466 let padding = "x".repeat(size - MIN_SIZE);
467 webauthn_data.extend_from_slice(
468 format!(r#"{{"type":"webauthn.get","challenge":"","origin":"{padding}"}}"#)
469 .as_bytes(),
470 );
471 PrimitiveSignature::WebAuthn(WebAuthnSignature {
472 webauthn_data: webauthn_data.into(),
473 r: B256::ZERO,
474 s: B256::ZERO,
475 pub_key_x: B256::ZERO,
476 pub_key_y: B256::ZERO,
477 })
478 }
479 };
480
481 if key_id.is_some() {
482 let signature = if is_t1c {
483 KeychainSignature::new(caller, signature)
484 } else {
485 KeychainSignature::new_v1(caller, signature)
486 };
487 TempoSignature::Keychain(signature)
488 } else {
489 TempoSignature::Primitive(signature)
490 }
491}
492
493fn call_config_from_tracer_config(
494 tracer_config: GethDebugTracerConfig,
495) -> Result<CallConfig, serde_json::Error> {
496 let mut tracer_config = tracer_config.into_json();
497 if let Some(config) = tracer_config.as_object_mut()
498 && !config.contains_key("onlyTopCall")
499 && let Some(only_top_level_call) = config.remove("onlyTopLevelCall")
500 {
501 config.insert("onlyTopCall".to_string(), only_top_level_call);
502 }
503
504 GethDebugTracerConfig(tracer_config).into_call_config()
505}
506
507pub type State = foundry_evm::utils::StateChangeset;
508
509#[derive(Clone, Debug, Default)]
510struct SimulationPrecompileOverrides {
511 moves: Vec<(Address, Address)>,
512}
513
514pub enum BlockRequest<T> {
516 Pending(Vec<Arc<PoolTransaction<T>>>),
517 Number(u64),
518}
519
520impl<T> fmt::Debug for BlockRequest<T> {
521 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
522 match self {
523 Self::Pending(txs) => f.debug_tuple("Pending").field(&txs.len()).finish(),
524 Self::Number(n) => f.debug_tuple("Number").field(n).finish(),
525 }
526 }
527}
528
529impl<T> BlockRequest<T> {
530 pub const fn block_number(&self) -> BlockNumber {
531 match *self {
532 Self::Pending(_) => BlockNumber::Pending,
533 Self::Number(n) => BlockNumber::Number(n),
534 }
535 }
536}
537
538pub struct Backend<N: Network> {
540 db: Arc<AsyncRwLock<Box<dyn Db>>>,
560 blockchain: Blockchain<N>,
562 states: Arc<RwLock<InMemoryBlockStates>>,
564 evm_env: Arc<RwLock<EvmEnv>>,
566 networks: NetworkConfigs,
568 hardfork: FoundryHardfork,
570 fork: Arc<RwLock<Option<ClientFork>>>,
572 time: TimeManager,
574 cheats: CheatsManager,
576 fees: FeeManager,
578 genesis: GenesisConfig,
580 new_block_listeners: Arc<Mutex<Vec<UnboundedSender<ChainNotification>>>>,
583 active_state_snapshots: Arc<Mutex<HashMap<U256, (u64, B256)>>>,
585 enable_steps_tracing: bool,
586 print_logs: bool,
587 print_traces: bool,
588 call_trace_decoder: Arc<CallTraceDecoder>,
590 prune_state_history_config: PruneStateHistoryConfig,
592 transaction_block_keeper: Option<usize>,
594 pub(crate) node_config: Arc<AsyncRwLock<NodeConfig>>,
595 slots_in_an_epoch: u64,
597 precompile_factory: Option<Arc<dyn PrecompileFactory>>,
599 mining: Arc<tokio::sync::Mutex<()>>,
601 disable_pool_balance_checks: bool,
603}
604
605impl<N: Network> Clone for Backend<N> {
606 fn clone(&self) -> Self {
607 Self {
608 db: self.db.clone(),
609 blockchain: self.blockchain.clone(),
610 states: self.states.clone(),
611 evm_env: self.evm_env.clone(),
612 networks: self.networks,
613 hardfork: self.hardfork,
614 fork: self.fork.clone(),
615 time: self.time.clone(),
616 cheats: self.cheats.clone(),
617 fees: self.fees.clone(),
618 genesis: self.genesis.clone(),
619 new_block_listeners: self.new_block_listeners.clone(),
620 active_state_snapshots: self.active_state_snapshots.clone(),
621 enable_steps_tracing: self.enable_steps_tracing,
622 print_logs: self.print_logs,
623 print_traces: self.print_traces,
624 call_trace_decoder: self.call_trace_decoder.clone(),
625 prune_state_history_config: self.prune_state_history_config,
626 transaction_block_keeper: self.transaction_block_keeper,
627 node_config: self.node_config.clone(),
628 slots_in_an_epoch: self.slots_in_an_epoch,
629 precompile_factory: self.precompile_factory.clone(),
630 mining: self.mining.clone(),
631 disable_pool_balance_checks: self.disable_pool_balance_checks,
632 }
633 }
634}
635
636impl<N: Network> fmt::Debug for Backend<N> {
637 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
638 f.debug_struct("Backend").finish_non_exhaustive()
639 }
640}
641
642impl<N: Network> Backend<N> {
644 pub fn impersonate(&self, addr: Address) -> bool {
648 if self.cheats.impersonated_accounts().contains(&addr) {
649 return true;
650 }
651 self.evm_env.write().cfg_env.disable_eip3607 = true;
653 self.cheats.impersonate(addr)
654 }
655
656 pub fn stop_impersonating(&self, addr: Address) {
660 self.cheats.stop_impersonating(&addr);
661 }
662
663 pub fn auto_impersonate_account(&self, enabled: bool) {
665 self.cheats.set_auto_impersonate_account(enabled);
666 }
667
668 pub fn get_fork(&self) -> Option<ClientFork> {
670 self.fork.read().clone()
671 }
672
673 pub fn get_db(&self) -> &Arc<AsyncRwLock<Box<dyn Db>>> {
675 &self.db
676 }
677
678 pub async fn get_account(&self, address: Address) -> DatabaseResult<AccountInfo> {
680 Ok(self.db.read().await.basic_ref(address)?.unwrap_or_default())
681 }
682
683 pub fn is_fork(&self) -> bool {
685 self.fork.read().is_some()
686 }
687
688 pub async fn set_create2_deployer(&self, address: Address) -> DatabaseResult<()> {
690 self.set_code(address, Bytes::from_static(DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE)).await?;
691 Ok(())
692 }
693
694 pub(crate) fn update_interval_mine_block_time(&self, block_time: Duration) {
696 self.states.write().update_interval_mine_block_time(block_time)
697 }
698
699 pub const fn time(&self) -> &TimeManager {
701 &self.time
702 }
703
704 pub const fn cheats(&self) -> &CheatsManager {
706 &self.cheats
707 }
708
709 pub fn skip_blob_validation(&self, impersonator: Option<Address>) -> bool {
711 self.cheats().auto_impersonate_accounts()
712 || impersonator
713 .is_some_and(|addr| self.cheats().impersonated_accounts().contains(&addr))
714 }
715
716 pub const fn fees(&self) -> &FeeManager {
718 &self.fees
719 }
720
721 pub const fn evm_env(&self) -> &Arc<RwLock<EvmEnv>> {
723 &self.evm_env
724 }
725
726 pub fn best_hash(&self) -> B256 {
728 self.blockchain.storage.read().best_hash
729 }
730
731 pub fn best_number(&self) -> u64 {
733 self.blockchain.storage.read().best_number
734 }
735
736 pub fn set_block_number(&self, number: u64) {
738 self.evm_env.write().block_env.number = U256::from(number);
739 }
740
741 pub fn coinbase(&self) -> Address {
743 self.evm_env.read().block_env.beneficiary
744 }
745
746 pub fn chain_id(&self) -> U256 {
748 U256::from(self.evm_env.read().cfg_env.chain_id)
749 }
750
751 pub fn set_chain_id(&self, chain_id: u64) {
752 self.evm_env.write().cfg_env.chain_id = chain_id;
753 }
754
755 pub const fn genesis_time(&self) -> u64 {
757 self.genesis.timestamp
758 }
759
760 pub const fn genesis_number(&self) -> u64 {
762 self.genesis.number
763 }
764
765 pub async fn current_balance(&self, address: Address) -> DatabaseResult<U256> {
767 Ok(self.get_account(address).await?.balance)
768 }
769
770 pub async fn current_nonce(&self, address: Address) -> DatabaseResult<u64> {
772 Ok(self.get_account(address).await?.nonce)
773 }
774
775 pub fn set_coinbase(&self, address: Address) {
777 self.evm_env.write().block_env.beneficiary = address;
778 }
779
780 pub fn set_next_block_prevrandao(&self, prevrandao: B256) {
785 self.cheats.set_next_block_prevrandao(prevrandao);
786 }
787
788 pub async fn set_nonce(&self, address: Address, nonce: U256) -> DatabaseResult<()> {
790 self.db.write().await.set_nonce(address, nonce.try_into().unwrap_or(u64::MAX))
791 }
792
793 pub async fn set_balance(&self, address: Address, balance: U256) -> DatabaseResult<()> {
795 self.db.write().await.set_balance(address, balance)
796 }
797
798 pub async fn set_code(&self, address: Address, code: Bytes) -> DatabaseResult<()> {
800 self.db.write().await.set_code(address, code)
801 }
802
803 pub async fn set_storage_at(
805 &self,
806 address: Address,
807 slot: U256,
808 val: B256,
809 ) -> DatabaseResult<()> {
810 self.db.write().await.set_storage_at(address, slot.into(), val)
811 }
812
813 pub fn spec_id(&self) -> SpecId {
815 *self.evm_env.read().spec_id()
816 }
817
818 pub fn is_eip1559(&self) -> bool {
820 (self.spec_id() as u8) >= (SpecId::LONDON as u8)
821 }
822
823 pub fn is_eip3675(&self) -> bool {
825 (self.spec_id() as u8) >= (SpecId::MERGE as u8)
826 }
827
828 pub fn is_eip2930(&self) -> bool {
830 (self.spec_id() as u8) >= (SpecId::BERLIN as u8)
831 }
832
833 pub fn is_eip4844(&self) -> bool {
835 (self.spec_id() as u8) >= (SpecId::CANCUN as u8)
836 }
837
838 pub fn is_eip7702(&self) -> bool {
840 (self.spec_id() as u8) >= (SpecId::PRAGUE as u8)
841 }
842
843 #[cfg(feature = "optimism")]
845 pub const fn is_optimism(&self) -> bool {
846 self.networks.is_optimism()
847 }
848
849 #[cfg(not(feature = "optimism"))]
853 pub const fn is_optimism(&self) -> bool {
854 false
855 }
856
857 pub const fn is_tempo(&self) -> bool {
859 self.networks.is_tempo()
860 }
861
862 pub fn hardfork(&self) -> FoundryHardfork {
864 if let Some(hardfork) =
865 self.fork.read().as_ref().and_then(|fork| fork.config.read().hardfork)
866 {
867 return hardfork;
868 }
869 self.hardfork
870 }
871
872 fn ethereum_block_transitions(
874 &self,
875 hardfork: FoundryHardfork,
876 parent_beacon_block_root: Option<B256>,
877 execution_kind: BlockExecutionKind,
878 ) -> Option<EthereumBlockTransitions> {
879 if self.is_optimism() || self.is_tempo() {
880 return None;
881 }
882 let FoundryHardfork::Ethereum(hardfork) = hardfork else { return None };
883 Some(EthereumBlockTransitions {
884 hardfork,
885 deposit_contract_address: self.ethereum_deposit_contract_address(),
886 parent_beacon_block_root,
887 execution_kind,
888 })
889 }
890
891 fn ethereum_deposit_contract_address(&self) -> Address {
893 if let Some(address) = self
894 .genesis
895 .genesis_init
896 .as_ref()
897 .and_then(|genesis| genesis.config.deposit_contract_address)
898 {
899 return address;
900 }
901
902 match NamedChain::try_from(self.evm_env.read().cfg_env.chain_id) {
903 Ok(NamedChain::Sepolia) => SEPOLIA_DEPOSIT_CONTRACT_ADDRESS,
904 Ok(NamedChain::Holesky) => HOLESKY_DEPOSIT_CONTRACT_ADDRESS,
905 _ => MAINNET_DEPOSIT_CONTRACT_ADDRESS,
907 }
908 }
909
910 pub fn tempo_hardfork(&self) -> TempoHardfork {
912 TempoHardfork::from(self.hardfork())
913 }
914
915 pub fn is_tempo_hardfork_active(&self, hardfork: TempoHardfork) -> bool {
917 self.is_tempo() && self.tempo_hardfork() >= hardfork
918 }
919
920 pub fn precompiles(&self) -> BTreeMap<String, Address> {
922 let spec_id = self.spec_id();
923 let mut precompiles =
924 PrecompilesMap::from_static(Precompiles::new(PrecompileSpecId::from_spec_id(spec_id)));
925 let (chain_id, timestamp) = {
926 let evm_env = self.evm_env.read();
927 (evm_env.cfg_env.chain_id, evm_env.block_env.timestamp.saturating_to())
928 };
929 self.networks.inject_chain_precompiles(&mut precompiles, chain_id, timestamp);
930
931 let mut precompiles_map = BTreeMap::<String, Address>::default();
932 for address in precompiles.addresses() {
933 let precompile = precompiles.get(address).expect("precompile address must resolve");
934 precompiles_map.insert(precompile.precompile_id().name().to_string(), *address);
935 }
936
937 precompiles_map
939 .extend(self.networks.precompiles(self.is_tempo().then(|| self.tempo_hardfork())));
940
941 if let Some(factory) = &self.precompile_factory {
942 for (address, precompile) in factory.precompiles() {
943 precompiles_map.insert(precompile.precompile_id().to_string(), address);
944 }
945 }
946
947 precompiles_map
948 }
949
950 pub fn system_contracts(&self) -> BTreeMap<SystemContract, Address> {
952 let mut system_contracts = BTreeMap::<SystemContract, Address>::default();
953
954 let spec_id = self.spec_id();
955
956 if spec_id >= SpecId::CANCUN {
957 system_contracts.extend(SystemContract::cancun());
958 }
959
960 if spec_id >= SpecId::PRAGUE {
961 system_contracts.extend(SystemContract::prague(None));
962 }
963
964 system_contracts
965 }
966
967 pub fn blob_params(&self) -> BlobParams {
969 get_blob_params_by_spec_id(self.spec_id())
970 }
971
972 fn simulation_blob_params_at_timestamp(&self, timestamp: u64) -> BlobParams {
973 let spec_id = self.spec_id();
974 let configured_hardfork = self.hardfork();
975 if let FoundryHardfork::Ethereum(
976 configured
977 @ (EthereumHardfork::Osaka | EthereumHardfork::Bpo1 | EthereumHardfork::Bpo2),
978 ) = configured_hardfork
979 && let Some(hardfork) = FoundryHardfork::from_chain_and_timestamp(
980 self.evm_env.read().cfg_env.chain_id,
981 timestamp,
982 )
983 && let FoundryHardfork::Ethereum(
984 scheduled @ (EthereumHardfork::Osaka
985 | EthereumHardfork::Bpo1
986 | EthereumHardfork::Bpo2),
987 ) = hardfork
988 {
989 let hardfork = match (configured, scheduled) {
990 (EthereumHardfork::Bpo2, _) | (_, EthereumHardfork::Bpo2) => EthereumHardfork::Bpo2,
991 (EthereumHardfork::Bpo1, _) | (_, EthereumHardfork::Bpo1) => EthereumHardfork::Bpo1,
992 _ => EthereumHardfork::Osaka,
993 };
994 return Self::simulation_blob_params_for_hardfork(hardfork.into(), spec_id);
995 }
996 Self::simulation_blob_params_for_hardfork(configured_hardfork, spec_id)
997 }
998
999 fn simulation_blob_params_for_hardfork(
1000 hardfork: FoundryHardfork,
1001 spec_id: SpecId,
1002 ) -> BlobParams {
1003 match hardfork {
1004 FoundryHardfork::Ethereum(EthereumHardfork::Prague) => BlobParams::prague(),
1005 FoundryHardfork::Ethereum(EthereumHardfork::Osaka) => BlobParams::osaka(),
1006 FoundryHardfork::Ethereum(EthereumHardfork::Bpo1) => BlobParams::bpo1(),
1007 FoundryHardfork::Ethereum(EthereumHardfork::Bpo2) => BlobParams::bpo2(),
1008 FoundryHardfork::Ethereum(
1009 EthereumHardfork::Bpo3
1010 | EthereumHardfork::Bpo4
1011 | EthereumHardfork::Bpo5
1012 | EthereumHardfork::Amsterdam,
1013 ) => BlobParams::bpo2(),
1014 _ => get_blob_params_by_spec_id(spec_id),
1015 }
1016 }
1017
1018 pub fn ensure_eip1559_active(&self) -> Result<(), BlockchainError> {
1020 if self.is_eip1559() {
1021 return Ok(());
1022 }
1023 Err(BlockchainError::EIP1559TransactionUnsupportedAtHardfork)
1024 }
1025
1026 pub fn ensure_eip2930_active(&self) -> Result<(), BlockchainError> {
1028 if self.is_eip2930() {
1029 return Ok(());
1030 }
1031 Err(BlockchainError::EIP2930TransactionUnsupportedAtHardfork)
1032 }
1033
1034 pub fn ensure_eip4844_active(&self) -> Result<(), BlockchainError> {
1035 if self.is_eip4844() {
1036 return Ok(());
1037 }
1038 Err(BlockchainError::EIP4844TransactionUnsupportedAtHardfork)
1039 }
1040
1041 pub fn ensure_eip7702_active(&self) -> Result<(), BlockchainError> {
1042 if self.is_eip7702() {
1043 return Ok(());
1044 }
1045 Err(BlockchainError::EIP7702TransactionUnsupportedAtHardfork)
1046 }
1047
1048 #[cfg(feature = "optimism")]
1050 pub const fn ensure_op_deposits_active(&self) -> Result<(), BlockchainError> {
1051 if self.is_optimism() {
1052 return Ok(());
1053 }
1054 Err(BlockchainError::DepositTransactionUnsupported)
1055 }
1056
1057 pub const fn ensure_tempo_active(&self) -> Result<(), BlockchainError> {
1059 if self.is_tempo() {
1060 return Ok(());
1061 }
1062 Err(BlockchainError::TempoTransactionUnsupported)
1063 }
1064
1065 fn inspector_tx_config(&self) -> InspectorTxConfig {
1067 InspectorTxConfig {
1068 print_traces: self.print_traces,
1069 print_logs: self.print_logs,
1070 enable_steps_tracing: self.enable_steps_tracing,
1071 call_trace_decoder: self.call_trace_decoder.clone(),
1072 }
1073 }
1074
1075 fn pool_tx_gas_config(&self, evm_env: &EvmEnv) -> PoolTxGasConfig {
1077 let spec_id = *evm_env.spec_id();
1078 let is_cancun = spec_id >= SpecId::CANCUN;
1079 let blob_params = self.blob_params();
1080 PoolTxGasConfig {
1081 disable_block_gas_limit: evm_env.cfg_env.disable_block_gas_limit,
1082 tx_gas_limit_cap: evm_env.cfg_env.tx_gas_limit_cap,
1083 tx_gas_limit_cap_resolved: evm_env.cfg_env.tx_gas_limit_cap(),
1084 max_blob_gas_per_block: blob_params.max_blob_gas_per_block(),
1085 is_cancun,
1086 }
1087 }
1088
1089 pub fn gas_limit(&self) -> u64 {
1091 self.evm_env.read().block_env.gas_limit
1092 }
1093
1094 pub fn set_gas_limit(&self, gas_limit: u64) {
1096 self.evm_env.write().block_env.gas_limit = gas_limit;
1097 }
1098
1099 pub fn base_fee(&self) -> u64 {
1101 self.fees.base_fee()
1102 }
1103
1104 pub const fn is_min_priority_fee_enforced(&self) -> bool {
1106 self.fees.is_min_priority_fee_enforced()
1107 }
1108
1109 pub fn excess_blob_gas_and_price(&self) -> Option<BlobExcessGasAndPrice> {
1110 self.fees.excess_blob_gas_and_price()
1111 }
1112
1113 pub fn set_base_fee(&self, basefee: u64) {
1115 self.fees.set_base_fee(basefee)
1116 }
1117
1118 pub fn set_gas_price(&self, price: u128) {
1120 self.fees.set_gas_price(price)
1121 }
1122
1123 pub fn elasticity(&self) -> f64 {
1124 self.fees.elasticity()
1125 }
1126
1127 pub fn total_difficulty(&self) -> U256 {
1132 self.blockchain.storage.read().total_difficulty
1133 }
1134
1135 pub async fn create_state_snapshot(&self) -> U256 {
1139 let num = self.best_number();
1140 let hash = self.best_hash();
1141 let id = self.db.write().await.snapshot_state();
1142 trace!(target: "backend", "creating snapshot {} at {}", id, num);
1143 self.active_state_snapshots.lock().insert(id, (num, hash));
1144 id
1145 }
1146
1147 pub fn list_state_snapshots(&self) -> BTreeMap<U256, (u64, B256)> {
1148 self.active_state_snapshots.lock().clone().into_iter().collect()
1149 }
1150
1151 fn next_evm_env(&self) -> EvmEnv {
1153 let mut evm_env = self.evm_env.read().clone();
1154 evm_env.block_env.number = evm_env.block_env.number.saturating_add(U256::from(1));
1156 evm_env.block_env.basefee = self.base_fee();
1157 evm_env.block_env.blob_excess_gas_and_price = self.excess_blob_gas_and_price();
1158 evm_env.block_env.timestamp = U256::from(self.time.current_call_timestamp());
1159 evm_env
1160 }
1161
1162 fn tx_replay_evm_env(&self, header: &impl BlockHeader) -> EvmEnv {
1164 let mut evm_env = self.evm_env.read().clone();
1165 evm_env.block_env = block_env_from_header(header);
1166 apply_chain_specific_tx_replay_env_changes(&mut evm_env);
1167 evm_env
1168 }
1169
1170 fn prepare_block_replay<'a>(
1176 &self,
1177 block: &Block,
1178 parent_state: &'a StateDb,
1179 ) -> Result<(CacheDB<&'a StateDb>, EvmEnv), BlockchainError> {
1180 let mut cache_db = AnvilCacheDB::new(parent_state);
1181 let evm_env = self.tx_replay_evm_env(&block.header);
1182 let spec_id = *evm_env.spec_id();
1183 let inspector_tx_config = self.inspector_tx_config();
1184 let gas_config = self.pool_tx_gas_config(&evm_env);
1185
1186 self.execute_with_block_executor(
1187 &mut cache_db,
1188 &evm_env,
1189 block.header.parent_hash,
1190 spec_id,
1191 block.header.parent_beacon_block_root,
1192 BlockExecutionKind::TransactionPrefix,
1193 &[],
1194 &gas_config,
1195 &inspector_tx_config,
1196 &|_, _| Ok(()),
1197 )?;
1198
1199 Ok((cache_db.0, evm_env))
1200 }
1201
1202 fn build_inspector(&self) -> AnvilInspector {
1204 let mut inspector = AnvilInspector::default();
1205
1206 if self.print_logs {
1207 inspector = inspector.with_log_collector();
1208 }
1209 if self.print_traces {
1210 inspector = inspector.with_trace_printer();
1211 }
1212
1213 inspector
1214 }
1215
1216 fn build_mining_inspector(&self) -> AnvilInspector {
1218 let mut inspector = AnvilInspector::default().with_tracing();
1219 if self.enable_steps_tracing {
1220 inspector = inspector.with_steps_tracing();
1221 }
1222 if self.print_logs {
1223 inspector = inspector.with_log_collector();
1224 }
1225 if self.print_traces {
1226 inspector = inspector.with_trace_printer();
1227 }
1228 inspector
1229 }
1230
1231 pub fn new_block_notifications(&self) -> ChainNotifications {
1234 let (tx, rx) = unbounded();
1235 self.new_block_listeners.lock().push(tx);
1236 trace!(target: "backed", "added new block listener");
1237 rx
1238 }
1239
1240 pub fn new_block_listeners_count(&self) -> usize {
1243 self.new_block_listeners.lock().len()
1244 }
1245
1246 fn notify_on_new_block(&self, header: Header, hash: B256) {
1248 self.new_block_listeners.lock().retain(|tx| !tx.is_closed());
1251
1252 let notification =
1253 ChainNotification::Block(NewBlockNotification { hash, header: Arc::new(header) });
1254
1255 self.new_block_listeners
1256 .lock()
1257 .retain(|tx| tx.unbounded_send(notification.clone()).is_ok());
1258 }
1259
1260 fn notify_on_removed_logs(&self, logs: Vec<Log>) {
1263 self.new_block_listeners.lock().retain(|tx| !tx.is_closed());
1266
1267 let notification = ChainNotification::RemovedLogs(Arc::new(logs));
1268
1269 self.new_block_listeners
1270 .lock()
1271 .retain(|tx| tx.unbounded_send(notification.clone()).is_ok());
1272 }
1273
1274 pub fn convert_block_number(&self, block: Option<BlockNumber>) -> u64 {
1276 let current = self.best_number();
1277 match block.unwrap_or(BlockNumber::Latest) {
1278 BlockNumber::Latest | BlockNumber::Pending => current,
1279 BlockNumber::Earliest => 0,
1280 BlockNumber::Number(num) => num,
1281 BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch),
1282 BlockNumber::Finalized => current.saturating_sub(self.slots_in_an_epoch * 2),
1283 }
1284 }
1285
1286 pub(crate) fn block_hash_by_number(&self, number: u64) -> Option<B256> {
1288 self.blockchain.hash(BlockNumber::Number(number).into(), self.slots_in_an_epoch)
1289 }
1290
1291 pub(crate) fn get_block_with_hash(&self, id: impl Into<BlockId>) -> Option<(Block, B256)> {
1293 let hash = self.blockchain.hash(id.into(), self.slots_in_an_epoch)?;
1294 let block = self.get_block_by_hash(hash)?;
1295 Some((block, hash))
1296 }
1297
1298 pub fn get_block(&self, id: impl Into<BlockId>) -> Option<Block> {
1299 self.get_block_with_hash(id).map(|(block, _)| block)
1300 }
1301
1302 pub fn get_block_by_hash(&self, hash: B256) -> Option<Block> {
1303 self.blockchain.get_block_by_hash(&hash)
1304 }
1305
1306 pub(crate) async fn fee_history_next_fees(&self, highest: u64) -> Option<(u128, u128)> {
1312 let _mining_guard = self.mining.lock().await;
1313 let next_number = highest.checked_add(1)?;
1314 if let Some(block) = self.get_block(next_number) {
1315 Some((
1316 block.header.base_fee_per_gas.unwrap_or_default() as u128,
1317 block.header.blob_fee(self.blob_params()).unwrap_or_default(),
1318 ))
1319 } else if highest == self.best_number() {
1320 Some((self.fees().base_fee() as u128, self.fees().base_fee_per_blob_gas()))
1321 } else {
1322 None
1323 }
1324 }
1325
1326 pub(crate) fn mined_parity_trace_transaction(
1328 &self,
1329 hash: B256,
1330 ) -> Option<Vec<LocalizedTransactionTrace>> {
1331 self.blockchain.storage.read().transactions.get(&hash).map(|tx| tx.parity_traces())
1332 }
1333
1334 pub(crate) fn mined_parity_trace_block(
1336 &self,
1337 block: u64,
1338 ) -> Option<Vec<LocalizedTransactionTrace>> {
1339 let block = self.get_block(block)?;
1340 let mut traces = vec![];
1341 let storage = self.blockchain.storage.read();
1342 for tx in block.body.transactions {
1343 if let Some(mined_tx) = storage.transactions.get(&tx.hash()) {
1344 traces.extend(mined_tx.parity_traces());
1345 }
1346 }
1347 Some(traces)
1348 }
1349
1350 pub(crate) fn mined_transaction(&self, hash: B256) -> Option<MinedTransaction<N>> {
1352 self.blockchain.storage.read().transactions.get(&hash).cloned()
1353 }
1354
1355 pub async fn impersonate_signature(
1357 &self,
1358 signature: Bytes,
1359 address: Address,
1360 ) -> Result<(), BlockchainError> {
1361 self.cheats.add_recover_override(signature, address);
1362 Ok(())
1363 }
1364
1365 pub async fn debug_code_by_hash(
1367 &self,
1368 code_hash: B256,
1369 block_id: Option<BlockId>,
1370 ) -> Result<Option<Bytes>, BlockchainError> {
1371 if let Ok(code) = self.db.read().await.code_by_hash_ref(code_hash) {
1372 return Ok(Some(code.original_bytes()));
1373 }
1374 if let Some(fork) = self.get_fork() {
1375 return Ok(fork.debug_code_by_hash(code_hash, block_id).await?);
1376 }
1377
1378 Ok(None)
1379 }
1380
1381 pub async fn debug_db_get(&self, key: String) -> Result<Option<Bytes>, BlockchainError> {
1389 let key_bytes = if key.starts_with("0x") {
1390 hex::decode(&key)
1391 .map_err(|_| BlockchainError::Message("Invalid hex key".to_string()))?
1392 } else {
1393 key.into_bytes()
1394 };
1395
1396 if key_bytes.len() != 33 {
1398 return Err(BlockchainError::Message(format!(
1399 "Invalid key length: expected 33 bytes, got {}",
1400 key_bytes.len()
1401 )));
1402 }
1403
1404 if key_bytes[0] != 0x63 {
1406 return Err(BlockchainError::Message(
1407 "Key prefix must be 0x63 for code hash lookups".to_string(),
1408 ));
1409 }
1410
1411 let code_hash = B256::from_slice(&key_bytes[1..33]);
1412
1413 self.debug_code_by_hash(code_hash, None).await
1415 }
1416
1417 fn mined_block_by_hash(&self, hash: B256) -> Option<AnyRpcBlock> {
1418 let block = self.blockchain.get_block_by_hash(&hash)?;
1419 Some(self.convert_block_with_hash(block, Some(hash)))
1420 }
1421
1422 pub(crate) async fn mined_transactions_by_block_number(
1423 &self,
1424 number: BlockNumber,
1425 ) -> Option<Vec<AnyRpcTransaction>> {
1426 if let Some(block) = self.get_block(number) {
1427 return self.mined_transactions_in_block(&block);
1428 }
1429 None
1430 }
1431
1432 pub(crate) fn mined_transactions_in_block(
1434 &self,
1435 block: &Block,
1436 ) -> Option<Vec<AnyRpcTransaction>> {
1437 let mut transactions = Vec::with_capacity(block.body.transactions.len());
1438 let base_fee = block.header.base_fee_per_gas();
1439 let storage = self.blockchain.storage.read();
1440 for hash in block.body.transactions.iter().map(|tx| tx.hash()) {
1441 let info = storage.transactions.get(&hash)?.info.clone();
1442 let tx = block.body.transactions.get(info.transaction_index as usize)?.clone();
1443
1444 let tx = transaction_build(Some(hash), tx, Some(block), Some(info), base_fee);
1445 transactions.push(tx);
1446 }
1447 Some(transactions)
1448 }
1449
1450 pub fn mined_block_by_number(&self, number: BlockNumber) -> Option<AnyRpcBlock> {
1451 let (block, hash) = self.get_block_with_hash(number)?;
1452 let mut block = self.convert_block_with_hash(block, Some(hash));
1453 block.transactions.convert_to_hashes();
1454 Some(block)
1455 }
1456
1457 pub fn get_full_block(&self, id: impl Into<BlockId>) -> Option<AnyRpcBlock> {
1458 let (block, hash) = self.get_block_with_hash(id)?;
1459 let transactions = self.mined_transactions_in_block(&block)?;
1460 let mut block = self.convert_block_with_hash(block, Some(hash));
1461 block.inner.transactions = BlockTransactions::Full(transactions);
1462 Some(block)
1463 }
1464
1465 pub fn convert_block(&self, block: Block) -> AnyRpcBlock {
1467 self.convert_block_with_hash(block, None)
1468 }
1469
1470 pub fn convert_block_with_hash(&self, block: Block, known_hash: Option<B256>) -> AnyRpcBlock {
1473 let size = U256::from(alloy_rlp::encode(canonical_block(block.clone())).len() as u32);
1474
1475 let header = block.header.clone();
1476 let transactions = block.body.transactions;
1477
1478 let hash = known_hash.unwrap_or_else(|| header.hash_slow());
1479 let number = header.number();
1480 let withdrawals_root = header.withdrawals_root();
1481 let tempo_fields = header
1482 .as_tempo()
1483 .map(|header| {
1484 (
1485 header.timestamp_millis(),
1486 header.general_gas_limit,
1487 header.shared_gas_limit,
1488 header.timestamp_millis_part,
1489 )
1490 })
1491 .or_else(|| {
1492 self.is_tempo()
1493 .then(|| (header.timestamp().saturating_mul(1000), header.gas_limit(), 0, 0))
1494 });
1495
1496 let block = AlloyBlock {
1497 header: AlloyHeader {
1498 inner: AnyHeader::from(header.into_inner()),
1499 hash,
1500 total_difficulty: Some(self.total_difficulty()),
1501 size: Some(size),
1502 },
1503 transactions: alloy_rpc_types::BlockTransactions::Hashes(
1504 transactions.into_iter().map(|tx| tx.hash()).collect(),
1505 ),
1506 uncles: vec![],
1507 withdrawals: withdrawals_root.map(|_| Default::default()),
1508 };
1509
1510 let mut block = WithOtherFields::new(block);
1511
1512 if is_arbitrum(self.chain_id().to::<u64>()) {
1514 block.other.insert("l1BlockNumber".to_string(), number.into());
1516 }
1517
1518 if let Some((
1519 timestamp_millis,
1520 general_gas_limit,
1521 shared_gas_limit,
1522 timestamp_millis_part,
1523 )) = tempo_fields
1524 {
1525 block.other.insert(
1526 "timestampMillis".to_string(),
1527 serde_json::Value::String(format!("0x{timestamp_millis:x}")),
1528 );
1529 block.other.insert(
1530 "mainBlockGeneralGasLimit".to_string(),
1531 serde_json::Value::String(format!("0x{general_gas_limit:x}")),
1532 );
1533 block.other.insert(
1534 "sharedGasLimit".to_string(),
1535 serde_json::Value::String(format!("0x{shared_gas_limit:x}")),
1536 );
1537 block.other.insert(
1538 "timestampMillisPart".to_string(),
1539 serde_json::Value::String(format!("0x{timestamp_millis_part:x}")),
1540 );
1541 }
1542
1543 AnyRpcBlock::from(block)
1544 }
1545
1546 pub async fn block_by_hash(&self, hash: B256) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1547 trace!(target: "backend", "get block by hash {:?}", hash);
1548 if let tx @ Some(_) = self.mined_block_by_hash(hash) {
1549 return Ok(tx);
1550 }
1551
1552 if let Some(fork) = self.get_fork() {
1553 return Ok(fork.block_by_hash(hash).await?);
1554 }
1555
1556 Ok(None)
1557 }
1558
1559 pub async fn block_by_hash_full(
1560 &self,
1561 hash: B256,
1562 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1563 trace!(target: "backend", "get block by hash {:?}", hash);
1564 if let tx @ Some(_) = self.get_full_block(hash) {
1565 return Ok(tx);
1566 }
1567
1568 if let Some(fork) = self.get_fork() {
1569 return Ok(fork.block_by_hash_full(hash).await?);
1570 }
1571
1572 Ok(None)
1573 }
1574
1575 pub async fn block_by_number(
1576 &self,
1577 number: BlockNumber,
1578 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1579 trace!(target: "backend", "get block by number {:?}", number);
1580 if let tx @ Some(_) = self.mined_block_by_number(number) {
1581 return Ok(tx);
1582 }
1583
1584 if let Some(fork) = self.get_fork() {
1585 let number = self.convert_block_number(Some(number));
1586 if fork.predates_fork_inclusive(number) {
1587 return Ok(fork.block_by_number(number).await?);
1588 }
1589 }
1590
1591 Ok(None)
1592 }
1593
1594 pub async fn block_by_number_full(
1595 &self,
1596 number: BlockNumber,
1597 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1598 trace!(target: "backend", "get block by number {:?}", number);
1599 if let tx @ Some(_) = self.get_full_block(number) {
1600 return Ok(tx);
1601 }
1602
1603 if let Some(fork) = self.get_fork() {
1604 let number = self.convert_block_number(Some(number));
1605 if fork.predates_fork_inclusive(number) {
1606 return Ok(fork.block_by_number_full(number).await?);
1607 }
1608 }
1609
1610 Ok(None)
1611 }
1612
1613 pub async fn ensure_block_number<T: Into<BlockId>>(
1619 &self,
1620 block_id: Option<T>,
1621 ) -> Result<u64, BlockchainError> {
1622 let current = self.best_number();
1623 let requested =
1624 match block_id.map(Into::into).unwrap_or(BlockId::Number(BlockNumber::Latest)) {
1625 BlockId::Hash(hash) => {
1626 self.block_by_hash(hash.block_hash)
1627 .await?
1628 .ok_or(BlockchainError::BlockNotFound)?
1629 .header
1630 .number
1631 }
1632 BlockId::Number(num) => match num {
1633 BlockNumber::Latest | BlockNumber::Pending => current,
1634 BlockNumber::Earliest => U64::ZERO.to::<u64>(),
1635 BlockNumber::Number(num) => num,
1636 BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch),
1637 BlockNumber::Finalized => current.saturating_sub(self.slots_in_an_epoch * 2),
1638 },
1639 };
1640
1641 if requested > current {
1642 Err(BlockchainError::BlockOutOfRange(current, requested))
1643 } else {
1644 Ok(requested)
1645 }
1646 }
1647
1648 fn inject_precompiles(&self, precompiles: &mut PrecompilesMap, evm_env: &EvmEnv) {
1656 self.networks.inject_precompiles(precompiles);
1657 self.networks.inject_chain_precompiles(
1658 precompiles,
1659 evm_env.cfg_env.chain_id,
1660 evm_env.block_env.timestamp.saturating_to(),
1661 );
1662
1663 if let Some(factory) = &self.precompile_factory {
1664 factory.install(precompiles);
1665 }
1666
1667 let cheats = Arc::new(self.cheats.clone());
1668 if cheats.has_recover_overrides() {
1669 let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats));
1670 precompiles.apply_precompile(&EC_RECOVER, move |_| {
1671 Some(DynPrecompile::new_stateful(
1672 cheat_ecrecover.precompile_id().clone(),
1673 move |input| cheat_ecrecover.call(input),
1674 ))
1675 });
1676 }
1677 }
1678
1679 fn inject_arbitrum_precompile(&self, precompiles: &mut PrecompilesMap, evm_env: &EvmEnv) {
1680 let Some(block_number) = self.arbitrum_block_number(evm_env) else { return };
1681 precompiles.apply_precompile(&arbitrum::ARB_SYS_ADDRESS, move |_| {
1682 Some(arbitrum::arb_sys_precompile(block_number))
1683 });
1684 }
1685
1686 fn simulation_precompile_overrides(
1687 &self,
1688 state_overrides: Option<&StateOverride>,
1689 evm_env: &EvmEnv,
1690 ) -> Result<SimulationPrecompileOverrides, BlockchainError> {
1691 let mut moves = state_overrides
1692 .into_iter()
1693 .flatten()
1694 .filter_map(|(source, account)| {
1695 account.move_precompile_to.map(|destination| (*source, destination))
1696 })
1697 .collect::<Vec<_>>();
1698 moves.sort_unstable();
1699 if moves.is_empty() {
1700 return Ok(SimulationPrecompileOverrides::default());
1701 }
1702 if self.is_optimism() || self.is_tempo() {
1703 return Err(simulate_rpc_error(
1704 -32000,
1705 "precompile moves are not supported on this network",
1706 ));
1707 }
1708
1709 let mut precompiles = PrecompilesMap::from_static(Precompiles::new(
1710 PrecompileSpecId::from_spec_id(*evm_env.spec_id()),
1711 ));
1712 self.inject_precompiles(&mut precompiles, evm_env);
1713 self.inject_arbitrum_precompile(&mut precompiles, evm_env);
1714 let precompile_addresses = precompiles.addresses().copied().collect::<HashSet<_>>();
1715
1716 for (source, _) in &moves {
1719 if !precompile_addresses.contains(source) {
1720 return Err(simulate_rpc_error(
1721 -32000,
1722 format!("account {source} is not a precompile"),
1723 ));
1724 }
1725 }
1726 for (source, destination) in &moves {
1727 if source == destination {
1728 return Err(simulate_rpc_error(
1729 -38022,
1730 format!("cannot move precompile {source} to itself"),
1731 ));
1732 }
1733 }
1734 let mut destinations = Vec::with_capacity(moves.len());
1735 for (_, destination) in &moves {
1736 if destinations.contains(destination) {
1737 return Err(simulate_rpc_error(
1738 -38023,
1739 format!("multiple precompiles moved to {destination}"),
1740 ));
1741 }
1742 destinations.push(*destination);
1743 }
1744
1745 Ok(SimulationPrecompileOverrides { moves })
1746 }
1747
1748 fn apply_simulation_precompile_overrides(
1749 &self,
1750 precompiles: &mut PrecompilesMap,
1751 overrides: &SimulationPrecompileOverrides,
1752 ) -> Result<alloy_primitives::map::AddressSet, BlockchainError> {
1753 let warm_addresses = precompiles.addresses().copied().collect();
1754 precompiles.move_precompiles(overrides.moves.iter().copied()).map_err(
1755 |MovePrecompileError::NotAPrecompile(address)| {
1756 simulate_rpc_error(-32000, format!("account {address} is not a precompile"))
1757 },
1758 )?;
1759
1760 let moved_sources =
1762 Arc::new(overrides.moves.iter().map(|(source, _)| *source).collect::<HashSet<_>>());
1763 precompiles.map_precompile_lookup(move |address, previous| {
1764 if moved_sources.contains(address) {
1765 None
1766 } else {
1767 previous.and_then(|lookup| lookup.lookup(address))
1768 }
1769 });
1770 Ok(warm_addresses)
1771 }
1772
1773 fn inject_tempo_precompiles<DB, I>(
1774 &self,
1775 evm: &mut tempo_evm::evm::TempoEvm<DB, I>,
1776 evm_env: &EvmEnv,
1777 ) where
1778 DB: Database,
1779 I: Inspector<TempoContext<DB>>,
1780 {
1781 self.inject_precompiles(evm.precompiles_mut(), evm_env);
1782 let cfg = evm.ctx().cfg.clone();
1784 let non_creditable_slots = evm.non_creditable_slots();
1785 extend_tempo_precompiles(
1786 evm.precompiles_mut(),
1787 &cfg,
1788 StorageActions::disabled(),
1789 non_creditable_slots,
1790 );
1791 }
1792
1793 fn transact_eth_with_inspector_ref<'db, I, DB>(
1798 &self,
1799 db: &'db DB,
1800 evm_env: &EvmEnv,
1801 inspector: &mut I,
1802 tx_env: TxEnv,
1803 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
1804 where
1805 DB: DatabaseRef + ?Sized,
1806 I: Inspector<EthEvmContext<WrapDatabaseRef<&'db DB>>>,
1807 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1808 {
1809 self.transact_eth_with_inspector_ref_and_precompile_overrides(
1810 db,
1811 evm_env,
1812 inspector,
1813 tx_env,
1814 &SimulationPrecompileOverrides::default(),
1815 )
1816 }
1817
1818 fn transact_eth_with_inspector_ref_and_precompile_overrides<'db, I, DB>(
1819 &self,
1820 db: &'db DB,
1821 evm_env: &EvmEnv,
1822 inspector: &mut I,
1823 tx_env: TxEnv,
1824 overrides: &SimulationPrecompileOverrides,
1825 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
1826 where
1827 DB: DatabaseRef + ?Sized,
1828 I: Inspector<EthEvmContext<WrapDatabaseRef<&'db DB>>>,
1829 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1830 {
1831 let mut evm = EthEvmFactory::default().create_evm_with_inspector(
1832 WrapDatabaseRef(db),
1833 evm_env.clone(),
1834 inspector,
1835 );
1836 self.inject_precompiles(evm.precompiles_mut(), evm_env);
1837 self.inject_arbitrum_precompile(evm.precompiles_mut(), evm_env);
1838 if !overrides.moves.is_empty() {
1839 let warm_addresses =
1840 self.apply_simulation_precompile_overrides(evm.precompiles_mut(), overrides)?;
1841 evm.ctx_mut().journal_mut().warm_precompiles(&warm_addresses);
1843 }
1844 Ok(evm.transact(tx_env)?)
1845 }
1846
1847 fn transact_eth_simulation_with_inspector_ref<'db, I, DB>(
1848 &self,
1849 db: &'db DB,
1850 evm_env: &EvmEnv,
1851 inspector: &mut I,
1852 tx_env: TxEnv,
1853 overrides: &SimulationPrecompileOverrides,
1854 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
1855 where
1856 DB: DatabaseRef + ?Sized,
1857 I: Inspector<EthEvmContext<WrapDatabaseRef<&'db DB>>>,
1858 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1859 {
1860 let mut evm = EthEvmFactory::default().create_evm_with_inspector(
1861 WrapDatabaseRef(db),
1862 evm_env.clone(),
1863 inspector,
1864 );
1865 self.inject_precompiles(evm.precompiles_mut(), evm_env);
1866 self.inject_arbitrum_precompile(evm.precompiles_mut(), evm_env);
1867 if !overrides.moves.is_empty() {
1868 let warm_addresses =
1869 self.apply_simulation_precompile_overrides(evm.precompiles_mut(), overrides)?;
1870 evm.ctx_mut().journal_mut().warm_precompiles(&warm_addresses);
1871 }
1872
1873 let mut evm = evm.into_inner();
1874 evm.ctx_mut().set_tx(tx_env);
1875 let mut handler = SimulationHandler::<
1876 _,
1877 revm::context::result::EVMError<DatabaseError>,
1878 EthFrame<EthInterpreter>,
1879 >::default();
1880 let result = handler.inspect_run(&mut evm)?;
1881 let state = evm.ctx_mut().journal_mut().finalize();
1882 Ok(ResultAndState { result, state })
1883 }
1884
1885 fn transact_envelope_with_inspector_ref<'db, I, DB>(
1888 &self,
1889 db: &'db DB,
1890 evm_env: &EvmEnv,
1891 inspector: &mut I,
1892 tx: &FoundryTxEnvelope,
1893 sender: Address,
1894 ) -> Result<(ResultAndState<HaltReason>, TxEnv), BlockchainError>
1895 where
1896 DB: DatabaseRef + ?Sized,
1897 I: BackendInspector<WrapDatabaseRef<&'db DB>>,
1898 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1899 {
1900 if tx.is_tempo() {
1901 let tx_env: TempoTxEnv =
1902 FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into());
1903 let base = tx_env.inner.clone();
1904 let result = self.transact_tempo_with_inspector_ref(db, evm_env, inspector, tx_env)?;
1905 return Ok((result, base));
1906 }
1907 #[cfg(feature = "optimism")]
1908 if self.is_optimism() {
1909 let op_tx: OpTransaction<TxEnv> =
1910 FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into());
1911 let base = op_tx.base.clone();
1912 let result = self.transact_op_with_inspector_ref(db, evm_env, inspector, op_tx)?;
1913 return Ok((result, base));
1914 }
1915 let tx_env: TxEnv =
1916 FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into());
1917 let base = tx_env.clone();
1918 let result = self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env)?;
1919 Ok((result, base))
1920 }
1921
1922 fn build_tempo_evm_env(&self, evm_env: &EvmEnv) -> EvmEnvFor<TempoEvmNetwork> {
1925 let hardfork = self.tempo_hardfork();
1926 EvmEnv::new(
1927 evm_env.cfg_env.clone().with_spec_and_gas_params(hardfork, tempo_gas_params(hardfork)),
1928 TempoBlockEnv {
1929 inner: evm_env.block_env.clone(),
1930 timestamp_millis_part: 0,
1931 ..Default::default()
1932 },
1933 )
1934 }
1935
1936 fn transact_tempo_with_inspector_ref<'db, I, DB>(
1938 &self,
1939 db: &'db DB,
1940 evm_env: &EvmEnv,
1941 inspector: &mut I,
1942 tx_env: TempoTxEnv,
1943 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
1944 where
1945 DB: DatabaseRef + ?Sized,
1946 I: Inspector<TempoContext<WrapDatabaseRef<&'db DB>>>,
1947 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1948 {
1949 let tempo_env = self.build_tempo_evm_env(evm_env);
1950 let mut evm = TempoEvmFactory::default().create_evm_with_inspector(
1951 WrapDatabaseRef(db),
1952 tempo_env,
1953 inspector,
1954 );
1955 self.inject_tempo_precompiles(&mut evm, evm_env);
1956 let result = evm.transact(tx_env)?;
1957 Ok(ResultAndState {
1958 result: result.result.map_haltreason(|h| match h {
1959 TempoHaltReason::Ethereum(eth) => eth,
1960 _ => HaltReason::PrecompileError,
1961 }),
1962 state: result.state,
1963 })
1964 }
1965
1966 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
1969 fn execute_with_block_executor<DB>(
1970 &self,
1971 db: DB,
1972 evm_env: &EvmEnv,
1973 parent_hash: B256,
1974 spec_id: SpecId,
1975 parent_beacon_block_root: Option<B256>,
1976 execution_kind: BlockExecutionKind,
1977 pool_transactions: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
1978 gas_config: &PoolTxGasConfig,
1979 inspector_tx_config: &InspectorTxConfig,
1980 validator: &dyn Fn(
1981 &PoolTransaction<FoundryTxEnvelope>,
1982 &AccountInfo,
1983 ) -> Result<(), InvalidTransactionError>,
1984 ) -> Result<
1985 (ExecutedPoolTransactions<FoundryTxEnvelope>, BlockExecutionResult<FoundryReceiptEnvelope>),
1986 BlockchainError,
1987 >
1988 where
1989 DB: StateDB<Error = DatabaseError>,
1990 {
1991 let inspector = self.build_mining_inspector();
1992 let ethereum_transitions = self.ethereum_block_transitions(
1993 self.hardfork(),
1994 parent_beacon_block_root,
1995 execution_kind,
1996 );
1997
1998 macro_rules! run {
1999 ($evm:expr) => {{
2000 self.inject_precompiles($evm.precompiles_mut(), evm_env);
2001 self.inject_arbitrum_precompile($evm.precompiles_mut(), evm_env);
2002 let mut executor =
2003 AnvilBlockExecutor::new($evm, parent_hash, spec_id, ethereum_transitions);
2004 executor
2005 .apply_pre_execution_changes()
2006 .map_err(|err| BlockchainError::Internal(err.to_string()))?;
2007 let pool_result = execute_pool_transactions(
2008 &mut executor,
2009 pool_transactions,
2010 gas_config,
2011 inspector_tx_config,
2012 self.cheats(),
2013 validator,
2014 );
2015 let (evm, block_result) =
2016 executor.finish().map_err(|err| BlockchainError::Internal(err.to_string()))?;
2017 drop(evm);
2018 Ok((pool_result, block_result))
2019 }};
2020 }
2021
2022 #[cfg(feature = "optimism")]
2023 if self.is_optimism() {
2024 let op_env = EvmEnv::new(
2025 evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(self.hardfork.into()),
2026 evm_env.block_env.clone(),
2027 );
2028 let mut evm =
2029 OpEvmFactory::<OpTx>::default().create_evm_with_inspector(db, op_env, inspector);
2030 return run!(evm);
2031 }
2032
2033 if self.is_tempo() {
2034 let tempo_env = self.build_tempo_evm_env(evm_env);
2035 let mut evm =
2036 TempoEvmFactory::default().create_evm_with_inspector(db, tempo_env, inspector);
2037 run!(evm)
2038 } else {
2039 let mut evm =
2040 EthEvmFactory::default().create_evm_with_inspector(db, evm_env.clone(), inspector);
2041 run!(evm)
2042 }
2043 }
2044
2045 fn apply_simulation_pre_execution_changes<DB>(
2047 &self,
2048 db: DB,
2049 evm_env: &EvmEnv,
2050 parent_hash: B256,
2051 transitions: EthereumBlockTransitions,
2052 ) -> Result<(), BlockchainError>
2053 where
2054 DB: StateDB<Error = DatabaseError>,
2055 {
2056 let inspector = self.build_mining_inspector();
2057 let mut evm =
2058 EthEvmFactory::default().create_evm_with_inspector(db, evm_env.clone(), inspector);
2059 self.inject_precompiles(evm.precompiles_mut(), evm_env);
2060 self.inject_arbitrum_precompile(evm.precompiles_mut(), evm_env);
2061 apply_ethereum_pre_execution_changes(&mut evm, parent_hash, transitions)
2062 .map_err(|err| BlockchainError::Internal(err.to_string()))
2063 }
2064
2065 fn apply_simulation_post_execution_changes<DB>(
2067 &self,
2068 db: DB,
2069 evm_env: &EvmEnv,
2070 transitions: EthereumBlockTransitions,
2071 receipts: &[FoundryReceiptEnvelope],
2072 ) -> Result<alloy_eips::eip7685::Requests, BlockchainError>
2073 where
2074 DB: StateDB<Error = DatabaseError>,
2075 {
2076 let inspector = self.build_mining_inspector();
2077 let mut evm =
2078 EthEvmFactory::default().create_evm_with_inspector(db, evm_env.clone(), inspector);
2079 self.inject_precompiles(evm.precompiles_mut(), evm_env);
2080 self.inject_arbitrum_precompile(evm.precompiles_mut(), evm_env);
2081 apply_ethereum_post_execution_changes(&mut evm, transitions, receipts)
2082 .map_err(|err| BlockchainError::Internal(err.to_string()))
2083 }
2084
2085 fn build_call_env(
2094 &self,
2095 request: WithOtherFields<TransactionRequest>,
2096 fee_details: FeeDetails,
2097 block_env: BlockEnv,
2098 ) -> (EvmEnv, TxEnv, OpCallDepositInfo) {
2099 let tx_type = request.minimal_tx_type() as u8;
2100
2101 let WithOtherFields::<TransactionRequest> {
2102 inner:
2103 TransactionRequest {
2104 from,
2105 to,
2106 gas,
2107 value,
2108 input,
2109 access_list,
2110 blob_versioned_hashes,
2111 authorization_list,
2112 nonce,
2113 sidecar: _,
2114 chain_id,
2115 .. },
2117 other,
2118 } = request;
2119
2120 let FeeDetails {
2121 gas_price,
2122 max_fee_per_gas,
2123 max_priority_fee_per_gas,
2124 max_fee_per_blob_gas,
2125 } = fee_details;
2126
2127 let gas_limit = gas.unwrap_or(block_env.gas_limit);
2128 let mut evm_env = self.evm_env.read().clone();
2129 evm_env.block_env = block_env;
2130 evm_env.cfg_env.disable_block_gas_limit = true;
2133 evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
2134
2135 evm_env.cfg_env.disable_base_fee = true;
2141
2142 evm_env.cfg_env.disable_nonce_check = true;
2144
2145 let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| {
2146 self.fees().raw_gas_price().saturating_add(MIN_SUGGESTED_PRIORITY_FEE)
2147 });
2148 let caller = from.unwrap_or_default();
2149 let to = to.as_ref().and_then(TxKind::to);
2150 let blob_hashes = blob_versioned_hashes.unwrap_or_default();
2151 let mut tx_env = TxEnv {
2152 caller,
2153 gas_limit,
2154 gas_price,
2155 gas_priority_fee: max_priority_fee_per_gas,
2156 max_fee_per_blob_gas: max_fee_per_blob_gas
2157 .or_else(|| {
2158 if blob_hashes.is_empty() { Some(0) } else { evm_env.block_env.blob_gasprice() }
2159 })
2160 .unwrap_or_default(),
2161 kind: match to {
2162 Some(addr) => TxKind::Call(*addr),
2163 None => TxKind::Create,
2164 },
2165 tx_type,
2166 value: value.unwrap_or_default(),
2167 data: input.into_input().unwrap_or_default(),
2168 chain_id: Some(chain_id.unwrap_or(self.chain_id().to::<u64>())),
2169 access_list: access_list.unwrap_or_default(),
2170 blob_hashes,
2171 ..Default::default()
2172 };
2173 tx_env.set_signed_authorization(authorization_list.unwrap_or_default());
2174
2175 if let Some(nonce) = nonce {
2176 tx_env.nonce = nonce;
2177 }
2178
2179 if evm_env.block_env.basefee == 0 {
2180 evm_env.cfg_env.disable_base_fee = true;
2183 }
2184
2185 #[cfg(feature = "optimism")]
2187 let op_deposit = if self.ensure_op_deposits_active().is_ok()
2188 && let Ok(deposit) = get_deposit_tx_parts(&other)
2189 {
2190 deposit
2191 } else {
2192 OpCallDepositInfo::default()
2193 };
2194 #[cfg(not(feature = "optimism"))]
2195 let op_deposit = {
2196 let _ = &other;
2198 OpCallDepositInfo
2199 };
2200
2201 (evm_env, tx_env, op_deposit)
2202 }
2203
2204 fn prepare_call_env(
2205 &self,
2206 state: &dyn DatabaseRef,
2207 request: WithOtherFields<TransactionRequest>,
2208 fee_details: FeeDetails,
2209 block_env: BlockEnv,
2210 ) -> Result<PreparedCall, BlockchainError> {
2211 let request = self.parse_transaction_request(request)?;
2212 self.prepare_typed_call_env(state, request, fee_details, block_env)
2213 }
2214
2215 fn prepare_base_call_env(
2216 &self,
2217 request: WithOtherFields<TransactionRequest>,
2218 fee_details: FeeDetails,
2219 block_env: BlockEnv,
2220 ) -> PreparedCall {
2221 let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block_env);
2222 #[cfg(feature = "optimism")]
2223 let tx_env = if self.is_optimism() {
2224 CallTxEnv::Op(OpTransaction { base: tx_env, deposit: op_deposit, ..Default::default() })
2225 } else if self.is_tempo() {
2226 CallTxEnv::Tempo(TempoTxEnv::from(tx_env))
2227 } else {
2228 CallTxEnv::Eth(tx_env)
2229 };
2230 #[cfg(not(feature = "optimism"))]
2231 let tx_env = {
2232 let _ = op_deposit;
2233 if self.is_tempo() {
2234 CallTxEnv::Tempo(TempoTxEnv::from(tx_env))
2235 } else {
2236 CallTxEnv::Eth(tx_env)
2237 }
2238 };
2239 PreparedCall { evm_env, tx_env, simulated_tempo_tx: None }
2240 }
2241
2242 pub(crate) fn parse_transaction_request(
2244 &self,
2245 request: WithOtherFields<TransactionRequest>,
2246 ) -> Result<FoundryTransactionRequest, BlockchainError> {
2247 let transaction_type = request.transaction_type;
2248 if !self.is_tempo() && transaction_type != Some(TEMPO_TX_TYPE_ID) {
2249 #[cfg(feature = "optimism")]
2250 if transaction_type == Some(DEPOSIT_TX_TYPE_ID)
2251 || transaction_type == Some(POST_EXEC_TX_TYPE_ID)
2252 || get_deposit_tx_parts(&request.other).is_ok()
2253 {
2254 return Ok(FoundryTransactionRequest::Op(request));
2255 }
2256 return Ok(FoundryTransactionRequest::Ethereum(request.into_inner()));
2257 }
2258
2259 let parsed: FoundryTransactionRequest =
2260 request.try_into().map_err(|err: serde_json::Error| {
2261 BlockchainError::InvalidTransactionRequest(err.to_string())
2262 })?;
2263 if parsed.is_tempo() {
2264 self.ensure_tempo_active()?;
2265 }
2266 if parsed.is_tempo()
2267 && self.is_tempo()
2268 && transaction_type.is_some_and(|ty| ty != TEMPO_TX_TYPE_ID)
2269 {
2270 return Err(BlockchainError::FailedToDecodeTransaction);
2271 }
2272 Ok(parsed)
2273 }
2274
2275 fn build_tempo_request_env(
2276 &self,
2277 request: TempoTransactionRequest,
2278 mut base: TxEnv,
2279 ) -> Result<(TempoTxEnv, AASigned), BlockchainError> {
2280 let fee_payer = request.fee_payer_signature.map(|_| {
2281 request.clone().build_aa().ok().and_then(|tx| tx.recover_fee_payer(base.caller).ok())
2282 });
2283
2284 let mut response_request = request.clone();
2287 response_request.inner.from = Some(base.caller);
2288 response_request.inner.gas = Some(base.gas_limit);
2289 response_request.inner.nonce = Some(base.nonce);
2290 response_request.inner.chain_id = base.chain_id;
2291 response_request.inner.max_fee_per_gas = Some(base.gas_price);
2292 response_request.inner.max_priority_fee_per_gas =
2293 Some(base.gas_priority_fee.unwrap_or_default());
2294 response_request.inner.access_list = Some(base.access_list.clone());
2295 if response_request.calls.is_empty()
2296 && response_request.inner.to.is_none()
2297 && !base.data.is_empty()
2298 {
2299 response_request.inner.to = Some(base.kind);
2300 }
2301 let response_tx = response_request
2302 .build_aa()
2303 .map_err(|err| BlockchainError::InvalidTransactionRequest(err.to_string()))?;
2304 let response_tx = response_tx.into_signed(TempoSignature::default());
2305 let key_type = request.key_type.unwrap_or(SignatureType::Secp256k1);
2306 let key_data = request.key_data.clone();
2307 let key_id = request.key_id;
2308 let signature = mock_tempo_signature(
2309 key_type,
2310 key_data,
2311 key_id,
2312 base.caller,
2313 self.tempo_hardfork().is_t1c(),
2314 );
2315 let mut calls = request.calls;
2316 if let Some(to) = request.inner.to {
2317 calls.push(Call {
2318 to,
2319 value: request.inner.value.unwrap_or_default(),
2320 input: request.inner.input.into_input().unwrap_or_default(),
2321 });
2322 } else if calls.is_empty() && !base.data.is_empty() {
2323 calls.push(Call { to: base.kind, value: base.value, input: base.data.clone() });
2326 }
2327 if let Some(first_call) = calls.first() {
2328 base.kind = first_call.to;
2329 base.value = first_call.value;
2330 base.data = first_call.input.clone();
2331 }
2332 let tx_env = TempoTxEnv {
2333 fee_token: request.fee_token,
2334 is_system_tx: false,
2335 unique_tx_identifier: Some(TEMPO_RPC_SIMULATION_CONTEXT),
2336 fee_payer,
2337 tempo_tx_env: Some(Box::new(TempoBatchCallEnv {
2338 aa_calls: calls,
2339 signature,
2340 tempo_authorization_list: request
2341 .tempo_authorization_list
2342 .into_iter()
2343 .map(RecoveredTempoAuthorization::new)
2344 .collect(),
2345 nonce_key: request.nonce_key.unwrap_or_default(),
2346 key_authorization: request.key_authorization,
2347 signature_hash: B256::ZERO,
2348 tx_hash: B256::ZERO,
2349 valid_before: request.valid_before.map(|value| value.get()),
2350 valid_after: request.valid_after.map(|value| value.get()),
2351 subblock_transaction: false,
2352 override_key_id: key_id,
2353 expiring_nonce_idx: None,
2354 })),
2355 inner: base,
2356 };
2357 Ok((tx_env, response_tx))
2358 }
2359
2360 fn prepare_typed_call_env(
2361 &self,
2362 state: &dyn DatabaseRef,
2363 request: FoundryTransactionRequest,
2364 fee_details: FeeDetails,
2365 block_env: BlockEnv,
2366 ) -> Result<PreparedCall, BlockchainError> {
2367 match request {
2368 FoundryTransactionRequest::Tempo(tempo_request) => {
2369 self.ensure_tempo_active()?;
2370 let mut tempo_request = *tempo_request;
2371 if tempo_request.inner.nonce.is_none() {
2372 let caller = tempo_request.inner.from.unwrap_or_default();
2373 tempo_request.inner.nonce = Some(tempo_nonce(
2374 state,
2375 caller,
2376 tempo_request.nonce_key.unwrap_or_default(),
2377 )?);
2378 }
2379 let inner = WithOtherFields::new(tempo_request.inner.clone());
2380 let (evm_env, base, _) = self.build_call_env(inner, fee_details, block_env);
2381 let (tx_env, simulated_tempo_tx) =
2382 self.build_tempo_request_env(tempo_request, base)?;
2383 Ok(PreparedCall {
2384 evm_env,
2385 tx_env: CallTxEnv::Tempo(tx_env),
2386 simulated_tempo_tx: Some(simulated_tempo_tx),
2387 })
2388 }
2389 FoundryTransactionRequest::Ethereum(request) => Ok(self.prepare_base_call_env(
2390 WithOtherFields::new(request),
2391 fee_details,
2392 block_env,
2393 )),
2394 #[cfg(feature = "optimism")]
2395 FoundryTransactionRequest::Op(request) => {
2396 Ok(self.prepare_base_call_env(request, fee_details, block_env))
2397 }
2398 }
2399 }
2400
2401 fn transact_call_with_inspector_ref<'db, I, DB>(
2402 &self,
2403 db: &'db DB,
2404 evm_env: &EvmEnv,
2405 inspector: &mut I,
2406 tx_env: CallTxEnv,
2407 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
2408 where
2409 DB: DatabaseRef + ?Sized,
2410 I: BackendInspector<WrapDatabaseRef<&'db DB>>,
2411 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
2412 {
2413 match tx_env {
2414 CallTxEnv::Eth(tx_env) => {
2415 self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env)
2416 }
2417 #[cfg(feature = "optimism")]
2418 CallTxEnv::Op(tx_env) => {
2419 self.transact_op_with_inspector_ref(db, evm_env, inspector, tx_env)
2420 }
2421 CallTxEnv::Tempo(tx_env) => {
2422 self.transact_tempo_with_inspector_ref(db, evm_env, inspector, tx_env)
2423 }
2424 }
2425 }
2426
2427 pub fn call_with_state(
2428 &self,
2429 state: &dyn DatabaseRef,
2430 request: WithOtherFields<TransactionRequest>,
2431 fee_details: FeeDetails,
2432 block_env: BlockEnv,
2433 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
2434 let mut inspector = self.build_inspector();
2435 let PreparedCall { evm_env, tx_env, .. } =
2436 self.prepare_call_env(state, request, fee_details, block_env)?;
2437 let ResultAndState { result, state } =
2438 self.transact_call_with_inspector_ref(state, &evm_env, &mut inspector, tx_env)?;
2439
2440 let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
2441 inspector.print_logs();
2442
2443 if self.print_traces {
2444 inspector.into_print_traces(self.call_trace_decoder.clone());
2445 }
2446
2447 Ok((exit_reason, out, gas_used as u128, state))
2448 }
2449
2450 pub(crate) fn call_with_state_typed_gas_limit(
2451 &self,
2452 state: &dyn DatabaseRef,
2453 request: FoundryTransactionRequest,
2454 fee_details: FeeDetails,
2455 block_env: BlockEnv,
2456 gas_limit: u64,
2457 disable_fee_charge: bool,
2458 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
2459 self.call_with_state_typed_inner(
2460 state,
2461 request,
2462 fee_details,
2463 block_env,
2464 TypedCallOverrides {
2465 gas_limit: Some(gas_limit),
2466 disable_fee_charge,
2467 ..Default::default()
2468 },
2469 )
2470 }
2471
2472 pub(crate) fn call_with_state_typed_access_list(
2473 &self,
2474 state: &dyn DatabaseRef,
2475 request: FoundryTransactionRequest,
2476 fee_details: FeeDetails,
2477 block_env: BlockEnv,
2478 access_list: AccessList,
2479 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
2480 self.call_with_state_typed_inner(
2481 state,
2482 request,
2483 fee_details,
2484 block_env,
2485 TypedCallOverrides { access_list: Some(access_list), ..Default::default() },
2486 )
2487 }
2488
2489 fn call_with_state_typed_inner(
2490 &self,
2491 state: &dyn DatabaseRef,
2492 request: FoundryTransactionRequest,
2493 fee_details: FeeDetails,
2494 block_env: BlockEnv,
2495 overrides: TypedCallOverrides,
2496 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
2497 let mut inspector = self.build_inspector();
2498 let PreparedCall { mut evm_env, mut tx_env, .. } =
2499 self.prepare_typed_call_env(state, request, fee_details, block_env)?;
2500 evm_env.cfg_env.disable_fee_charge = overrides.disable_fee_charge;
2501 if let Some(gas_limit) = overrides.gas_limit {
2502 tx_env.base_mut().gas_limit = gas_limit;
2503 }
2504 if let Some(access_list) = overrides.access_list {
2505 tx_env.base_mut().access_list = access_list;
2506 }
2507 let ResultAndState { result, state } =
2508 self.transact_call_with_inspector_ref(state, &evm_env, &mut inspector, tx_env)?;
2509 let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
2510 inspector.print_logs();
2511 Ok((exit_reason, out, gas_used as u128, state))
2512 }
2513
2514 pub fn build_access_list_with_state(
2515 &self,
2516 state: &dyn DatabaseRef,
2517 request: WithOtherFields<TransactionRequest>,
2518 fee_details: FeeDetails,
2519 block_env: BlockEnv,
2520 ) -> Result<(InstructionResult, Option<Output>, u64, AccessList), BlockchainError> {
2521 let mut inspector =
2522 AccessListInspector::new(request.access_list.clone().unwrap_or_default());
2523
2524 let PreparedCall { evm_env, tx_env, .. } =
2525 self.prepare_call_env(state, request, fee_details, block_env)?;
2526 let ResultAndState { result, state: _ } =
2527 self.transact_call_with_inspector_ref(state, &evm_env, &mut inspector, tx_env)?;
2528 let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
2529 let access_list = inspector.access_list();
2530 Ok((exit_reason, out, gas_used, access_list))
2531 }
2532
2533 fn arbitrum_block_number(&self, evm_env: &EvmEnv) -> Option<u64> {
2534 if !arbitrum::is_arbitrum_chain(evm_env.cfg_env.chain_id) {
2535 return None;
2536 }
2537
2538 let env_block = evm_env.block_env.number.saturating_to();
2539 Some(self.get_fork().map_or(env_block, |fork| fork.block_number().max(env_block)))
2540 }
2541
2542 pub fn get_code_with_state(
2543 &self,
2544 state: &dyn DatabaseRef,
2545 address: Address,
2546 ) -> Result<Bytes, BlockchainError> {
2547 trace!(target: "backend", "get code for {:?}", address);
2548 let account = state.basic_ref(address)?.unwrap_or_default();
2549 if account.code_hash == KECCAK_EMPTY {
2550 return Ok(Default::default());
2552 }
2553 let code = if let Some(code) = account.code {
2554 code
2555 } else {
2556 state.code_by_hash_ref(account.code_hash)?
2557 };
2558 Ok(code.bytes()[..code.len()].to_vec().into())
2559 }
2560
2561 pub fn get_balance_with_state<D>(
2562 &self,
2563 state: D,
2564 address: Address,
2565 ) -> Result<U256, BlockchainError>
2566 where
2567 D: DatabaseRef,
2568 {
2569 trace!(target: "backend", "get balance for {:?}", address);
2570 Ok(state.basic_ref(address)?.unwrap_or_default().balance)
2571 }
2572
2573 pub async fn transaction_by_block_number_and_index(
2574 &self,
2575 number: BlockNumber,
2576 index: Index,
2577 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
2578 if let Some(block) = self.mined_block_by_number(number) {
2579 return Ok(self.mined_transaction_by_block_hash_and_index(block.header.hash, index));
2580 }
2581
2582 if let Some(fork) = self.get_fork() {
2583 let number = self.convert_block_number(Some(number));
2584 if fork.predates_fork(number) {
2585 return Ok(fork
2586 .transaction_by_block_number_and_index(number, index.into())
2587 .await?);
2588 }
2589 }
2590
2591 Ok(None)
2592 }
2593
2594 pub async fn transaction_by_block_hash_and_index(
2595 &self,
2596 hash: B256,
2597 index: Index,
2598 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
2599 if let tx @ Some(_) = self.mined_transaction_by_block_hash_and_index(hash, index) {
2600 return Ok(tx);
2601 }
2602
2603 if let Some(fork) = self.get_fork() {
2604 return Ok(fork.transaction_by_block_hash_and_index(hash, index.into()).await?);
2605 }
2606
2607 Ok(None)
2608 }
2609
2610 pub fn mined_transaction_by_block_hash_and_index(
2611 &self,
2612 block_hash: B256,
2613 index: Index,
2614 ) -> Option<AnyRpcTransaction> {
2615 let (info, block, tx) = {
2616 let storage = self.blockchain.storage.read();
2617 let block = storage.blocks.get(&block_hash).cloned()?;
2618 let index: usize = index.into();
2619 let tx = block.body.transactions.get(index)?.clone();
2620 let info = storage.transactions.get(&tx.hash())?.info.clone();
2621 (info, block, tx)
2622 };
2623
2624 Some(transaction_build(
2625 Some(info.transaction_hash),
2626 tx,
2627 Some(&block),
2628 Some(info),
2629 block.header.base_fee_per_gas(),
2630 ))
2631 }
2632
2633 pub async fn transaction_by_hash(
2634 &self,
2635 hash: B256,
2636 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
2637 trace!(target: "backend", "transaction_by_hash={:?}", hash);
2638 if let tx @ Some(_) = self.mined_transaction_by_hash(hash) {
2639 return Ok(tx);
2640 }
2641
2642 if let Some(fork) = self.get_fork() {
2643 return fork
2644 .transaction_by_hash(hash)
2645 .await
2646 .map_err(BlockchainError::AlloyForkProvider);
2647 }
2648
2649 Ok(None)
2650 }
2651
2652 pub fn mined_transaction_by_hash(&self, hash: B256) -> Option<AnyRpcTransaction> {
2653 let (info, block) = {
2654 let storage = self.blockchain.storage.read();
2655 let MinedTransaction { info, block_hash, .. } =
2656 storage.transactions.get(&hash)?.clone();
2657 let block = storage.blocks.get(&block_hash).cloned()?;
2658 (info, block)
2659 };
2660 let tx = block.body.transactions.get(info.transaction_index as usize)?.clone();
2661
2662 Some(transaction_build(
2663 Some(info.transaction_hash),
2664 tx,
2665 Some(&block),
2666 Some(info),
2667 block.header.base_fee_per_gas(),
2668 ))
2669 }
2670
2671 pub async fn trace_transaction(
2673 &self,
2674 hash: B256,
2675 ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
2676 if let Some(traces) = self.mined_parity_trace_transaction(hash) {
2677 return Ok(traces);
2678 }
2679
2680 if let Some(fork) = self.get_fork() {
2681 return Ok(fork.trace_transaction(hash).await?);
2682 }
2683
2684 Ok(vec![])
2685 }
2686
2687 pub async fn trace_get(
2689 &self,
2690 hash: B256,
2691 indices: Vec<Index>,
2692 ) -> Result<Option<LocalizedTransactionTrace>, BlockchainError> {
2693 if indices.len() != 1 {
2694 return Ok(None);
2695 }
2696
2697 let index: usize = indices[0].into();
2698 if let Some(traces) = self.mined_parity_trace_transaction(hash) {
2699 return Ok(traces.into_iter().nth(index));
2700 }
2701
2702 if let Some(fork) = self.get_fork() {
2703 return Ok(fork.trace_get(hash, indices).await?);
2704 }
2705
2706 Ok(None)
2707 }
2708
2709 pub async fn trace_block(
2711 &self,
2712 block: BlockNumber,
2713 ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
2714 let number = self.convert_block_number(Some(block));
2715 if let Some(traces) = self.mined_parity_trace_block(number) {
2716 return Ok(traces);
2717 }
2718
2719 if let Some(fork) = self.get_fork()
2720 && fork.predates_fork(number)
2721 {
2722 return Ok(fork.trace_block(number).await?);
2723 }
2724
2725 Ok(vec![])
2726 }
2727
2728 pub async fn trace_call(
2730 &self,
2731 request: WithOtherFields<TransactionRequest>,
2732 fee_details: FeeDetails,
2733 trace_types: HashSet<TraceType>,
2734 block_request: BlockRequest<FoundryTxEnvelope>,
2735 block_id: BlockId,
2736 ) -> Result<TraceResults, BlockchainError>
2737 where
2738 Self: TransactionValidator<FoundryTxEnvelope>,
2739 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
2740 {
2741 if let BlockRequest::Number(number) = &block_request
2742 && let Some(fork) = self.get_fork()
2743 && fork.predates_fork(*number)
2744 {
2745 return Ok(fork.trace_call(request, trace_types, block_id).await?);
2746 }
2747
2748 self.with_database_at(Some(block_request), |state, block| {
2749 let cache_db = CacheDB::new(state);
2750 let mut inspector =
2751 TracingInspector::new(TracingInspectorConfig::from_parity_config(&trace_types));
2752 let PreparedCall { evm_env, tx_env, .. } =
2753 self.prepare_call_env(&cache_db, request, fee_details, block)?;
2754 let result =
2755 self.transact_call_with_inspector_ref(&cache_db, &evm_env, &mut inspector, tx_env)?;
2756
2757 inspector
2758 .into_parity_builder()
2759 .into_trace_results_with_state(&result, &trace_types, &cache_db)
2760 .map_err(Into::into)
2761 })
2762 .await?
2763 }
2764
2765 pub async fn trace_replay_block_transactions(
2767 &self,
2768 block: BlockNumber,
2769 trace_types: HashSet<TraceType>,
2770 ) -> Result<Vec<TraceResultsWithTransactionHash>, BlockchainError> {
2771 let block_number = self.convert_block_number(Some(block));
2772
2773 if let Some(results) =
2775 self.mined_parity_trace_replay_block_transactions(block_number, &trace_types)?
2776 {
2777 return Ok(results);
2778 }
2779
2780 if let Some(fork) = self.get_fork()
2782 && fork.predates_fork(block_number)
2783 {
2784 return Ok(fork.trace_replay_block_transactions(block_number, trace_types).await?);
2785 }
2786
2787 Ok(vec![])
2788 }
2789
2790 pub async fn trace_replay_transaction(
2792 &self,
2793 hash: B256,
2794 trace_types: HashSet<TraceType>,
2795 ) -> Result<TraceResults, BlockchainError> {
2796 let block_number =
2797 self.blockchain.storage.read().transactions.get(&hash).map(|tx| tx.block_number);
2798
2799 if let Some(block_number) = block_number {
2803 let results = self
2804 .mined_parity_trace_replay_block_transactions(block_number, &trace_types)?
2805 .ok_or(BlockchainError::BlockNotFound)?;
2806
2807 return results
2808 .into_iter()
2809 .find(|result| result.transaction_hash == hash)
2810 .map(|result| result.full_trace)
2811 .ok_or_else(|| {
2812 BlockchainError::Internal(format!(
2813 "replayed block {block_number} for local transaction {hash:?}, \
2814 but its trace was missing"
2815 ))
2816 });
2817 }
2818
2819 if let Some(fork) = self.get_fork() {
2821 return Ok(fork.trace_replay_transaction(hash, trace_types).await?);
2822 }
2823
2824 Err(BlockchainError::TransactionNotFound)
2825 }
2826
2827 pub async fn trace_raw_transaction(
2829 &self,
2830 pending_transaction: PendingTransaction<FoundryTxEnvelope>,
2831 trace_types: HashSet<TraceType>,
2832 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
2833 ) -> Result<TraceResults, BlockchainError>
2834 where
2835 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
2836 {
2837 let trace_config = TracingInspectorConfig::from_parity_config(&trace_types);
2838
2839 self.with_database_at(block_request, |state, block_env| {
2840 let cache_db = CacheDB::new(state);
2841 let mut evm_env = self.evm_env.read().clone();
2842 evm_env.block_env = block_env;
2843
2844 let mut inspector = TracingInspector::new(trace_config);
2845 let (result, _) = self.transact_envelope_with_inspector_ref(
2846 &cache_db,
2847 &evm_env,
2848 &mut inspector,
2849 pending_transaction.transaction.as_ref(),
2850 *pending_transaction.sender(),
2851 )?;
2852
2853 inspector
2854 .into_parity_builder()
2855 .into_trace_results_with_state(&result, &trace_types, &cache_db)
2856 .map_err(BlockchainError::from)
2857 })
2858 .await?
2859 }
2860
2861 pub async fn trace_call_many(
2863 &self,
2864 calls: Vec<(WithOtherFields<TransactionRequest>, HashSet<TraceType>)>,
2865 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
2866 ) -> Result<Vec<TraceResults>, BlockchainError>
2867 where
2868 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
2869 {
2870 self.with_database_at(block_request, |state, block_env| {
2871 let mut cache_db = CacheDB::new(state);
2872 let mut results = Vec::with_capacity(calls.len());
2873 let mut calls = calls.into_iter().peekable();
2874
2875 while let Some((request, trace_types)) = calls.next() {
2876 let fee_details = FeeDetails::new(
2877 request.gas_price,
2878 request.max_fee_per_gas,
2879 request.max_priority_fee_per_gas,
2880 request.max_fee_per_blob_gas,
2881 )?
2882 .or_zero_fees();
2883 let PreparedCall { evm_env, mut tx_env, simulated_tempo_tx } =
2884 self.prepare_call_env(&cache_db, request, fee_details, block_env.clone())?;
2885 apply_tempo_envelope_identity(&mut tx_env, simulated_tempo_tx.as_ref());
2886
2887 let trace_config = TracingInspectorConfig::from_parity_config(&trace_types);
2888 let mut inspector = TracingInspector::new(trace_config);
2889 let result = self.transact_call_with_inspector_ref(
2890 &cache_db,
2891 &evm_env,
2892 &mut inspector,
2893 tx_env,
2894 )?;
2895
2896 let trace_result = inspector
2897 .into_parity_builder()
2898 .into_trace_results_with_state(&result, &trace_types, &cache_db)
2899 .map_err(BlockchainError::from)?;
2900 results.push(trace_result);
2901
2902 if calls.peek().is_some() {
2903 cache_db.commit(result.state);
2904 }
2905 }
2906
2907 Ok(results)
2908 })
2909 .await?
2910 }
2911
2912 fn mined_parity_trace_replay_block_transactions(
2914 &self,
2915 block_number: u64,
2916 trace_types: &HashSet<TraceType>,
2917 ) -> Result<Option<Vec<TraceResultsWithTransactionHash>>, BlockchainError> {
2918 let Some(block) = self.get_block(block_number) else { return Ok(None) };
2919
2920 let parent_hash = block.header.parent_hash;
2922 let trace_config = TracingInspectorConfig::from_parity_config(trace_types);
2923
2924 let read_guard = self.states.upgradable_read();
2925 if let Some(state) = read_guard.get_state(&parent_hash) {
2926 self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types)
2927 .map(Some)
2928 } else {
2929 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
2930 let Some(state) = write_guard.get_on_disk_state(&parent_hash) else {
2931 return Ok(None);
2932 };
2933 self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types)
2934 .map(Some)
2935 }
2936 }
2937
2938 fn replay_block_transactions_with_inspector(
2940 &self,
2941 block: &Block,
2942 parent_state: &StateDb,
2943 trace_config: TracingInspectorConfig,
2944 trace_types: &HashSet<TraceType>,
2945 ) -> Result<Vec<TraceResultsWithTransactionHash>, BlockchainError> {
2946 let (mut cache_db, evm_env) = self.prepare_block_replay(block, parent_state)?;
2947 let mut results = Vec::new();
2948
2949 for tx_envelope in &block.body.transactions {
2951 let tx_hash = tx_envelope.hash();
2952
2953 let mut inspector = TracingInspector::new(trace_config);
2955
2956 let pending_tx = PendingTransaction::from_maybe_impersonated(tx_envelope.clone())?;
2958 let (result, _) = self.transact_envelope_with_inspector_ref(
2959 &cache_db,
2960 &evm_env,
2961 &mut inspector,
2962 pending_tx.transaction.as_ref(),
2963 *pending_tx.sender(),
2964 )?;
2965
2966 let full_trace = inspector
2968 .into_parity_builder()
2969 .into_trace_results_with_state(&result, trace_types, &cache_db)
2970 .map_err(BlockchainError::from)?;
2971
2972 results.push(TraceResultsWithTransactionHash { transaction_hash: tx_hash, full_trace });
2973
2974 cache_db.commit(result.state);
2976 }
2977
2978 Ok(results)
2979 }
2980
2981 pub async fn trace_filter(
2983 &self,
2984 filter: TraceFilter,
2985 ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
2986 let matcher = filter.matcher();
2987 let start = filter.from_block.unwrap_or(0);
2988 let end = filter.to_block.unwrap_or_else(|| self.best_number());
2989
2990 if start > end {
2991 return Err(BlockchainError::RpcError(RpcError::invalid_params(
2992 "invalid block range, ensure that to block is greater than from block".to_string(),
2993 )));
2994 }
2995
2996 let dist = end - start;
2997 if dist > 300 {
2998 return Err(BlockchainError::RpcError(RpcError::invalid_params(
2999 "block range too large, currently limited to 300".to_string(),
3000 )));
3001 }
3002
3003 let mut trace_tasks = vec![];
3005 for num in start..=end {
3006 trace_tasks.push(self.trace_block(num.into()));
3007 }
3008
3009 let traces = futures::future::try_join_all(trace_tasks).await?;
3011 let filtered_traces =
3012 traces.into_iter().flatten().filter(|trace| matcher.matches(&trace.trace));
3013
3014 let filtered_traces: Vec<_> = if let Some(after) = filter.after {
3016 filtered_traces.skip(after as usize).collect()
3017 } else {
3018 filtered_traces.collect()
3019 };
3020
3021 let filtered_traces: Vec<_> = if let Some(count) = filter.count {
3022 filtered_traces.into_iter().take(count as usize).collect()
3023 } else {
3024 filtered_traces
3025 };
3026
3027 Ok(filtered_traces)
3028 }
3029
3030 pub fn get_blobs_by_block_id(
3031 &self,
3032 id: impl Into<BlockId>,
3033 versioned_hashes: Vec<B256>,
3034 ) -> Result<Option<Vec<alloy_consensus::Blob>>> {
3035 Ok(self.get_block(id).map(|block| {
3036 block
3037 .body
3038 .transactions
3039 .iter()
3040 .filter_map(|tx| tx.as_ref().sidecar())
3041 .flat_map(|sidecar| {
3042 sidecar.sidecar.blobs().iter().zip(sidecar.sidecar.commitments().iter())
3043 })
3044 .filter(|(_, commitment)| {
3045 versioned_hashes.is_empty()
3047 || versioned_hashes.contains(&kzg_to_versioned_hash(commitment.as_slice()))
3048 })
3049 .map(|(blob, _)| *blob)
3050 .collect()
3051 }))
3052 }
3053
3054 #[allow(clippy::large_stack_frames)]
3055 pub fn get_blob_by_versioned_hash(&self, hash: B256) -> Result<Option<Blob>> {
3056 let storage = self.blockchain.storage.read();
3057 for block in storage.blocks.values() {
3058 for tx in &block.body.transactions {
3059 let typed_tx = tx.as_ref();
3060 if let Some(sidecar) = typed_tx.sidecar() {
3061 for versioned_hash in sidecar.sidecar.versioned_hashes() {
3062 if versioned_hash == hash
3063 && let Some(index) =
3064 sidecar.sidecar.commitments().iter().position(|commitment| {
3065 kzg_to_versioned_hash(commitment.as_slice()) == *hash
3066 })
3067 && let Some(blob) = sidecar.sidecar.blobs().get(index)
3068 {
3069 return Ok(Some(*blob));
3070 }
3071 }
3072 }
3073 }
3074 }
3075 Ok(None)
3076 }
3077
3078 #[expect(clippy::too_many_arguments)]
3080 pub async fn with_genesis(
3081 db: Arc<AsyncRwLock<Box<dyn Db>>>,
3082 env: Arc<RwLock<EvmEnv>>,
3083 networks: NetworkConfigs,
3084 genesis: GenesisConfig,
3085 fees: FeeManager,
3086 fork: Arc<RwLock<Option<ClientFork>>>,
3087 enable_steps_tracing: bool,
3088 print_logs: bool,
3089 print_traces: bool,
3090 call_trace_decoder: Arc<CallTraceDecoder>,
3091 prune_state_history_config: PruneStateHistoryConfig,
3092 max_persisted_states: Option<usize>,
3093 transaction_block_keeper: Option<usize>,
3094 automine_block_time: Option<Duration>,
3095 cache_path: Option<PathBuf>,
3096 node_config: Arc<AsyncRwLock<NodeConfig>>,
3097 ) -> Result<Self> {
3098 let blockchain = if let Some(fork) = fork.read().as_ref() {
3100 trace!(target: "backend", "using forked blockchain at {}", fork.block_number());
3101 Blockchain::forked(fork.block_number(), fork.block_hash(), fork.total_difficulty())
3102 } else {
3103 Blockchain::new(
3104 &env.read(),
3105 fees.is_eip1559().then(|| fees.base_fee()),
3106 genesis.timestamp,
3107 genesis.number,
3108 networks.is_tempo(),
3109 )
3110 };
3111
3112 if fork.read().is_none() {
3115 env.write().block_env.number = U256::from(genesis.number);
3116
3117 if fees.tempo_hardfork().is_some() {
3121 let env = env.read();
3122 let next_base_fee = fees.get_next_block_base_fee_per_gas(
3123 0,
3124 env.block_env.gas_limit,
3125 env.block_env.basefee,
3126 );
3127 drop(env);
3128 fees.set_base_fee(next_base_fee);
3129 }
3130 }
3131
3132 let start_timestamp = if let Some(fork) = fork.read().as_ref() {
3133 fork.timestamp()
3134 } else {
3135 genesis.timestamp
3136 };
3137
3138 let mut states = if prune_state_history_config.is_config_enabled() {
3139 prune_state_history_config
3141 .max_memory_history
3142 .map(|limit| InMemoryBlockStates::new(limit, 0))
3143 .unwrap_or_default()
3144 .memory_only()
3145 } else if max_persisted_states.is_some() {
3146 max_persisted_states
3147 .map(|limit| InMemoryBlockStates::new(DEFAULT_HISTORY_LIMIT, limit))
3148 .unwrap_or_default()
3149 } else {
3150 Default::default()
3151 };
3152
3153 if let Some(cache_path) = cache_path {
3154 states = states.disk_path(cache_path);
3155 }
3156
3157 let (slots_in_an_epoch, precompile_factory, disable_pool_balance_checks, hardfork) = {
3158 let cfg = node_config.read().await;
3159 (
3160 cfg.slots_in_an_epoch,
3161 cfg.precompile_factory.clone(),
3162 cfg.disable_pool_balance_checks,
3163 cfg.get_hardfork(),
3164 )
3165 };
3166
3167 let backend = Self {
3168 db,
3169 blockchain,
3170 states: Arc::new(RwLock::new(states)),
3171 evm_env: env,
3172 networks,
3173 hardfork,
3174 fork,
3175 time: TimeManager::new(start_timestamp),
3176 cheats: Default::default(),
3177 new_block_listeners: Default::default(),
3178 fees,
3179 genesis,
3180 active_state_snapshots: Arc::new(Mutex::new(Default::default())),
3181 enable_steps_tracing,
3182 print_logs,
3183 print_traces,
3184 call_trace_decoder,
3185 prune_state_history_config,
3186 transaction_block_keeper,
3187 node_config,
3188 slots_in_an_epoch,
3189 precompile_factory,
3190 mining: Arc::new(tokio::sync::Mutex::new(())),
3191 disable_pool_balance_checks,
3192 };
3193
3194 if let Some(interval_block_time) = automine_block_time {
3195 backend.update_interval_mine_block_time(interval_block_time);
3196 }
3197
3198 backend.apply_genesis().await.wrap_err("failed to create genesis")?;
3200 Ok(backend)
3201 }
3202
3203 async fn apply_genesis(&self) -> Result<(), DatabaseError> {
3207 trace!(target: "backend", "setting genesis balances");
3208
3209 if self.fork.read().is_some() {
3210 let mut genesis_accounts_futures = Vec::with_capacity(self.genesis.accounts.len());
3212 for address in self.genesis.accounts.iter().copied() {
3213 let db = Arc::clone(&self.db);
3214
3215 genesis_accounts_futures.push(tokio::task::spawn(async move {
3218 let db = db.read().await;
3219 let info = db.basic_ref(address)?.unwrap_or_default();
3220 Ok::<_, DatabaseError>((address, info))
3221 }));
3222 }
3223
3224 let genesis_accounts = futures::future::join_all(genesis_accounts_futures).await;
3225
3226 let mut db = self.db.write().await;
3227
3228 for res in genesis_accounts {
3229 let (address, mut info) = res.unwrap()?;
3230 info.balance = self.genesis.balance;
3231 db.insert_account(address, info.clone());
3232 }
3233 } else {
3234 let mut db = self.db.write().await;
3235 for (account, info) in self.genesis.account_infos() {
3236 db.insert_account(account, info);
3237 }
3238
3239 db.insert_block_hash(U256::from(self.best_number()), self.best_hash());
3242
3243 if let Some(transitions) =
3244 self.ethereum_block_transitions(self.hardfork(), None, BlockExecutionKind::Complete)
3245 {
3246 if transitions.hardfork >= EthereumHardfork::Cancun {
3247 db.set_code(eip4788::BEACON_ROOTS_ADDRESS, eip4788::BEACON_ROOTS_CODE.clone())?;
3248 }
3249 if transitions.hardfork >= EthereumHardfork::Prague {
3250 db.set_code(
3251 eip2935::HISTORY_STORAGE_ADDRESS,
3252 eip2935::HISTORY_STORAGE_CODE.clone(),
3253 )?;
3254 db.set_code(
3255 eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
3256 eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_CODE.clone(),
3257 )?;
3258 db.set_code(
3259 eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
3260 eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_CODE.clone(),
3261 )?;
3262 }
3263 }
3264 }
3265
3266 let db = self.db.write().await;
3267 self.genesis.apply_genesis_json_alloc(db)?;
3269
3270 if self.networks.is_tempo() && !self.is_fork() {
3273 let chain_id = self.evm_env.read().cfg_env.chain_id;
3274 let timestamp = self.genesis.timestamp;
3275 let test_accounts: Vec<Address> = self.genesis.accounts.clone();
3276 let hardfork = self.tempo_hardfork();
3277 let mut db = self.db.write().await;
3278 crate::eth::backend::tempo::initialize_tempo_precompiles(
3279 &mut **db,
3280 chain_id,
3281 timestamp,
3282 &test_accounts,
3283 hardfork,
3284 )
3285 .map_err(|e| {
3286 tracing::error!(target: "backend", "failed to initialize Tempo precompiles: {e}");
3287 DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}")))
3288 })?;
3289 trace!(target: "backend", "initialized Tempo precompiles and fee tokens for {} accounts", test_accounts.len());
3290 }
3291
3292 trace!(target: "backend", "set genesis balances");
3293
3294 Ok(())
3295 }
3296
3297 pub async fn reset_fork(&self, forking: Forking) -> Result<(), BlockchainError> {
3299 if !self.is_fork() {
3300 if let Some(eth_rpc_url) = forking.json_rpc_url.clone() {
3301 let mut evm_env = self.evm_env.read().clone();
3302 let mut node_config = self.node_config.read().await.clone();
3303
3304 node_config.base_fee.take();
3307 node_config.fork_urls = vec![eth_rpc_url.clone()];
3308 node_config.apply_tempo_fork_beneficiary_default(&mut evm_env);
3309
3310 let (db, config) =
3311 node_config.setup_fork_db_config(eth_rpc_url, &mut evm_env, &self.fees).await?;
3312
3313 *self.db.write().await = Box::new(db);
3314
3315 let fork = ClientFork::new(config, Arc::clone(&self.db));
3316
3317 *self.node_config.write().await = node_config;
3318 *self.evm_env.write() = evm_env;
3319 *self.fork.write() = Some(fork);
3320 } else {
3321 return Err(RpcError::invalid_params(
3322 "Forking not enabled and RPC URL not provided to start forking",
3323 )
3324 .into());
3325 }
3326 }
3327
3328 if let Some(fork) = self.get_fork() {
3329 let block_number =
3330 forking.block_number.map(BlockNumber::from).unwrap_or(BlockNumber::Latest);
3331 let reset_urls =
3333 forking.json_rpc_url.as_ref().map(|url| vec![url.clone()]).unwrap_or_default();
3334 let target_rpc_url = forking.json_rpc_url.clone().or_else(|| fork.eth_rpc_url());
3335 let rpc_url_changed = target_rpc_url != fork.database_rpc_url();
3336 fork.prepare_reset(reset_urls, block_number.into()).await?;
3337 if rpc_url_changed {
3338 fork.database.write().await.clear_into_state_snapshot();
3340 }
3341 fork.database.read().await.maybe_flush_cache().map_err(BlockchainError::Internal)?;
3344 let fork_block_number = fork.block_number();
3345 if rpc_url_changed {
3346 let cache_dir = {
3347 let config = self.node_config.read().await;
3348 if config.no_storage_caching || config.fork_urls.is_empty() {
3349 None
3350 } else {
3351 foundry_config::Config::foundry_chain_cache_dir(config.get_chain_id())
3352 }
3353 };
3354 if let Some(cache_dir) = cache_dir
3355 && let Err(err) = std::fs::remove_dir_all(&cache_dir)
3356 && err.kind() != std::io::ErrorKind::NotFound
3357 {
3358 return Err(BlockchainError::Internal(format!(
3359 "failed to invalidate fork cache at {}: {err}",
3360 cache_dir.display()
3361 )));
3362 }
3363 }
3364 let fork_block = fork
3365 .block_by_number(fork_block_number)
3366 .await?
3367 .ok_or(BlockchainError::BlockNotFound)?;
3368 {
3370 if let Some(fork_url) = forking.json_rpc_url {
3371 self.reset_block_number(fork_url, fork_block_number, true).await?;
3372 } else {
3373 if let Some(fork_url) = target_rpc_url.clone() {
3376 self.reset_block_number(fork_url, fork_block_number, false).await?;
3377 }
3378
3379 let gas_limit = self.node_config.read().await.fork_gas_limit(&fork_block);
3380 let mut env = self.evm_env.write();
3381
3382 env.cfg_env.chain_id = fork.chain_id();
3383 env.block_env = BlockEnv {
3384 number: U256::from(fork_block_number),
3385 timestamp: U256::from(fork_block.header.timestamp()),
3386 gas_limit,
3387 difficulty: fork_block.header.difficulty(),
3388 prevrandao: Some(fork_block.header.mix_hash().unwrap_or_default()),
3389 beneficiary: env.block_env.beneficiary,
3391 basefee: env.block_env.basefee,
3392 ..env.block_env.clone()
3393 };
3394
3395 let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
3398 fork_block.header.gas_used(),
3399 gas_limit,
3400 fork_block.header.base_fee_per_gas().unwrap_or_default(),
3401 );
3402
3403 self.fees.set_base_fee(next_block_base_fee);
3404 }
3405
3406 self.time.reset(fork_block.header.timestamp());
3408 self.cheats.clear_next_block_prevrandao();
3410
3411 self.blockchain.storage.write().total_difficulty = fork.total_difficulty();
3413 }
3414 *self.blockchain.storage.write() = BlockchainStorage::forked(
3416 fork.block_number(),
3417 fork.block_hash(),
3418 fork.total_difficulty(),
3419 );
3420 self.states.write().clear();
3421 self.apply_genesis().await?;
3422 fork.set_database_rpc_url(target_rpc_url);
3423
3424 trace!(target: "backend", "reset fork");
3425
3426 Ok(())
3427 } else {
3428 Err(RpcError::invalid_params("Forking not enabled").into())
3429 }
3430 }
3431
3432 pub async fn reset_to_in_mem(&self) -> Result<(), BlockchainError> {
3434 *self.fork.write() = None;
3436
3437 let genesis_timestamp = self.genesis.timestamp;
3438 let genesis_number = self.genesis.number;
3439
3440 let reset_base_fee = self.fees.tempo_hardfork().map(crate::config::tempo_default_base_fee);
3444 let genesis_base_fee = reset_base_fee.unwrap_or_else(|| self.fees.base_fee());
3445
3446 {
3448 let mut env = self.evm_env.write();
3449 env.block_env.number = U256::from(genesis_number);
3450 env.block_env.timestamp = U256::from(genesis_timestamp);
3451 env.block_env.basefee = genesis_base_fee;
3453 env.block_env.prevrandao = Some(B256::ZERO);
3454 }
3455
3456 let base_fee = self.fees.is_eip1559().then_some(genesis_base_fee);
3458 *self.blockchain.storage.write() = BlockchainStorage::new(
3459 &self.evm_env.read(),
3460 base_fee,
3461 genesis_timestamp,
3462 genesis_number,
3463 self.is_tempo(),
3464 );
3465 self.states.write().clear();
3466
3467 self.db.write().await.clear();
3469
3470 self.time.reset(genesis_timestamp);
3472 self.cheats.clear_next_block_prevrandao();
3474
3475 if self.fees.is_eip1559() {
3479 let next_base_fee = match reset_base_fee {
3480 Some(genesis_base_fee) => {
3481 self.fees.set_base_fee(genesis_base_fee);
3484 let gas_limit = self.evm_env.read().block_env.gas_limit;
3485 self.fees.get_next_block_base_fee_per_gas(0, gas_limit, genesis_base_fee)
3486 }
3487 None => crate::eth::fees::INITIAL_BASE_FEE,
3488 };
3489 self.fees.set_base_fee(next_base_fee);
3490 }
3491
3492 self.fees.set_gas_price(crate::eth::fees::INITIAL_GAS_PRICE);
3493
3494 self.apply_genesis().await?;
3496
3497 trace!(target: "backend", "reset to fresh in-memory state");
3498
3499 Ok(())
3500 }
3501
3502 async fn reset_block_number(
3503 &self,
3504 fork_url: String,
3505 fork_block_number: u64,
3506 validated_url: bool,
3507 ) -> Result<(), BlockchainError> {
3508 let mut node_config = self.node_config.read().await.clone();
3509 node_config.fork_choice = Some(ForkChoice::Block(fork_block_number as i128));
3510 let override_chain_id =
3511 self.get_fork().and_then(|fork| fork.config.read().override_chain_id);
3512 node_config.set_chain_id(override_chain_id);
3513 if validated_url {
3514 node_config.fork_chain_id = None;
3515 }
3516 node_config.fork_urls = vec![fork_url.clone()];
3518
3519 let mut evm_env = self.evm_env.read().clone();
3520 let (forked_db, client_fork_config) =
3521 node_config.setup_fork_db_config(fork_url, &mut evm_env, &self.fees).await?;
3522
3523 *self.db.write().await = Box::new(forked_db);
3524 let fork = ClientFork::new(client_fork_config, Arc::clone(&self.db));
3525 *self.node_config.write().await = node_config;
3526 *self.fork.write() = Some(fork);
3527 *self.evm_env.write() = evm_env;
3528
3529 Ok(())
3530 }
3531
3532 pub async fn revert_state_snapshot(&self, id: U256) -> Result<bool, BlockchainError> {
3534 let block = { self.active_state_snapshots.lock().remove(&id) };
3535 if let Some((num, hash)) = block {
3536 let best_block_hash = {
3537 let current_height = self.best_number();
3539 let mut storage = self.blockchain.storage.write();
3540
3541 for n in ((num + 1)..=current_height).rev() {
3542 trace!(target: "backend", "reverting block {}", n);
3543 if let Some(hash) = storage.hashes.remove(&n)
3544 && let Some(block) = storage.blocks.remove(&hash)
3545 {
3546 for tx in block.body.transactions {
3547 let _ = storage.transactions.remove(&tx.hash());
3548 }
3549 }
3550 }
3551
3552 storage.best_number = num;
3553 storage.best_hash = hash;
3554 hash
3555 };
3556 let block =
3557 self.block_by_hash(best_block_hash).await?.ok_or(BlockchainError::BlockNotFound)?;
3558
3559 let reset_time = block.header.timestamp();
3560 self.time.reset(reset_time);
3561 self.cheats.clear_next_block_prevrandao();
3563
3564 let mut env = self.evm_env.write();
3565 env.block_env = BlockEnv {
3566 number: U256::from(num),
3567 timestamp: U256::from(block.header.timestamp()),
3568 difficulty: block.header.difficulty(),
3569 prevrandao: Some(block.header.mix_hash().unwrap_or_default()),
3571 gas_limit: block.header.gas_limit(),
3572 beneficiary: env.block_env.beneficiary,
3574 basefee: env.block_env.basefee,
3575 ..Default::default()
3576 }
3577 }
3578 Ok(self.db.write().await.revert_state(id, RevertStateSnapshotAction::RevertRemove))
3579 }
3580
3581 pub async fn inspect_tx(
3583 &self,
3584 tx: Arc<PoolTransaction<FoundryTxEnvelope>>,
3585 ) -> Result<
3586 (InstructionResult, Option<Output>, u64, State, Vec<revm::primitives::Log>),
3587 BlockchainError,
3588 > {
3589 let evm_env = self.next_evm_env();
3590 let db = self.db.read().await;
3591 let mut inspector = self.build_inspector();
3592 let (ResultAndState { result, state }, _) = self.transact_envelope_with_inspector_ref(
3593 &**db,
3594 &evm_env,
3595 &mut inspector,
3596 tx.pending_transaction.transaction.as_ref(),
3597 *tx.pending_transaction.sender(),
3598 )?;
3599 let (exit_reason, gas_used, out, logs) = unpack_execution_result(result);
3600
3601 inspector.print_logs();
3602
3603 if self.print_traces {
3604 inspector.print_traces(self.call_trace_decoder.clone());
3605 }
3606
3607 Ok((exit_reason, out, gas_used, state, logs))
3608 }
3609}
3610
3611impl<N: Network> Backend<N>
3612where
3613 N::ReceiptEnvelope: TxReceipt<Log = alloy_primitives::Log>,
3614{
3615 fn mined_logs_for_block(&self, filter: Filter, block: Block, block_hash: B256) -> Vec<Log> {
3617 let mut all_logs = Vec::new();
3618 let mut block_log_index = 0u32;
3619
3620 let storage = self.blockchain.storage.read();
3621
3622 for tx in block.body.transactions {
3623 let Some(tx) = storage.transactions.get(&tx.hash()) else {
3624 continue;
3625 };
3626
3627 let logs = tx.receipt.logs();
3628 let transaction_hash = tx.info.transaction_hash;
3629
3630 for log in logs {
3631 if filter.matches(log) {
3632 all_logs.push(Log {
3633 inner: log.clone(),
3634 block_hash: Some(block_hash),
3635 block_number: Some(block.header.number()),
3636 block_timestamp: Some(block.header.timestamp()),
3637 transaction_hash: Some(transaction_hash),
3638 transaction_index: Some(tx.info.transaction_index),
3639 log_index: Some(block_log_index as u64),
3640 removed: false,
3641 });
3642 }
3643 block_log_index += 1;
3644 }
3645 }
3646 all_logs
3647 }
3648
3649 fn removed_logs_since(&self, block_number: u64) -> Vec<Log> {
3656 let storage = self.blockchain.storage.read();
3657 let mut all_logs = Vec::new();
3658
3659 for num in (block_number + 1)..=storage.best_number {
3660 if let Some(hash) = storage.hashes.get(&num)
3661 && let Some(block) = storage.blocks.get(hash)
3662 {
3663 let mut block_log_index = 0u64;
3664 for tx in &block.body.transactions {
3665 if let Some(tx) = storage.transactions.get(&tx.hash()) {
3666 for log in tx.receipt.logs() {
3667 all_logs.push(Log {
3668 inner: log.clone(),
3669 block_hash: Some(*hash),
3670 block_number: Some(num),
3671 block_timestamp: Some(block.header.timestamp()),
3672 transaction_hash: Some(tx.info.transaction_hash),
3673 transaction_index: Some(tx.info.transaction_index),
3674 log_index: Some(block_log_index),
3675 removed: true,
3676 });
3677 block_log_index += 1;
3678 }
3679 }
3680 }
3681 }
3682 }
3683
3684 all_logs
3685 }
3686
3687 async fn logs_for_block(
3689 &self,
3690 filter: Filter,
3691 hash: B256,
3692 ) -> Result<Vec<Log>, BlockchainError> {
3693 if let Some(block) = self.blockchain.get_block_by_hash(&hash) {
3694 return Ok(self.mined_logs_for_block(filter, block, hash));
3695 }
3696
3697 if let Some(fork) = self.get_fork() {
3698 return Ok(fork.logs(&filter).await?);
3699 }
3700
3701 Err(BlockchainError::UnknownBlock)
3702 }
3703
3704 async fn logs_for_range(
3706 &self,
3707 filter: &Filter,
3708 mut from: u64,
3709 to: u64,
3710 ) -> Result<Vec<Log>, BlockchainError> {
3711 let mut all_logs = Vec::new();
3712
3713 if let Some(fork) = self.get_fork() {
3715 let to_on_fork = if fork.predates_fork(to) {
3716 to
3717 } else {
3718 fork.block_number()
3720 };
3721
3722 if fork.predates_fork_inclusive(from) {
3723 let filter = filter.clone().from_block(from).to_block(to_on_fork);
3725 all_logs = fork.logs(&filter).await?;
3726
3727 from = fork.block_number() + 1;
3729 }
3730 }
3731
3732 for number in from..=to {
3733 if let Some((block, hash)) = self.get_block_with_hash(number) {
3734 all_logs.extend(self.mined_logs_for_block(filter.clone(), block, hash));
3735 }
3736 }
3737
3738 Ok(all_logs)
3739 }
3740
3741 pub async fn logs(&self, filter: Filter) -> Result<Vec<Log>, BlockchainError> {
3743 trace!(target: "backend", "get logs [{:?}]", filter);
3744 if let Some(hash) = filter.get_block_hash() {
3745 self.logs_for_block(filter, hash).await
3746 } else {
3747 let best = self.best_number();
3748 let to_block =
3749 self.convert_block_number(filter.block_option.get_to_block().copied()).min(best);
3750 let from_block =
3751 self.convert_block_number(filter.block_option.get_from_block().copied());
3752 if from_block > best {
3753 return Err(BlockchainError::BlockOutOfRange(best, from_block));
3754 }
3755
3756 self.logs_for_range(&filter, from_block, to_block).await
3757 }
3758 }
3759
3760 pub fn mined_receipts(&self, hash: B256) -> Option<Vec<N::ReceiptEnvelope>> {
3762 let storage = self.blockchain.storage.read();
3763 let block = storage.blocks.get(&hash)?;
3764 block
3765 .body
3766 .transactions
3767 .iter()
3768 .map(|transaction| {
3769 storage.transactions.get(&transaction.hash()).map(|tx| tx.receipt.clone())
3770 })
3771 .collect()
3772 }
3773}
3774
3775impl<N: Network> Backend<N>
3777where
3778 Self: TransactionValidator<FoundryTxEnvelope>,
3779 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
3780{
3781 pub async fn mine_block(
3786 &self,
3787 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
3788 ) -> Result<MinedBlockOutcome<FoundryTxEnvelope>, BlockchainError> {
3789 self.do_mine_block(pool_transactions).await
3790 }
3791
3792 pub(crate) async fn apply_fork_transaction_replay(
3794 &self,
3795 replay: ForkTransactionReplay,
3796 ) -> Result<()> {
3797 let PreparedForkTransactionReplay { transactions, timestamp, parent_beacon_block_root } =
3798 prepare_fork_transaction_replay(replay)?;
3799 eyre::ensure!(!transactions.is_empty(), "fork transaction replay prefix is empty");
3800 let next_timestamp = timestamp.checked_add(1).ok_or_else(|| {
3801 eyre::eyre!("fork transaction replay timestamp cannot be incremented")
3802 })?;
3803
3804 let _mining_guard = self.mining.lock().await;
3805 let current_base_fee = self.base_fee();
3806 let current_excess_blob_gas_and_price = self.excess_blob_gas_and_price();
3807 let mut evm_env = self.evm_env.read().clone();
3808 if evm_env.block_env.basefee == 0 {
3809 evm_env.cfg_env.disable_base_fee = true;
3810 }
3811
3812 let block_number = self.blockchain.storage.read().best_number.saturating_add(1);
3813 if is_arbitrum(evm_env.cfg_env.chain_id) {
3814 evm_env.block_env.number = U256::from(block_number);
3815 } else {
3816 evm_env.block_env.number = evm_env.block_env.number.saturating_add(U256::from(1));
3817 }
3818 evm_env.block_env.basefee = current_base_fee;
3819 evm_env.block_env.blob_excess_gas_and_price = current_excess_blob_gas_and_price;
3820 evm_env.block_env.timestamp = U256::from(timestamp);
3821
3822 let best_hash = self.blockchain.storage.read().best_hash;
3823 let mut prevrandao_input = [0u8; 40];
3824 prevrandao_input[..32].copy_from_slice(best_hash.as_slice());
3825 prevrandao_input[32..].copy_from_slice(&block_number.to_le_bytes());
3826 evm_env.block_env.prevrandao = Some(
3827 self.cheats.take_next_block_prevrandao().unwrap_or_else(|| keccak256(prevrandao_input)),
3828 );
3829
3830 let mut replay_env = evm_env.clone();
3831 apply_chain_specific_tx_replay_env_changes(&mut replay_env);
3832 let inspector_tx_config = self.inspector_tx_config();
3833
3834 let hardfork =
3837 FoundryHardfork::from_chain_and_timestamp(evm_env.cfg_env.chain_id, timestamp)
3838 .unwrap_or_else(|| self.hardfork());
3839 if !self.is_optimism() && !self.is_tempo() {
3840 replay_env.cfg_env.spec = SpecId::from(hardfork);
3841 if replay_env.cfg_env.spec >= SpecId::CANCUN
3843 && replay_env.block_env.blob_excess_gas_and_price.is_none()
3844 {
3845 replay_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new(
3846 0,
3847 get_blob_base_fee_update_fraction_by_spec_id(replay_env.cfg_env.spec),
3848 ));
3849 }
3850 }
3851
3852 let (block_info, state_changes, block_hash) = {
3853 let db = self.db.read().await;
3854 let mut overlay = AnvilCacheDB::new(&**db);
3855 let ExecutedHistoricalReplay {
3856 block_result,
3857 transactions,
3858 transaction_infos,
3859 state_changes,
3860 } = self.execute_with_replay_block_executor(
3861 &mut overlay,
3862 &replay_env,
3863 best_hash,
3864 hardfork,
3865 parent_beacon_block_root,
3866 &transactions,
3867 &inspector_tx_config,
3868 )?;
3869 let state_root = overlay.maybe_state_root().unwrap_or_default();
3870 let block_info = self.build_block_info(
3871 &replay_env,
3872 best_hash,
3873 block_number,
3874 state_root,
3875 block_result,
3876 transactions,
3877 transaction_infos,
3878 parent_beacon_block_root,
3879 );
3880 let block_hash = block_info.block.header.hash_slow();
3881 (block_info, state_changes, block_hash)
3882 };
3883
3884 if self.prune_state_history_config.is_state_history_supported() {
3885 let state = self.db.read().await.current_state();
3886 self.states.write().insert(best_hash, state);
3887 }
3888
3889 {
3890 let mut db = self.db.write().await;
3891 for state in state_changes {
3892 db.commit(state);
3893 }
3894 db.insert_block_hash(U256::from(block_number), block_hash);
3895 }
3896
3897 let BlockInfo { block, transactions, receipts } = block_info;
3898 let header = block.header.clone();
3899 {
3900 let mut storage = self.blockchain.storage.write();
3901 storage.best_number = block_number;
3902 storage.best_hash = block_hash;
3903 if !self.is_eip3675() {
3904 storage.total_difficulty =
3905 storage.total_difficulty.saturating_add(header.difficulty);
3906 }
3907 storage.blocks.insert(block_hash, block);
3908 storage.hashes.insert(block_number, block_hash);
3909 for (info, receipt) in transactions.into_iter().zip(receipts) {
3910 let mined_tx = MinedTransaction { info, receipt, block_hash, block_number };
3911 storage.transactions.insert(mined_tx.info.transaction_hash, mined_tx);
3912 }
3913
3914 if let Some(transaction_block_keeper) = self.transaction_block_keeper
3915 && storage.blocks.len() > transaction_block_keeper
3916 {
3917 let to_clear = block_number
3918 .saturating_sub(transaction_block_keeper.try_into().unwrap_or(u64::MAX));
3919 storage.remove_block_transactions_by_number(to_clear)
3920 }
3921 }
3922
3923 evm_env.block_env.difficulty = U256::ZERO;
3924 *self.evm_env.write() = evm_env;
3925 self.time.reset(timestamp);
3926 self.time.set_next_block_timestamp(next_timestamp)?;
3927
3928 let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
3929 header.gas_used,
3930 header.gas_limit,
3931 header.base_fee_per_gas.unwrap_or_default(),
3932 );
3933 let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas(
3934 header.excess_blob_gas.unwrap_or_default(),
3935 header.blob_gas_used.unwrap_or_default(),
3936 );
3937 self.fees.set_base_fee(next_block_base_fee);
3938 self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
3939 next_block_excess_blob_gas,
3940 get_blob_base_fee_update_fraction_by_spec_id(*self.evm_env.read().spec_id()),
3941 ));
3942 self.notify_on_new_block(header.into_inner(), block_hash);
3943
3944 Ok(())
3945 }
3946
3947 #[allow(clippy::too_many_arguments)]
3948 fn execute_with_replay_block_executor<DB>(
3949 &self,
3950 db: DB,
3951 evm_env: &EvmEnv,
3952 parent_hash: B256,
3953 hardfork: FoundryHardfork,
3954 parent_beacon_block_root: Option<B256>,
3955 transactions: &[HistoricalReplayTransaction],
3956 inspector_tx_config: &InspectorTxConfig,
3957 ) -> Result<ExecutedHistoricalReplay>
3958 where
3959 DB: StateDB<Error = DatabaseError>,
3960 {
3961 let inspector = self.build_mining_inspector();
3962 let ethereum_transitions = self.ethereum_block_transitions(
3963 hardfork,
3964 parent_beacon_block_root,
3965 BlockExecutionKind::TransactionPrefix,
3966 );
3967
3968 macro_rules! run {
3969 ($evm:expr) => {{
3970 self.inject_precompiles($evm.precompiles_mut(), evm_env);
3971 self.inject_arbitrum_precompile($evm.precompiles_mut(), evm_env);
3972 let mut executor = AnvilBlockExecutor::new(
3973 $evm,
3974 parent_hash,
3975 *evm_env.spec_id(),
3976 ethereum_transitions,
3977 )
3978 .with_state_changes();
3979 executor
3980 .apply_pre_execution_changes()
3981 .wrap_err("failed to apply replay block-start transitions")?;
3982 let (stored_transactions, transaction_infos) =
3983 execute_historical_replay(&mut executor, transactions, inspector_tx_config)?;
3984 let state_changes = executor.take_state_changes();
3985 let (evm, block_result) =
3986 executor.finish().wrap_err("failed to finish replay block execution")?;
3987 drop(evm);
3988 Ok(ExecutedHistoricalReplay {
3989 block_result,
3990 transactions: stored_transactions,
3991 transaction_infos,
3992 state_changes,
3993 })
3994 }};
3995 }
3996
3997 #[cfg(feature = "optimism")]
3998 if self.is_optimism() {
3999 let op_env = EvmEnv::new(
4000 evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(self.hardfork.into()),
4001 evm_env.block_env.clone(),
4002 );
4003 let mut evm =
4004 OpEvmFactory::<OpTx>::default().create_evm_with_inspector(db, op_env, inspector);
4005 return run!(evm);
4006 }
4007
4008 if self.is_tempo() {
4009 let tempo_env = self.build_tempo_evm_env(evm_env);
4010 let mut evm =
4011 TempoEvmFactory::default().create_evm_with_inspector(db, tempo_env, inspector);
4012 run!(evm)
4013 } else {
4014 let mut evm =
4015 EthEvmFactory::default().create_evm_with_inspector(db, evm_env.clone(), inspector);
4016 run!(evm)
4017 }
4018 }
4019
4020 #[allow(clippy::too_many_arguments)]
4022 fn build_block_info(
4023 &self,
4024 evm_env: &EvmEnv,
4025 parent_hash: B256,
4026 number: u64,
4027 state_root: B256,
4028 block_result: BlockExecutionResult<FoundryReceiptEnvelope>,
4029 transactions: Vec<MaybeImpersonatedTransaction<FoundryTxEnvelope>>,
4030 transaction_infos: Vec<TransactionInfo>,
4031 parent_beacon_block_root: Option<B256>,
4032 ) -> BlockInfo<N> {
4033 let spec_id = *evm_env.spec_id();
4034 let is_shanghai = spec_id >= SpecId::SHANGHAI;
4035 let is_cancun = spec_id >= SpecId::CANCUN;
4036 let is_prague = spec_id >= SpecId::PRAGUE;
4037
4038 let receipts_root = calculate_receipt_root(&block_result.receipts);
4039 let cumulative_blob_gas_used = is_cancun.then_some(block_result.blob_gas_used);
4040 let bloom = block_result.receipts.iter().fold(Bloom::default(), |mut b, r| {
4041 b.accrue_bloom(r.logs_bloom());
4042 b
4043 });
4044
4045 let header = Header {
4046 parent_hash,
4047 ommers_hash: Default::default(),
4048 beneficiary: evm_env.block_env.beneficiary,
4049 state_root,
4050 transactions_root: Default::default(),
4051 receipts_root,
4052 logs_bloom: bloom,
4053 difficulty: evm_env.block_env.difficulty,
4054 number,
4055 gas_limit: evm_env.block_env.gas_limit,
4056 gas_used: block_result.gas_used,
4057 timestamp: evm_env.block_env.timestamp.saturating_to(),
4058 extra_data: Default::default(),
4059 mix_hash: evm_env.block_env.prevrandao.unwrap_or_default(),
4060 nonce: Default::default(),
4061 base_fee_per_gas: (spec_id >= SpecId::LONDON).then_some(evm_env.block_env.basefee),
4062 parent_beacon_block_root: is_cancun
4063 .then(|| parent_beacon_block_root.unwrap_or_default()),
4064 blob_gas_used: cumulative_blob_gas_used,
4065 excess_blob_gas: if is_cancun { evm_env.block_env.blob_excess_gas() } else { None },
4066 withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
4067 requests_hash: is_prague.then(|| block_result.requests.requests_hash()),
4068 block_access_list_hash: None,
4069 slot_number: None,
4070 };
4071
4072 let block = create_block(FoundryHeader::new(header, self.is_tempo()), transactions);
4073 BlockInfo { block, transactions: transaction_infos, receipts: block_result.receipts }
4074 }
4075
4076 async fn do_mine_block(
4077 &self,
4078 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
4079 ) -> Result<MinedBlockOutcome<FoundryTxEnvelope>, BlockchainError> {
4080 let _mining_guard = self.mining.lock().await;
4081 trace!(target: "backend", "creating new block with {} transactions", pool_transactions.len());
4082
4083 let (outcome, header, block_hash) = {
4084 let current_base_fee = self.base_fee();
4085 let current_excess_blob_gas_and_price = self.excess_blob_gas_and_price();
4086
4087 let mut evm_env = self.evm_env.read().clone();
4088
4089 if evm_env.block_env.basefee == 0 {
4090 evm_env.cfg_env.disable_base_fee = true;
4093 }
4094
4095 let block_number = self.blockchain.storage.read().best_number.saturating_add(1);
4096
4097 if is_arbitrum(evm_env.cfg_env.chain_id) {
4099 evm_env.block_env.number = U256::from(block_number);
4101 } else {
4102 evm_env.block_env.number = evm_env.block_env.number.saturating_add(U256::from(1));
4103 }
4104
4105 evm_env.block_env.basefee = current_base_fee;
4106 evm_env.block_env.blob_excess_gas_and_price = current_excess_blob_gas_and_price;
4107
4108 let best_hash = self.blockchain.storage.read().best_hash;
4109
4110 let mut input = [0u8; 40];
4111 input[..32].copy_from_slice(best_hash.as_slice());
4112 input[32..].copy_from_slice(&block_number.to_le_bytes());
4113 let next_prevrandao = self.cheats.prepare_next_block_prevrandao();
4117 evm_env.block_env.prevrandao =
4118 Some(next_prevrandao.map_or_else(|| keccak256(input), |pending| pending.value));
4119
4120 let (block_info, included, invalid, not_yet_valid, block_hash, parent_state) = {
4121 let mut db = self.db.write().await;
4122
4123 let pending_timestamp = self.time.prepare_next_timestamp();
4127 evm_env.block_env.timestamp = U256::from(pending_timestamp.timestamp);
4128
4129 let mut mining_evm_env = evm_env.clone();
4132 if pool_transactions.iter().any(|tx| tx.is_replay) {
4133 apply_chain_specific_tx_replay_env_changes(&mut mining_evm_env);
4134 }
4135
4136 let spec_id = *mining_evm_env.spec_id();
4137
4138 let inspector_tx_config = self.inspector_tx_config();
4139 let gas_config = self.pool_tx_gas_config(&mining_evm_env);
4140
4141 let mut candidate_db = AnvilCacheDB::new(&**db);
4142 let (pool_result, block_result) = self.execute_with_block_executor(
4143 &mut candidate_db,
4144 &mining_evm_env,
4145 best_hash,
4146 spec_id,
4147 Some(B256::ZERO),
4148 BlockExecutionKind::Complete,
4149 &pool_transactions,
4150 &gas_config,
4151 &inspector_tx_config,
4152 &|pool_tx, account| {
4153 let validation_env =
4154 if pool_tx.is_replay { &mining_evm_env } else { &evm_env };
4155 self.validate_pool_transaction_for(
4156 &pool_tx.pending_transaction,
4157 account,
4158 validation_env,
4159 )
4160 },
4161 )?;
4162
4163 let included = pool_result.included;
4164 let invalid = pool_result.invalid;
4165 let not_yet_valid = pool_result.not_yet_valid;
4166
4167 let CacheDB { cache, db: _ } = candidate_db.0;
4168 let parent_state = self
4169 .prune_state_history_config
4170 .is_state_history_supported()
4171 .then(|| db.current_state());
4172 commit_cache(&mut **db, cache)?;
4173 let state_root = db.maybe_state_root().unwrap_or_default();
4174 let block_info = self.build_block_info(
4175 &mining_evm_env,
4176 best_hash,
4177 block_number,
4178 state_root,
4179 block_result,
4180 pool_result.txs,
4181 pool_result.tx_info,
4182 Some(B256::ZERO),
4183 );
4184
4185 let block_hash = block_info.block.header.hash_slow();
4187 db.insert_block_hash(U256::from(block_info.block.header.number()), block_hash);
4188 self.time.commit_next_timestamp(pending_timestamp);
4189 if let Some(pending) = next_prevrandao {
4190 self.cheats.consume_next_block_prevrandao(pending);
4191 }
4192
4193 (block_info, included, invalid, not_yet_valid, block_hash, parent_state)
4194 };
4195
4196 let BlockInfo { block, transactions, receipts } = block_info;
4198
4199 let header = block.header.clone();
4200
4201 if let Some(parent_state) = parent_state {
4202 self.states.write().insert(best_hash, parent_state);
4203 }
4204
4205 trace!(
4206 target: "backend",
4207 "Mined block {} with {} tx {:?}",
4208 block_number,
4209 transactions.len(),
4210 transactions.iter().map(|tx| tx.transaction_hash).collect::<Vec<_>>()
4211 );
4212 let mut storage = self.blockchain.storage.write();
4213 storage.best_number = block_number;
4215 storage.best_hash = block_hash;
4216 if !self.is_eip3675() {
4219 storage.total_difficulty =
4220 storage.total_difficulty.saturating_add(header.difficulty);
4221 }
4222
4223 storage.blocks.insert(block_hash, block);
4224 storage.hashes.insert(block_number, block_hash);
4225
4226 node_info!("");
4227 for (info, receipt) in transactions.into_iter().zip(receipts) {
4229 node_info!(" Transaction: {:?}", info.transaction_hash);
4231 if let Some(contract) = &info.contract_address {
4232 node_info!(" Contract created: {contract}");
4233 }
4234 node_info!(" Gas used: {}", info.gas_used);
4235 if !info.exit.is_ok() {
4236 let r = RevertDecoder::new().decode(
4237 info.out.as_ref().map(|b| &b[..]).unwrap_or_default(),
4238 Some(info.exit),
4239 );
4240 node_info!(" Error: reverted with: {r}");
4241 }
4242 node_info!("");
4243
4244 let mined_tx = MinedTransaction { info, receipt, block_hash, block_number };
4245 storage.transactions.insert(mined_tx.info.transaction_hash, mined_tx);
4246 }
4247
4248 if let Some(transaction_block_keeper) = self.transaction_block_keeper
4250 && storage.blocks.len() > transaction_block_keeper
4251 {
4252 let to_clear = block_number
4253 .saturating_sub(transaction_block_keeper.try_into().unwrap_or(u64::MAX));
4254 storage.remove_block_transactions_by_number(to_clear)
4255 }
4256
4257 evm_env.block_env.difficulty = U256::from(0);
4259
4260 *self.evm_env.write() = evm_env;
4262
4263 let timestamp = utc_from_secs(header.timestamp);
4264
4265 node_info!(" Block Number: {}", block_number);
4266 node_info!(" Block Hash: {:?}", block_hash);
4267 if timestamp.year() > 9999 {
4268 node_info!(" Block Time: {:?}\n", timestamp.to_rfc3339());
4270 } else {
4271 node_info!(" Block Time: {:?}\n", timestamp.to_rfc2822());
4272 }
4273
4274 let outcome = MinedBlockOutcome { block_number, included, invalid, not_yet_valid };
4275
4276 (outcome, header, block_hash)
4277 };
4278 let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
4279 header.gas_used,
4280 header.gas_limit,
4281 header.base_fee_per_gas.unwrap_or_default(),
4282 );
4283 let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas(
4284 header.excess_blob_gas.unwrap_or_default(),
4285 header.blob_gas_used.unwrap_or_default(),
4286 );
4287
4288 self.fees.set_base_fee(next_block_base_fee);
4290
4291 self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
4292 next_block_excess_blob_gas,
4293 get_blob_base_fee_update_fraction_by_spec_id(*self.evm_env.read().spec_id()),
4294 ));
4295
4296 self.notify_on_new_block(header.into_inner(), block_hash);
4298
4299 Ok(outcome)
4300 }
4301
4302 pub async fn reorg(
4309 &self,
4310 depth: u64,
4311 tx_pairs: HashMap<u64, Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>>,
4312 common_block: Block,
4313 ) -> Result<(), BlockchainError> {
4314 self.rollback(common_block).await?;
4315 for i in 0..depth {
4317 let to_be_mined = tx_pairs.get(&i).cloned().unwrap_or_else(Vec::new);
4318 let outcome = self.do_mine_block(to_be_mined).await?;
4319 node_info!(
4320 " Mined reorg block number {}. With {} valid txs and with invalid {} txs",
4321 outcome.block_number,
4322 outcome.included.len(),
4323 outcome.invalid.len()
4324 );
4325 }
4326
4327 Ok(())
4328 }
4329
4330 pub async fn pending_block(
4334 &self,
4335 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
4336 ) -> BlockInfo<N> {
4337 self.with_pending_block(pool_transactions, |_, block| block).await
4338 }
4339
4340 pub async fn with_pending_block<F, T>(
4344 &self,
4345 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
4346 f: F,
4347 ) -> T
4348 where
4349 F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockInfo<N>) -> T,
4350 {
4351 let db = self.db.read().await;
4352 let evm_env = self.next_evm_env();
4353
4354 let mut cache_db = AnvilCacheDB::new(&*db);
4355
4356 let parent_hash = self.blockchain.storage.read().best_hash;
4357
4358 let spec_id = *evm_env.spec_id();
4359
4360 let inspector_tx_config = self.inspector_tx_config();
4361 let gas_config = self.pool_tx_gas_config(&evm_env);
4362
4363 let (pool_result, block_result) = self
4364 .execute_with_block_executor(
4365 &mut cache_db,
4366 &evm_env,
4367 parent_hash,
4368 spec_id,
4369 Some(B256::ZERO),
4370 BlockExecutionKind::Complete,
4371 &pool_transactions,
4372 &gas_config,
4373 &inspector_tx_config,
4374 &|pool_tx, account| {
4375 self.validate_pool_transaction_for(
4376 &pool_tx.pending_transaction,
4377 account,
4378 &evm_env,
4379 )
4380 },
4381 )
4382 .expect("pending block execution failed");
4383
4384 let cache_db = cache_db.0;
4386
4387 let state_root = cache_db.maybe_state_root().unwrap_or_default();
4388 let block_number = evm_env.block_env.number.saturating_to();
4389 let block_info = self.build_block_info(
4390 &evm_env,
4391 parent_hash,
4392 block_number,
4393 state_root,
4394 block_result,
4395 pool_result.txs,
4396 pool_result.tx_info,
4397 Some(B256::ZERO),
4398 );
4399
4400 f(Box::new(cache_db), block_info)
4401 }
4402
4403 pub async fn get_fee_token_balance(
4408 &self,
4409 token: Address,
4410 account: Address,
4411 ) -> Result<U256, BlockchainError> {
4412 let mut calldata = vec![0x70, 0xa0, 0x82, 0x31];
4414 calldata.extend_from_slice(&[0u8; 12]);
4416 calldata.extend_from_slice(account.as_slice());
4417
4418 let request = WithOtherFields::new(TransactionRequest {
4419 from: Some(Address::ZERO),
4420 to: Some(TxKind::Call(token)),
4421 input: calldata.into(),
4422 ..Default::default()
4423 });
4424
4425 let fee_details = FeeDetails::zero();
4426 let (exit, out, _, _) = self.call(request, fee_details, None, Default::default()).await?;
4427
4428 if exit != InstructionResult::Return && exit != InstructionResult::Stop {
4430 return Ok(U256::ZERO);
4432 }
4433
4434 match out {
4436 Some(Output::Call(data)) if data.len() >= 32 => Ok(U256::from_be_slice(&data[..32])),
4437 _ => Ok(U256::ZERO),
4438 }
4439 }
4440
4441 pub async fn call(
4447 &self,
4448 request: WithOtherFields<TransactionRequest>,
4449 fee_details: FeeDetails,
4450 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4451 overrides: EvmOverrides,
4452 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
4453 self.with_database_at(block_request, |state, mut block| {
4454 let block_number = block.number;
4455 let (exit, out, gas, state) = {
4456 let mut cache_db = CacheDB::new(state);
4457 if let Some(state_overrides) = overrides.state {
4458 apply_state_overrides(state_overrides.into_iter().collect(), &mut cache_db)?;
4459 }
4460 if let Some(block_overrides) = overrides.block {
4461 cache_db.apply_block_overrides(*block_overrides, &mut block);
4462 }
4463 self.call_with_state(&cache_db, request, fee_details, block)
4464 }?;
4465 trace!(target: "backend", "call return {:?} out: {:?} gas {} on block {}", exit, out, gas, block_number);
4466 Ok((exit, out, gas, state))
4467 }).await?
4468 }
4469
4470 pub async fn call_with_tracing(
4471 &self,
4472 request: WithOtherFields<TransactionRequest>,
4473 fee_details: FeeDetails,
4474 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4475 opts: GethDebugTracingCallOptions,
4476 ) -> Result<GethTrace, BlockchainError> {
4477 let GethDebugTracingCallOptions {
4478 tracing_options,
4479 block_overrides,
4480 state_overrides,
4481 tx_index,
4482 } = opts;
4483
4484 if let Some(tx_index) = tx_index {
4485 return self
4486 .call_with_tracing_at_tx_index(
4487 request,
4488 fee_details,
4489 block_request,
4490 tx_index,
4491 tracing_options,
4492 state_overrides,
4493 block_overrides,
4494 )
4495 .await;
4496 }
4497
4498 self.with_database_at(block_request, |state, block| {
4499 let cache_db = CacheDB::new(state);
4500 self.trace_call_with_state(
4501 request,
4502 fee_details,
4503 block,
4504 cache_db,
4505 tracing_options,
4506 state_overrides,
4507 block_overrides,
4508 )
4509 })
4510 .await?
4511 }
4512
4513 #[allow(clippy::too_many_arguments)]
4514 async fn call_with_tracing_at_tx_index(
4515 &self,
4516 request: WithOtherFields<TransactionRequest>,
4517 fee_details: FeeDetails,
4518 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4519 tx_index: u64,
4520 tracing_options: GethDebugTracingOptions,
4521 state_overrides: Option<StateOverride>,
4522 block_overrides: Option<BlockOverrides>,
4523 ) -> Result<GethTrace, BlockchainError> {
4524 let tx_index = usize::try_from(tx_index).map_err(|_| {
4525 BlockchainError::RpcError(RpcError::invalid_params(format!(
4526 "tx_index {tx_index} does not fit in usize"
4527 )))
4528 })?;
4529 let block_number = match block_request {
4530 Some(BlockRequest::Pending(_)) => {
4531 return Err(BlockchainError::RpcError(RpcError::invalid_params(
4532 "tx_index is not supported for pending blocks".to_string(),
4533 )));
4534 }
4535 Some(BlockRequest::Number(number)) => number,
4536 None => self.best_number(),
4537 };
4538 let block_id = BlockId::Number(BlockNumber::Number(block_number));
4539
4540 if let Some(block) = self.get_block(block_id) {
4541 return self.mined_trace_call_at_tx_index(
4542 request,
4543 fee_details,
4544 &block,
4545 tx_index,
4546 tracing_options,
4547 state_overrides,
4548 block_overrides,
4549 );
4550 }
4551
4552 if let Some(fork) = self.get_fork()
4553 && fork.predates_fork_inclusive(block_number)
4554 {
4555 let opts = GethDebugTracingCallOptions {
4556 tracing_options,
4557 state_overrides,
4558 block_overrides,
4559 tx_index: Some(tx_index as u64),
4560 };
4561 return Ok(fork.debug_trace_call(request, block_id, opts).await?);
4562 }
4563
4564 Err(BlockchainError::BlockNotFound)
4565 }
4566
4567 #[allow(clippy::too_many_arguments)]
4568 fn mined_trace_call_at_tx_index(
4569 &self,
4570 request: WithOtherFields<TransactionRequest>,
4571 fee_details: FeeDetails,
4572 block: &Block,
4573 tx_index: usize,
4574 tracing_options: GethDebugTracingOptions,
4575 state_overrides: Option<StateOverride>,
4576 block_overrides: Option<BlockOverrides>,
4577 ) -> Result<GethTrace, BlockchainError> {
4578 let transaction_count = block.body.transactions.len();
4579 if tx_index >= transaction_count {
4580 return Err(BlockchainError::RpcError(RpcError::invalid_params(format!(
4581 "tx_index {tx_index} out of bounds for block with {transaction_count} transactions"
4582 ))));
4583 }
4584
4585 let pool_txs: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>> = block.body.transactions
4586 [..tx_index]
4587 .iter()
4588 .map(|tx| {
4589 let pending_tx =
4590 PendingTransaction::from_maybe_impersonated(tx.clone()).expect("is valid");
4591 Arc::new(PoolTransaction {
4592 pending_transaction: pending_tx,
4593 requires: vec![],
4594 provides: vec![],
4595 priority: crate::eth::pool::transactions::TransactionPriority(0),
4596 is_replay: true,
4597 })
4598 })
4599 .collect();
4600
4601 let trace = |parent_state: &StateDb| -> Result<GethTrace, BlockchainError> {
4602 let mut cache_db =
4603 AnvilCacheDB::new(Box::new(parent_state) as Box<dyn MaybeFullDatabase + '_>);
4604
4605 let evm_env = self.tx_replay_evm_env(&block.header);
4606
4607 let spec_id = *evm_env.spec_id();
4608 let inspector_tx_config = self.inspector_tx_config();
4609 let gas_config = self.pool_tx_gas_config(&evm_env);
4610
4611 self.execute_with_block_executor(
4612 &mut cache_db,
4613 &evm_env,
4614 block.header.parent_hash,
4615 spec_id,
4616 block.header.parent_beacon_block_root,
4617 BlockExecutionKind::TransactionPrefix,
4618 &pool_txs,
4619 &gas_config,
4620 &inspector_tx_config,
4621 &|pool_tx, account| {
4622 self.validate_pool_transaction_for(
4623 &pool_tx.pending_transaction,
4624 account,
4625 &evm_env,
4626 )
4627 },
4628 )?;
4629
4630 let cache_db = cache_db.0;
4631 self.trace_call_with_state(
4632 request,
4633 fee_details,
4634 evm_env.block_env,
4635 cache_db,
4636 tracing_options,
4637 state_overrides,
4638 block_overrides,
4639 )
4640 };
4641
4642 let read_guard = self.states.upgradable_read();
4643 if let Some(state) = read_guard.get_state(&block.header.parent_hash) {
4644 trace(state)
4645 } else {
4646 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
4647 let state = write_guard
4648 .get_on_disk_state(&block.header.parent_hash)
4649 .ok_or(BlockchainError::BlockNotFound)?;
4650 trace(state)
4651 }
4652 }
4653
4654 #[allow(clippy::too_many_arguments)]
4655 fn trace_call_with_state(
4656 &self,
4657 request: WithOtherFields<TransactionRequest>,
4658 fee_details: FeeDetails,
4659 mut block: BlockEnv,
4660 mut cache_db: CacheDB<Box<dyn MaybeFullDatabase + '_>>,
4661 tracing_options: GethDebugTracingOptions,
4662 state_overrides: Option<StateOverride>,
4663 block_overrides: Option<BlockOverrides>,
4664 ) -> Result<GethTrace, BlockchainError> {
4665 let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options;
4666 let block_number = block.number;
4667
4668 if let Some(state_overrides) = state_overrides {
4669 apply_state_overrides(state_overrides, &mut cache_db)?;
4670 }
4671 if let Some(block_overrides) = block_overrides {
4672 cache_db.apply_block_overrides(block_overrides, &mut block);
4673 }
4674
4675 if let Some(tracer) = tracer {
4676 return match tracer {
4677 GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
4678 GethDebugBuiltInTracerType::CallTracer => {
4679 let call_config = call_config_from_tracer_config(tracer_config)
4680 .map_err(|e| RpcError::invalid_params(e.to_string()))?;
4681
4682 let mut inspector = self.build_inspector().with_tracing_config(
4683 TracingInspectorConfig::from_geth_call_config(&call_config),
4684 );
4685
4686 let PreparedCall { evm_env, tx_env, .. } =
4687 self.prepare_call_env(&cache_db, request, fee_details, block)?;
4688 let ResultAndState { result, state: _ } = self
4689 .transact_call_with_inspector_ref(
4690 &cache_db,
4691 &evm_env,
4692 &mut inspector,
4693 tx_env,
4694 )?;
4695
4696 inspector.print_logs();
4697 if self.print_traces {
4698 inspector.print_traces(self.call_trace_decoder.clone());
4699 }
4700
4701 let tracing_inspector = inspector.tracer.expect("tracer disappeared");
4702
4703 Ok(tracing_inspector
4704 .into_geth_builder()
4705 .geth_call_traces(call_config, result.tx_gas_used())
4706 .into())
4707 }
4708 GethDebugBuiltInTracerType::PreStateTracer => {
4709 let pre_state_config = tracer_config
4710 .into_pre_state_config()
4711 .map_err(|e| RpcError::invalid_params(e.to_string()))?;
4712
4713 let mut inspector = TracingInspector::new(
4714 TracingInspectorConfig::from_geth_prestate_config(&pre_state_config),
4715 );
4716
4717 let PreparedCall { evm_env, tx_env, .. } =
4718 self.prepare_call_env(&cache_db, request, fee_details, block)?;
4719 let result = self.transact_call_with_inspector_ref(
4720 &cache_db,
4721 &evm_env,
4722 &mut inspector,
4723 tx_env,
4724 )?;
4725
4726 Ok(inspector
4727 .into_geth_builder()
4728 .geth_prestate_traces(&result, &pre_state_config, cache_db)?
4729 .into())
4730 }
4731 GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()),
4732 GethDebugBuiltInTracerType::FourByteTracer
4733 | GethDebugBuiltInTracerType::MuxTracer
4734 | GethDebugBuiltInTracerType::FlatCallTracer
4735 | GethDebugBuiltInTracerType::Erc7562Tracer => {
4736 Err(RpcError::invalid_params("unsupported tracer type").into())
4737 }
4738 },
4739 #[cfg(not(feature = "js-tracer"))]
4740 GethDebugTracerType::JsTracer(_) => {
4741 Err(RpcError::invalid_params("unsupported tracer type").into())
4742 }
4743 #[cfg(feature = "js-tracer")]
4744 GethDebugTracerType::JsTracer(code) => {
4745 let config = tracer_config.into_json();
4746 let mut inspector =
4747 revm_inspectors::tracing::js::JsInspector::new(code, config)
4748 .map_err(|err| BlockchainError::Message(err.to_string()))?;
4749
4750 let PreparedCall { evm_env, tx_env, .. } =
4751 self.prepare_call_env(&cache_db, request, fee_details, block.clone())?;
4752 let result = self.transact_call_with_inspector_ref(
4753 &cache_db,
4754 &evm_env,
4755 &mut inspector,
4756 tx_env.clone(),
4757 )?;
4758 let res = inspector
4759 .json_result(result, tx_env.base(), &block, &cache_db)
4760 .map_err(|err| BlockchainError::Message(err.to_string()))?;
4761
4762 Ok(GethTrace::JS(res))
4763 }
4764 };
4765 }
4766
4767 let mut inspector = self
4769 .build_inspector()
4770 .with_tracing_config(TracingInspectorConfig::from_geth_config(&config));
4771
4772 let PreparedCall { evm_env, tx_env, .. } =
4773 self.prepare_call_env(&cache_db, request, fee_details, block)?;
4774 let ResultAndState { result, state: _ } =
4775 self.transact_call_with_inspector_ref(&cache_db, &evm_env, &mut inspector, tx_env)?;
4776
4777 let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
4778
4779 let tracing_inspector = inspector.tracer.expect("tracer disappeared");
4780 let return_value = out.as_ref().map(|o| o.data()).cloned().unwrap_or_default();
4781
4782 trace!(target: "backend", ?exit_reason, ?out, %gas_used, %block_number, "trace call");
4783
4784 let res = tracing_inspector
4785 .into_geth_builder()
4786 .geth_traces(gas_used, return_value, config)
4787 .into();
4788
4789 Ok(res)
4790 }
4791
4792 pub async fn with_database_at<F, T>(
4794 &self,
4795 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4796 f: F,
4797 ) -> Result<T, BlockchainError>
4798 where
4799 F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockEnv) -> T,
4800 {
4801 let block_number = match block_request {
4802 Some(BlockRequest::Pending(pool_transactions)) => {
4803 let result = self
4804 .with_pending_block(pool_transactions, |state, block| {
4805 let block = block.block;
4806 f(state, block_env_from_header(&block.header))
4807 })
4808 .await;
4809 return Ok(result);
4810 }
4811 Some(BlockRequest::Number(bn)) => Some(BlockNumber::Number(bn)),
4812 None => None,
4813 };
4814 let block_number = self.convert_block_number(block_number);
4815 let current_number = self.best_number();
4816
4817 if block_number > current_number {
4819 return Err(BlockchainError::BlockOutOfRange(current_number, block_number));
4820 }
4821
4822 if block_number < current_number {
4823 if let Some((block_hash, block)) = self
4824 .block_by_number(BlockNumber::Number(block_number))
4825 .await?
4826 .map(|block| (block.header.hash, block))
4827 {
4828 let read_guard = self.states.upgradable_read();
4829 if let Some(state_db) = read_guard.get_state(&block_hash) {
4830 return Ok(f(Box::new(state_db), block_env_from_header(&block.header)));
4831 }
4832
4833 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
4834 if let Some(state) = write_guard.get_on_disk_state(&block_hash) {
4835 return Ok(f(Box::new(state), block_env_from_header(&block.header)));
4836 }
4837 }
4838
4839 warn!(target: "backend", "Not historic state found for block={}", block_number);
4840 return Err(BlockchainError::BlockOutOfRange(current_number, block_number));
4841 }
4842
4843 let db = self.db.read().await;
4844 let block = self.evm_env.read().block_env.clone();
4845 Ok(f(Box::new(&**db), block))
4846 }
4847
4848 pub async fn storage_at(
4849 &self,
4850 address: Address,
4851 index: U256,
4852 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4853 ) -> Result<B256, BlockchainError> {
4854 self.with_database_at(block_request, |db, _| {
4855 trace!(target: "backend", "get storage for {:?} at {:?}", address, index);
4856 let val = db.storage_ref(address, index)?;
4857 Ok(val.into())
4858 })
4859 .await?
4860 }
4861
4862 pub async fn tempo_nonce(
4863 &self,
4864 caller: Address,
4865 nonce_key: U256,
4866 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4867 ) -> Result<u64, BlockchainError> {
4868 self.with_database_at(block_request, |state, _| tempo_nonce(&state, caller, nonce_key))
4869 .await?
4870 }
4871
4872 pub async fn storage_values(
4874 &self,
4875 requests: HashMap<Address, Vec<B256>>,
4876 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4877 ) -> Result<HashMap<Address, Vec<B256>>, BlockchainError> {
4878 self.with_database_at(block_request, |db, _| {
4879 trace!(target: "backend", "get storage values for {} addresses", requests.len());
4880 let mut result: HashMap<Address, Vec<B256>> = HashMap::default();
4881 for (address, slots) in &requests {
4882 let mut values = Vec::with_capacity(slots.len());
4883 for slot in slots {
4884 let val = db.storage_ref(*address, (*slot).into())?;
4885 values.push(val.into());
4886 }
4887 result.insert(*address, values);
4888 }
4889 Ok(result)
4890 })
4891 .await?
4892 }
4893
4894 pub async fn get_code(
4899 &self,
4900 address: Address,
4901 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4902 ) -> Result<Bytes, BlockchainError> {
4903 self.with_database_at(block_request, |db, _| self.get_code_with_state(&db, address)).await?
4904 }
4905
4906 pub async fn get_balance(
4910 &self,
4911 address: Address,
4912 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4913 ) -> Result<U256, BlockchainError> {
4914 self.with_database_at(block_request, |db, _| self.get_balance_with_state(db, address))
4915 .await?
4916 }
4917
4918 pub async fn get_account_at_block(
4919 &self,
4920 address: Address,
4921 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4922 ) -> Result<TrieAccount, BlockchainError> {
4923 self.with_database_at(block_request, |block_db, _| {
4924 let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
4925 let account = db.get(&address).cloned().unwrap_or_default();
4926 let storage_root = storage_root(&account.storage);
4927 let code_hash = account.info.code_hash;
4928 let balance = account.info.balance;
4929 let nonce = account.info.nonce;
4930 Ok(TrieAccount { balance, nonce, code_hash, storage_root })
4931 })
4932 .await?
4933 }
4934
4935 pub async fn get_nonce(
4939 &self,
4940 address: Address,
4941 block_request: BlockRequest<FoundryTxEnvelope>,
4942 ) -> Result<u64, BlockchainError> {
4943 if let BlockRequest::Pending(pool_transactions) = &block_request
4944 && let Some(value) = get_pool_transactions_nonce(pool_transactions, address)
4945 {
4946 return Ok(value);
4947 }
4948 let final_block_request = match block_request {
4949 BlockRequest::Pending(_) => BlockRequest::Number(self.best_number()),
4950 BlockRequest::Number(bn) => BlockRequest::Number(bn),
4951 };
4952
4953 self.with_database_at(Some(final_block_request), |db, _| {
4954 trace!(target: "backend", "get nonce for {:?}", address);
4955 Ok(db.basic_ref(address)?.unwrap_or_default().nonce)
4956 })
4957 .await?
4958 }
4959
4960 fn replay_tx_with_inspector<I, F, T>(
4961 &self,
4962 hash: B256,
4963 mut inspector: I,
4964 f: F,
4965 ) -> Result<T, BlockchainError>
4966 where
4967 for<'a> I: BackendInspector<WrapDatabaseRef<&'a CacheDB<Box<&'a StateDb>>>> + 'a,
4968 for<'a> F:
4969 FnOnce(ResultAndState<HaltReason>, CacheDB<Box<&'a StateDb>>, I, TxEnv, EvmEnv) -> T,
4970 {
4971 let block = {
4972 let storage = self.blockchain.storage.read();
4973 let MinedTransaction { block_hash, .. } = storage
4974 .transactions
4975 .get(&hash)
4976 .cloned()
4977 .ok_or(BlockchainError::TransactionNotFound)?;
4978
4979 storage.blocks.get(&block_hash).cloned().ok_or(BlockchainError::BlockNotFound)?
4980 };
4981
4982 let index = block
4983 .body
4984 .transactions
4985 .iter()
4986 .position(|tx| tx.hash() == hash)
4987 .expect("transaction not found in block");
4988
4989 let pool_txs: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>> = block.body.transactions
4990 [..index]
4991 .iter()
4992 .map(|tx| {
4993 let pending_tx =
4994 PendingTransaction::from_maybe_impersonated(tx.clone()).expect("is valid");
4995 Arc::new(PoolTransaction {
4996 pending_transaction: pending_tx,
4997 requires: vec![],
4998 provides: vec![],
4999 priority: crate::eth::pool::transactions::TransactionPriority(0),
5000 is_replay: true,
5001 })
5002 })
5003 .collect();
5004
5005 let trace = |parent_state: &StateDb| -> Result<T, BlockchainError> {
5006 let mut cache_db = AnvilCacheDB::new(Box::new(parent_state));
5007
5008 let evm_env = self.tx_replay_evm_env(&block.header);
5009
5010 let spec_id = *evm_env.spec_id();
5011
5012 let inspector_tx_config = self.inspector_tx_config();
5013 let gas_config = self.pool_tx_gas_config(&evm_env);
5014
5015 self.execute_with_block_executor(
5016 &mut cache_db,
5017 &evm_env,
5018 block.header.parent_hash,
5019 spec_id,
5020 block.header.parent_beacon_block_root,
5021 BlockExecutionKind::TransactionPrefix,
5022 &pool_txs,
5023 &gas_config,
5024 &inspector_tx_config,
5025 &|pool_tx, account| {
5026 self.validate_pool_transaction_for(
5027 &pool_tx.pending_transaction,
5028 account,
5029 &evm_env,
5030 )
5031 },
5032 )?;
5033
5034 let cache_db = cache_db.0;
5036
5037 let target_tx = block.body.transactions[index].clone();
5038 let target_tx = PendingTransaction::from_maybe_impersonated(target_tx)?;
5039 let (result, base_tx_env) = self.transact_envelope_with_inspector_ref(
5040 &cache_db,
5041 &evm_env,
5042 &mut inspector,
5043 target_tx.transaction.as_ref(),
5044 *target_tx.sender(),
5045 )?;
5046
5047 Ok(f(result, cache_db, inspector, base_tx_env, evm_env))
5048 };
5049
5050 let read_guard = self.states.upgradable_read();
5051 if let Some(state) = read_guard.get_state(&block.header.parent_hash) {
5052 trace(state)
5053 } else {
5054 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
5055 let state = write_guard
5056 .get_on_disk_state(&block.header.parent_hash)
5057 .ok_or(BlockchainError::BlockNotFound)?;
5058 trace(state)
5059 }
5060 }
5061
5062 #[cfg(feature = "js-tracer")]
5064 pub async fn trace_tx_with_js_tracer(
5065 &self,
5066 hash: B256,
5067 code: String,
5068 opts: GethDebugTracingOptions,
5069 ) -> Result<GethTrace, BlockchainError> {
5070 let GethDebugTracingOptions { tracer_config, .. } = opts;
5071 let config = tracer_config.into_json();
5072 let inspector = revm_inspectors::tracing::js::JsInspector::new(code, config)
5073 .map_err(|err| BlockchainError::Message(err.to_string()))?;
5074 let trace = self.replay_tx_with_inspector(
5075 hash,
5076 inspector,
5077 |result, cache_db, mut inspector, tx_env, evm_env| {
5078 inspector
5079 .json_result(
5080 result,
5081 &alloy_evm::IntoTxEnv::into_tx_env(tx_env),
5082 &evm_env.block_env,
5083 &cache_db,
5084 )
5085 .map_err(|e| BlockchainError::Message(e.to_string()))
5086 },
5087 )??;
5088 Ok(GethTrace::JS(trace))
5089 }
5090
5091 pub async fn prove_account_at(
5095 &self,
5096 address: Address,
5097 keys: Vec<B256>,
5098 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
5099 ) -> Result<AccountProof, BlockchainError> {
5100 let block_number = block_request.as_ref().map(|r| r.block_number());
5101
5102 self.with_database_at(block_request, |block_db, _| {
5103 trace!(target: "backend", "get proof for {:?} at {:?}", address, block_number);
5104 let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
5105 let account = db.get(&address).cloned().unwrap_or_default();
5106
5107 let mut builder = HashBuilder::default()
5108 .with_proof_retainer(ProofRetainer::new(vec![Nibbles::unpack(keccak256(address))]));
5109
5110 for (key, account) in trie_accounts(db) {
5111 builder.add_leaf(key, &account);
5112 }
5113
5114 let _ = builder.root();
5115
5116 let proof = builder
5117 .take_proof_nodes()
5118 .into_nodes_sorted()
5119 .into_iter()
5120 .map(|(_, v)| v)
5121 .collect();
5122 let (storage_hash, storage_proofs) = prove_storage(&account.storage, &keys);
5123
5124 let account_proof = AccountProof {
5125 address,
5126 balance: account.info.balance,
5127 nonce: account.info.nonce,
5128 code_hash: account.info.code_hash,
5129 storage_hash,
5130 account_proof: proof,
5131 storage_proof: keys
5132 .into_iter()
5133 .zip(storage_proofs)
5134 .map(|(key, proof)| {
5135 let storage_key: U256 = key.into();
5136 let value = account.storage.get(&storage_key).copied().unwrap_or_default();
5137 StorageProof { key: JsonStorageKey::Hash(key), value, proof }
5138 })
5139 .collect(),
5140 };
5141
5142 Ok(account_proof)
5143 })
5144 .await?
5145 }
5146}
5147
5148impl<N: Network> Backend<N>
5149where
5150 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
5151{
5152 pub async fn trace_transaction_opcode_gas(
5154 &self,
5155 hash: B256,
5156 ) -> Result<Option<TransactionOpcodeGas>, BlockchainError> {
5157 match self.replay_tx_with_inspector(
5158 hash,
5159 OpcodeGasInspector::default(),
5160 move |_, _, inspector, _, _| TransactionOpcodeGas {
5161 transaction_hash: hash,
5162 opcode_gas: inspector.opcode_gas_iter().collect(),
5163 },
5164 ) {
5165 Ok(trace) => Ok(Some(trace)),
5166 Err(BlockchainError::TransactionNotFound) => {
5167 if let Some(fork) = self.get_fork() {
5168 return Ok(fork.trace_transaction_opcode_gas(hash).await?);
5169 }
5170
5171 Ok(None)
5172 }
5173 Err(err) => Err(err),
5174 }
5175 }
5176
5177 pub async fn trace_block_opcode_gas(
5179 &self,
5180 block_id: BlockId,
5181 ) -> Result<Option<BlockOpcodeGas>, BlockchainError> {
5182 if let Some((block, block_hash)) = self.get_block_with_hash(block_id) {
5183 return self.mined_block_opcode_gas(&block, block_hash).map(Some);
5184 }
5185
5186 if let Some(fork) = self.get_fork() {
5187 let number = self.ensure_block_number(Some(block_id)).await?;
5188 if fork.predates_fork_inclusive(number) {
5189 return Ok(fork.trace_block_opcode_gas(block_id).await?);
5190 }
5191 }
5192
5193 Err(BlockchainError::BlockNotFound)
5194 }
5195
5196 fn mined_block_opcode_gas(
5197 &self,
5198 block: &Block,
5199 block_hash: B256,
5200 ) -> Result<BlockOpcodeGas, BlockchainError> {
5201 if block.header.number() == self.genesis_number() {
5203 return Ok(BlockOpcodeGas {
5204 block_hash,
5205 block_number: block.header.number(),
5206 transactions: Vec::new(),
5207 });
5208 }
5209
5210 let parent_hash = block.header.parent_hash;
5211
5212 let trace = |parent_state: &StateDb| -> Result<Vec<TransactionOpcodeGas>, BlockchainError> {
5213 let (mut cache_db, evm_env) = self.prepare_block_replay(block, parent_state)?;
5214 let mut transactions = Vec::with_capacity(block.body.transactions.len());
5215
5216 for tx_envelope in &block.body.transactions {
5217 let mut inspector = OpcodeGasInspector::default();
5218 let pending_tx = PendingTransaction::from_maybe_impersonated(tx_envelope.clone())?;
5219 let (result, _) = self.transact_envelope_with_inspector_ref(
5220 &cache_db,
5221 &evm_env,
5222 &mut inspector,
5223 pending_tx.transaction.as_ref(),
5224 *pending_tx.sender(),
5225 )?;
5226
5227 transactions.push(TransactionOpcodeGas {
5228 transaction_hash: tx_envelope.hash(),
5229 opcode_gas: inspector.opcode_gas_iter().collect(),
5230 });
5231
5232 cache_db.commit(result.state);
5233 }
5234
5235 Ok(transactions)
5236 };
5237
5238 let read_guard = self.states.upgradable_read();
5239 let transactions = if let Some(state) = read_guard.get_state(&parent_hash) {
5240 trace(state)?
5241 } else {
5242 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
5243 let state = write_guard
5244 .get_on_disk_state(&parent_hash)
5245 .ok_or(BlockchainError::BlockNotFound)?;
5246 trace(state)?
5247 };
5248
5249 Ok(BlockOpcodeGas { block_hash, block_number: block.header.number(), transactions })
5250 }
5251
5252 pub async fn debug_account_info_at(
5254 &self,
5255 block_id: BlockId,
5256 tx_index: Index,
5257 address: Address,
5258 ) -> Result<Option<RpcAccountInfo>, BlockchainError> {
5259 if let Some((block, _)) = self.get_block_with_hash(block_id) {
5260 return self.mined_debug_account_info_at(&block, tx_index, address).map(Some);
5261 }
5262
5263 if let Some(fork) = self.get_fork() {
5264 let number = self.ensure_block_number(Some(block_id)).await?;
5265 if fork.predates_fork_inclusive(number) {
5266 let resolved = match block_id {
5270 BlockId::Hash(_) => block_id,
5271 _ => BlockId::number(number),
5272 };
5273 return Ok(fork.debug_account_info_at(resolved, tx_index, address).await?);
5274 }
5275 }
5276
5277 Err(BlockchainError::BlockNotFound)
5278 }
5279
5280 fn mined_debug_account_info_at(
5281 &self,
5282 block: &Block,
5283 tx_index: Index,
5284 address: Address,
5285 ) -> Result<RpcAccountInfo, BlockchainError> {
5286 let tx_index = tx_index.0;
5287 let transaction_count = block.body.transactions.len();
5288 if tx_index >= transaction_count {
5289 return Err(BlockchainError::RpcError(RpcError::invalid_params(format!(
5290 "tx_index {tx_index} out of bounds for block with {transaction_count} transactions"
5291 ))));
5292 }
5293
5294 let pool_txs: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>> = block.body.transactions
5295 [..=tx_index]
5296 .iter()
5297 .map(|tx| {
5298 let pending_tx =
5299 PendingTransaction::from_maybe_impersonated(tx.clone()).expect("is valid");
5300 Arc::new(PoolTransaction {
5301 pending_transaction: pending_tx,
5302 requires: vec![],
5303 provides: vec![],
5304 priority: crate::eth::pool::transactions::TransactionPriority(0),
5305 is_replay: true,
5306 })
5307 })
5308 .collect();
5309
5310 let trace = |parent_state: &StateDb| -> Result<RpcAccountInfo, BlockchainError> {
5311 let mut cache_db = AnvilCacheDB::new(Box::new(parent_state));
5312 let evm_env = self.tx_replay_evm_env(&block.header);
5313
5314 let spec_id = *evm_env.spec_id();
5315 let inspector_tx_config = self.inspector_tx_config();
5316 let gas_config = self.pool_tx_gas_config(&evm_env);
5317
5318 self.execute_with_block_executor(
5319 &mut cache_db,
5320 &evm_env,
5321 block.header.parent_hash,
5322 spec_id,
5323 block.header.parent_beacon_block_root,
5324 BlockExecutionKind::TransactionPrefix,
5325 &pool_txs,
5326 &gas_config,
5327 &inspector_tx_config,
5328 &|pool_tx, account| {
5329 self.validate_pool_transaction_for(
5330 &pool_tx.pending_transaction,
5331 account,
5332 &evm_env,
5333 )
5334 },
5335 )?;
5336
5337 let cache_db = cache_db.0;
5338 let account = revm::DatabaseRef::basic_ref(&cache_db, address)?.unwrap_or_default();
5339 let code = self.get_code_with_state(&cache_db, address)?;
5340 Ok(RpcAccountInfo { balance: account.balance, nonce: account.nonce, code })
5341 };
5342
5343 let read_guard = self.states.upgradable_read();
5344 if let Some(state) = read_guard.get_state(&block.header.parent_hash) {
5345 trace(state)
5346 } else {
5347 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
5348 let state = write_guard
5349 .get_on_disk_state(&block.header.parent_hash)
5350 .ok_or(BlockchainError::BlockNotFound)?;
5351 trace(state)
5352 }
5353 }
5354
5355 pub async fn rollback(&self, common_block: Block) -> Result<(), BlockchainError> {
5360 let hash = common_block.header.hash_slow();
5361
5362 let common_state = {
5364 let return_state_or_throw_err =
5365 |db: Option<&StateDb>| -> Result<AddressMap<DbAccount>, BlockchainError> {
5366 let state_db = db.ok_or(BlockchainError::DataUnavailable)?;
5367 let db_full =
5368 state_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
5369 Ok(db_full.clone())
5370 };
5371
5372 let read_guard = self.states.upgradable_read();
5373 if let Some(db) = read_guard.get_state(&hash) {
5374 return_state_or_throw_err(Some(db))?
5375 } else {
5376 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
5377 return_state_or_throw_err(write_guard.get_on_disk_state(&hash))?
5378 }
5379 };
5380
5381 {
5382 let removed_logs = self.removed_logs_since(common_block.header.number());
5385
5386 let removed_blocks =
5388 self.blockchain.storage.write().unwind_to(common_block.header.number(), hash);
5389
5390 let removed_hashes: Vec<_> =
5392 removed_blocks.iter().map(|b| b.header.hash_slow()).collect();
5393 self.states.write().remove_block_states(&removed_hashes);
5394
5395 if !removed_logs.is_empty() {
5398 self.notify_on_removed_logs(removed_logs);
5399 }
5400
5401 let mut env = self.evm_env.write();
5403 env.block_env.number = U256::from(common_block.header.number());
5404 env.block_env.timestamp = U256::from(common_block.header.timestamp());
5405 env.block_env.gas_limit = common_block.header.gas_limit();
5406 env.block_env.difficulty = common_block.header.difficulty();
5407 env.block_env.prevrandao = common_block.header.mix_hash();
5408
5409 self.time.reset(env.block_env.timestamp.saturating_to());
5410 self.cheats.clear_next_block_prevrandao();
5412 }
5413
5414 {
5415 let block_hashes: Vec<_> = {
5419 let storage = self.blockchain.storage.read();
5420 let min_block = common_block.header.number().saturating_sub(256);
5421 storage
5422 .hashes
5423 .iter()
5424 .filter(|(num, _)| **num >= min_block)
5425 .map(|(&num, &hash)| (num, hash))
5426 .collect()
5427 };
5428
5429 let mut db = self.db.write().await;
5431 db.clear();
5432
5433 for (address, acc) in common_state {
5435 db.insert_account(address, acc.info);
5436 for (key, value) in acc.storage {
5437 db.set_storage_at(address, key.into(), value.into())?;
5438 }
5439 }
5440
5441 for (block_num, hash) in block_hashes {
5444 db.insert_block_hash(U256::from(block_num), hash);
5445 }
5446 }
5447
5448 Ok(())
5449 }
5450
5451 pub async fn debug_trace_transaction(
5453 &self,
5454 hash: B256,
5455 opts: GethDebugTracingOptions,
5456 ) -> Result<GethTrace, BlockchainError> {
5457 #[cfg(feature = "js-tracer")]
5458 if let Some(tracer_type) = opts.tracer.as_ref()
5459 && tracer_type.is_js()
5460 {
5461 return self
5462 .trace_tx_with_js_tracer(hash, tracer_type.as_str().to_string(), opts.clone())
5463 .await;
5464 }
5465
5466 if let Some(trace) = self.mined_geth_trace_transaction(hash, opts.clone()).await {
5467 return trace;
5468 }
5469
5470 if let Some(fork) = self.get_fork() {
5471 return Ok(fork.debug_trace_transaction(hash, opts).await?);
5472 }
5473
5474 Ok(GethTrace::Default(Default::default()))
5475 }
5476
5477 pub async fn debug_trace_block(
5479 &self,
5480 rlp_block: Bytes,
5481 opts: GethDebugTracingOptions,
5482 ) -> Result<Vec<TraceResult>, BlockchainError> {
5483 let mut rlp = rlp_block.as_ref();
5484 let block = Block::<FoundryTxEnvelope>::decode(&mut rlp).map_err(|err| {
5485 BlockchainError::RpcError(RpcError::invalid_params(format!(
5486 "failed to decode block: {err}"
5487 )))
5488 })?;
5489 if !rlp.is_empty() {
5490 return Err(BlockchainError::RpcError(RpcError::invalid_params(
5491 "failed to decode block: trailing bytes".to_string(),
5492 )));
5493 }
5494
5495 self.debug_trace_block_by_hash(block.header.hash_slow(), opts).await
5496 }
5497
5498 pub async fn debug_trace_block_by_hash(
5500 &self,
5501 block_hash: B256,
5502 opts: GethDebugTracingOptions,
5503 ) -> Result<Vec<TraceResult>, BlockchainError> {
5504 if let Some(block) = self.blockchain.get_block_by_hash(&block_hash) {
5505 let mut traces = Vec::new();
5506 for tx in &block.body.transactions {
5507 let tx_hash = tx.hash();
5508 match self.debug_trace_transaction(tx_hash, opts.clone()).await {
5509 Ok(trace) => {
5510 traces.push(TraceResult::Success { result: trace, tx_hash: Some(tx_hash) });
5511 }
5512 Err(error) => {
5513 traces.push(TraceResult::Error {
5514 error: error.to_string(),
5515 tx_hash: Some(tx_hash),
5516 });
5517 }
5518 }
5519 }
5520 return Ok(traces);
5521 }
5522
5523 if let Some(fork) = self.get_fork() {
5524 return Ok(fork.debug_trace_block_by_hash(block_hash, opts).await?);
5525 }
5526
5527 Err(BlockchainError::BlockNotFound)
5528 }
5529
5530 pub async fn debug_trace_block_by_number(
5532 &self,
5533 block_number: BlockNumber,
5534 opts: GethDebugTracingOptions,
5535 ) -> Result<Vec<TraceResult>, BlockchainError> {
5536 let number = self.convert_block_number(Some(block_number));
5537
5538 if let Some(block) = self.get_block(BlockId::Number(BlockNumber::Number(number))) {
5539 let mut traces = Vec::new();
5540 for tx in &block.body.transactions {
5541 let tx_hash = tx.hash();
5542 match self.debug_trace_transaction(tx_hash, opts.clone()).await {
5543 Ok(trace) => {
5544 traces.push(TraceResult::Success { result: trace, tx_hash: Some(tx_hash) });
5545 }
5546 Err(error) => {
5547 traces.push(TraceResult::Error {
5548 error: error.to_string(),
5549 tx_hash: Some(tx_hash),
5550 });
5551 }
5552 }
5553 }
5554 return Ok(traces);
5555 }
5556
5557 if let Some(fork) = self.get_fork() {
5558 return Ok(fork.debug_trace_block_by_number(number, opts).await?);
5559 }
5560
5561 Err(BlockchainError::BlockNotFound)
5562 }
5563
5564 fn geth_trace(
5565 &self,
5566 tx: &MinedTransaction<N>,
5567 opts: GethDebugTracingOptions,
5568 ) -> Result<GethTrace, BlockchainError> {
5569 let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts;
5570
5571 if let Some(tracer) = tracer {
5572 match tracer {
5573 GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
5574 GethDebugBuiltInTracerType::FourByteTracer => {
5575 let inspector = FourByteInspector::default();
5576 let res = self.replay_tx_with_inspector(
5577 tx.info.transaction_hash,
5578 inspector,
5579 |_, _, inspector, _, _| FourByteFrame::from(inspector).into(),
5580 )?;
5581 return Ok(res);
5582 }
5583 GethDebugBuiltInTracerType::CallTracer => {
5584 return match call_config_from_tracer_config(tracer_config) {
5585 Ok(call_config) => {
5586 let inspector = TracingInspector::new(
5587 TracingInspectorConfig::from_geth_call_config(&call_config),
5588 );
5589 let frame = self.replay_tx_with_inspector(
5590 tx.info.transaction_hash,
5591 inspector,
5592 |_, _, inspector, _, _| {
5593 inspector
5594 .geth_builder()
5595 .geth_call_traces(call_config, tx.info.gas_used)
5596 .into()
5597 },
5598 )?;
5599 Ok(frame)
5600 }
5601 Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
5602 };
5603 }
5604 GethDebugBuiltInTracerType::PreStateTracer => {
5605 return match tracer_config.into_pre_state_config() {
5606 Ok(pre_state_config) => {
5607 let inspector = TracingInspector::new(
5608 TracingInspectorConfig::from_geth_prestate_config(
5609 &pre_state_config,
5610 ),
5611 );
5612 let frame = self.replay_tx_with_inspector(
5613 tx.info.transaction_hash,
5614 inspector,
5615 |state, db, inspector, _, _| {
5616 inspector.geth_builder().geth_prestate_traces(
5617 &state,
5618 &pre_state_config,
5619 db,
5620 )
5621 },
5622 )??;
5623 Ok(frame.into())
5624 }
5625 Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
5626 };
5627 }
5628 GethDebugBuiltInTracerType::NoopTracer
5629 | GethDebugBuiltInTracerType::MuxTracer
5630 | GethDebugBuiltInTracerType::Erc7562Tracer
5631 | GethDebugBuiltInTracerType::FlatCallTracer => {}
5632 },
5633 GethDebugTracerType::JsTracer(_code) => {}
5634 }
5635
5636 return Ok(NoopFrame::default().into());
5637 }
5638
5639 Ok(GethTraceBuilder::new(tx.info.traces.clone())
5641 .geth_traces(tx.info.gas_used, tx.info.out.clone().unwrap_or_default(), config)
5642 .into())
5643 }
5644
5645 async fn mined_geth_trace_transaction(
5646 &self,
5647 hash: B256,
5648 opts: GethDebugTracingOptions,
5649 ) -> Option<Result<GethTrace, BlockchainError>> {
5650 self.blockchain.storage.read().transactions.get(&hash).map(|tx| self.geth_trace(tx, opts))
5651 }
5652
5653 fn get_receipts(
5655 &self,
5656 tx_hashes: impl IntoIterator<Item = TxHash>,
5657 ) -> Vec<FoundryReceiptEnvelope> {
5658 let storage = self.blockchain.storage.read();
5659 let mut receipts = vec![];
5660
5661 for hash in tx_hashes {
5662 if let Some(tx) = storage.transactions.get(&hash) {
5663 receipts.push(tx.receipt.clone());
5664 }
5665 }
5666
5667 receipts
5668 }
5669
5670 pub async fn transaction_receipt(
5671 &self,
5672 hash: B256,
5673 ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
5674 if let Some(receipt) = self.mined_transaction_receipt(hash) {
5675 return Ok(Some(receipt.inner));
5676 }
5677
5678 if let Some(fork) = self.get_fork() {
5679 let receipt = fork.transaction_receipt(hash).await?;
5680 let number = self.convert_block_number(
5681 receipt.clone().and_then(|r| r.block_number()).map(BlockNumber::from),
5682 );
5683
5684 if fork.predates_fork_inclusive(number) {
5685 return Ok(receipt);
5686 }
5687 }
5688
5689 Ok(None)
5690 }
5691
5692 pub fn mined_block_receipts(&self, id: impl Into<BlockId>) -> Option<Vec<FoundryTxReceipt>> {
5694 let storage = self.blockchain.storage.read();
5695 let hash = match id.into() {
5696 BlockId::Hash(hash) => hash.block_hash,
5697 BlockId::Number(number) => storage.hash(number, self.slots_in_an_epoch)?,
5698 };
5699 let block = storage.blocks.get(&hash)?.clone();
5700
5701 if block.body.transactions.iter().enumerate().any(|(index, transaction)| {
5702 storage.transactions.get(&transaction.hash()).is_none_or(|transaction| {
5703 transaction.block_hash != hash
5704 || transaction.info.transaction_index as usize != index
5705 })
5706 }) {
5707 drop(storage);
5708 return block
5709 .body
5710 .transactions
5711 .into_iter()
5712 .map(|transaction| {
5713 self.mined_transaction_receipt(transaction.hash()).map(|receipt| receipt.inner)
5714 })
5715 .collect();
5716 }
5717
5718 let mut receipts = Vec::with_capacity(block.body.transactions.len());
5719 let mut next_log_index = 0;
5720
5721 for block_transaction in &block.body.transactions {
5722 let transaction = storage.transactions.get(&block_transaction.hash())?;
5723 let log_count = transaction.receipt.logs().len();
5724 let receipt = self.build_mined_transaction_receipt(
5725 &transaction.info,
5726 transaction.receipt.clone(),
5727 transaction.block_hash,
5728 &block,
5729 next_log_index,
5730 );
5731 receipts.push(receipt.inner);
5732 next_log_index += log_count;
5733 }
5734
5735 Some(receipts)
5736 }
5737
5738 pub(crate) fn mined_transaction_receipt(
5740 &self,
5741 hash: B256,
5742 ) -> Option<MinedTransactionReceipt<FoundryNetwork>> {
5743 let transaction = self.blockchain.get_transaction_by_hash(&hash)?;
5744
5745 let index = transaction.info.transaction_index as usize;
5746 let block = self.blockchain.get_block_by_hash(&transaction.block_hash)?;
5747 let receipts = self.get_receipts(block.body.transactions.iter().map(|tx| tx.hash()));
5748 let next_log_index = receipts[..index].iter().map(|r| r.logs().len()).sum::<usize>();
5749
5750 let MinedTransaction { info, receipt, block_hash, .. } = transaction;
5751 Some(self.build_mined_transaction_receipt(
5752 &info,
5753 receipt,
5754 block_hash,
5755 &block,
5756 next_log_index,
5757 ))
5758 }
5759
5760 fn build_mined_transaction_receipt(
5761 &self,
5762 info: &TransactionInfo,
5763 tx_receipt: FoundryReceiptEnvelope,
5764 block_hash: B256,
5765 block: &Block,
5766 next_log_index: usize,
5767 ) -> MinedTransactionReceipt<FoundryNetwork> {
5768 let transaction = block.body.transactions[info.transaction_index as usize].clone();
5769
5770 let excess_blob_gas = block.header.excess_blob_gas();
5772 let blob_gas_price =
5773 alloy_eips::eip4844::calc_blob_gasprice(excess_blob_gas.unwrap_or_default());
5774 let blob_gas_used = transaction.blob_gas_used();
5775
5776 let effective_gas_price = transaction.effective_gas_price(block.header.base_fee_per_gas());
5777
5778 let tx_receipt = tx_receipt.convert_logs_rpc(
5779 BlockNumHash::new(block.header.number(), block_hash),
5780 block.header.timestamp(),
5781 info.transaction_hash,
5782 info.transaction_index,
5783 next_log_index,
5784 );
5785
5786 let receipt = TransactionReceipt {
5787 inner: tx_receipt,
5788 transaction_hash: info.transaction_hash,
5789 transaction_index: Some(info.transaction_index),
5790 block_number: Some(block.header.number()),
5791 gas_used: info.gas_used,
5792 contract_address: info.contract_address,
5793 effective_gas_price,
5794 block_hash: Some(block_hash),
5795 from: info.from,
5796 to: info.to,
5797 blob_gas_price: Some(blob_gas_price),
5798 blob_gas_used,
5799 };
5800
5801 let mut inner = FoundryTxReceipt::with_timestamp(receipt, block.header.timestamp());
5803 if self.is_tempo() {
5804 let fee_payer = match &*transaction {
5805 FoundryTxEnvelope::Tempo(tx) => match tx.tx().recover_fee_payer(info.from) {
5806 Ok(fee_payer) => fee_payer,
5807 Err(error) => {
5808 warn!(
5809 target: "backend",
5810 %error,
5811 tx_hash = ?info.transaction_hash,
5812 "failed to recover Tempo fee payer for mined receipt"
5813 );
5814 info.from
5815 }
5816 },
5817 _ => info.from,
5818 };
5819 inner = inner.with_fee_payer(fee_payer);
5820
5821 if inner.effective_gas_price() > 0
5824 && inner.gas_used() > 0
5825 && let Some(fee_token) = inner.0.inner.logs().last().map(|log| log.address())
5826 {
5827 inner = inner.with_fee_token(fee_token);
5828 }
5829 }
5830 MinedTransactionReceipt { inner, out: info.out.clone() }
5831 }
5832
5833 pub async fn block_receipts(
5835 &self,
5836 number: BlockId,
5837 ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
5838 if let Some(receipts) = self.mined_block_receipts(number) {
5839 return Ok(Some(receipts));
5840 }
5841
5842 if let Some(fork) = self.get_fork() {
5843 let number = match self.ensure_block_number(Some(number)).await {
5844 Err(_) => return Ok(None),
5845 Ok(n) => n,
5846 };
5847
5848 if fork.predates_fork_inclusive(number) {
5849 let receipts = fork.block_receipts(number).await?;
5850
5851 return Ok(receipts);
5852 }
5853 }
5854
5855 Ok(None)
5856 }
5857}
5858
5859impl<N: Network<ReceiptEnvelope = FoundryReceiptEnvelope>> Backend<N> {
5860 pub async fn serialized_state(
5862 &self,
5863 preserve_historical_states: bool,
5864 ) -> Result<SerializableState, BlockchainError> {
5865 let at = self.evm_env.read().block_env.clone();
5866 let best_number = self.blockchain.storage.read().best_number;
5867 let blocks = self.blockchain.storage.read().serialized_blocks();
5868 let transactions = self.blockchain.storage.read().serialized_transactions();
5869 let historical_states =
5870 preserve_historical_states.then(|| self.states.write().serialized_states());
5871
5872 let state = self.db.read().await.dump_state(
5873 at,
5874 best_number,
5875 blocks,
5876 transactions,
5877 historical_states,
5878 )?;
5879 state.ok_or_else(|| {
5880 RpcError::invalid_params("Dumping state not supported with the current configuration")
5881 .into()
5882 })
5883 }
5884
5885 pub async fn dump_state(
5887 &self,
5888 preserve_historical_states: bool,
5889 ) -> Result<Bytes, BlockchainError> {
5890 let state = self.serialized_state(preserve_historical_states).await?;
5891 let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
5892 encoder
5893 .write_all(&serde_json::to_vec(&state).unwrap_or_default())
5894 .map_err(|_| BlockchainError::DataUnavailable)?;
5895 Ok(encoder.finish().unwrap_or_default().into())
5896 }
5897
5898 pub async fn load_state(&self, mut state: SerializableState) -> Result<bool, BlockchainError> {
5900 let _mining_guard = self.mining.lock().await;
5901 let mut block_env = state.block.take();
5902 let mut selected_head = None;
5903 let mut selected_header = None;
5904 let mut checkpoint = None;
5905 let fork_head = self.get_fork().map(|f| (f.block_number(), f.block_hash(), f.timestamp()));
5906 if let Some(block) = &mut block_env {
5907 if self.is_tempo() && self.is_fork() && block.beneficiary.is_zero() {
5908 block.beneficiary = TIP_FEE_MANAGER_ADDRESS;
5909 }
5910 let best_number = state.best_block_number.unwrap_or(block.number.saturating_to());
5913 let (selected_best_number, selected_best_hash) = if let Some((number, hash, _)) =
5914 fork_head
5915 {
5916 trace!(target: "backend", state_block_number=?best_number, fork_block_number=?number);
5917 if best_number > number {
5921 (best_number, None)
5922 } else {
5923 (number, Some(hash))
5926 }
5927 } else {
5928 (best_number, None)
5929 };
5930
5931 let best_hash = if let Some(hash) = selected_best_hash {
5932 selected_header = state
5933 .blocks
5934 .iter()
5935 .rev()
5936 .find(|block| block.header.hash_slow() == hash)
5937 .map(|block| block.header.clone());
5938 hash
5939 } else if state.blocks.is_empty() {
5940 let spec_id = self.spec_id();
5941 let is_cancun = spec_id >= SpecId::CANCUN;
5942 let parent_hash = selected_best_number
5943 .checked_sub(1)
5944 .and_then(|number| self.blockchain.storage.read().hashes.get(&number).copied())
5945 .unwrap_or_default();
5946 let header = Header {
5947 parent_hash,
5948 beneficiary: block.beneficiary,
5949 difficulty: block.difficulty,
5950 number: selected_best_number,
5951 gas_limit: block.gas_limit,
5952 timestamp: block.timestamp.saturating_to(),
5953 mix_hash: block.prevrandao.unwrap_or_default(),
5954 base_fee_per_gas: (spec_id >= SpecId::LONDON).then_some(block.basefee),
5955 parent_beacon_block_root: is_cancun.then_some(Default::default()),
5956 blob_gas_used: is_cancun.then_some(0),
5957 excess_blob_gas: if is_cancun { block.blob_excess_gas() } else { None },
5958 withdrawals_root: (spec_id >= SpecId::SHANGHAI).then_some(EMPTY_WITHDRAWALS),
5959 requests_hash: (spec_id >= SpecId::PRAGUE).then_some(EMPTY_REQUESTS_HASH),
5960 ..Default::default()
5961 };
5962 let header = FoundryHeader::new(header, self.is_tempo());
5963 let best_hash = header.hash_slow();
5964 selected_header = Some(header.clone());
5965 checkpoint = Some(create_block(
5966 header,
5967 Vec::<MaybeImpersonatedTransaction<FoundryTxEnvelope>>::new(),
5968 ));
5969 warn!(
5970 target: "backend",
5971 block_number = selected_best_number,
5972 "state dump has no block history; created a synthetic checkpoint block"
5973 );
5974 best_hash
5975 } else if let Some(header) = state
5976 .blocks
5977 .iter()
5978 .rev()
5979 .find(|block| block.header.number() == selected_best_number)
5980 .map(|block| block.header.clone())
5981 {
5982 let best_hash = header.hash_slow();
5983 selected_header = Some(header);
5984 best_hash
5985 } else {
5986 return Err(BlockchainError::RpcError(RpcError::internal_error_with(format!(
5987 "Best hash not found for best number {selected_best_number}",
5988 ))));
5989 };
5990
5991 selected_head = Some((selected_best_number, best_hash));
5992 }
5993
5994 let canonical_timestamp = {
5997 let mut storage = self.blockchain.storage.write();
5998 storage.load_blocks(std::mem::take(&mut state.blocks));
5999 storage.load_transactions(std::mem::take(&mut state.transactions));
6000 if let Some(checkpoint) = checkpoint {
6001 storage.insert_block(checkpoint);
6002 }
6003 if let Some((number, hash)) = selected_head {
6004 storage.hashes.insert(number, hash);
6005 storage.best_number = number;
6006 storage.best_hash = hash;
6007 }
6008
6009 match fork_head {
6016 Some((_, fork_hash, fork_timestamp)) if storage.best_hash == fork_hash => {
6017 Some(fork_timestamp)
6018 }
6019 _ => storage.blocks.get(&storage.best_hash).map(|b| b.header.timestamp),
6020 }
6021 };
6022 if let Some(timestamp) = canonical_timestamp {
6023 self.time.reset(timestamp);
6024 }
6025
6026 if let Some(mut block) = block_env {
6027 if !is_arbitrum(self.chain_id().to())
6030 && let Some((number, _)) = selected_head
6031 {
6032 block.number = U256::from(number);
6033 }
6034 self.evm_env.write().block_env = block;
6035 }
6036
6037 if let Some(header) = selected_header.as_ref() {
6038 let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
6039 header.gas_used(),
6040 header.gas_limit(),
6041 header.base_fee_per_gas().unwrap_or_default(),
6042 );
6043 let next_block_excess_blob_gas =
6044 self.fees.blob_params().next_block_excess_blob_gas_osaka(
6045 header.excess_blob_gas().unwrap_or_default(),
6046 header.blob_gas_used().unwrap_or_default(),
6047 header.base_fee_per_gas().unwrap_or_default(),
6048 );
6049
6050 self.fees.set_base_fee(next_block_base_fee);
6052
6053 self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
6054 next_block_excess_blob_gas,
6055 get_blob_base_fee_update_fraction(
6056 self.evm_env.read().cfg_env.chain_id,
6057 header.timestamp,
6058 ),
6059 ));
6060 }
6061
6062 let historical_states = state.historical_states.take();
6063 if !self.db.write().await.load_state(state)? {
6064 return Err(RpcError::invalid_params(
6065 "Loading state not supported with the current configuration",
6066 )
6067 .into());
6068 }
6069
6070 let block_hashes = {
6075 let storage = self.blockchain.storage.read();
6076 let min_block = storage.best_number.saturating_sub(256);
6077 storage
6078 .hashes
6079 .iter()
6080 .filter(|(num, _)| (min_block..=storage.best_number).contains(*num))
6081 .map(|(&num, &hash)| (U256::from(num), hash))
6082 .collect()
6083 };
6084 self.db.write().await.set_block_hashes(block_hashes);
6085
6086 if let Some(historical_states) = historical_states {
6087 self.states.write().load_states(historical_states);
6088 }
6089
6090 Ok(true)
6091 }
6092
6093 pub async fn load_state_bytes(&self, buf: Bytes) -> Result<bool, BlockchainError> {
6095 let orig_buf = &buf.0[..];
6096 let mut decoder = GzDecoder::new(orig_buf);
6097 let mut decoded_data = Vec::new();
6098
6099 let state: SerializableState = serde_json::from_slice(if decoder.header().is_some() {
6100 decoder
6101 .read_to_end(decoded_data.as_mut())
6102 .map_err(|_| BlockchainError::FailedToDecodeStateDump)?;
6103 &decoded_data
6104 } else {
6105 &buf.0
6106 })
6107 .map_err(|_| BlockchainError::FailedToDecodeStateDump)?;
6108
6109 self.load_state(state).await
6110 }
6111}
6112
6113impl Backend<FoundryNetwork> {
6114 pub async fn call_bundle(
6116 &self,
6117 bundle: EthCallBundle,
6118 transactions: Vec<PendingTransaction<FoundryTxEnvelope>>,
6119 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
6120 ) -> Result<EthCallBundleResponse, BlockchainError> {
6121 let EthCallBundle {
6122 block_number,
6123 coinbase,
6124 timestamp,
6125 gas_limit,
6126 difficulty,
6127 base_fee,
6128 ..
6129 } = bundle;
6130
6131 let blob_gas_used = transactions
6132 .iter()
6133 .filter_map(|transaction| transaction.transaction.blob_gas_used())
6134 .sum::<u64>();
6135 let max_blob_gas = self.blob_params().max_blob_gas_per_block();
6136 if blob_gas_used > max_blob_gas {
6137 return Err(BlockchainError::RpcError(RpcError::invalid_params(format!(
6138 "blob gas usage exceeds the limit of {max_blob_gas} gas per block."
6139 ))));
6140 }
6141
6142 self.with_database_at(block_request, |state, mut block_env| {
6143 let state_block_number = block_env.number.to::<u64>();
6144 block_env.number = U256::from(block_number);
6145 block_env.timestamp = timestamp
6146 .map(U256::from)
6147 .unwrap_or_else(|| block_env.timestamp.saturating_add(U256::from(12)));
6148 if let Some(coinbase) = coinbase {
6149 block_env.beneficiary = coinbase;
6150 }
6151 if let Some(gas_limit) = gas_limit {
6152 block_env.gas_limit = gas_limit;
6153 }
6154 if let Some(difficulty) = difficulty {
6155 block_env.difficulty = difficulty;
6156 }
6157 if let Some(base_fee) = base_fee {
6158 block_env.basefee = base_fee.try_into().unwrap_or(u64::MAX);
6159 }
6160
6161 let mut evm_env = self.evm_env.read().clone();
6162 evm_env.block_env = block_env;
6163 let coinbase = evm_env.block_env.beneficiary;
6164 let base_fee = evm_env.block_env.basefee;
6165 let mut cache_db = CacheDB::new(state);
6166 let initial_coinbase = revm::DatabaseRef::basic_ref(&cache_db, coinbase)?
6167 .map(|account| account.balance)
6168 .unwrap_or_default();
6169 let mut coinbase_balance_before_tx = initial_coinbase;
6170 let mut coinbase_balance_after_tx = initial_coinbase;
6171 let mut total_gas_used = 0u64;
6172 let mut total_gas_fees = U256::ZERO;
6173 let mut bundle_hash = alloy_primitives::Keccak256::new();
6174 let mut results = Vec::with_capacity(transactions.len());
6175
6176 for transaction in transactions {
6177 let sender = *transaction.sender();
6178 let tx = transaction.transaction.into_inner();
6179 let tx_hash = tx.hash();
6180 bundle_hash.update(tx_hash);
6181
6182 let mut inspector = self.build_inspector();
6183 let (ResultAndState { result, state }, _) = self
6184 .transact_envelope_with_inspector_ref(
6185 &cache_db,
6186 &evm_env,
6187 &mut inspector,
6188 &tx,
6189 sender,
6190 )?;
6191
6192 let gas_price = tx.effective_tip_per_gas(base_fee).unwrap_or_default();
6193 let gas_used = result.tx_gas_used();
6194 let gas_fees = U256::from(gas_used) * U256::from(gas_price);
6195 total_gas_used += gas_used;
6196 total_gas_fees += gas_fees;
6197
6198 coinbase_balance_after_tx = state
6199 .get(&coinbase)
6200 .map(|account| account.info.balance)
6201 .unwrap_or(coinbase_balance_before_tx);
6202 let coinbase_diff =
6203 coinbase_balance_after_tx.saturating_sub(coinbase_balance_before_tx);
6204 let eth_sent_to_coinbase = coinbase_diff.saturating_sub(gas_fees);
6205 coinbase_balance_before_tx = coinbase_balance_after_tx;
6206
6207 let output = result.output().cloned().unwrap_or_default();
6208 let (value, revert) =
6209 if result.is_success() { (Some(output), None) } else { (None, Some(output)) };
6210
6211 results.push(EthCallBundleTransactionResult {
6212 coinbase_diff,
6213 eth_sent_to_coinbase,
6214 from_address: sender,
6215 gas_fees,
6216 gas_price: U256::from(gas_price),
6217 gas_used,
6218 to_address: tx.to(),
6219 tx_hash,
6220 value,
6221 revert,
6222 });
6223 cache_db.commit(state);
6224 }
6225
6226 let coinbase_diff = coinbase_balance_after_tx.saturating_sub(initial_coinbase);
6227 let eth_sent_to_coinbase = coinbase_diff.saturating_sub(total_gas_fees);
6228 let bundle_gas_price =
6229 coinbase_diff.checked_div(U256::from(total_gas_used)).unwrap_or_default();
6230
6231 Ok(EthCallBundleResponse {
6232 bundle_hash: bundle_hash.finalize(),
6233 bundle_gas_price,
6234 coinbase_diff,
6235 eth_sent_to_coinbase,
6236 gas_fees: total_gas_fees,
6237 results,
6238 state_block_number,
6239 total_gas_used,
6240 })
6241 })
6242 .await?
6243 }
6244
6245 pub async fn call_many(
6247 &self,
6248 bundles: Vec<Bundle<WithOtherFields<TransactionRequest>>>,
6249 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
6250 state_override: Option<alloy_rpc_types::state::StateOverride>,
6251 ) -> Result<Vec<Vec<EthCallResponse>>, BlockchainError> {
6252 if bundles.is_empty() {
6253 return Err(BlockchainError::RpcError(RpcError::invalid_params(
6254 "bundles are empty.".to_string(),
6255 )));
6256 }
6257
6258 self.with_database_at(block_request, |state, block_env| {
6259 let mut cache_db = CacheDB::new(state);
6260 if let Some(state_override) = state_override {
6261 apply_state_overrides(state_override, &mut cache_db)?;
6262 }
6263
6264 let mut results = Vec::with_capacity(bundles.len());
6265 for bundle in bundles {
6266 let Bundle { transactions, block_override } = bundle;
6267 let mut bundle_block_env = block_env.clone();
6268 if let Some(block_override) = block_override {
6269 cache_db.apply_block_overrides(block_override, &mut bundle_block_env);
6270 }
6271
6272 let mut bundle_results = Vec::with_capacity(transactions.len());
6273 for request in transactions {
6274 let fee_details = FeeDetails::new(
6275 request.gas_price,
6276 request.max_fee_per_gas,
6277 request.max_priority_fee_per_gas,
6278 request.max_fee_per_blob_gas,
6279 )?
6280 .or_zero_fees();
6281 let PreparedCall { evm_env, mut tx_env, simulated_tempo_tx } = self
6282 .prepare_call_env(
6283 &cache_db,
6284 request,
6285 fee_details,
6286 bundle_block_env.clone(),
6287 )?;
6288 apply_tempo_envelope_identity(&mut tx_env, simulated_tempo_tx.as_ref());
6289
6290 let mut inspector = self.build_inspector();
6291 let ResultAndState { result, state } = self.transact_call_with_inspector_ref(
6292 &cache_db,
6293 &evm_env,
6294 &mut inspector,
6295 tx_env,
6296 )?;
6297
6298 let output = result.output().cloned().unwrap_or_default();
6299 let response = if result.is_success() {
6300 EthCallResponse { value: Some(output), error: None }
6301 } else {
6302 let error = RevertDecoder::new()
6303 .maybe_decode(&output, None)
6304 .unwrap_or_else(|| "execution failed".to_string());
6305 EthCallResponse { value: None, error: Some(error) }
6306 };
6307
6308 cache_db.commit(state);
6309 bundle_results.push(response);
6310 }
6311
6312 results.push(bundle_results);
6313 }
6314
6315 Ok(results)
6316 })
6317 .await?
6318 }
6319
6320 pub async fn simulate(
6322 &self,
6323 request: SimulatePayload,
6324 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
6325 block_interval: u64,
6326 ) -> Result<Vec<SimulatedBlock<AnyRpcBlock>>, BlockchainError> {
6327 self.simulate_raw(
6328 preserve_simulation_request_fields(request),
6329 block_request,
6330 block_interval,
6331 )
6332 .await
6333 }
6334
6335 pub(crate) async fn simulate_raw(
6337 &self,
6338 request: SimulatePayload<WithOtherFields<TransactionRequest>>,
6339 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
6340 block_interval: u64,
6341 ) -> Result<Vec<SimulatedBlock<AnyRpcBlock>>, BlockchainError> {
6342 let simulate_at = |state: Box<dyn MaybeFullDatabase + '_>,
6343 base_block_env: BlockEnv,
6344 base_number,
6345 base_timestamp,
6346 base_hash,
6347 base_fee,
6348 base_base_fee_per_gas,
6349 base_excess_blob_gas,
6350 base_blob_gas_used| {
6351 let SimulatePayload {
6352 block_state_calls,
6353 trace_transfers,
6354 validation,
6355 return_full_transactions,
6356 } = request;
6357 let block_state_calls = sanitize_simulation_blocks(
6358 block_state_calls,
6359 base_number,
6360 base_timestamp,
6361 block_interval,
6362 )?;
6363 let mut cache_db = CacheDB::new(state);
6364 cache_db.cache.block_hashes.insert(U256::from(base_number), base_hash);
6365 let mut block_res = Vec::with_capacity(block_state_calls.len());
6366 let mut parent_hash = base_hash;
6367 let mut next_base_fee = base_fee;
6368 let mut inherited_block_env = base_block_env;
6369 let (is_cancun, is_amsterdam, tx_gas_limit_cap) = {
6370 let cfg_env = &self.evm_env.read().cfg_env;
6371 (
6372 cfg_env.spec >= SpecId::CANCUN,
6373 cfg_env.spec >= SpecId::AMSTERDAM,
6374 cfg_env.tx_gas_limit_cap(),
6375 )
6376 };
6377 let mut parent_base_fee_per_gas = base_base_fee_per_gas;
6378 let mut parent_excess_blob_gas = base_excess_blob_gas;
6379 let mut parent_blob_gas_used = base_blob_gas_used;
6380 let mut rpc_gas_budget = SIMULATE_GAS_CAP;
6381
6382 for block in block_state_calls {
6384 let SimBlock { block_overrides, state_overrides, calls } = block;
6385 let mut block_env = inherited_block_env.clone();
6386 let overridden_beacon_root =
6387 block_overrides.as_ref().and_then(|overrides| overrides.beacon_root);
6388 let block_timestamp = block_overrides
6389 .as_ref()
6390 .and_then(|overrides| overrides.time)
6391 .unwrap_or_else(|| block_env.timestamp.saturating_to());
6392 let blob_params = self.simulation_blob_params_at_timestamp(block_timestamp);
6393 if is_cancun {
6394 let excess_blob_gas = blob_params.next_block_excess_blob_gas_osaka(
6395 parent_excess_blob_gas,
6396 parent_blob_gas_used,
6397 parent_base_fee_per_gas,
6398 );
6399 block_env.set_blob_excess_gas_and_price(
6400 excess_blob_gas,
6401 blob_params.update_fraction as u64,
6402 );
6403 } else {
6404 block_env.blob_excess_gas_and_price = None;
6405 }
6406 block_env.basefee = if validation { next_base_fee } else { 0 };
6407 block_env.prevrandao = Some(B256::ZERO);
6408 let mut call_res = Vec::with_capacity(calls.len());
6409 let mut log_index = 0;
6410 let mut cumulative_gas_used = 0;
6411 let mut block_regular_gas_used = 0;
6412 let mut block_state_gas_used = 0;
6413 let mut block_blob_gas_used = 0u64;
6414 let mut transactions = Vec::with_capacity(calls.len());
6415 let mut transaction_envelopes = Vec::with_capacity(calls.len());
6416 let mut receipts = Vec::with_capacity(calls.len());
6417 let overridden_block_hashes = block_overrides
6418 .as_ref()
6419 .and_then(|overrides| overrides.block_hash.as_ref())
6420 .map(|overrides| {
6421 overrides
6422 .keys()
6423 .map(|number| {
6424 let number = U256::from(*number);
6425 (number, cache_db.cache.block_hashes.get(&number).copied())
6426 })
6427 .collect::<Vec<_>>()
6428 })
6429 .unwrap_or_default();
6430
6431 if let Some(block_overrides) = block_overrides {
6432 cache_db.apply_block_overrides(block_overrides, &mut block_env);
6433 }
6434 let simulation_evm_env =
6435 EvmEnv::new(self.evm_env.read().cfg_env.clone(), block_env.clone());
6436 let spec_id = *simulation_evm_env.spec_id();
6437 let ethereum_transitions = self
6438 .ethereum_block_transitions(self.hardfork(), None, BlockExecutionKind::Complete)
6439 .map(|mut transitions| {
6440 transitions.parent_beacon_block_root = (transitions.hardfork
6441 >= EthereumHardfork::Cancun)
6442 .then_some(overridden_beacon_root.unwrap_or_default());
6443 transitions
6444 });
6445 let precompile_overrides = self.simulation_precompile_overrides(
6446 state_overrides.as_ref(),
6447 &simulation_evm_env,
6448 )?;
6449
6450 if let Some(mut state_overrides) = state_overrides {
6453 state_overrides.retain(|_, account| {
6454 account.balance.is_some()
6455 || account.nonce.is_some()
6456 || account.code.is_some()
6457 || account.state.is_some()
6458 || account
6459 .state_diff
6460 .as_ref()
6461 .is_some_and(|state_diff| !state_diff.is_empty())
6462 });
6463 let previously_deleted = previously_deleted_accounts(
6464 &cache_db.cache.accounts,
6465 state_overrides.keys().copied(),
6466 );
6467 apply_state_overrides(state_overrides, &mut cache_db)?;
6468 preserve_deleted_storage(&mut cache_db.cache.accounts, previously_deleted);
6469 }
6470
6471 if let Some(transitions) = ethereum_transitions {
6472 self.apply_simulation_pre_execution_changes(
6473 &mut cache_db,
6474 &simulation_evm_env,
6475 parent_hash,
6476 transitions,
6477 )?;
6478 }
6479
6480 for (req_idx, mut request) in calls.into_iter().enumerate() {
6482 let classified_request = self.parse_transaction_request(request.clone())?;
6483 let is_ethereum_request = classified_request.is_ethereum();
6484 let mut parsed_request = self.is_tempo().then_some(classified_request);
6485 if is_ethereum_request {
6486 request.populate_blob_hashes();
6487 let preferred_type = request.preferred_type();
6488 request.transaction_type = Some(preferred_type as u8);
6489 request.trim_conflicting_keys();
6490 request.populate_blob_hashes();
6491 }
6492 let request_blob_gas_used = if is_ethereum_request {
6493 u64::try_from(request.blob_versioned_hashes.as_ref().map_or(0, Vec::len))
6494 .unwrap_or(u64::MAX)
6495 .saturating_mul(DATA_GAS_PER_BLOB)
6496 } else {
6497 0
6498 };
6499 let max_blob_gas = blob_params.max_blob_gas_per_block();
6500 if block_blob_gas_used.saturating_add(request_blob_gas_used) > max_blob_gas {
6501 return Err(BlockchainError::RpcError(RpcError::invalid_params(format!(
6502 "blob gas usage exceeds the limit of {max_blob_gas} gas per block."
6503 ))));
6504 }
6505 block_blob_gas_used = block_blob_gas_used.saturating_add(request_blob_gas_used);
6506
6507 let inner = request.as_ref();
6508 let remaining_regular_gas =
6509 block_env.gas_limit.saturating_sub(block_regular_gas_used);
6510 let remaining_state_gas =
6511 block_env.gas_limit.saturating_sub(block_state_gas_used);
6512 let remaining_gas = if is_amsterdam {
6513 remaining_regular_gas.min(remaining_state_gas)
6514 } else {
6515 block_env.gas_limit.saturating_sub(cumulative_gas_used)
6516 };
6517 let requested_gas = inner.gas.unwrap_or(remaining_gas);
6518 let exceeds_gas_limit = if is_amsterdam {
6519 let requested_regular_gas = requested_gas.min(tx_gas_limit_cap);
6520 requested_regular_gas > remaining_regular_gas
6521 || requested_gas > remaining_state_gas
6522 } else {
6523 requested_gas > remaining_gas
6524 };
6525 if exceeds_gas_limit {
6526 return Err(BlockchainError::RpcError(RpcError {
6527 code: ErrorCode::ServerError(-38015),
6528 message: format!(
6529 "block gas limit exceeded: remaining {remaining_gas}, requested {requested_gas}"
6530 )
6531 .into(),
6532 data: None,
6533 }));
6534 }
6535 let execution_gas_limit = requested_gas.min(rpc_gas_budget);
6536 let preserve_signed_gas = matches!(
6537 &parsed_request,
6538 Some(FoundryTransactionRequest::Tempo(request))
6539 if request.fee_payer_signature.is_some()
6540 );
6541 request.gas = Some(execution_gas_limit);
6542 if !preserve_signed_gas && let Some(parsed_request) = &mut parsed_request {
6543 parsed_request.as_mut().gas = Some(execution_gas_limit);
6544 }
6545
6546 let caller = request.from.unwrap_or_default();
6547 let caller_nonce = RevmDatabase::basic(&mut cache_db, caller)?
6548 .map(|account| account.nonce)
6549 .unwrap_or_default();
6550 let tempo_nonce_key =
6551 parsed_request.as_ref().and_then(|request| match request {
6552 FoundryTransactionRequest::Tempo(request) => request.nonce_key,
6553 _ => None,
6554 });
6555 if request.nonce.is_none() {
6556 let nonce = tempo_nonce_key.map_or(Ok(caller_nonce), |nonce_key| {
6557 tempo_nonce(&cache_db, caller, nonce_key)
6558 })?;
6559 request.nonce = Some(nonce);
6560 if let Some(parsed_request) = &mut parsed_request {
6561 parsed_request.as_mut().nonce = Some(nonce);
6562 }
6563 }
6564
6565 if is_ethereum_request {
6566 let mut canonical_request =
6567 FoundryTransactionRequest::Ethereum(request.inner.clone());
6568 canonical_request.prep_for_submission();
6569 request.inner = canonical_request.as_ref().clone();
6570 if let Some(parsed_request) = &mut parsed_request {
6571 *parsed_request = canonical_request;
6572 }
6573 }
6574
6575 let fee_details = FeeDetails::new(
6576 request.gas_price,
6577 request.max_fee_per_gas,
6578 request.max_priority_fee_per_gas,
6579 request.max_fee_per_blob_gas,
6580 )?
6581 .or_zero_fees();
6582
6583 let PreparedCall { mut evm_env, mut tx_env, simulated_tempo_tx } =
6584 if let Some(parsed_request) = parsed_request {
6585 self.prepare_typed_call_env(
6586 &cache_db,
6587 parsed_request,
6588 fee_details,
6589 block_env.clone(),
6590 )?
6591 } else {
6592 self.prepare_call_env(
6593 &cache_db,
6594 request.clone(),
6595 fee_details,
6596 block_env.clone(),
6597 )?
6598 };
6599 tx_env.base_mut().gas_limit = execution_gas_limit;
6600 apply_tempo_envelope_identity(&mut tx_env, simulated_tempo_tx.as_ref());
6601 if !validation
6602 && tempo_nonce_key.is_none_or(|key| key.is_zero())
6603 && request.nonce == Some(u64::MAX)
6604 {
6605 tx_env.base_mut().nonce = 0;
6606 }
6607 let uses_protocol_call_nonce = tx_env.uses_protocol_call_nonce();
6608 let simulated_envelope = simulated_tempo_tx.map(FoundryTxEnvelope::Tempo);
6609
6610 if is_amsterdam {
6611 let spec = evm_env.cfg_env.spec;
6613 evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec);
6614 }
6615
6616 evm_env.cfg_env.disable_eip3607 = true;
6618
6619 if validation {
6620 evm_env.cfg_env.disable_nonce_check = false;
6621 evm_env.cfg_env.disable_base_fee = false;
6622 evm_env.cfg_env.disable_block_gas_limit = false;
6623 }
6624
6625 let mut inspector = self.build_inspector();
6626
6627 inspector = inspector.with_simulation_logs(trace_transfers);
6629 trace!(target: "backend", env=?evm_env, spec=?evm_env.spec_id(),"simulate evm env");
6630 let execution_result = match tx_env {
6631 CallTxEnv::Eth(tx_env)
6632 if !validation
6633 && tx_env.tx_type == 3
6634 && tx_env.max_fee_per_blob_gas == 0 =>
6635 {
6636 self.transact_eth_simulation_with_inspector_ref(
6637 &cache_db,
6638 &evm_env,
6639 &mut inspector,
6640 tx_env,
6641 &precompile_overrides,
6642 )
6643 }
6644 tx_env if precompile_overrides.moves.is_empty() => self
6645 .transact_call_with_inspector_ref(
6646 &cache_db,
6647 &evm_env,
6648 &mut inspector,
6649 tx_env,
6650 ),
6651 tx_env => self.transact_eth_with_inspector_ref_and_precompile_overrides(
6652 &cache_db,
6653 &evm_env,
6654 &mut inspector,
6655 tx_env.into_base(),
6656 &precompile_overrides,
6657 ),
6658 };
6659 let ResultAndState { result, mut state } = match execution_result {
6660 Err(BlockchainError::InvalidTransaction(error)) => {
6661 return Err(simulate_transaction_error(error));
6662 }
6663 result => result?,
6664 };
6665 if !validation
6666 && caller_nonce == u64::MAX
6667 && uses_protocol_call_nonce
6668 && let Some(account) = state.get_mut(&caller)
6669 {
6670 account.info.nonce = 0;
6671 }
6672 trace!(target: "backend", ?result, ?request, "simulate call");
6673
6674 let canonical_logs = result.clone().into_logs();
6675 let (response_logs, attempted_log_count) = inspector
6676 .take_simulation_logs(&canonical_logs, result.is_success())
6677 .expect("simulation log collector is installed");
6678 inspector.print_logs();
6679 if self.print_traces {
6680 inspector.into_print_traces(self.call_trace_decoder.clone());
6681 }
6682
6683 let previously_deleted = previously_deleted_accounts(
6687 &cache_db.cache.accounts,
6688 state.keys().copied(),
6689 );
6690
6691 cache_db.commit(state);
6693 preserve_deleted_storage(&mut cache_db.cache.accounts, previously_deleted);
6694 let post_state = if spec_id < SpecId::BYZANTIUM {
6695 let accounts =
6696 cache_db.maybe_full_db().ok_or(BlockchainError::DataUnavailable)?;
6697 Some(state_root(&accounts))
6698 } else {
6699 None
6700 };
6701 rpc_gas_budget = rpc_gas_budget.saturating_sub(result.tx_gas_used());
6702 cumulative_gas_used = cumulative_gas_used.saturating_add(result.tx_gas_used());
6703 block_regular_gas_used = block_regular_gas_used
6704 .saturating_add(result.gas().block_regular_gas_used());
6705 block_state_gas_used =
6706 block_state_gas_used.saturating_add(result.gas().block_state_gas_used());
6707
6708 let from = caller;
6710 request.sidecar = None;
6711 let tx = if let Some(envelope) = simulated_envelope {
6712 MaybeImpersonatedTransaction::impersonated(envelope, from)
6713 } else {
6714 if request.to.is_none() {
6715 request.to = Some(TxKind::Create);
6716 }
6717 let mut request = self.parse_transaction_request(request)?;
6718 request.prep_for_submission();
6719 let typed_tx = request.build_unsigned().map_err(|e| {
6720 BlockchainError::InvalidTransactionRequest(e.to_string())
6721 })?;
6722 MaybeImpersonatedTransaction::impersonated(
6723 typed_tx.into_impersonated(),
6724 from,
6725 )
6726 };
6727 let tx_hash = tx.as_ref().hash();
6728 #[cfg(feature = "optimism")]
6729 let (deposit_nonce, deposit_receipt_version) =
6730 if matches!(tx.as_ref(), FoundryTxEnvelope::Deposit(_)) {
6731 let hardfork = OpHardfork::from(self.hardfork());
6732 (
6733 (hardfork >= OpHardfork::Regolith).then_some(caller_nonce),
6734 (hardfork >= OpHardfork::Canyon).then_some(1),
6735 )
6736 } else {
6737 (None, None)
6738 };
6739 #[cfg(not(feature = "optimism"))]
6740 let (deposit_nonce, deposit_receipt_version) = (None, None);
6741 receipts.push(FoundryReceiptBuilder::build_simulated_receipt(
6742 tx.as_ref().tx_type(),
6743 &result,
6744 canonical_logs.clone(),
6745 cumulative_gas_used,
6746 post_state,
6747 deposit_nonce,
6748 deposit_receipt_version,
6749 ));
6750 transaction_envelopes.push(tx.as_ref().clone());
6751 let rpc_tx =
6752 transaction_build(Some(tx_hash), tx, None, None, Some(block_env.basefee));
6753 transactions.push(rpc_tx);
6754
6755 let return_data = if result.is_success() {
6756 result.output().cloned().unwrap_or_default()
6757 } else {
6758 Bytes::new()
6759 };
6760 let sim_res = SimCallResult {
6761 return_data,
6762 gas_used: result.tx_gas_used(),
6763 max_used_gas: Some(
6764 result.gas().total_gas_spent().max(result.gas().floor_gas()),
6765 ),
6766 status: result.is_success(),
6767 error: match &result {
6768 ExecutionResult::Success { .. } => None,
6769 ExecutionResult::Revert { output, .. } => {
6770 let message = RevertDecoder::new()
6771 .maybe_decode(output, None)
6772 .map(|reason| format!("execution reverted: {reason}"))
6773 .unwrap_or_else(|| "execution reverted".to_string());
6774 Some(SimulateError {
6775 code: SimulateError::EXECUTION_REVERTED_CODE,
6776 message,
6777 data: Some(output.clone()),
6778 })
6779 }
6780 ExecutionResult::Halt { reason, .. } => Some(SimulateError {
6781 code: SimulateError::VM_EXECUTION_ERROR_CODE,
6782 message: if matches!(reason, HaltReason::OutOfGas(_)) {
6783 "out of gas".to_string()
6784 } else {
6785 format!("vm execution error: {reason}")
6786 },
6787 data: None,
6788 }),
6789 },
6790 logs: response_logs
6791 .into_iter()
6792 .map(|(idx, log)| Log {
6793 inner: log,
6794 block_number: Some(block_env.number.saturating_to()),
6795 block_timestamp: Some(block_env.timestamp.saturating_to()),
6796 transaction_index: Some(req_idx as u64),
6797 log_index: Some(idx + log_index),
6798 removed: false,
6799
6800 block_hash: None,
6801 transaction_hash: Some(tx_hash),
6802 })
6803 .collect(),
6804 };
6805 log_index += attempted_log_count;
6806 call_res.push(sim_res);
6807 }
6808
6809 for (number, hash) in overridden_block_hashes {
6810 if let Some(hash) = hash {
6811 cache_db.cache.block_hashes.insert(number, hash);
6812 } else {
6813 cache_db.cache.block_hashes.remove(&number);
6814 }
6815 }
6816
6817 let gas_used = if is_amsterdam {
6818 block_regular_gas_used.max(block_state_gas_used)
6819 } else {
6820 cumulative_gas_used
6821 };
6822 let requests = if let Some(transitions) = ethereum_transitions {
6823 self.apply_simulation_post_execution_changes(
6824 &mut cache_db,
6825 &simulation_evm_env,
6826 transitions,
6827 &receipts,
6828 )?
6829 } else {
6830 Default::default()
6831 };
6832
6833 let accounts = cache_db.maybe_full_db().ok_or(BlockchainError::DataUnavailable)?;
6836 let state_root = state_root(&accounts);
6837 let header = Header {
6838 logs_bloom: receipts.iter().fold(Bloom::ZERO, |mut bloom, receipt| {
6839 bloom.accrue_bloom(receipt.logs_bloom());
6840 bloom
6841 }),
6842 transactions_root: calculate_transaction_root(&transaction_envelopes),
6843 receipts_root: calculate_receipt_root(&receipts),
6844 parent_hash,
6845 beneficiary: block_env.beneficiary,
6846 state_root,
6847 difficulty: block_env.difficulty,
6848 number: block_env.number.saturating_to(),
6849 gas_limit: block_env.gas_limit,
6850 gas_used,
6851 timestamp: block_env.timestamp.saturating_to(),
6852 extra_data: Default::default(),
6853 mix_hash: block_env.prevrandao.unwrap_or_default(),
6854 nonce: Default::default(),
6855 base_fee_per_gas: (spec_id >= SpecId::LONDON).then_some(block_env.basefee),
6856 withdrawals_root: (spec_id >= SpecId::SHANGHAI).then_some(EMPTY_WITHDRAWALS),
6857 blob_gas_used: is_cancun.then_some(block_blob_gas_used),
6858 excess_blob_gas: if is_cancun { block_env.blob_excess_gas() } else { None },
6859 parent_beacon_block_root: ethereum_transitions.and_then(|transitions| {
6860 (transitions.hardfork >= EthereumHardfork::Cancun)
6861 .then_some(transitions.parent_beacon_block_root.unwrap_or_default())
6862 }),
6863 requests_hash: ethereum_transitions.and_then(|transitions| {
6864 (transitions.hardfork >= EthereumHardfork::Prague)
6865 .then(|| requests.requests_hash())
6866 }),
6867 ..Default::default()
6868 };
6869 let block_hash = header.hash_slow();
6870 for (transaction_index, transaction) in transactions.iter_mut().enumerate() {
6871 transaction.block_hash = Some(block_hash);
6872 transaction.block_number = Some(header.number);
6873 transaction.transaction_index = Some(transaction_index as u64);
6874 transaction.block_timestamp = Some(header.timestamp);
6875 }
6876 let mut block = alloy_rpc_types::Block {
6877 header: AnyRpcHeader {
6878 hash: block_hash,
6879 inner: header.into(),
6880 total_difficulty: None,
6881 size: None,
6882 },
6883 uncles: vec![],
6884 transactions: BlockTransactions::Full(transactions),
6885 withdrawals: (spec_id >= SpecId::SHANGHAI).then_some(Default::default()),
6886 };
6887
6888 if !return_full_transactions {
6889 block.transactions.convert_to_hashes();
6890 }
6891
6892 for res in &mut call_res {
6893 res.logs.iter_mut().for_each(|log| {
6894 log.block_hash = Some(block.header.hash);
6895 });
6896 }
6897
6898 let simulated_block = SimulatedBlock {
6899 inner: AnyRpcBlock::new(WithOtherFields::new(block)),
6900 calls: call_res,
6901 };
6902
6903 parent_hash = block_hash;
6904 cache_db.cache.block_hashes.insert(block_env.number, block_hash);
6905 inherited_block_env.beneficiary = block_env.beneficiary;
6906 inherited_block_env.difficulty = block_env.difficulty;
6907 inherited_block_env.gas_limit = block_env.gas_limit;
6908 let header = &simulated_block.inner.header;
6910 next_base_fee = self.fees.calculate_next_block_base_fee_per_gas(
6911 header.gas_used(),
6912 header.gas_limit(),
6913 header.base_fee_per_gas().unwrap_or_default(),
6914 );
6915 parent_base_fee_per_gas = header.base_fee_per_gas().unwrap_or_default();
6916 parent_excess_blob_gas = header.excess_blob_gas().unwrap_or_default();
6917 parent_blob_gas_used = header.blob_gas_used().unwrap_or_default();
6918
6919 block_res.push(simulated_block);
6920 }
6921
6922 Ok(block_res)
6923 };
6924
6925 match block_request {
6926 Some(BlockRequest::Pending(pool_transactions)) => {
6927 self.with_pending_block(pool_transactions, |state, block| {
6928 let header = &block.block.header;
6929 let base_fee = self.fees.calculate_next_block_base_fee_per_gas(
6930 header.gas_used(),
6931 header.gas_limit(),
6932 header.base_fee_per_gas().unwrap_or_default(),
6933 );
6934 simulate_at(
6935 state,
6936 block_env_from_header(header),
6937 header.number(),
6938 header.timestamp(),
6939 header.hash_slow(),
6940 base_fee,
6941 header.base_fee_per_gas().unwrap_or_default(),
6942 header.excess_blob_gas().unwrap_or_default(),
6943 header.blob_gas_used().unwrap_or_default(),
6944 )
6945 })
6946 .await
6947 }
6948 block_request => {
6949 let base_block_number = match block_request.as_ref() {
6950 Some(BlockRequest::Number(number)) => BlockNumber::Number(*number),
6951 Some(BlockRequest::Pending(_)) => unreachable!(),
6952 None => BlockNumber::Latest,
6953 };
6954 let base_block = self
6955 .block_by_number(base_block_number)
6956 .await?
6957 .ok_or(BlockchainError::BlockNotFound)?;
6958 let base_number = base_block.header.number();
6959 let base_timestamp = base_block.header.timestamp();
6960 let base_hash = base_block.header.hash;
6961 let base_fee = self.fees.calculate_next_block_base_fee_per_gas(
6962 base_block.header.gas_used(),
6963 base_block.header.gas_limit(),
6964 base_block.header.base_fee_per_gas().unwrap_or_default(),
6965 );
6966 self.with_database_at(block_request, |state, block_env| {
6967 simulate_at(
6968 state,
6969 block_env,
6970 base_number,
6971 base_timestamp,
6972 base_hash,
6973 base_fee,
6974 base_block.header.base_fee_per_gas().unwrap_or_default(),
6975 base_block.header.excess_blob_gas().unwrap_or_default(),
6976 base_block.header.blob_gas_used().unwrap_or_default(),
6977 )
6978 })
6979 .await?
6980 }
6981 }
6982 }
6983
6984 pub fn get_blob_by_tx_hash(&self, hash: B256) -> Result<Option<Vec<alloy_consensus::Blob>>> {
6985 let storage = self.blockchain.storage.read();
6986 Ok(storage.transactions.get(&hash).and_then(|mined| {
6987 storage
6988 .blocks
6989 .get(&mined.block_hash)?
6990 .body
6991 .transactions
6992 .get(mined.info.transaction_index as usize)?
6993 .as_ref()
6994 .sidecar()
6995 .map(|sidecar| sidecar.sidecar.blobs().to_vec())
6996 }))
6997 }
6998
6999 pub async fn set_fee_token(&self, user: Address, token: Address) -> DatabaseResult<()> {
7001 self.with_tempo_storage(|| {
7002 let mut fee_manager = TipFeeManager::new();
7003 fee_manager
7004 .set_user_token(user, IFeeManager::setUserTokenCall { token })
7005 .map_err(tempo_db_err)
7006 })
7007 .await
7008 }
7009
7010 pub async fn set_validator_fee_token(
7012 &self,
7013 validator: Address,
7014 token: Address,
7015 ) -> DatabaseResult<()> {
7016 self.with_tempo_storage(|| {
7017 let mut fee_manager = TipFeeManager::new();
7018 fee_manager
7020 .set_validator_token(
7021 validator,
7022 IFeeManager::setValidatorTokenCall { token },
7023 Address::ZERO,
7024 )
7025 .map_err(tempo_db_err)
7026 })
7027 .await
7028 }
7029
7030 pub async fn set_fee_amm_liquidity(
7032 &self,
7033 user_token: Address,
7034 validator_token: Address,
7035 amount: U256,
7036 ) -> DatabaseResult<()> {
7037 let admin = Address::repeat_byte(0x11);
7039 self.with_tempo_storage(|| {
7040 for &token_address in &[user_token, validator_token] {
7043 let mut token = TIP20Token::from_address(token_address).map_err(tempo_db_err)?;
7044 token.grant_role_internal(admin, *ISSUER_ROLE).map_err(tempo_db_err)?;
7045 token.mint(admin, ITIP20::mintCall { to: admin, amount }).map_err(tempo_db_err)?;
7046 }
7047 let mut fee_manager = TipFeeManager::new();
7048 fee_manager
7049 .mint(admin, user_token, validator_token, amount, admin)
7050 .map_err(tempo_db_err)?;
7051 Ok(())
7052 })
7053 .await
7054 }
7055
7056 pub async fn set_tip20_balance(
7058 &self,
7059 address: Address,
7060 token_address: Address,
7061 balance: U256,
7062 ) -> DatabaseResult<()> {
7063 if self.try_set_tip20_balance(address, token_address, balance).await? {
7064 return Ok(());
7065 }
7066
7067 Err(tempo_db_err(format!("address {token_address} is not a deployed TIP-20 token")))
7068 }
7069
7070 pub async fn try_set_tip20_balance(
7072 &self,
7073 address: Address,
7074 token_address: Address,
7075 balance: U256,
7076 ) -> DatabaseResult<bool> {
7077 self.with_tempo_storage(|| {
7078 if !TIP20Factory::new().is_tip20(token_address).map_err(tempo_db_err)? {
7079 return Ok(false);
7080 }
7081
7082 let mut token = TIP20Token::from_address(token_address).map_err(tempo_db_err)?;
7083 token.balances[address].write(balance).map_err(tempo_db_err)?;
7084 Ok(true)
7085 })
7086 .await
7087 }
7088
7089 async fn with_tempo_storage<R>(&self, f: impl FnOnce() -> R) -> R {
7092 let hardfork = self.hardfork();
7093 let (chain_id, timestamp, block_number) = {
7095 let env = self.evm_env.read();
7096 (
7097 env.cfg_env.chain_id,
7098 U256::from(env.block_env.timestamp),
7099 env.block_env.number.to::<u64>(),
7100 )
7101 };
7102 let mut db = self.db.write().await;
7103 let mut storage = AnvilStorageProvider::new(
7104 &mut **db,
7105 chain_id,
7106 timestamp,
7107 block_number,
7108 hardfork.into(),
7109 );
7110 StorageCtx::enter(&mut storage, f)
7111 }
7112}
7113
7114fn tempo_db_err<E: std::fmt::Display>(e: E) -> DatabaseError {
7116 DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}")))
7117}
7118
7119fn get_pool_transactions_nonce(
7121 pool_transactions: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
7122 address: Address,
7123) -> Option<u64> {
7124 if let Some(highest_nonce) = pool_transactions
7125 .iter()
7126 .filter(|tx| {
7127 *tx.pending_transaction.sender() == address
7128 && !tx.pending_transaction.transaction.as_ref().has_nonzero_tempo_nonce_key()
7129 })
7130 .map(|tx| tx.pending_transaction.nonce())
7131 .max()
7132 {
7133 let tx_count = highest_nonce.saturating_add(1);
7134 return Some(tx_count);
7135 }
7136 None
7137}
7138
7139#[async_trait::async_trait]
7140impl<N: Network> TransactionValidator<FoundryTxEnvelope> for Backend<N>
7141where
7142 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
7143{
7144 async fn validate_pool_transaction(
7145 &self,
7146 tx: &PendingTransaction<FoundryTxEnvelope>,
7147 ) -> Result<(), BlockchainError> {
7148 let address = *tx.sender();
7149 let account = self.get_account(address).await?;
7150 let evm_env = self.next_evm_env();
7151
7152 if let FoundryTxEnvelope::Tempo(aa_tx) = tx.transaction.as_ref() {
7154 let tempo_tx = aa_tx.tx();
7155 let current_time = evm_env.block_env.timestamp.saturating_to::<u64>();
7156
7157 const AA_VALID_BEFORE_MIN_SECS: u64 = 3;
7159 if let Some(valid_before) = tempo_tx.valid_before.map(|v| v.get()) {
7160 let min_allowed = current_time.saturating_add(AA_VALID_BEFORE_MIN_SECS);
7161 if valid_before <= min_allowed {
7162 return Err(InvalidTransactionError::TempoValidBeforeExpired {
7163 valid_before,
7164 min_allowed,
7165 }
7166 .into());
7167 }
7168 }
7169
7170 const AA_VALID_AFTER_MAX_SECS: u64 = 3600;
7172 if let Some(valid_after) = tempo_tx.valid_after.map(|v| v.get()) {
7173 let max_allowed = current_time.saturating_add(AA_VALID_AFTER_MAX_SECS);
7174 if valid_after > max_allowed {
7175 return Err(InvalidTransactionError::TempoValidAfterTooFar {
7176 valid_after,
7177 max_allowed,
7178 }
7179 .into());
7180 }
7181 }
7182
7183 let fee_payer = tempo_tx.recover_fee_payer(address).unwrap_or(address);
7185 let fee_token =
7186 tempo_tx.fee_token.unwrap_or(foundry_evm::core::tempo::PATH_USD_ADDRESS);
7187
7188 let required_wei =
7190 U256::from(tempo_tx.gas_limit).saturating_mul(U256::from(tempo_tx.max_fee_per_gas));
7191 let required = required_wei / U256::from(10u64.pow(12));
7192
7193 let balance = self.get_fee_token_balance(fee_token, fee_payer).await?;
7194 if balance < required {
7195 return Err(InvalidTransactionError::TempoInsufficientFeeTokenBalance {
7196 balance,
7197 required,
7198 }
7199 .into());
7200 }
7201 }
7202
7203 Ok(self.validate_pool_transaction_for(tx, &account, &evm_env)?)
7204 }
7205
7206 fn validate_pool_transaction_for(
7207 &self,
7208 pending: &PendingTransaction<FoundryTxEnvelope>,
7209 account: &AccountInfo,
7210 evm_env: &EvmEnv,
7211 ) -> Result<(), InvalidTransactionError> {
7212 let tx = &pending.transaction;
7213
7214 if let Some(tx_chain_id) = tx.chain_id() {
7215 let chain_id = self.chain_id();
7216 if chain_id.to::<u64>() != tx_chain_id {
7217 if let FoundryTxEnvelope::Legacy(tx) = tx.as_ref() {
7218 if evm_env.cfg_env.spec >= SpecId::SPURIOUS_DRAGON && tx.chain_id().is_none() {
7220 debug!(target: "backend", ?chain_id, ?tx_chain_id, "incompatible EIP155-based V");
7221 return Err(InvalidTransactionError::IncompatibleEIP155);
7222 }
7223 } else {
7224 debug!(target: "backend", ?chain_id, ?tx_chain_id, "invalid chain id");
7225 return Err(InvalidTransactionError::InvalidChainId);
7226 }
7227 }
7228 }
7229
7230 if self.is_tempo() && !tx.value().is_zero() {
7232 warn!(target: "backend", "[{:?}] native value transfer not allowed in Tempo mode", tx.hash());
7233 return Err(InvalidTransactionError::TempoNativeValueTransfer);
7234 }
7235
7236 if self.is_tempo_hardfork_active(TempoHardfork::T5)
7238 && let FoundryTxEnvelope::Tempo(aa_tx) = tx.as_ref()
7239 {
7240 const MAX_TEMPO_AUTHORIZATIONS: usize = 16;
7241 let auth_count = aa_tx.tx().tempo_authorization_list.len();
7242 if auth_count > MAX_TEMPO_AUTHORIZATIONS {
7243 warn!(target: "backend", "[{:?}] Tempo tx has too many authorizations: {}", tx.hash(), auth_count);
7244 return Err(InvalidTransactionError::TempoTooManyAuthorizations {
7245 count: auth_count,
7246 max: MAX_TEMPO_AUTHORIZATIONS,
7247 });
7248 }
7249 }
7250
7251 #[cfg(feature = "optimism")]
7253 let is_deposit_tx = pending.transaction.as_ref().is_deposit();
7254 #[cfg(not(feature = "optimism"))]
7255 let is_deposit_tx = false;
7256 let is_tempo_tx = pending.transaction.as_ref().is_tempo();
7257 let nonce = tx.nonce();
7258 if nonce < account.nonce && !is_deposit_tx && !is_tempo_tx {
7259 debug!(target: "backend", "[{:?}] nonce too low", tx.hash());
7260 return Err(InvalidTransactionError::NonceTooLow);
7261 }
7262
7263 if evm_env.cfg_env.spec >= SpecId::CANCUN && tx.is_eip4844() {
7265 let blob_tx = match tx.as_ref() {
7267 FoundryTxEnvelope::Eip4844(tx) => tx.tx(),
7268 _ => unreachable!(),
7269 };
7270
7271 let blob_count = blob_tx.tx().blob_versioned_hashes.len();
7272
7273 if blob_count == 0 {
7275 return Err(InvalidTransactionError::NoBlobHashes);
7276 }
7277
7278 let max_blobs_per_tx = self.blob_params().max_blobs_per_tx as usize;
7280 if blob_count > max_blobs_per_tx {
7281 return Err(InvalidTransactionError::TooManyBlobs(blob_count, max_blobs_per_tx));
7282 }
7283
7284 if !self.skip_blob_validation(Some(*pending.sender()))
7286 && let Err(err) = blob_tx.validate(EnvKzgSettings::default().get())
7287 {
7288 return Err(InvalidTransactionError::BlobTransactionValidationError(err));
7289 }
7290 }
7291
7292 if evm_env.cfg_env.spec >= SpecId::SHANGHAI && tx.kind() == TxKind::Create {
7294 let max_initcode_size = evm_env
7295 .cfg_env
7296 .limit_contract_code_size
7297 .map(|limit| limit.saturating_mul(2))
7298 .unwrap_or(revm::primitives::eip3860::MAX_INITCODE_SIZE);
7299 if tx.input().len() > max_initcode_size {
7300 return Err(InvalidTransactionError::MaxInitCodeSizeExceeded);
7301 }
7302 }
7303
7304 if !self.disable_pool_balance_checks {
7306 if tx.gas_limit() < MIN_TRANSACTION_GAS as u64 {
7308 debug!(target: "backend", "[{:?}] gas too low", tx.hash());
7309 return Err(InvalidTransactionError::GasTooLow);
7310 }
7311
7312 if !evm_env.cfg_env.disable_block_gas_limit
7314 && tx.gas_limit() > evm_env.block_env.gas_limit
7315 {
7316 debug!(target: "backend", "[{:?}] gas too high", tx.hash());
7317 return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
7318 detail: String::from("tx.gas_limit > env.block.gas_limit"),
7319 }));
7320 }
7321
7322 if evm_env.cfg_env.tx_gas_limit_cap.is_none()
7324 && tx.gas_limit() > evm_env.cfg_env().tx_gas_limit_cap()
7325 {
7326 debug!(target: "backend", "[{:?}] gas too high", tx.hash());
7327 return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
7328 detail: String::from("tx.gas_limit > env.cfg.tx_gas_limit_cap"),
7329 }));
7330 }
7331
7332 if evm_env.cfg_env.spec >= SpecId::LONDON {
7334 if tx.max_fee_per_gas() < evm_env.block_env.basefee.into() && !is_deposit_tx {
7335 debug!(target: "backend", "max fee per gas={}, too low, block basefee={}", tx.max_fee_per_gas(), evm_env.block_env.basefee);
7336 return Err(InvalidTransactionError::FeeCapTooLow);
7337 }
7338
7339 if !evm_env.cfg_env.disable_priority_fee_check
7340 && let (Some(max_priority_fee_per_gas), max_fee_per_gas) =
7341 (tx.as_ref().max_priority_fee_per_gas(), tx.as_ref().max_fee_per_gas())
7342 && max_priority_fee_per_gas > max_fee_per_gas
7343 {
7344 debug!(target: "backend", "max priority fee per gas={}, too high, max fee per gas={}", max_priority_fee_per_gas, max_fee_per_gas);
7345 return Err(InvalidTransactionError::TipAboveFeeCap);
7346 }
7347 }
7348
7349 if evm_env.cfg_env.spec >= SpecId::CANCUN
7351 && tx.is_eip4844()
7352 && let Some(max_fee_per_blob_gas) = tx.max_fee_per_blob_gas()
7353 && let Some(blob_gas_and_price) = &evm_env.block_env.blob_excess_gas_and_price
7354 && max_fee_per_blob_gas < blob_gas_and_price.blob_gasprice
7355 {
7356 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);
7357 return Err(InvalidTransactionError::BlobFeeCapTooLow(
7358 max_fee_per_blob_gas,
7359 blob_gas_and_price.blob_gasprice,
7360 ));
7361 }
7362
7363 let max_cost =
7364 (tx.gas_limit() as u128).saturating_mul(tx.max_fee_per_gas()).saturating_add(
7365 tx.blob_gas_used()
7366 .map(|g| g as u128)
7367 .unwrap_or(0)
7368 .mul(tx.max_fee_per_blob_gas().unwrap_or(0)),
7369 );
7370 let value = tx.value();
7371 match tx.as_ref() {
7372 #[cfg(feature = "optimism")]
7373 FoundryTxEnvelope::Deposit(deposit_tx) => {
7374 if value > account.balance + U256::from(deposit_tx.mint) {
7380 debug!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance + U256::from(deposit_tx.mint), value, *pending.sender());
7381 return Err(InvalidTransactionError::InsufficientFunds);
7382 }
7383 }
7384 FoundryTxEnvelope::Tempo(_) => {
7385 }
7388 _ => {
7389 let req_funds =
7391 max_cost.checked_add(value.saturating_to()).ok_or_else(|| {
7392 debug!(target: "backend", "[{:?}] cost too high", tx.hash());
7393 InvalidTransactionError::InsufficientFunds
7394 })?;
7395 if account.balance < U256::from(req_funds) {
7396 debug!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender());
7397 return Err(InvalidTransactionError::InsufficientFunds);
7398 }
7399 }
7400 }
7401 }
7402 Ok(())
7403 }
7404
7405 fn validate_for(
7406 &self,
7407 tx: &PendingTransaction<FoundryTxEnvelope>,
7408 account: &AccountInfo,
7409 evm_env: &EvmEnv,
7410 ) -> Result<(), InvalidTransactionError> {
7411 self.validate_pool_transaction_for(tx, account, evm_env)?;
7412 if tx.nonce() > account.nonce {
7413 return Err(InvalidTransactionError::NonceTooHigh);
7414 }
7415 Ok(())
7416 }
7417}
7418
7419fn rehash<T>(signed: Signed<T>, hash: B256) -> Signed<T>
7421where
7422 T: alloy_consensus::transaction::RlpEcdsaEncodableTx,
7423{
7424 let (t, sig, _) = signed.into_parts();
7425 Signed::new_unchecked(t, sig, hash)
7426}
7427
7428pub fn transaction_build(
7430 tx_hash: Option<B256>,
7431 eth_transaction: MaybeImpersonatedTransaction<FoundryTxEnvelope>,
7432 block: Option<&Block>,
7433 info: Option<TransactionInfo>,
7434 base_fee: Option<u64>,
7435) -> AnyRpcTransaction {
7436 #[cfg(feature = "optimism")]
7437 if let FoundryTxEnvelope::Deposit(deposit_tx) = eth_transaction.as_ref() {
7438 let dep_tx = deposit_tx;
7439
7440 let ser = serde_json::to_value(dep_tx).expect("could not serialize TxDeposit");
7441 let maybe_deposit_fields = OtherFields::try_from(ser);
7442
7443 match maybe_deposit_fields {
7444 Ok(mut fields) => {
7445 fields.insert("v".to_string(), serde_json::to_value("0x0").unwrap());
7448 fields.insert("r".to_string(), serde_json::to_value(B256::ZERO).unwrap());
7449 fields.insert(String::from("s"), serde_json::to_value(B256::ZERO).unwrap());
7450 fields.insert(String::from("nonce"), serde_json::to_value("0x0").unwrap());
7451
7452 let inner = UnknownTypedTransaction {
7453 ty: AnyTxType(DEPOSIT_TX_TYPE_ID),
7454 fields,
7455 memo: Default::default(),
7456 };
7457
7458 let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
7459 hash: tx_hash.unwrap_or_else(|| eth_transaction.hash()),
7460 inner,
7461 });
7462
7463 let tx = Transaction {
7464 inner: Recovered::new_unchecked(envelope, deposit_tx.from),
7465 block_hash: block
7466 .as_ref()
7467 .map(|block| B256::from(keccak256(alloy_rlp::encode(&block.header)))),
7468 block_number: block.as_ref().map(|block| block.header.number()),
7469 transaction_index: info.as_ref().map(|info| info.transaction_index),
7470 effective_gas_price: None,
7471 block_timestamp: block.as_ref().map(|block| block.header.timestamp()),
7472 };
7473
7474 return AnyRpcTransaction::from(WithOtherFields::new(tx));
7475 }
7476 Err(_) => {
7477 error!(target: "backend", "failed to serialize deposit transaction");
7478 }
7479 }
7480 }
7481
7482 if let FoundryTxEnvelope::Tempo(tempo_tx) = eth_transaction.as_ref() {
7483 let from = eth_transaction.recover().unwrap_or_default();
7484 let ser = serde_json::to_value(tempo_tx).expect("could not serialize Tempo transaction");
7485 let maybe_tempo_fields = OtherFields::try_from(ser);
7486
7487 match maybe_tempo_fields {
7488 Ok(fields) => {
7489 let inner = UnknownTypedTransaction {
7490 ty: AnyTxType(TEMPO_TX_TYPE_ID),
7491 fields,
7492 memo: Default::default(),
7493 };
7494
7495 let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
7496 hash: tx_hash.unwrap_or_else(|| eth_transaction.hash()),
7497 inner,
7498 });
7499
7500 let tx = Transaction {
7501 inner: Recovered::new_unchecked(envelope, from),
7502 block_hash: block.as_ref().map(|block| block.header.hash_slow()),
7503 block_number: block.as_ref().map(|block| block.header.number()),
7504 transaction_index: info.as_ref().map(|info| info.transaction_index),
7505 effective_gas_price: None,
7506 block_timestamp: block.as_ref().map(|block| block.header.timestamp()),
7507 };
7508
7509 return AnyRpcTransaction::from(WithOtherFields::new(tx));
7510 }
7511 Err(_) => {
7512 error!(target: "backend", "failed to serialize tempo transaction");
7513 }
7514 }
7515 }
7516
7517 let from = eth_transaction.recover().unwrap_or_default();
7518 let effective_gas_price = eth_transaction.effective_gas_price(base_fee);
7519
7520 let hash = tx_hash.unwrap_or_else(|| eth_transaction.hash());
7526
7527 let eth_envelope = FoundryTxEnvelope::from(eth_transaction)
7528 .try_into_eth()
7529 .expect("non-standard transactions are handled above");
7530
7531 let envelope = match eth_envelope {
7532 TxEnvelope::Legacy(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(rehash(s, hash))),
7533 TxEnvelope::Eip1559(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip1559(rehash(s, hash))),
7534 TxEnvelope::Eip2930(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip2930(rehash(s, hash))),
7535 TxEnvelope::Eip4844(s) => {
7536 let s = if block.is_some() { s.map(TxEip4844Variant::drop_sidecar) } else { s };
7537 AnyTxEnvelope::Ethereum(TxEnvelope::Eip4844(rehash(s, hash)))
7538 }
7539 TxEnvelope::Eip7702(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip7702(rehash(s, hash))),
7540 };
7541
7542 let tx = Transaction {
7543 inner: Recovered::new_unchecked(envelope, from),
7544 block_hash: block.as_ref().map(|block| block.header.hash_slow()),
7545 block_number: block.as_ref().map(|block| block.header.number()),
7546 transaction_index: info.as_ref().map(|info| info.transaction_index),
7547 effective_gas_price: Some(effective_gas_price),
7549 block_timestamp: block.as_ref().map(|block| block.header.timestamp()),
7550 };
7551 AnyRpcTransaction::from(WithOtherFields::new(tx))
7552}
7553
7554pub fn prove_storage(
7560 storage: &alloy_primitives::map::U256Map<U256>,
7561 keys: &[B256],
7562) -> (B256, Vec<Vec<Bytes>>) {
7563 let keys: Vec<_> = keys.iter().map(|key| Nibbles::unpack(keccak256(key))).collect();
7564
7565 let mut builder = HashBuilder::default().with_proof_retainer(ProofRetainer::new(keys.clone()));
7566
7567 for (key, value) in trie_storage(storage) {
7568 builder.add_leaf(key, &value);
7569 }
7570
7571 let root = builder.root();
7572
7573 let mut proofs = Vec::new();
7574 let all_proof_nodes = builder.take_proof_nodes();
7575
7576 for proof_key in keys {
7577 let matching_proof_nodes =
7580 all_proof_nodes.matching_nodes_sorted(&proof_key).into_iter().map(|(_, node)| node);
7581 proofs.push(matching_proof_nodes.collect());
7582 }
7583
7584 (root, proofs)
7585}
7586
7587pub fn is_arbitrum(chain_id: u64) -> bool {
7588 if let Ok(chain) = NamedChain::try_from(chain_id) {
7589 return chain.is_arbitrum();
7590 }
7591 false
7592}
7593
7594fn commit_cache(db: &mut dyn Db, cache: revm::database::Cache) -> Result<(), BlockchainError> {
7596 let revm::database::Cache { accounts, contracts, .. } = cache;
7597 let mut changes = EvmState::default();
7598 for (address, db_account) in accounts {
7599 if db_account.account_state == AccountState::None {
7600 continue;
7601 }
7602
7603 let DbAccount { mut info, account_state, storage } = db_account;
7604 if account_state == AccountState::NotExisting && db.basic(address)?.is_none() {
7607 continue;
7608 }
7609 if info.code.is_none() {
7610 info.code = contracts.get(&info.code_hash).cloned();
7611 }
7612 let mut account = Account::from(info);
7613 account.mark_touch();
7614 match account_state {
7615 AccountState::NotExisting => account.mark_selfdestruct(),
7616 AccountState::StorageCleared => account.mark_created(),
7617 AccountState::Touched => {}
7618 AccountState::None => unreachable!(),
7619 }
7620 for (slot, value) in storage {
7621 let original = if account_state == AccountState::StorageCleared {
7622 U256::ZERO
7623 } else {
7624 db.storage(address, slot)?
7625 };
7626 account
7627 .storage
7628 .insert(slot, EvmStorageSlot::new_changed(original, value, TransactionId::ZERO));
7629 }
7630 changes.insert(address, account);
7631 }
7632 db.commit(changes);
7633 Ok(())
7634}
7635
7636fn simulate_rpc_error(code: i64, message: impl Into<String>) -> BlockchainError {
7637 BlockchainError::RpcError(RpcError {
7638 code: ErrorCode::from(code),
7639 message: message.into().into(),
7640 data: None,
7641 })
7642}
7643
7644fn previously_deleted_accounts(
7645 accounts: &AddressMap<DbAccount>,
7646 addresses: impl IntoIterator<Item = Address>,
7647) -> Vec<Address> {
7648 addresses
7649 .into_iter()
7650 .filter(|address| {
7651 accounts
7652 .get(address)
7653 .is_some_and(|account| account.account_state == AccountState::NotExisting)
7654 })
7655 .collect()
7656}
7657
7658fn preserve_deleted_storage(
7659 accounts: &mut AddressMap<DbAccount>,
7660 previously_deleted: Vec<Address>,
7661) {
7662 for address in previously_deleted {
7663 if let Some(account) = accounts.get_mut(&address)
7664 && account.account_state != AccountState::NotExisting
7665 {
7666 account.account_state = AccountState::StorageCleared;
7667 }
7668 }
7669}
7670
7671fn simulate_transaction_error(error: InvalidTransactionError) -> BlockchainError {
7672 let code = match &error {
7673 InvalidTransactionError::NonceTooLow => -38010,
7674 InvalidTransactionError::NonceTooHigh => -38011,
7675 InvalidTransactionError::NonceMaxValue => -32603,
7676 InvalidTransactionError::FeeCapTooLow => -38012,
7677 InvalidTransactionError::GasTooLow | InvalidTransactionError::GasTooHigh(_) => -38013,
7678 InvalidTransactionError::InsufficientFunds
7679 | InvalidTransactionError::InsufficientFundsForTransfer => -38014,
7680 _ => return BlockchainError::InvalidTransaction(error),
7681 };
7682
7683 simulate_rpc_error(code, format!("err: {error}"))
7684}
7685
7686pub(in crate::eth) fn sanitize_simulation_blocks<T>(
7687 blocks: Vec<SimBlock<T>>,
7688 base_number: u64,
7689 base_timestamp: u64,
7690 block_interval: u64,
7691) -> Result<Vec<SimBlock<T>>, BlockchainError> {
7692 let block_interval = block_interval.max(1);
7693 let mut sanitized = Vec::with_capacity(blocks.len());
7694 let mut previous_number = base_number;
7695 let mut previous_timestamp = base_timestamp;
7696
7697 for mut block in blocks {
7698 let mut overrides = block.block_overrides.take().unwrap_or_default();
7699 let default_number = previous_number.checked_add(1).ok_or_else(|| {
7700 simulate_rpc_error(-38020, "block number overflow while constructing sequence")
7701 })?;
7702 let number =
7703 overrides.number.map(|number| number.saturating_to()).unwrap_or(default_number);
7704
7705 if number <= previous_number {
7706 return Err(simulate_rpc_error(
7707 -38020,
7708 format!("block numbers must be in order: {number} <= {previous_number}"),
7709 ));
7710 }
7711
7712 let gap = number - previous_number - 1;
7713 let remaining = MAX_SIMULATE_BLOCKS as usize - sanitized.len();
7714 if gap as usize >= remaining {
7715 return Err(simulate_rpc_error(-38026, "too many blocks"));
7716 }
7717
7718 for offset in 0..gap {
7719 let timestamp = previous_timestamp.checked_add(block_interval).ok_or_else(|| {
7720 simulate_rpc_error(-38021, "block timestamp overflow while filling number gap")
7721 })?;
7722 sanitized.push(SimBlock {
7723 block_overrides: Some(BlockOverrides {
7724 number: Some(U256::from(default_number + offset)),
7725 time: Some(timestamp),
7726 ..Default::default()
7727 }),
7728 state_overrides: None,
7729 calls: Vec::new(),
7730 });
7731 previous_timestamp = timestamp;
7732 }
7733
7734 let timestamp = match overrides.time {
7735 Some(timestamp) => timestamp,
7736 None => previous_timestamp.checked_add(block_interval).ok_or_else(|| {
7737 simulate_rpc_error(-38021, "block timestamp overflow while constructing sequence")
7738 })?,
7739 };
7740 if timestamp <= previous_timestamp {
7741 return Err(simulate_rpc_error(
7742 -38021,
7743 format!("block timestamps must be in order: {timestamp} <= {previous_timestamp}"),
7744 ));
7745 }
7746
7747 overrides.number = Some(U256::from(number));
7748 overrides.time = Some(timestamp);
7749 block.block_overrides = Some(overrides);
7750 sanitized.push(block);
7751 previous_number = number;
7752 previous_timestamp = timestamp;
7753 }
7754
7755 Ok(sanitized)
7756}
7757
7758fn unpack_execution_result<H: IntoInstructionResult>(
7760 result: ExecutionResult<H>,
7761) -> (InstructionResult, u64, Option<Output>, Vec<revm::primitives::Log>) {
7762 match result {
7763 ExecutionResult::Success { reason, gas, output, logs, .. } => {
7764 (reason.into(), gas.tx_gas_used(), Some(output), logs)
7765 }
7766 ExecutionResult::Revert { gas, output, logs, .. } => {
7767 (InstructionResult::Revert, gas.tx_gas_used(), Some(Output::Call(output)), logs)
7768 }
7769 ExecutionResult::Halt { reason, gas, logs, .. } => {
7770 (reason.into_instruction_result(), gas.tx_gas_used(), None, logs)
7771 }
7772 }
7773}
7774
7775pub use foundry_evm::core::evm::IntoInstructionResult;
7780
7781#[cfg(test)]
7782mod tests {
7783 use crate::{NodeConfig, spawn};
7784
7785 #[tokio::test]
7786 async fn test_deterministic_block_mining() {
7787 let genesis_timestamp = 1743944919u64;
7789
7790 let config_a = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
7792 let config_b = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
7793
7794 let (api_a, _handle_a) = spawn(config_a).await;
7795 let (api_b, _handle_b) = spawn(config_b).await;
7796
7797 let outcome_a_1 = api_a.backend.mine_block(vec![]).await.unwrap();
7799 let outcome_b_1 = api_b.backend.mine_block(vec![]).await.unwrap();
7800
7801 assert_eq!(outcome_a_1.block_number, outcome_b_1.block_number);
7803
7804 let block_a_1 =
7806 api_a.block_by_number(outcome_a_1.block_number.into()).await.unwrap().unwrap();
7807 let block_b_1 =
7808 api_b.block_by_number(outcome_b_1.block_number.into()).await.unwrap().unwrap();
7809
7810 assert_eq!(
7812 block_a_1.header.hash, block_b_1.header.hash,
7813 "Block hashes should be deterministic. Got {} vs {}",
7814 block_a_1.header.hash, block_b_1.header.hash
7815 );
7816
7817 let outcome_a_2 = api_a.backend.mine_block(vec![]).await.unwrap();
7819 let outcome_b_2 = api_b.backend.mine_block(vec![]).await.unwrap();
7820
7821 let block_a_2 =
7822 api_a.block_by_number(outcome_a_2.block_number.into()).await.unwrap().unwrap();
7823 let block_b_2 =
7824 api_b.block_by_number(outcome_b_2.block_number.into()).await.unwrap().unwrap();
7825
7826 assert_eq!(
7827 block_a_2.header.hash, block_b_2.header.hash,
7828 "Second block hashes should also be deterministic. Got {} vs {}",
7829 block_a_2.header.hash, block_b_2.header.hash
7830 );
7831
7832 assert_ne!(
7834 block_a_1.header.hash, block_a_2.header.hash,
7835 "Different blocks should have different hashes"
7836 );
7837 }
7838}