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