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