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::{NewBlockNotification, NewBlockNotifications},
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, 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_rpc_types::{
67 AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions,
68 EIP1186AccountProofResponse as AccountProof, EIP1186StorageProof as StorageProof, Filter,
69 Header as AlloyHeader, Index, Log, Transaction, TransactionReceipt,
70 anvil::Forking,
71 request::TransactionRequest,
72 serde_helpers::JsonStorageKey,
73 simulate::{SimBlock, SimCallResult, SimulatePayload, SimulatedBlock},
74 state::EvmOverrides,
75 trace::{
76 filter::TraceFilter,
77 geth::{
78 FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerType,
79 GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, NoopFrame,
80 TraceResult,
81 },
82 parity::{LocalizedTransactionTrace, TraceResultsWithTransactionHash, TraceType},
83 },
84};
85use alloy_serde::{OtherFields, WithOtherFields};
86use alloy_trie::{HashBuilder, Nibbles, proof::ProofRetainer};
87use anvil_core::eth::{
88 block::{Block, BlockInfo, create_block},
89 transaction::{MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo},
90};
91use anvil_rpc::error::RpcError;
92use chrono::Datelike;
93use eyre::{Context, Result};
94use flate2::{Compression, read::GzDecoder, write::GzEncoder};
95use foundry_evm::{
96 backend::{DatabaseError, DatabaseResult, RevertStateSnapshotAction},
97 constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE,
98 core::precompiles::EC_RECOVER,
99 decode::RevertDecoder,
100 hardfork::FoundryHardfork,
101 inspectors::AccessListInspector,
102 traces::{
103 CallTraceDecoder, FourByteInspector, GethTraceBuilder, TracingInspector,
104 TracingInspectorConfig,
105 },
106 utils::{
107 block_env_from_header, get_blob_base_fee_update_fraction,
108 get_blob_base_fee_update_fraction_by_spec_id, get_blob_params_by_spec_id,
109 },
110};
111use foundry_evm_networks::NetworkConfigs;
112#[cfg(feature = "optimism")]
113use foundry_primitives::get_deposit_tx_parts;
114use foundry_primitives::{
115 FoundryNetwork, FoundryReceiptEnvelope, FoundryTransactionRequest, FoundryTxEnvelope,
116 FoundryTxReceipt,
117};
118use futures::channel::mpsc::{UnboundedSender, unbounded};
119#[cfg(feature = "optimism")]
120use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, OpTransaction as OpTransactionTrait};
121#[cfg(feature = "optimism")]
122use op_revm::{OpTransaction, transaction::deposit::DepositTransactionParts};
123
124#[cfg(feature = "optimism")]
131type OpCallDepositInfo = DepositTransactionParts;
132#[cfg(not(feature = "optimism"))]
133#[derive(Default, Clone, Debug)]
134struct OpCallDepositInfo;
135
136#[cfg(feature = "optimism")]
140pub trait BackendInspector<DB: Database>:
141 Inspector<EthEvmContext<DB>> + Inspector<OpEvmContext<DB>> + Inspector<TempoContext<DB>>
142{
143}
144#[cfg(feature = "optimism")]
145impl<DB: Database, T> BackendInspector<DB> for T where
146 T: Inspector<EthEvmContext<DB>> + Inspector<OpEvmContext<DB>> + Inspector<TempoContext<DB>>
147{
148}
149#[cfg(not(feature = "optimism"))]
150pub trait BackendInspector<DB: Database>:
151 Inspector<EthEvmContext<DB>> + Inspector<TempoContext<DB>>
152{
153}
154#[cfg(not(feature = "optimism"))]
155impl<DB: Database, T> BackendInspector<DB> for T where
156 T: Inspector<EthEvmContext<DB>> + Inspector<TempoContext<DB>>
157{
158}
159use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
160use revm::{
161 DatabaseCommit, Inspector,
162 context::{Block as RevmBlock, BlockEnv, Cfg, CfgEnv, TxEnv},
163 context_interface::{
164 block::BlobExcessGasAndPrice,
165 result::{ExecutionResult, HaltReason, Output, ResultAndState},
166 },
167 database::{CacheDB, DbAccount, WrapDatabaseRef},
168 interpreter::InstructionResult,
169 precompile::{PrecompileSpecId, Precompiles},
170 primitives::{KECCAK_EMPTY, hardfork::SpecId},
171 state::AccountInfo,
172};
173use std::{
174 collections::BTreeMap,
175 fmt::{self, Debug},
176 io::{Read, Write},
177 ops::{Mul, Not},
178 path::PathBuf,
179 sync::Arc,
180 time::Duration,
181};
182use storage::{Blockchain, DEFAULT_HISTORY_LIMIT, MinedTransaction};
183use tempo_chainspec::hardfork::TempoHardfork;
184use tempo_evm::evm::TempoEvmFactory;
185use tempo_precompiles::{
186 extend_tempo_precompiles,
187 storage::StorageCtx,
188 tip_fee_manager::{IFeeManager, TipFeeManager},
189 tip20::{ISSUER_ROLE, ITIP20, TIP20Token},
190};
191use tempo_primitives::TEMPO_TX_TYPE_ID;
192use tempo_revm::{
193 TempoBatchCallEnv, TempoBlockEnv, TempoHaltReason, TempoTxEnv, evm::TempoContext,
194 gas_params::tempo_gas_params,
195};
196use tokio::sync::RwLock as AsyncRwLock;
197
198pub mod cache;
199pub mod fork_db;
200pub mod in_memory_db;
201pub mod inspector;
202#[cfg(feature = "optimism")]
203pub mod optimism;
204pub mod state;
205pub mod storage;
206
207pub trait DatabaseRef: revm::DatabaseRef<Error = DatabaseError> + Debug {}
211impl<T> DatabaseRef for T where T: revm::DatabaseRef<Error = DatabaseError> + Debug {}
212impl DatabaseRef for dyn crate::eth::backend::db::Db {}
213
214pub const MIN_TRANSACTION_GAS: u128 = 21000;
216pub const MIN_CREATE_GAS: u128 = 53000;
218
219pub type State = foundry_evm::utils::StateChangeset;
220
221pub enum BlockRequest<T> {
223 Pending(Vec<Arc<PoolTransaction<T>>>),
224 Number(u64),
225}
226
227impl<T> fmt::Debug for BlockRequest<T> {
228 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229 match self {
230 Self::Pending(txs) => f.debug_tuple("Pending").field(&txs.len()).finish(),
231 Self::Number(n) => f.debug_tuple("Number").field(n).finish(),
232 }
233 }
234}
235
236impl<T> BlockRequest<T> {
237 pub const fn block_number(&self) -> BlockNumber {
238 match *self {
239 Self::Pending(_) => BlockNumber::Pending,
240 Self::Number(n) => BlockNumber::Number(n),
241 }
242 }
243}
244
245pub struct Backend<N: Network> {
247 db: Arc<AsyncRwLock<Box<dyn Db>>>,
267 blockchain: Blockchain<N>,
269 states: Arc<RwLock<InMemoryBlockStates>>,
271 evm_env: Arc<RwLock<EvmEnv>>,
273 networks: NetworkConfigs,
275 hardfork: FoundryHardfork,
277 fork: Arc<RwLock<Option<ClientFork>>>,
279 time: TimeManager,
281 cheats: CheatsManager,
283 fees: FeeManager,
285 genesis: GenesisConfig,
287 new_block_listeners: Arc<Mutex<Vec<UnboundedSender<NewBlockNotification>>>>,
289 active_state_snapshots: Arc<Mutex<HashMap<U256, (u64, B256)>>>,
291 enable_steps_tracing: bool,
292 print_logs: bool,
293 print_traces: bool,
294 call_trace_decoder: Arc<CallTraceDecoder>,
296 prune_state_history_config: PruneStateHistoryConfig,
298 transaction_block_keeper: Option<usize>,
300 pub(crate) node_config: Arc<AsyncRwLock<NodeConfig>>,
301 slots_in_an_epoch: u64,
303 precompile_factory: Option<Arc<dyn PrecompileFactory>>,
305 mining: Arc<tokio::sync::Mutex<()>>,
307 disable_pool_balance_checks: bool,
309}
310
311impl<N: Network> Clone for Backend<N> {
312 fn clone(&self) -> Self {
313 Self {
314 db: self.db.clone(),
315 blockchain: self.blockchain.clone(),
316 states: self.states.clone(),
317 evm_env: self.evm_env.clone(),
318 networks: self.networks,
319 hardfork: self.hardfork,
320 fork: self.fork.clone(),
321 time: self.time.clone(),
322 cheats: self.cheats.clone(),
323 fees: self.fees.clone(),
324 genesis: self.genesis.clone(),
325 new_block_listeners: self.new_block_listeners.clone(),
326 active_state_snapshots: self.active_state_snapshots.clone(),
327 enable_steps_tracing: self.enable_steps_tracing,
328 print_logs: self.print_logs,
329 print_traces: self.print_traces,
330 call_trace_decoder: self.call_trace_decoder.clone(),
331 prune_state_history_config: self.prune_state_history_config,
332 transaction_block_keeper: self.transaction_block_keeper,
333 node_config: self.node_config.clone(),
334 slots_in_an_epoch: self.slots_in_an_epoch,
335 precompile_factory: self.precompile_factory.clone(),
336 mining: self.mining.clone(),
337 disable_pool_balance_checks: self.disable_pool_balance_checks,
338 }
339 }
340}
341
342impl<N: Network> fmt::Debug for Backend<N> {
343 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 f.debug_struct("Backend").finish_non_exhaustive()
345 }
346}
347
348impl<N: Network> Backend<N> {
350 pub fn impersonate(&self, addr: Address) -> bool {
354 if self.cheats.impersonated_accounts().contains(&addr) {
355 return true;
356 }
357 self.evm_env.write().cfg_env.disable_eip3607 = true;
359 self.cheats.impersonate(addr)
360 }
361
362 pub fn stop_impersonating(&self, addr: Address) {
366 self.cheats.stop_impersonating(&addr);
367 }
368
369 pub fn auto_impersonate_account(&self, enabled: bool) {
371 self.cheats.set_auto_impersonate_account(enabled);
372 }
373
374 pub fn get_fork(&self) -> Option<ClientFork> {
376 self.fork.read().clone()
377 }
378
379 pub fn get_db(&self) -> &Arc<AsyncRwLock<Box<dyn Db>>> {
381 &self.db
382 }
383
384 pub async fn get_account(&self, address: Address) -> DatabaseResult<AccountInfo> {
386 Ok(self.db.read().await.basic_ref(address)?.unwrap_or_default())
387 }
388
389 pub fn is_fork(&self) -> bool {
391 self.fork.read().is_some()
392 }
393
394 pub async fn set_create2_deployer(&self, address: Address) -> DatabaseResult<()> {
396 self.set_code(address, Bytes::from_static(DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE)).await?;
397 Ok(())
398 }
399
400 pub(crate) fn update_interval_mine_block_time(&self, block_time: Duration) {
402 self.states.write().update_interval_mine_block_time(block_time)
403 }
404
405 pub const fn time(&self) -> &TimeManager {
407 &self.time
408 }
409
410 pub const fn cheats(&self) -> &CheatsManager {
412 &self.cheats
413 }
414
415 pub fn skip_blob_validation(&self, impersonator: Option<Address>) -> bool {
417 self.cheats().auto_impersonate_accounts()
418 || impersonator
419 .is_some_and(|addr| self.cheats().impersonated_accounts().contains(&addr))
420 }
421
422 pub const fn fees(&self) -> &FeeManager {
424 &self.fees
425 }
426
427 pub const fn evm_env(&self) -> &Arc<RwLock<EvmEnv>> {
429 &self.evm_env
430 }
431
432 pub fn best_hash(&self) -> B256 {
434 self.blockchain.storage.read().best_hash
435 }
436
437 pub fn best_number(&self) -> u64 {
439 self.blockchain.storage.read().best_number
440 }
441
442 pub fn set_block_number(&self, number: u64) {
444 self.evm_env.write().block_env.number = U256::from(number);
445 }
446
447 pub fn coinbase(&self) -> Address {
449 self.evm_env.read().block_env.beneficiary
450 }
451
452 pub fn chain_id(&self) -> U256 {
454 U256::from(self.evm_env.read().cfg_env.chain_id)
455 }
456
457 pub fn set_chain_id(&self, chain_id: u64) {
458 self.evm_env.write().cfg_env.chain_id = chain_id;
459 }
460
461 pub const fn genesis_time(&self) -> u64 {
463 self.genesis.timestamp
464 }
465
466 pub const fn genesis_number(&self) -> u64 {
468 self.genesis.number
469 }
470
471 pub async fn current_balance(&self, address: Address) -> DatabaseResult<U256> {
473 Ok(self.get_account(address).await?.balance)
474 }
475
476 pub async fn current_nonce(&self, address: Address) -> DatabaseResult<u64> {
478 Ok(self.get_account(address).await?.nonce)
479 }
480
481 pub fn set_coinbase(&self, address: Address) {
483 self.evm_env.write().block_env.beneficiary = address;
484 }
485
486 pub async fn set_nonce(&self, address: Address, nonce: U256) -> DatabaseResult<()> {
488 self.db.write().await.set_nonce(address, nonce.try_into().unwrap_or(u64::MAX))
489 }
490
491 pub async fn set_balance(&self, address: Address, balance: U256) -> DatabaseResult<()> {
493 self.db.write().await.set_balance(address, balance)
494 }
495
496 pub async fn set_code(&self, address: Address, code: Bytes) -> DatabaseResult<()> {
498 self.db.write().await.set_code(address, code)
499 }
500
501 pub async fn set_storage_at(
503 &self,
504 address: Address,
505 slot: U256,
506 val: B256,
507 ) -> DatabaseResult<()> {
508 self.db.write().await.set_storage_at(address, slot.into(), val)
509 }
510
511 pub fn spec_id(&self) -> SpecId {
513 *self.evm_env.read().spec_id()
514 }
515
516 pub fn is_eip1559(&self) -> bool {
518 (self.spec_id() as u8) >= (SpecId::LONDON as u8)
519 }
520
521 pub fn is_eip3675(&self) -> bool {
523 (self.spec_id() as u8) >= (SpecId::MERGE as u8)
524 }
525
526 pub fn is_eip2930(&self) -> bool {
528 (self.spec_id() as u8) >= (SpecId::BERLIN as u8)
529 }
530
531 pub fn is_eip4844(&self) -> bool {
533 (self.spec_id() as u8) >= (SpecId::CANCUN as u8)
534 }
535
536 pub fn is_eip7702(&self) -> bool {
538 (self.spec_id() as u8) >= (SpecId::PRAGUE as u8)
539 }
540
541 #[cfg(feature = "optimism")]
543 pub const fn is_optimism(&self) -> bool {
544 self.networks.is_optimism()
545 }
546
547 #[cfg(not(feature = "optimism"))]
551 pub const fn is_optimism(&self) -> bool {
552 false
553 }
554
555 pub const fn is_tempo(&self) -> bool {
557 self.networks.is_tempo()
558 }
559
560 pub fn hardfork(&self) -> FoundryHardfork {
562 if let Some(hardfork) =
563 self.fork.read().as_ref().and_then(|fork| fork.config.read().hardfork)
564 {
565 return hardfork;
566 }
567 self.hardfork
568 }
569
570 pub fn tempo_hardfork(&self) -> TempoHardfork {
572 TempoHardfork::from(self.hardfork())
573 }
574
575 pub fn is_tempo_hardfork_active(&self, hardfork: TempoHardfork) -> bool {
577 self.is_tempo() && self.tempo_hardfork() >= hardfork
578 }
579
580 pub fn precompiles(&self) -> BTreeMap<String, Address> {
582 let spec_id = self.spec_id();
583 let precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(spec_id));
584
585 let mut precompiles_map = BTreeMap::<String, Address>::default();
586 for (address, precompile) in precompiles.inner() {
587 precompiles_map.insert(precompile.id().name().to_string(), *address);
588 }
589
590 precompiles_map
592 .extend(self.networks.precompiles(self.is_tempo().then(|| self.tempo_hardfork())));
593
594 if let Some(factory) = &self.precompile_factory {
595 for (address, precompile) in factory.precompiles() {
596 precompiles_map.insert(precompile.precompile_id().to_string(), address);
597 }
598 }
599
600 precompiles_map
601 }
602
603 pub fn system_contracts(&self) -> BTreeMap<SystemContract, Address> {
605 let mut system_contracts = BTreeMap::<SystemContract, Address>::default();
606
607 let spec_id = self.spec_id();
608
609 if spec_id >= SpecId::CANCUN {
610 system_contracts.extend(SystemContract::cancun());
611 }
612
613 if spec_id >= SpecId::PRAGUE {
614 system_contracts.extend(SystemContract::prague(None));
615 }
616
617 system_contracts
618 }
619
620 pub fn blob_params(&self) -> BlobParams {
622 get_blob_params_by_spec_id(self.spec_id())
623 }
624
625 pub fn ensure_eip1559_active(&self) -> Result<(), BlockchainError> {
627 if self.is_eip1559() {
628 return Ok(());
629 }
630 Err(BlockchainError::EIP1559TransactionUnsupportedAtHardfork)
631 }
632
633 pub fn ensure_eip2930_active(&self) -> Result<(), BlockchainError> {
635 if self.is_eip2930() {
636 return Ok(());
637 }
638 Err(BlockchainError::EIP2930TransactionUnsupportedAtHardfork)
639 }
640
641 pub fn ensure_eip4844_active(&self) -> Result<(), BlockchainError> {
642 if self.is_eip4844() {
643 return Ok(());
644 }
645 Err(BlockchainError::EIP4844TransactionUnsupportedAtHardfork)
646 }
647
648 pub fn ensure_eip7702_active(&self) -> Result<(), BlockchainError> {
649 if self.is_eip7702() {
650 return Ok(());
651 }
652 Err(BlockchainError::EIP7702TransactionUnsupportedAtHardfork)
653 }
654
655 #[cfg(feature = "optimism")]
657 pub const fn ensure_op_deposits_active(&self) -> Result<(), BlockchainError> {
658 if self.is_optimism() {
659 return Ok(());
660 }
661 Err(BlockchainError::DepositTransactionUnsupported)
662 }
663
664 pub const fn ensure_tempo_active(&self) -> Result<(), BlockchainError> {
666 if self.is_tempo() {
667 return Ok(());
668 }
669 Err(BlockchainError::TempoTransactionUnsupported)
670 }
671
672 fn inspector_tx_config(&self) -> InspectorTxConfig {
674 InspectorTxConfig {
675 print_traces: self.print_traces,
676 print_logs: self.print_logs,
677 enable_steps_tracing: self.enable_steps_tracing,
678 call_trace_decoder: self.call_trace_decoder.clone(),
679 }
680 }
681
682 fn pool_tx_gas_config(&self, evm_env: &EvmEnv) -> PoolTxGasConfig {
684 let spec_id = *evm_env.spec_id();
685 let is_cancun = spec_id >= SpecId::CANCUN;
686 let blob_params = self.blob_params();
687 PoolTxGasConfig {
688 disable_block_gas_limit: evm_env.cfg_env.disable_block_gas_limit,
689 tx_gas_limit_cap: evm_env.cfg_env.tx_gas_limit_cap,
690 tx_gas_limit_cap_resolved: evm_env.cfg_env.tx_gas_limit_cap(),
691 max_blob_gas_per_block: blob_params.max_blob_gas_per_block(),
692 is_cancun,
693 }
694 }
695
696 pub fn gas_limit(&self) -> u64 {
698 self.evm_env.read().block_env.gas_limit
699 }
700
701 pub fn set_gas_limit(&self, gas_limit: u64) {
703 self.evm_env.write().block_env.gas_limit = gas_limit;
704 }
705
706 pub fn base_fee(&self) -> u64 {
708 self.fees.base_fee()
709 }
710
711 pub const fn is_min_priority_fee_enforced(&self) -> bool {
713 self.fees.is_min_priority_fee_enforced()
714 }
715
716 pub fn excess_blob_gas_and_price(&self) -> Option<BlobExcessGasAndPrice> {
717 self.fees.excess_blob_gas_and_price()
718 }
719
720 pub fn set_base_fee(&self, basefee: u64) {
722 self.fees.set_base_fee(basefee)
723 }
724
725 pub fn set_gas_price(&self, price: u128) {
727 self.fees.set_gas_price(price)
728 }
729
730 pub fn elasticity(&self) -> f64 {
731 self.fees.elasticity()
732 }
733
734 pub fn total_difficulty(&self) -> U256 {
739 self.blockchain.storage.read().total_difficulty
740 }
741
742 pub async fn create_state_snapshot(&self) -> U256 {
746 let num = self.best_number();
747 let hash = self.best_hash();
748 let id = self.db.write().await.snapshot_state();
749 trace!(target: "backend", "creating snapshot {} at {}", id, num);
750 self.active_state_snapshots.lock().insert(id, (num, hash));
751 id
752 }
753
754 pub fn list_state_snapshots(&self) -> BTreeMap<U256, (u64, B256)> {
755 self.active_state_snapshots.lock().clone().into_iter().collect()
756 }
757
758 fn next_evm_env(&self) -> EvmEnv {
760 let mut evm_env = self.evm_env.read().clone();
761 evm_env.block_env.number = evm_env.block_env.number.saturating_add(U256::from(1));
763 evm_env.block_env.basefee = self.base_fee();
764 evm_env.block_env.blob_excess_gas_and_price = self.excess_blob_gas_and_price();
765 evm_env.block_env.timestamp = U256::from(self.time.current_call_timestamp());
766 evm_env
767 }
768
769 fn build_inspector(&self) -> AnvilInspector {
771 let mut inspector = AnvilInspector::default();
772
773 if self.print_logs {
774 inspector = inspector.with_log_collector();
775 }
776 if self.print_traces {
777 inspector = inspector.with_trace_printer();
778 }
779
780 inspector
781 }
782
783 fn build_mining_inspector(&self) -> AnvilInspector {
785 let mut inspector = AnvilInspector::default().with_tracing();
786 if self.enable_steps_tracing {
787 inspector = inspector.with_steps_tracing();
788 }
789 if self.print_logs {
790 inspector = inspector.with_log_collector();
791 }
792 if self.print_traces {
793 inspector = inspector.with_trace_printer();
794 }
795 inspector
796 }
797
798 pub fn new_block_notifications(&self) -> NewBlockNotifications {
800 let (tx, rx) = unbounded();
801 self.new_block_listeners.lock().push(tx);
802 trace!(target: "backed", "added new block listener");
803 rx
804 }
805
806 fn notify_on_new_block(&self, header: Header, hash: B256) {
808 self.new_block_listeners.lock().retain(|tx| !tx.is_closed());
811
812 let notification = NewBlockNotification { hash, header: Arc::new(header) };
813
814 self.new_block_listeners
815 .lock()
816 .retain(|tx| tx.unbounded_send(notification.clone()).is_ok());
817 }
818
819 pub fn convert_block_number(&self, block: Option<BlockNumber>) -> u64 {
821 let current = self.best_number();
822 match block.unwrap_or(BlockNumber::Latest) {
823 BlockNumber::Latest | BlockNumber::Pending => current,
824 BlockNumber::Earliest => 0,
825 BlockNumber::Number(num) => num,
826 BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch),
827 BlockNumber::Finalized => current.saturating_sub(self.slots_in_an_epoch * 2),
828 }
829 }
830
831 fn get_block_with_hash(&self, id: impl Into<BlockId>) -> Option<(Block, B256)> {
833 let hash = self.blockchain.hash(id.into(), self.slots_in_an_epoch)?;
834 let block = self.get_block_by_hash(hash)?;
835 Some((block, hash))
836 }
837
838 pub fn get_block(&self, id: impl Into<BlockId>) -> Option<Block> {
839 self.get_block_with_hash(id).map(|(block, _)| block)
840 }
841
842 pub fn get_block_by_hash(&self, hash: B256) -> Option<Block> {
843 self.blockchain.get_block_by_hash(&hash)
844 }
845
846 pub(crate) fn mined_parity_trace_transaction(
848 &self,
849 hash: B256,
850 ) -> Option<Vec<LocalizedTransactionTrace>> {
851 self.blockchain.storage.read().transactions.get(&hash).map(|tx| tx.parity_traces())
852 }
853
854 pub(crate) fn mined_parity_trace_block(
856 &self,
857 block: u64,
858 ) -> Option<Vec<LocalizedTransactionTrace>> {
859 let block = self.get_block(block)?;
860 let mut traces = vec![];
861 let storage = self.blockchain.storage.read();
862 for tx in block.body.transactions {
863 if let Some(mined_tx) = storage.transactions.get(&tx.hash()) {
864 traces.extend(mined_tx.parity_traces());
865 }
866 }
867 Some(traces)
868 }
869
870 pub(crate) fn mined_transaction(&self, hash: B256) -> Option<MinedTransaction<N>> {
872 self.blockchain.storage.read().transactions.get(&hash).cloned()
873 }
874
875 pub async fn impersonate_signature(
877 &self,
878 signature: Bytes,
879 address: Address,
880 ) -> Result<(), BlockchainError> {
881 self.cheats.add_recover_override(signature, address);
882 Ok(())
883 }
884
885 pub async fn debug_code_by_hash(
887 &self,
888 code_hash: B256,
889 block_id: Option<BlockId>,
890 ) -> Result<Option<Bytes>, BlockchainError> {
891 if let Ok(code) = self.db.read().await.code_by_hash_ref(code_hash) {
892 return Ok(Some(code.original_bytes()));
893 }
894 if let Some(fork) = self.get_fork() {
895 return Ok(fork.debug_code_by_hash(code_hash, block_id).await?);
896 }
897
898 Ok(None)
899 }
900
901 pub async fn debug_db_get(&self, key: String) -> Result<Option<Bytes>, BlockchainError> {
909 let key_bytes = if key.starts_with("0x") {
910 hex::decode(&key)
911 .map_err(|_| BlockchainError::Message("Invalid hex key".to_string()))?
912 } else {
913 key.into_bytes()
914 };
915
916 if key_bytes.len() != 33 {
918 return Err(BlockchainError::Message(format!(
919 "Invalid key length: expected 33 bytes, got {}",
920 key_bytes.len()
921 )));
922 }
923
924 if key_bytes[0] != 0x63 {
926 return Err(BlockchainError::Message(
927 "Key prefix must be 0x63 for code hash lookups".to_string(),
928 ));
929 }
930
931 let code_hash = B256::from_slice(&key_bytes[1..33]);
932
933 self.debug_code_by_hash(code_hash, None).await
935 }
936
937 fn mined_block_by_hash(&self, hash: B256) -> Option<AnyRpcBlock> {
938 let block = self.blockchain.get_block_by_hash(&hash)?;
939 Some(self.convert_block_with_hash(block, Some(hash)))
940 }
941
942 pub(crate) async fn mined_transactions_by_block_number(
943 &self,
944 number: BlockNumber,
945 ) -> Option<Vec<AnyRpcTransaction>> {
946 if let Some(block) = self.get_block(number) {
947 return self.mined_transactions_in_block(&block);
948 }
949 None
950 }
951
952 pub(crate) fn mined_transactions_in_block(
954 &self,
955 block: &Block,
956 ) -> Option<Vec<AnyRpcTransaction>> {
957 let mut transactions = Vec::with_capacity(block.body.transactions.len());
958 let base_fee = block.header.base_fee_per_gas();
959 let storage = self.blockchain.storage.read();
960 for hash in block.body.transactions.iter().map(|tx| tx.hash()) {
961 let info = storage.transactions.get(&hash)?.info.clone();
962 let tx = block.body.transactions.get(info.transaction_index as usize)?.clone();
963
964 let tx = transaction_build(Some(hash), tx, Some(block), Some(info), base_fee);
965 transactions.push(tx);
966 }
967 Some(transactions)
968 }
969
970 pub fn mined_block_by_number(&self, number: BlockNumber) -> Option<AnyRpcBlock> {
971 let (block, hash) = self.get_block_with_hash(number)?;
972 let mut block = self.convert_block_with_hash(block, Some(hash));
973 block.transactions.convert_to_hashes();
974 Some(block)
975 }
976
977 pub fn get_full_block(&self, id: impl Into<BlockId>) -> Option<AnyRpcBlock> {
978 let (block, hash) = self.get_block_with_hash(id)?;
979 let transactions = self.mined_transactions_in_block(&block)?;
980 let mut block = self.convert_block_with_hash(block, Some(hash));
981 block.inner.transactions = BlockTransactions::Full(transactions);
982 Some(block)
983 }
984
985 pub fn convert_block(&self, block: Block) -> AnyRpcBlock {
987 self.convert_block_with_hash(block, None)
988 }
989
990 pub fn convert_block_with_hash(&self, block: Block, known_hash: Option<B256>) -> AnyRpcBlock {
993 let size = U256::from(alloy_rlp::encode(&block).len() as u32);
994
995 let header = block.header.clone();
996 let transactions = block.body.transactions;
997
998 let hash = known_hash.unwrap_or_else(|| header.hash_slow());
999 let Header { number, withdrawals_root, .. } = header;
1000
1001 let block = AlloyBlock {
1002 header: AlloyHeader {
1003 inner: AnyHeader::from(header),
1004 hash,
1005 total_difficulty: Some(self.total_difficulty()),
1006 size: Some(size),
1007 },
1008 transactions: alloy_rpc_types::BlockTransactions::Hashes(
1009 transactions.into_iter().map(|tx| tx.hash()).collect(),
1010 ),
1011 uncles: vec![],
1012 withdrawals: withdrawals_root.map(|_| Default::default()),
1013 };
1014
1015 let mut block = WithOtherFields::new(block);
1016
1017 if is_arbitrum(self.chain_id().to::<u64>()) {
1019 block.other.insert("l1BlockNumber".to_string(), number.into());
1021 }
1022
1023 if self.is_tempo() {
1025 let timestamp = block.header.timestamp();
1026 let gas_limit = block.header.gas_limit();
1027 block.other.insert(
1028 "timestampMillis".to_string(),
1029 serde_json::Value::String(format!("0x{:x}", timestamp.saturating_mul(1000))),
1030 );
1031 block.other.insert(
1032 "mainBlockGeneralGasLimit".to_string(),
1033 serde_json::Value::String(format!("0x{gas_limit:x}")),
1034 );
1035 block
1036 .other
1037 .insert("sharedGasLimit".to_string(), serde_json::Value::String("0x0".to_string()));
1038 block.other.insert(
1039 "timestampMillisPart".to_string(),
1040 serde_json::Value::String("0x0".to_string()),
1041 );
1042 }
1043
1044 AnyRpcBlock::from(block)
1045 }
1046
1047 pub async fn block_by_hash(&self, hash: B256) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1048 trace!(target: "backend", "get block by hash {:?}", hash);
1049 if let tx @ Some(_) = self.mined_block_by_hash(hash) {
1050 return Ok(tx);
1051 }
1052
1053 if let Some(fork) = self.get_fork() {
1054 return Ok(fork.block_by_hash(hash).await?);
1055 }
1056
1057 Ok(None)
1058 }
1059
1060 pub async fn block_by_hash_full(
1061 &self,
1062 hash: B256,
1063 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1064 trace!(target: "backend", "get block by hash {:?}", hash);
1065 if let tx @ Some(_) = self.get_full_block(hash) {
1066 return Ok(tx);
1067 }
1068
1069 if let Some(fork) = self.get_fork() {
1070 return Ok(fork.block_by_hash_full(hash).await?);
1071 }
1072
1073 Ok(None)
1074 }
1075
1076 pub async fn block_by_number(
1077 &self,
1078 number: BlockNumber,
1079 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1080 trace!(target: "backend", "get block by number {:?}", number);
1081 if let tx @ Some(_) = self.mined_block_by_number(number) {
1082 return Ok(tx);
1083 }
1084
1085 if let Some(fork) = self.get_fork() {
1086 let number = self.convert_block_number(Some(number));
1087 if fork.predates_fork_inclusive(number) {
1088 return Ok(fork.block_by_number(number).await?);
1089 }
1090 }
1091
1092 Ok(None)
1093 }
1094
1095 pub async fn block_by_number_full(
1096 &self,
1097 number: BlockNumber,
1098 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
1099 trace!(target: "backend", "get block by number {:?}", number);
1100 if let tx @ Some(_) = self.get_full_block(number) {
1101 return Ok(tx);
1102 }
1103
1104 if let Some(fork) = self.get_fork() {
1105 let number = self.convert_block_number(Some(number));
1106 if fork.predates_fork_inclusive(number) {
1107 return Ok(fork.block_by_number_full(number).await?);
1108 }
1109 }
1110
1111 Ok(None)
1112 }
1113
1114 pub async fn ensure_block_number<T: Into<BlockId>>(
1120 &self,
1121 block_id: Option<T>,
1122 ) -> Result<u64, BlockchainError> {
1123 let current = self.best_number();
1124 let requested =
1125 match block_id.map(Into::into).unwrap_or(BlockId::Number(BlockNumber::Latest)) {
1126 BlockId::Hash(hash) => {
1127 self.block_by_hash(hash.block_hash)
1128 .await?
1129 .ok_or(BlockchainError::BlockNotFound)?
1130 .header
1131 .number
1132 }
1133 BlockId::Number(num) => match num {
1134 BlockNumber::Latest | BlockNumber::Pending => current,
1135 BlockNumber::Earliest => U64::ZERO.to::<u64>(),
1136 BlockNumber::Number(num) => num,
1137 BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch),
1138 BlockNumber::Finalized => current.saturating_sub(self.slots_in_an_epoch * 2),
1139 },
1140 };
1141
1142 if requested > current {
1143 Err(BlockchainError::BlockOutOfRange(current, requested))
1144 } else {
1145 Ok(requested)
1146 }
1147 }
1148
1149 fn inject_precompiles(&self, precompiles: &mut PrecompilesMap) {
1156 self.networks.inject_precompiles(precompiles);
1157
1158 if let Some(factory) = &self.precompile_factory {
1159 precompiles.extend_precompiles(factory.precompiles());
1160 }
1161
1162 let cheats = Arc::new(self.cheats.clone());
1163 if cheats.has_recover_overrides() {
1164 let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats));
1165 precompiles.apply_precompile(&EC_RECOVER, move |_| {
1166 Some(DynPrecompile::new_stateful(
1167 cheat_ecrecover.precompile_id().clone(),
1168 move |input| cheat_ecrecover.call(input),
1169 ))
1170 });
1171 }
1172 }
1173
1174 fn inject_tempo_precompiles(
1175 &self,
1176 precompiles: &mut PrecompilesMap,
1177 cfg_env: &CfgEnv<TempoHardfork>,
1178 ) {
1179 self.inject_precompiles(precompiles);
1180 extend_tempo_precompiles(precompiles, cfg_env);
1181 }
1182
1183 fn transact_with_inspector_ref<'db, I, DB>(
1186 &self,
1187 db: &'db DB,
1188 evm_env: &EvmEnv,
1189 inspector: &mut I,
1190 tx_env: TxEnv,
1191 op_deposit: OpCallDepositInfo,
1192 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
1193 where
1194 DB: DatabaseRef + ?Sized,
1195 I: BackendInspector<WrapDatabaseRef<&'db DB>>,
1196 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1197 {
1198 #[cfg(feature = "optimism")]
1199 if self.is_optimism() {
1200 let op_tx = OpTransaction { base: tx_env, deposit: op_deposit, ..Default::default() };
1201 return self.transact_op_with_inspector_ref(db, evm_env, inspector, op_tx);
1202 }
1203 let _ = op_deposit;
1205 if self.is_tempo() {
1206 self.transact_tempo_with_inspector_ref(db, evm_env, inspector, TempoTxEnv::from(tx_env))
1207 } else {
1208 self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env)
1209 }
1210 }
1211
1212 fn transact_eth_with_inspector_ref<'db, I, DB>(
1217 &self,
1218 db: &'db DB,
1219 evm_env: &EvmEnv,
1220 inspector: &mut I,
1221 tx_env: TxEnv,
1222 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
1223 where
1224 DB: DatabaseRef + ?Sized,
1225 I: Inspector<EthEvmContext<WrapDatabaseRef<&'db DB>>>,
1226 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1227 {
1228 let mut evm = EthEvmFactory::default().create_evm_with_inspector(
1229 WrapDatabaseRef(db),
1230 evm_env.clone(),
1231 inspector,
1232 );
1233 self.inject_precompiles(evm.precompiles_mut());
1234 Ok(evm.transact(tx_env)?)
1235 }
1236
1237 fn transact_envelope_with_inspector_ref<'db, I, DB>(
1240 &self,
1241 db: &'db DB,
1242 evm_env: &EvmEnv,
1243 inspector: &mut I,
1244 tx: &FoundryTxEnvelope,
1245 sender: Address,
1246 ) -> Result<(ResultAndState<HaltReason>, TxEnv), BlockchainError>
1247 where
1248 DB: DatabaseRef + ?Sized,
1249 I: BackendInspector<WrapDatabaseRef<&'db DB>>,
1250 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1251 {
1252 if tx.is_tempo() {
1253 let tx_env: TempoTxEnv =
1254 FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into());
1255 let base = tx_env.inner.clone();
1256 let result = self.transact_tempo_with_inspector_ref(db, evm_env, inspector, tx_env)?;
1257 return Ok((result, base));
1258 }
1259 #[cfg(feature = "optimism")]
1260 if self.is_optimism() {
1261 let op_tx: OpTransaction<TxEnv> =
1262 FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into());
1263 let base = op_tx.base.clone();
1264 let result = self.transact_op_with_inspector_ref(db, evm_env, inspector, op_tx)?;
1265 return Ok((result, base));
1266 }
1267 let tx_env: TxEnv =
1268 FromTxWithEncoded::from_encoded_tx(tx, sender, tx.encoded_2718().into());
1269 let base = tx_env.clone();
1270 let result = self.transact_eth_with_inspector_ref(db, evm_env, inspector, tx_env)?;
1271 Ok((result, base))
1272 }
1273
1274 fn transact_tempo_with_inspector_ref<'db, I, DB>(
1276 &self,
1277 db: &'db DB,
1278 evm_env: &EvmEnv,
1279 inspector: &mut I,
1280 tx_env: TempoTxEnv,
1281 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
1282 where
1283 DB: DatabaseRef + ?Sized,
1284 I: Inspector<TempoContext<WrapDatabaseRef<&'db DB>>>,
1285 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1286 {
1287 let hardfork = self.tempo_hardfork();
1288 let tempo_env = EvmEnv::new(
1289 evm_env.cfg_env.clone().with_spec_and_gas_params(hardfork, tempo_gas_params(hardfork)),
1290 TempoBlockEnv { inner: evm_env.block_env.clone(), timestamp_millis_part: 0 },
1291 );
1292 let mut evm = TempoEvmFactory::default().create_evm_with_inspector(
1293 WrapDatabaseRef(db),
1294 tempo_env,
1295 inspector,
1296 );
1297 let cfg = evm.cfg.clone();
1298 self.inject_tempo_precompiles(evm.precompiles_mut(), &cfg);
1299 let result = evm.transact(tx_env)?;
1300 Ok(ResultAndState {
1301 result: result.result.map_haltreason(|h| match h {
1302 TempoHaltReason::Ethereum(eth) => eth,
1303 _ => HaltReason::PrecompileError,
1304 }),
1305 state: result.state,
1306 })
1307 }
1308
1309 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
1312 fn execute_with_block_executor<DB>(
1313 &self,
1314 db: DB,
1315 evm_env: &EvmEnv,
1316 parent_hash: B256,
1317 spec_id: SpecId,
1318 pool_transactions: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
1319 gas_config: &PoolTxGasConfig,
1320 inspector_tx_config: &InspectorTxConfig,
1321 validator: &dyn Fn(
1322 &PendingTransaction<FoundryTxEnvelope>,
1323 &AccountInfo,
1324 ) -> Result<(), InvalidTransactionError>,
1325 ) -> (ExecutedPoolTransactions<FoundryTxEnvelope>, BlockExecutionResult<FoundryReceiptEnvelope>)
1326 where
1327 DB: StateDB<Error = DatabaseError>,
1328 {
1329 let inspector = self.build_mining_inspector();
1330
1331 macro_rules! run {
1332 ($evm:expr) => {{
1333 self.inject_precompiles($evm.precompiles_mut());
1334 let mut executor = AnvilBlockExecutor::new($evm, parent_hash, spec_id);
1335 executor.apply_pre_execution_changes().expect("pre-execution changes failed");
1336 let pool_result = execute_pool_transactions(
1337 &mut executor,
1338 pool_transactions,
1339 gas_config,
1340 inspector_tx_config,
1341 self.cheats(),
1342 validator,
1343 );
1344 let (evm, block_result) = executor.finish().expect("executor finish failed");
1345 drop(evm);
1346 (pool_result, block_result)
1347 }};
1348 }
1349
1350 #[cfg(feature = "optimism")]
1351 if self.is_optimism() {
1352 let op_env = EvmEnv::new(
1353 evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(self.hardfork.into()),
1354 evm_env.block_env.clone(),
1355 );
1356 let mut evm =
1357 OpEvmFactory::<OpTx>::default().create_evm_with_inspector(db, op_env, inspector);
1358 return run!(evm);
1359 }
1360
1361 if self.is_tempo() {
1362 let hardfork = self.tempo_hardfork();
1363 let tempo_env = EvmEnv::new(
1364 evm_env
1365 .cfg_env
1366 .clone()
1367 .with_spec_and_gas_params(hardfork, tempo_gas_params(hardfork)),
1368 TempoBlockEnv { inner: evm_env.block_env.clone(), timestamp_millis_part: 0 },
1369 );
1370 let mut evm =
1371 TempoEvmFactory::default().create_evm_with_inspector(db, tempo_env, inspector);
1372 run!(evm)
1373 } else {
1374 let mut evm =
1375 EthEvmFactory::default().create_evm_with_inspector(db, evm_env.clone(), inspector);
1376 run!(evm)
1377 }
1378 }
1379
1380 fn build_call_env(
1389 &self,
1390 request: WithOtherFields<TransactionRequest>,
1391 fee_details: FeeDetails,
1392 block_env: BlockEnv,
1393 ) -> (EvmEnv, TxEnv, OpCallDepositInfo) {
1394 let tx_type = request.minimal_tx_type() as u8;
1395
1396 let WithOtherFields::<TransactionRequest> {
1397 inner:
1398 TransactionRequest {
1399 from,
1400 to,
1401 gas,
1402 value,
1403 input,
1404 access_list,
1405 blob_versioned_hashes,
1406 authorization_list,
1407 nonce,
1408 sidecar: _,
1409 chain_id,
1410 .. },
1412 other,
1413 } = request;
1414
1415 let FeeDetails {
1416 gas_price,
1417 max_fee_per_gas,
1418 max_priority_fee_per_gas,
1419 max_fee_per_blob_gas,
1420 } = fee_details;
1421
1422 let gas_limit = gas.unwrap_or(block_env.gas_limit);
1423 let mut evm_env = self.evm_env.read().clone();
1424 evm_env.block_env = block_env;
1425 evm_env.cfg_env.disable_block_gas_limit = true;
1428 evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
1429
1430 evm_env.cfg_env.disable_base_fee = true;
1436
1437 evm_env.cfg_env.disable_nonce_check = true;
1439
1440 let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| {
1441 self.fees().raw_gas_price().saturating_add(MIN_SUGGESTED_PRIORITY_FEE)
1442 });
1443 let caller = from.unwrap_or_default();
1444 let to = to.as_ref().and_then(TxKind::to);
1445 let blob_hashes = blob_versioned_hashes.unwrap_or_default();
1446 let mut tx_env = TxEnv {
1447 caller,
1448 gas_limit,
1449 gas_price,
1450 gas_priority_fee: max_priority_fee_per_gas,
1451 max_fee_per_blob_gas: max_fee_per_blob_gas
1452 .or_else(|| {
1453 if blob_hashes.is_empty() { Some(0) } else { evm_env.block_env.blob_gasprice() }
1454 })
1455 .unwrap_or_default(),
1456 kind: match to {
1457 Some(addr) => TxKind::Call(*addr),
1458 None => TxKind::Create,
1459 },
1460 tx_type,
1461 value: value.unwrap_or_default(),
1462 data: input.into_input().unwrap_or_default(),
1463 chain_id: Some(chain_id.unwrap_or(self.chain_id().to::<u64>())),
1464 access_list: access_list.unwrap_or_default(),
1465 blob_hashes,
1466 ..Default::default()
1467 };
1468 tx_env.set_signed_authorization(authorization_list.unwrap_or_default());
1469
1470 if let Some(nonce) = nonce {
1471 tx_env.nonce = nonce;
1472 }
1473
1474 if evm_env.block_env.basefee == 0 {
1475 evm_env.cfg_env.disable_base_fee = true;
1478 }
1479
1480 #[cfg(feature = "optimism")]
1482 let op_deposit = if self.ensure_op_deposits_active().is_ok()
1483 && let Ok(deposit) = get_deposit_tx_parts(&other)
1484 {
1485 deposit
1486 } else {
1487 OpCallDepositInfo::default()
1488 };
1489 #[cfg(not(feature = "optimism"))]
1490 let op_deposit = {
1491 let _ = &other;
1493 OpCallDepositInfo
1494 };
1495
1496 (evm_env, tx_env, op_deposit)
1497 }
1498
1499 pub fn call_with_state(
1500 &self,
1501 state: &dyn DatabaseRef,
1502 request: WithOtherFields<TransactionRequest>,
1503 fee_details: FeeDetails,
1504 block_env: BlockEnv,
1505 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
1506 let mut inspector = self.build_inspector();
1507
1508 let tempo_overrides = self.is_tempo().then(|| {
1510 let fee_token =
1511 request.other.get_deserialized::<Address>("feeToken").and_then(|r| r.ok());
1512 let nonce_key = request
1513 .other
1514 .get_deserialized::<U256>("nonceKey")
1515 .and_then(|r| r.ok())
1516 .unwrap_or_default();
1517 let valid_before = request
1518 .other
1519 .get_deserialized::<U256>("validBefore")
1520 .and_then(|r| r.ok())
1521 .map(|v| v.saturating_to::<u64>());
1522 let valid_after = request
1523 .other
1524 .get_deserialized::<U256>("validAfter")
1525 .and_then(|r| r.ok())
1526 .map(|v| v.saturating_to::<u64>());
1527 (fee_token, nonce_key, valid_before, valid_after)
1528 });
1529
1530 let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block_env);
1531
1532 let ResultAndState { result, state } =
1533 if let Some((fee_token, nonce_key, valid_before, valid_after)) = tempo_overrides {
1534 use tempo_primitives::transaction::Call;
1535
1536 let base = tx_env;
1537 let mut tempo_tx = TempoTxEnv::from(base.clone());
1538 tempo_tx.fee_token = fee_token;
1539
1540 if !nonce_key.is_zero() || valid_before.is_some() || valid_after.is_some() {
1541 let estimation_hash = keccak256(base.data.as_ref());
1546 tempo_tx.unique_tx_identifier = Some(estimation_hash);
1550 tempo_tx.tempo_tx_env = Some(Box::new(TempoBatchCallEnv {
1551 nonce_key,
1552 valid_before,
1553 valid_after,
1554 aa_calls: vec![Call { to: base.kind, value: base.value, input: base.data }],
1555 tx_hash: estimation_hash,
1556 expiring_nonce_idx: Some(0),
1557 ..Default::default()
1558 }));
1559 }
1560 self.transact_tempo_with_inspector_ref(state, &evm_env, &mut inspector, tempo_tx)?
1561 } else {
1562 self.transact_with_inspector_ref(
1563 state,
1564 &evm_env,
1565 &mut inspector,
1566 tx_env,
1567 op_deposit,
1568 )?
1569 };
1570
1571 let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
1572 inspector.print_logs();
1573
1574 if self.print_traces {
1575 inspector.into_print_traces(self.call_trace_decoder.clone());
1576 }
1577
1578 Ok((exit_reason, out, gas_used as u128, state))
1579 }
1580
1581 pub fn build_access_list_with_state(
1582 &self,
1583 state: &dyn DatabaseRef,
1584 request: WithOtherFields<TransactionRequest>,
1585 fee_details: FeeDetails,
1586 block_env: BlockEnv,
1587 ) -> Result<(InstructionResult, Option<Output>, u64, AccessList), BlockchainError> {
1588 let mut inspector =
1589 AccessListInspector::new(request.access_list.clone().unwrap_or_default());
1590
1591 let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block_env);
1592 let ResultAndState { result, state: _ } =
1593 self.transact_with_inspector_ref(state, &evm_env, &mut inspector, tx_env, op_deposit)?;
1594 let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
1595 let access_list = inspector.access_list();
1596 Ok((exit_reason, out, gas_used, access_list))
1597 }
1598
1599 pub fn get_code_with_state(
1600 &self,
1601 state: &dyn DatabaseRef,
1602 address: Address,
1603 ) -> Result<Bytes, BlockchainError> {
1604 trace!(target: "backend", "get code for {:?}", address);
1605 let account = state.basic_ref(address)?.unwrap_or_default();
1606 if account.code_hash == KECCAK_EMPTY {
1607 return Ok(Default::default());
1609 }
1610 let code = if let Some(code) = account.code {
1611 code
1612 } else {
1613 state.code_by_hash_ref(account.code_hash)?
1614 };
1615 Ok(code.bytes()[..code.len()].to_vec().into())
1616 }
1617
1618 pub fn get_balance_with_state<D>(
1619 &self,
1620 state: D,
1621 address: Address,
1622 ) -> Result<U256, BlockchainError>
1623 where
1624 D: DatabaseRef,
1625 {
1626 trace!(target: "backend", "get balance for {:?}", address);
1627 Ok(state.basic_ref(address)?.unwrap_or_default().balance)
1628 }
1629
1630 pub async fn transaction_by_block_number_and_index(
1631 &self,
1632 number: BlockNumber,
1633 index: Index,
1634 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
1635 if let Some(block) = self.mined_block_by_number(number) {
1636 return Ok(self.mined_transaction_by_block_hash_and_index(block.header.hash, index));
1637 }
1638
1639 if let Some(fork) = self.get_fork() {
1640 let number = self.convert_block_number(Some(number));
1641 if fork.predates_fork(number) {
1642 return Ok(fork
1643 .transaction_by_block_number_and_index(number, index.into())
1644 .await?);
1645 }
1646 }
1647
1648 Ok(None)
1649 }
1650
1651 pub async fn transaction_by_block_hash_and_index(
1652 &self,
1653 hash: B256,
1654 index: Index,
1655 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
1656 if let tx @ Some(_) = self.mined_transaction_by_block_hash_and_index(hash, index) {
1657 return Ok(tx);
1658 }
1659
1660 if let Some(fork) = self.get_fork() {
1661 return Ok(fork.transaction_by_block_hash_and_index(hash, index.into()).await?);
1662 }
1663
1664 Ok(None)
1665 }
1666
1667 pub fn mined_transaction_by_block_hash_and_index(
1668 &self,
1669 block_hash: B256,
1670 index: Index,
1671 ) -> Option<AnyRpcTransaction> {
1672 let (info, block, tx) = {
1673 let storage = self.blockchain.storage.read();
1674 let block = storage.blocks.get(&block_hash).cloned()?;
1675 let index: usize = index.into();
1676 let tx = block.body.transactions.get(index)?.clone();
1677 let info = storage.transactions.get(&tx.hash())?.info.clone();
1678 (info, block, tx)
1679 };
1680
1681 Some(transaction_build(
1682 Some(info.transaction_hash),
1683 tx,
1684 Some(&block),
1685 Some(info),
1686 block.header.base_fee_per_gas(),
1687 ))
1688 }
1689
1690 pub async fn transaction_by_hash(
1691 &self,
1692 hash: B256,
1693 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
1694 trace!(target: "backend", "transaction_by_hash={:?}", hash);
1695 if let tx @ Some(_) = self.mined_transaction_by_hash(hash) {
1696 return Ok(tx);
1697 }
1698
1699 if let Some(fork) = self.get_fork() {
1700 return fork
1701 .transaction_by_hash(hash)
1702 .await
1703 .map_err(BlockchainError::AlloyForkProvider);
1704 }
1705
1706 Ok(None)
1707 }
1708
1709 pub fn mined_transaction_by_hash(&self, hash: B256) -> Option<AnyRpcTransaction> {
1710 let (info, block) = {
1711 let storage = self.blockchain.storage.read();
1712 let MinedTransaction { info, block_hash, .. } =
1713 storage.transactions.get(&hash)?.clone();
1714 let block = storage.blocks.get(&block_hash).cloned()?;
1715 (info, block)
1716 };
1717 let tx = block.body.transactions.get(info.transaction_index as usize)?.clone();
1718
1719 Some(transaction_build(
1720 Some(info.transaction_hash),
1721 tx,
1722 Some(&block),
1723 Some(info),
1724 block.header.base_fee_per_gas(),
1725 ))
1726 }
1727
1728 pub async fn trace_transaction(
1730 &self,
1731 hash: B256,
1732 ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
1733 if let Some(traces) = self.mined_parity_trace_transaction(hash) {
1734 return Ok(traces);
1735 }
1736
1737 if let Some(fork) = self.get_fork() {
1738 return Ok(fork.trace_transaction(hash).await?);
1739 }
1740
1741 Ok(vec![])
1742 }
1743
1744 pub async fn trace_block(
1746 &self,
1747 block: BlockNumber,
1748 ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
1749 let number = self.convert_block_number(Some(block));
1750 if let Some(traces) = self.mined_parity_trace_block(number) {
1751 return Ok(traces);
1752 }
1753
1754 if let Some(fork) = self.get_fork()
1755 && fork.predates_fork(number)
1756 {
1757 return Ok(fork.trace_block(number).await?);
1758 }
1759
1760 Ok(vec![])
1761 }
1762
1763 pub async fn trace_replay_block_transactions(
1765 &self,
1766 block: BlockNumber,
1767 trace_types: HashSet<TraceType>,
1768 ) -> Result<Vec<TraceResultsWithTransactionHash>, BlockchainError> {
1769 let block_number = self.convert_block_number(Some(block));
1770
1771 if let Some(results) =
1773 self.mined_parity_trace_replay_block_transactions(block_number, &trace_types)
1774 {
1775 return Ok(results);
1776 }
1777
1778 if let Some(fork) = self.get_fork()
1780 && fork.predates_fork(block_number)
1781 {
1782 return Ok(fork.trace_replay_block_transactions(block_number, trace_types).await?);
1783 }
1784
1785 Ok(vec![])
1786 }
1787
1788 fn mined_parity_trace_replay_block_transactions(
1790 &self,
1791 block_number: u64,
1792 trace_types: &HashSet<TraceType>,
1793 ) -> Option<Vec<TraceResultsWithTransactionHash>> {
1794 let block = self.get_block(block_number)?;
1795
1796 let parent_hash = block.header.parent_hash;
1798 let trace_config = TracingInspectorConfig::from_parity_config(trace_types);
1799
1800 let read_guard = self.states.upgradable_read();
1801 if let Some(state) = read_guard.get_state(&parent_hash) {
1802 self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types)
1803 } else {
1804 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
1805 let state = write_guard.get_on_disk_state(&parent_hash)?;
1806 self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types)
1807 }
1808 }
1809
1810 fn replay_block_transactions_with_inspector(
1812 &self,
1813 block: &Block,
1814 parent_state: &StateDb,
1815 trace_config: TracingInspectorConfig,
1816 trace_types: &HashSet<TraceType>,
1817 ) -> Option<Vec<TraceResultsWithTransactionHash>> {
1818 let mut cache_db = CacheDB::new(Box::new(parent_state));
1819 let mut results = Vec::new();
1820
1821 let mut evm_env = self.evm_env.read().clone();
1823 evm_env.block_env = block_env_from_header(&block.header);
1824
1825 for tx_envelope in &block.body.transactions {
1827 let tx_hash = tx_envelope.hash();
1828
1829 let mut inspector = TracingInspector::new(trace_config);
1831
1832 let pending_tx =
1834 PendingTransaction::from_maybe_impersonated(tx_envelope.clone()).ok()?;
1835 let (result, _) = self
1836 .transact_envelope_with_inspector_ref(
1837 &cache_db,
1838 &evm_env,
1839 &mut inspector,
1840 pending_tx.transaction.as_ref(),
1841 *pending_tx.sender(),
1842 )
1843 .ok()?;
1844
1845 let full_trace = inspector
1847 .into_parity_builder()
1848 .into_trace_results_with_state(&result, trace_types, &cache_db)
1849 .ok()?;
1850
1851 results.push(TraceResultsWithTransactionHash { transaction_hash: tx_hash, full_trace });
1852
1853 cache_db.commit(result.state);
1855 }
1856
1857 Some(results)
1858 }
1859
1860 pub async fn trace_filter(
1862 &self,
1863 filter: TraceFilter,
1864 ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
1865 let matcher = filter.matcher();
1866 let start = filter.from_block.unwrap_or(0);
1867 let end = filter.to_block.unwrap_or_else(|| self.best_number());
1868
1869 if start > end {
1870 return Err(BlockchainError::RpcError(RpcError::invalid_params(
1871 "invalid block range, ensure that to block is greater than from block".to_string(),
1872 )));
1873 }
1874
1875 let dist = end - start;
1876 if dist > 300 {
1877 return Err(BlockchainError::RpcError(RpcError::invalid_params(
1878 "block range too large, currently limited to 300".to_string(),
1879 )));
1880 }
1881
1882 let mut trace_tasks = vec![];
1884 for num in start..=end {
1885 trace_tasks.push(self.trace_block(num.into()));
1886 }
1887
1888 let traces = futures::future::try_join_all(trace_tasks).await?;
1890 let filtered_traces =
1891 traces.into_iter().flatten().filter(|trace| matcher.matches(&trace.trace));
1892
1893 let filtered_traces: Vec<_> = if let Some(after) = filter.after {
1895 filtered_traces.skip(after as usize).collect()
1896 } else {
1897 filtered_traces.collect()
1898 };
1899
1900 let filtered_traces: Vec<_> = if let Some(count) = filter.count {
1901 filtered_traces.into_iter().take(count as usize).collect()
1902 } else {
1903 filtered_traces
1904 };
1905
1906 Ok(filtered_traces)
1907 }
1908
1909 pub fn get_blobs_by_block_id(
1910 &self,
1911 id: impl Into<BlockId>,
1912 versioned_hashes: Vec<B256>,
1913 ) -> Result<Option<Vec<alloy_consensus::Blob>>> {
1914 Ok(self.get_block(id).map(|block| {
1915 block
1916 .body
1917 .transactions
1918 .iter()
1919 .filter_map(|tx| tx.as_ref().sidecar())
1920 .flat_map(|sidecar| {
1921 sidecar.sidecar.blobs().iter().zip(sidecar.sidecar.commitments().iter())
1922 })
1923 .filter(|(_, commitment)| {
1924 versioned_hashes.is_empty()
1926 || versioned_hashes.contains(&kzg_to_versioned_hash(commitment.as_slice()))
1927 })
1928 .map(|(blob, _)| *blob)
1929 .collect()
1930 }))
1931 }
1932
1933 #[allow(clippy::large_stack_frames)]
1934 pub fn get_blob_by_versioned_hash(&self, hash: B256) -> Result<Option<Blob>> {
1935 let storage = self.blockchain.storage.read();
1936 for block in storage.blocks.values() {
1937 for tx in &block.body.transactions {
1938 let typed_tx = tx.as_ref();
1939 if let Some(sidecar) = typed_tx.sidecar() {
1940 for versioned_hash in sidecar.sidecar.versioned_hashes() {
1941 if versioned_hash == hash
1942 && let Some(index) =
1943 sidecar.sidecar.commitments().iter().position(|commitment| {
1944 kzg_to_versioned_hash(commitment.as_slice()) == *hash
1945 })
1946 && let Some(blob) = sidecar.sidecar.blobs().get(index)
1947 {
1948 return Ok(Some(*blob));
1949 }
1950 }
1951 }
1952 }
1953 }
1954 Ok(None)
1955 }
1956
1957 #[expect(clippy::too_many_arguments)]
1959 pub async fn with_genesis(
1960 db: Arc<AsyncRwLock<Box<dyn Db>>>,
1961 env: Arc<RwLock<EvmEnv>>,
1962 networks: NetworkConfigs,
1963 genesis: GenesisConfig,
1964 fees: FeeManager,
1965 fork: Arc<RwLock<Option<ClientFork>>>,
1966 enable_steps_tracing: bool,
1967 print_logs: bool,
1968 print_traces: bool,
1969 call_trace_decoder: Arc<CallTraceDecoder>,
1970 prune_state_history_config: PruneStateHistoryConfig,
1971 max_persisted_states: Option<usize>,
1972 transaction_block_keeper: Option<usize>,
1973 automine_block_time: Option<Duration>,
1974 cache_path: Option<PathBuf>,
1975 node_config: Arc<AsyncRwLock<NodeConfig>>,
1976 ) -> Result<Self> {
1977 let blockchain = if let Some(fork) = fork.read().as_ref() {
1979 trace!(target: "backend", "using forked blockchain at {}", fork.block_number());
1980 Blockchain::forked(fork.block_number(), fork.block_hash(), fork.total_difficulty())
1981 } else {
1982 Blockchain::new(
1983 &env.read(),
1984 fees.is_eip1559().then(|| fees.base_fee()),
1985 genesis.timestamp,
1986 genesis.number,
1987 )
1988 };
1989
1990 if fork.read().is_none() {
1993 env.write().block_env.number = U256::from(genesis.number);
1994 }
1995
1996 let start_timestamp = if let Some(fork) = fork.read().as_ref() {
1997 fork.timestamp()
1998 } else {
1999 genesis.timestamp
2000 };
2001
2002 let mut states = if prune_state_history_config.is_config_enabled() {
2003 prune_state_history_config
2005 .max_memory_history
2006 .map(|limit| InMemoryBlockStates::new(limit, 0))
2007 .unwrap_or_default()
2008 .memory_only()
2009 } else if max_persisted_states.is_some() {
2010 max_persisted_states
2011 .map(|limit| InMemoryBlockStates::new(DEFAULT_HISTORY_LIMIT, limit))
2012 .unwrap_or_default()
2013 } else {
2014 Default::default()
2015 };
2016
2017 if let Some(cache_path) = cache_path {
2018 states = states.disk_path(cache_path);
2019 }
2020
2021 let (slots_in_an_epoch, precompile_factory, disable_pool_balance_checks, hardfork) = {
2022 let cfg = node_config.read().await;
2023 (
2024 cfg.slots_in_an_epoch,
2025 cfg.precompile_factory.clone(),
2026 cfg.disable_pool_balance_checks,
2027 cfg.get_hardfork(),
2028 )
2029 };
2030
2031 let backend = Self {
2032 db,
2033 blockchain,
2034 states: Arc::new(RwLock::new(states)),
2035 evm_env: env,
2036 networks,
2037 hardfork,
2038 fork,
2039 time: TimeManager::new(start_timestamp),
2040 cheats: Default::default(),
2041 new_block_listeners: Default::default(),
2042 fees,
2043 genesis,
2044 active_state_snapshots: Arc::new(Mutex::new(Default::default())),
2045 enable_steps_tracing,
2046 print_logs,
2047 print_traces,
2048 call_trace_decoder,
2049 prune_state_history_config,
2050 transaction_block_keeper,
2051 node_config,
2052 slots_in_an_epoch,
2053 precompile_factory,
2054 mining: Arc::new(tokio::sync::Mutex::new(())),
2055 disable_pool_balance_checks,
2056 };
2057
2058 if let Some(interval_block_time) = automine_block_time {
2059 backend.update_interval_mine_block_time(interval_block_time);
2060 }
2061
2062 backend.apply_genesis().await.wrap_err("failed to create genesis")?;
2064 Ok(backend)
2065 }
2066
2067 async fn apply_genesis(&self) -> Result<(), DatabaseError> {
2071 trace!(target: "backend", "setting genesis balances");
2072
2073 if self.fork.read().is_some() {
2074 let mut genesis_accounts_futures = Vec::with_capacity(self.genesis.accounts.len());
2076 for address in self.genesis.accounts.iter().copied() {
2077 let db = Arc::clone(&self.db);
2078
2079 genesis_accounts_futures.push(tokio::task::spawn(async move {
2082 let db = db.read().await;
2083 let info = db.basic_ref(address)?.unwrap_or_default();
2084 Ok::<_, DatabaseError>((address, info))
2085 }));
2086 }
2087
2088 let genesis_accounts = futures::future::join_all(genesis_accounts_futures).await;
2089
2090 let mut db = self.db.write().await;
2091
2092 for res in genesis_accounts {
2093 let (address, mut info) = res.unwrap()?;
2094 info.balance = self.genesis.balance;
2095 db.insert_account(address, info.clone());
2096 }
2097 } else {
2098 let mut db = self.db.write().await;
2099 for (account, info) in self.genesis.account_infos() {
2100 db.insert_account(account, info);
2101 }
2102
2103 db.insert_block_hash(U256::from(self.best_number()), self.best_hash());
2106
2107 if self.spec_id() >= SpecId::PRAGUE {
2109 db.set_code(
2110 eip2935::HISTORY_STORAGE_ADDRESS,
2111 eip2935::HISTORY_STORAGE_CODE.clone(),
2112 )?;
2113 }
2114 }
2115
2116 let db = self.db.write().await;
2117 self.genesis.apply_genesis_json_alloc(db)?;
2119
2120 if self.networks.is_tempo() && !self.is_fork() {
2123 let chain_id = self.evm_env.read().cfg_env.chain_id;
2124 let timestamp = self.genesis.timestamp;
2125 let test_accounts: Vec<Address> = self.genesis.accounts.clone();
2126 let hardfork = self.tempo_hardfork();
2127 let mut db = self.db.write().await;
2128 crate::eth::backend::tempo::initialize_tempo_precompiles(
2129 &mut **db,
2130 chain_id,
2131 timestamp,
2132 &test_accounts,
2133 hardfork,
2134 )
2135 .map_err(|e| {
2136 tracing::error!(target: "backend", "failed to initialize Tempo precompiles: {e}");
2137 DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}")))
2138 })?;
2139 trace!(target: "backend", "initialized Tempo precompiles and fee tokens for {} accounts", test_accounts.len());
2140 }
2141
2142 trace!(target: "backend", "set genesis balances");
2143
2144 Ok(())
2145 }
2146
2147 pub async fn reset_fork(&self, forking: Forking) -> Result<(), BlockchainError> {
2149 if !self.is_fork() {
2150 if let Some(eth_rpc_url) = forking.json_rpc_url.clone() {
2151 let mut evm_env = self.evm_env.read().clone();
2152
2153 let (db, config) = {
2154 let mut node_config = self.node_config.write().await;
2155
2156 node_config.base_fee.take();
2159 node_config.fork_urls = vec![eth_rpc_url.clone()];
2160
2161 node_config.setup_fork_db_config(eth_rpc_url, &mut evm_env, &self.fees).await?
2162 };
2163
2164 *self.db.write().await = Box::new(db);
2165
2166 let fork = ClientFork::new(config, Arc::clone(&self.db));
2167
2168 *self.evm_env.write() = evm_env;
2169 *self.fork.write() = Some(fork);
2170 } else {
2171 return Err(RpcError::invalid_params(
2172 "Forking not enabled and RPC URL not provided to start forking",
2173 )
2174 .into());
2175 }
2176 }
2177
2178 if let Some(fork) = self.get_fork() {
2179 let block_number =
2180 forking.block_number.map(BlockNumber::from).unwrap_or(BlockNumber::Latest);
2181 let reset_urls =
2183 forking.json_rpc_url.as_ref().map(|url| vec![url.clone()]).unwrap_or_default();
2184 fork.reset(reset_urls, block_number).await?;
2185 let fork_block_number = fork.block_number();
2186 let fork_block = fork
2187 .block_by_number(fork_block_number)
2188 .await?
2189 .ok_or(BlockchainError::BlockNotFound)?;
2190 {
2192 if let Some(fork_url) = forking.json_rpc_url {
2193 self.reset_block_number(fork_url, fork_block_number).await?;
2194 } else {
2195 {
2198 let maybe_fork_url =
2199 { self.node_config.read().await.fork_urls.first().cloned() };
2200 if let Some(fork_url) = maybe_fork_url {
2201 self.reset_block_number(fork_url, fork_block_number).await?;
2202 }
2203 }
2204
2205 let gas_limit = self.node_config.read().await.fork_gas_limit(&fork_block);
2206 let mut env = self.evm_env.write();
2207
2208 env.cfg_env.chain_id = fork.chain_id();
2209 env.block_env = BlockEnv {
2210 number: U256::from(fork_block_number),
2211 timestamp: U256::from(fork_block.header.timestamp()),
2212 gas_limit,
2213 difficulty: fork_block.header.difficulty(),
2214 prevrandao: Some(fork_block.header.mix_hash().unwrap_or_default()),
2215 beneficiary: env.block_env.beneficiary,
2217 basefee: env.block_env.basefee,
2218 ..env.block_env.clone()
2219 };
2220
2221 let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
2224 fork_block.header.gas_used(),
2225 gas_limit,
2226 fork_block.header.base_fee_per_gas().unwrap_or_default(),
2227 );
2228
2229 self.fees.set_base_fee(next_block_base_fee);
2230 }
2231
2232 self.time.reset(fork_block.header.timestamp());
2234
2235 self.blockchain.storage.write().total_difficulty = fork.total_difficulty();
2237 }
2238 *self.blockchain.storage.write() = BlockchainStorage::forked(
2240 fork.block_number(),
2241 fork.block_hash(),
2242 fork.total_difficulty(),
2243 );
2244 self.states.write().clear();
2245 self.db.write().await.clear();
2246
2247 self.apply_genesis().await?;
2248
2249 trace!(target: "backend", "reset fork");
2250
2251 Ok(())
2252 } else {
2253 Err(RpcError::invalid_params("Forking not enabled").into())
2254 }
2255 }
2256
2257 pub async fn reset_to_in_mem(&self) -> Result<(), BlockchainError> {
2259 *self.fork.write() = None;
2261
2262 let genesis_timestamp = self.genesis.timestamp;
2263 let genesis_number = self.genesis.number;
2264
2265 {
2267 let mut env = self.evm_env.write();
2268 env.block_env.number = U256::from(genesis_number);
2269 env.block_env.timestamp = U256::from(genesis_timestamp);
2270 env.block_env.basefee = self.fees.base_fee();
2272 env.block_env.prevrandao = Some(B256::ZERO);
2273 }
2274
2275 let base_fee = self.fees.is_eip1559().then(|| self.fees.base_fee());
2277 *self.blockchain.storage.write() = BlockchainStorage::new(
2278 &self.evm_env.read(),
2279 base_fee,
2280 genesis_timestamp,
2281 genesis_number,
2282 );
2283 self.states.write().clear();
2284
2285 self.db.write().await.clear();
2287
2288 self.time.reset(genesis_timestamp);
2290
2291 if self.fees.is_eip1559() {
2293 self.fees.set_base_fee(crate::eth::fees::INITIAL_BASE_FEE);
2294 }
2295
2296 self.fees.set_gas_price(crate::eth::fees::INITIAL_GAS_PRICE);
2297
2298 self.apply_genesis().await?;
2300
2301 trace!(target: "backend", "reset to fresh in-memory state");
2302
2303 Ok(())
2304 }
2305
2306 async fn reset_block_number(
2307 &self,
2308 fork_url: String,
2309 fork_block_number: u64,
2310 ) -> Result<(), BlockchainError> {
2311 let mut node_config = self.node_config.write().await;
2312 node_config.fork_choice = Some(ForkChoice::Block(fork_block_number as i128));
2313 node_config.fork_urls = vec![fork_url.clone()];
2315
2316 let mut evm_env = self.evm_env.read().clone();
2317 let (forked_db, client_fork_config) =
2318 node_config.setup_fork_db_config(fork_url, &mut evm_env, &self.fees).await?;
2319
2320 *self.db.write().await = Box::new(forked_db);
2321 let fork = ClientFork::new(client_fork_config, Arc::clone(&self.db));
2322 *self.fork.write() = Some(fork);
2323 *self.evm_env.write() = evm_env;
2324
2325 Ok(())
2326 }
2327
2328 pub async fn revert_state_snapshot(&self, id: U256) -> Result<bool, BlockchainError> {
2330 let block = { self.active_state_snapshots.lock().remove(&id) };
2331 if let Some((num, hash)) = block {
2332 let best_block_hash = {
2333 let current_height = self.best_number();
2335 let mut storage = self.blockchain.storage.write();
2336
2337 for n in ((num + 1)..=current_height).rev() {
2338 trace!(target: "backend", "reverting block {}", n);
2339 if let Some(hash) = storage.hashes.remove(&n)
2340 && let Some(block) = storage.blocks.remove(&hash)
2341 {
2342 for tx in block.body.transactions {
2343 let _ = storage.transactions.remove(&tx.hash());
2344 }
2345 }
2346 }
2347
2348 storage.best_number = num;
2349 storage.best_hash = hash;
2350 hash
2351 };
2352 let block =
2353 self.block_by_hash(best_block_hash).await?.ok_or(BlockchainError::BlockNotFound)?;
2354
2355 let reset_time = block.header.timestamp();
2356 self.time.reset(reset_time);
2357
2358 let mut env = self.evm_env.write();
2359 env.block_env = BlockEnv {
2360 number: U256::from(num),
2361 timestamp: U256::from(block.header.timestamp()),
2362 difficulty: block.header.difficulty(),
2363 prevrandao: Some(block.header.mix_hash().unwrap_or_default()),
2365 gas_limit: block.header.gas_limit(),
2366 beneficiary: env.block_env.beneficiary,
2368 basefee: env.block_env.basefee,
2369 ..Default::default()
2370 }
2371 }
2372 Ok(self.db.write().await.revert_state(id, RevertStateSnapshotAction::RevertRemove))
2373 }
2374
2375 pub async fn inspect_tx(
2377 &self,
2378 tx: Arc<PoolTransaction<FoundryTxEnvelope>>,
2379 ) -> Result<
2380 (InstructionResult, Option<Output>, u64, State, Vec<revm::primitives::Log>),
2381 BlockchainError,
2382 > {
2383 let evm_env = self.next_evm_env();
2384 let db = self.db.read().await;
2385 let mut inspector = self.build_inspector();
2386 let (ResultAndState { result, state }, _) = self.transact_envelope_with_inspector_ref(
2387 &**db,
2388 &evm_env,
2389 &mut inspector,
2390 tx.pending_transaction.transaction.as_ref(),
2391 *tx.pending_transaction.sender(),
2392 )?;
2393 let (exit_reason, gas_used, out, logs) = unpack_execution_result(result);
2394
2395 inspector.print_logs();
2396
2397 if self.print_traces {
2398 inspector.print_traces(self.call_trace_decoder.clone());
2399 }
2400
2401 Ok((exit_reason, out, gas_used, state, logs))
2402 }
2403}
2404
2405impl<N: Network> Backend<N>
2406where
2407 N::ReceiptEnvelope: TxReceipt<Log = alloy_primitives::Log>,
2408{
2409 fn mined_logs_for_block(&self, filter: Filter, block: Block, block_hash: B256) -> Vec<Log> {
2411 let mut all_logs = Vec::new();
2412 let mut block_log_index = 0u32;
2413
2414 let storage = self.blockchain.storage.read();
2415
2416 for tx in block.body.transactions {
2417 let Some(tx) = storage.transactions.get(&tx.hash()) else {
2418 continue;
2419 };
2420
2421 let logs = tx.receipt.logs();
2422 let transaction_hash = tx.info.transaction_hash;
2423
2424 for log in logs {
2425 if filter.matches(log) {
2426 all_logs.push(Log {
2427 inner: log.clone(),
2428 block_hash: Some(block_hash),
2429 block_number: Some(block.header.number()),
2430 block_timestamp: Some(block.header.timestamp()),
2431 transaction_hash: Some(transaction_hash),
2432 transaction_index: Some(tx.info.transaction_index),
2433 log_index: Some(block_log_index as u64),
2434 removed: false,
2435 });
2436 }
2437 block_log_index += 1;
2438 }
2439 }
2440 all_logs
2441 }
2442
2443 async fn logs_for_block(
2445 &self,
2446 filter: Filter,
2447 hash: B256,
2448 ) -> Result<Vec<Log>, BlockchainError> {
2449 if let Some(block) = self.blockchain.get_block_by_hash(&hash) {
2450 return Ok(self.mined_logs_for_block(filter, block, hash));
2451 }
2452
2453 if let Some(fork) = self.get_fork() {
2454 return Ok(fork.logs(&filter).await?);
2455 }
2456
2457 Err(BlockchainError::UnknownBlock)
2458 }
2459
2460 async fn logs_for_range(
2462 &self,
2463 filter: &Filter,
2464 mut from: u64,
2465 to: u64,
2466 ) -> Result<Vec<Log>, BlockchainError> {
2467 let mut all_logs = Vec::new();
2468
2469 if let Some(fork) = self.get_fork() {
2471 let to_on_fork = if fork.predates_fork(to) {
2472 to
2473 } else {
2474 fork.block_number()
2476 };
2477
2478 if fork.predates_fork_inclusive(from) {
2479 let filter = filter.clone().from_block(from).to_block(to_on_fork);
2481 all_logs = fork.logs(&filter).await?;
2482
2483 from = fork.block_number() + 1;
2485 }
2486 }
2487
2488 for number in from..=to {
2489 if let Some((block, hash)) = self.get_block_with_hash(number) {
2490 all_logs.extend(self.mined_logs_for_block(filter.clone(), block, hash));
2491 }
2492 }
2493
2494 Ok(all_logs)
2495 }
2496
2497 pub async fn logs(&self, filter: Filter) -> Result<Vec<Log>, BlockchainError> {
2499 trace!(target: "backend", "get logs [{:?}]", filter);
2500 if let Some(hash) = filter.get_block_hash() {
2501 self.logs_for_block(filter, hash).await
2502 } else {
2503 let best = self.best_number();
2504 let to_block =
2505 self.convert_block_number(filter.block_option.get_to_block().copied()).min(best);
2506 let from_block =
2507 self.convert_block_number(filter.block_option.get_from_block().copied());
2508 if from_block > best {
2509 return Err(BlockchainError::BlockOutOfRange(best, from_block));
2510 }
2511
2512 self.logs_for_range(&filter, from_block, to_block).await
2513 }
2514 }
2515
2516 pub fn mined_receipts(&self, hash: B256) -> Option<Vec<N::ReceiptEnvelope>> {
2518 let block = self.mined_block_by_hash(hash)?;
2519 let mut receipts = Vec::new();
2520 let storage = self.blockchain.storage.read();
2521 for tx in block.transactions.hashes() {
2522 let receipt = storage.transactions.get(&tx)?.receipt.clone();
2523 receipts.push(receipt);
2524 }
2525 Some(receipts)
2526 }
2527}
2528
2529impl<N: Network> Backend<N>
2531where
2532 Self: TransactionValidator<FoundryTxEnvelope>,
2533 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
2534{
2535 pub async fn mine_block(
2540 &self,
2541 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
2542 ) -> MinedBlockOutcome<FoundryTxEnvelope> {
2543 self.do_mine_block(pool_transactions).await
2544 }
2545
2546 fn build_block_info(
2548 evm_env: &EvmEnv,
2549 parent_hash: B256,
2550 number: u64,
2551 state_root: B256,
2552 block_result: BlockExecutionResult<FoundryReceiptEnvelope>,
2553 transactions: Vec<MaybeImpersonatedTransaction<FoundryTxEnvelope>>,
2554 transaction_infos: Vec<TransactionInfo>,
2555 ) -> BlockInfo<N> {
2556 let spec_id = *evm_env.spec_id();
2557 let is_shanghai = spec_id >= SpecId::SHANGHAI;
2558 let is_cancun = spec_id >= SpecId::CANCUN;
2559 let is_prague = spec_id >= SpecId::PRAGUE;
2560
2561 let receipts_root = calculate_receipt_root(&block_result.receipts);
2562 let cumulative_blob_gas_used = is_cancun.then_some(block_result.blob_gas_used);
2563 let bloom = block_result.receipts.iter().fold(Bloom::default(), |mut b, r| {
2564 b.accrue_bloom(r.logs_bloom());
2565 b
2566 });
2567
2568 let header = Header {
2569 parent_hash,
2570 ommers_hash: Default::default(),
2571 beneficiary: evm_env.block_env.beneficiary,
2572 state_root,
2573 transactions_root: Default::default(),
2574 receipts_root,
2575 logs_bloom: bloom,
2576 difficulty: evm_env.block_env.difficulty,
2577 number,
2578 gas_limit: evm_env.block_env.gas_limit,
2579 gas_used: block_result.gas_used,
2580 timestamp: evm_env.block_env.timestamp.saturating_to(),
2581 extra_data: Default::default(),
2582 mix_hash: evm_env.block_env.prevrandao.unwrap_or_default(),
2583 nonce: Default::default(),
2584 base_fee_per_gas: (spec_id >= SpecId::LONDON).then_some(evm_env.block_env.basefee),
2585 parent_beacon_block_root: is_cancun.then_some(Default::default()),
2586 blob_gas_used: cumulative_blob_gas_used,
2587 excess_blob_gas: if is_cancun { evm_env.block_env.blob_excess_gas() } else { None },
2588 withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
2589 requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH),
2590 block_access_list_hash: None,
2591 slot_number: None,
2592 };
2593
2594 let block = create_block(header, transactions);
2595 BlockInfo { block, transactions: transaction_infos, receipts: block_result.receipts }
2596 }
2597
2598 async fn do_mine_block(
2599 &self,
2600 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
2601 ) -> MinedBlockOutcome<FoundryTxEnvelope> {
2602 let _mining_guard = self.mining.lock().await;
2603 trace!(target: "backend", "creating new block with {} transactions", pool_transactions.len());
2604
2605 let (outcome, header, block_hash) = {
2606 let current_base_fee = self.base_fee();
2607 let current_excess_blob_gas_and_price = self.excess_blob_gas_and_price();
2608
2609 let mut evm_env = self.evm_env.read().clone();
2610
2611 if evm_env.block_env.basefee == 0 {
2612 evm_env.cfg_env.disable_base_fee = true;
2615 }
2616
2617 let block_number = self.blockchain.storage.read().best_number.saturating_add(1);
2618
2619 if is_arbitrum(evm_env.cfg_env.chain_id) {
2621 evm_env.block_env.number = U256::from(block_number);
2623 } else {
2624 evm_env.block_env.number = evm_env.block_env.number.saturating_add(U256::from(1));
2625 }
2626
2627 evm_env.block_env.basefee = current_base_fee;
2628 evm_env.block_env.blob_excess_gas_and_price = current_excess_blob_gas_and_price;
2629
2630 let best_hash = self.blockchain.storage.read().best_hash;
2631
2632 let mut input = Vec::with_capacity(40);
2633 input.extend_from_slice(best_hash.as_slice());
2634 input.extend_from_slice(&block_number.to_le_bytes());
2635 evm_env.block_env.prevrandao = Some(keccak256(&input));
2636
2637 if self.prune_state_history_config.is_state_history_supported() {
2638 let db = self.db.read().await.current_state();
2639 self.states.write().insert(best_hash, db);
2641 }
2642
2643 let (block_info, included, invalid, not_yet_valid, block_hash) = {
2644 let mut db = self.db.write().await;
2645
2646 evm_env.block_env.timestamp = U256::from(self.time.next_timestamp());
2650
2651 let spec_id = *evm_env.spec_id();
2652
2653 let inspector_tx_config = self.inspector_tx_config();
2654 let gas_config = self.pool_tx_gas_config(&evm_env);
2655
2656 let (pool_result, block_result) = self.execute_with_block_executor(
2657 &mut **db,
2658 &evm_env,
2659 best_hash,
2660 spec_id,
2661 &pool_transactions,
2662 &gas_config,
2663 &inspector_tx_config,
2664 &|pending, account| {
2665 self.validate_pool_transaction_for(pending, account, &evm_env)
2666 },
2667 );
2668
2669 let included = pool_result.included;
2670 let invalid = pool_result.invalid;
2671 let not_yet_valid = pool_result.not_yet_valid;
2672
2673 let state_root = db.maybe_state_root().unwrap_or_default();
2674 let block_info = Self::build_block_info(
2675 &evm_env,
2676 best_hash,
2677 block_number,
2678 state_root,
2679 block_result,
2680 pool_result.txs,
2681 pool_result.tx_info,
2682 );
2683
2684 let block_hash = block_info.block.header.hash_slow();
2686 db.insert_block_hash(U256::from(block_info.block.header.number()), block_hash);
2687
2688 (block_info, included, invalid, not_yet_valid, block_hash)
2689 };
2690
2691 let BlockInfo { block, transactions, receipts } = block_info;
2693
2694 let header = block.header.clone();
2695
2696 trace!(
2697 target: "backend",
2698 "Mined block {} with {} tx {:?}",
2699 block_number,
2700 transactions.len(),
2701 transactions.iter().map(|tx| tx.transaction_hash).collect::<Vec<_>>()
2702 );
2703 let mut storage = self.blockchain.storage.write();
2704 storage.best_number = block_number;
2706 storage.best_hash = block_hash;
2707 if !self.is_eip3675() {
2710 storage.total_difficulty =
2711 storage.total_difficulty.saturating_add(header.difficulty);
2712 }
2713
2714 storage.blocks.insert(block_hash, block);
2715 storage.hashes.insert(block_number, block_hash);
2716
2717 node_info!("");
2718 for (info, receipt) in transactions.into_iter().zip(receipts) {
2720 node_info!(" Transaction: {:?}", info.transaction_hash);
2722 if let Some(contract) = &info.contract_address {
2723 node_info!(" Contract created: {contract}");
2724 }
2725 node_info!(" Gas used: {}", receipt.cumulative_gas_used());
2726 if !info.exit.is_ok() {
2727 let r = RevertDecoder::new().decode(
2728 info.out.as_ref().map(|b| &b[..]).unwrap_or_default(),
2729 Some(info.exit),
2730 );
2731 node_info!(" Error: reverted with: {r}");
2732 }
2733 node_info!("");
2734
2735 let mined_tx = MinedTransaction { info, receipt, block_hash, block_number };
2736 storage.transactions.insert(mined_tx.info.transaction_hash, mined_tx);
2737 }
2738
2739 if let Some(transaction_block_keeper) = self.transaction_block_keeper
2741 && storage.blocks.len() > transaction_block_keeper
2742 {
2743 let to_clear = block_number
2744 .saturating_sub(transaction_block_keeper.try_into().unwrap_or(u64::MAX));
2745 storage.remove_block_transactions_by_number(to_clear)
2746 }
2747
2748 evm_env.block_env.difficulty = U256::from(0);
2750
2751 *self.evm_env.write() = evm_env;
2753
2754 let timestamp = utc_from_secs(header.timestamp);
2755
2756 node_info!(" Block Number: {}", block_number);
2757 node_info!(" Block Hash: {:?}", block_hash);
2758 if timestamp.year() > 9999 {
2759 node_info!(" Block Time: {:?}\n", timestamp.to_rfc3339());
2761 } else {
2762 node_info!(" Block Time: {:?}\n", timestamp.to_rfc2822());
2763 }
2764
2765 let outcome = MinedBlockOutcome { block_number, included, invalid, not_yet_valid };
2766
2767 (outcome, header, block_hash)
2768 };
2769 let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
2770 header.gas_used,
2771 header.gas_limit,
2772 header.base_fee_per_gas.unwrap_or_default(),
2773 );
2774 let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas(
2775 header.excess_blob_gas.unwrap_or_default(),
2776 header.blob_gas_used.unwrap_or_default(),
2777 );
2778
2779 self.fees.set_base_fee(next_block_base_fee);
2781
2782 self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
2783 next_block_excess_blob_gas,
2784 get_blob_base_fee_update_fraction_by_spec_id(*self.evm_env.read().spec_id()),
2785 ));
2786
2787 self.notify_on_new_block(header, block_hash);
2789
2790 outcome
2791 }
2792
2793 pub async fn reorg(
2800 &self,
2801 depth: u64,
2802 tx_pairs: HashMap<u64, Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>>,
2803 common_block: Block,
2804 ) -> Result<(), BlockchainError> {
2805 self.rollback(common_block).await?;
2806 for i in 0..depth {
2808 let to_be_mined = tx_pairs.get(&i).cloned().unwrap_or_else(Vec::new);
2809 let outcome = self.do_mine_block(to_be_mined).await;
2810 node_info!(
2811 " Mined reorg block number {}. With {} valid txs and with invalid {} txs",
2812 outcome.block_number,
2813 outcome.included.len(),
2814 outcome.invalid.len()
2815 );
2816 }
2817
2818 Ok(())
2819 }
2820
2821 pub async fn pending_block(
2825 &self,
2826 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
2827 ) -> BlockInfo<N> {
2828 self.with_pending_block(pool_transactions, |_, block| block).await
2829 }
2830
2831 pub async fn with_pending_block<F, T>(
2835 &self,
2836 pool_transactions: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>,
2837 f: F,
2838 ) -> T
2839 where
2840 F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockInfo<N>) -> T,
2841 {
2842 let db = self.db.read().await;
2843 let evm_env = self.next_evm_env();
2844
2845 let mut cache_db = AnvilCacheDB::new(&*db);
2846
2847 let parent_hash = self.blockchain.storage.read().best_hash;
2848
2849 let spec_id = *evm_env.spec_id();
2850
2851 let inspector_tx_config = self.inspector_tx_config();
2852 let gas_config = self.pool_tx_gas_config(&evm_env);
2853
2854 let (pool_result, block_result) = self.execute_with_block_executor(
2855 &mut cache_db,
2856 &evm_env,
2857 parent_hash,
2858 spec_id,
2859 &pool_transactions,
2860 &gas_config,
2861 &inspector_tx_config,
2862 &|pending, account| self.validate_pool_transaction_for(pending, account, &evm_env),
2863 );
2864
2865 let cache_db = cache_db.0;
2867
2868 let state_root = cache_db.maybe_state_root().unwrap_or_default();
2869 let block_number = evm_env.block_env.number.saturating_to();
2870 let block_info = Self::build_block_info(
2871 &evm_env,
2872 parent_hash,
2873 block_number,
2874 state_root,
2875 block_result,
2876 pool_result.txs,
2877 pool_result.tx_info,
2878 );
2879
2880 f(Box::new(cache_db), block_info)
2881 }
2882
2883 pub async fn get_fee_token_balance(
2888 &self,
2889 token: Address,
2890 account: Address,
2891 ) -> Result<U256, BlockchainError> {
2892 let mut calldata = vec![0x70, 0xa0, 0x82, 0x31];
2894 calldata.extend_from_slice(&[0u8; 12]);
2896 calldata.extend_from_slice(account.as_slice());
2897
2898 let request = WithOtherFields::new(TransactionRequest {
2899 from: Some(Address::ZERO),
2900 to: Some(TxKind::Call(token)),
2901 input: calldata.into(),
2902 ..Default::default()
2903 });
2904
2905 let fee_details = FeeDetails::zero();
2906 let (exit, out, _, _) = self.call(request, fee_details, None, Default::default()).await?;
2907
2908 if exit != InstructionResult::Return && exit != InstructionResult::Stop {
2910 return Ok(U256::ZERO);
2912 }
2913
2914 match out {
2916 Some(Output::Call(data)) if data.len() >= 32 => Ok(U256::from_be_slice(&data[..32])),
2917 _ => Ok(U256::ZERO),
2918 }
2919 }
2920
2921 pub async fn call(
2927 &self,
2928 request: WithOtherFields<TransactionRequest>,
2929 fee_details: FeeDetails,
2930 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
2931 overrides: EvmOverrides,
2932 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
2933 self.with_database_at(block_request, |state, mut block| {
2934 let block_number = block.number;
2935 let (exit, out, gas, state) = {
2936 let mut cache_db = CacheDB::new(state);
2937 if let Some(state_overrides) = overrides.state {
2938 apply_state_overrides(state_overrides.into_iter().collect(), &mut cache_db)?;
2939 }
2940 if let Some(block_overrides) = overrides.block {
2941 cache_db.apply_block_overrides(*block_overrides, &mut block);
2942 }
2943 self.call_with_state(&cache_db, request, fee_details, block)
2944 }?;
2945 trace!(target: "backend", "call return {:?} out: {:?} gas {} on block {}", exit, out, gas, block_number);
2946 Ok((exit, out, gas, state))
2947 }).await?
2948 }
2949
2950 pub async fn call_with_tracing(
2951 &self,
2952 request: WithOtherFields<TransactionRequest>,
2953 fee_details: FeeDetails,
2954 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
2955 opts: GethDebugTracingCallOptions,
2956 ) -> Result<GethTrace, BlockchainError> {
2957 let GethDebugTracingCallOptions {
2958 tracing_options, block_overrides, state_overrides, ..
2959 } = opts;
2960 let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options;
2961
2962 self.with_database_at(block_request, |state, mut block| {
2963 let block_number = block.number;
2964
2965 let mut cache_db = CacheDB::new(state);
2966 if let Some(state_overrides) = state_overrides {
2967 apply_state_overrides(state_overrides, &mut cache_db)?;
2968 }
2969 if let Some(block_overrides) = block_overrides {
2970 cache_db.apply_block_overrides(block_overrides, &mut block);
2971 }
2972
2973 if let Some(tracer) = tracer {
2974 return match tracer {
2975 GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
2976 GethDebugBuiltInTracerType::CallTracer => {
2977 let call_config = tracer_config
2978 .into_call_config()
2979 .map_err(|e| RpcError::invalid_params(e.to_string()))?;
2980
2981 let mut inspector = self.build_inspector().with_tracing_config(
2982 TracingInspectorConfig::from_geth_call_config(&call_config),
2983 );
2984
2985 let (evm_env, tx_env, op_deposit) =
2986 self.build_call_env(request, fee_details, block);
2987 let ResultAndState { result, state: _ } = self
2988 .transact_with_inspector_ref(
2989 &cache_db,
2990 &evm_env,
2991 &mut inspector,
2992 tx_env,
2993 op_deposit,
2994 )?;
2995
2996 inspector.print_logs();
2997 if self.print_traces {
2998 inspector.print_traces(self.call_trace_decoder.clone());
2999 }
3000
3001 let tracing_inspector = inspector.tracer.expect("tracer disappeared");
3002
3003 Ok(tracing_inspector
3004 .into_geth_builder()
3005 .geth_call_traces(call_config, result.tx_gas_used())
3006 .into())
3007 }
3008 GethDebugBuiltInTracerType::PreStateTracer => {
3009 let pre_state_config = tracer_config
3010 .into_pre_state_config()
3011 .map_err(|e| RpcError::invalid_params(e.to_string()))?;
3012
3013 let mut inspector = TracingInspector::new(
3014 TracingInspectorConfig::from_geth_prestate_config(
3015 &pre_state_config,
3016 ),
3017 );
3018
3019 let (evm_env, tx_env, op_deposit) =
3020 self.build_call_env(request, fee_details, block);
3021 let result = self.transact_with_inspector_ref(
3022 &cache_db,
3023 &evm_env,
3024 &mut inspector,
3025 tx_env,
3026 op_deposit,
3027 )?;
3028
3029 Ok(inspector
3030 .into_geth_builder()
3031 .geth_prestate_traces(&result, &pre_state_config, cache_db)?
3032 .into())
3033 }
3034 GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()),
3035 GethDebugBuiltInTracerType::FourByteTracer
3036 | GethDebugBuiltInTracerType::MuxTracer
3037 | GethDebugBuiltInTracerType::FlatCallTracer
3038 | GethDebugBuiltInTracerType::Erc7562Tracer => {
3039 Err(RpcError::invalid_params("unsupported tracer type").into())
3040 }
3041 },
3042 #[cfg(not(feature = "js-tracer"))]
3043 GethDebugTracerType::JsTracer(_) => {
3044 Err(RpcError::invalid_params("unsupported tracer type").into())
3045 }
3046 #[cfg(feature = "js-tracer")]
3047 GethDebugTracerType::JsTracer(code) => {
3048 let config = tracer_config.into_json();
3049 let mut inspector =
3050 revm_inspectors::tracing::js::JsInspector::new(code, config)
3051 .map_err(|err| BlockchainError::Message(err.to_string()))?;
3052
3053 let (evm_env, tx_env, op_deposit) =
3054 self.build_call_env(request, fee_details, block.clone());
3055 let result = self.transact_with_inspector_ref(
3056 &cache_db,
3057 &evm_env,
3058 &mut inspector,
3059 tx_env.clone(),
3060 op_deposit,
3061 )?;
3062 let res = inspector
3063 .json_result(result, &tx_env, &block, &cache_db)
3064 .map_err(|err| BlockchainError::Message(err.to_string()))?;
3065
3066 Ok(GethTrace::JS(res))
3067 }
3068 };
3069 }
3070
3071 let mut inspector = self
3073 .build_inspector()
3074 .with_tracing_config(TracingInspectorConfig::from_geth_config(&config));
3075
3076 let (evm_env, tx_env, op_deposit) = self.build_call_env(request, fee_details, block);
3077 let ResultAndState { result, state: _ } = self.transact_with_inspector_ref(
3078 &cache_db,
3079 &evm_env,
3080 &mut inspector,
3081 tx_env,
3082 op_deposit,
3083 )?;
3084
3085 let (exit_reason, gas_used, out, _logs) = unpack_execution_result(result);
3086
3087 let tracing_inspector = inspector.tracer.expect("tracer disappeared");
3088 let return_value = out.as_ref().map(|o| o.data()).cloned().unwrap_or_default();
3089
3090 trace!(target: "backend", ?exit_reason, ?out, %gas_used, %block_number, "trace call");
3091
3092 let res = tracing_inspector
3093 .into_geth_builder()
3094 .geth_traces(gas_used, return_value, config)
3095 .into();
3096
3097 Ok(res)
3098 })
3099 .await?
3100 }
3101
3102 pub async fn with_database_at<F, T>(
3104 &self,
3105 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3106 f: F,
3107 ) -> Result<T, BlockchainError>
3108 where
3109 F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockEnv) -> T,
3110 {
3111 let block_number = match block_request {
3112 Some(BlockRequest::Pending(pool_transactions)) => {
3113 let result = self
3114 .with_pending_block(pool_transactions, |state, block| {
3115 let block = block.block;
3116 f(state, block_env_from_header(&block.header))
3117 })
3118 .await;
3119 return Ok(result);
3120 }
3121 Some(BlockRequest::Number(bn)) => Some(BlockNumber::Number(bn)),
3122 None => None,
3123 };
3124 let block_number = self.convert_block_number(block_number);
3125 let current_number = self.best_number();
3126
3127 if block_number > current_number {
3129 return Err(BlockchainError::BlockOutOfRange(current_number, block_number));
3130 }
3131
3132 if block_number < current_number {
3133 if let Some((block_hash, block)) = self
3134 .block_by_number(BlockNumber::Number(block_number))
3135 .await?
3136 .map(|block| (block.header.hash, block))
3137 {
3138 let read_guard = self.states.upgradable_read();
3139 if let Some(state_db) = read_guard.get_state(&block_hash) {
3140 return Ok(f(Box::new(state_db), block_env_from_header(&block.header)));
3141 }
3142
3143 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
3144 if let Some(state) = write_guard.get_on_disk_state(&block_hash) {
3145 return Ok(f(Box::new(state), block_env_from_header(&block.header)));
3146 }
3147 }
3148
3149 warn!(target: "backend", "Not historic state found for block={}", block_number);
3150 return Err(BlockchainError::BlockOutOfRange(current_number, block_number));
3151 }
3152
3153 let db = self.db.read().await;
3154 let block = self.evm_env.read().block_env.clone();
3155 Ok(f(Box::new(&**db), block))
3156 }
3157
3158 pub async fn storage_at(
3159 &self,
3160 address: Address,
3161 index: U256,
3162 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3163 ) -> Result<B256, BlockchainError> {
3164 self.with_database_at(block_request, |db, _| {
3165 trace!(target: "backend", "get storage for {:?} at {:?}", address, index);
3166 let val = db.storage_ref(address, index)?;
3167 Ok(val.into())
3168 })
3169 .await?
3170 }
3171
3172 pub async fn storage_values(
3174 &self,
3175 requests: HashMap<Address, Vec<B256>>,
3176 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3177 ) -> Result<HashMap<Address, Vec<B256>>, BlockchainError> {
3178 self.with_database_at(block_request, |db, _| {
3179 trace!(target: "backend", "get storage values for {} addresses", requests.len());
3180 let mut result: HashMap<Address, Vec<B256>> = HashMap::default();
3181 for (address, slots) in &requests {
3182 let mut values = Vec::with_capacity(slots.len());
3183 for slot in slots {
3184 let val = db.storage_ref(*address, (*slot).into())?;
3185 values.push(val.into());
3186 }
3187 result.insert(*address, values);
3188 }
3189 Ok(result)
3190 })
3191 .await?
3192 }
3193
3194 pub async fn get_code(
3199 &self,
3200 address: Address,
3201 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3202 ) -> Result<Bytes, BlockchainError> {
3203 self.with_database_at(block_request, |db, _| self.get_code_with_state(&db, address)).await?
3204 }
3205
3206 pub async fn get_balance(
3210 &self,
3211 address: Address,
3212 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3213 ) -> Result<U256, BlockchainError> {
3214 self.with_database_at(block_request, |db, _| self.get_balance_with_state(db, address))
3215 .await?
3216 }
3217
3218 pub async fn get_account_at_block(
3219 &self,
3220 address: Address,
3221 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3222 ) -> Result<TrieAccount, BlockchainError> {
3223 self.with_database_at(block_request, |block_db, _| {
3224 let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
3225 let account = db.get(&address).cloned().unwrap_or_default();
3226 let storage_root = storage_root(&account.storage);
3227 let code_hash = account.info.code_hash;
3228 let balance = account.info.balance;
3229 let nonce = account.info.nonce;
3230 Ok(TrieAccount { balance, nonce, code_hash, storage_root })
3231 })
3232 .await?
3233 }
3234
3235 pub async fn get_nonce(
3239 &self,
3240 address: Address,
3241 block_request: BlockRequest<FoundryTxEnvelope>,
3242 ) -> Result<u64, BlockchainError> {
3243 if let BlockRequest::Pending(pool_transactions) = &block_request
3244 && let Some(value) = get_pool_transactions_nonce(pool_transactions, address)
3245 {
3246 return Ok(value);
3247 }
3248 let final_block_request = match block_request {
3249 BlockRequest::Pending(_) => BlockRequest::Number(self.best_number()),
3250 BlockRequest::Number(bn) => BlockRequest::Number(bn),
3251 };
3252
3253 self.with_database_at(Some(final_block_request), |db, _| {
3254 trace!(target: "backend", "get nonce for {:?}", address);
3255 Ok(db.basic_ref(address)?.unwrap_or_default().nonce)
3256 })
3257 .await?
3258 }
3259
3260 fn replay_tx_with_inspector<I, F, T>(
3261 &self,
3262 hash: B256,
3263 mut inspector: I,
3264 f: F,
3265 ) -> Result<T, BlockchainError>
3266 where
3267 for<'a> I: BackendInspector<WrapDatabaseRef<&'a CacheDB<Box<&'a StateDb>>>> + 'a,
3268 for<'a> F:
3269 FnOnce(ResultAndState<HaltReason>, CacheDB<Box<&'a StateDb>>, I, TxEnv, EvmEnv) -> T,
3270 {
3271 let block = {
3272 let storage = self.blockchain.storage.read();
3273 let MinedTransaction { block_hash, .. } = storage
3274 .transactions
3275 .get(&hash)
3276 .cloned()
3277 .ok_or(BlockchainError::TransactionNotFound)?;
3278
3279 storage.blocks.get(&block_hash).cloned().ok_or(BlockchainError::BlockNotFound)?
3280 };
3281
3282 let index = block
3283 .body
3284 .transactions
3285 .iter()
3286 .position(|tx| tx.hash() == hash)
3287 .expect("transaction not found in block");
3288
3289 let pool_txs: Vec<Arc<PoolTransaction<FoundryTxEnvelope>>> = block.body.transactions
3290 [..index]
3291 .iter()
3292 .map(|tx| {
3293 let pending_tx =
3294 PendingTransaction::from_maybe_impersonated(tx.clone()).expect("is valid");
3295 Arc::new(PoolTransaction {
3296 pending_transaction: pending_tx,
3297 requires: vec![],
3298 provides: vec![],
3299 priority: crate::eth::pool::transactions::TransactionPriority(0),
3300 })
3301 })
3302 .collect();
3303
3304 let trace = |parent_state: &StateDb| -> Result<T, BlockchainError> {
3305 let mut cache_db = AnvilCacheDB::new(Box::new(parent_state));
3306
3307 let mut evm_env = self.evm_env.read().clone();
3309
3310 evm_env.block_env = block_env_from_header(&block.header);
3311
3312 let spec_id = *evm_env.spec_id();
3313
3314 let inspector_tx_config = self.inspector_tx_config();
3315 let gas_config = self.pool_tx_gas_config(&evm_env);
3316
3317 self.execute_with_block_executor(
3318 &mut cache_db,
3319 &evm_env,
3320 block.header.parent_hash,
3321 spec_id,
3322 &pool_txs,
3323 &gas_config,
3324 &inspector_tx_config,
3325 &|pending, account| self.validate_pool_transaction_for(pending, account, &evm_env),
3326 );
3327
3328 let cache_db = cache_db.0;
3330
3331 let target_tx = block.body.transactions[index].clone();
3332 let target_tx = PendingTransaction::from_maybe_impersonated(target_tx)?;
3333 let (result, base_tx_env) = self.transact_envelope_with_inspector_ref(
3334 &cache_db,
3335 &evm_env,
3336 &mut inspector,
3337 target_tx.transaction.as_ref(),
3338 *target_tx.sender(),
3339 )?;
3340
3341 Ok(f(result, cache_db, inspector, base_tx_env, evm_env))
3342 };
3343
3344 let read_guard = self.states.upgradable_read();
3345 if let Some(state) = read_guard.get_state(&block.header.parent_hash) {
3346 trace(state)
3347 } else {
3348 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
3349 let state = write_guard
3350 .get_on_disk_state(&block.header.parent_hash)
3351 .ok_or(BlockchainError::BlockNotFound)?;
3352 trace(state)
3353 }
3354 }
3355
3356 #[cfg(feature = "js-tracer")]
3358 pub async fn trace_tx_with_js_tracer(
3359 &self,
3360 hash: B256,
3361 code: String,
3362 opts: GethDebugTracingOptions,
3363 ) -> Result<GethTrace, BlockchainError> {
3364 let GethDebugTracingOptions { tracer_config, .. } = opts;
3365 let config = tracer_config.into_json();
3366 let inspector = revm_inspectors::tracing::js::JsInspector::new(code, config)
3367 .map_err(|err| BlockchainError::Message(err.to_string()))?;
3368 let trace = self.replay_tx_with_inspector(
3369 hash,
3370 inspector,
3371 |result, cache_db, mut inspector, tx_env, evm_env| {
3372 inspector
3373 .json_result(
3374 result,
3375 &alloy_evm::IntoTxEnv::into_tx_env(tx_env),
3376 &evm_env.block_env,
3377 &cache_db,
3378 )
3379 .map_err(|e| BlockchainError::Message(e.to_string()))
3380 },
3381 )??;
3382 Ok(GethTrace::JS(trace))
3383 }
3384
3385 pub async fn prove_account_at(
3389 &self,
3390 address: Address,
3391 keys: Vec<B256>,
3392 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
3393 ) -> Result<AccountProof, BlockchainError> {
3394 let block_number = block_request.as_ref().map(|r| r.block_number());
3395
3396 self.with_database_at(block_request, |block_db, _| {
3397 trace!(target: "backend", "get proof for {:?} at {:?}", address, block_number);
3398 let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
3399 let account = db.get(&address).cloned().unwrap_or_default();
3400
3401 let mut builder = HashBuilder::default()
3402 .with_proof_retainer(ProofRetainer::new(vec![Nibbles::unpack(keccak256(address))]));
3403
3404 for (key, account) in trie_accounts(db) {
3405 builder.add_leaf(key, &account);
3406 }
3407
3408 let _ = builder.root();
3409
3410 let proof = builder
3411 .take_proof_nodes()
3412 .into_nodes_sorted()
3413 .into_iter()
3414 .map(|(_, v)| v)
3415 .collect();
3416 let (storage_hash, storage_proofs) = prove_storage(&account.storage, &keys);
3417
3418 let account_proof = AccountProof {
3419 address,
3420 balance: account.info.balance,
3421 nonce: account.info.nonce,
3422 code_hash: account.info.code_hash,
3423 storage_hash,
3424 account_proof: proof,
3425 storage_proof: keys
3426 .into_iter()
3427 .zip(storage_proofs)
3428 .map(|(key, proof)| {
3429 let storage_key: U256 = key.into();
3430 let value = account.storage.get(&storage_key).copied().unwrap_or_default();
3431 StorageProof { key: JsonStorageKey::Hash(key), value, proof }
3432 })
3433 .collect(),
3434 };
3435
3436 Ok(account_proof)
3437 })
3438 .await?
3439 }
3440}
3441
3442impl<N: Network> Backend<N>
3443where
3444 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
3445{
3446 pub async fn rollback(&self, common_block: Block) -> Result<(), BlockchainError> {
3451 let hash = common_block.header.hash_slow();
3452
3453 let common_state = {
3455 let return_state_or_throw_err =
3456 |db: Option<&StateDb>| -> Result<AddressMap<DbAccount>, BlockchainError> {
3457 let state_db = db.ok_or(BlockchainError::DataUnavailable)?;
3458 let db_full =
3459 state_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
3460 Ok(db_full.clone())
3461 };
3462
3463 let read_guard = self.states.upgradable_read();
3464 if let Some(db) = read_guard.get_state(&hash) {
3465 return_state_or_throw_err(Some(db))?
3466 } else {
3467 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
3468 return_state_or_throw_err(write_guard.get_on_disk_state(&hash))?
3469 }
3470 };
3471
3472 {
3473 let removed_blocks =
3475 self.blockchain.storage.write().unwind_to(common_block.header.number(), hash);
3476
3477 let removed_hashes: Vec<_> =
3479 removed_blocks.iter().map(|b| b.header.hash_slow()).collect();
3480 self.states.write().remove_block_states(&removed_hashes);
3481
3482 let mut env = self.evm_env.write();
3484 env.block_env.number = U256::from(common_block.header.number());
3485 env.block_env.timestamp = U256::from(common_block.header.timestamp());
3486 env.block_env.gas_limit = common_block.header.gas_limit();
3487 env.block_env.difficulty = common_block.header.difficulty();
3488 env.block_env.prevrandao = common_block.header.mix_hash();
3489
3490 self.time.reset(env.block_env.timestamp.saturating_to());
3491 }
3492
3493 {
3494 let block_hashes: Vec<_> = {
3498 let storage = self.blockchain.storage.read();
3499 let min_block = common_block.header.number().saturating_sub(256);
3500 storage
3501 .hashes
3502 .iter()
3503 .filter(|(num, _)| **num >= min_block)
3504 .map(|(&num, &hash)| (num, hash))
3505 .collect()
3506 };
3507
3508 let mut db = self.db.write().await;
3510 db.clear();
3511
3512 for (address, acc) in common_state {
3514 db.insert_account(address, acc.info);
3515 for (key, value) in acc.storage {
3516 db.set_storage_at(address, key.into(), value.into())?;
3517 }
3518 }
3519
3520 for (block_num, hash) in block_hashes {
3523 db.insert_block_hash(U256::from(block_num), hash);
3524 }
3525 }
3526
3527 Ok(())
3528 }
3529
3530 pub async fn debug_trace_transaction(
3532 &self,
3533 hash: B256,
3534 opts: GethDebugTracingOptions,
3535 ) -> Result<GethTrace, BlockchainError> {
3536 #[cfg(feature = "js-tracer")]
3537 if let Some(tracer_type) = opts.tracer.as_ref()
3538 && tracer_type.is_js()
3539 {
3540 return self
3541 .trace_tx_with_js_tracer(hash, tracer_type.as_str().to_string(), opts.clone())
3542 .await;
3543 }
3544
3545 if let Some(trace) = self.mined_geth_trace_transaction(hash, opts.clone()).await {
3546 return trace;
3547 }
3548
3549 if let Some(fork) = self.get_fork() {
3550 return Ok(fork.debug_trace_transaction(hash, opts).await?);
3551 }
3552
3553 Ok(GethTrace::Default(Default::default()))
3554 }
3555
3556 pub async fn debug_trace_block_by_hash(
3558 &self,
3559 block_hash: B256,
3560 opts: GethDebugTracingOptions,
3561 ) -> Result<Vec<TraceResult>, BlockchainError> {
3562 if let Some(block) = self.blockchain.get_block_by_hash(&block_hash) {
3563 let mut traces = Vec::new();
3564 for tx in &block.body.transactions {
3565 let tx_hash = tx.hash();
3566 match self.debug_trace_transaction(tx_hash, opts.clone()).await {
3567 Ok(trace) => {
3568 traces.push(TraceResult::Success { result: trace, tx_hash: Some(tx_hash) });
3569 }
3570 Err(error) => {
3571 traces.push(TraceResult::Error {
3572 error: error.to_string(),
3573 tx_hash: Some(tx_hash),
3574 });
3575 }
3576 }
3577 }
3578 return Ok(traces);
3579 }
3580
3581 if let Some(fork) = self.get_fork() {
3582 return Ok(fork.debug_trace_block_by_hash(block_hash, opts).await?);
3583 }
3584
3585 Err(BlockchainError::BlockNotFound)
3586 }
3587
3588 pub async fn debug_trace_block_by_number(
3590 &self,
3591 block_number: BlockNumber,
3592 opts: GethDebugTracingOptions,
3593 ) -> Result<Vec<TraceResult>, BlockchainError> {
3594 let number = self.convert_block_number(Some(block_number));
3595
3596 if let Some(block) = self.get_block(BlockId::Number(BlockNumber::Number(number))) {
3597 let mut traces = Vec::new();
3598 for tx in &block.body.transactions {
3599 let tx_hash = tx.hash();
3600 match self.debug_trace_transaction(tx_hash, opts.clone()).await {
3601 Ok(trace) => {
3602 traces.push(TraceResult::Success { result: trace, tx_hash: Some(tx_hash) });
3603 }
3604 Err(error) => {
3605 traces.push(TraceResult::Error {
3606 error: error.to_string(),
3607 tx_hash: Some(tx_hash),
3608 });
3609 }
3610 }
3611 }
3612 return Ok(traces);
3613 }
3614
3615 if let Some(fork) = self.get_fork() {
3616 return Ok(fork.debug_trace_block_by_number(number, opts).await?);
3617 }
3618
3619 Err(BlockchainError::BlockNotFound)
3620 }
3621
3622 fn geth_trace(
3623 &self,
3624 tx: &MinedTransaction<N>,
3625 opts: GethDebugTracingOptions,
3626 ) -> Result<GethTrace, BlockchainError> {
3627 let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts;
3628
3629 if let Some(tracer) = tracer {
3630 match tracer {
3631 GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
3632 GethDebugBuiltInTracerType::FourByteTracer => {
3633 let inspector = FourByteInspector::default();
3634 let res = self.replay_tx_with_inspector(
3635 tx.info.transaction_hash,
3636 inspector,
3637 |_, _, inspector, _, _| FourByteFrame::from(inspector).into(),
3638 )?;
3639 return Ok(res);
3640 }
3641 GethDebugBuiltInTracerType::CallTracer => {
3642 return match tracer_config.into_call_config() {
3643 Ok(call_config) => {
3644 let inspector = TracingInspector::new(
3645 TracingInspectorConfig::from_geth_call_config(&call_config),
3646 );
3647 let frame = self.replay_tx_with_inspector(
3648 tx.info.transaction_hash,
3649 inspector,
3650 |_, _, inspector, _, _| {
3651 inspector
3652 .geth_builder()
3653 .geth_call_traces(
3654 call_config,
3655 tx.receipt.cumulative_gas_used(),
3656 )
3657 .into()
3658 },
3659 )?;
3660 Ok(frame)
3661 }
3662 Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
3663 };
3664 }
3665 GethDebugBuiltInTracerType::PreStateTracer => {
3666 return match tracer_config.into_pre_state_config() {
3667 Ok(pre_state_config) => {
3668 let inspector = TracingInspector::new(
3669 TracingInspectorConfig::from_geth_prestate_config(
3670 &pre_state_config,
3671 ),
3672 );
3673 let frame = self.replay_tx_with_inspector(
3674 tx.info.transaction_hash,
3675 inspector,
3676 |state, db, inspector, _, _| {
3677 inspector.geth_builder().geth_prestate_traces(
3678 &state,
3679 &pre_state_config,
3680 db,
3681 )
3682 },
3683 )??;
3684 Ok(frame.into())
3685 }
3686 Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
3687 };
3688 }
3689 GethDebugBuiltInTracerType::NoopTracer
3690 | GethDebugBuiltInTracerType::MuxTracer
3691 | GethDebugBuiltInTracerType::Erc7562Tracer
3692 | GethDebugBuiltInTracerType::FlatCallTracer => {}
3693 },
3694 GethDebugTracerType::JsTracer(_code) => {}
3695 }
3696
3697 return Ok(NoopFrame::default().into());
3698 }
3699
3700 Ok(GethTraceBuilder::new(tx.info.traces.clone())
3702 .geth_traces(
3703 tx.receipt.cumulative_gas_used(),
3704 tx.info.out.clone().unwrap_or_default(),
3705 config,
3706 )
3707 .into())
3708 }
3709
3710 async fn mined_geth_trace_transaction(
3711 &self,
3712 hash: B256,
3713 opts: GethDebugTracingOptions,
3714 ) -> Option<Result<GethTrace, BlockchainError>> {
3715 self.blockchain.storage.read().transactions.get(&hash).map(|tx| self.geth_trace(tx, opts))
3716 }
3717
3718 fn get_receipts(
3720 &self,
3721 tx_hashes: impl IntoIterator<Item = TxHash>,
3722 ) -> Vec<FoundryReceiptEnvelope> {
3723 let storage = self.blockchain.storage.read();
3724 let mut receipts = vec![];
3725
3726 for hash in tx_hashes {
3727 if let Some(tx) = storage.transactions.get(&hash) {
3728 receipts.push(tx.receipt.clone());
3729 }
3730 }
3731
3732 receipts
3733 }
3734
3735 pub async fn transaction_receipt(
3736 &self,
3737 hash: B256,
3738 ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
3739 if let Some(receipt) = self.mined_transaction_receipt(hash) {
3740 return Ok(Some(receipt.inner));
3741 }
3742
3743 if let Some(fork) = self.get_fork() {
3744 let receipt = fork.transaction_receipt(hash).await?;
3745 let number = self.convert_block_number(
3746 receipt.clone().and_then(|r| r.block_number()).map(BlockNumber::from),
3747 );
3748
3749 if fork.predates_fork_inclusive(number) {
3750 return Ok(receipt);
3751 }
3752 }
3753
3754 Ok(None)
3755 }
3756
3757 pub fn mined_block_receipts(&self, id: impl Into<BlockId>) -> Option<Vec<FoundryTxReceipt>> {
3759 let mut receipts = Vec::new();
3760 let block = self.get_block(id)?;
3761
3762 for transaction in block.body.transactions {
3763 let receipt = self.mined_transaction_receipt(transaction.hash())?;
3764 receipts.push(receipt.inner);
3765 }
3766
3767 Some(receipts)
3768 }
3769
3770 pub(crate) fn mined_transaction_receipt(
3772 &self,
3773 hash: B256,
3774 ) -> Option<MinedTransactionReceipt<FoundryNetwork>> {
3775 let MinedTransaction { info, receipt: tx_receipt, block_hash, .. } =
3776 self.blockchain.get_transaction_by_hash(&hash)?;
3777
3778 let index = info.transaction_index as usize;
3779 let block = self.blockchain.get_block_by_hash(&block_hash)?;
3780 let transaction = block.body.transactions[index].clone();
3781
3782 let excess_blob_gas = block.header.excess_blob_gas();
3784 let blob_gas_price =
3785 alloy_eips::eip4844::calc_blob_gasprice(excess_blob_gas.unwrap_or_default());
3786 let blob_gas_used = transaction.blob_gas_used();
3787
3788 let effective_gas_price = transaction.effective_gas_price(block.header.base_fee_per_gas());
3789
3790 let receipts = self.get_receipts(block.body.transactions.iter().map(|tx| tx.hash()));
3791 let next_log_index = receipts[..index].iter().map(|r| r.logs().len()).sum::<usize>();
3792
3793 let tx_receipt = tx_receipt.convert_logs_rpc(
3794 BlockNumHash::new(block.header.number(), block_hash),
3795 block.header.timestamp(),
3796 info.transaction_hash,
3797 info.transaction_index,
3798 next_log_index,
3799 );
3800
3801 let receipt = TransactionReceipt {
3802 inner: tx_receipt,
3803 transaction_hash: info.transaction_hash,
3804 transaction_index: Some(info.transaction_index),
3805 block_number: Some(block.header.number()),
3806 gas_used: info.gas_used,
3807 contract_address: info.contract_address,
3808 effective_gas_price,
3809 block_hash: Some(block_hash),
3810 from: info.from,
3811 to: info.to,
3812 blob_gas_price: Some(blob_gas_price),
3813 blob_gas_used,
3814 };
3815
3816 let mut inner = FoundryTxReceipt::with_timestamp(receipt, block.header.timestamp());
3818 if self.is_tempo() {
3819 inner = inner.with_fee_payer(info.from);
3820 }
3821 Some(MinedTransactionReceipt { inner, out: info.out })
3822 }
3823
3824 pub async fn block_receipts(
3826 &self,
3827 number: BlockId,
3828 ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
3829 if let Some(receipts) = self.mined_block_receipts(number) {
3830 return Ok(Some(receipts));
3831 }
3832
3833 if let Some(fork) = self.get_fork() {
3834 let number = match self.ensure_block_number(Some(number)).await {
3835 Err(_) => return Ok(None),
3836 Ok(n) => n,
3837 };
3838
3839 if fork.predates_fork_inclusive(number) {
3840 let receipts = fork.block_receipts(number).await?;
3841
3842 return Ok(receipts);
3843 }
3844 }
3845
3846 Ok(None)
3847 }
3848}
3849
3850impl<N: Network<ReceiptEnvelope = FoundryReceiptEnvelope>> Backend<N> {
3851 pub async fn serialized_state(
3853 &self,
3854 preserve_historical_states: bool,
3855 ) -> Result<SerializableState, BlockchainError> {
3856 let at = self.evm_env.read().block_env.clone();
3857 let best_number = self.blockchain.storage.read().best_number;
3858 let blocks = self.blockchain.storage.read().serialized_blocks();
3859 let transactions = self.blockchain.storage.read().serialized_transactions();
3860 let historical_states =
3861 preserve_historical_states.then(|| self.states.write().serialized_states());
3862
3863 let state = self.db.read().await.dump_state(
3864 at,
3865 best_number,
3866 blocks,
3867 transactions,
3868 historical_states,
3869 )?;
3870 state.ok_or_else(|| {
3871 RpcError::invalid_params("Dumping state not supported with the current configuration")
3872 .into()
3873 })
3874 }
3875
3876 pub async fn dump_state(
3878 &self,
3879 preserve_historical_states: bool,
3880 ) -> Result<Bytes, BlockchainError> {
3881 let state = self.serialized_state(preserve_historical_states).await?;
3882 let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
3883 encoder
3884 .write_all(&serde_json::to_vec(&state).unwrap_or_default())
3885 .map_err(|_| BlockchainError::DataUnavailable)?;
3886 Ok(encoder.finish().unwrap_or_default().into())
3887 }
3888
3889 pub async fn load_state(&self, state: SerializableState) -> Result<bool, BlockchainError> {
3891 {
3894 let mut storage = self.blockchain.storage.write();
3895 storage.load_blocks(state.blocks.clone());
3896 storage.load_transactions(state.transactions.clone());
3897 }
3898 if let Some(block) = state.block.clone() {
3900 self.evm_env.write().block_env = block.clone();
3901
3902 let fork_num_and_hash = self.get_fork().map(|f| (f.block_number(), f.block_hash()));
3905
3906 let best_number = state.best_block_number.unwrap_or(block.number.saturating_to());
3907 if let Some((number, hash)) = fork_num_and_hash {
3908 trace!(target: "backend", state_block_number=?best_number, fork_block_number=?number);
3909 if best_number > number {
3913 self.blockchain.storage.write().best_number = best_number;
3914 let best_hash = self
3915 .blockchain
3916 .storage
3917 .read()
3918 .hash(best_number.into(), self.slots_in_an_epoch)
3919 .ok_or_else(|| {
3920 BlockchainError::RpcError(RpcError::internal_error_with(format!(
3921 "Best hash not found for best number {best_number}",
3922 )))
3923 })?;
3924 self.blockchain.storage.write().best_hash = best_hash;
3925 } else {
3926 self.blockchain.storage.write().best_number = number;
3929 self.blockchain.storage.write().best_hash = hash;
3930 }
3931 } else {
3932 self.blockchain.storage.write().best_number = best_number;
3933
3934 let best_hash = self
3936 .blockchain
3937 .storage
3938 .read()
3939 .hash(best_number.into(), self.slots_in_an_epoch)
3940 .ok_or_else(|| {
3941 BlockchainError::RpcError(RpcError::internal_error_with(format!(
3942 "Best hash not found for best number {best_number}",
3943 )))
3944 })?;
3945
3946 self.blockchain.storage.write().best_hash = best_hash;
3947 }
3948 }
3949
3950 if let Some(latest) = state.blocks.iter().max_by_key(|b| b.header.number()) {
3951 let header = &latest.header;
3952 let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
3953 header.gas_used(),
3954 header.gas_limit(),
3955 header.base_fee_per_gas().unwrap_or_default(),
3956 );
3957 let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas(
3958 header.excess_blob_gas().unwrap_or_default(),
3959 header.blob_gas_used().unwrap_or_default(),
3960 );
3961
3962 self.fees.set_base_fee(next_block_base_fee);
3964
3965 self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
3966 next_block_excess_blob_gas,
3967 get_blob_base_fee_update_fraction(
3968 self.evm_env.read().cfg_env.chain_id,
3969 header.timestamp,
3970 ),
3971 ));
3972 }
3973
3974 if !self.db.write().await.load_state(state.clone())? {
3975 return Err(RpcError::invalid_params(
3976 "Loading state not supported with the current configuration",
3977 )
3978 .into());
3979 }
3980
3981 if let Some(historical_states) = state.historical_states {
3982 self.states.write().load_states(historical_states);
3983 }
3984
3985 Ok(true)
3986 }
3987
3988 pub async fn load_state_bytes(&self, buf: Bytes) -> Result<bool, BlockchainError> {
3990 let orig_buf = &buf.0[..];
3991 let mut decoder = GzDecoder::new(orig_buf);
3992 let mut decoded_data = Vec::new();
3993
3994 let state: SerializableState = serde_json::from_slice(if decoder.header().is_some() {
3995 decoder
3996 .read_to_end(decoded_data.as_mut())
3997 .map_err(|_| BlockchainError::FailedToDecodeStateDump)?;
3998 &decoded_data
3999 } else {
4000 &buf.0
4001 })
4002 .map_err(|_| BlockchainError::FailedToDecodeStateDump)?;
4003
4004 self.load_state(state).await
4005 }
4006}
4007
4008impl Backend<FoundryNetwork> {
4009 pub async fn simulate(
4011 &self,
4012 request: SimulatePayload,
4013 block_request: Option<BlockRequest<FoundryTxEnvelope>>,
4014 ) -> Result<Vec<SimulatedBlock<AnyRpcBlock>>, BlockchainError> {
4015 self.with_database_at(block_request, |state, mut block_env| {
4016 let SimulatePayload {
4017 block_state_calls,
4018 trace_transfers,
4019 validation,
4020 return_full_transactions,
4021 } = request;
4022 let mut cache_db = CacheDB::new(state);
4023 let mut block_res = Vec::with_capacity(block_state_calls.len());
4024
4025 for block in block_state_calls {
4027 let SimBlock { block_overrides, state_overrides, calls } = block;
4028 let mut call_res = Vec::with_capacity(calls.len());
4029 let mut log_index = 0;
4030 let mut gas_used = 0;
4031 let mut transactions = Vec::with_capacity(calls.len());
4032 let mut logs= Vec::new();
4033
4034 if let Some(state_overrides) = state_overrides {
4036 apply_state_overrides(state_overrides, &mut cache_db)?;
4037 }
4038 if let Some(block_overrides) = block_overrides {
4039 cache_db.apply_block_overrides(block_overrides, &mut block_env);
4040 }
4041
4042 for (req_idx, request) in calls.into_iter().enumerate() {
4044 let fee_details = FeeDetails::new(
4045 request.gas_price,
4046 request.max_fee_per_gas,
4047 request.max_priority_fee_per_gas,
4048 request.max_fee_per_blob_gas,
4049 )?
4050 .or_zero_fees();
4051
4052 let (mut evm_env, tx_env, op_deposit) = self.build_call_env(
4053 WithOtherFields::new(request.clone()),
4054 fee_details,
4055 block_env.clone(),
4056 );
4057
4058 evm_env.cfg_env.disable_eip3607 = true;
4060
4061 if !validation {
4062 evm_env.cfg_env.disable_base_fee = !validation;
4063 evm_env.block_env.basefee = 0;
4064 }
4065
4066 let mut inspector = self.build_inspector();
4067
4068 if trace_transfers {
4070 inspector = inspector.with_transfers();
4071 }
4072 trace!(target: "backend", env=?evm_env, spec=?evm_env.spec_id(),"simulate evm env");
4073 let ResultAndState { result, state } = self.transact_with_inspector_ref(
4074 &cache_db,
4075 &evm_env,
4076 &mut inspector,
4077 tx_env,
4078 op_deposit,
4079 )?;
4080 trace!(target: "backend", ?result, ?request, "simulate call");
4081
4082 inspector.print_logs();
4083 if self.print_traces {
4084 inspector.into_print_traces(self.call_trace_decoder.clone());
4085 }
4086
4087 cache_db.commit(state);
4089 gas_used += result.tx_gas_used();
4090
4091 let from = request.from.unwrap_or_default();
4093
4094 let mut request = Into::<FoundryTransactionRequest>::into(WithOtherFields::new(request));
4095 request.prep_for_submission();
4096
4097 let typed_tx = request.build_unsigned().map_err(|e| BlockchainError::InvalidTransactionRequest(e.to_string()))?;
4098
4099 let tx = build_impersonated(typed_tx);
4100 let tx_hash = tx.hash();
4101 let rpc_tx = transaction_build(
4102 None,
4103 MaybeImpersonatedTransaction::impersonated(tx, from),
4104 None,
4105 None,
4106 Some(block_env.basefee),
4107 );
4108 transactions.push(rpc_tx);
4109
4110 let return_data = result.output().cloned().unwrap_or_default();
4111 let sim_res = SimCallResult {
4112 return_data,
4113 gas_used: result.tx_gas_used(),
4114 max_used_gas: None,
4115 status: result.is_success(),
4116 error: result.is_success().not().then(|| {
4117 alloy_rpc_types::simulate::SimulateError {
4118 code: -3200,
4119 message: "execution failed".to_string(),
4120 data: None,
4121 }
4122 }),
4123 logs: result.clone()
4124 .into_logs()
4125 .into_iter()
4126 .enumerate()
4127 .map(|(idx, log)| Log {
4128 inner: log,
4129 block_number: Some(block_env.number.saturating_to()),
4130 block_timestamp: Some(block_env.timestamp.saturating_to()),
4131 transaction_index: Some(req_idx as u64),
4132 log_index: Some((idx + log_index) as u64),
4133 removed: false,
4134
4135 block_hash: None,
4136 transaction_hash: Some(tx_hash),
4137 })
4138 .collect(),
4139 };
4140 logs.extend(sim_res.logs.iter().map(|log| log.inner.clone()));
4141 log_index += sim_res.logs.len();
4142 call_res.push(sim_res);
4143 }
4144
4145 let transactions_envelopes: Vec<AnyTxEnvelope> = transactions
4146 .iter()
4147 .map(|tx| AnyTxEnvelope::from(tx.clone()))
4148 .collect();
4149 let header = Header {
4150 logs_bloom: logs_bloom(logs.iter()),
4151 transactions_root: calculate_transaction_root(&transactions_envelopes),
4152 receipts_root: calculate_receipt_root(&transactions_envelopes),
4153 parent_hash: Default::default(),
4154 beneficiary: block_env.beneficiary,
4155 state_root: Default::default(),
4156 difficulty: Default::default(),
4157 number: block_env.number.saturating_to(),
4158 gas_limit: block_env.gas_limit,
4159 gas_used,
4160 timestamp: block_env.timestamp.saturating_to(),
4161 extra_data: Default::default(),
4162 mix_hash: Default::default(),
4163 nonce: Default::default(),
4164 base_fee_per_gas: Some(block_env.basefee),
4165 withdrawals_root: None,
4166 blob_gas_used: None,
4167 excess_blob_gas: None,
4168 parent_beacon_block_root: None,
4169 requests_hash: None,
4170 ..Default::default()
4171 };
4172 let mut block = alloy_rpc_types::Block {
4173 header: AnyRpcHeader {
4174 hash: header.hash_slow(),
4175 inner: header.into(),
4176 total_difficulty: None,
4177 size: None,
4178 },
4179 uncles: vec![],
4180 transactions: BlockTransactions::Full(transactions),
4181 withdrawals: None,
4182 };
4183
4184 if !return_full_transactions {
4185 block.transactions.convert_to_hashes();
4186 }
4187
4188 for res in &mut call_res {
4189 res.logs.iter_mut().for_each(|log| {
4190 log.block_hash = Some(block.header.hash);
4191 });
4192 }
4193
4194 let simulated_block = SimulatedBlock {
4195 inner: AnyRpcBlock::new(WithOtherFields::new(block)),
4196 calls: call_res,
4197 };
4198
4199 block_env.number += U256::from(1);
4201 block_env.timestamp += U256::from(12);
4202 block_env.basefee = simulated_block
4203 .inner
4204 .header
4205 .next_block_base_fee(self.fees.base_fee_params())
4206 .unwrap_or_default();
4207
4208 block_res.push(simulated_block);
4209 }
4210
4211 Ok(block_res)
4212 })
4213 .await?
4214 }
4215
4216 pub fn get_blob_by_tx_hash(&self, hash: B256) -> Result<Option<Vec<alloy_consensus::Blob>>> {
4217 if let Some(tx) = self.mined_transaction_by_hash(hash)
4219 && let Ok(typed_tx) = FoundryTxEnvelope::try_from(tx)
4220 && let Some(sidecar) = typed_tx.sidecar()
4221 {
4222 return Ok(Some(sidecar.sidecar.blobs().to_vec()));
4223 }
4224
4225 Ok(None)
4226 }
4227
4228 pub async fn set_fee_token(&self, user: Address, token: Address) -> DatabaseResult<()> {
4230 let hardfork = self.hardfork();
4231 let chain_id = self.evm_env.read().cfg_env.chain_id;
4232 let timestamp = U256::from(self.evm_env.read().block_env.timestamp);
4233 let block_number: u64 = self.evm_env.read().block_env.number.to();
4234 let mut db = self.db.write().await;
4235 let mut storage = AnvilStorageProvider::new(
4236 &mut **db,
4237 chain_id,
4238 timestamp,
4239 block_number,
4240 hardfork.into(),
4241 );
4242 StorageCtx::enter(&mut storage, || {
4243 let mut fee_manager = TipFeeManager::new();
4244 fee_manager
4245 .set_user_token(user, IFeeManager::setUserTokenCall { token })
4246 .map_err(|e| DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}"))))
4247 })
4248 }
4249
4250 pub async fn set_validator_fee_token(
4252 &self,
4253 validator: Address,
4254 token: Address,
4255 ) -> DatabaseResult<()> {
4256 let hardfork = self.hardfork();
4257 let chain_id = self.evm_env.read().cfg_env.chain_id;
4258 let timestamp = U256::from(self.evm_env.read().block_env.timestamp);
4259 let block_number: u64 = self.evm_env.read().block_env.number.to();
4260 let mut db = self.db.write().await;
4261 let mut storage = AnvilStorageProvider::new(
4262 &mut **db,
4263 chain_id,
4264 timestamp,
4265 block_number,
4266 hardfork.into(),
4267 );
4268 StorageCtx::enter(&mut storage, || {
4269 let mut fee_manager = TipFeeManager::new();
4270 fee_manager
4272 .set_validator_token(
4273 validator,
4274 IFeeManager::setValidatorTokenCall { token },
4275 Address::ZERO,
4276 )
4277 .map_err(|e| DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}"))))
4278 })
4279 }
4280
4281 pub async fn set_fee_amm_liquidity(
4283 &self,
4284 user_token: Address,
4285 validator_token: Address,
4286 amount: U256,
4287 ) -> DatabaseResult<()> {
4288 let hardfork = self.hardfork();
4289 let chain_id = self.evm_env.read().cfg_env.chain_id;
4290 let timestamp = U256::from(self.evm_env.read().block_env.timestamp);
4291 let block_number: u64 = self.evm_env.read().block_env.number.to();
4292 let admin = Address::ZERO;
4293 let mut db = self.db.write().await;
4294 let mut storage = AnvilStorageProvider::new(
4295 &mut **db,
4296 chain_id,
4297 timestamp,
4298 block_number,
4299 hardfork.into(),
4300 );
4301 StorageCtx::enter(&mut storage, || {
4302 for &token_address in &[user_token, validator_token] {
4305 let mut token = TIP20Token::from_address(token_address)
4306 .map_err(|e| DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}"))))?;
4307 token
4308 .grant_role_internal(admin, *ISSUER_ROLE)
4309 .map_err(|e| DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}"))))?;
4310 token
4311 .mint(admin, ITIP20::mintCall { to: admin, amount })
4312 .map_err(|e| DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}"))))?;
4313 }
4314 let mut fee_manager = TipFeeManager::new();
4315 fee_manager
4316 .mint(admin, user_token, validator_token, amount, admin)
4317 .map_err(|e| DatabaseError::AnyRequest(Arc::new(eyre::eyre!("{e}"))))?;
4318 Ok(())
4319 })
4320 }
4321}
4322
4323fn get_pool_transactions_nonce(
4325 pool_transactions: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
4326 address: Address,
4327) -> Option<u64> {
4328 if let Some(highest_nonce) = pool_transactions
4329 .iter()
4330 .filter(|tx| {
4331 *tx.pending_transaction.sender() == address
4332 && !tx.pending_transaction.transaction.as_ref().has_nonzero_tempo_nonce_key()
4333 })
4334 .map(|tx| tx.pending_transaction.nonce())
4335 .max()
4336 {
4337 let tx_count = highest_nonce.saturating_add(1);
4338 return Some(tx_count);
4339 }
4340 None
4341}
4342
4343#[async_trait::async_trait]
4344impl<N: Network> TransactionValidator<FoundryTxEnvelope> for Backend<N>
4345where
4346 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
4347{
4348 async fn validate_pool_transaction(
4349 &self,
4350 tx: &PendingTransaction<FoundryTxEnvelope>,
4351 ) -> Result<(), BlockchainError> {
4352 let address = *tx.sender();
4353 let account = self.get_account(address).await?;
4354 let evm_env = self.next_evm_env();
4355
4356 if let FoundryTxEnvelope::Tempo(aa_tx) = tx.transaction.as_ref() {
4358 let tempo_tx = aa_tx.tx();
4359 let current_time = evm_env.block_env.timestamp.saturating_to::<u64>();
4360
4361 const AA_VALID_BEFORE_MIN_SECS: u64 = 3;
4363 if let Some(valid_before) = tempo_tx.valid_before.map(|v| v.get()) {
4364 let min_allowed = current_time.saturating_add(AA_VALID_BEFORE_MIN_SECS);
4365 if valid_before <= min_allowed {
4366 return Err(InvalidTransactionError::TempoValidBeforeExpired {
4367 valid_before,
4368 min_allowed,
4369 }
4370 .into());
4371 }
4372 }
4373
4374 const AA_VALID_AFTER_MAX_SECS: u64 = 3600;
4376 if let Some(valid_after) = tempo_tx.valid_after.map(|v| v.get()) {
4377 let max_allowed = current_time.saturating_add(AA_VALID_AFTER_MAX_SECS);
4378 if valid_after > max_allowed {
4379 return Err(InvalidTransactionError::TempoValidAfterTooFar {
4380 valid_after,
4381 max_allowed,
4382 }
4383 .into());
4384 }
4385 }
4386
4387 let fee_payer = tempo_tx.recover_fee_payer(address).unwrap_or(address);
4389 let fee_token =
4390 tempo_tx.fee_token.unwrap_or(foundry_evm::core::tempo::PATH_USD_ADDRESS);
4391
4392 let required_wei =
4394 U256::from(tempo_tx.gas_limit).saturating_mul(U256::from(tempo_tx.max_fee_per_gas));
4395 let required = required_wei / U256::from(10u64.pow(12));
4396
4397 let balance = self.get_fee_token_balance(fee_token, fee_payer).await?;
4398 if balance < required {
4399 return Err(InvalidTransactionError::TempoInsufficientFeeTokenBalance {
4400 balance,
4401 required,
4402 }
4403 .into());
4404 }
4405 }
4406
4407 Ok(self.validate_pool_transaction_for(tx, &account, &evm_env)?)
4408 }
4409
4410 fn validate_pool_transaction_for(
4411 &self,
4412 pending: &PendingTransaction<FoundryTxEnvelope>,
4413 account: &AccountInfo,
4414 evm_env: &EvmEnv,
4415 ) -> Result<(), InvalidTransactionError> {
4416 let tx = &pending.transaction;
4417
4418 if let Some(tx_chain_id) = tx.chain_id() {
4419 let chain_id = self.chain_id();
4420 if chain_id.to::<u64>() != tx_chain_id {
4421 if let FoundryTxEnvelope::Legacy(tx) = tx.as_ref() {
4422 if evm_env.cfg_env.spec >= SpecId::SPURIOUS_DRAGON && tx.chain_id().is_none() {
4424 debug!(target: "backend", ?chain_id, ?tx_chain_id, "incompatible EIP155-based V");
4425 return Err(InvalidTransactionError::IncompatibleEIP155);
4426 }
4427 } else {
4428 debug!(target: "backend", ?chain_id, ?tx_chain_id, "invalid chain id");
4429 return Err(InvalidTransactionError::InvalidChainId);
4430 }
4431 }
4432 }
4433
4434 if self.is_tempo() && !tx.value().is_zero() {
4436 warn!(target: "backend", "[{:?}] native value transfer not allowed in Tempo mode", tx.hash());
4437 return Err(InvalidTransactionError::TempoNativeValueTransfer);
4438 }
4439
4440 if self.is_tempo_hardfork_active(TempoHardfork::T5)
4442 && let FoundryTxEnvelope::Tempo(aa_tx) = tx.as_ref()
4443 {
4444 const MAX_TEMPO_AUTHORIZATIONS: usize = 16;
4445 let auth_count = aa_tx.tx().tempo_authorization_list.len();
4446 if auth_count > MAX_TEMPO_AUTHORIZATIONS {
4447 warn!(target: "backend", "[{:?}] Tempo tx has too many authorizations: {}", tx.hash(), auth_count);
4448 return Err(InvalidTransactionError::TempoTooManyAuthorizations {
4449 count: auth_count,
4450 max: MAX_TEMPO_AUTHORIZATIONS,
4451 });
4452 }
4453 }
4454
4455 #[cfg(feature = "optimism")]
4457 let is_deposit_tx = pending.transaction.as_ref().is_deposit();
4458 #[cfg(not(feature = "optimism"))]
4459 let is_deposit_tx = false;
4460 let is_tempo_tx = pending.transaction.as_ref().is_tempo();
4461 let nonce = tx.nonce();
4462 if nonce < account.nonce && !is_deposit_tx && !is_tempo_tx {
4463 debug!(target: "backend", "[{:?}] nonce too low", tx.hash());
4464 return Err(InvalidTransactionError::NonceTooLow);
4465 }
4466
4467 if evm_env.cfg_env.spec >= SpecId::CANCUN && tx.is_eip4844() {
4469 let blob_tx = match tx.as_ref() {
4471 FoundryTxEnvelope::Eip4844(tx) => tx.tx(),
4472 _ => unreachable!(),
4473 };
4474
4475 let blob_count = blob_tx.tx().blob_versioned_hashes.len();
4476
4477 if blob_count == 0 {
4479 return Err(InvalidTransactionError::NoBlobHashes);
4480 }
4481
4482 let max_blobs_per_tx = self.blob_params().max_blobs_per_tx as usize;
4484 if blob_count > max_blobs_per_tx {
4485 return Err(InvalidTransactionError::TooManyBlobs(blob_count, max_blobs_per_tx));
4486 }
4487
4488 if !self.skip_blob_validation(Some(*pending.sender()))
4490 && let Err(err) = blob_tx.validate(EnvKzgSettings::default().get())
4491 {
4492 return Err(InvalidTransactionError::BlobTransactionValidationError(err));
4493 }
4494 }
4495
4496 if evm_env.cfg_env.spec >= SpecId::SHANGHAI && tx.kind() == TxKind::Create {
4498 let max_initcode_size = evm_env
4499 .cfg_env
4500 .limit_contract_code_size
4501 .map(|limit| limit.saturating_mul(2))
4502 .unwrap_or(revm::primitives::eip3860::MAX_INITCODE_SIZE);
4503 if tx.input().len() > max_initcode_size {
4504 return Err(InvalidTransactionError::MaxInitCodeSizeExceeded);
4505 }
4506 }
4507
4508 if !self.disable_pool_balance_checks {
4510 if tx.gas_limit() < MIN_TRANSACTION_GAS as u64 {
4512 debug!(target: "backend", "[{:?}] gas too low", tx.hash());
4513 return Err(InvalidTransactionError::GasTooLow);
4514 }
4515
4516 if !evm_env.cfg_env.disable_block_gas_limit
4518 && tx.gas_limit() > evm_env.block_env.gas_limit
4519 {
4520 debug!(target: "backend", "[{:?}] gas too high", tx.hash());
4521 return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
4522 detail: String::from("tx.gas_limit > env.block.gas_limit"),
4523 }));
4524 }
4525
4526 if evm_env.cfg_env.tx_gas_limit_cap.is_none()
4528 && tx.gas_limit() > evm_env.cfg_env().tx_gas_limit_cap()
4529 {
4530 debug!(target: "backend", "[{:?}] gas too high", tx.hash());
4531 return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
4532 detail: String::from("tx.gas_limit > env.cfg.tx_gas_limit_cap"),
4533 }));
4534 }
4535
4536 if evm_env.cfg_env.spec >= SpecId::LONDON {
4538 if tx.max_fee_per_gas() < evm_env.block_env.basefee.into() && !is_deposit_tx {
4539 debug!(target: "backend", "max fee per gas={}, too low, block basefee={}", tx.max_fee_per_gas(), evm_env.block_env.basefee);
4540 return Err(InvalidTransactionError::FeeCapTooLow);
4541 }
4542
4543 if let (Some(max_priority_fee_per_gas), max_fee_per_gas) =
4544 (tx.as_ref().max_priority_fee_per_gas(), tx.as_ref().max_fee_per_gas())
4545 && max_priority_fee_per_gas > max_fee_per_gas
4546 {
4547 debug!(target: "backend", "max priority fee per gas={}, too high, max fee per gas={}", max_priority_fee_per_gas, max_fee_per_gas);
4548 return Err(InvalidTransactionError::TipAboveFeeCap);
4549 }
4550 }
4551
4552 if evm_env.cfg_env.spec >= SpecId::CANCUN
4554 && tx.is_eip4844()
4555 && let Some(max_fee_per_blob_gas) = tx.max_fee_per_blob_gas()
4556 && let Some(blob_gas_and_price) = &evm_env.block_env.blob_excess_gas_and_price
4557 && max_fee_per_blob_gas < blob_gas_and_price.blob_gasprice
4558 {
4559 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);
4560 return Err(InvalidTransactionError::BlobFeeCapTooLow(
4561 max_fee_per_blob_gas,
4562 blob_gas_and_price.blob_gasprice,
4563 ));
4564 }
4565
4566 let max_cost =
4567 (tx.gas_limit() as u128).saturating_mul(tx.max_fee_per_gas()).saturating_add(
4568 tx.blob_gas_used()
4569 .map(|g| g as u128)
4570 .unwrap_or(0)
4571 .mul(tx.max_fee_per_blob_gas().unwrap_or(0)),
4572 );
4573 let value = tx.value();
4574 match tx.as_ref() {
4575 #[cfg(feature = "optimism")]
4576 FoundryTxEnvelope::Deposit(deposit_tx) => {
4577 if value > account.balance + U256::from(deposit_tx.mint) {
4583 debug!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance + U256::from(deposit_tx.mint), value, *pending.sender());
4584 return Err(InvalidTransactionError::InsufficientFunds);
4585 }
4586 }
4587 FoundryTxEnvelope::Tempo(_) => {
4588 }
4591 _ => {
4592 let req_funds =
4594 max_cost.checked_add(value.saturating_to()).ok_or_else(|| {
4595 debug!(target: "backend", "[{:?}] cost too high", tx.hash());
4596 InvalidTransactionError::InsufficientFunds
4597 })?;
4598 if account.balance < U256::from(req_funds) {
4599 debug!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender());
4600 return Err(InvalidTransactionError::InsufficientFunds);
4601 }
4602 }
4603 }
4604 }
4605 Ok(())
4606 }
4607
4608 fn validate_for(
4609 &self,
4610 tx: &PendingTransaction<FoundryTxEnvelope>,
4611 account: &AccountInfo,
4612 evm_env: &EvmEnv,
4613 ) -> Result<(), InvalidTransactionError> {
4614 self.validate_pool_transaction_for(tx, account, evm_env)?;
4615 if tx.nonce() > account.nonce {
4616 return Err(InvalidTransactionError::NonceTooHigh);
4617 }
4618 Ok(())
4619 }
4620}
4621
4622fn rehash<T>(signed: Signed<T>, hash: B256) -> Signed<T>
4624where
4625 T: alloy_consensus::transaction::RlpEcdsaEncodableTx,
4626{
4627 let (t, sig, _) = signed.into_parts();
4628 Signed::new_unchecked(t, sig, hash)
4629}
4630
4631pub fn transaction_build(
4633 tx_hash: Option<B256>,
4634 eth_transaction: MaybeImpersonatedTransaction<FoundryTxEnvelope>,
4635 block: Option<&Block>,
4636 info: Option<TransactionInfo>,
4637 base_fee: Option<u64>,
4638) -> AnyRpcTransaction {
4639 #[cfg(feature = "optimism")]
4640 if let FoundryTxEnvelope::Deposit(deposit_tx) = eth_transaction.as_ref() {
4641 let dep_tx = deposit_tx;
4642
4643 let ser = serde_json::to_value(dep_tx).expect("could not serialize TxDeposit");
4644 let maybe_deposit_fields = OtherFields::try_from(ser);
4645
4646 match maybe_deposit_fields {
4647 Ok(mut fields) => {
4648 fields.insert("v".to_string(), serde_json::to_value("0x0").unwrap());
4651 fields.insert("r".to_string(), serde_json::to_value(B256::ZERO).unwrap());
4652 fields.insert(String::from("s"), serde_json::to_value(B256::ZERO).unwrap());
4653 fields.insert(String::from("nonce"), serde_json::to_value("0x0").unwrap());
4654
4655 let inner = UnknownTypedTransaction {
4656 ty: AnyTxType(DEPOSIT_TX_TYPE_ID),
4657 fields,
4658 memo: Default::default(),
4659 };
4660
4661 let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
4662 hash: eth_transaction.hash(),
4663 inner,
4664 });
4665
4666 let tx = Transaction {
4667 inner: Recovered::new_unchecked(envelope, deposit_tx.from),
4668 block_hash: block
4669 .as_ref()
4670 .map(|block| B256::from(keccak256(alloy_rlp::encode(&block.header)))),
4671 block_number: block.as_ref().map(|block| block.header.number()),
4672 transaction_index: info.as_ref().map(|info| info.transaction_index),
4673 effective_gas_price: None,
4674 block_timestamp: block.as_ref().map(|block| block.header.timestamp()),
4675 };
4676
4677 return AnyRpcTransaction::from(WithOtherFields::new(tx));
4678 }
4679 Err(_) => {
4680 error!(target: "backend", "failed to serialize deposit transaction");
4681 }
4682 }
4683 }
4684
4685 if let FoundryTxEnvelope::Tempo(tempo_tx) = eth_transaction.as_ref() {
4686 let from = eth_transaction.recover().unwrap_or_default();
4687 let ser = serde_json::to_value(tempo_tx).expect("could not serialize Tempo transaction");
4688 let maybe_tempo_fields = OtherFields::try_from(ser);
4689
4690 match maybe_tempo_fields {
4691 Ok(fields) => {
4692 let inner = UnknownTypedTransaction {
4693 ty: AnyTxType(TEMPO_TX_TYPE_ID),
4694 fields,
4695 memo: Default::default(),
4696 };
4697
4698 let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
4699 hash: eth_transaction.hash(),
4700 inner,
4701 });
4702
4703 let tx = Transaction {
4704 inner: Recovered::new_unchecked(envelope, from),
4705 block_hash: block.as_ref().map(|block| block.header.hash_slow()),
4706 block_number: block.as_ref().map(|block| block.header.number()),
4707 transaction_index: info.as_ref().map(|info| info.transaction_index),
4708 effective_gas_price: None,
4709 block_timestamp: block.as_ref().map(|block| block.header.timestamp()),
4710 };
4711
4712 return AnyRpcTransaction::from(WithOtherFields::new(tx));
4713 }
4714 Err(_) => {
4715 error!(target: "backend", "failed to serialize tempo transaction");
4716 }
4717 }
4718 }
4719
4720 let from = eth_transaction.recover().unwrap_or_default();
4721 let effective_gas_price = eth_transaction.effective_gas_price(base_fee);
4722
4723 let hash = tx_hash.unwrap_or_else(|| eth_transaction.hash());
4729
4730 let eth_envelope = FoundryTxEnvelope::from(eth_transaction)
4731 .try_into_eth()
4732 .expect("non-standard transactions are handled above");
4733
4734 let envelope = match eth_envelope {
4735 TxEnvelope::Legacy(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(rehash(s, hash))),
4736 TxEnvelope::Eip1559(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip1559(rehash(s, hash))),
4737 TxEnvelope::Eip2930(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip2930(rehash(s, hash))),
4738 TxEnvelope::Eip4844(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip4844(rehash(s, hash))),
4739 TxEnvelope::Eip7702(s) => AnyTxEnvelope::Ethereum(TxEnvelope::Eip7702(rehash(s, hash))),
4740 };
4741
4742 let tx = Transaction {
4743 inner: Recovered::new_unchecked(envelope, from),
4744 block_hash: block.as_ref().map(|block| block.header.hash_slow()),
4745 block_number: block.as_ref().map(|block| block.header.number()),
4746 transaction_index: info.as_ref().map(|info| info.transaction_index),
4747 effective_gas_price: Some(effective_gas_price),
4749 block_timestamp: block.as_ref().map(|block| block.header.timestamp()),
4750 };
4751 AnyRpcTransaction::from(WithOtherFields::new(tx))
4752}
4753
4754pub fn prove_storage(
4760 storage: &alloy_primitives::map::U256Map<U256>,
4761 keys: &[B256],
4762) -> (B256, Vec<Vec<Bytes>>) {
4763 let keys: Vec<_> = keys.iter().map(|key| Nibbles::unpack(keccak256(key))).collect();
4764
4765 let mut builder = HashBuilder::default().with_proof_retainer(ProofRetainer::new(keys.clone()));
4766
4767 for (key, value) in trie_storage(storage) {
4768 builder.add_leaf(key, &value);
4769 }
4770
4771 let root = builder.root();
4772
4773 let mut proofs = Vec::new();
4774 let all_proof_nodes = builder.take_proof_nodes();
4775
4776 for proof_key in keys {
4777 let matching_proof_nodes =
4780 all_proof_nodes.matching_nodes_sorted(&proof_key).into_iter().map(|(_, node)| node);
4781 proofs.push(matching_proof_nodes.collect());
4782 }
4783
4784 (root, proofs)
4785}
4786
4787pub fn is_arbitrum(chain_id: u64) -> bool {
4788 if let Ok(chain) = NamedChain::try_from(chain_id) {
4789 return chain.is_arbitrum();
4790 }
4791 false
4792}
4793
4794fn unpack_execution_result<H: IntoInstructionResult>(
4796 result: ExecutionResult<H>,
4797) -> (InstructionResult, u64, Option<Output>, Vec<revm::primitives::Log>) {
4798 match result {
4799 ExecutionResult::Success { reason, gas, output, logs, .. } => {
4800 (reason.into(), gas.tx_gas_used(), Some(output), logs)
4801 }
4802 ExecutionResult::Revert { gas, output, logs, .. } => {
4803 (InstructionResult::Revert, gas.tx_gas_used(), Some(Output::Call(output)), logs)
4804 }
4805 ExecutionResult::Halt { reason, gas, logs, .. } => {
4806 (reason.into_instruction_result(), gas.tx_gas_used(), None, logs)
4807 }
4808 }
4809}
4810
4811pub use foundry_evm::core::evm::IntoInstructionResult;
4816
4817#[cfg(test)]
4818mod tests {
4819 use crate::{NodeConfig, spawn};
4820
4821 #[tokio::test]
4822 async fn test_deterministic_block_mining() {
4823 let genesis_timestamp = 1743944919u64;
4825
4826 let config_a = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
4828 let config_b = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
4829
4830 let (api_a, _handle_a) = spawn(config_a).await;
4831 let (api_b, _handle_b) = spawn(config_b).await;
4832
4833 let outcome_a_1 = api_a.backend.mine_block(vec![]).await;
4835 let outcome_b_1 = api_b.backend.mine_block(vec![]).await;
4836
4837 assert_eq!(outcome_a_1.block_number, outcome_b_1.block_number);
4839
4840 let block_a_1 =
4842 api_a.block_by_number(outcome_a_1.block_number.into()).await.unwrap().unwrap();
4843 let block_b_1 =
4844 api_b.block_by_number(outcome_b_1.block_number.into()).await.unwrap().unwrap();
4845
4846 assert_eq!(
4848 block_a_1.header.hash, block_b_1.header.hash,
4849 "Block hashes should be deterministic. Got {} vs {}",
4850 block_a_1.header.hash, block_b_1.header.hash
4851 );
4852
4853 let outcome_a_2 = api_a.backend.mine_block(vec![]).await;
4855 let outcome_b_2 = api_b.backend.mine_block(vec![]).await;
4856
4857 let block_a_2 =
4858 api_a.block_by_number(outcome_a_2.block_number.into()).await.unwrap().unwrap();
4859 let block_b_2 =
4860 api_b.block_by_number(outcome_b_2.block_number.into()).await.unwrap().unwrap();
4861
4862 assert_eq!(
4863 block_a_2.header.hash, block_b_2.header.hash,
4864 "Second block hashes should also be deterministic. Got {} vs {}",
4865 block_a_2.header.hash, block_b_2.header.hash
4866 );
4867
4868 assert_ne!(
4870 block_a_1.header.hash, block_a_2.header.hash,
4871 "Different blocks should have different hashes"
4872 );
4873 }
4874}