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