1use self::state::trie_storage;
3
4use crate::{
5 ForkChoice, NodeConfig, PrecompileFactory,
6 config::PruneStateHistoryConfig,
7 eth::{
8 backend::{
9 cheats::{CheatEcrecover, CheatsManager},
10 db::{AnvilCacheDB, Db, MaybeFullDatabase, SerializableState, StateDb},
11 executor::{
12 AnvilBlockExecutor, ExecutedPoolTransactions, PoolTxGasConfig,
13 execute_pool_transactions,
14 },
15 fork::ClientFork,
16 genesis::GenesisConfig,
17 mem::{
18 state::{storage_root, trie_accounts},
19 storage::MinedTransactionReceipt,
20 },
21 notifications::{ChainNotification, ChainNotifications, NewBlockNotification},
22 tempo::AnvilStorageProvider,
23 time::{TimeManager, utc_from_secs},
24 validate::TransactionValidator,
25 },
26 error::{BlockchainError, ErrDetail, InvalidTransactionError},
27 fees::{FeeDetails, FeeManager, MIN_SUGGESTED_PRIORITY_FEE},
28 macros::node_info,
29 pool::transactions::PoolTransaction,
30 sign::build_impersonated,
31 },
32 mem::{
33 inspector::{AnvilInspector, InspectorTxConfig},
34 storage::{BlockchainStorage, InMemoryBlockStates, MinedBlockOutcome},
35 },
36};
37use alloy_chains::NamedChain;
38use alloy_consensus::{
39 Blob, BlockHeader, EnvKzgSettings, Header, Signed, Transaction as TransactionTrait,
40 TrieAccount, TxEip4844Variant, TxEnvelope, TxReceipt, Typed2718,
41 constants::EMPTY_WITHDRAWALS,
42 proofs::{calculate_receipt_root, calculate_transaction_root},
43 transaction::Recovered,
44};
45use alloy_eips::{
46 BlockNumHash, Encodable2718, eip2935, eip4844::kzg_to_versioned_hash,
47 eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams, eip7910::SystemContract,
48};
49use alloy_evm::{
50 Database, EthEvmFactory, Evm, EvmEnv, EvmFactory, FromTxWithEncoded,
51 block::{BlockExecutionResult, BlockExecutor, StateDB},
52 eth::EthEvmContext,
53 overrides::{OverrideBlockHashes, apply_state_overrides},
54 precompiles::{DynPrecompile, Precompile, PrecompilesMap},
55};
56use alloy_network::{
57 AnyHeader, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope, AnyTxType, Network,
58 NetworkTransactionBuilder, ReceiptResponse, UnknownTxEnvelope, UnknownTypedTransaction,
59};
60#[cfg(feature = "optimism")]
61use alloy_op_evm::{OpEvmContext, OpEvmFactory, OpTx};
62use alloy_primitives::{
63 Address, B256, Bloom, Bytes, TxHash, TxKind, U64, U256, hex, keccak256, logs_bloom,
64 map::{AddressMap, HashMap, HashSet},
65};
66use alloy_rlp::Decodable;
67use alloy_rpc_types::{
68 AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, BlockOverrides,
69 BlockTransactions, EIP1186AccountProofResponse as AccountProof,
70 EIP1186StorageProof as StorageProof, Filter, Header as AlloyHeader, Index, Log, Transaction,
71 TransactionReceipt,
72 anvil::Forking,
73 request::TransactionRequest,
74 serde_helpers::JsonStorageKey,
75 simulate::{SimBlock, SimCallResult, SimulateError, SimulatePayload, SimulatedBlock},
76 state::{EvmOverrides, StateOverride},
77 trace::{
78 filter::TraceFilter,
79 geth::{
80 CallConfig, FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerConfig,
81 GethDebugTracerType, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace,
82 NoopFrame, TraceResult,
83 },
84 opcode::{BlockOpcodeGas, TransactionOpcodeGas},
85 parity::{
86 LocalizedTransactionTrace, TraceResults, TraceResultsWithTransactionHash, TraceType,
87 },
88 },
89};
90use alloy_rpc_types_eth::{AccountInfo as RpcAccountInfo, Bundle, EthCallResponse};
91use alloy_rpc_types_mev::{EthCallBundle, EthCallBundleResponse, EthCallBundleTransactionResult};
92use alloy_serde::{OtherFields, WithOtherFields};
93use alloy_trie::{HashBuilder, Nibbles, proof::ProofRetainer};
94use anvil_core::eth::{
95 block::{Block, BlockInfo, canonical_block, create_block},
96 transaction::{MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo},
97};
98use anvil_rpc::error::{ErrorCode, RpcError};
99use chrono::Datelike;
100use eyre::{Context, Result};
101use flate2::{Compression, read::GzDecoder, write::GzEncoder};
102use foundry_evm::{
103 backend::{DatabaseError, DatabaseResult, RevertStateSnapshotAction},
104 constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE,
105 core::{
106 evm::{EvmEnvFor, TempoEvmNetwork},
107 precompiles::EC_RECOVER,
108 },
109 decode::RevertDecoder,
110 hardfork::FoundryHardfork,
111 inspectors::AccessListInspector,
112 traces::{
113 CallTraceDecoder, FourByteInspector, GethTraceBuilder, TracingInspector,
114 TracingInspectorConfig,
115 },
116 utils::{
117 block_env_from_header, get_blob_base_fee_update_fraction,
118 get_blob_base_fee_update_fraction_by_spec_id, get_blob_params_by_spec_id,
119 },
120};
121use foundry_evm_networks::{NetworkConfigs, arbitrum};
122#[cfg(feature = "optimism")]
123use foundry_primitives::get_deposit_tx_parts;
124use foundry_primitives::{
125 FoundryHeader, FoundryNetwork, FoundryReceiptEnvelope, FoundryTransactionRequest,
126 FoundryTxEnvelope, FoundryTxReceipt,
127};
128use futures::channel::mpsc::{UnboundedSender, unbounded};
129#[cfg(feature = "optimism")]
130use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, OpTransaction as OpTransactionTrait};
131#[cfg(feature = "optimism")]
132use op_revm::{OpTransaction, transaction::deposit::DepositTransactionParts};
133
134#[cfg(feature = "optimism")]
141type OpCallDepositInfo = DepositTransactionParts;
142#[cfg(not(feature = "optimism"))]
143#[derive(Default, Clone, Debug)]
144struct OpCallDepositInfo;
145
146const SIMULATE_GAS_CAP: u64 = 50_000_000;
148
149#[cfg(feature = "optimism")]
153pub trait BackendInspector<DB: Database>:
154 Inspector<EthEvmContext<DB>> + Inspector<OpEvmContext<DB>> + Inspector<TempoContext<DB>>
155{
156}
157#[cfg(feature = "optimism")]
158impl<DB: Database, T> BackendInspector<DB> for T where
159 T: Inspector<EthEvmContext<DB>> + Inspector<OpEvmContext<DB>> + Inspector<TempoContext<DB>>
160{
161}
162#[cfg(not(feature = "optimism"))]
163pub trait BackendInspector<DB: Database>:
164 Inspector<EthEvmContext<DB>> + Inspector<TempoContext<DB>>
165{
166}
167#[cfg(not(feature = "optimism"))]
168impl<DB: Database, T> BackendInspector<DB> for T where
169 T: Inspector<EthEvmContext<DB>> + Inspector<TempoContext<DB>>
170{
171}
172use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
173use revm::{
174 Database as RevmDatabase, DatabaseCommit, Inspector,
175 context::{Block as RevmBlock, BlockEnv, Cfg, TxEnv},
176 context_interface::{
177 block::BlobExcessGasAndPrice,
178 result::{ExecutionResult, HaltReason, Output, ResultAndState},
179 },
180 database::{CacheDB, DbAccount, WrapDatabaseRef},
181 interpreter::InstructionResult,
182 precompile::{PrecompileSpecId, Precompiles},
183 primitives::{KECCAK_EMPTY, hardfork::SpecId},
184 state::AccountInfo,
185};
186use revm_inspectors::opcode::OpcodeGasInspector;
187use std::{
188 collections::BTreeMap,
189 fmt::{self, Debug},
190 io::{Read, Write},
191 ops::Mul,
192 path::PathBuf,
193 sync::Arc,
194 time::Duration,
195};
196use storage::{Blockchain, DEFAULT_HISTORY_LIMIT, MinedTransaction};
197use tempo_evm::evm::TempoEvmFactory;
198use tempo_hardfork::TempoHardfork;
199use tempo_precompiles::{
200 TIP_FEE_MANAGER_ADDRESS, extend_tempo_precompiles,
201 storage::{Handler, StorageActions, StorageCtx},
202 tip_fee_manager::{IFeeManager, TipFeeManager},
203 tip20::{ISSUER_ROLE, ITIP20, TIP20Token},
204 tip20_factory::TIP20Factory,
205};
206use tempo_primitives::TEMPO_TX_TYPE_ID;
207use tempo_revm::{
208 TempoBatchCallEnv, TempoBlockEnv, TempoHaltReason, TempoTxEnv, evm::TempoContext,
209 gas_params::tempo_gas_params,
210};
211use tokio::sync::RwLock as AsyncRwLock;
212
213pub mod cache;
214pub mod fork_db;
215pub mod in_memory_db;
216pub mod inspector;
217#[cfg(feature = "optimism")]
218pub mod optimism;
219pub mod state;
220pub mod storage;
221
222pub trait DatabaseRef: revm::DatabaseRef<Error = DatabaseError> + Debug {}
226impl<T> DatabaseRef for T where T: revm::DatabaseRef<Error = DatabaseError> + Debug {}
227impl DatabaseRef for dyn crate::eth::backend::db::Db {}
228
229pub const MIN_TRANSACTION_GAS: u128 = 21000;
231pub const MIN_CREATE_GAS: u128 = 53000;
233
234fn call_config_from_tracer_config(
235 tracer_config: GethDebugTracerConfig,
236) -> Result<CallConfig, serde_json::Error> {
237 let mut tracer_config = tracer_config.into_json();
238 if let Some(config) = tracer_config.as_object_mut()
239 && !config.contains_key("onlyTopCall")
240 && let Some(only_top_level_call) = config.remove("onlyTopLevelCall")
241 {
242 config.insert("onlyTopCall".to_string(), only_top_level_call);
243 }
244
245 GethDebugTracerConfig(tracer_config).into_call_config()
246}
247
248pub type State = foundry_evm::utils::StateChangeset;
249
250pub enum BlockRequest<T> {
252 Pending(Vec<Arc<PoolTransaction<T>>>),
253 Number(u64),
254}
255
256impl<T> fmt::Debug for BlockRequest<T> {
257 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258 match self {
259 Self::Pending(txs) => f.debug_tuple("Pending").field(&txs.len()).finish(),
260 Self::Number(n) => f.debug_tuple("Number").field(n).finish(),
261 }
262 }
263}
264
265impl<T> BlockRequest<T> {
266 pub const fn block_number(&self) -> BlockNumber {
267 match *self {
268 Self::Pending(_) => BlockNumber::Pending,
269 Self::Number(n) => BlockNumber::Number(n),
270 }
271 }
272}
273
274pub struct Backend<N: Network> {
276 db: Arc<AsyncRwLock<Box<dyn Db>>>,
296 blockchain: Blockchain<N>,
298 states: Arc<RwLock<InMemoryBlockStates>>,
300 evm_env: Arc<RwLock<EvmEnv>>,
302 networks: NetworkConfigs,
304 hardfork: FoundryHardfork,
306 fork: Arc<RwLock<Option<ClientFork>>>,
308 time: TimeManager,
310 cheats: CheatsManager,
312 fees: FeeManager,
314 genesis: GenesisConfig,
316 new_block_listeners: Arc<Mutex<Vec<UnboundedSender<ChainNotification>>>>,
319 active_state_snapshots: Arc<Mutex<HashMap<U256, (u64, B256)>>>,
321 enable_steps_tracing: bool,
322 print_logs: bool,
323 print_traces: bool,
324 call_trace_decoder: Arc<CallTraceDecoder>,
326 prune_state_history_config: PruneStateHistoryConfig,
328 transaction_block_keeper: Option<usize>,
330 pub(crate) node_config: Arc<AsyncRwLock<NodeConfig>>,
331 slots_in_an_epoch: u64,
333 precompile_factory: Option<Arc<dyn PrecompileFactory>>,
335 mining: Arc<tokio::sync::Mutex<()>>,
337 disable_pool_balance_checks: bool,
339}
340
341impl<N: Network> Clone for Backend<N> {
342 fn clone(&self) -> Self {
343 Self {
344 db: self.db.clone(),
345 blockchain: self.blockchain.clone(),
346 states: self.states.clone(),
347 evm_env: self.evm_env.clone(),
348 networks: self.networks,
349 hardfork: self.hardfork,
350 fork: self.fork.clone(),
351 time: self.time.clone(),
352 cheats: self.cheats.clone(),
353 fees: self.fees.clone(),
354 genesis: self.genesis.clone(),
355 new_block_listeners: self.new_block_listeners.clone(),
356 active_state_snapshots: self.active_state_snapshots.clone(),
357 enable_steps_tracing: self.enable_steps_tracing,
358 print_logs: self.print_logs,
359 print_traces: self.print_traces,
360 call_trace_decoder: self.call_trace_decoder.clone(),
361 prune_state_history_config: self.prune_state_history_config,
362 transaction_block_keeper: self.transaction_block_keeper,
363 node_config: self.node_config.clone(),
364 slots_in_an_epoch: self.slots_in_an_epoch,
365 precompile_factory: self.precompile_factory.clone(),
366 mining: self.mining.clone(),
367 disable_pool_balance_checks: self.disable_pool_balance_checks,
368 }
369 }
370}
371
372impl<N: Network> fmt::Debug for Backend<N> {
373 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
374 f.debug_struct("Backend").finish_non_exhaustive()
375 }
376}
377
378impl<N: Network> Backend<N> {
380 pub fn impersonate(&self, addr: Address) -> bool {
384 if self.cheats.impersonated_accounts().contains(&addr) {
385 return true;
386 }
387 self.evm_env.write().cfg_env.disable_eip3607 = true;
389 self.cheats.impersonate(addr)
390 }
391
392 pub fn stop_impersonating(&self, addr: Address) {
396 self.cheats.stop_impersonating(&addr);
397 }
398
399 pub fn auto_impersonate_account(&self, enabled: bool) {
401 self.cheats.set_auto_impersonate_account(enabled);
402 }
403
404 pub fn get_fork(&self) -> Option<ClientFork> {
406 self.fork.read().clone()
407 }
408
409 pub fn get_db(&self) -> &Arc<AsyncRwLock<Box<dyn Db>>> {
411 &self.db
412 }
413
414 pub async fn get_account(&self, address: Address) -> DatabaseResult<AccountInfo> {
416 Ok(self.db.read().await.basic_ref(address)?.unwrap_or_default())
417 }
418
419 pub fn is_fork(&self) -> bool {
421 self.fork.read().is_some()
422 }
423
424 pub async fn set_create2_deployer(&self, address: Address) -> DatabaseResult<()> {
426 self.set_code(address, Bytes::from_static(DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE)).await?;
427 Ok(())
428 }
429
430 pub(crate) fn update_interval_mine_block_time(&self, block_time: Duration) {
432 self.states.write().update_interval_mine_block_time(block_time)
433 }
434
435 pub const fn time(&self) -> &TimeManager {
437 &self.time
438 }
439
440 pub const fn cheats(&self) -> &CheatsManager {
442 &self.cheats
443 }
444
445 pub fn skip_blob_validation(&self, impersonator: Option<Address>) -> bool {
447 self.cheats().auto_impersonate_accounts()
448 || impersonator
449 .is_some_and(|addr| self.cheats().impersonated_accounts().contains(&addr))
450 }
451
452 pub const fn fees(&self) -> &FeeManager {
454 &self.fees
455 }
456
457 pub const fn evm_env(&self) -> &Arc<RwLock<EvmEnv>> {
459 &self.evm_env
460 }
461
462 pub fn best_hash(&self) -> B256 {
464 self.blockchain.storage.read().best_hash
465 }
466
467 pub fn best_number(&self) -> u64 {
469 self.blockchain.storage.read().best_number
470 }
471
472 pub fn set_block_number(&self, number: u64) {
474 self.evm_env.write().block_env.number = U256::from(number);
475 }
476
477 pub fn coinbase(&self) -> Address {
479 self.evm_env.read().block_env.beneficiary
480 }
481
482 pub fn chain_id(&self) -> U256 {
484 U256::from(self.evm_env.read().cfg_env.chain_id)
485 }
486
487 pub fn set_chain_id(&self, chain_id: u64) {
488 self.evm_env.write().cfg_env.chain_id = chain_id;
489 }
490
491 pub const fn genesis_time(&self) -> u64 {
493 self.genesis.timestamp
494 }
495
496 pub const fn genesis_number(&self) -> u64 {
498 self.genesis.number
499 }
500
501 pub async fn current_balance(&self, address: Address) -> DatabaseResult<U256> {
503 Ok(self.get_account(address).await?.balance)
504 }
505
506 pub async fn current_nonce(&self, address: Address) -> DatabaseResult<u64> {
508 Ok(self.get_account(address).await?.nonce)
509 }
510
511 pub fn set_coinbase(&self, address: Address) {
513 self.evm_env.write().block_env.beneficiary = address;
514 }
515
516 pub fn set_next_block_prevrandao(&self, prevrandao: B256) {
521 self.cheats.set_next_block_prevrandao(prevrandao);
522 }
523
524 pub async fn set_nonce(&self, address: Address, nonce: U256) -> DatabaseResult<()> {
526 self.db.write().await.set_nonce(address, nonce.try_into().unwrap_or(u64::MAX))
527 }
528
529 pub async fn set_balance(&self, address: Address, balance: U256) -> DatabaseResult<()> {
531 self.db.write().await.set_balance(address, balance)
532 }
533
534 pub async fn set_code(&self, address: Address, code: Bytes) -> DatabaseResult<()> {
536 self.db.write().await.set_code(address, code)
537 }
538
539 pub async fn set_storage_at(
541 &self,
542 address: Address,
543 slot: U256,
544 val: B256,
545 ) -> DatabaseResult<()> {
546 self.db.write().await.set_storage_at(address, slot.into(), val)
547 }
548
549 pub fn spec_id(&self) -> SpecId {
551 *self.evm_env.read().spec_id()
552 }
553
554 pub fn is_eip1559(&self) -> bool {
556 (self.spec_id() as u8) >= (SpecId::LONDON as u8)
557 }
558
559 pub fn is_eip3675(&self) -> bool {
561 (self.spec_id() as u8) >= (SpecId::MERGE as u8)
562 }
563
564 pub fn is_eip2930(&self) -> bool {
566 (self.spec_id() as u8) >= (SpecId::BERLIN as u8)
567 }
568
569 pub fn is_eip4844(&self) -> bool {
571 (self.spec_id() as u8) >= (SpecId::CANCUN as u8)
572 }
573
574 pub fn is_eip7702(&self) -> bool {
576 (self.spec_id() as u8) >= (SpecId::PRAGUE as u8)
577 }
578
579 #[cfg(feature = "optimism")]
581 pub const fn is_optimism(&self) -> bool {
582 self.networks.is_optimism()
583 }
584
585 #[cfg(not(feature = "optimism"))]
589 pub const fn is_optimism(&self) -> bool {
590 false
591 }
592
593 pub const fn is_tempo(&self) -> bool {
595 self.networks.is_tempo()
596 }
597
598 pub fn hardfork(&self) -> FoundryHardfork {
600 if let Some(hardfork) =
601 self.fork.read().as_ref().and_then(|fork| fork.config.read().hardfork)
602 {
603 return hardfork;
604 }
605 self.hardfork
606 }
607
608 pub fn tempo_hardfork(&self) -> TempoHardfork {
610 TempoHardfork::from(self.hardfork())
611 }
612
613 pub fn is_tempo_hardfork_active(&self, hardfork: TempoHardfork) -> bool {
615 self.is_tempo() && self.tempo_hardfork() >= hardfork
616 }
617
618 pub fn precompiles(&self) -> BTreeMap<String, Address> {
620 let spec_id = self.spec_id();
621 let mut precompiles =
622 PrecompilesMap::from_static(Precompiles::new(PrecompileSpecId::from_spec_id(spec_id)));
623 let (chain_id, timestamp) = {
624 let evm_env = self.evm_env.read();
625 (evm_env.cfg_env.chain_id, evm_env.block_env.timestamp.saturating_to())
626 };
627 self.networks.inject_chain_precompiles(&mut precompiles, chain_id, timestamp);
628
629 let mut precompiles_map = BTreeMap::<String, Address>::default();
630 for address in precompiles.addresses() {
631 let precompile = precompiles.get(address).expect("precompile address must resolve");
632 precompiles_map.insert(precompile.precompile_id().name().to_string(), *address);
633 }
634
635 precompiles_map
637 .extend(self.networks.precompiles(self.is_tempo().then(|| self.tempo_hardfork())));
638
639 if let Some(factory) = &self.precompile_factory {
640 for (address, precompile) in factory.precompiles() {
641 precompiles_map.insert(precompile.precompile_id().to_string(), address);
642 }
643 }
644
645 precompiles_map
646 }
647
648 pub fn system_contracts(&self) -> BTreeMap<SystemContract, Address> {
650 let mut system_contracts = BTreeMap::<SystemContract, Address>::default();
651
652 let spec_id = self.spec_id();
653
654 if spec_id >= SpecId::CANCUN {
655 system_contracts.extend(SystemContract::cancun());
656 }
657
658 if spec_id >= SpecId::PRAGUE {
659 system_contracts.extend(SystemContract::prague(None));
660 }
661
662 system_contracts
663 }
664
665 pub fn blob_params(&self) -> BlobParams {
667 get_blob_params_by_spec_id(self.spec_id())
668 }
669
670 pub fn ensure_eip1559_active(&self) -> Result<(), BlockchainError> {
672 if self.is_eip1559() {
673 return Ok(());
674 }
675 Err(BlockchainError::EIP1559TransactionUnsupportedAtHardfork)
676 }
677
678 pub fn ensure_eip2930_active(&self) -> Result<(), BlockchainError> {
680 if self.is_eip2930() {
681 return Ok(());
682 }
683 Err(BlockchainError::EIP2930TransactionUnsupportedAtHardfork)
684 }
685
686 pub fn ensure_eip4844_active(&self) -> Result<(), BlockchainError> {
687 if self.is_eip4844() {
688 return Ok(());
689 }
690 Err(BlockchainError::EIP4844TransactionUnsupportedAtHardfork)
691 }
692
693 pub fn ensure_eip7702_active(&self) -> Result<(), BlockchainError> {
694 if self.is_eip7702() {
695 return Ok(());
696 }
697 Err(BlockchainError::EIP7702TransactionUnsupportedAtHardfork)
698 }
699
700 #[cfg(feature = "optimism")]
702 pub const fn ensure_op_deposits_active(&self) -> Result<(), BlockchainError> {
703 if self.is_optimism() {
704 return Ok(());
705 }
706 Err(BlockchainError::DepositTransactionUnsupported)
707 }
708
709 pub const fn ensure_tempo_active(&self) -> Result<(), BlockchainError> {
711 if self.is_tempo() {
712 return Ok(());
713 }
714 Err(BlockchainError::TempoTransactionUnsupported)
715 }
716
717 fn inspector_tx_config(&self) -> InspectorTxConfig {
719 InspectorTxConfig {
720 print_traces: self.print_traces,
721 print_logs: self.print_logs,
722 enable_steps_tracing: self.enable_steps_tracing,
723 call_trace_decoder: self.call_trace_decoder.clone(),
724 }
725 }
726
727 fn pool_tx_gas_config(&self, evm_env: &EvmEnv) -> PoolTxGasConfig {
729 let spec_id = *evm_env.spec_id();
730 let is_cancun = spec_id >= SpecId::CANCUN;
731 let blob_params = self.blob_params();
732 PoolTxGasConfig {
733 disable_block_gas_limit: evm_env.cfg_env.disable_block_gas_limit,
734 tx_gas_limit_cap: evm_env.cfg_env.tx_gas_limit_cap,
735 tx_gas_limit_cap_resolved: evm_env.cfg_env.tx_gas_limit_cap(),
736 max_blob_gas_per_block: blob_params.max_blob_gas_per_block(),
737 is_cancun,
738 }
739 }
740
741 pub fn gas_limit(&self) -> u64 {
743 self.evm_env.read().block_env.gas_limit
744 }
745
746 pub fn set_gas_limit(&self, gas_limit: u64) {
748 self.evm_env.write().block_env.gas_limit = gas_limit;
749 }
750
751 pub fn base_fee(&self) -> u64 {
753 self.fees.base_fee()
754 }
755
756 pub const fn is_min_priority_fee_enforced(&self) -> bool {
758 self.fees.is_min_priority_fee_enforced()
759 }
760
761 pub fn excess_blob_gas_and_price(&self) -> Option<BlobExcessGasAndPrice> {
762 self.fees.excess_blob_gas_and_price()
763 }
764
765 pub fn set_base_fee(&self, basefee: u64) {
767 self.fees.set_base_fee(basefee)
768 }
769
770 pub fn set_gas_price(&self, price: u128) {
772 self.fees.set_gas_price(price)
773 }
774
775 pub fn elasticity(&self) -> f64 {
776 self.fees.elasticity()
777 }
778
779 pub fn total_difficulty(&self) -> U256 {
784 self.blockchain.storage.read().total_difficulty
785 }
786
787 pub async fn create_state_snapshot(&self) -> U256 {
791 let num = self.best_number();
792 let hash = self.best_hash();
793 let id = self.db.write().await.snapshot_state();
794 trace!(target: "backend", "creating snapshot {} at {}", id, num);
795 self.active_state_snapshots.lock().insert(id, (num, hash));
796 id
797 }
798
799 pub fn list_state_snapshots(&self) -> BTreeMap<U256, (u64, B256)> {
800 self.active_state_snapshots.lock().clone().into_iter().collect()
801 }
802
803 fn next_evm_env(&self) -> EvmEnv {
805 let mut evm_env = self.evm_env.read().clone();
806 evm_env.block_env.number = evm_env.block_env.number.saturating_add(U256::from(1));
808 evm_env.block_env.basefee = self.base_fee();
809 evm_env.block_env.blob_excess_gas_and_price = self.excess_blob_gas_and_price();
810 evm_env.block_env.timestamp = U256::from(self.time.current_call_timestamp());
811 evm_env
812 }
813
814 fn build_inspector(&self) -> AnvilInspector {
816 let mut inspector = AnvilInspector::default();
817
818 if self.print_logs {
819 inspector = inspector.with_log_collector();
820 }
821 if self.print_traces {
822 inspector = inspector.with_trace_printer();
823 }
824
825 inspector
826 }
827
828 fn build_mining_inspector(&self) -> AnvilInspector {
830 let mut inspector = AnvilInspector::default().with_tracing();
831 if self.enable_steps_tracing {
832 inspector = inspector.with_steps_tracing();
833 }
834 if self.print_logs {
835 inspector = inspector.with_log_collector();
836 }
837 if self.print_traces {
838 inspector = inspector.with_trace_printer();
839 }
840 inspector
841 }
842
843 pub fn new_block_notifications(&self) -> ChainNotifications {
846 let (tx, rx) = unbounded();
847 self.new_block_listeners.lock().push(tx);
848 trace!(target: "backed", "added new block listener");
849 rx
850 }
851
852 pub fn new_block_listeners_count(&self) -> usize {
855 self.new_block_listeners.lock().len()
856 }
857
858 fn notify_on_new_block(&self, header: Header, hash: B256) {
860 self.new_block_listeners.lock().retain(|tx| !tx.is_closed());
863
864 let notification =
865 ChainNotification::Block(NewBlockNotification { hash, header: Arc::new(header) });
866
867 self.new_block_listeners
868 .lock()
869 .retain(|tx| tx.unbounded_send(notification.clone()).is_ok());
870 }
871
872 fn notify_on_removed_logs(&self, logs: Vec<Log>) {
875 self.new_block_listeners.lock().retain(|tx| !tx.is_closed());
878
879 let notification = ChainNotification::RemovedLogs(Arc::new(logs));
880
881 self.new_block_listeners
882 .lock()
883 .retain(|tx| tx.unbounded_send(notification.clone()).is_ok());
884 }
885
886 pub fn convert_block_number(&self, block: Option<BlockNumber>) -> u64 {
888 let current = self.best_number();
889 match block.unwrap_or(BlockNumber::Latest) {
890 BlockNumber::Latest | BlockNumber::Pending => current,
891 BlockNumber::Earliest => 0,
892 BlockNumber::Number(num) => num,
893 BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch),
894 BlockNumber::Finalized => current.saturating_sub(self.slots_in_an_epoch * 2),
895 }
896 }
897
898 fn get_block_with_hash(&self, id: impl Into<BlockId>) -> Option<(Block, B256)> {
900 let hash = self.blockchain.hash(id.into(), self.slots_in_an_epoch)?;
901 let block = self.get_block_by_hash(hash)?;
902 Some((block, hash))
903 }
904
905 pub fn get_block(&self, id: impl Into<BlockId>) -> Option<Block> {
906 self.get_block_with_hash(id).map(|(block, _)| block)
907 }
908
909 pub fn get_block_by_hash(&self, hash: B256) -> Option<Block> {
910 self.blockchain.get_block_by_hash(&hash)
911 }
912
913 pub(crate) fn mined_parity_trace_transaction(
915 &self,
916 hash: B256,
917 ) -> Option<Vec<LocalizedTransactionTrace>> {
918 self.blockchain.storage.read().transactions.get(&hash).map(|tx| tx.parity_traces())
919 }
920
921 pub(crate) fn mined_parity_trace_block(
923 &self,
924 block: u64,
925 ) -> Option<Vec<LocalizedTransactionTrace>> {
926 let block = self.get_block(block)?;
927 let mut traces = vec![];
928 let storage = self.blockchain.storage.read();
929 for tx in block.body.transactions {
930 if let Some(mined_tx) = storage.transactions.get(&tx.hash()) {
931 traces.extend(mined_tx.parity_traces());
932 }
933 }
934 Some(traces)
935 }
936
937 pub(crate) fn mined_transaction(&self, hash: B256) -> Option<MinedTransaction<N>> {
939 self.blockchain.storage.read().transactions.get(&hash).cloned()
940 }
941
942 pub async fn impersonate_signature(
944 &self,
945 signature: Bytes,
946 address: Address,
947 ) -> Result<(), BlockchainError> {
948 self.cheats.add_recover_override(signature, address);
949 Ok(())
950 }
951
952 pub async fn debug_code_by_hash(
954 &self,
955 code_hash: B256,
956 block_id: Option<BlockId>,
957 ) -> Result<Option<Bytes>, BlockchainError> {
958 if let Ok(code) = self.db.read().await.code_by_hash_ref(code_hash) {
959 return Ok(Some(code.original_bytes()));
960 }
961 if let Some(fork) = self.get_fork() {
962 return Ok(fork.debug_code_by_hash(code_hash, block_id).await?);
963 }
964
965 Ok(None)
966 }
967
968 pub async fn debug_db_get(&self, key: String) -> Result<Option<Bytes>, BlockchainError> {
976 let key_bytes = if key.starts_with("0x") {
977 hex::decode(&key)
978 .map_err(|_| BlockchainError::Message("Invalid hex key".to_string()))?
979 } else {
980 key.into_bytes()
981 };
982
983 if key_bytes.len() != 33 {
985 return Err(BlockchainError::Message(format!(
986 "Invalid key length: expected 33 bytes, got {}",
987 key_bytes.len()
988 )));
989 }
990
991 if key_bytes[0] != 0x63 {
993 return Err(BlockchainError::Message(
994 "Key prefix must be 0x63 for code hash lookups".to_string(),
995 ));
996 }
997
998 let code_hash = B256::from_slice(&key_bytes[1..33]);
999
1000 self.debug_code_by_hash(code_hash, None).await
1002 }
1003
1004 fn mined_block_by_hash(&self, hash: B256) -> Option<AnyRpcBlock> {
1005 let block = self.blockchain.get_block_by_hash(&hash)?;
1006 Some(self.convert_block_with_hash(block, Some(hash)))
1007 }
1008
1009 pub(crate) async fn mined_transactions_by_block_number(
1010 &self,
1011 number: BlockNumber,
1012 ) -> Option<Vec<AnyRpcTransaction>> {
1013 if let Some(block) = self.get_block(number) {
1014 return self.mined_transactions_in_block(&block);
1015 }
1016 None
1017 }
1018
1019 pub(crate) fn mined_transactions_in_block(
1021 &self,
1022 block: &Block,
1023 ) -> Option<Vec<AnyRpcTransaction>> {
1024 let mut transactions = Vec::with_capacity(block.body.transactions.len());
1025 let base_fee = block.header.base_fee_per_gas();
1026 let storage = self.blockchain.storage.read();
1027 for hash in block.body.transactions.iter().map(|tx| tx.hash()) {
1028 let info = storage.transactions.get(&hash)?.info.clone();
1029 let tx = block.body.transactions.get(info.transaction_index as usize)?.clone();
1030
1031 let tx = transaction_build(Some(hash), tx, Some(block), Some(info), base_fee);
1032 transactions.push(tx);
1033 }
1034 Some(transactions)
1035 }
1036
1037 pub fn mined_block_by_number(&self, number: BlockNumber) -> Option<AnyRpcBlock> {
1038 let (block, hash) = self.get_block_with_hash(number)?;
1039 let mut block = self.convert_block_with_hash(block, Some(hash));
1040 block.transactions.convert_to_hashes();
1041 Some(block)
1042 }
1043
1044 pub fn get_full_block(&self, id: impl Into<BlockId>) -> Option<AnyRpcBlock> {
1045 let (block, hash) = self.get_block_with_hash(id)?;
1046 let transactions = self.mined_transactions_in_block(&block)?;
1047 let mut block = self.convert_block_with_hash(block, Some(hash));
1048 block.inner.transactions = BlockTransactions::Full(transactions);
1049 Some(block)
1050 }
1051
1052 pub fn convert_block(&self, block: Block) -> AnyRpcBlock {
1054 self.convert_block_with_hash(block, None)
1055 }
1056
1057 pub fn convert_block_with_hash(&self, block: Block, known_hash: Option<B256>) -> AnyRpcBlock {
1060 let size = U256::from(alloy_rlp::encode(canonical_block(block.clone())).len() as u32);
1061
1062 let header = block.header.clone();
1063 let transactions = block.body.transactions;
1064
1065 let hash = known_hash.unwrap_or_else(|| header.hash_slow());
1066 let number = header.number();
1067 let withdrawals_root = header.withdrawals_root();
1068 let tempo_fields = header
1069 .as_tempo()
1070 .map(|header| {
1071 (
1072 header.timestamp_millis(),
1073 header.general_gas_limit,
1074 header.shared_gas_limit,
1075 header.timestamp_millis_part,
1076 )
1077 })
1078 .or_else(|| {
1079 self.is_tempo()
1080 .then(|| (header.timestamp().saturating_mul(1000), header.gas_limit(), 0, 0))
1081 });
1082
1083 let block = AlloyBlock {
1084 header: AlloyHeader {
1085 inner: AnyHeader::from(header.into_inner()),
1086 hash,
1087 total_difficulty: Some(self.total_difficulty()),
1088 size: Some(size),
1089 },
1090 transactions: alloy_rpc_types::BlockTransactions::Hashes(
1091 transactions.into_iter().map(|tx| tx.hash()).collect(),
1092 ),
1093 uncles: vec![],
1094 withdrawals: withdrawals_root.map(|_| Default::default()),
1095 };
1096
1097 let mut block = WithOtherFields::new(block);
1098
1099 if is_arbitrum(self.chain_id().to::<u64>()) {
1101 block.other.insert("l1BlockNumber".to_string(), number.into());
1103 }
1104
1105 if let Some((
1106 timestamp_millis,
1107 general_gas_limit,
1108 shared_gas_limit,
1109 timestamp_millis_part,
1110 )) = tempo_fields
1111 {
1112 block.other.insert(
1113 "timestampMillis".to_string(),
1114 serde_json::Value::String(format!("0x{timestamp_millis:x}")),
1115 );
1116 block.other.insert(
1117 "mainBlockGeneralGasLimit".to_string(),
1118 serde_json::Value::String(format!("0x{general_gas_limit:x}")),
1119 );
1120 block.other.insert(
1121 "sharedGasLimit".to_string(),
1122 serde_json::Value::String(format!("0x{shared_gas_limit:x}")),
1123 );
1124 block.other.insert(
1125 "timestampMillisPart".to_string(),
1126 serde_json::Value::String(format!("0x{timestamp_millis_part:x}")),
1127 );
1128 }
1129
1130 AnyRpcBlock::from(block)
1131 }
1132
1133 pub async fn block_by_hash(&self, hash: B256) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1134 trace!(target: "backend", "get block by hash {:?}", hash);
1135 if let tx @ Some(_) = self.mined_block_by_hash(hash) {
1136 return Ok(tx);
1137 }
1138
1139 if let Some(fork) = self.get_fork() {
1140 return Ok(fork.block_by_hash(hash).await?);
1141 }
1142
1143 Ok(None)
1144 }
1145
1146 pub async fn block_by_hash_full(
1147 &self,
1148 hash: B256,
1149 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1150 trace!(target: "backend", "get block by hash {:?}", hash);
1151 if let tx @ Some(_) = self.get_full_block(hash) {
1152 return Ok(tx);
1153 }
1154
1155 if let Some(fork) = self.get_fork() {
1156 return Ok(fork.block_by_hash_full(hash).await?);
1157 }
1158
1159 Ok(None)
1160 }
1161
1162 pub async fn block_by_number(
1163 &self,
1164 number: BlockNumber,
1165 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1166 trace!(target: "backend", "get block by number {:?}", number);
1167 if let tx @ Some(_) = self.mined_block_by_number(number) {
1168 return Ok(tx);
1169 }
1170
1171 if let Some(fork) = self.get_fork() {
1172 let number = self.convert_block_number(Some(number));
1173 if fork.predates_fork_inclusive(number) {
1174 return Ok(fork.block_by_number(number).await?);
1175 }
1176 }
1177
1178 Ok(None)
1179 }
1180
1181 pub async fn block_by_number_full(
1182 &self,
1183 number: BlockNumber,
1184 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1185 trace!(target: "backend", "get block by number {:?}", number);
1186 if let tx @ Some(_) = self.get_full_block(number) {
1187 return Ok(tx);
1188 }
1189
1190 if let Some(fork) = self.get_fork() {
1191 let number = self.convert_block_number(Some(number));
1192 if fork.predates_fork_inclusive(number) {
1193 return Ok(fork.block_by_number_full(number).await?);
1194 }
1195 }
1196
1197 Ok(None)
1198 }
1199
1200 pub async fn ensure_block_number<T: Into<BlockId>>(
1206 &self,
1207 block_id: Option<T>,
1208 ) -> Result<u64, BlockchainError> {
1209 let current = self.best_number();
1210 let requested =
1211 match block_id.map(Into::into).unwrap_or(BlockId::Number(BlockNumber::Latest)) {
1212 BlockId::Hash(hash) => {
1213 self.block_by_hash(hash.block_hash)
1214 .await?
1215 .ok_or(BlockchainError::BlockNotFound)?
1216 .header
1217 .number
1218 }
1219 BlockId::Number(num) => match num {
1220 BlockNumber::Latest | BlockNumber::Pending => current,
1221 BlockNumber::Earliest => U64::ZERO.to::<u64>(),
1222 BlockNumber::Number(num) => num,
1223 BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch),
1224 BlockNumber::Finalized => current.saturating_sub(self.slots_in_an_epoch * 2),
1225 },
1226 };
1227
1228 if requested > current {
1229 Err(BlockchainError::BlockOutOfRange(current, requested))
1230 } else {
1231 Ok(requested)
1232 }
1233 }
1234
1235 fn inject_precompiles(&self, precompiles: &mut PrecompilesMap, evm_env: &EvmEnv) {
1243 self.networks.inject_precompiles(precompiles);
1244 self.networks.inject_chain_precompiles(
1245 precompiles,
1246 evm_env.cfg_env.chain_id,
1247 evm_env.block_env.timestamp.saturating_to(),
1248 );
1249
1250 if let Some(factory) = &self.precompile_factory {
1251 factory.install(precompiles);
1252 }
1253
1254 let cheats = Arc::new(self.cheats.clone());
1255 if cheats.has_recover_overrides() {
1256 let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats));
1257 precompiles.apply_precompile(&EC_RECOVER, move |_| {
1258 Some(DynPrecompile::new_stateful(
1259 cheat_ecrecover.precompile_id().clone(),
1260 move |input| cheat_ecrecover.call(input),
1261 ))
1262 });
1263 }
1264 }
1265
1266 fn inject_arbitrum_precompile(&self, precompiles: &mut PrecompilesMap, evm_env: &EvmEnv) {
1267 let Some(block_number) = self.arbitrum_block_number(evm_env) else { return };
1268 precompiles.apply_precompile(&arbitrum::ARB_SYS_ADDRESS, move |_| {
1269 Some(arbitrum::arb_sys_precompile(block_number))
1270 });
1271 }
1272
1273 fn inject_tempo_precompiles<DB, I>(
1274 &self,
1275 evm: &mut tempo_evm::evm::TempoEvm<DB, I>,
1276 evm_env: &EvmEnv,
1277 ) where
1278 DB: Database,
1279 I: Inspector<TempoContext<DB>>,
1280 {
1281 self.inject_precompiles(evm.precompiles_mut(), evm_env);
1282 let cfg = evm.ctx().cfg.clone();
1284 let non_creditable_slots = evm.non_creditable_slots();
1285 extend_tempo_precompiles(
1286 evm.precompiles_mut(),
1287 &cfg,
1288 StorageActions::disabled(),
1289 non_creditable_slots,
1290 );
1291 }
1292
1293 fn transact_with_inspector_ref<'db, I, DB>(
1296 &self,
1297 db: &'db DB,
1298 evm_env: &EvmEnv,
1299 inspector: &mut I,
1300 tx_env: TxEnv,
1301 op_deposit: OpCallDepositInfo,
1302 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
1303 where
1304 DB: DatabaseRef + ?Sized,
1305 I: BackendInspector<WrapDatabaseRef<&'db DB>>,
1306 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1307 {
1308 #[cfg(feature = "optimism")]
1309 if self.is_optimism() {
1310 let op_tx = OpTransaction { base: tx_env, deposit: op_deposit, ..Default::default() };
1311 return self.transact_op_with_inspector_ref(db, evm_env, inspector, op_tx);
1312 }
1313 let _ = op_deposit;
1315 if self.is_tempo() {
1316 self.transact_tempo_with_inspector_ref(db, evm_env, inspector, TempoTxEnv::from(tx_env))
1317 } else {
1318 self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env)
1319 }
1320 }
1321
1322 fn transact_eth_with_inspector_ref<'db, I, DB>(
1327 &self,
1328 db: &'db DB,
1329 evm_env: &EvmEnv,
1330 inspector: &mut I,
1331 tx_env: TxEnv,
1332 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
1333 where
1334 DB: DatabaseRef + ?Sized,
1335 I: Inspector<EthEvmContext<WrapDatabaseRef<&'db DB>>>,
1336 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1337 {
1338 let mut evm = EthEvmFactory::default().create_evm_with_inspector(
1339 WrapDatabaseRef(db),
1340 evm_env.clone(),
1341 inspector,
1342 );
1343 self.inject_precompiles(evm.precompiles_mut(), evm_env);
1344 self.inject_arbitrum_precompile(evm.precompiles_mut(), evm_env);
1345 Ok(evm.transact(tx_env)?)
1346 }
1347
1348 fn transact_envelope_with_inspector_ref<'db, I, DB>(
1351 &self,
1352 db: &'db DB,
1353 evm_env: &EvmEnv,
1354 inspector: &mut I,
1355 tx: &FoundryTxEnvelope,
1356 sender: Address,
1357 ) -> Result<(ResultAndState<HaltReason>, TxEnv), BlockchainError>
1358 where
1359 DB: DatabaseRef + ?Sized,
1360 I: BackendInspector<WrapDatabaseRef<&'db DB>>,
1361 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1362 {
1363 if tx.is_tempo() {
1364 let tx_env: TempoTxEnv =
1365 FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into());
1366 let base = tx_env.inner.clone();
1367 let result = self.transact_tempo_with_inspector_ref(db, evm_env, inspector, tx_env)?;
1368 return Ok((result, base));
1369 }
1370 #[cfg(feature = "optimism")]
1371 if self.is_optimism() {
1372 let op_tx: OpTransaction<TxEnv> =
1373 FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into());
1374 let base = op_tx.base.clone();
1375 let result = self.transact_op_with_inspector_ref(db, evm_env, inspector, op_tx)?;
1376 return Ok((result, base));
1377 }
1378 let tx_env: TxEnv =
1379 FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into());
1380 let base = tx_env.clone();
1381 let result = self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env)?;
1382 Ok((result, base))
1383 }
1384
1385 fn build_tempo_evm_env(&self, evm_env: &EvmEnv) -> EvmEnvFor<TempoEvmNetwork> {
1388 let hardfork = self.tempo_hardfork();
1389 EvmEnv::new(
1390 evm_env.cfg_env.clone().with_spec_and_gas_params(hardfork, tempo_gas_params(hardfork)),
1391 TempoBlockEnv {
1392 inner: evm_env.block_env.clone(),
1393 timestamp_millis_part: 0,
1394 ..Default::default()
1395 },
1396 )
1397 }
1398
1399 fn transact_tempo_with_inspector_ref<'db, I, DB>(
1401 &self,
1402 db: &'db DB,
1403 evm_env: &EvmEnv,
1404 inspector: &mut I,
1405 tx_env: TempoTxEnv,
1406 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
1407 where
1408 DB: DatabaseRef + ?Sized,
1409 I: Inspector<TempoContext<WrapDatabaseRef<&'db DB>>>,
1410 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1411 {
1412 let tempo_env = self.build_tempo_evm_env(evm_env);
1413 let mut evm = TempoEvmFactory::default().create_evm_with_inspector(
1414 WrapDatabaseRef(db),
1415 tempo_env,
1416 inspector,
1417 );
1418 self.inject_tempo_precompiles(&mut evm, evm_env);
1419 let result = evm.transact(tx_env)?;
1420 Ok(ResultAndState {
1421 result: result.result.map_haltreason(|h| match h {
1422 TempoHaltReason::Ethereum(eth) => eth,
1423 _ => HaltReason::PrecompileError,
1424 }),
1425 state: result.state,
1426 })
1427 }
1428
1429 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
1432 fn execute_with_block_executor<DB>(
1433 &self,
1434 db: DB,
1435 evm_env: &EvmEnv,
1436 parent_hash: B256,
1437 spec_id: SpecId,
1438 pool_transactions: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
1439 gas_config: &PoolTxGasConfig,
1440 inspector_tx_config: &InspectorTxConfig,
1441 validator: &dyn Fn(
1442 &PendingTransaction<FoundryTxEnvelope>,
1443 &AccountInfo,
1444 ) -> Result<(), InvalidTransactionError>,
1445 ) -> (ExecutedPoolTransactions<FoundryTxEnvelope>, BlockExecutionResult<FoundryReceiptEnvelope>)
1446 where
1447 DB: StateDB<Error = DatabaseError>,
1448 {
1449 let inspector = self.build_mining_inspector();
1450
1451 macro_rules! run {
1452 ($evm:expr) => {{
1453 self.inject_precompiles($evm.precompiles_mut(), evm_env);
1454 self.inject_arbitrum_precompile($evm.precompiles_mut(), evm_env);
1455 let mut executor = AnvilBlockExecutor::new($evm, parent_hash, spec_id);
1456 executor.apply_pre_execution_changes().expect("pre-execution changes failed");
1457 let pool_result = execute_pool_transactions(
1458 &mut executor,
1459 pool_transactions,
1460 gas_config,
1461 inspector_tx_config,
1462 self.cheats(),
1463 validator,
1464 );
1465 let (evm, block_result) = executor.finish().expect("executor finish failed");
1466 drop(evm);
1467 (pool_result, block_result)
1468 }};
1469 }
1470
1471 #[cfg(feature = "optimism")]
1472 if self.is_optimism() {
1473 let op_env = EvmEnv::new(
1474 evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(self.hardfork.into()),
1475 evm_env.block_env.clone(),
1476 );
1477 let mut evm =
1478 OpEvmFactory::<OpTx>::default().create_evm_with_inspector(db, op_env, inspector);
1479 return run!(evm);
1480 }
1481
1482 if self.is_tempo() {
1483 let tempo_env = self.build_tempo_evm_env(evm_env);
1484 let mut evm =
1485 TempoEvmFactory::default().create_evm_with_inspector(db, tempo_env, inspector);
1486 run!(evm)
1487 } else {
1488 let mut evm =
1489 EthEvmFactory::default().create_evm_with_inspector(db, evm_env.clone(), inspector);
1490 run!(evm)
1491 }
1492 }
1493
1494 fn build_call_env(
1503 &self,
1504 request: WithOtherFields<TransactionRequest>,
1505 fee_details: FeeDetails,
1506 block_env: BlockEnv,
1507 ) -> (EvmEnv, TxEnv, OpCallDepositInfo) {
1508 let tx_type = request.minimal_tx_type() as u8;
1509
1510 let WithOtherFields::<TransactionRequest> {
1511 inner:
1512 TransactionRequest {
1513 from,
1514 to,
1515 gas,
1516 value,
1517 input,
1518 access_list,
1519 blob_versioned_hashes,
1520 authorization_list,
1521 nonce,
1522 sidecar: _,
1523 chain_id,
1524 .. },
1526 other,
1527 } = request;
1528
1529 let FeeDetails {
1530 gas_price,
1531 max_fee_per_gas,
1532 max_priority_fee_per_gas,
1533 max_fee_per_blob_gas,
1534 } = fee_details;
1535
1536 let gas_limit = gas.unwrap_or(block_env.gas_limit);
1537 let mut evm_env = self.evm_env.read().clone();
1538 evm_env.block_env = block_env;
1539 evm_env.cfg_env.disable_block_gas_limit = true;
1542 evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
1543
1544 evm_env.cfg_env.disable_base_fee = true;
1550
1551 evm_env.cfg_env.disable_nonce_check = true;
1553
1554 let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| {
1555 self.fees().raw_gas_price().saturating_add(MIN_SUGGESTED_PRIORITY_FEE)
1556 });
1557 let caller = from.unwrap_or_default();
1558 let to = to.as_ref().and_then(TxKind::to);
1559 let blob_hashes = blob_versioned_hashes.unwrap_or_default();
1560 let mut tx_env = TxEnv {
1561 caller,
1562 gas_limit,
1563 gas_price,
1564 gas_priority_fee: max_priority_fee_per_gas,
1565 max_fee_per_blob_gas: max_fee_per_blob_gas
1566 .or_else(|| {
1567 if blob_hashes.is_empty() { Some(0) } else { evm_env.block_env.blob_gasprice() }
1568 })
1569 .unwrap_or_default(),
1570 kind: match to {
1571 Some(addr) => TxKind::Call(*addr),
1572 None => TxKind::Create,
1573 },
1574 tx_type,
1575 value: value.unwrap_or_default(),
1576 data: input.into_input().unwrap_or_default(),
1577 chain_id: Some(chain_id.unwrap_or(self.chain_id().to::<u64>())),
1578 access_list: access_list.unwrap_or_default(),
1579 blob_hashes,
1580 ..Default::default()
1581 };
1582 tx_env.set_signed_authorization(authorization_list.unwrap_or_default());
1583
1584 if let Some(nonce) = nonce {
1585 tx_env.nonce = nonce;
1586 }
1587
1588 if evm_env.block_env.basefee == 0 {
1589 evm_env.cfg_env.disable_base_fee = true;
1592 }
1593
1594 #[cfg(feature = "optimism")]
1596 let op_deposit = if self.ensure_op_deposits_active().is_ok()
1597 && let Ok(deposit) = get_deposit_tx_parts(&other)
1598 {
1599 deposit
1600 } else {
1601 OpCallDepositInfo::default()
1602 };
1603 #[cfg(not(feature = "optimism"))]
1604 let op_deposit = {
1605 let _ = &other;
1607 OpCallDepositInfo
1608 };
1609
1610 (evm_env, tx_env, op_deposit)
1611 }
1612
1613 pub fn call_with_state(
1614 &self,
1615 state: &dyn DatabaseRef,
1616 request: WithOtherFields<TransactionRequest>,
1617 fee_details: FeeDetails,
1618 block_env: BlockEnv,
1619 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
1620 let mut inspector = self.build_inspector();
1621
1622 let tempo_overrides = self.is_tempo().then(|| {
1624 let fee_token =
1625 request.other.get_deserialized::<Address>("feeToken").and_then(|r| r.ok());
1626 let nonce_key = request
1627 .other
1628 .get_deserialized::<U256>("nonceKey")
1629 .and_then(|r| r.ok())
1630 .unwrap_or_default();
1631 let valid_before = request
1632 .other
1633 .get_deserialized::<U256>("validBefore")
1634 .and_then(|r| r.ok())
1635 .map(|v| v.saturating_to::<u64>());
1636 let valid_after = request
1637 .other
1638 .get_deserialized::<U256>("validAfter")
1639 .and_then(|r| r.ok())
1640 .map(|v| v.saturating_to::<u64>());
1641 (fee_token, nonce_key, valid_before, valid_after)
1642 });
1643
1644 let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block_env);
1645
1646 let ResultAndState { result, state } =
1647 if let Some((fee_token, nonce_key, valid_before, valid_after)) = tempo_overrides {
1648 use tempo_primitives::transaction::Call;
1649
1650 let base = tx_env;
1651 let mut tempo_tx = TempoTxEnv::from(base.clone());
1652 tempo_tx.fee_token = fee_token;
1653
1654 if !nonce_key.is_zero() || valid_before.is_some() || valid_after.is_some() {
1655 let estimation_hash = keccak256(base.data.as_ref());
1660 tempo_tx.unique_tx_identifier = Some(estimation_hash);
1664 tempo_tx.tempo_tx_env = Some(Box::new(TempoBatchCallEnv {
1665 nonce_key,
1666 valid_before,
1667 valid_after,
1668 aa_calls: vec![Call { to: base.kind, value: base.value, input: base.data }],
1669 tx_hash: estimation_hash,
1670 expiring_nonce_idx: Some(0),
1671 ..Default::default()
1672 }));
1673 }
1674 self.transact_tempo_with_inspector_ref(state, &evm_env, &mut inspector, tempo_tx)?
1675 } else {
1676 self.transact_with_inspector_ref(
1677 state,
1678 &evm_env,
1679 &mut inspector,
1680 tx_env,
1681 op_deposit,
1682 )?
1683 };
1684
1685 let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
1686 inspector.print_logs();
1687
1688 if self.print_traces {
1689 inspector.into_print_traces(self.call_trace_decoder.clone());
1690 }
1691
1692 Ok((exit_reason, out, gas_used as u128, state))
1693 }
1694
1695 pub fn build_access_list_with_state(
1696 &self,
1697 state: &dyn DatabaseRef,
1698 request: WithOtherFields<TransactionRequest>,
1699 fee_details: FeeDetails,
1700 block_env: BlockEnv,
1701 ) -> Result<(InstructionResult, Option<Output>, u64, AccessList), BlockchainError> {
1702 let mut inspector =
1703 AccessListInspector::new(request.access_list.clone().unwrap_or_default());
1704
1705 let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block_env);
1706 let ResultAndState { result, state: _ } =
1707 self.transact_with_inspector_ref(state, &evm_env, &mut inspector, tx_env, op_deposit)?;
1708 let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
1709 let access_list = inspector.access_list();
1710 Ok((exit_reason, out, gas_used, access_list))
1711 }
1712
1713 fn arbitrum_block_number(&self, evm_env: &EvmEnv) -> Option<u64> {
1714 if !arbitrum::is_arbitrum_chain(evm_env.cfg_env.chain_id) {
1715 return None;
1716 }
1717
1718 let env_block = evm_env.block_env.number.saturating_to();
1719 Some(self.get_fork().map_or(env_block, |fork| fork.block_number().max(env_block)))
1720 }
1721
1722 pub fn get_code_with_state(
1723 &self,
1724 state: &dyn DatabaseRef,
1725 address: Address,
1726 ) -> Result<Bytes, BlockchainError> {
1727 trace!(target: "backend", "get code for {:?}", address);
1728 let account = state.basic_ref(address)?.unwrap_or_default();
1729 if account.code_hash == KECCAK_EMPTY {
1730 return Ok(Default::default());
1732 }
1733 let code = if let Some(code) = account.code {
1734 code
1735 } else {
1736 state.code_by_hash_ref(account.code_hash)?
1737 };
1738 Ok(code.bytes()[..code.len()].to_vec().into())
1739 }
1740
1741 pub fn get_balance_with_state<D>(
1742 &self,
1743 state: D,
1744 address: Address,
1745 ) -> Result<U256, BlockchainError>
1746 where
1747 D: DatabaseRef,
1748 {
1749 trace!(target: "backend", "get balance for {:?}", address);
1750 Ok(state.basic_ref(address)?.unwrap_or_default().balance)
1751 }
1752
1753 pub async fn transaction_by_block_number_and_index(
1754 &self,
1755 number: BlockNumber,
1756 index: Index,
1757 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
1758 if let Some(block) = self.mined_block_by_number(number) {
1759 return Ok(self.mined_transaction_by_block_hash_and_index(block.header.hash, index));
1760 }
1761
1762 if let Some(fork) = self.get_fork() {
1763 let number = self.convert_block_number(Some(number));
1764 if fork.predates_fork(number) {
1765 return Ok(fork
1766 .transaction_by_block_number_and_index(number, index.into())
1767 .await?);
1768 }
1769 }
1770
1771 Ok(None)
1772 }
1773
1774 pub async fn transaction_by_block_hash_and_index(
1775 &self,
1776 hash: B256,
1777 index: Index,
1778 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
1779 if let tx @ Some(_) = self.mined_transaction_by_block_hash_and_index(hash, index) {
1780 return Ok(tx);
1781 }
1782
1783 if let Some(fork) = self.get_fork() {
1784 return Ok(fork.transaction_by_block_hash_and_index(hash, index.into()).await?);
1785 }
1786
1787 Ok(None)
1788 }
1789
1790 pub fn mined_transaction_by_block_hash_and_index(
1791 &self,
1792 block_hash: B256,
1793 index: Index,
1794 ) -> Option<AnyRpcTransaction> {
1795 let (info, block, tx) = {
1796 let storage = self.blockchain.storage.read();
1797 let block = storage.blocks.get(&block_hash).cloned()?;
1798 let index: usize = index.into();
1799 let tx = block.body.transactions.get(index)?.clone();
1800 let info = storage.transactions.get(&tx.hash())?.info.clone();
1801 (info, block, tx)
1802 };
1803
1804 Some(transaction_build(
1805 Some(info.transaction_hash),
1806 tx,
1807 Some(&block),
1808 Some(info),
1809 block.header.base_fee_per_gas(),
1810 ))
1811 }
1812
1813 pub async fn transaction_by_hash(
1814 &self,
1815 hash: B256,
1816 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
1817 trace!(target: "backend", "transaction_by_hash={:?}", hash);
1818 if let tx @ Some(_) = self.mined_transaction_by_hash(hash) {
1819 return Ok(tx);
1820 }
1821
1822 if let Some(fork) = self.get_fork() {
1823 return fork
1824 .transaction_by_hash(hash)
1825 .await
1826 .map_err(BlockchainError::AlloyForkProvider);
1827 }
1828
1829 Ok(None)
1830 }
1831
1832 pub fn mined_transaction_by_hash(&self, hash: B256) -> Option<AnyRpcTransaction> {
1833 let (info, block) = {
1834 let storage = self.blockchain.storage.read();
1835 let MinedTransaction { info, block_hash, .. } =
1836 storage.transactions.get(&hash)?.clone();
1837 let block = storage.blocks.get(&block_hash).cloned()?;
1838 (info, block)
1839 };
1840 let tx = block.body.transactions.get(info.transaction_index as usize)?.clone();
1841
1842 Some(transaction_build(
1843 Some(info.transaction_hash),
1844 tx,
1845 Some(&block),
1846 Some(info),
1847 block.header.base_fee_per_gas(),
1848 ))
1849 }
1850
1851 pub async fn trace_transaction(
1853 &self,
1854 hash: B256,
1855 ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
1856 if let Some(traces) = self.mined_parity_trace_transaction(hash) {
1857 return Ok(traces);
1858 }
1859
1860 if let Some(fork) = self.get_fork() {
1861 return Ok(fork.trace_transaction(hash).await?);
1862 }
1863
1864 Ok(vec![])
1865 }
1866
1867 pub async fn trace_get(
1869 &self,
1870 hash: B256,
1871 indices: Vec<Index>,
1872 ) -> Result<Option<LocalizedTransactionTrace>, BlockchainError> {
1873 if indices.len() != 1 {
1874 return Ok(None);
1875 }
1876
1877 let index: usize = indices[0].into();
1878 if let Some(traces) = self.mined_parity_trace_transaction(hash) {
1879 return Ok(traces.into_iter().nth(index));
1880 }
1881
1882 if let Some(fork) = self.get_fork() {
1883 return Ok(fork.trace_get(hash, indices).await?);
1884 }
1885
1886 Ok(None)
1887 }
1888
1889 pub async fn trace_block(
1891 &self,
1892 block: BlockNumber,
1893 ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
1894 let number = self.convert_block_number(Some(block));
1895 if let Some(traces) = self.mined_parity_trace_block(number) {
1896 return Ok(traces);
1897 }
1898
1899 if let Some(fork) = self.get_fork()
1900 && fork.predates_fork(number)
1901 {
1902 return Ok(fork.trace_block(number).await?);
1903 }
1904
1905 Ok(vec![])
1906 }
1907
1908 pub async fn trace_call(
1910 &self,
1911 request: WithOtherFields<TransactionRequest>,
1912 fee_details: FeeDetails,
1913 trace_types: HashSet<TraceType>,
1914 block_request: BlockRequest<FoundryTxEnvelope>,
1915 block_id: BlockId,
1916 ) -> Result<TraceResults, BlockchainError>
1917 where
1918 Self: TransactionValidator<FoundryTxEnvelope>,
1919 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
1920 {
1921 if let BlockRequest::Number(number) = &block_request
1922 && let Some(fork) = self.get_fork()
1923 && fork.predates_fork(*number)
1924 {
1925 return Ok(fork.trace_call(request, trace_types, block_id).await?);
1926 }
1927
1928 self.with_database_at(Some(block_request), |state, block| {
1929 let cache_db = CacheDB::new(state);
1930 let mut inspector =
1931 TracingInspector::new(TracingInspectorConfig::from_parity_config(&trace_types));
1932 let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block);
1933 let result = self.transact_with_inspector_ref(
1934 &cache_db,
1935 &evm_env,
1936 &mut inspector,
1937 tx_env,
1938 op_deposit,
1939 )?;
1940
1941 inspector
1942 .into_parity_builder()
1943 .into_trace_results_with_state(&result, &trace_types, &cache_db)
1944 .map_err(Into::into)
1945 })
1946 .await?
1947 }
1948
1949 pub async fn trace_replay_block_transactions(
1951 &self,
1952 block: BlockNumber,
1953 trace_types: HashSet<TraceType>,
1954 ) -> Result<Vec<TraceResultsWithTransactionHash>, BlockchainError> {
1955 let block_number = self.convert_block_number(Some(block));
1956
1957 if let Some(results) =
1959 self.mined_parity_trace_replay_block_transactions(block_number, &trace_types)
1960 {
1961 return Ok(results);
1962 }
1963
1964 if let Some(fork) = self.get_fork()
1966 && fork.predates_fork(block_number)
1967 {
1968 return Ok(fork.trace_replay_block_transactions(block_number, trace_types).await?);
1969 }
1970
1971 Ok(vec![])
1972 }
1973
1974 pub async fn trace_replay_transaction(
1976 &self,
1977 hash: B256,
1978 trace_types: HashSet<TraceType>,
1979 ) -> Result<TraceResults, BlockchainError> {
1980 let block_number =
1981 self.blockchain.storage.read().transactions.get(&hash).map(|tx| tx.block_number);
1982
1983 if let Some(block_number) = block_number {
1987 let results = self
1988 .mined_parity_trace_replay_block_transactions(block_number, &trace_types)
1989 .ok_or(BlockchainError::BlockNotFound)?;
1990
1991 return results
1992 .into_iter()
1993 .find(|result| result.transaction_hash == hash)
1994 .map(|result| result.full_trace)
1995 .ok_or_else(|| {
1996 BlockchainError::Internal(format!(
1997 "replayed block {block_number} for local transaction {hash:?}, \
1998 but its trace was missing"
1999 ))
2000 });
2001 }
2002
2003 if let Some(fork) = self.get_fork() {
2005 return Ok(fork.trace_replay_transaction(hash, trace_types).await?);
2006 }
2007
2008 Err(BlockchainError::TransactionNotFound)
2009 }
2010
2011 pub async fn trace_raw_transaction(
2013 &self,
2014 pending_transaction: PendingTransaction<FoundryTxEnvelope>,
2015 trace_types: HashSet<TraceType>,
2016 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
2017 ) -> Result<TraceResults, BlockchainError>
2018 where
2019 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
2020 {
2021 let trace_config = TracingInspectorConfig::from_parity_config(&trace_types);
2022
2023 self.with_database_at(block_request, |state, block_env| {
2024 let cache_db = CacheDB::new(state);
2025 let mut evm_env = self.evm_env.read().clone();
2026 evm_env.block_env = block_env;
2027
2028 let mut inspector = TracingInspector::new(trace_config);
2029 let (result, _) = self.transact_envelope_with_inspector_ref(
2030 &cache_db,
2031 &evm_env,
2032 &mut inspector,
2033 pending_transaction.transaction.as_ref(),
2034 *pending_transaction.sender(),
2035 )?;
2036
2037 inspector
2038 .into_parity_builder()
2039 .into_trace_results_with_state(&result, &trace_types, &cache_db)
2040 .map_err(BlockchainError::from)
2041 })
2042 .await?
2043 }
2044
2045 pub async fn trace_call_many(
2047 &self,
2048 calls: Vec<(WithOtherFields<TransactionRequest>, HashSet<TraceType>)>,
2049 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
2050 ) -> Result<Vec<TraceResults>, BlockchainError>
2051 where
2052 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
2053 {
2054 self.with_database_at(block_request, |state, block_env| {
2055 let mut cache_db = CacheDB::new(state);
2056 let mut results = Vec::with_capacity(calls.len());
2057 let mut calls = calls.into_iter().peekable();
2058
2059 while let Some((request, trace_types)) = calls.next() {
2060 let fee_details = FeeDetails::new(
2061 request.gas_price,
2062 request.max_fee_per_gas,
2063 request.max_priority_fee_per_gas,
2064 request.max_fee_per_blob_gas,
2065 )?
2066 .or_zero_fees();
2067 let (evm_env, tx_env, op_deposit) =
2068 self.build_call_env(request, fee_details, block_env.clone());
2069
2070 let trace_config = TracingInspectorConfig::from_parity_config(&trace_types);
2071 let mut inspector = TracingInspector::new(trace_config);
2072 let result = self.transact_with_inspector_ref(
2073 &cache_db,
2074 &evm_env,
2075 &mut inspector,
2076 tx_env,
2077 op_deposit,
2078 )?;
2079
2080 let trace_result = inspector
2081 .into_parity_builder()
2082 .into_trace_results_with_state(&result, &trace_types, &cache_db)
2083 .map_err(BlockchainError::from)?;
2084 results.push(trace_result);
2085
2086 if calls.peek().is_some() {
2087 cache_db.commit(result.state);
2088 }
2089 }
2090
2091 Ok(results)
2092 })
2093 .await?
2094 }
2095
2096 fn mined_parity_trace_replay_block_transactions(
2098 &self,
2099 block_number: u64,
2100 trace_types: &HashSet<TraceType>,
2101 ) -> Option<Vec<TraceResultsWithTransactionHash>> {
2102 let block = self.get_block(block_number)?;
2103
2104 let parent_hash = block.header.parent_hash;
2106 let trace_config = TracingInspectorConfig::from_parity_config(trace_types);
2107
2108 let read_guard = self.states.upgradable_read();
2109 if let Some(state) = read_guard.get_state(&parent_hash) {
2110 self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types)
2111 } else {
2112 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
2113 let state = write_guard.get_on_disk_state(&parent_hash)?;
2114 self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types)
2115 }
2116 }
2117
2118 fn replay_block_transactions_with_inspector(
2120 &self,
2121 block: &Block,
2122 parent_state: &StateDb,
2123 trace_config: TracingInspectorConfig,
2124 trace_types: &HashSet<TraceType>,
2125 ) -> Option<Vec<TraceResultsWithTransactionHash>> {
2126 let mut cache_db = CacheDB::new(Box::new(parent_state));
2127 let mut results = Vec::new();
2128
2129 let mut evm_env = self.evm_env.read().clone();
2131 evm_env.block_env = block_env_from_header(&block.header);
2132
2133 for tx_envelope in &block.body.transactions {
2135 let tx_hash = tx_envelope.hash();
2136
2137 let mut inspector = TracingInspector::new(trace_config);
2139
2140 let pending_tx =
2142 PendingTransaction::from_maybe_impersonated(tx_envelope.clone()).ok()?;
2143 let (result, _) = self
2144 .transact_envelope_with_inspector_ref(
2145 &cache_db,
2146 &evm_env,
2147 &mut inspector,
2148 pending_tx.transaction.as_ref(),
2149 *pending_tx.sender(),
2150 )
2151 .ok()?;
2152
2153 let full_trace = inspector
2155 .into_parity_builder()
2156 .into_trace_results_with_state(&result, trace_types, &cache_db)
2157 .ok()?;
2158
2159 results.push(TraceResultsWithTransactionHash { transaction_hash: tx_hash, full_trace });
2160
2161 cache_db.commit(result.state);
2163 }
2164
2165 Some(results)
2166 }
2167
2168 pub async fn trace_filter(
2170 &self,
2171 filter: TraceFilter,
2172 ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
2173 let matcher = filter.matcher();
2174 let start = filter.from_block.unwrap_or(0);
2175 let end = filter.to_block.unwrap_or_else(|| self.best_number());
2176
2177 if start > end {
2178 return Err(BlockchainError::RpcError(RpcError::invalid_params(
2179 "invalid block range, ensure that to block is greater than from block".to_string(),
2180 )));
2181 }
2182
2183 let dist = end - start;
2184 if dist > 300 {
2185 return Err(BlockchainError::RpcError(RpcError::invalid_params(
2186 "block range too large, currently limited to 300".to_string(),
2187 )));
2188 }
2189
2190 let mut trace_tasks = vec![];
2192 for num in start..=end {
2193 trace_tasks.push(self.trace_block(num.into()));
2194 }
2195
2196 let traces = futures::future::try_join_all(trace_tasks).await?;
2198 let filtered_traces =
2199 traces.into_iter().flatten().filter(|trace| matcher.matches(&trace.trace));
2200
2201 let filtered_traces: Vec<_> = if let Some(after) = filter.after {
2203 filtered_traces.skip(after as usize).collect()
2204 } else {
2205 filtered_traces.collect()
2206 };
2207
2208 let filtered_traces: Vec<_> = if let Some(count) = filter.count {
2209 filtered_traces.into_iter().take(count as usize).collect()
2210 } else {
2211 filtered_traces
2212 };
2213
2214 Ok(filtered_traces)
2215 }
2216
2217 pub fn get_blobs_by_block_id(
2218 &self,
2219 id: impl Into<BlockId>,
2220 versioned_hashes: Vec<B256>,
2221 ) -> Result<Option<Vec<alloy_consensus::Blob>>> {
2222 Ok(self.get_block(id).map(|block| {
2223 block
2224 .body
2225 .transactions
2226 .iter()
2227 .filter_map(|tx| tx.as_ref().sidecar())
2228 .flat_map(|sidecar| {
2229 sidecar.sidecar.blobs().iter().zip(sidecar.sidecar.commitments().iter())
2230 })
2231 .filter(|(_, commitment)| {
2232 versioned_hashes.is_empty()
2234 || versioned_hashes.contains(&kzg_to_versioned_hash(commitment.as_slice()))
2235 })
2236 .map(|(blob, _)| *blob)
2237 .collect()
2238 }))
2239 }
2240
2241 #[allow(clippy::large_stack_frames)]
2242 pub fn get_blob_by_versioned_hash(&self, hash: B256) -> Result<Option<Blob>> {
2243 let storage = self.blockchain.storage.read();
2244 for block in storage.blocks.values() {
2245 for tx in &block.body.transactions {
2246 let typed_tx = tx.as_ref();
2247 if let Some(sidecar) = typed_tx.sidecar() {
2248 for versioned_hash in sidecar.sidecar.versioned_hashes() {
2249 if versioned_hash == hash
2250 && let Some(index) =
2251 sidecar.sidecar.commitments().iter().position(|commitment| {
2252 kzg_to_versioned_hash(commitment.as_slice()) == *hash
2253 })
2254 && let Some(blob) = sidecar.sidecar.blobs().get(index)
2255 {
2256 return Ok(Some(*blob));
2257 }
2258 }
2259 }
2260 }
2261 }
2262 Ok(None)
2263 }
2264
2265 #[expect(clippy::too_many_arguments)]
2267 pub async fn with_genesis(
2268 db: Arc<AsyncRwLock<Box<dyn Db>>>,
2269 env: Arc<RwLock<EvmEnv>>,
2270 networks: NetworkConfigs,
2271 genesis: GenesisConfig,
2272 fees: FeeManager,
2273 fork: Arc<RwLock<Option<ClientFork>>>,
2274 enable_steps_tracing: bool,
2275 print_logs: bool,
2276 print_traces: bool,
2277 call_trace_decoder: Arc<CallTraceDecoder>,
2278 prune_state_history_config: PruneStateHistoryConfig,
2279 max_persisted_states: Option<usize>,
2280 transaction_block_keeper: Option<usize>,
2281 automine_block_time: Option<Duration>,
2282 cache_path: Option<PathBuf>,
2283 node_config: Arc<AsyncRwLock<NodeConfig>>,
2284 ) -> Result<Self> {
2285 let blockchain = if let Some(fork) = fork.read().as_ref() {
2287 trace!(target: "backend", "using forked blockchain at {}", fork.block_number());
2288 Blockchain::forked(fork.block_number(), fork.block_hash(), fork.total_difficulty())
2289 } else {
2290 Blockchain::new(
2291 &env.read(),
2292 fees.is_eip1559().then(|| fees.base_fee()),
2293 genesis.timestamp,
2294 genesis.number,
2295 networks.is_tempo(),
2296 )
2297 };
2298
2299 if fork.read().is_none() {
2302 env.write().block_env.number = U256::from(genesis.number);
2303
2304 if fees.tempo_hardfork().is_some() {
2308 let env = env.read();
2309 let next_base_fee = fees.get_next_block_base_fee_per_gas(
2310 0,
2311 env.block_env.gas_limit,
2312 env.block_env.basefee,
2313 );
2314 drop(env);
2315 fees.set_base_fee(next_base_fee);
2316 }
2317 }
2318
2319 let start_timestamp = if let Some(fork) = fork.read().as_ref() {
2320 fork.timestamp()
2321 } else {
2322 genesis.timestamp
2323 };
2324
2325 let mut states = if prune_state_history_config.is_config_enabled() {
2326 prune_state_history_config
2328 .max_memory_history
2329 .map(|limit| InMemoryBlockStates::new(limit, 0))
2330 .unwrap_or_default()
2331 .memory_only()
2332 } else if max_persisted_states.is_some() {
2333 max_persisted_states
2334 .map(|limit| InMemoryBlockStates::new(DEFAULT_HISTORY_LIMIT, limit))
2335 .unwrap_or_default()
2336 } else {
2337 Default::default()
2338 };
2339
2340 if let Some(cache_path) = cache_path {
2341 states = states.disk_path(cache_path);
2342 }
2343
2344 let (slots_in_an_epoch, precompile_factory, disable_pool_balance_checks, hardfork) = {
2345 let cfg = node_config.read().await;
2346 (
2347 cfg.slots_in_an_epoch,
2348 cfg.precompile_factory.clone(),
2349 cfg.disable_pool_balance_checks,
2350 cfg.get_hardfork(),
2351 )
2352 };
2353
2354 let backend = Self {
2355 db,
2356 blockchain,
2357 states: Arc::new(RwLock::new(states)),
2358 evm_env: env,
2359 networks,
2360 hardfork,
2361 fork,
2362 time: TimeManager::new(start_timestamp),
2363 cheats: Default::default(),
2364 new_block_listeners: Default::default(),
2365 fees,
2366 genesis,
2367 active_state_snapshots: Arc::new(Mutex::new(Default::default())),
2368 enable_steps_tracing,
2369 print_logs,
2370 print_traces,
2371 call_trace_decoder,
2372 prune_state_history_config,
2373 transaction_block_keeper,
2374 node_config,
2375 slots_in_an_epoch,
2376 precompile_factory,
2377 mining: Arc::new(tokio::sync::Mutex::new(())),
2378 disable_pool_balance_checks,
2379 };
2380
2381 if let Some(interval_block_time) = automine_block_time {
2382 backend.update_interval_mine_block_time(interval_block_time);
2383 }
2384
2385 backend.apply_genesis().await.wrap_err("failed to create genesis")?;
2387 Ok(backend)
2388 }
2389
2390 async fn apply_genesis(&self) -> Result<(), DatabaseError> {
2394 trace!(target: "backend", "setting genesis balances");
2395
2396 if self.fork.read().is_some() {
2397 let mut genesis_accounts_futures = Vec::with_capacity(self.genesis.accounts.len());
2399 for address in self.genesis.accounts.iter().copied() {
2400 let db = Arc::clone(&self.db);
2401
2402 genesis_accounts_futures.push(tokio::task::spawn(async move {
2405 let db = db.read().await;
2406 let info = db.basic_ref(address)?.unwrap_or_default();
2407 Ok::<_, DatabaseError>((address, info))
2408 }));
2409 }
2410
2411 let genesis_accounts = futures::future::join_all(genesis_accounts_futures).await;
2412
2413 let mut db = self.db.write().await;
2414
2415 for res in genesis_accounts {
2416 let (address, mut info) = res.unwrap()?;
2417 info.balance = self.genesis.balance;
2418 db.insert_account(address, info.clone());
2419 }
2420 } else {
2421 let mut db = self.db.write().await;
2422 for (account, info) in self.genesis.account_infos() {
2423 db.insert_account(account, info);
2424 }
2425
2426 db.insert_block_hash(U256::from(self.best_number()), self.best_hash());
2429
2430 if self.spec_id() >= SpecId::PRAGUE {
2432 db.set_code(
2433 eip2935::HISTORY_STORAGE_ADDRESS,
2434 eip2935::HISTORY_STORAGE_CODE.clone(),
2435 )?;
2436 }
2437 }
2438
2439 let db = self.db.write().await;
2440 self.genesis.apply_genesis_json_alloc(db)?;
2442
2443 if self.networks.is_tempo() && !self.is_fork() {
2446 let chain_id = self.evm_env.read().cfg_env.chain_id;
2447 let timestamp = self.genesis.timestamp;
2448 let test_accounts: Vec<Address> = self.genesis.accounts.clone();
2449 let hardfork = self.tempo_hardfork();
2450 let mut db = self.db.write().await;
2451 crate::eth::backend::tempo::initialize_tempo_precompiles(
2452 &mut **db,
2453 chain_id,
2454 timestamp,
2455 &test_accounts,
2456 hardfork,
2457 )
2458 .map_err(|e| {
2459 tracing::error!(target: "backend", "failed to initialize Tempo precompiles: {e}");
2460 DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}")))
2461 })?;
2462 trace!(target: "backend", "initialized Tempo precompiles and fee tokens for {} accounts", test_accounts.len());
2463 }
2464
2465 trace!(target: "backend", "set genesis balances");
2466
2467 Ok(())
2468 }
2469
2470 pub async fn reset_fork(&self, forking: Forking) -> Result<(), BlockchainError> {
2472 if !self.is_fork() {
2473 if let Some(eth_rpc_url) = forking.json_rpc_url.clone() {
2474 let mut evm_env = self.evm_env.read().clone();
2475
2476 let (db, config) = {
2477 let mut node_config = self.node_config.write().await;
2478
2479 node_config.base_fee.take();
2482 node_config.fork_urls = vec![eth_rpc_url.clone()];
2483 node_config.apply_tempo_fork_beneficiary_default(&mut evm_env);
2484
2485 node_config.setup_fork_db_config(eth_rpc_url, &mut evm_env, &self.fees).await?
2486 };
2487
2488 *self.db.write().await = Box::new(db);
2489
2490 let fork = ClientFork::new(config, Arc::clone(&self.db));
2491
2492 *self.evm_env.write() = evm_env;
2493 *self.fork.write() = Some(fork);
2494 } else {
2495 return Err(RpcError::invalid_params(
2496 "Forking not enabled and RPC URL not provided to start forking",
2497 )
2498 .into());
2499 }
2500 }
2501
2502 if let Some(fork) = self.get_fork() {
2503 let block_number =
2504 forking.block_number.map(BlockNumber::from).unwrap_or(BlockNumber::Latest);
2505 let reset_urls =
2507 forking.json_rpc_url.as_ref().map(|url| vec![url.clone()]).unwrap_or_default();
2508 let target_rpc_url = forking.json_rpc_url.clone().or_else(|| fork.eth_rpc_url());
2509 let rpc_url_changed = target_rpc_url != fork.database_rpc_url();
2510 fork.prepare_reset(reset_urls, block_number.into()).await?;
2511 if rpc_url_changed {
2512 fork.database.write().await.clear_into_state_snapshot();
2514 }
2515 fork.database.read().await.maybe_flush_cache().map_err(BlockchainError::Internal)?;
2518 let fork_block_number = fork.block_number();
2519 if rpc_url_changed {
2520 let cache_dir = {
2521 let config = self.node_config.read().await;
2522 if config.no_storage_caching || config.fork_urls.is_empty() {
2523 None
2524 } else {
2525 foundry_config::Config::foundry_chain_cache_dir(config.get_chain_id())
2526 }
2527 };
2528 if let Some(cache_dir) = cache_dir
2529 && let Err(err) = std::fs::remove_dir_all(&cache_dir)
2530 && err.kind() != std::io::ErrorKind::NotFound
2531 {
2532 return Err(BlockchainError::Internal(format!(
2533 "failed to invalidate fork cache at {}: {err}",
2534 cache_dir.display()
2535 )));
2536 }
2537 }
2538 let fork_block = fork
2539 .block_by_number(fork_block_number)
2540 .await?
2541 .ok_or(BlockchainError::BlockNotFound)?;
2542 {
2544 if let Some(fork_url) = forking.json_rpc_url {
2545 self.reset_block_number(fork_url, fork_block_number).await?;
2546 } else {
2547 if let Some(fork_url) = target_rpc_url.clone() {
2550 self.reset_block_number(fork_url, fork_block_number).await?;
2551 }
2552
2553 let gas_limit = self.node_config.read().await.fork_gas_limit(&fork_block);
2554 let mut env = self.evm_env.write();
2555
2556 env.cfg_env.chain_id = fork.chain_id();
2557 env.block_env = BlockEnv {
2558 number: U256::from(fork_block_number),
2559 timestamp: U256::from(fork_block.header.timestamp()),
2560 gas_limit,
2561 difficulty: fork_block.header.difficulty(),
2562 prevrandao: Some(fork_block.header.mix_hash().unwrap_or_default()),
2563 beneficiary: env.block_env.beneficiary,
2565 basefee: env.block_env.basefee,
2566 ..env.block_env.clone()
2567 };
2568
2569 let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
2572 fork_block.header.gas_used(),
2573 gas_limit,
2574 fork_block.header.base_fee_per_gas().unwrap_or_default(),
2575 );
2576
2577 self.fees.set_base_fee(next_block_base_fee);
2578 }
2579
2580 self.time.reset(fork_block.header.timestamp());
2582 self.cheats.clear_next_block_prevrandao();
2584
2585 self.blockchain.storage.write().total_difficulty = fork.total_difficulty();
2587 }
2588 *self.blockchain.storage.write() = BlockchainStorage::forked(
2590 fork.block_number(),
2591 fork.block_hash(),
2592 fork.total_difficulty(),
2593 );
2594 self.states.write().clear();
2595 self.apply_genesis().await?;
2596 fork.set_database_rpc_url(target_rpc_url);
2597
2598 trace!(target: "backend", "reset fork");
2599
2600 Ok(())
2601 } else {
2602 Err(RpcError::invalid_params("Forking not enabled").into())
2603 }
2604 }
2605
2606 pub async fn reset_to_in_mem(&self) -> Result<(), BlockchainError> {
2608 *self.fork.write() = None;
2610
2611 let genesis_timestamp = self.genesis.timestamp;
2612 let genesis_number = self.genesis.number;
2613
2614 let reset_base_fee = self.fees.tempo_hardfork().map(crate::config::tempo_default_base_fee);
2618 let genesis_base_fee = reset_base_fee.unwrap_or_else(|| self.fees.base_fee());
2619
2620 {
2622 let mut env = self.evm_env.write();
2623 env.block_env.number = U256::from(genesis_number);
2624 env.block_env.timestamp = U256::from(genesis_timestamp);
2625 env.block_env.basefee = genesis_base_fee;
2627 env.block_env.prevrandao = Some(B256::ZERO);
2628 }
2629
2630 let base_fee = self.fees.is_eip1559().then_some(genesis_base_fee);
2632 *self.blockchain.storage.write() = BlockchainStorage::new(
2633 &self.evm_env.read(),
2634 base_fee,
2635 genesis_timestamp,
2636 genesis_number,
2637 self.is_tempo(),
2638 );
2639 self.states.write().clear();
2640
2641 self.db.write().await.clear();
2643
2644 self.time.reset(genesis_timestamp);
2646 self.cheats.clear_next_block_prevrandao();
2648
2649 if self.fees.is_eip1559() {
2653 let next_base_fee = match reset_base_fee {
2654 Some(genesis_base_fee) => {
2655 self.fees.set_base_fee(genesis_base_fee);
2658 let gas_limit = self.evm_env.read().block_env.gas_limit;
2659 self.fees.get_next_block_base_fee_per_gas(0, gas_limit, genesis_base_fee)
2660 }
2661 None => crate::eth::fees::INITIAL_BASE_FEE,
2662 };
2663 self.fees.set_base_fee(next_base_fee);
2664 }
2665
2666 self.fees.set_gas_price(crate::eth::fees::INITIAL_GAS_PRICE);
2667
2668 self.apply_genesis().await?;
2670
2671 trace!(target: "backend", "reset to fresh in-memory state");
2672
2673 Ok(())
2674 }
2675
2676 async fn reset_block_number(
2677 &self,
2678 fork_url: String,
2679 fork_block_number: u64,
2680 ) -> Result<(), BlockchainError> {
2681 let mut node_config = self.node_config.write().await;
2682 node_config.fork_choice = Some(ForkChoice::Block(fork_block_number as i128));
2683 node_config.fork_urls = vec![fork_url.clone()];
2685
2686 let mut evm_env = self.evm_env.read().clone();
2687 let (forked_db, client_fork_config) =
2688 node_config.setup_fork_db_config(fork_url, &mut evm_env, &self.fees).await?;
2689
2690 *self.db.write().await = Box::new(forked_db);
2691 let fork = ClientFork::new(client_fork_config, Arc::clone(&self.db));
2692 *self.fork.write() = Some(fork);
2693 *self.evm_env.write() = evm_env;
2694
2695 Ok(())
2696 }
2697
2698 pub async fn revert_state_snapshot(&self, id: U256) -> Result<bool, BlockchainError> {
2700 let block = { self.active_state_snapshots.lock().remove(&id) };
2701 if let Some((num, hash)) = block {
2702 let best_block_hash = {
2703 let current_height = self.best_number();
2705 let mut storage = self.blockchain.storage.write();
2706
2707 for n in ((num + 1)..=current_height).rev() {
2708 trace!(target: "backend", "reverting block {}", n);
2709 if let Some(hash) = storage.hashes.remove(&n)
2710 && let Some(block) = storage.blocks.remove(&hash)
2711 {
2712 for tx in block.body.transactions {
2713 let _ = storage.transactions.remove(&tx.hash());
2714 }
2715 }
2716 }
2717
2718 storage.best_number = num;
2719 storage.best_hash = hash;
2720 hash
2721 };
2722 let block =
2723 self.block_by_hash(best_block_hash).await?.ok_or(BlockchainError::BlockNotFound)?;
2724
2725 let reset_time = block.header.timestamp();
2726 self.time.reset(reset_time);
2727 self.cheats.clear_next_block_prevrandao();
2729
2730 let mut env = self.evm_env.write();
2731 env.block_env = BlockEnv {
2732 number: U256::from(num),
2733 timestamp: U256::from(block.header.timestamp()),
2734 difficulty: block.header.difficulty(),
2735 prevrandao: Some(block.header.mix_hash().unwrap_or_default()),
2737 gas_limit: block.header.gas_limit(),
2738 beneficiary: env.block_env.beneficiary,
2740 basefee: env.block_env.basefee,
2741 ..Default::default()
2742 }
2743 }
2744 Ok(self.db.write().await.revert_state(id, RevertStateSnapshotAction::RevertRemove))
2745 }
2746
2747 pub async fn inspect_tx(
2749 &self,
2750 tx: Arc<PoolTransaction<FoundryTxEnvelope>>,
2751 ) -> Result<
2752 (InstructionResult, Option<Output>, u64, State, Vec<revm::primitives::Log>),
2753 BlockchainError,
2754 > {
2755 let evm_env = self.next_evm_env();
2756 let db = self.db.read().await;
2757 let mut inspector = self.build_inspector();
2758 let (ResultAndState { result, state }, _) = self.transact_envelope_with_inspector_ref(
2759 &**db,
2760 &evm_env,
2761 &mut inspector,
2762 tx.pending_transaction.transaction.as_ref(),
2763 *tx.pending_transaction.sender(),
2764 )?;
2765 let (exit_reason, gas_used, out, logs) = unpack_execution_result(result);
2766
2767 inspector.print_logs();
2768
2769 if self.print_traces {
2770 inspector.print_traces(self.call_trace_decoder.clone());
2771 }
2772
2773 Ok((exit_reason, out, gas_used, state, logs))
2774 }
2775}
2776
2777impl<N: Network> Backend<N>
2778where
2779 N::ReceiptEnvelope: TxReceipt<Log = alloy_primitives::Log>,
2780{
2781 fn mined_logs_for_block(&self, filter: Filter, block: Block, block_hash: B256) -> Vec<Log> {
2783 let mut all_logs = Vec::new();
2784 let mut block_log_index = 0u32;
2785
2786 let storage = self.blockchain.storage.read();
2787
2788 for tx in block.body.transactions {
2789 let Some(tx) = storage.transactions.get(&tx.hash()) else {
2790 continue;
2791 };
2792
2793 let logs = tx.receipt.logs();
2794 let transaction_hash = tx.info.transaction_hash;
2795
2796 for log in logs {
2797 if filter.matches(log) {
2798 all_logs.push(Log {
2799 inner: log.clone(),
2800 block_hash: Some(block_hash),
2801 block_number: Some(block.header.number()),
2802 block_timestamp: Some(block.header.timestamp()),
2803 transaction_hash: Some(transaction_hash),
2804 transaction_index: Some(tx.info.transaction_index),
2805 log_index: Some(block_log_index as u64),
2806 removed: false,
2807 });
2808 }
2809 block_log_index += 1;
2810 }
2811 }
2812 all_logs
2813 }
2814
2815 fn removed_logs_since(&self, block_number: u64) -> Vec<Log> {
2822 let storage = self.blockchain.storage.read();
2823 let mut all_logs = Vec::new();
2824
2825 for num in (block_number + 1)..=storage.best_number {
2826 if let Some(hash) = storage.hashes.get(&num)
2827 && let Some(block) = storage.blocks.get(hash)
2828 {
2829 let mut block_log_index = 0u64;
2830 for tx in &block.body.transactions {
2831 if let Some(tx) = storage.transactions.get(&tx.hash()) {
2832 for log in tx.receipt.logs() {
2833 all_logs.push(Log {
2834 inner: log.clone(),
2835 block_hash: Some(*hash),
2836 block_number: Some(num),
2837 block_timestamp: Some(block.header.timestamp()),
2838 transaction_hash: Some(tx.info.transaction_hash),
2839 transaction_index: Some(tx.info.transaction_index),
2840 log_index: Some(block_log_index),
2841 removed: true,
2842 });
2843 block_log_index += 1;
2844 }
2845 }
2846 }
2847 }
2848 }
2849
2850 all_logs
2851 }
2852
2853 async fn logs_for_block(
2855 &self,
2856 filter: Filter,
2857 hash: B256,
2858 ) -> Result<Vec<Log>, BlockchainError> {
2859 if let Some(block) = self.blockchain.get_block_by_hash(&hash) {
2860 return Ok(self.mined_logs_for_block(filter, block, hash));
2861 }
2862
2863 if let Some(fork) = self.get_fork() {
2864 return Ok(fork.logs(&filter).await?);
2865 }
2866
2867 Err(BlockchainError::UnknownBlock)
2868 }
2869
2870 async fn logs_for_range(
2872 &self,
2873 filter: &Filter,
2874 mut from: u64,
2875 to: u64,
2876 ) -> Result<Vec<Log>, BlockchainError> {
2877 let mut all_logs = Vec::new();
2878
2879 if let Some(fork) = self.get_fork() {
2881 let to_on_fork = if fork.predates_fork(to) {
2882 to
2883 } else {
2884 fork.block_number()
2886 };
2887
2888 if fork.predates_fork_inclusive(from) {
2889 let filter = filter.clone().from_block(from).to_block(to_on_fork);
2891 all_logs = fork.logs(&filter).await?;
2892
2893 from = fork.block_number() + 1;
2895 }
2896 }
2897
2898 for number in from..=to {
2899 if let Some((block, hash)) = self.get_block_with_hash(number) {
2900 all_logs.extend(self.mined_logs_for_block(filter.clone(), block, hash));
2901 }
2902 }
2903
2904 Ok(all_logs)
2905 }
2906
2907 pub async fn logs(&self, filter: Filter) -> Result<Vec<Log>, BlockchainError> {
2909 trace!(target: "backend", "get logs [{:?}]", filter);
2910 if let Some(hash) = filter.get_block_hash() {
2911 self.logs_for_block(filter, hash).await
2912 } else {
2913 let best = self.best_number();
2914 let to_block =
2915 self.convert_block_number(filter.block_option.get_to_block().copied()).min(best);
2916 let from_block =
2917 self.convert_block_number(filter.block_option.get_from_block().copied());
2918 if from_block > best {
2919 return Err(BlockchainError::BlockOutOfRange(best, from_block));
2920 }
2921
2922 self.logs_for_range(&filter, from_block, to_block).await
2923 }
2924 }
2925
2926 pub fn mined_receipts(&self, hash: B256) -> Option<Vec<N::ReceiptEnvelope>> {
2928 let block = self.mined_block_by_hash(hash)?;
2929 let mut receipts = Vec::new();
2930 let storage = self.blockchain.storage.read();
2931 for tx in block.transactions.hashes() {
2932 let receipt = storage.transactions.get(&tx)?.receipt.clone();
2933 receipts.push(receipt);
2934 }
2935 Some(receipts)
2936 }
2937}
2938
2939impl<N: Network> Backend<N>
2941where
2942 Self: TransactionValidator<FoundryTxEnvelope>,
2943 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
2944{
2945 pub async fn mine_block(
2950 &self,
2951 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
2952 ) -> MinedBlockOutcome<FoundryTxEnvelope> {
2953 self.do_mine_block(pool_transactions).await
2954 }
2955
2956 #[allow(clippy::too_many_arguments)]
2958 fn build_block_info(
2959 &self,
2960 evm_env: &EvmEnv,
2961 parent_hash: B256,
2962 number: u64,
2963 state_root: B256,
2964 block_result: BlockExecutionResult<FoundryReceiptEnvelope>,
2965 transactions: Vec<MaybeImpersonatedTransaction<FoundryTxEnvelope>>,
2966 transaction_infos: Vec<TransactionInfo>,
2967 ) -> BlockInfo<N> {
2968 let spec_id = *evm_env.spec_id();
2969 let is_shanghai = spec_id >= SpecId::SHANGHAI;
2970 let is_cancun = spec_id >= SpecId::CANCUN;
2971 let is_prague = spec_id >= SpecId::PRAGUE;
2972
2973 let receipts_root = calculate_receipt_root(&block_result.receipts);
2974 let cumulative_blob_gas_used = is_cancun.then_some(block_result.blob_gas_used);
2975 let bloom = block_result.receipts.iter().fold(Bloom::default(), |mut b, r| {
2976 b.accrue_bloom(r.logs_bloom());
2977 b
2978 });
2979
2980 let header = Header {
2981 parent_hash,
2982 ommers_hash: Default::default(),
2983 beneficiary: evm_env.block_env.beneficiary,
2984 state_root,
2985 transactions_root: Default::default(),
2986 receipts_root,
2987 logs_bloom: bloom,
2988 difficulty: evm_env.block_env.difficulty,
2989 number,
2990 gas_limit: evm_env.block_env.gas_limit,
2991 gas_used: block_result.gas_used,
2992 timestamp: evm_env.block_env.timestamp.saturating_to(),
2993 extra_data: Default::default(),
2994 mix_hash: evm_env.block_env.prevrandao.unwrap_or_default(),
2995 nonce: Default::default(),
2996 base_fee_per_gas: (spec_id >= SpecId::LONDON).then_some(evm_env.block_env.basefee),
2997 parent_beacon_block_root: is_cancun.then_some(Default::default()),
2998 blob_gas_used: cumulative_blob_gas_used,
2999 excess_blob_gas: if is_cancun { evm_env.block_env.blob_excess_gas() } else { None },
3000 withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
3001 requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH),
3002 block_access_list_hash: None,
3003 slot_number: None,
3004 };
3005
3006 let block = create_block(FoundryHeader::new(header, self.is_tempo()), transactions);
3007 BlockInfo { block, transactions: transaction_infos, receipts: block_result.receipts }
3008 }
3009
3010 async fn do_mine_block(
3011 &self,
3012 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
3013 ) -> MinedBlockOutcome<FoundryTxEnvelope> {
3014 let _mining_guard = self.mining.lock().await;
3015 trace!(target: "backend", "creating new block with {} transactions", pool_transactions.len());
3016
3017 let (outcome, header, block_hash) = {
3018 let current_base_fee = self.base_fee();
3019 let current_excess_blob_gas_and_price = self.excess_blob_gas_and_price();
3020
3021 let mut evm_env = self.evm_env.read().clone();
3022
3023 if evm_env.block_env.basefee == 0 {
3024 evm_env.cfg_env.disable_base_fee = true;
3027 }
3028
3029 let block_number = self.blockchain.storage.read().best_number.saturating_add(1);
3030
3031 if is_arbitrum(evm_env.cfg_env.chain_id) {
3033 evm_env.block_env.number = U256::from(block_number);
3035 } else {
3036 evm_env.block_env.number = evm_env.block_env.number.saturating_add(U256::from(1));
3037 }
3038
3039 evm_env.block_env.basefee = current_base_fee;
3040 evm_env.block_env.blob_excess_gas_and_price = current_excess_blob_gas_and_price;
3041
3042 let best_hash = self.blockchain.storage.read().best_hash;
3043
3044 let mut input = [0u8; 40];
3045 input[..32].copy_from_slice(best_hash.as_slice());
3046 input[32..].copy_from_slice(&block_number.to_le_bytes());
3047 evm_env.block_env.prevrandao =
3051 Some(self.cheats.take_next_block_prevrandao().unwrap_or_else(|| keccak256(input)));
3052
3053 if self.prune_state_history_config.is_state_history_supported() {
3054 let db = self.db.read().await.current_state();
3055 self.states.write().insert(best_hash, db);
3057 }
3058
3059 let (block_info, included, invalid, not_yet_valid, block_hash) = {
3060 let mut db = self.db.write().await;
3061
3062 evm_env.block_env.timestamp = U256::from(self.time.next_timestamp());
3066
3067 let spec_id = *evm_env.spec_id();
3068
3069 let inspector_tx_config = self.inspector_tx_config();
3070 let gas_config = self.pool_tx_gas_config(&evm_env);
3071
3072 let (pool_result, block_result) = self.execute_with_block_executor(
3073 &mut **db,
3074 &evm_env,
3075 best_hash,
3076 spec_id,
3077 &pool_transactions,
3078 &gas_config,
3079 &inspector_tx_config,
3080 &|pending, account| {
3081 self.validate_pool_transaction_for(pending, account, &evm_env)
3082 },
3083 );
3084
3085 let included = pool_result.included;
3086 let invalid = pool_result.invalid;
3087 let not_yet_valid = pool_result.not_yet_valid;
3088
3089 let state_root = db.maybe_state_root().unwrap_or_default();
3090 let block_info = self.build_block_info(
3091 &evm_env,
3092 best_hash,
3093 block_number,
3094 state_root,
3095 block_result,
3096 pool_result.txs,
3097 pool_result.tx_info,
3098 );
3099
3100 let block_hash = block_info.block.header.hash_slow();
3102 db.insert_block_hash(U256::from(block_info.block.header.number()), block_hash);
3103
3104 (block_info, included, invalid, not_yet_valid, block_hash)
3105 };
3106
3107 let BlockInfo { block, transactions, receipts } = block_info;
3109
3110 let header = block.header.clone();
3111
3112 trace!(
3113 target: "backend",
3114 "Mined block {} with {} tx {:?}",
3115 block_number,
3116 transactions.len(),
3117 transactions.iter().map(|tx| tx.transaction_hash).collect::<Vec<_>>()
3118 );
3119 let mut storage = self.blockchain.storage.write();
3120 storage.best_number = block_number;
3122 storage.best_hash = block_hash;
3123 if !self.is_eip3675() {
3126 storage.total_difficulty =
3127 storage.total_difficulty.saturating_add(header.difficulty);
3128 }
3129
3130 storage.blocks.insert(block_hash, block);
3131 storage.hashes.insert(block_number, block_hash);
3132
3133 node_info!("");
3134 for (info, receipt) in transactions.into_iter().zip(receipts) {
3136 node_info!(" Transaction: {:?}", info.transaction_hash);
3138 if let Some(contract) = &info.contract_address {
3139 node_info!(" Contract created: {contract}");
3140 }
3141 node_info!(" Gas used: {}", receipt.cumulative_gas_used());
3142 if !info.exit.is_ok() {
3143 let r = RevertDecoder::new().decode(
3144 info.out.as_ref().map(|b| &b[..]).unwrap_or_default(),
3145 Some(info.exit),
3146 );
3147 node_info!(" Error: reverted with: {r}");
3148 }
3149 node_info!("");
3150
3151 let mined_tx = MinedTransaction { info, receipt, block_hash, block_number };
3152 storage.transactions.insert(mined_tx.info.transaction_hash, mined_tx);
3153 }
3154
3155 if let Some(transaction_block_keeper) = self.transaction_block_keeper
3157 && storage.blocks.len() > transaction_block_keeper
3158 {
3159 let to_clear = block_number
3160 .saturating_sub(transaction_block_keeper.try_into().unwrap_or(u64::MAX));
3161 storage.remove_block_transactions_by_number(to_clear)
3162 }
3163
3164 evm_env.block_env.difficulty = U256::from(0);
3166
3167 *self.evm_env.write() = evm_env;
3169
3170 let timestamp = utc_from_secs(header.timestamp);
3171
3172 node_info!(" Block Number: {}", block_number);
3173 node_info!(" Block Hash: {:?}", block_hash);
3174 if timestamp.year() > 9999 {
3175 node_info!(" Block Time: {:?}\n", timestamp.to_rfc3339());
3177 } else {
3178 node_info!(" Block Time: {:?}\n", timestamp.to_rfc2822());
3179 }
3180
3181 let outcome = MinedBlockOutcome { block_number, included, invalid, not_yet_valid };
3182
3183 (outcome, header, block_hash)
3184 };
3185 let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
3186 header.gas_used,
3187 header.gas_limit,
3188 header.base_fee_per_gas.unwrap_or_default(),
3189 );
3190 let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas(
3191 header.excess_blob_gas.unwrap_or_default(),
3192 header.blob_gas_used.unwrap_or_default(),
3193 );
3194
3195 self.fees.set_base_fee(next_block_base_fee);
3197
3198 self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
3199 next_block_excess_blob_gas,
3200 get_blob_base_fee_update_fraction_by_spec_id(*self.evm_env.read().spec_id()),
3201 ));
3202
3203 self.notify_on_new_block(header.into_inner(), block_hash);
3205
3206 outcome
3207 }
3208
3209 pub async fn reorg(
3216 &self,
3217 depth: u64,
3218 tx_pairs: HashMap<u64, Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>>,
3219 common_block: Block,
3220 ) -> Result<(), BlockchainError> {
3221 self.rollback(common_block).await?;
3222 for i in 0..depth {
3224 let to_be_mined = tx_pairs.get(&i).cloned().unwrap_or_else(Vec::new);
3225 let outcome = self.do_mine_block(to_be_mined).await;
3226 node_info!(
3227 " Mined reorg block number {}. With {} valid txs and with invalid {} txs",
3228 outcome.block_number,
3229 outcome.included.len(),
3230 outcome.invalid.len()
3231 );
3232 }
3233
3234 Ok(())
3235 }
3236
3237 pub async fn pending_block(
3241 &self,
3242 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
3243 ) -> BlockInfo<N> {
3244 self.with_pending_block(pool_transactions, |_, block| block).await
3245 }
3246
3247 pub async fn with_pending_block<F, T>(
3251 &self,
3252 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
3253 f: F,
3254 ) -> T
3255 where
3256 F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockInfo<N>) -> T,
3257 {
3258 let db = self.db.read().await;
3259 let evm_env = self.next_evm_env();
3260
3261 let mut cache_db = AnvilCacheDB::new(&*db);
3262
3263 let parent_hash = self.blockchain.storage.read().best_hash;
3264
3265 let spec_id = *evm_env.spec_id();
3266
3267 let inspector_tx_config = self.inspector_tx_config();
3268 let gas_config = self.pool_tx_gas_config(&evm_env);
3269
3270 let (pool_result, block_result) = self.execute_with_block_executor(
3271 &mut cache_db,
3272 &evm_env,
3273 parent_hash,
3274 spec_id,
3275 &pool_transactions,
3276 &gas_config,
3277 &inspector_tx_config,
3278 &|pending, account| self.validate_pool_transaction_for(pending, account, &evm_env),
3279 );
3280
3281 let cache_db = cache_db.0;
3283
3284 let state_root = cache_db.maybe_state_root().unwrap_or_default();
3285 let block_number = evm_env.block_env.number.saturating_to();
3286 let block_info = self.build_block_info(
3287 &evm_env,
3288 parent_hash,
3289 block_number,
3290 state_root,
3291 block_result,
3292 pool_result.txs,
3293 pool_result.tx_info,
3294 );
3295
3296 f(Box::new(cache_db), block_info)
3297 }
3298
3299 pub async fn get_fee_token_balance(
3304 &self,
3305 token: Address,
3306 account: Address,
3307 ) -> Result<U256, BlockchainError> {
3308 let mut calldata = vec![0x70, 0xa0, 0x82, 0x31];
3310 calldata.extend_from_slice(&[0u8; 12]);
3312 calldata.extend_from_slice(account.as_slice());
3313
3314 let request = WithOtherFields::new(TransactionRequest {
3315 from: Some(Address::ZERO),
3316 to: Some(TxKind::Call(token)),
3317 input: calldata.into(),
3318 ..Default::default()
3319 });
3320
3321 let fee_details = FeeDetails::zero();
3322 let (exit, out, _, _) = self.call(request, fee_details, None, Default::default()).await?;
3323
3324 if exit != InstructionResult::Return && exit != InstructionResult::Stop {
3326 return Ok(U256::ZERO);
3328 }
3329
3330 match out {
3332 Some(Output::Call(data)) if data.len() >= 32 => Ok(U256::from_be_slice(&data[..32])),
3333 _ => Ok(U256::ZERO),
3334 }
3335 }
3336
3337 pub async fn call(
3343 &self,
3344 request: WithOtherFields<TransactionRequest>,
3345 fee_details: FeeDetails,
3346 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3347 overrides: EvmOverrides,
3348 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
3349 self.with_database_at(block_request, |state, mut block| {
3350 let block_number = block.number;
3351 let (exit, out, gas, state) = {
3352 let mut cache_db = CacheDB::new(state);
3353 if let Some(state_overrides) = overrides.state {
3354 apply_state_overrides(state_overrides.into_iter().collect(), &mut cache_db)?;
3355 }
3356 if let Some(block_overrides) = overrides.block {
3357 cache_db.apply_block_overrides(*block_overrides, &mut block);
3358 }
3359 self.call_with_state(&cache_db, request, fee_details, block)
3360 }?;
3361 trace!(target: "backend", "call return {:?} out: {:?} gas {} on block {}", exit, out, gas, block_number);
3362 Ok((exit, out, gas, state))
3363 }).await?
3364 }
3365
3366 pub async fn call_with_tracing(
3367 &self,
3368 request: WithOtherFields<TransactionRequest>,
3369 fee_details: FeeDetails,
3370 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3371 opts: GethDebugTracingCallOptions,
3372 ) -> Result<GethTrace, BlockchainError> {
3373 let GethDebugTracingCallOptions {
3374 tracing_options,
3375 block_overrides,
3376 state_overrides,
3377 tx_index,
3378 } = opts;
3379
3380 if let Some(tx_index) = tx_index {
3381 return self
3382 .call_with_tracing_at_tx_index(
3383 request,
3384 fee_details,
3385 block_request,
3386 tx_index,
3387 tracing_options,
3388 state_overrides,
3389 block_overrides,
3390 )
3391 .await;
3392 }
3393
3394 self.with_database_at(block_request, |state, block| {
3395 let cache_db = CacheDB::new(state);
3396 self.trace_call_with_state(
3397 request,
3398 fee_details,
3399 block,
3400 cache_db,
3401 tracing_options,
3402 state_overrides,
3403 block_overrides,
3404 )
3405 })
3406 .await?
3407 }
3408
3409 #[allow(clippy::too_many_arguments)]
3410 async fn call_with_tracing_at_tx_index(
3411 &self,
3412 request: WithOtherFields<TransactionRequest>,
3413 fee_details: FeeDetails,
3414 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3415 tx_index: u64,
3416 tracing_options: GethDebugTracingOptions,
3417 state_overrides: Option<StateOverride>,
3418 block_overrides: Option<BlockOverrides>,
3419 ) -> Result<GethTrace, BlockchainError> {
3420 let tx_index = usize::try_from(tx_index).map_err(|_| {
3421 BlockchainError::RpcError(RpcError::invalid_params(format!(
3422 "tx_index {tx_index} does not fit in usize"
3423 )))
3424 })?;
3425 let block_number = match block_request {
3426 Some(BlockRequest::Pending(_)) => {
3427 return Err(BlockchainError::RpcError(RpcError::invalid_params(
3428 "tx_index is not supported for pending blocks".to_string(),
3429 )));
3430 }
3431 Some(BlockRequest::Number(number)) => number,
3432 None => self.best_number(),
3433 };
3434 let block_id = BlockId::Number(BlockNumber::Number(block_number));
3435
3436 if let Some(block) = self.get_block(block_id) {
3437 return self.mined_trace_call_at_tx_index(
3438 request,
3439 fee_details,
3440 &block,
3441 tx_index,
3442 tracing_options,
3443 state_overrides,
3444 block_overrides,
3445 );
3446 }
3447
3448 if let Some(fork) = self.get_fork()
3449 && fork.predates_fork_inclusive(block_number)
3450 {
3451 let opts = GethDebugTracingCallOptions {
3452 tracing_options,
3453 state_overrides,
3454 block_overrides,
3455 tx_index: Some(tx_index as u64),
3456 };
3457 return Ok(fork.debug_trace_call(request, block_id, opts).await?);
3458 }
3459
3460 Err(BlockchainError::BlockNotFound)
3461 }
3462
3463 #[allow(clippy::too_many_arguments)]
3464 fn mined_trace_call_at_tx_index(
3465 &self,
3466 request: WithOtherFields<TransactionRequest>,
3467 fee_details: FeeDetails,
3468 block: &Block,
3469 tx_index: usize,
3470 tracing_options: GethDebugTracingOptions,
3471 state_overrides: Option<StateOverride>,
3472 block_overrides: Option<BlockOverrides>,
3473 ) -> Result<GethTrace, BlockchainError> {
3474 let transaction_count = block.body.transactions.len();
3475 if tx_index >= transaction_count {
3476 return Err(BlockchainError::RpcError(RpcError::invalid_params(format!(
3477 "tx_index {tx_index} out of bounds for block with {transaction_count} transactions"
3478 ))));
3479 }
3480
3481 let pool_txs: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>> = block.body.transactions
3482 [..tx_index]
3483 .iter()
3484 .map(|tx| {
3485 let pending_tx =
3486 PendingTransaction::from_maybe_impersonated(tx.clone()).expect("is valid");
3487 Arc::new(PoolTransaction {
3488 pending_transaction: pending_tx,
3489 requires: vec![],
3490 provides: vec![],
3491 priority: crate::eth::pool::transactions::TransactionPriority(0),
3492 })
3493 })
3494 .collect();
3495
3496 let trace = |parent_state: &StateDb| -> Result<GethTrace, BlockchainError> {
3497 let mut cache_db =
3498 AnvilCacheDB::new(Box::new(parent_state) as Box<dyn MaybeFullDatabase + '_>);
3499
3500 let mut evm_env = self.evm_env.read().clone();
3501 evm_env.block_env = block_env_from_header(&block.header);
3502
3503 let spec_id = *evm_env.spec_id();
3504 let inspector_tx_config = self.inspector_tx_config();
3505 let gas_config = self.pool_tx_gas_config(&evm_env);
3506
3507 self.execute_with_block_executor(
3508 &mut cache_db,
3509 &evm_env,
3510 block.header.parent_hash,
3511 spec_id,
3512 &pool_txs,
3513 &gas_config,
3514 &inspector_tx_config,
3515 &|pending, account| self.validate_pool_transaction_for(pending, account, &evm_env),
3516 );
3517
3518 let cache_db = cache_db.0;
3519 self.trace_call_with_state(
3520 request,
3521 fee_details,
3522 evm_env.block_env,
3523 cache_db,
3524 tracing_options,
3525 state_overrides,
3526 block_overrides,
3527 )
3528 };
3529
3530 let read_guard = self.states.upgradable_read();
3531 if let Some(state) = read_guard.get_state(&block.header.parent_hash) {
3532 trace(state)
3533 } else {
3534 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
3535 let state = write_guard
3536 .get_on_disk_state(&block.header.parent_hash)
3537 .ok_or(BlockchainError::BlockNotFound)?;
3538 trace(state)
3539 }
3540 }
3541
3542 #[allow(clippy::too_many_arguments)]
3543 fn trace_call_with_state(
3544 &self,
3545 request: WithOtherFields<TransactionRequest>,
3546 fee_details: FeeDetails,
3547 mut block: BlockEnv,
3548 mut cache_db: CacheDB<Box<dyn MaybeFullDatabase + '_>>,
3549 tracing_options: GethDebugTracingOptions,
3550 state_overrides: Option<StateOverride>,
3551 block_overrides: Option<BlockOverrides>,
3552 ) -> Result<GethTrace, BlockchainError> {
3553 let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options;
3554 let block_number = block.number;
3555
3556 if let Some(state_overrides) = state_overrides {
3557 apply_state_overrides(state_overrides, &mut cache_db)?;
3558 }
3559 if let Some(block_overrides) = block_overrides {
3560 cache_db.apply_block_overrides(block_overrides, &mut block);
3561 }
3562
3563 if let Some(tracer) = tracer {
3564 return match tracer {
3565 GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
3566 GethDebugBuiltInTracerType::CallTracer => {
3567 let call_config = call_config_from_tracer_config(tracer_config)
3568 .map_err(|e| RpcError::invalid_params(e.to_string()))?;
3569
3570 let mut inspector = self.build_inspector().with_tracing_config(
3571 TracingInspectorConfig::from_geth_call_config(&call_config),
3572 );
3573
3574 let (evm_env, tx_env, op_deposit) =
3575 self.build_call_env(request, fee_details, block);
3576 let ResultAndState { result, state: _ } = self
3577 .transact_with_inspector_ref(
3578 &cache_db,
3579 &evm_env,
3580 &mut inspector,
3581 tx_env,
3582 op_deposit,
3583 )?;
3584
3585 inspector.print_logs();
3586 if self.print_traces {
3587 inspector.print_traces(self.call_trace_decoder.clone());
3588 }
3589
3590 let tracing_inspector = inspector.tracer.expect("tracer disappeared");
3591
3592 Ok(tracing_inspector
3593 .into_geth_builder()
3594 .geth_call_traces(call_config, result.tx_gas_used())
3595 .into())
3596 }
3597 GethDebugBuiltInTracerType::PreStateTracer => {
3598 let pre_state_config = tracer_config
3599 .into_pre_state_config()
3600 .map_err(|e| RpcError::invalid_params(e.to_string()))?;
3601
3602 let mut inspector = TracingInspector::new(
3603 TracingInspectorConfig::from_geth_prestate_config(&pre_state_config),
3604 );
3605
3606 let (evm_env, tx_env, op_deposit) =
3607 self.build_call_env(request, fee_details, block);
3608 let result = self.transact_with_inspector_ref(
3609 &cache_db,
3610 &evm_env,
3611 &mut inspector,
3612 tx_env,
3613 op_deposit,
3614 )?;
3615
3616 Ok(inspector
3617 .into_geth_builder()
3618 .geth_prestate_traces(&result, &pre_state_config, cache_db)?
3619 .into())
3620 }
3621 GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()),
3622 GethDebugBuiltInTracerType::FourByteTracer
3623 | GethDebugBuiltInTracerType::MuxTracer
3624 | GethDebugBuiltInTracerType::FlatCallTracer
3625 | GethDebugBuiltInTracerType::Erc7562Tracer => {
3626 Err(RpcError::invalid_params("unsupported tracer type").into())
3627 }
3628 },
3629 #[cfg(not(feature = "js-tracer"))]
3630 GethDebugTracerType::JsTracer(_) => {
3631 Err(RpcError::invalid_params("unsupported tracer type").into())
3632 }
3633 #[cfg(feature = "js-tracer")]
3634 GethDebugTracerType::JsTracer(code) => {
3635 let config = tracer_config.into_json();
3636 let mut inspector =
3637 revm_inspectors::tracing::js::JsInspector::new(code, config)
3638 .map_err(|err| BlockchainError::Message(err.to_string()))?;
3639
3640 let (evm_env, tx_env, op_deposit) =
3641 self.build_call_env(request, fee_details, block.clone());
3642 let result = self.transact_with_inspector_ref(
3643 &cache_db,
3644 &evm_env,
3645 &mut inspector,
3646 tx_env.clone(),
3647 op_deposit,
3648 )?;
3649 let res = inspector
3650 .json_result(result, &tx_env, &block, &cache_db)
3651 .map_err(|err| BlockchainError::Message(err.to_string()))?;
3652
3653 Ok(GethTrace::JS(res))
3654 }
3655 };
3656 }
3657
3658 let mut inspector = self
3660 .build_inspector()
3661 .with_tracing_config(TracingInspectorConfig::from_geth_config(&config));
3662
3663 let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block);
3664 let ResultAndState { result, state: _ } = self.transact_with_inspector_ref(
3665 &cache_db,
3666 &evm_env,
3667 &mut inspector,
3668 tx_env,
3669 op_deposit,
3670 )?;
3671
3672 let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
3673
3674 let tracing_inspector = inspector.tracer.expect("tracer disappeared");
3675 let return_value = out.as_ref().map(|o| o.data()).cloned().unwrap_or_default();
3676
3677 trace!(target: "backend", ?exit_reason, ?out, %gas_used, %block_number, "trace call");
3678
3679 let res = tracing_inspector
3680 .into_geth_builder()
3681 .geth_traces(gas_used, return_value, config)
3682 .into();
3683
3684 Ok(res)
3685 }
3686
3687 pub async fn with_database_at<F, T>(
3689 &self,
3690 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3691 f: F,
3692 ) -> Result<T, BlockchainError>
3693 where
3694 F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockEnv) -> T,
3695 {
3696 let block_number = match block_request {
3697 Some(BlockRequest::Pending(pool_transactions)) => {
3698 let result = self
3699 .with_pending_block(pool_transactions, |state, block| {
3700 let block = block.block;
3701 f(state, block_env_from_header(&block.header))
3702 })
3703 .await;
3704 return Ok(result);
3705 }
3706 Some(BlockRequest::Number(bn)) => Some(BlockNumber::Number(bn)),
3707 None => None,
3708 };
3709 let block_number = self.convert_block_number(block_number);
3710 let current_number = self.best_number();
3711
3712 if block_number > current_number {
3714 return Err(BlockchainError::BlockOutOfRange(current_number, block_number));
3715 }
3716
3717 if block_number < current_number {
3718 if let Some((block_hash, block)) = self
3719 .block_by_number(BlockNumber::Number(block_number))
3720 .await?
3721 .map(|block| (block.header.hash, block))
3722 {
3723 let read_guard = self.states.upgradable_read();
3724 if let Some(state_db) = read_guard.get_state(&block_hash) {
3725 return Ok(f(Box::new(state_db), block_env_from_header(&block.header)));
3726 }
3727
3728 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
3729 if let Some(state) = write_guard.get_on_disk_state(&block_hash) {
3730 return Ok(f(Box::new(state), block_env_from_header(&block.header)));
3731 }
3732 }
3733
3734 warn!(target: "backend", "Not historic state found for block={}", block_number);
3735 return Err(BlockchainError::BlockOutOfRange(current_number, block_number));
3736 }
3737
3738 let db = self.db.read().await;
3739 let block = self.evm_env.read().block_env.clone();
3740 Ok(f(Box::new(&**db), block))
3741 }
3742
3743 pub async fn storage_at(
3744 &self,
3745 address: Address,
3746 index: U256,
3747 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3748 ) -> Result<B256, BlockchainError> {
3749 self.with_database_at(block_request, |db, _| {
3750 trace!(target: "backend", "get storage for {:?} at {:?}", address, index);
3751 let val = db.storage_ref(address, index)?;
3752 Ok(val.into())
3753 })
3754 .await?
3755 }
3756
3757 pub async fn storage_values(
3759 &self,
3760 requests: HashMap<Address, Vec<B256>>,
3761 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3762 ) -> Result<HashMap<Address, Vec<B256>>, BlockchainError> {
3763 self.with_database_at(block_request, |db, _| {
3764 trace!(target: "backend", "get storage values for {} addresses", requests.len());
3765 let mut result: HashMap<Address, Vec<B256>> = HashMap::default();
3766 for (address, slots) in &requests {
3767 let mut values = Vec::with_capacity(slots.len());
3768 for slot in slots {
3769 let val = db.storage_ref(*address, (*slot).into())?;
3770 values.push(val.into());
3771 }
3772 result.insert(*address, values);
3773 }
3774 Ok(result)
3775 })
3776 .await?
3777 }
3778
3779 pub async fn get_code(
3784 &self,
3785 address: Address,
3786 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3787 ) -> Result<Bytes, BlockchainError> {
3788 self.with_database_at(block_request, |db, _| self.get_code_with_state(&db, address)).await?
3789 }
3790
3791 pub async fn get_balance(
3795 &self,
3796 address: Address,
3797 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3798 ) -> Result<U256, BlockchainError> {
3799 self.with_database_at(block_request, |db, _| self.get_balance_with_state(db, address))
3800 .await?
3801 }
3802
3803 pub async fn get_account_at_block(
3804 &self,
3805 address: Address,
3806 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3807 ) -> Result<TrieAccount, BlockchainError> {
3808 self.with_database_at(block_request, |block_db, _| {
3809 let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
3810 let account = db.get(&address).cloned().unwrap_or_default();
3811 let storage_root = storage_root(&account.storage);
3812 let code_hash = account.info.code_hash;
3813 let balance = account.info.balance;
3814 let nonce = account.info.nonce;
3815 Ok(TrieAccount { balance, nonce, code_hash, storage_root })
3816 })
3817 .await?
3818 }
3819
3820 pub async fn get_nonce(
3824 &self,
3825 address: Address,
3826 block_request: BlockRequest<FoundryTxEnvelope>,
3827 ) -> Result<u64, BlockchainError> {
3828 if let BlockRequest::Pending(pool_transactions) = &block_request
3829 && let Some(value) = get_pool_transactions_nonce(pool_transactions, address)
3830 {
3831 return Ok(value);
3832 }
3833 let final_block_request = match block_request {
3834 BlockRequest::Pending(_) => BlockRequest::Number(self.best_number()),
3835 BlockRequest::Number(bn) => BlockRequest::Number(bn),
3836 };
3837
3838 self.with_database_at(Some(final_block_request), |db, _| {
3839 trace!(target: "backend", "get nonce for {:?}", address);
3840 Ok(db.basic_ref(address)?.unwrap_or_default().nonce)
3841 })
3842 .await?
3843 }
3844
3845 fn replay_tx_with_inspector<I, F, T>(
3846 &self,
3847 hash: B256,
3848 mut inspector: I,
3849 f: F,
3850 ) -> Result<T, BlockchainError>
3851 where
3852 for<'a> I: BackendInspector<WrapDatabaseRef<&'a CacheDB<Box<&'a StateDb>>>> + 'a,
3853 for<'a> F:
3854 FnOnce(ResultAndState<HaltReason>, CacheDB<Box<&'a StateDb>>, I, TxEnv, EvmEnv) -> T,
3855 {
3856 let block = {
3857 let storage = self.blockchain.storage.read();
3858 let MinedTransaction { block_hash, .. } = storage
3859 .transactions
3860 .get(&hash)
3861 .cloned()
3862 .ok_or(BlockchainError::TransactionNotFound)?;
3863
3864 storage.blocks.get(&block_hash).cloned().ok_or(BlockchainError::BlockNotFound)?
3865 };
3866
3867 let index = block
3868 .body
3869 .transactions
3870 .iter()
3871 .position(|tx| tx.hash() == hash)
3872 .expect("transaction not found in block");
3873
3874 let pool_txs: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>> = block.body.transactions
3875 [..index]
3876 .iter()
3877 .map(|tx| {
3878 let pending_tx =
3879 PendingTransaction::from_maybe_impersonated(tx.clone()).expect("is valid");
3880 Arc::new(PoolTransaction {
3881 pending_transaction: pending_tx,
3882 requires: vec![],
3883 provides: vec![],
3884 priority: crate::eth::pool::transactions::TransactionPriority(0),
3885 })
3886 })
3887 .collect();
3888
3889 let trace = |parent_state: &StateDb| -> Result<T, BlockchainError> {
3890 let mut cache_db = AnvilCacheDB::new(Box::new(parent_state));
3891
3892 let mut evm_env = self.evm_env.read().clone();
3894
3895 evm_env.block_env = block_env_from_header(&block.header);
3896
3897 let spec_id = *evm_env.spec_id();
3898
3899 let inspector_tx_config = self.inspector_tx_config();
3900 let gas_config = self.pool_tx_gas_config(&evm_env);
3901
3902 self.execute_with_block_executor(
3903 &mut cache_db,
3904 &evm_env,
3905 block.header.parent_hash,
3906 spec_id,
3907 &pool_txs,
3908 &gas_config,
3909 &inspector_tx_config,
3910 &|pending, account| self.validate_pool_transaction_for(pending, account, &evm_env),
3911 );
3912
3913 let cache_db = cache_db.0;
3915
3916 let target_tx = block.body.transactions[index].clone();
3917 let target_tx = PendingTransaction::from_maybe_impersonated(target_tx)?;
3918 let (result, base_tx_env) = self.transact_envelope_with_inspector_ref(
3919 &cache_db,
3920 &evm_env,
3921 &mut inspector,
3922 target_tx.transaction.as_ref(),
3923 *target_tx.sender(),
3924 )?;
3925
3926 Ok(f(result, cache_db, inspector, base_tx_env, evm_env))
3927 };
3928
3929 let read_guard = self.states.upgradable_read();
3930 if let Some(state) = read_guard.get_state(&block.header.parent_hash) {
3931 trace(state)
3932 } else {
3933 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
3934 let state = write_guard
3935 .get_on_disk_state(&block.header.parent_hash)
3936 .ok_or(BlockchainError::BlockNotFound)?;
3937 trace(state)
3938 }
3939 }
3940
3941 #[cfg(feature = "js-tracer")]
3943 pub async fn trace_tx_with_js_tracer(
3944 &self,
3945 hash: B256,
3946 code: String,
3947 opts: GethDebugTracingOptions,
3948 ) -> Result<GethTrace, BlockchainError> {
3949 let GethDebugTracingOptions { tracer_config, .. } = opts;
3950 let config = tracer_config.into_json();
3951 let inspector = revm_inspectors::tracing::js::JsInspector::new(code, config)
3952 .map_err(|err| BlockchainError::Message(err.to_string()))?;
3953 let trace = self.replay_tx_with_inspector(
3954 hash,
3955 inspector,
3956 |result, cache_db, mut inspector, tx_env, evm_env| {
3957 inspector
3958 .json_result(
3959 result,
3960 &alloy_evm::IntoTxEnv::into_tx_env(tx_env),
3961 &evm_env.block_env,
3962 &cache_db,
3963 )
3964 .map_err(|e| BlockchainError::Message(e.to_string()))
3965 },
3966 )??;
3967 Ok(GethTrace::JS(trace))
3968 }
3969
3970 pub async fn prove_account_at(
3974 &self,
3975 address: Address,
3976 keys: Vec<B256>,
3977 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3978 ) -> Result<AccountProof, BlockchainError> {
3979 let block_number = block_request.as_ref().map(|r| r.block_number());
3980
3981 self.with_database_at(block_request, |block_db, _| {
3982 trace!(target: "backend", "get proof for {:?} at {:?}", address, block_number);
3983 let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
3984 let account = db.get(&address).cloned().unwrap_or_default();
3985
3986 let mut builder = HashBuilder::default()
3987 .with_proof_retainer(ProofRetainer::new(vec![Nibbles::unpack(keccak256(address))]));
3988
3989 for (key, account) in trie_accounts(db) {
3990 builder.add_leaf(key, &account);
3991 }
3992
3993 let _ = builder.root();
3994
3995 let proof = builder
3996 .take_proof_nodes()
3997 .into_nodes_sorted()
3998 .into_iter()
3999 .map(|(_, v)| v)
4000 .collect();
4001 let (storage_hash, storage_proofs) = prove_storage(&account.storage, &keys);
4002
4003 let account_proof = AccountProof {
4004 address,
4005 balance: account.info.balance,
4006 nonce: account.info.nonce,
4007 code_hash: account.info.code_hash,
4008 storage_hash,
4009 account_proof: proof,
4010 storage_proof: keys
4011 .into_iter()
4012 .zip(storage_proofs)
4013 .map(|(key, proof)| {
4014 let storage_key: U256 = key.into();
4015 let value = account.storage.get(&storage_key).copied().unwrap_or_default();
4016 StorageProof { key: JsonStorageKey::Hash(key), value, proof }
4017 })
4018 .collect(),
4019 };
4020
4021 Ok(account_proof)
4022 })
4023 .await?
4024 }
4025}
4026
4027impl<N: Network> Backend<N>
4028where
4029 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
4030{
4031 pub async fn trace_transaction_opcode_gas(
4033 &self,
4034 hash: B256,
4035 ) -> Result<Option<TransactionOpcodeGas>, BlockchainError> {
4036 match self.replay_tx_with_inspector(
4037 hash,
4038 OpcodeGasInspector::default(),
4039 move |_, _, inspector, _, _| TransactionOpcodeGas {
4040 transaction_hash: hash,
4041 opcode_gas: inspector.opcode_gas_iter().collect(),
4042 },
4043 ) {
4044 Ok(trace) => Ok(Some(trace)),
4045 Err(BlockchainError::TransactionNotFound) => {
4046 if let Some(fork) = self.get_fork() {
4047 return Ok(fork.trace_transaction_opcode_gas(hash).await?);
4048 }
4049
4050 Ok(None)
4051 }
4052 Err(err) => Err(err),
4053 }
4054 }
4055
4056 pub async fn trace_block_opcode_gas(
4058 &self,
4059 block_id: BlockId,
4060 ) -> Result<Option<BlockOpcodeGas>, BlockchainError> {
4061 if let Some((block, block_hash)) = self.get_block_with_hash(block_id) {
4062 return self.mined_block_opcode_gas(&block, block_hash).map(Some);
4063 }
4064
4065 if let Some(fork) = self.get_fork() {
4066 let number = self.ensure_block_number(Some(block_id)).await?;
4067 if fork.predates_fork_inclusive(number) {
4068 return Ok(fork.trace_block_opcode_gas(block_id).await?);
4069 }
4070 }
4071
4072 Err(BlockchainError::BlockNotFound)
4073 }
4074
4075 fn mined_block_opcode_gas(
4076 &self,
4077 block: &Block,
4078 block_hash: B256,
4079 ) -> Result<BlockOpcodeGas, BlockchainError> {
4080 if block.body.transactions.is_empty() {
4081 return Ok(BlockOpcodeGas {
4082 block_hash,
4083 block_number: block.header.number(),
4084 transactions: Vec::new(),
4085 });
4086 }
4087
4088 let parent_hash = block.header.parent_hash;
4089
4090 let trace = |parent_state: &StateDb| -> Result<Vec<TransactionOpcodeGas>, BlockchainError> {
4091 let mut cache_db = CacheDB::new(Box::new(parent_state));
4092 let mut transactions = Vec::with_capacity(block.body.transactions.len());
4093
4094 let mut evm_env = self.evm_env.read().clone();
4095 evm_env.block_env = block_env_from_header(&block.header);
4096
4097 for tx_envelope in &block.body.transactions {
4098 let mut inspector = OpcodeGasInspector::default();
4099 let pending_tx = PendingTransaction::from_maybe_impersonated(tx_envelope.clone())?;
4100 let (result, _) = self.transact_envelope_with_inspector_ref(
4101 &cache_db,
4102 &evm_env,
4103 &mut inspector,
4104 pending_tx.transaction.as_ref(),
4105 *pending_tx.sender(),
4106 )?;
4107
4108 transactions.push(TransactionOpcodeGas {
4109 transaction_hash: tx_envelope.hash(),
4110 opcode_gas: inspector.opcode_gas_iter().collect(),
4111 });
4112
4113 cache_db.commit(result.state);
4114 }
4115
4116 Ok(transactions)
4117 };
4118
4119 let read_guard = self.states.upgradable_read();
4120 let transactions = if let Some(state) = read_guard.get_state(&parent_hash) {
4121 trace(state)?
4122 } else {
4123 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
4124 let state = write_guard
4125 .get_on_disk_state(&parent_hash)
4126 .ok_or(BlockchainError::BlockNotFound)?;
4127 trace(state)?
4128 };
4129
4130 Ok(BlockOpcodeGas { block_hash, block_number: block.header.number(), transactions })
4131 }
4132
4133 pub async fn debug_account_info_at(
4135 &self,
4136 block_id: BlockId,
4137 tx_index: Index,
4138 address: Address,
4139 ) -> Result<Option<RpcAccountInfo>, BlockchainError> {
4140 if let Some((block, _)) = self.get_block_with_hash(block_id) {
4141 return self.mined_debug_account_info_at(&block, tx_index, address).map(Some);
4142 }
4143
4144 if let Some(fork) = self.get_fork() {
4145 let number = self.ensure_block_number(Some(block_id)).await?;
4146 if fork.predates_fork_inclusive(number) {
4147 let resolved = match block_id {
4151 BlockId::Hash(_) => block_id,
4152 _ => BlockId::number(number),
4153 };
4154 return Ok(fork.debug_account_info_at(resolved, tx_index, address).await?);
4155 }
4156 }
4157
4158 Err(BlockchainError::BlockNotFound)
4159 }
4160
4161 fn mined_debug_account_info_at(
4162 &self,
4163 block: &Block,
4164 tx_index: Index,
4165 address: Address,
4166 ) -> Result<RpcAccountInfo, BlockchainError> {
4167 let tx_index = tx_index.0;
4168 let transaction_count = block.body.transactions.len();
4169 if tx_index >= transaction_count {
4170 return Err(BlockchainError::RpcError(RpcError::invalid_params(format!(
4171 "tx_index {tx_index} out of bounds for block with {transaction_count} transactions"
4172 ))));
4173 }
4174
4175 let pool_txs: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>> = block.body.transactions
4176 [..=tx_index]
4177 .iter()
4178 .map(|tx| {
4179 let pending_tx =
4180 PendingTransaction::from_maybe_impersonated(tx.clone()).expect("is valid");
4181 Arc::new(PoolTransaction {
4182 pending_transaction: pending_tx,
4183 requires: vec![],
4184 provides: vec![],
4185 priority: crate::eth::pool::transactions::TransactionPriority(0),
4186 })
4187 })
4188 .collect();
4189
4190 let trace = |parent_state: &StateDb| -> Result<RpcAccountInfo, BlockchainError> {
4191 let mut cache_db = AnvilCacheDB::new(Box::new(parent_state));
4192 let mut evm_env = self.evm_env.read().clone();
4193 evm_env.block_env = block_env_from_header(&block.header);
4194
4195 let spec_id = *evm_env.spec_id();
4196 let inspector_tx_config = self.inspector_tx_config();
4197 let gas_config = self.pool_tx_gas_config(&evm_env);
4198
4199 self.execute_with_block_executor(
4200 &mut cache_db,
4201 &evm_env,
4202 block.header.parent_hash,
4203 spec_id,
4204 &pool_txs,
4205 &gas_config,
4206 &inspector_tx_config,
4207 &|pending, account| self.validate_pool_transaction_for(pending, account, &evm_env),
4208 );
4209
4210 let cache_db = cache_db.0;
4211 let account = revm::DatabaseRef::basic_ref(&cache_db, address)?.unwrap_or_default();
4212 let code = self.get_code_with_state(&cache_db, address)?;
4213 Ok(RpcAccountInfo { balance: account.balance, nonce: account.nonce, code })
4214 };
4215
4216 let read_guard = self.states.upgradable_read();
4217 if let Some(state) = read_guard.get_state(&block.header.parent_hash) {
4218 trace(state)
4219 } else {
4220 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
4221 let state = write_guard
4222 .get_on_disk_state(&block.header.parent_hash)
4223 .ok_or(BlockchainError::BlockNotFound)?;
4224 trace(state)
4225 }
4226 }
4227
4228 pub async fn rollback(&self, common_block: Block) -> Result<(), BlockchainError> {
4233 let hash = common_block.header.hash_slow();
4234
4235 let common_state = {
4237 let return_state_or_throw_err =
4238 |db: Option<&StateDb>| -> Result<AddressMap<DbAccount>, BlockchainError> {
4239 let state_db = db.ok_or(BlockchainError::DataUnavailable)?;
4240 let db_full =
4241 state_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
4242 Ok(db_full.clone())
4243 };
4244
4245 let read_guard = self.states.upgradable_read();
4246 if let Some(db) = read_guard.get_state(&hash) {
4247 return_state_or_throw_err(Some(db))?
4248 } else {
4249 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
4250 return_state_or_throw_err(write_guard.get_on_disk_state(&hash))?
4251 }
4252 };
4253
4254 {
4255 let removed_logs = self.removed_logs_since(common_block.header.number());
4258
4259 let removed_blocks =
4261 self.blockchain.storage.write().unwind_to(common_block.header.number(), hash);
4262
4263 let removed_hashes: Vec<_> =
4265 removed_blocks.iter().map(|b| b.header.hash_slow()).collect();
4266 self.states.write().remove_block_states(&removed_hashes);
4267
4268 if !removed_logs.is_empty() {
4271 self.notify_on_removed_logs(removed_logs);
4272 }
4273
4274 let mut env = self.evm_env.write();
4276 env.block_env.number = U256::from(common_block.header.number());
4277 env.block_env.timestamp = U256::from(common_block.header.timestamp());
4278 env.block_env.gas_limit = common_block.header.gas_limit();
4279 env.block_env.difficulty = common_block.header.difficulty();
4280 env.block_env.prevrandao = common_block.header.mix_hash();
4281
4282 self.time.reset(env.block_env.timestamp.saturating_to());
4283 self.cheats.clear_next_block_prevrandao();
4285 }
4286
4287 {
4288 let block_hashes: Vec<_> = {
4292 let storage = self.blockchain.storage.read();
4293 let min_block = common_block.header.number().saturating_sub(256);
4294 storage
4295 .hashes
4296 .iter()
4297 .filter(|(num, _)| **num >= min_block)
4298 .map(|(&num, &hash)| (num, hash))
4299 .collect()
4300 };
4301
4302 let mut db = self.db.write().await;
4304 db.clear();
4305
4306 for (address, acc) in common_state {
4308 db.insert_account(address, acc.info);
4309 for (key, value) in acc.storage {
4310 db.set_storage_at(address, key.into(), value.into())?;
4311 }
4312 }
4313
4314 for (block_num, hash) in block_hashes {
4317 db.insert_block_hash(U256::from(block_num), hash);
4318 }
4319 }
4320
4321 Ok(())
4322 }
4323
4324 pub async fn debug_trace_transaction(
4326 &self,
4327 hash: B256,
4328 opts: GethDebugTracingOptions,
4329 ) -> Result<GethTrace, BlockchainError> {
4330 #[cfg(feature = "js-tracer")]
4331 if let Some(tracer_type) = opts.tracer.as_ref()
4332 && tracer_type.is_js()
4333 {
4334 return self
4335 .trace_tx_with_js_tracer(hash, tracer_type.as_str().to_string(), opts.clone())
4336 .await;
4337 }
4338
4339 if let Some(trace) = self.mined_geth_trace_transaction(hash, opts.clone()).await {
4340 return trace;
4341 }
4342
4343 if let Some(fork) = self.get_fork() {
4344 return Ok(fork.debug_trace_transaction(hash, opts).await?);
4345 }
4346
4347 Ok(GethTrace::Default(Default::default()))
4348 }
4349
4350 pub async fn debug_trace_block(
4352 &self,
4353 rlp_block: Bytes,
4354 opts: GethDebugTracingOptions,
4355 ) -> Result<Vec<TraceResult>, BlockchainError> {
4356 let mut rlp = rlp_block.as_ref();
4357 let block = Block::<FoundryTxEnvelope>::decode(&mut rlp).map_err(|err| {
4358 BlockchainError::RpcError(RpcError::invalid_params(format!(
4359 "failed to decode block: {err}"
4360 )))
4361 })?;
4362 if !rlp.is_empty() {
4363 return Err(BlockchainError::RpcError(RpcError::invalid_params(
4364 "failed to decode block: trailing bytes".to_string(),
4365 )));
4366 }
4367
4368 self.debug_trace_block_by_hash(block.header.hash_slow(), opts).await
4369 }
4370
4371 pub async fn debug_trace_block_by_hash(
4373 &self,
4374 block_hash: B256,
4375 opts: GethDebugTracingOptions,
4376 ) -> Result<Vec<TraceResult>, BlockchainError> {
4377 if let Some(block) = self.blockchain.get_block_by_hash(&block_hash) {
4378 let mut traces = Vec::new();
4379 for tx in &block.body.transactions {
4380 let tx_hash = tx.hash();
4381 match self.debug_trace_transaction(tx_hash, opts.clone()).await {
4382 Ok(trace) => {
4383 traces.push(TraceResult::Success { result: trace, tx_hash: Some(tx_hash) });
4384 }
4385 Err(error) => {
4386 traces.push(TraceResult::Error {
4387 error: error.to_string(),
4388 tx_hash: Some(tx_hash),
4389 });
4390 }
4391 }
4392 }
4393 return Ok(traces);
4394 }
4395
4396 if let Some(fork) = self.get_fork() {
4397 return Ok(fork.debug_trace_block_by_hash(block_hash, opts).await?);
4398 }
4399
4400 Err(BlockchainError::BlockNotFound)
4401 }
4402
4403 pub async fn debug_trace_block_by_number(
4405 &self,
4406 block_number: BlockNumber,
4407 opts: GethDebugTracingOptions,
4408 ) -> Result<Vec<TraceResult>, BlockchainError> {
4409 let number = self.convert_block_number(Some(block_number));
4410
4411 if let Some(block) = self.get_block(BlockId::Number(BlockNumber::Number(number))) {
4412 let mut traces = Vec::new();
4413 for tx in &block.body.transactions {
4414 let tx_hash = tx.hash();
4415 match self.debug_trace_transaction(tx_hash, opts.clone()).await {
4416 Ok(trace) => {
4417 traces.push(TraceResult::Success { result: trace, tx_hash: Some(tx_hash) });
4418 }
4419 Err(error) => {
4420 traces.push(TraceResult::Error {
4421 error: error.to_string(),
4422 tx_hash: Some(tx_hash),
4423 });
4424 }
4425 }
4426 }
4427 return Ok(traces);
4428 }
4429
4430 if let Some(fork) = self.get_fork() {
4431 return Ok(fork.debug_trace_block_by_number(number, opts).await?);
4432 }
4433
4434 Err(BlockchainError::BlockNotFound)
4435 }
4436
4437 fn geth_trace(
4438 &self,
4439 tx: &MinedTransaction<N>,
4440 opts: GethDebugTracingOptions,
4441 ) -> Result<GethTrace, BlockchainError> {
4442 let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts;
4443
4444 if let Some(tracer) = tracer {
4445 match tracer {
4446 GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
4447 GethDebugBuiltInTracerType::FourByteTracer => {
4448 let inspector = FourByteInspector::default();
4449 let res = self.replay_tx_with_inspector(
4450 tx.info.transaction_hash,
4451 inspector,
4452 |_, _, inspector, _, _| FourByteFrame::from(inspector).into(),
4453 )?;
4454 return Ok(res);
4455 }
4456 GethDebugBuiltInTracerType::CallTracer => {
4457 return match call_config_from_tracer_config(tracer_config) {
4458 Ok(call_config) => {
4459 let inspector = TracingInspector::new(
4460 TracingInspectorConfig::from_geth_call_config(&call_config),
4461 );
4462 let frame = self.replay_tx_with_inspector(
4463 tx.info.transaction_hash,
4464 inspector,
4465 |_, _, inspector, _, _| {
4466 inspector
4467 .geth_builder()
4468 .geth_call_traces(
4469 call_config,
4470 tx.receipt.cumulative_gas_used(),
4471 )
4472 .into()
4473 },
4474 )?;
4475 Ok(frame)
4476 }
4477 Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
4478 };
4479 }
4480 GethDebugBuiltInTracerType::PreStateTracer => {
4481 return match tracer_config.into_pre_state_config() {
4482 Ok(pre_state_config) => {
4483 let inspector = TracingInspector::new(
4484 TracingInspectorConfig::from_geth_prestate_config(
4485 &pre_state_config,
4486 ),
4487 );
4488 let frame = self.replay_tx_with_inspector(
4489 tx.info.transaction_hash,
4490 inspector,
4491 |state, db, inspector, _, _| {
4492 inspector.geth_builder().geth_prestate_traces(
4493 &state,
4494 &pre_state_config,
4495 db,
4496 )
4497 },
4498 )??;
4499 Ok(frame.into())
4500 }
4501 Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
4502 };
4503 }
4504 GethDebugBuiltInTracerType::NoopTracer
4505 | GethDebugBuiltInTracerType::MuxTracer
4506 | GethDebugBuiltInTracerType::Erc7562Tracer
4507 | GethDebugBuiltInTracerType::FlatCallTracer => {}
4508 },
4509 GethDebugTracerType::JsTracer(_code) => {}
4510 }
4511
4512 return Ok(NoopFrame::default().into());
4513 }
4514
4515 Ok(GethTraceBuilder::new(tx.info.traces.clone())
4517 .geth_traces(
4518 tx.receipt.cumulative_gas_used(),
4519 tx.info.out.clone().unwrap_or_default(),
4520 config,
4521 )
4522 .into())
4523 }
4524
4525 async fn mined_geth_trace_transaction(
4526 &self,
4527 hash: B256,
4528 opts: GethDebugTracingOptions,
4529 ) -> Option<Result<GethTrace, BlockchainError>> {
4530 self.blockchain.storage.read().transactions.get(&hash).map(|tx| self.geth_trace(tx, opts))
4531 }
4532
4533 fn get_receipts(
4535 &self,
4536 tx_hashes: impl IntoIterator<Item = TxHash>,
4537 ) -> Vec<FoundryReceiptEnvelope> {
4538 let storage = self.blockchain.storage.read();
4539 let mut receipts = vec![];
4540
4541 for hash in tx_hashes {
4542 if let Some(tx) = storage.transactions.get(&hash) {
4543 receipts.push(tx.receipt.clone());
4544 }
4545 }
4546
4547 receipts
4548 }
4549
4550 pub async fn transaction_receipt(
4551 &self,
4552 hash: B256,
4553 ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
4554 if let Some(receipt) = self.mined_transaction_receipt(hash) {
4555 return Ok(Some(receipt.inner));
4556 }
4557
4558 if let Some(fork) = self.get_fork() {
4559 let receipt = fork.transaction_receipt(hash).await?;
4560 let number = self.convert_block_number(
4561 receipt.clone().and_then(|r| r.block_number()).map(BlockNumber::from),
4562 );
4563
4564 if fork.predates_fork_inclusive(number) {
4565 return Ok(receipt);
4566 }
4567 }
4568
4569 Ok(None)
4570 }
4571
4572 pub fn mined_block_receipts(&self, id: impl Into<BlockId>) -> Option<Vec<FoundryTxReceipt>> {
4574 let mut receipts = Vec::new();
4575 let block = self.get_block(id)?;
4576
4577 for transaction in block.body.transactions {
4578 let receipt = self.mined_transaction_receipt(transaction.hash())?;
4579 receipts.push(receipt.inner);
4580 }
4581
4582 Some(receipts)
4583 }
4584
4585 pub(crate) fn mined_transaction_receipt(
4587 &self,
4588 hash: B256,
4589 ) -> Option<MinedTransactionReceipt<FoundryNetwork>> {
4590 let MinedTransaction { info, receipt: tx_receipt, block_hash, .. } =
4591 self.blockchain.get_transaction_by_hash(&hash)?;
4592
4593 let index = info.transaction_index as usize;
4594 let block = self.blockchain.get_block_by_hash(&block_hash)?;
4595 let transaction = block.body.transactions[index].clone();
4596
4597 let excess_blob_gas = block.header.excess_blob_gas();
4599 let blob_gas_price =
4600 alloy_eips::eip4844::calc_blob_gasprice(excess_blob_gas.unwrap_or_default());
4601 let blob_gas_used = transaction.blob_gas_used();
4602
4603 let effective_gas_price = transaction.effective_gas_price(block.header.base_fee_per_gas());
4604
4605 let receipts = self.get_receipts(block.body.transactions.iter().map(|tx| tx.hash()));
4606 let next_log_index = receipts[..index].iter().map(|r| r.logs().len()).sum::<usize>();
4607
4608 let tx_receipt = tx_receipt.convert_logs_rpc(
4609 BlockNumHash::new(block.header.number(), block_hash),
4610 block.header.timestamp(),
4611 info.transaction_hash,
4612 info.transaction_index,
4613 next_log_index,
4614 );
4615
4616 let receipt = TransactionReceipt {
4617 inner: tx_receipt,
4618 transaction_hash: info.transaction_hash,
4619 transaction_index: Some(info.transaction_index),
4620 block_number: Some(block.header.number()),
4621 gas_used: info.gas_used,
4622 contract_address: info.contract_address,
4623 effective_gas_price,
4624 block_hash: Some(block_hash),
4625 from: info.from,
4626 to: info.to,
4627 blob_gas_price: Some(blob_gas_price),
4628 blob_gas_used,
4629 };
4630
4631 let mut inner = FoundryTxReceipt::with_timestamp(receipt, block.header.timestamp());
4633 if self.is_tempo() {
4634 inner = inner.with_fee_payer(info.from);
4635 }
4636 Some(MinedTransactionReceipt { inner, out: info.out })
4637 }
4638
4639 pub async fn block_receipts(
4641 &self,
4642 number: BlockId,
4643 ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
4644 if let Some(receipts) = self.mined_block_receipts(number) {
4645 return Ok(Some(receipts));
4646 }
4647
4648 if let Some(fork) = self.get_fork() {
4649 let number = match self.ensure_block_number(Some(number)).await {
4650 Err(_) => return Ok(None),
4651 Ok(n) => n,
4652 };
4653
4654 if fork.predates_fork_inclusive(number) {
4655 let receipts = fork.block_receipts(number).await?;
4656
4657 return Ok(receipts);
4658 }
4659 }
4660
4661 Ok(None)
4662 }
4663}
4664
4665impl<N: Network<ReceiptEnvelope = FoundryReceiptEnvelope>> Backend<N> {
4666 pub async fn serialized_state(
4668 &self,
4669 preserve_historical_states: bool,
4670 ) -> Result<SerializableState, BlockchainError> {
4671 let at = self.evm_env.read().block_env.clone();
4672 let best_number = self.blockchain.storage.read().best_number;
4673 let blocks = self.blockchain.storage.read().serialized_blocks();
4674 let transactions = self.blockchain.storage.read().serialized_transactions();
4675 let historical_states =
4676 preserve_historical_states.then(|| self.states.write().serialized_states());
4677
4678 let state = self.db.read().await.dump_state(
4679 at,
4680 best_number,
4681 blocks,
4682 transactions,
4683 historical_states,
4684 )?;
4685 state.ok_or_else(|| {
4686 RpcError::invalid_params("Dumping state not supported with the current configuration")
4687 .into()
4688 })
4689 }
4690
4691 pub async fn dump_state(
4693 &self,
4694 preserve_historical_states: bool,
4695 ) -> Result<Bytes, BlockchainError> {
4696 let state = self.serialized_state(preserve_historical_states).await?;
4697 let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
4698 encoder
4699 .write_all(&serde_json::to_vec(&state).unwrap_or_default())
4700 .map_err(|_| BlockchainError::DataUnavailable)?;
4701 Ok(encoder.finish().unwrap_or_default().into())
4702 }
4703
4704 pub async fn load_state(&self, state: SerializableState) -> Result<bool, BlockchainError> {
4706 {
4709 let mut storage = self.blockchain.storage.write();
4710 storage.load_blocks(state.blocks.clone());
4711 storage.load_transactions(state.transactions.clone());
4712 }
4713 if let Some(block) = state.block.clone() {
4715 {
4716 let mut env = self.evm_env.write();
4717 env.block_env = block.clone();
4718 if self.is_tempo() && self.is_fork() && env.block_env.beneficiary.is_zero() {
4719 env.block_env.beneficiary = TIP_FEE_MANAGER_ADDRESS;
4720 }
4721 }
4722
4723 let fork_num_and_hash = self.get_fork().map(|f| (f.block_number(), f.block_hash()));
4726
4727 let best_number = state.best_block_number.unwrap_or(block.number.saturating_to());
4728 let selected_best_number = if let Some((number, hash)) = fork_num_and_hash {
4729 trace!(target: "backend", state_block_number=?best_number, fork_block_number=?number);
4730 if best_number > number {
4734 self.blockchain.storage.write().best_number = best_number;
4735 let best_hash = self
4736 .blockchain
4737 .storage
4738 .read()
4739 .hash(best_number.into(), self.slots_in_an_epoch)
4740 .ok_or_else(|| {
4741 BlockchainError::RpcError(RpcError::internal_error_with(format!(
4742 "Best hash not found for best number {best_number}",
4743 )))
4744 })?;
4745 self.blockchain.storage.write().best_hash = best_hash;
4746 best_number
4747 } else {
4748 self.blockchain.storage.write().best_number = number;
4751 self.blockchain.storage.write().best_hash = hash;
4752 number
4753 }
4754 } else {
4755 self.blockchain.storage.write().best_number = best_number;
4756
4757 let best_hash = self
4759 .blockchain
4760 .storage
4761 .read()
4762 .hash(best_number.into(), self.slots_in_an_epoch)
4763 .ok_or_else(|| {
4764 BlockchainError::RpcError(RpcError::internal_error_with(format!(
4765 "Best hash not found for best number {best_number}",
4766 )))
4767 })?;
4768
4769 self.blockchain.storage.write().best_hash = best_hash;
4770 best_number
4771 };
4772
4773 if !is_arbitrum(self.chain_id().to()) {
4776 self.set_block_number(selected_best_number);
4777 }
4778 }
4779
4780 if let Some(latest) = state.blocks.iter().max_by_key(|b| b.header.number()) {
4781 let header = &latest.header;
4782 let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
4783 header.gas_used(),
4784 header.gas_limit(),
4785 header.base_fee_per_gas().unwrap_or_default(),
4786 );
4787 let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas(
4788 header.excess_blob_gas().unwrap_or_default(),
4789 header.blob_gas_used().unwrap_or_default(),
4790 );
4791
4792 self.fees.set_base_fee(next_block_base_fee);
4794
4795 self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
4796 next_block_excess_blob_gas,
4797 get_blob_base_fee_update_fraction(
4798 self.evm_env.read().cfg_env.chain_id,
4799 header.timestamp,
4800 ),
4801 ));
4802 }
4803
4804 if !self.db.write().await.load_state(state.clone())? {
4805 return Err(RpcError::invalid_params(
4806 "Loading state not supported with the current configuration",
4807 )
4808 .into());
4809 }
4810
4811 let block_hashes = {
4816 let storage = self.blockchain.storage.read();
4817 let min_block = storage.best_number.saturating_sub(256);
4818 storage
4819 .hashes
4820 .iter()
4821 .filter(|(num, _)| (min_block..=storage.best_number).contains(*num))
4822 .map(|(&num, &hash)| (U256::from(num), hash))
4823 .collect()
4824 };
4825 self.db.write().await.set_block_hashes(block_hashes);
4826
4827 if let Some(historical_states) = state.historical_states {
4828 self.states.write().load_states(historical_states);
4829 }
4830
4831 Ok(true)
4832 }
4833
4834 pub async fn load_state_bytes(&self, buf: Bytes) -> Result<bool, BlockchainError> {
4836 let orig_buf = &buf.0[..];
4837 let mut decoder = GzDecoder::new(orig_buf);
4838 let mut decoded_data = Vec::new();
4839
4840 let state: SerializableState = serde_json::from_slice(if decoder.header().is_some() {
4841 decoder
4842 .read_to_end(decoded_data.as_mut())
4843 .map_err(|_| BlockchainError::FailedToDecodeStateDump)?;
4844 &decoded_data
4845 } else {
4846 &buf.0
4847 })
4848 .map_err(|_| BlockchainError::FailedToDecodeStateDump)?;
4849
4850 self.load_state(state).await
4851 }
4852}
4853
4854impl Backend<FoundryNetwork> {
4855 pub async fn call_bundle(
4857 &self,
4858 bundle: EthCallBundle,
4859 transactions: Vec<PendingTransaction<FoundryTxEnvelope>>,
4860 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4861 ) -> Result<EthCallBundleResponse, BlockchainError> {
4862 let EthCallBundle {
4863 block_number,
4864 coinbase,
4865 timestamp,
4866 gas_limit,
4867 difficulty,
4868 base_fee,
4869 ..
4870 } = bundle;
4871
4872 let blob_gas_used = transactions
4873 .iter()
4874 .filter_map(|transaction| transaction.transaction.blob_gas_used())
4875 .sum::<u64>();
4876 let max_blob_gas = self.blob_params().max_blob_gas_per_block();
4877 if blob_gas_used > max_blob_gas {
4878 return Err(BlockchainError::RpcError(RpcError::invalid_params(format!(
4879 "blob gas usage exceeds the limit of {max_blob_gas} gas per block."
4880 ))));
4881 }
4882
4883 self.with_database_at(block_request, |state, mut block_env| {
4884 let state_block_number = block_env.number.to::<u64>();
4885 block_env.number = U256::from(block_number);
4886 block_env.timestamp = timestamp
4887 .map(U256::from)
4888 .unwrap_or_else(|| block_env.timestamp.saturating_add(U256::from(12)));
4889 if let Some(coinbase) = coinbase {
4890 block_env.beneficiary = coinbase;
4891 }
4892 if let Some(gas_limit) = gas_limit {
4893 block_env.gas_limit = gas_limit;
4894 }
4895 if let Some(difficulty) = difficulty {
4896 block_env.difficulty = difficulty;
4897 }
4898 if let Some(base_fee) = base_fee {
4899 block_env.basefee = base_fee.try_into().unwrap_or(u64::MAX);
4900 }
4901
4902 let mut evm_env = self.evm_env.read().clone();
4903 evm_env.block_env = block_env;
4904 let coinbase = evm_env.block_env.beneficiary;
4905 let base_fee = evm_env.block_env.basefee;
4906 let mut cache_db = CacheDB::new(state);
4907 let initial_coinbase = revm::DatabaseRef::basic_ref(&cache_db, coinbase)?
4908 .map(|account| account.balance)
4909 .unwrap_or_default();
4910 let mut coinbase_balance_before_tx = initial_coinbase;
4911 let mut coinbase_balance_after_tx = initial_coinbase;
4912 let mut total_gas_used = 0u64;
4913 let mut total_gas_fees = U256::ZERO;
4914 let mut bundle_hash = alloy_primitives::Keccak256::new();
4915 let mut results = Vec::with_capacity(transactions.len());
4916
4917 for transaction in transactions {
4918 let sender = *transaction.sender();
4919 let tx = transaction.transaction.into_inner();
4920 let tx_hash = tx.hash();
4921 bundle_hash.update(tx_hash);
4922
4923 let mut inspector = self.build_inspector();
4924 let (ResultAndState { result, state }, _) = self
4925 .transact_envelope_with_inspector_ref(
4926 &cache_db,
4927 &evm_env,
4928 &mut inspector,
4929 &tx,
4930 sender,
4931 )?;
4932
4933 let gas_price = tx.effective_tip_per_gas(base_fee).unwrap_or_default();
4934 let gas_used = result.tx_gas_used();
4935 let gas_fees = U256::from(gas_used) * U256::from(gas_price);
4936 total_gas_used += gas_used;
4937 total_gas_fees += gas_fees;
4938
4939 coinbase_balance_after_tx = state
4940 .get(&coinbase)
4941 .map(|account| account.info.balance)
4942 .unwrap_or(coinbase_balance_before_tx);
4943 let coinbase_diff =
4944 coinbase_balance_after_tx.saturating_sub(coinbase_balance_before_tx);
4945 let eth_sent_to_coinbase = coinbase_diff.saturating_sub(gas_fees);
4946 coinbase_balance_before_tx = coinbase_balance_after_tx;
4947
4948 let output = result.output().cloned().unwrap_or_default();
4949 let (value, revert) =
4950 if result.is_success() { (Some(output), None) } else { (None, Some(output)) };
4951
4952 results.push(EthCallBundleTransactionResult {
4953 coinbase_diff,
4954 eth_sent_to_coinbase,
4955 from_address: sender,
4956 gas_fees,
4957 gas_price: U256::from(gas_price),
4958 gas_used,
4959 to_address: tx.to(),
4960 tx_hash,
4961 value,
4962 revert,
4963 });
4964 cache_db.commit(state);
4965 }
4966
4967 let coinbase_diff = coinbase_balance_after_tx.saturating_sub(initial_coinbase);
4968 let eth_sent_to_coinbase = coinbase_diff.saturating_sub(total_gas_fees);
4969 let bundle_gas_price =
4970 coinbase_diff.checked_div(U256::from(total_gas_used)).unwrap_or_default();
4971
4972 Ok(EthCallBundleResponse {
4973 bundle_hash: bundle_hash.finalize(),
4974 bundle_gas_price,
4975 coinbase_diff,
4976 eth_sent_to_coinbase,
4977 gas_fees: total_gas_fees,
4978 results,
4979 state_block_number,
4980 total_gas_used,
4981 })
4982 })
4983 .await?
4984 }
4985
4986 pub async fn call_many(
4988 &self,
4989 bundles: Vec<Bundle<WithOtherFields<TransactionRequest>>>,
4990 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4991 state_override: Option<alloy_rpc_types::state::StateOverride>,
4992 ) -> Result<Vec<Vec<EthCallResponse>>, BlockchainError> {
4993 if bundles.is_empty() {
4994 return Err(BlockchainError::RpcError(RpcError::invalid_params(
4995 "bundles are empty.".to_string(),
4996 )));
4997 }
4998
4999 self.with_database_at(block_request, |state, block_env| {
5000 let mut cache_db = CacheDB::new(state);
5001 if let Some(state_override) = state_override {
5002 apply_state_overrides(state_override, &mut cache_db)?;
5003 }
5004
5005 let mut results = Vec::with_capacity(bundles.len());
5006 for bundle in bundles {
5007 let Bundle { transactions, block_override } = bundle;
5008 let mut bundle_block_env = block_env.clone();
5009 if let Some(block_override) = block_override {
5010 cache_db.apply_block_overrides(block_override, &mut bundle_block_env);
5011 }
5012
5013 let mut bundle_results = Vec::with_capacity(transactions.len());
5014 for request in transactions {
5015 let fee_details = FeeDetails::new(
5016 request.gas_price,
5017 request.max_fee_per_gas,
5018 request.max_priority_fee_per_gas,
5019 request.max_fee_per_blob_gas,
5020 )?
5021 .or_zero_fees();
5022 let (evm_env, tx_env, op_deposit) =
5023 self.build_call_env(request, fee_details, bundle_block_env.clone());
5024
5025 let mut inspector = self.build_inspector();
5026 let ResultAndState { result, state } = self.transact_with_inspector_ref(
5027 &cache_db,
5028 &evm_env,
5029 &mut inspector,
5030 tx_env,
5031 op_deposit,
5032 )?;
5033
5034 let output = result.output().cloned().unwrap_or_default();
5035 let response = if result.is_success() {
5036 EthCallResponse { value: Some(output), error: None }
5037 } else {
5038 let error = RevertDecoder::new()
5039 .maybe_decode(&output, None)
5040 .unwrap_or_else(|| "execution failed".to_string());
5041 EthCallResponse { value: None, error: Some(error) }
5042 };
5043
5044 cache_db.commit(state);
5045 bundle_results.push(response);
5046 }
5047
5048 results.push(bundle_results);
5049 }
5050
5051 Ok(results)
5052 })
5053 .await?
5054 }
5055
5056 pub async fn simulate(
5058 &self,
5059 request: SimulatePayload,
5060 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
5061 ) -> Result<Vec<SimulatedBlock<AnyRpcBlock>>, BlockchainError> {
5062 self.with_database_at(block_request, |state, mut block_env| {
5063 let SimulatePayload {
5064 block_state_calls,
5065 trace_transfers,
5066 validation,
5067 return_full_transactions,
5068 } = request;
5069 let mut cache_db = CacheDB::new(state);
5070 let mut block_res = Vec::with_capacity(block_state_calls.len());
5071 let (is_amsterdam, tx_gas_limit_cap) = {
5072 let cfg_env = &self.evm_env.read().cfg_env;
5073 (cfg_env.spec >= SpecId::AMSTERDAM, cfg_env.tx_gas_limit_cap())
5074 };
5075 let mut rpc_gas_budget = SIMULATE_GAS_CAP;
5076
5077 for block in block_state_calls {
5079 let SimBlock { block_overrides, state_overrides, calls } = block;
5080 let mut call_res = Vec::with_capacity(calls.len());
5081 let mut log_index = 0;
5082 let mut cumulative_gas_used = 0;
5083 let mut block_regular_gas_used = 0;
5084 let mut block_state_gas_used = 0;
5085 let mut transactions = Vec::with_capacity(calls.len());
5086 let mut logs= Vec::new();
5087
5088 if let Some(state_overrides) = state_overrides {
5090 apply_state_overrides(state_overrides, &mut cache_db)?;
5091 }
5092 if !validation {
5093 block_env.basefee = 0;
5094 }
5095 if let Some(block_overrides) = block_overrides {
5096 cache_db.apply_block_overrides(block_overrides, &mut block_env);
5097 }
5098
5099 for (req_idx, mut request) in calls.into_iter().enumerate() {
5101 let remaining_regular_gas =
5102 block_env.gas_limit.saturating_sub(block_regular_gas_used);
5103 let remaining_state_gas =
5104 block_env.gas_limit.saturating_sub(block_state_gas_used);
5105 let remaining_gas = if is_amsterdam {
5106 remaining_regular_gas.min(remaining_state_gas)
5107 } else {
5108 block_env.gas_limit.saturating_sub(cumulative_gas_used)
5109 };
5110 let requested_gas = request.gas.unwrap_or(remaining_gas);
5111 let exceeds_gas_limit = if is_amsterdam {
5112 let requested_regular_gas = requested_gas.min(tx_gas_limit_cap);
5113 requested_regular_gas > remaining_regular_gas
5114 || requested_gas > remaining_state_gas
5115 } else {
5116 requested_gas > remaining_gas
5117 };
5118 if exceeds_gas_limit {
5119 return Err(BlockchainError::RpcError(RpcError {
5120 code: ErrorCode::ServerError(-38015),
5121 message: format!(
5122 "block gas limit exceeded: remaining {remaining_gas}, requested {requested_gas}"
5123 )
5124 .into(),
5125 data: None,
5126 }));
5127 }
5128 request.gas = Some(requested_gas.min(rpc_gas_budget));
5129
5130 let caller = request.from.unwrap_or_default();
5131 let caller_nonce = RevmDatabase::basic(&mut cache_db, caller)?
5132 .map(|account| account.nonce)
5133 .unwrap_or_default();
5134 if request.nonce.is_none() {
5135 request.nonce = Some(caller_nonce);
5136 }
5137
5138 let fee_details = FeeDetails::new(
5139 request.gas_price,
5140 request.max_fee_per_gas,
5141 request.max_priority_fee_per_gas,
5142 request.max_fee_per_blob_gas,
5143 )?
5144 .or_zero_fees();
5145
5146 let mut execution_request = request.clone();
5147 if !validation {
5148 execution_request.nonce = None;
5149 }
5150 let (mut evm_env, tx_env, op_deposit) = self.build_call_env(
5151 WithOtherFields::new(execution_request),
5152 fee_details,
5153 block_env.clone(),
5154 );
5155
5156 if is_amsterdam {
5157 let spec = evm_env.cfg_env.spec;
5159 evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec);
5160 }
5161
5162 evm_env.cfg_env.disable_eip3607 = true;
5164
5165 if validation {
5166 evm_env.cfg_env.disable_nonce_check = false;
5167 evm_env.cfg_env.disable_base_fee = false;
5168 evm_env.cfg_env.disable_block_gas_limit = false;
5169 }
5170
5171 let mut inspector = self.build_inspector();
5172
5173 if trace_transfers {
5175 inspector = inspector.with_transfers();
5176 }
5177 trace!(target: "backend", env=?evm_env, spec=?evm_env.spec_id(),"simulate evm env");
5178 let execution_result = self.transact_with_inspector_ref(
5179 &cache_db,
5180 &evm_env,
5181 &mut inspector,
5182 tx_env,
5183 op_deposit,
5184 );
5185 let ResultAndState { result, mut state } = match execution_result {
5186 Err(BlockchainError::InvalidTransaction(error)) => {
5187 return Err(simulate_transaction_error(error));
5188 }
5189 result => result?,
5190 };
5191 if !validation && caller_nonce == u64::MAX &&
5192 matches!(request.to.as_ref(), Some(TxKind::Call(_))) &&
5193 let Some(account) = state.get_mut(&caller)
5194 {
5195 account.info.nonce = 0;
5196 }
5197 trace!(target: "backend", ?result, ?request, "simulate call");
5198
5199 inspector.print_logs();
5200 if self.print_traces {
5201 inspector.into_print_traces(self.call_trace_decoder.clone());
5202 }
5203
5204 cache_db.commit(state);
5206 rpc_gas_budget = rpc_gas_budget.saturating_sub(result.tx_gas_used());
5207 cumulative_gas_used =
5208 cumulative_gas_used.saturating_add(result.tx_gas_used());
5209 block_regular_gas_used = block_regular_gas_used
5210 .saturating_add(result.gas().block_regular_gas_used());
5211 block_state_gas_used = block_state_gas_used
5212 .saturating_add(result.gas().block_state_gas_used());
5213
5214 let from = caller;
5216
5217 let mut request =
5218 Into::<FoundryTransactionRequest>::into(WithOtherFields::new(request));
5219 if request.as_ref().to.is_none() {
5220 request.as_mut().to = Some(TxKind::Create);
5221 }
5222 request.prep_for_submission();
5223
5224 let typed_tx = request.build_unsigned().map_err(|e| {
5225 BlockchainError::InvalidTransactionRequest(e.to_string())
5226 })?;
5227
5228 let tx = build_impersonated(typed_tx);
5229 let tx_hash = tx.hash();
5230 let rpc_tx = transaction_build(
5231 None,
5232 MaybeImpersonatedTransaction::impersonated(tx, from),
5233 None,
5234 None,
5235 Some(block_env.basefee),
5236 );
5237 transactions.push(rpc_tx);
5238
5239 let return_data = if result.is_success() {
5240 result.output().cloned().unwrap_or_default()
5241 } else {
5242 Bytes::new()
5243 };
5244 let sim_res = SimCallResult {
5245 return_data,
5246 gas_used: result.tx_gas_used(),
5247 max_used_gas: None,
5248 status: result.is_success(),
5249 error: match &result {
5250 ExecutionResult::Success { .. } => None,
5251 ExecutionResult::Revert { output, .. } => {
5252 let message = RevertDecoder::new()
5253 .maybe_decode(output, None)
5254 .map(|reason| format!("execution reverted: {reason}"))
5255 .unwrap_or_else(|| "execution reverted".to_string());
5256 Some(SimulateError {
5257 code: SimulateError::EXECUTION_REVERTED_CODE,
5258 message,
5259 data: Some(output.clone()),
5260 })
5261 }
5262 ExecutionResult::Halt {
5263 reason: HaltReason::OutOfGas(_), ..
5264 } => Some(SimulateError {
5265 code: SimulateError::VM_EXECUTION_ERROR_CODE,
5266 message: "out of gas".to_string(),
5267 data: None,
5268 }),
5269 _ => Some(SimulateError {
5270 code: -3200,
5271 message: "execution failed".to_string(),
5272 data: None,
5273 }),
5274 },
5275 logs: result.clone()
5276 .into_logs()
5277 .into_iter()
5278 .enumerate()
5279 .map(|(idx, log)| Log {
5280 inner: log,
5281 block_number: Some(block_env.number.saturating_to()),
5282 block_timestamp: Some(block_env.timestamp.saturating_to()),
5283 transaction_index: Some(req_idx as u64),
5284 log_index: Some((idx + log_index) as u64),
5285 removed: false,
5286
5287 block_hash: None,
5288 transaction_hash: Some(tx_hash),
5289 })
5290 .collect(),
5291 };
5292 logs.extend(sim_res.logs.iter().map(|log| log.inner.clone()));
5293 log_index += sim_res.logs.len();
5294 call_res.push(sim_res);
5295 }
5296
5297 let gas_used = if is_amsterdam {
5298 block_regular_gas_used.max(block_state_gas_used)
5299 } else {
5300 cumulative_gas_used
5301 };
5302
5303 let transactions_envelopes: Vec<AnyTxEnvelope> = transactions
5304 .iter()
5305 .map(|tx| AnyTxEnvelope::from(tx.clone()))
5306 .collect();
5307 let header = Header {
5308 logs_bloom: logs_bloom(logs.iter()),
5309 transactions_root: calculate_transaction_root(&transactions_envelopes),
5310 receipts_root: calculate_receipt_root(&transactions_envelopes),
5311 parent_hash: Default::default(),
5312 beneficiary: block_env.beneficiary,
5313 state_root: Default::default(),
5314 difficulty: Default::default(),
5315 number: block_env.number.saturating_to(),
5316 gas_limit: block_env.gas_limit,
5317 gas_used,
5318 timestamp: block_env.timestamp.saturating_to(),
5319 extra_data: Default::default(),
5320 mix_hash: Default::default(),
5321 nonce: Default::default(),
5322 base_fee_per_gas: Some(block_env.basefee),
5323 withdrawals_root: None,
5324 blob_gas_used: None,
5325 excess_blob_gas: None,
5326 parent_beacon_block_root: None,
5327 requests_hash: None,
5328 ..Default::default()
5329 };
5330 let mut block = alloy_rpc_types::Block {
5331 header: AnyRpcHeader {
5332 hash: header.hash_slow(),
5333 inner: header.into(),
5334 total_difficulty: None,
5335 size: None,
5336 },
5337 uncles: vec![],
5338 transactions: BlockTransactions::Full(transactions),
5339 withdrawals: None,
5340 };
5341
5342 if !return_full_transactions {
5343 block.transactions.convert_to_hashes();
5344 }
5345
5346 for res in &mut call_res {
5347 res.logs.iter_mut().for_each(|log| {
5348 log.block_hash = Some(block.header.hash);
5349 });
5350 }
5351
5352 let simulated_block = SimulatedBlock {
5353 inner: AnyRpcBlock::new(WithOtherFields::new(block)),
5354 calls: call_res,
5355 };
5356
5357 block_env.number += U256::from(1);
5359 block_env.timestamp += U256::from(12);
5360 let header = &simulated_block.inner.header;
5362 block_env.basefee = self.fees.get_next_block_base_fee_per_gas(
5363 header.gas_used(),
5364 header.gas_limit(),
5365 header.base_fee_per_gas().unwrap_or_default(),
5366 );
5367
5368 block_res.push(simulated_block);
5369 }
5370
5371 Ok(block_res)
5372 })
5373 .await?
5374 }
5375
5376 pub fn get_blob_by_tx_hash(&self, hash: B256) -> Result<Option<Vec<alloy_consensus::Blob>>> {
5377 let storage = self.blockchain.storage.read();
5378 Ok(storage.transactions.get(&hash).and_then(|mined| {
5379 storage
5380 .blocks
5381 .get(&mined.block_hash)?
5382 .body
5383 .transactions
5384 .get(mined.info.transaction_index as usize)?
5385 .as_ref()
5386 .sidecar()
5387 .map(|sidecar| sidecar.sidecar.blobs().to_vec())
5388 }))
5389 }
5390
5391 pub async fn set_fee_token(&self, user: Address, token: Address) -> DatabaseResult<()> {
5393 self.with_tempo_storage(|| {
5394 let mut fee_manager = TipFeeManager::new();
5395 fee_manager
5396 .set_user_token(user, IFeeManager::setUserTokenCall { token })
5397 .map_err(tempo_db_err)
5398 })
5399 .await
5400 }
5401
5402 pub async fn set_validator_fee_token(
5404 &self,
5405 validator: Address,
5406 token: Address,
5407 ) -> DatabaseResult<()> {
5408 self.with_tempo_storage(|| {
5409 let mut fee_manager = TipFeeManager::new();
5410 fee_manager
5412 .set_validator_token(
5413 validator,
5414 IFeeManager::setValidatorTokenCall { token },
5415 Address::ZERO,
5416 )
5417 .map_err(tempo_db_err)
5418 })
5419 .await
5420 }
5421
5422 pub async fn set_fee_amm_liquidity(
5424 &self,
5425 user_token: Address,
5426 validator_token: Address,
5427 amount: U256,
5428 ) -> DatabaseResult<()> {
5429 let admin = Address::repeat_byte(0x11);
5431 self.with_tempo_storage(|| {
5432 for &token_address in &[user_token, validator_token] {
5435 let mut token = TIP20Token::from_address(token_address).map_err(tempo_db_err)?;
5436 token.grant_role_internal(admin, *ISSUER_ROLE).map_err(tempo_db_err)?;
5437 token.mint(admin, ITIP20::mintCall { to: admin, amount }).map_err(tempo_db_err)?;
5438 }
5439 let mut fee_manager = TipFeeManager::new();
5440 fee_manager
5441 .mint(admin, user_token, validator_token, amount, admin)
5442 .map_err(tempo_db_err)?;
5443 Ok(())
5444 })
5445 .await
5446 }
5447
5448 pub async fn set_tip20_balance(
5450 &self,
5451 address: Address,
5452 token_address: Address,
5453 balance: U256,
5454 ) -> DatabaseResult<()> {
5455 if self.try_set_tip20_balance(address, token_address, balance).await? {
5456 return Ok(());
5457 }
5458
5459 Err(tempo_db_err(format!("address {token_address} is not a deployed TIP-20 token")))
5460 }
5461
5462 pub async fn try_set_tip20_balance(
5464 &self,
5465 address: Address,
5466 token_address: Address,
5467 balance: U256,
5468 ) -> DatabaseResult<bool> {
5469 self.with_tempo_storage(|| {
5470 if !TIP20Factory::new().is_tip20(token_address).map_err(tempo_db_err)? {
5471 return Ok(false);
5472 }
5473
5474 let mut token = TIP20Token::from_address(token_address).map_err(tempo_db_err)?;
5475 token.balances[address].write(balance).map_err(tempo_db_err)?;
5476 Ok(true)
5477 })
5478 .await
5479 }
5480
5481 async fn with_tempo_storage<R>(&self, f: impl FnOnce() -> R) -> R {
5484 let hardfork = self.hardfork();
5485 let (chain_id, timestamp, block_number) = {
5487 let env = self.evm_env.read();
5488 (
5489 env.cfg_env.chain_id,
5490 U256::from(env.block_env.timestamp),
5491 env.block_env.number.to::<u64>(),
5492 )
5493 };
5494 let mut db = self.db.write().await;
5495 let mut storage = AnvilStorageProvider::new(
5496 &mut **db,
5497 chain_id,
5498 timestamp,
5499 block_number,
5500 hardfork.into(),
5501 );
5502 StorageCtx::enter(&mut storage, f)
5503 }
5504}
5505
5506fn tempo_db_err<E: std::fmt::Display>(e: E) -> DatabaseError {
5508 DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}")))
5509}
5510
5511fn get_pool_transactions_nonce(
5513 pool_transactions: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
5514 address: Address,
5515) -> Option<u64> {
5516 if let Some(highest_nonce) = pool_transactions
5517 .iter()
5518 .filter(|tx| {
5519 *tx.pending_transaction.sender() == address
5520 && !tx.pending_transaction.transaction.as_ref().has_nonzero_tempo_nonce_key()
5521 })
5522 .map(|tx| tx.pending_transaction.nonce())
5523 .max()
5524 {
5525 let tx_count = highest_nonce.saturating_add(1);
5526 return Some(tx_count);
5527 }
5528 None
5529}
5530
5531#[async_trait::async_trait]
5532impl<N: Network> TransactionValidator<FoundryTxEnvelope> for Backend<N>
5533where
5534 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
5535{
5536 async fn validate_pool_transaction(
5537 &self,
5538 tx: &PendingTransaction<FoundryTxEnvelope>,
5539 ) -> Result<(), BlockchainError> {
5540 let address = *tx.sender();
5541 let account = self.get_account(address).await?;
5542 let evm_env = self.next_evm_env();
5543
5544 if let FoundryTxEnvelope::Tempo(aa_tx) = tx.transaction.as_ref() {
5546 let tempo_tx = aa_tx.tx();
5547 let current_time = evm_env.block_env.timestamp.saturating_to::<u64>();
5548
5549 const AA_VALID_BEFORE_MIN_SECS: u64 = 3;
5551 if let Some(valid_before) = tempo_tx.valid_before.map(|v| v.get()) {
5552 let min_allowed = current_time.saturating_add(AA_VALID_BEFORE_MIN_SECS);
5553 if valid_before <= min_allowed {
5554 return Err(InvalidTransactionError::TempoValidBeforeExpired {
5555 valid_before,
5556 min_allowed,
5557 }
5558 .into());
5559 }
5560 }
5561
5562 const AA_VALID_AFTER_MAX_SECS: u64 = 3600;
5564 if let Some(valid_after) = tempo_tx.valid_after.map(|v| v.get()) {
5565 let max_allowed = current_time.saturating_add(AA_VALID_AFTER_MAX_SECS);
5566 if valid_after > max_allowed {
5567 return Err(InvalidTransactionError::TempoValidAfterTooFar {
5568 valid_after,
5569 max_allowed,
5570 }
5571 .into());
5572 }
5573 }
5574
5575 let fee_payer = tempo_tx.recover_fee_payer(address).unwrap_or(address);
5577 let fee_token =
5578 tempo_tx.fee_token.unwrap_or(foundry_evm::core::tempo::PATH_USD_ADDRESS);
5579
5580 let required_wei =
5582 U256::from(tempo_tx.gas_limit).saturating_mul(U256::from(tempo_tx.max_fee_per_gas));
5583 let required = required_wei / U256::from(10u64.pow(12));
5584
5585 let balance = self.get_fee_token_balance(fee_token, fee_payer).await?;
5586 if balance < required {
5587 return Err(InvalidTransactionError::TempoInsufficientFeeTokenBalance {
5588 balance,
5589 required,
5590 }
5591 .into());
5592 }
5593 }
5594
5595 Ok(self.validate_pool_transaction_for(tx, &account, &evm_env)?)
5596 }
5597
5598 fn validate_pool_transaction_for(
5599 &self,
5600 pending: &PendingTransaction<FoundryTxEnvelope>,
5601 account: &AccountInfo,
5602 evm_env: &EvmEnv,
5603 ) -> Result<(), InvalidTransactionError> {
5604 let tx = &pending.transaction;
5605
5606 if let Some(tx_chain_id) = tx.chain_id() {
5607 let chain_id = self.chain_id();
5608 if chain_id.to::<u64>() != tx_chain_id {
5609 if let FoundryTxEnvelope::Legacy(tx) = tx.as_ref() {
5610 if evm_env.cfg_env.spec >= SpecId::SPURIOUS_DRAGON && tx.chain_id().is_none() {
5612 debug!(target: "backend", ?chain_id, ?tx_chain_id, "incompatible EIP155-based V");
5613 return Err(InvalidTransactionError::IncompatibleEIP155);
5614 }
5615 } else {
5616 debug!(target: "backend", ?chain_id, ?tx_chain_id, "invalid chain id");
5617 return Err(InvalidTransactionError::InvalidChainId);
5618 }
5619 }
5620 }
5621
5622 if self.is_tempo() && !tx.value().is_zero() {
5624 warn!(target: "backend", "[{:?}] native value transfer not allowed in Tempo mode", tx.hash());
5625 return Err(InvalidTransactionError::TempoNativeValueTransfer);
5626 }
5627
5628 if self.is_tempo_hardfork_active(TempoHardfork::T5)
5630 && let FoundryTxEnvelope::Tempo(aa_tx) = tx.as_ref()
5631 {
5632 const MAX_TEMPO_AUTHORIZATIONS: usize = 16;
5633 let auth_count = aa_tx.tx().tempo_authorization_list.len();
5634 if auth_count > MAX_TEMPO_AUTHORIZATIONS {
5635 warn!(target: "backend", "[{:?}] Tempo tx has too many authorizations: {}", tx.hash(), auth_count);
5636 return Err(InvalidTransactionError::TempoTooManyAuthorizations {
5637 count: auth_count,
5638 max: MAX_TEMPO_AUTHORIZATIONS,
5639 });
5640 }
5641 }
5642
5643 #[cfg(feature = "optimism")]
5645 let is_deposit_tx = pending.transaction.as_ref().is_deposit();
5646 #[cfg(not(feature = "optimism"))]
5647 let is_deposit_tx = false;
5648 let is_tempo_tx = pending.transaction.as_ref().is_tempo();
5649 let nonce = tx.nonce();
5650 if nonce < account.nonce && !is_deposit_tx && !is_tempo_tx {
5651 debug!(target: "backend", "[{:?}] nonce too low", tx.hash());
5652 return Err(InvalidTransactionError::NonceTooLow);
5653 }
5654
5655 if evm_env.cfg_env.spec >= SpecId::CANCUN && tx.is_eip4844() {
5657 let blob_tx = match tx.as_ref() {
5659 FoundryTxEnvelope::Eip4844(tx) => tx.tx(),
5660 _ => unreachable!(),
5661 };
5662
5663 let blob_count = blob_tx.tx().blob_versioned_hashes.len();
5664
5665 if blob_count == 0 {
5667 return Err(InvalidTransactionError::NoBlobHashes);
5668 }
5669
5670 let max_blobs_per_tx = self.blob_params().max_blobs_per_tx as usize;
5672 if blob_count > max_blobs_per_tx {
5673 return Err(InvalidTransactionError::TooManyBlobs(blob_count, max_blobs_per_tx));
5674 }
5675
5676 if !self.skip_blob_validation(Some(*pending.sender()))
5678 && let Err(err) = blob_tx.validate(EnvKzgSettings::default().get())
5679 {
5680 return Err(InvalidTransactionError::BlobTransactionValidationError(err));
5681 }
5682 }
5683
5684 if evm_env.cfg_env.spec >= SpecId::SHANGHAI && tx.kind() == TxKind::Create {
5686 let max_initcode_size = evm_env
5687 .cfg_env
5688 .limit_contract_code_size
5689 .map(|limit| limit.saturating_mul(2))
5690 .unwrap_or(revm::primitives::eip3860::MAX_INITCODE_SIZE);
5691 if tx.input().len() > max_initcode_size {
5692 return Err(InvalidTransactionError::MaxInitCodeSizeExceeded);
5693 }
5694 }
5695
5696 if !self.disable_pool_balance_checks {
5698 if tx.gas_limit() < MIN_TRANSACTION_GAS as u64 {
5700 debug!(target: "backend", "[{:?}] gas too low", tx.hash());
5701 return Err(InvalidTransactionError::GasTooLow);
5702 }
5703
5704 if !evm_env.cfg_env.disable_block_gas_limit
5706 && tx.gas_limit() > evm_env.block_env.gas_limit
5707 {
5708 debug!(target: "backend", "[{:?}] gas too high", tx.hash());
5709 return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
5710 detail: String::from("tx.gas_limit > env.block.gas_limit"),
5711 }));
5712 }
5713
5714 if evm_env.cfg_env.tx_gas_limit_cap.is_none()
5716 && tx.gas_limit() > evm_env.cfg_env().tx_gas_limit_cap()
5717 {
5718 debug!(target: "backend", "[{:?}] gas too high", tx.hash());
5719 return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
5720 detail: String::from("tx.gas_limit > env.cfg.tx_gas_limit_cap"),
5721 }));
5722 }
5723
5724 if evm_env.cfg_env.spec >= SpecId::LONDON {
5726 if tx.max_fee_per_gas() < evm_env.block_env.basefee.into() && !is_deposit_tx {
5727 debug!(target: "backend", "max fee per gas={}, too low, block basefee={}", tx.max_fee_per_gas(), evm_env.block_env.basefee);
5728 return Err(InvalidTransactionError::FeeCapTooLow);
5729 }
5730
5731 if let (Some(max_priority_fee_per_gas), max_fee_per_gas) =
5732 (tx.as_ref().max_priority_fee_per_gas(), tx.as_ref().max_fee_per_gas())
5733 && max_priority_fee_per_gas > max_fee_per_gas
5734 {
5735 debug!(target: "backend", "max priority fee per gas={}, too high, max fee per gas={}", max_priority_fee_per_gas, max_fee_per_gas);
5736 return Err(InvalidTransactionError::TipAboveFeeCap);
5737 }
5738 }
5739
5740 if evm_env.cfg_env.spec >= SpecId::CANCUN
5742 && tx.is_eip4844()
5743 && let Some(max_fee_per_blob_gas) = tx.max_fee_per_blob_gas()
5744 && let Some(blob_gas_and_price) = &evm_env.block_env.blob_excess_gas_and_price
5745 && max_fee_per_blob_gas < blob_gas_and_price.blob_gasprice
5746 {
5747 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);
5748 return Err(InvalidTransactionError::BlobFeeCapTooLow(
5749 max_fee_per_blob_gas,
5750 blob_gas_and_price.blob_gasprice,
5751 ));
5752 }
5753
5754 let max_cost =
5755 (tx.gas_limit() as u128).saturating_mul(tx.max_fee_per_gas()).saturating_add(
5756 tx.blob_gas_used()
5757 .map(|g| g as u128)
5758 .unwrap_or(0)
5759 .mul(tx.max_fee_per_blob_gas().unwrap_or(0)),
5760 );
5761 let value = tx.value();
5762 match tx.as_ref() {
5763 #[cfg(feature = "optimism")]
5764 FoundryTxEnvelope::Deposit(deposit_tx) => {
5765 if value > account.balance + U256::from(deposit_tx.mint) {
5771 debug!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance + U256::from(deposit_tx.mint), value, *pending.sender());
5772 return Err(InvalidTransactionError::InsufficientFunds);
5773 }
5774 }
5775 FoundryTxEnvelope::Tempo(_) => {
5776 }
5779 _ => {
5780 let req_funds =
5782 max_cost.checked_add(value.saturating_to()).ok_or_else(|| {
5783 debug!(target: "backend", "[{:?}] cost too high", tx.hash());
5784 InvalidTransactionError::InsufficientFunds
5785 })?;
5786 if account.balance < U256::from(req_funds) {
5787 debug!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender());
5788 return Err(InvalidTransactionError::InsufficientFunds);
5789 }
5790 }
5791 }
5792 }
5793 Ok(())
5794 }
5795
5796 fn validate_for(
5797 &self,
5798 tx: &PendingTransaction<FoundryTxEnvelope>,
5799 account: &AccountInfo,
5800 evm_env: &EvmEnv,
5801 ) -> Result<(), InvalidTransactionError> {
5802 self.validate_pool_transaction_for(tx, account, evm_env)?;
5803 if tx.nonce() > account.nonce {
5804 return Err(InvalidTransactionError::NonceTooHigh);
5805 }
5806 Ok(())
5807 }
5808}
5809
5810fn rehash<T>(signed: Signed<T>, hash: B256) -> Signed<T>
5812where
5813 T: alloy_consensus::transaction::RlpEcdsaEncodableTx,
5814{
5815 let (t, sig, _) = signed.into_parts();
5816 Signed::new_unchecked(t, sig, hash)
5817}
5818
5819pub fn transaction_build(
5821 tx_hash: Option<B256>,
5822 eth_transaction: MaybeImpersonatedTransaction<FoundryTxEnvelope>,
5823 block: Option<&Block>,
5824 info: Option<TransactionInfo>,
5825 base_fee: Option<u64>,
5826) -> AnyRpcTransaction {
5827 #[cfg(feature = "optimism")]
5828 if let FoundryTxEnvelope::Deposit(deposit_tx) = eth_transaction.as_ref() {
5829 let dep_tx = deposit_tx;
5830
5831 let ser = serde_json::to_value(dep_tx).expect("could not serialize TxDeposit");
5832 let maybe_deposit_fields = OtherFields::try_from(ser);
5833
5834 match maybe_deposit_fields {
5835 Ok(mut fields) => {
5836 fields.insert("v".to_string(), serde_json::to_value("0x0").unwrap());
5839 fields.insert("r".to_string(), serde_json::to_value(B256::ZERO).unwrap());
5840 fields.insert(String::from("s"), serde_json::to_value(B256::ZERO).unwrap());
5841 fields.insert(String::from("nonce"), serde_json::to_value("0x0").unwrap());
5842
5843 let inner = UnknownTypedTransaction {
5844 ty: AnyTxType(DEPOSIT_TX_TYPE_ID),
5845 fields,
5846 memo: Default::default(),
5847 };
5848
5849 let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
5850 hash: eth_transaction.hash(),
5851 inner,
5852 });
5853
5854 let tx = Transaction {
5855 inner: Recovered::new_unchecked(envelope, deposit_tx.from),
5856 block_hash: block
5857 .as_ref()
5858 .map(|block| B256::from(keccak256(alloy_rlp::encode(&block.header)))),
5859 block_number: block.as_ref().map(|block| block.header.number()),
5860 transaction_index: info.as_ref().map(|info| info.transaction_index),
5861 effective_gas_price: None,
5862 block_timestamp: block.as_ref().map(|block| block.header.timestamp()),
5863 };
5864
5865 return AnyRpcTransaction::from(WithOtherFields::new(tx));
5866 }
5867 Err(_) => {
5868 error!(target: "backend", "failed to serialize deposit transaction");
5869 }
5870 }
5871 }
5872
5873 if let FoundryTxEnvelope::Tempo(tempo_tx) = eth_transaction.as_ref() {
5874 let from = eth_transaction.recover().unwrap_or_default();
5875 let ser = serde_json::to_value(tempo_tx).expect("could not serialize Tempo transaction");
5876 let maybe_tempo_fields = OtherFields::try_from(ser);
5877
5878 match maybe_tempo_fields {
5879 Ok(fields) => {
5880 let inner = UnknownTypedTransaction {
5881 ty: AnyTxType(TEMPO_TX_TYPE_ID),
5882 fields,
5883 memo: Default::default(),
5884 };
5885
5886 let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
5887 hash: eth_transaction.hash(),
5888 inner,
5889 });
5890
5891 let tx = Transaction {
5892 inner: Recovered::new_unchecked(envelope, from),
5893 block_hash: block.as_ref().map(|block| block.header.hash_slow()),
5894 block_number: block.as_ref().map(|block| block.header.number()),
5895 transaction_index: info.as_ref().map(|info| info.transaction_index),
5896 effective_gas_price: None,
5897 block_timestamp: block.as_ref().map(|block| block.header.timestamp()),
5898 };
5899
5900 return AnyRpcTransaction::from(WithOtherFields::new(tx));
5901 }
5902 Err(_) => {
5903 error!(target: "backend", "failed to serialize tempo transaction");
5904 }
5905 }
5906 }
5907
5908 let from = eth_transaction.recover().unwrap_or_default();
5909 let effective_gas_price = eth_transaction.effective_gas_price(base_fee);
5910
5911 let hash = tx_hash.unwrap_or_else(|| eth_transaction.hash());
5917
5918 let eth_envelope = FoundryTxEnvelope::from(eth_transaction)
5919 .try_into_eth()
5920 .expect("non-standard transactions are handled above");
5921
5922 let envelope = match eth_envelope {
5923 TxEnvelope::Legacy(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(rehash(s, hash))),
5924 TxEnvelope::Eip1559(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip1559(rehash(s, hash))),
5925 TxEnvelope::Eip2930(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip2930(rehash(s, hash))),
5926 TxEnvelope::Eip4844(s) => {
5927 let s = if block.is_some() { s.map(TxEip4844Variant::drop_sidecar) } else { s };
5928 AnyTxEnvelope::Ethereum(TxEnvelope::Eip4844(rehash(s, hash)))
5929 }
5930 TxEnvelope::Eip7702(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip7702(rehash(s, hash))),
5931 };
5932
5933 let tx = Transaction {
5934 inner: Recovered::new_unchecked(envelope, from),
5935 block_hash: block.as_ref().map(|block| block.header.hash_slow()),
5936 block_number: block.as_ref().map(|block| block.header.number()),
5937 transaction_index: info.as_ref().map(|info| info.transaction_index),
5938 effective_gas_price: Some(effective_gas_price),
5940 block_timestamp: block.as_ref().map(|block| block.header.timestamp()),
5941 };
5942 AnyRpcTransaction::from(WithOtherFields::new(tx))
5943}
5944
5945pub fn prove_storage(
5951 storage: &alloy_primitives::map::U256Map<U256>,
5952 keys: &[B256],
5953) -> (B256, Vec<Vec<Bytes>>) {
5954 let keys: Vec<_> = keys.iter().map(|key| Nibbles::unpack(keccak256(key))).collect();
5955
5956 let mut builder = HashBuilder::default().with_proof_retainer(ProofRetainer::new(keys.clone()));
5957
5958 for (key, value) in trie_storage(storage) {
5959 builder.add_leaf(key, &value);
5960 }
5961
5962 let root = builder.root();
5963
5964 let mut proofs = Vec::new();
5965 let all_proof_nodes = builder.take_proof_nodes();
5966
5967 for proof_key in keys {
5968 let matching_proof_nodes =
5971 all_proof_nodes.matching_nodes_sorted(&proof_key).into_iter().map(|(_, node)| node);
5972 proofs.push(matching_proof_nodes.collect());
5973 }
5974
5975 (root, proofs)
5976}
5977
5978pub fn is_arbitrum(chain_id: u64) -> bool {
5979 if let Ok(chain) = NamedChain::try_from(chain_id) {
5980 return chain.is_arbitrum();
5981 }
5982 false
5983}
5984
5985fn simulate_transaction_error(error: InvalidTransactionError) -> BlockchainError {
5986 let code = match &error {
5987 InvalidTransactionError::NonceTooLow => -38010,
5988 InvalidTransactionError::NonceTooHigh => -38011,
5989 InvalidTransactionError::NonceMaxValue => -32603,
5990 InvalidTransactionError::FeeCapTooLow => -38012,
5991 InvalidTransactionError::GasTooLow | InvalidTransactionError::GasTooHigh(_) => -38013,
5992 InvalidTransactionError::InsufficientFunds
5993 | InvalidTransactionError::InsufficientFundsForTransfer => -38014,
5994 _ => return BlockchainError::InvalidTransaction(error),
5995 };
5996
5997 BlockchainError::RpcError(RpcError {
5998 code: ErrorCode::from(code),
5999 message: format!("err: {error}").into(),
6000 data: None,
6001 })
6002}
6003
6004fn unpack_execution_result<H: IntoInstructionResult>(
6006 result: ExecutionResult<H>,
6007) -> (InstructionResult, u64, Option<Output>, Vec<revm::primitives::Log>) {
6008 match result {
6009 ExecutionResult::Success { reason, gas, output, logs, .. } => {
6010 (reason.into(), gas.tx_gas_used(), Some(output), logs)
6011 }
6012 ExecutionResult::Revert { gas, output, logs, .. } => {
6013 (InstructionResult::Revert, gas.tx_gas_used(), Some(Output::Call(output)), logs)
6014 }
6015 ExecutionResult::Halt { reason, gas, logs, .. } => {
6016 (reason.into_instruction_result(), gas.tx_gas_used(), None, logs)
6017 }
6018 }
6019}
6020
6021pub use foundry_evm::core::evm::IntoInstructionResult;
6026
6027#[cfg(test)]
6028mod tests {
6029 use crate::{NodeConfig, spawn};
6030
6031 #[tokio::test]
6032 async fn test_deterministic_block_mining() {
6033 let genesis_timestamp = 1743944919u64;
6035
6036 let config_a = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
6038 let config_b = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
6039
6040 let (api_a, _handle_a) = spawn(config_a).await;
6041 let (api_b, _handle_b) = spawn(config_b).await;
6042
6043 let outcome_a_1 = api_a.backend.mine_block(vec![]).await;
6045 let outcome_b_1 = api_b.backend.mine_block(vec![]).await;
6046
6047 assert_eq!(outcome_a_1.block_number, outcome_b_1.block_number);
6049
6050 let block_a_1 =
6052 api_a.block_by_number(outcome_a_1.block_number.into()).await.unwrap().unwrap();
6053 let block_b_1 =
6054 api_b.block_by_number(outcome_b_1.block_number.into()).await.unwrap().unwrap();
6055
6056 assert_eq!(
6058 block_a_1.header.hash, block_b_1.header.hash,
6059 "Block hashes should be deterministic. Got {} vs {}",
6060 block_a_1.header.hash, block_b_1.header.hash
6061 );
6062
6063 let outcome_a_2 = api_a.backend.mine_block(vec![]).await;
6065 let outcome_b_2 = api_b.backend.mine_block(vec![]).await;
6066
6067 let block_a_2 =
6068 api_a.block_by_number(outcome_a_2.block_number.into()).await.unwrap().unwrap();
6069 let block_b_2 =
6070 api_b.block_by_number(outcome_b_2.block_number.into()).await.unwrap().unwrap();
6071
6072 assert_eq!(
6073 block_a_2.header.hash, block_b_2.header.hash,
6074 "Second block hashes should also be deterministic. Got {} vs {}",
6075 block_a_2.header.hash, block_b_2.header.hash
6076 );
6077
6078 assert_ne!(
6080 block_a_1.header.hash, block_a_2.header.hash,
6081 "Different blocks should have different hashes"
6082 );
6083 }
6084}