1use self::state::trie_storage;
3use super::executor::new_evm_with_inspector;
4use crate::{
5 ForkChoice, NodeConfig, PrecompileFactory,
6 config::PruneStateHistoryConfig,
7 eth::{
8 backend::{
9 cheats::{CheatEcrecover, CheatsManager},
10 db::{Db, MaybeFullDatabase, SerializableState, StateDb},
11 env::Env,
12 executor::{ExecutedTransactions, TransactionExecutor},
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_typed_transaction,
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, Typed2718,
38 proofs::{calculate_receipt_root, calculate_transaction_root},
39 transaction::Recovered,
40};
41use alloy_eips::{
42 BlockNumHash, Encodable2718, eip4844::kzg_to_versioned_hash, eip7840::BlobParams,
43 eip7910::SystemContract,
44};
45use alloy_evm::{
46 Database, Evm, FromRecoveredTx,
47 eth::EthEvmContext,
48 overrides::{OverrideBlockHashes, apply_state_overrides},
49 precompiles::{DynPrecompile, Precompile, PrecompilesMap},
50};
51use alloy_network::{
52 AnyHeader, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope, AnyTxType,
53 ReceiptResponse, TransactionBuilder, UnknownTxEnvelope, UnknownTypedTransaction,
54};
55use alloy_primitives::{
56 Address, B256, Bytes, TxHash, TxKind, U64, U256, hex, keccak256, logs_bloom,
57 map::{AddressMap, HashMap, HashSet},
58};
59use alloy_rpc_types::{
60 AccessList, Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions,
61 EIP1186AccountProofResponse as AccountProof, EIP1186StorageProof as StorageProof, Filter,
62 Header as AlloyHeader, Index, Log, Transaction, TransactionReceipt,
63 anvil::Forking,
64 request::TransactionRequest,
65 serde_helpers::JsonStorageKey,
66 simulate::{SimBlock, SimCallResult, SimulatePayload, SimulatedBlock},
67 state::EvmOverrides,
68 trace::{
69 filter::TraceFilter,
70 geth::{
71 FourByteFrame, GethDebugBuiltInTracerType, GethDebugTracerType,
72 GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, NoopFrame,
73 },
74 parity::{LocalizedTransactionTrace, TraceResultsWithTransactionHash, TraceType},
75 },
76};
77use alloy_serde::{OtherFields, WithOtherFields};
78use alloy_signer::Signature;
79use alloy_trie::{HashBuilder, Nibbles, proof::ProofRetainer};
80use anvil_core::eth::{
81 block::{Block, BlockInfo},
82 transaction::{MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo},
83};
84use anvil_rpc::error::RpcError;
85use chrono::Datelike;
86use eyre::{Context, Result};
87use flate2::{Compression, read::GzDecoder, write::GzEncoder};
88use foundry_evm::{
89 backend::{DatabaseError, DatabaseResult, RevertStateSnapshotAction},
90 constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE,
91 core::{either_evm::EitherEvm, precompiles::EC_RECOVER},
92 decode::RevertDecoder,
93 inspectors::AccessListInspector,
94 traces::{
95 CallTraceDecoder, FourByteInspector, GethTraceBuilder, TracingInspector,
96 TracingInspectorConfig,
97 },
98 utils::{get_blob_base_fee_update_fraction, get_blob_base_fee_update_fraction_by_spec_id},
99};
100use foundry_primitives::{
101 FoundryReceiptEnvelope, FoundryTransactionRequest, FoundryTxEnvelope, FoundryTxReceipt,
102 get_deposit_tx_parts,
103};
104use futures::channel::mpsc::{UnboundedSender, unbounded};
105use op_alloy_consensus::DEPOSIT_TX_TYPE_ID;
106use op_revm::{OpContext, OpHaltReason, OpTransaction};
107use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
108use revm::{
109 DatabaseCommit, Inspector,
110 context::{Block as RevmBlock, BlockEnv, Cfg, TxEnv},
111 context_interface::{
112 block::BlobExcessGasAndPrice,
113 result::{ExecutionResult, Output, ResultAndState},
114 },
115 database::{CacheDB, DbAccount, WrapDatabaseRef},
116 interpreter::InstructionResult,
117 precompile::{PrecompileSpecId, Precompiles},
118 primitives::{KECCAK_EMPTY, hardfork::SpecId},
119 state::AccountInfo,
120};
121use std::{
122 collections::BTreeMap,
123 fmt::Debug,
124 io::{Read, Write},
125 ops::{Mul, Not},
126 path::PathBuf,
127 sync::Arc,
128 time::Duration,
129};
130use storage::{Blockchain, DEFAULT_HISTORY_LIMIT, MinedTransaction};
131use tokio::sync::RwLock as AsyncRwLock;
132
133pub mod cache;
134pub mod fork_db;
135pub mod in_memory_db;
136pub mod inspector;
137pub mod state;
138pub mod storage;
139
140pub trait DatabaseRef: revm::DatabaseRef<Error = DatabaseError> + Debug {}
144impl<T> DatabaseRef for T where T: revm::DatabaseRef<Error = DatabaseError> + Debug {}
145impl DatabaseRef for dyn crate::eth::backend::db::Db {}
146
147pub const MIN_TRANSACTION_GAS: u128 = 21000;
149pub const MIN_CREATE_GAS: u128 = 53000;
151
152pub type State = foundry_evm::utils::StateChangeset;
153
154#[derive(Debug)]
156pub enum BlockRequest {
157 Pending(Vec<Arc<PoolTransaction>>),
158 Number(u64),
159}
160
161impl BlockRequest {
162 pub fn block_number(&self) -> BlockNumber {
163 match *self {
164 Self::Pending(_) => BlockNumber::Pending,
165 Self::Number(n) => BlockNumber::Number(n),
166 }
167 }
168}
169
170#[derive(Clone, Debug)]
172pub struct Backend {
173 db: Arc<AsyncRwLock<Box<dyn Db>>>,
193 blockchain: Blockchain,
195 states: Arc<RwLock<InMemoryBlockStates>>,
197 env: Arc<RwLock<Env>>,
199 fork: Arc<RwLock<Option<ClientFork>>>,
201 time: TimeManager,
203 cheats: CheatsManager,
205 fees: FeeManager,
207 genesis: GenesisConfig,
209 new_block_listeners: Arc<Mutex<Vec<UnboundedSender<NewBlockNotification>>>>,
211 active_state_snapshots: Arc<Mutex<HashMap<U256, (u64, B256)>>>,
213 enable_steps_tracing: bool,
214 print_logs: bool,
215 print_traces: bool,
216 call_trace_decoder: Arc<CallTraceDecoder>,
218 prune_state_history_config: PruneStateHistoryConfig,
220 transaction_block_keeper: Option<usize>,
222 node_config: Arc<AsyncRwLock<NodeConfig>>,
223 slots_in_an_epoch: u64,
225 precompile_factory: Option<Arc<dyn PrecompileFactory>>,
227 mining: Arc<tokio::sync::Mutex<()>>,
229 disable_pool_balance_checks: bool,
231}
232
233impl Backend {
234 #[expect(clippy::too_many_arguments)]
236 pub async fn with_genesis(
237 db: Arc<AsyncRwLock<Box<dyn Db>>>,
238 env: Arc<RwLock<Env>>,
239 genesis: GenesisConfig,
240 fees: FeeManager,
241 fork: Arc<RwLock<Option<ClientFork>>>,
242 enable_steps_tracing: bool,
243 print_logs: bool,
244 print_traces: bool,
245 call_trace_decoder: Arc<CallTraceDecoder>,
246 prune_state_history_config: PruneStateHistoryConfig,
247 max_persisted_states: Option<usize>,
248 transaction_block_keeper: Option<usize>,
249 automine_block_time: Option<Duration>,
250 cache_path: Option<PathBuf>,
251 node_config: Arc<AsyncRwLock<NodeConfig>>,
252 ) -> Result<Self> {
253 let blockchain = if let Some(fork) = fork.read().as_ref() {
255 trace!(target: "backend", "using forked blockchain at {}", fork.block_number());
256 Blockchain::forked(fork.block_number(), fork.block_hash(), fork.total_difficulty())
257 } else {
258 let env = env.read();
259 Blockchain::new(
260 &env,
261 env.evm_env.cfg_env.spec,
262 fees.is_eip1559().then(|| fees.base_fee()),
263 genesis.timestamp,
264 genesis.number,
265 )
266 };
267
268 if fork.read().is_none() {
271 let mut write_env = env.write();
272 write_env.evm_env.block_env.number = U256::from(genesis.number);
273 }
274
275 let start_timestamp = if let Some(fork) = fork.read().as_ref() {
276 fork.timestamp()
277 } else {
278 genesis.timestamp
279 };
280
281 let mut states = if prune_state_history_config.is_config_enabled() {
282 prune_state_history_config
284 .max_memory_history
285 .map(|limit| InMemoryBlockStates::new(limit, 0))
286 .unwrap_or_default()
287 .memory_only()
288 } else if max_persisted_states.is_some() {
289 max_persisted_states
290 .map(|limit| InMemoryBlockStates::new(DEFAULT_HISTORY_LIMIT, limit))
291 .unwrap_or_default()
292 } else {
293 Default::default()
294 };
295
296 if let Some(cache_path) = cache_path {
297 states = states.disk_path(cache_path);
298 }
299
300 let (slots_in_an_epoch, precompile_factory, disable_pool_balance_checks) = {
301 let cfg = node_config.read().await;
302 (cfg.slots_in_an_epoch, cfg.precompile_factory.clone(), cfg.disable_pool_balance_checks)
303 };
304
305 let backend = Self {
306 db,
307 blockchain,
308 states: Arc::new(RwLock::new(states)),
309 env,
310 fork,
311 time: TimeManager::new(start_timestamp),
312 cheats: Default::default(),
313 new_block_listeners: Default::default(),
314 fees,
315 genesis,
316 active_state_snapshots: Arc::new(Mutex::new(Default::default())),
317 enable_steps_tracing,
318 print_logs,
319 print_traces,
320 call_trace_decoder,
321 prune_state_history_config,
322 transaction_block_keeper,
323 node_config,
324 slots_in_an_epoch,
325 precompile_factory,
326 mining: Arc::new(tokio::sync::Mutex::new(())),
327 disable_pool_balance_checks,
328 };
329
330 if let Some(interval_block_time) = automine_block_time {
331 backend.update_interval_mine_block_time(interval_block_time);
332 }
333
334 backend.apply_genesis().await.wrap_err("failed to create genesis")?;
336 Ok(backend)
337 }
338
339 pub async fn set_create2_deployer(&self, address: Address) -> DatabaseResult<()> {
341 self.set_code(address, Bytes::from_static(DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE)).await?;
342
343 Ok(())
344 }
345
346 pub(crate) fn update_interval_mine_block_time(&self, block_time: Duration) {
348 self.states.write().update_interval_mine_block_time(block_time)
349 }
350
351 async fn apply_genesis(&self) -> Result<(), DatabaseError> {
355 trace!(target: "backend", "setting genesis balances");
356
357 if self.fork.read().is_some() {
358 let mut genesis_accounts_futures = Vec::with_capacity(self.genesis.accounts.len());
360 for address in self.genesis.accounts.iter().copied() {
361 let db = Arc::clone(&self.db);
362
363 genesis_accounts_futures.push(tokio::task::spawn(async move {
366 let db = db.read().await;
367 let info = db.basic_ref(address)?.unwrap_or_default();
368 Ok::<_, DatabaseError>((address, info))
369 }));
370 }
371
372 let genesis_accounts = futures::future::join_all(genesis_accounts_futures).await;
373
374 let mut db = self.db.write().await;
375
376 for res in genesis_accounts {
377 let (address, mut info) = res.unwrap()?;
378 info.balance = self.genesis.balance;
379 db.insert_account(address, info.clone());
380 }
381 } else {
382 let mut db = self.db.write().await;
383 for (account, info) in self.genesis.account_infos() {
384 db.insert_account(account, info);
385 }
386
387 db.insert_block_hash(U256::from(self.best_number()), self.best_hash());
390 }
391
392 let db = self.db.write().await;
393 self.genesis.apply_genesis_json_alloc(db)?;
395
396 trace!(target: "backend", "set genesis balances");
397
398 Ok(())
399 }
400
401 pub fn impersonate(&self, addr: Address) -> bool {
405 if self.cheats.impersonated_accounts().contains(&addr) {
406 return true;
407 }
408 let mut env = self.env.write();
410 env.evm_env.cfg_env.disable_eip3607 = true;
411 self.cheats.impersonate(addr)
412 }
413
414 pub fn stop_impersonating(&self, addr: Address) {
418 self.cheats.stop_impersonating(&addr);
419 }
420
421 pub fn auto_impersonate_account(&self, enabled: bool) {
423 self.cheats.set_auto_impersonate_account(enabled);
424 }
425
426 pub fn get_fork(&self) -> Option<ClientFork> {
428 self.fork.read().clone()
429 }
430
431 pub fn get_db(&self) -> &Arc<AsyncRwLock<Box<dyn Db>>> {
433 &self.db
434 }
435
436 pub async fn get_account(&self, address: Address) -> DatabaseResult<AccountInfo> {
438 Ok(self.db.read().await.basic_ref(address)?.unwrap_or_default())
439 }
440
441 pub fn is_fork(&self) -> bool {
443 self.fork.read().is_some()
444 }
445
446 pub async fn reset_fork(&self, forking: Forking) -> Result<(), BlockchainError> {
448 if !self.is_fork() {
449 if let Some(eth_rpc_url) = forking.clone().json_rpc_url {
450 let mut env = self.env.read().clone();
451
452 let (db, config) = {
453 let mut node_config = self.node_config.write().await;
454
455 node_config.base_fee.take();
458
459 node_config.setup_fork_db_config(eth_rpc_url, &mut env, &self.fees).await?
460 };
461
462 *self.db.write().await = Box::new(db);
463
464 let fork = ClientFork::new(config, Arc::clone(&self.db));
465
466 *self.env.write() = env;
467 *self.fork.write() = Some(fork);
468 } else {
469 return Err(RpcError::invalid_params(
470 "Forking not enabled and RPC URL not provided to start forking",
471 )
472 .into());
473 }
474 }
475
476 if let Some(fork) = self.get_fork() {
477 let block_number =
478 forking.block_number.map(BlockNumber::from).unwrap_or(BlockNumber::Latest);
479 fork.reset(forking.json_rpc_url.clone(), block_number).await?;
481 let fork_block_number = fork.block_number();
482 let fork_block = fork
483 .block_by_number(fork_block_number)
484 .await?
485 .ok_or(BlockchainError::BlockNotFound)?;
486 {
488 if let Some(fork_url) = forking.json_rpc_url {
489 self.reset_block_number(fork_url, fork_block_number).await?;
490 } else {
491 {
494 let maybe_fork_url = { self.node_config.read().await.eth_rpc_url.clone() };
495 if let Some(fork_url) = maybe_fork_url {
496 self.reset_block_number(fork_url, fork_block_number).await?;
497 }
498 }
499
500 let gas_limit = self.node_config.read().await.fork_gas_limit(&fork_block);
501 let mut env = self.env.write();
502
503 env.evm_env.cfg_env.chain_id = fork.chain_id();
504 env.evm_env.block_env = BlockEnv {
505 number: U256::from(fork_block_number),
506 timestamp: U256::from(fork_block.header.timestamp),
507 gas_limit,
508 difficulty: fork_block.header.difficulty,
509 prevrandao: Some(fork_block.header.mix_hash.unwrap_or_default()),
510 beneficiary: env.evm_env.block_env.beneficiary,
512 basefee: env.evm_env.block_env.basefee,
513 ..env.evm_env.block_env.clone()
514 };
515
516 let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
519 fork_block.header.gas_used,
520 gas_limit,
521 fork_block.header.base_fee_per_gas.unwrap_or_default(),
522 );
523
524 self.fees.set_base_fee(next_block_base_fee);
525 }
526
527 self.time.reset(fork_block.header.timestamp);
529
530 self.blockchain.storage.write().total_difficulty = fork.total_difficulty();
532 }
533 *self.blockchain.storage.write() = BlockchainStorage::forked(
535 fork.block_number(),
536 fork.block_hash(),
537 fork.total_difficulty(),
538 );
539 self.states.write().clear();
540 self.db.write().await.clear();
541
542 self.apply_genesis().await?;
543
544 trace!(target: "backend", "reset fork");
545
546 Ok(())
547 } else {
548 Err(RpcError::invalid_params("Forking not enabled").into())
549 }
550 }
551
552 pub async fn reset_to_in_mem(&self) -> Result<(), BlockchainError> {
554 *self.fork.write() = None;
556
557 let env = self.env.read().clone();
559 let genesis_timestamp = self.genesis.timestamp;
560 let genesis_number = self.genesis.number;
561 let spec_id = self.spec_id();
562
563 {
565 let mut env = self.env.write();
566 env.evm_env.block_env.number = U256::from(genesis_number);
567 env.evm_env.block_env.timestamp = U256::from(genesis_timestamp);
568 env.evm_env.block_env.basefee = self.fees.base_fee();
570 env.evm_env.block_env.prevrandao = Some(B256::ZERO);
571 }
572
573 let base_fee = if self.fees.is_eip1559() { Some(self.fees.base_fee()) } else { None };
575 *self.blockchain.storage.write() =
576 BlockchainStorage::new(&env, spec_id, base_fee, genesis_timestamp, genesis_number);
577 self.states.write().clear();
578
579 self.db.write().await.clear();
581
582 self.time.reset(genesis_timestamp);
584
585 if self.fees.is_eip1559() {
587 self.fees.set_base_fee(crate::eth::fees::INITIAL_BASE_FEE);
588 }
589
590 self.fees.set_gas_price(crate::eth::fees::INITIAL_GAS_PRICE);
591
592 self.apply_genesis().await?;
594
595 trace!(target: "backend", "reset to fresh in-memory state");
596
597 Ok(())
598 }
599
600 async fn reset_block_number(
601 &self,
602 fork_url: String,
603 fork_block_number: u64,
604 ) -> Result<(), BlockchainError> {
605 let mut node_config = self.node_config.write().await;
606 node_config.fork_choice = Some(ForkChoice::Block(fork_block_number as i128));
607
608 let mut env = self.env.read().clone();
609 let (forked_db, client_fork_config) =
610 node_config.setup_fork_db_config(fork_url, &mut env, &self.fees).await?;
611
612 *self.db.write().await = Box::new(forked_db);
613 let fork = ClientFork::new(client_fork_config, Arc::clone(&self.db));
614 *self.fork.write() = Some(fork);
615 *self.env.write() = env;
616
617 Ok(())
618 }
619
620 pub fn time(&self) -> &TimeManager {
622 &self.time
623 }
624
625 pub fn cheats(&self) -> &CheatsManager {
627 &self.cheats
628 }
629
630 pub fn skip_blob_validation(&self, impersonator: Option<Address>) -> bool {
632 self.cheats().auto_impersonate_accounts()
633 || impersonator
634 .is_some_and(|addr| self.cheats().impersonated_accounts().contains(&addr))
635 }
636
637 pub fn fees(&self) -> &FeeManager {
639 &self.fees
640 }
641
642 pub fn env(&self) -> &Arc<RwLock<Env>> {
644 &self.env
645 }
646
647 pub fn best_hash(&self) -> B256 {
649 self.blockchain.storage.read().best_hash
650 }
651
652 pub fn best_number(&self) -> u64 {
654 self.blockchain.storage.read().best_number
655 }
656
657 pub fn set_block_number(&self, number: u64) {
659 let mut env = self.env.write();
660 env.evm_env.block_env.number = U256::from(number);
661 }
662
663 pub fn coinbase(&self) -> Address {
665 self.env.read().evm_env.block_env.beneficiary
666 }
667
668 pub fn chain_id(&self) -> U256 {
670 U256::from(self.env.read().evm_env.cfg_env.chain_id)
671 }
672
673 pub fn set_chain_id(&self, chain_id: u64) {
674 self.env.write().evm_env.cfg_env.chain_id = chain_id;
675 }
676
677 pub fn genesis_time(&self) -> u64 {
679 self.genesis.timestamp
680 }
681
682 pub async fn current_balance(&self, address: Address) -> DatabaseResult<U256> {
684 Ok(self.get_account(address).await?.balance)
685 }
686
687 pub async fn current_nonce(&self, address: Address) -> DatabaseResult<u64> {
689 Ok(self.get_account(address).await?.nonce)
690 }
691
692 pub fn set_coinbase(&self, address: Address) {
694 self.env.write().evm_env.block_env.beneficiary = address;
695 }
696
697 pub async fn set_nonce(&self, address: Address, nonce: U256) -> DatabaseResult<()> {
699 self.db.write().await.set_nonce(address, nonce.try_into().unwrap_or(u64::MAX))
700 }
701
702 pub async fn set_balance(&self, address: Address, balance: U256) -> DatabaseResult<()> {
704 self.db.write().await.set_balance(address, balance)
705 }
706
707 pub async fn set_code(&self, address: Address, code: Bytes) -> DatabaseResult<()> {
709 self.db.write().await.set_code(address, code)
710 }
711
712 pub async fn set_storage_at(
714 &self,
715 address: Address,
716 slot: U256,
717 val: B256,
718 ) -> DatabaseResult<()> {
719 self.db.write().await.set_storage_at(address, slot.into(), val)
720 }
721
722 pub fn spec_id(&self) -> SpecId {
724 self.env.read().evm_env.cfg_env.spec
725 }
726
727 pub fn is_eip1559(&self) -> bool {
729 (self.spec_id() as u8) >= (SpecId::LONDON as u8)
730 }
731
732 pub fn is_eip3675(&self) -> bool {
734 (self.spec_id() as u8) >= (SpecId::MERGE as u8)
735 }
736
737 pub fn is_eip2930(&self) -> bool {
739 (self.spec_id() as u8) >= (SpecId::BERLIN as u8)
740 }
741
742 pub fn is_eip4844(&self) -> bool {
744 (self.spec_id() as u8) >= (SpecId::CANCUN as u8)
745 }
746
747 pub fn is_eip7702(&self) -> bool {
749 (self.spec_id() as u8) >= (SpecId::PRAGUE as u8)
750 }
751
752 pub fn is_optimism(&self) -> bool {
754 self.env.read().networks.is_optimism()
755 }
756
757 pub fn precompiles(&self) -> BTreeMap<String, Address> {
759 let spec_id = self.env.read().evm_env.cfg_env.spec;
760 let precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(spec_id));
761
762 let mut precompiles_map = BTreeMap::<String, Address>::default();
763 for (address, precompile) in precompiles.inner() {
764 precompiles_map.insert(precompile.id().name().to_string(), *address);
765 }
766
767 precompiles_map.extend(self.env.read().networks.precompiles());
769
770 if let Some(factory) = &self.precompile_factory {
771 for (address, precompile) in factory.precompiles() {
772 precompiles_map.insert(precompile.precompile_id().to_string(), address);
773 }
774 }
775
776 precompiles_map
777 }
778
779 pub fn system_contracts(&self) -> BTreeMap<SystemContract, Address> {
781 let mut system_contracts = BTreeMap::<SystemContract, Address>::default();
782
783 let spec_id = self.env.read().evm_env.cfg_env.spec;
784
785 if spec_id >= SpecId::CANCUN {
786 system_contracts.extend(SystemContract::cancun());
787 }
788
789 if spec_id >= SpecId::PRAGUE {
790 system_contracts.extend(SystemContract::prague(None));
791 }
792
793 system_contracts
794 }
795
796 pub fn blob_params(&self) -> BlobParams {
798 let spec_id = self.env.read().evm_env.cfg_env.spec;
799
800 if spec_id >= SpecId::OSAKA {
801 return BlobParams::osaka();
802 }
803
804 if spec_id >= SpecId::PRAGUE {
805 return BlobParams::prague();
806 }
807
808 BlobParams::cancun()
809 }
810
811 pub fn ensure_eip1559_active(&self) -> Result<(), BlockchainError> {
813 if self.is_eip1559() {
814 return Ok(());
815 }
816 Err(BlockchainError::EIP1559TransactionUnsupportedAtHardfork)
817 }
818
819 pub fn ensure_eip2930_active(&self) -> Result<(), BlockchainError> {
821 if self.is_eip2930() {
822 return Ok(());
823 }
824 Err(BlockchainError::EIP2930TransactionUnsupportedAtHardfork)
825 }
826
827 pub fn ensure_eip4844_active(&self) -> Result<(), BlockchainError> {
828 if self.is_eip4844() {
829 return Ok(());
830 }
831 Err(BlockchainError::EIP4844TransactionUnsupportedAtHardfork)
832 }
833
834 pub fn ensure_eip7702_active(&self) -> Result<(), BlockchainError> {
835 if self.is_eip7702() {
836 return Ok(());
837 }
838 Err(BlockchainError::EIP7702TransactionUnsupportedAtHardfork)
839 }
840
841 pub fn ensure_op_deposits_active(&self) -> Result<(), BlockchainError> {
843 if self.is_optimism() {
844 return Ok(());
845 }
846 Err(BlockchainError::DepositTransactionUnsupported)
847 }
848
849 pub fn gas_limit(&self) -> u64 {
851 self.env.read().evm_env.block_env.gas_limit
852 }
853
854 pub fn set_gas_limit(&self, gas_limit: u64) {
856 self.env.write().evm_env.block_env.gas_limit = gas_limit;
857 }
858
859 pub fn base_fee(&self) -> u64 {
861 self.fees.base_fee()
862 }
863
864 pub fn is_min_priority_fee_enforced(&self) -> bool {
866 self.fees.is_min_priority_fee_enforced()
867 }
868
869 pub fn excess_blob_gas_and_price(&self) -> Option<BlobExcessGasAndPrice> {
870 self.fees.excess_blob_gas_and_price()
871 }
872
873 pub fn set_base_fee(&self, basefee: u64) {
875 self.fees.set_base_fee(basefee)
876 }
877
878 pub fn set_gas_price(&self, price: u128) {
880 self.fees.set_gas_price(price)
881 }
882
883 pub fn elasticity(&self) -> f64 {
884 self.fees.elasticity()
885 }
886
887 pub fn total_difficulty(&self) -> U256 {
892 self.blockchain.storage.read().total_difficulty
893 }
894
895 pub async fn create_state_snapshot(&self) -> U256 {
899 let num = self.best_number();
900 let hash = self.best_hash();
901 let id = self.db.write().await.snapshot_state();
902 trace!(target: "backend", "creating snapshot {} at {}", id, num);
903 self.active_state_snapshots.lock().insert(id, (num, hash));
904 id
905 }
906
907 pub async fn revert_state_snapshot(&self, id: U256) -> Result<bool, BlockchainError> {
909 let block = { self.active_state_snapshots.lock().remove(&id) };
910 if let Some((num, hash)) = block {
911 let best_block_hash = {
912 let current_height = self.best_number();
914 let mut storage = self.blockchain.storage.write();
915
916 for n in ((num + 1)..=current_height).rev() {
917 trace!(target: "backend", "reverting block {}", n);
918 if let Some(hash) = storage.hashes.remove(&n)
919 && let Some(block) = storage.blocks.remove(&hash)
920 {
921 for tx in block.body.transactions {
922 let _ = storage.transactions.remove(&tx.hash());
923 }
924 }
925 }
926
927 storage.best_number = num;
928 storage.best_hash = hash;
929 hash
930 };
931 let block =
932 self.block_by_hash(best_block_hash).await?.ok_or(BlockchainError::BlockNotFound)?;
933
934 let reset_time = block.header.timestamp;
935 self.time.reset(reset_time);
936
937 let mut env = self.env.write();
938 env.evm_env.block_env = BlockEnv {
939 number: U256::from(num),
940 timestamp: U256::from(block.header.timestamp),
941 difficulty: block.header.difficulty,
942 prevrandao: Some(block.header.mix_hash.unwrap_or_default()),
944 gas_limit: block.header.gas_limit,
945 beneficiary: env.evm_env.block_env.beneficiary,
947 basefee: env.evm_env.block_env.basefee,
948 ..Default::default()
949 }
950 }
951 Ok(self.db.write().await.revert_state(id, RevertStateSnapshotAction::RevertRemove))
952 }
953
954 pub fn list_state_snapshots(&self) -> BTreeMap<U256, (u64, B256)> {
955 self.active_state_snapshots.lock().clone().into_iter().collect()
956 }
957
958 pub async fn serialized_state(
960 &self,
961 preserve_historical_states: bool,
962 ) -> Result<SerializableState, BlockchainError> {
963 let at = self.env.read().evm_env.block_env.clone();
964 let best_number = self.blockchain.storage.read().best_number;
965 let blocks = self.blockchain.storage.read().serialized_blocks();
966 let transactions = self.blockchain.storage.read().serialized_transactions();
967 let historical_states = if preserve_historical_states {
968 Some(self.states.write().serialized_states())
969 } else {
970 None
971 };
972
973 let state = self.db.read().await.dump_state(
974 at,
975 best_number,
976 blocks,
977 transactions,
978 historical_states,
979 )?;
980 state.ok_or_else(|| {
981 RpcError::invalid_params("Dumping state not supported with the current configuration")
982 .into()
983 })
984 }
985
986 pub async fn dump_state(
988 &self,
989 preserve_historical_states: bool,
990 ) -> Result<Bytes, BlockchainError> {
991 let state = self.serialized_state(preserve_historical_states).await?;
992 let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
993 encoder
994 .write_all(&serde_json::to_vec(&state).unwrap_or_default())
995 .map_err(|_| BlockchainError::DataUnavailable)?;
996 Ok(encoder.finish().unwrap_or_default().into())
997 }
998
999 pub async fn load_state(&self, state: SerializableState) -> Result<bool, BlockchainError> {
1001 self.blockchain.storage.write().load_blocks(state.blocks.clone());
1003 self.blockchain.storage.write().load_transactions(state.transactions.clone());
1004 if let Some(block) = state.block.clone() {
1006 self.env.write().evm_env.block_env = block.clone();
1007
1008 let fork_num_and_hash = self.get_fork().map(|f| (f.block_number(), f.block_hash()));
1011
1012 let best_number = state.best_block_number.unwrap_or(block.number.saturating_to());
1013 if let Some((number, hash)) = fork_num_and_hash {
1014 trace!(target: "backend", state_block_number=?best_number, fork_block_number=?number);
1015 if best_number > number {
1019 self.blockchain.storage.write().best_number = best_number;
1020 let best_hash =
1021 self.blockchain.storage.read().hash(best_number.into()).ok_or_else(
1022 || {
1023 BlockchainError::RpcError(RpcError::internal_error_with(format!(
1024 "Best hash not found for best number {best_number}",
1025 )))
1026 },
1027 )?;
1028 self.blockchain.storage.write().best_hash = best_hash;
1029 } else {
1030 self.blockchain.storage.write().best_number = number;
1033 self.blockchain.storage.write().best_hash = hash;
1034 }
1035 } else {
1036 self.blockchain.storage.write().best_number = best_number;
1037
1038 let best_hash =
1040 self.blockchain.storage.read().hash(best_number.into()).ok_or_else(|| {
1041 BlockchainError::RpcError(RpcError::internal_error_with(format!(
1042 "Best hash not found for best number {best_number}",
1043 )))
1044 })?;
1045
1046 self.blockchain.storage.write().best_hash = best_hash;
1047 }
1048 }
1049
1050 if let Some(latest) = state.blocks.iter().max_by_key(|b| b.header.number) {
1051 let header = &latest.header;
1052 let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
1053 header.gas_used,
1054 header.gas_limit,
1055 header.base_fee_per_gas.unwrap_or_default(),
1056 );
1057 let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas(
1058 header.excess_blob_gas.unwrap_or_default(),
1059 header.blob_gas_used.unwrap_or_default(),
1060 );
1061
1062 self.fees.set_base_fee(next_block_base_fee);
1064
1065 self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
1066 next_block_excess_blob_gas,
1067 get_blob_base_fee_update_fraction(
1068 self.env.read().evm_env.cfg_env.chain_id,
1069 header.timestamp,
1070 ),
1071 ));
1072 }
1073
1074 if !self.db.write().await.load_state(state.clone())? {
1075 return Err(RpcError::invalid_params(
1076 "Loading state not supported with the current configuration",
1077 )
1078 .into());
1079 }
1080
1081 if let Some(historical_states) = state.historical_states {
1082 self.states.write().load_states(historical_states);
1083 }
1084
1085 Ok(true)
1086 }
1087
1088 pub async fn load_state_bytes(&self, buf: Bytes) -> Result<bool, BlockchainError> {
1090 let orig_buf = &buf.0[..];
1091 let mut decoder = GzDecoder::new(orig_buf);
1092 let mut decoded_data = Vec::new();
1093
1094 let state: SerializableState = serde_json::from_slice(if decoder.header().is_some() {
1095 decoder
1096 .read_to_end(decoded_data.as_mut())
1097 .map_err(|_| BlockchainError::FailedToDecodeStateDump)?;
1098 &decoded_data
1099 } else {
1100 &buf.0
1101 })
1102 .map_err(|_| BlockchainError::FailedToDecodeStateDump)?;
1103
1104 self.load_state(state).await
1105 }
1106
1107 fn next_env(&self) -> Env {
1112 let mut env = self.env.read().clone();
1113 env.evm_env.block_env.number = env.evm_env.block_env.number.saturating_add(U256::from(1));
1115 env.evm_env.block_env.basefee = self.base_fee();
1116 env.evm_env.block_env.blob_excess_gas_and_price = self.excess_blob_gas_and_price();
1117 env.evm_env.block_env.timestamp = U256::from(self.time.current_call_timestamp());
1118 env
1119 }
1120
1121 fn new_evm_with_inspector_ref<'db, I, DB>(
1123 &self,
1124 db: &'db DB,
1125 env: &Env,
1126 inspector: &'db mut I,
1127 ) -> EitherEvm<WrapDatabaseRef<&'db DB>, &'db mut I, PrecompilesMap>
1128 where
1129 DB: DatabaseRef + ?Sized,
1130 I: Inspector<EthEvmContext<WrapDatabaseRef<&'db DB>>>
1131 + Inspector<OpContext<WrapDatabaseRef<&'db DB>>>,
1132 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
1133 {
1134 let mut evm = new_evm_with_inspector(WrapDatabaseRef(db), env, inspector);
1135 self.env.read().networks.inject_precompiles(evm.precompiles_mut());
1136
1137 if let Some(factory) = &self.precompile_factory {
1138 evm.precompiles_mut().extend_precompiles(factory.precompiles());
1139 }
1140
1141 let cheats = Arc::new(self.cheats.clone());
1142 if cheats.has_recover_overrides() {
1143 let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats));
1144 evm.precompiles_mut().apply_precompile(&EC_RECOVER, move |_| {
1145 Some(DynPrecompile::new_stateful(
1146 cheat_ecrecover.precompile_id().clone(),
1147 move |input| cheat_ecrecover.call(input),
1148 ))
1149 });
1150 }
1151
1152 evm
1153 }
1154
1155 pub async fn inspect_tx(
1157 &self,
1158 tx: Arc<PoolTransaction>,
1159 ) -> Result<
1160 (InstructionResult, Option<Output>, u64, State, Vec<revm::primitives::Log>),
1161 BlockchainError,
1162 > {
1163 let mut env = self.next_env();
1164 env.tx = FromRecoveredTx::from_recovered_tx(
1165 tx.pending_transaction.transaction.as_ref(),
1166 *tx.pending_transaction.sender(),
1167 );
1168
1169 if env.networks.is_optimism() {
1170 env.tx.enveloped_tx =
1171 Some(alloy_rlp::encode(tx.pending_transaction.transaction.as_ref()).into());
1172 }
1173
1174 let db = self.db.read().await;
1175 let mut inspector = self.build_inspector();
1176 let mut evm = self.new_evm_with_inspector_ref(&**db, &env, &mut inspector);
1177 let ResultAndState { result, state } = evm.transact(env.tx)?;
1178 let (exit_reason, gas_used, out, logs) = match result {
1179 ExecutionResult::Success { reason, gas_used, logs, output, .. } => {
1180 (reason.into(), gas_used, Some(output), Some(logs))
1181 }
1182 ExecutionResult::Revert { gas_used, output } => {
1183 (InstructionResult::Revert, gas_used, Some(Output::Call(output)), None)
1184 }
1185 ExecutionResult::Halt { reason, gas_used } => {
1186 let eth_reason = op_haltreason_to_instruction_result(reason);
1187 (eth_reason, gas_used, None, None)
1188 }
1189 };
1190
1191 drop(evm);
1192 inspector.print_logs();
1193
1194 if self.print_traces {
1195 inspector.print_traces(self.call_trace_decoder.clone());
1196 }
1197
1198 Ok((exit_reason, out, gas_used, state, logs.unwrap_or_default()))
1199 }
1200
1201 pub async fn pending_block(&self, pool_transactions: Vec<Arc<PoolTransaction>>) -> BlockInfo {
1205 self.with_pending_block(pool_transactions, |_, block| block).await
1206 }
1207
1208 pub async fn with_pending_block<F, T>(
1212 &self,
1213 pool_transactions: Vec<Arc<PoolTransaction>>,
1214 f: F,
1215 ) -> T
1216 where
1217 F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockInfo) -> T,
1218 {
1219 let db = self.db.read().await;
1220 let env = self.next_env();
1221
1222 let mut cache_db = CacheDB::new(&*db);
1223
1224 let storage = self.blockchain.storage.read();
1225
1226 let executor = TransactionExecutor {
1227 db: &mut cache_db,
1228 validator: self,
1229 pending: pool_transactions.into_iter(),
1230 evm_env: env.evm_env,
1231 parent_hash: storage.best_hash,
1232 gas_used: 0,
1233 blob_gas_used: 0,
1234 enable_steps_tracing: self.enable_steps_tracing,
1235 print_logs: self.print_logs,
1236 print_traces: self.print_traces,
1237 call_trace_decoder: self.call_trace_decoder.clone(),
1238 precompile_factory: self.precompile_factory.clone(),
1239 networks: self.env.read().networks,
1240 blob_params: self.blob_params(),
1241 cheats: self.cheats().clone(),
1242 };
1243
1244 let executed = executor.execute();
1246 f(Box::new(cache_db), executed.block)
1247 }
1248
1249 pub async fn mine_block(
1254 &self,
1255 pool_transactions: Vec<Arc<PoolTransaction>>,
1256 ) -> MinedBlockOutcome {
1257 self.do_mine_block(pool_transactions).await
1258 }
1259
1260 async fn do_mine_block(
1261 &self,
1262 pool_transactions: Vec<Arc<PoolTransaction>>,
1263 ) -> MinedBlockOutcome {
1264 let _mining_guard = self.mining.lock().await;
1265 trace!(target: "backend", "creating new block with {} transactions", pool_transactions.len());
1266
1267 let (outcome, header, block_hash) = {
1268 let current_base_fee = self.base_fee();
1269 let current_excess_blob_gas_and_price = self.excess_blob_gas_and_price();
1270
1271 let mut env = self.env.read().clone();
1272
1273 if env.evm_env.block_env.basefee == 0 {
1274 env.evm_env.cfg_env.disable_base_fee = true;
1277 }
1278
1279 let block_number = self.blockchain.storage.read().best_number.saturating_add(1);
1280
1281 if is_arbitrum(env.evm_env.cfg_env.chain_id) {
1283 env.evm_env.block_env.number = U256::from(block_number);
1285 } else {
1286 env.evm_env.block_env.number =
1287 env.evm_env.block_env.number.saturating_add(U256::from(1));
1288 }
1289
1290 env.evm_env.block_env.basefee = current_base_fee;
1291 env.evm_env.block_env.blob_excess_gas_and_price = current_excess_blob_gas_and_price;
1292
1293 let best_hash = self.blockchain.storage.read().best_hash;
1294
1295 let mut input = Vec::with_capacity(40);
1296 input.extend_from_slice(best_hash.as_slice());
1297 input.extend_from_slice(&block_number.to_le_bytes());
1298 env.evm_env.block_env.prevrandao = Some(keccak256(&input));
1299
1300 if self.prune_state_history_config.is_state_history_supported() {
1301 let db = self.db.read().await.current_state();
1302 self.states.write().insert(best_hash, db);
1304 }
1305
1306 let (executed_tx, block_hash) = {
1307 let mut db = self.db.write().await;
1308
1309 env.evm_env.block_env.timestamp = U256::from(self.time.next_timestamp());
1313
1314 let executor = TransactionExecutor {
1315 db: &mut **db,
1316 validator: self,
1317 pending: pool_transactions.into_iter(),
1318 evm_env: env.evm_env.clone(),
1319 parent_hash: best_hash,
1320 gas_used: 0,
1321 blob_gas_used: 0,
1322 enable_steps_tracing: self.enable_steps_tracing,
1323 print_logs: self.print_logs,
1324 print_traces: self.print_traces,
1325 call_trace_decoder: self.call_trace_decoder.clone(),
1326 networks: self.env.read().networks,
1327 precompile_factory: self.precompile_factory.clone(),
1328 blob_params: self.blob_params(),
1329 cheats: self.cheats().clone(),
1330 };
1331 let executed_tx = executor.execute();
1332
1333 let block_hash = executed_tx.block.block.header.hash_slow();
1335 db.insert_block_hash(U256::from(executed_tx.block.block.header.number), block_hash);
1336
1337 (executed_tx, block_hash)
1338 };
1339
1340 let ExecutedTransactions { block, included, invalid } = executed_tx;
1342 let BlockInfo { block, transactions, receipts } = block;
1343
1344 let header = block.header.clone();
1345
1346 trace!(
1347 target: "backend",
1348 "Mined block {} with {} tx {:?}",
1349 block_number,
1350 transactions.len(),
1351 transactions.iter().map(|tx| tx.transaction_hash).collect::<Vec<_>>()
1352 );
1353 let mut storage = self.blockchain.storage.write();
1354 storage.best_number = block_number;
1356 storage.best_hash = block_hash;
1357 if !self.is_eip3675() {
1360 storage.total_difficulty =
1361 storage.total_difficulty.saturating_add(header.difficulty);
1362 }
1363
1364 storage.blocks.insert(block_hash, block);
1365 storage.hashes.insert(block_number, block_hash);
1366
1367 node_info!("");
1368 for (info, receipt) in transactions.into_iter().zip(receipts) {
1370 node_info!(" Transaction: {:?}", info.transaction_hash);
1372 if let Some(contract) = &info.contract_address {
1373 node_info!(" Contract created: {contract}");
1374 }
1375 node_info!(" Gas used: {}", receipt.cumulative_gas_used());
1376 if !info.exit.is_ok() {
1377 let r = RevertDecoder::new().decode(
1378 info.out.as_ref().map(|b| &b[..]).unwrap_or_default(),
1379 Some(info.exit),
1380 );
1381 node_info!(" Error: reverted with: {r}");
1382 }
1383 node_info!("");
1384
1385 let mined_tx = MinedTransaction { info, receipt, block_hash, block_number };
1386 storage.transactions.insert(mined_tx.info.transaction_hash, mined_tx);
1387 }
1388
1389 if let Some(transaction_block_keeper) = self.transaction_block_keeper
1391 && storage.blocks.len() > transaction_block_keeper
1392 {
1393 let to_clear = block_number
1394 .saturating_sub(transaction_block_keeper.try_into().unwrap_or(u64::MAX));
1395 storage.remove_block_transactions_by_number(to_clear)
1396 }
1397
1398 env.evm_env.block_env.difficulty = U256::from(0);
1400
1401 *self.env.write() = env;
1403
1404 let timestamp = utc_from_secs(header.timestamp);
1405
1406 node_info!(" Block Number: {}", block_number);
1407 node_info!(" Block Hash: {:?}", block_hash);
1408 if timestamp.year() > 9999 {
1409 node_info!(" Block Time: {:?}\n", timestamp.to_rfc3339());
1411 } else {
1412 node_info!(" Block Time: {:?}\n", timestamp.to_rfc2822());
1413 }
1414
1415 let outcome = MinedBlockOutcome { block_number, included, invalid };
1416
1417 (outcome, header, block_hash)
1418 };
1419 let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas(
1420 header.gas_used,
1421 header.gas_limit,
1422 header.base_fee_per_gas.unwrap_or_default(),
1423 );
1424 let next_block_excess_blob_gas = self.fees.get_next_block_blob_excess_gas(
1425 header.excess_blob_gas.unwrap_or_default(),
1426 header.blob_gas_used.unwrap_or_default(),
1427 );
1428
1429 self.fees.set_base_fee(next_block_base_fee);
1431
1432 self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
1433 next_block_excess_blob_gas,
1434 get_blob_base_fee_update_fraction_by_spec_id(*self.env.read().evm_env.spec_id()),
1435 ));
1436
1437 self.notify_on_new_block(header, block_hash);
1439
1440 outcome
1441 }
1442
1443 pub async fn call(
1449 &self,
1450 request: WithOtherFields<TransactionRequest>,
1451 fee_details: FeeDetails,
1452 block_request: Option<BlockRequest>,
1453 overrides: EvmOverrides,
1454 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
1455 self.with_database_at(block_request, |state, mut block| {
1456 let block_number = block.number;
1457 let (exit, out, gas, state) = {
1458 let mut cache_db = CacheDB::new(state);
1459 if let Some(state_overrides) = overrides.state {
1460 apply_state_overrides(state_overrides.into_iter().collect(), &mut cache_db)?;
1461 }
1462 if let Some(block_overrides) = overrides.block {
1463 cache_db.apply_block_overrides(*block_overrides, &mut block);
1464 }
1465 self.call_with_state(&cache_db, request, fee_details, block)
1466 }?;
1467 trace!(target: "backend", "call return {:?} out: {:?} gas {} on block {}", exit, out, gas, block_number);
1468 Ok((exit, out, gas, state))
1469 }).await?
1470 }
1471
1472 fn build_call_env(
1481 &self,
1482 request: WithOtherFields<TransactionRequest>,
1483 fee_details: FeeDetails,
1484 block_env: BlockEnv,
1485 ) -> Env {
1486 let tx_type = request.minimal_tx_type() as u8;
1487
1488 let WithOtherFields::<TransactionRequest> {
1489 inner:
1490 TransactionRequest {
1491 from,
1492 to,
1493 gas,
1494 value,
1495 input,
1496 access_list,
1497 blob_versioned_hashes,
1498 authorization_list,
1499 nonce,
1500 sidecar: _,
1501 chain_id,
1502 .. },
1504 other,
1505 } = request;
1506
1507 let FeeDetails {
1508 gas_price,
1509 max_fee_per_gas,
1510 max_priority_fee_per_gas,
1511 max_fee_per_blob_gas,
1512 } = fee_details;
1513
1514 let gas_limit = gas.unwrap_or(block_env.gas_limit);
1515 let mut env = self.env.read().clone();
1516 env.evm_env.block_env = block_env;
1517 env.evm_env.cfg_env.disable_block_gas_limit = true;
1520 env.evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
1521
1522 env.evm_env.cfg_env.disable_base_fee = true;
1528
1529 env.evm_env.cfg_env.disable_nonce_check = true;
1531
1532 let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| {
1533 self.fees().raw_gas_price().saturating_add(MIN_SUGGESTED_PRIORITY_FEE)
1534 });
1535 let caller = from.unwrap_or_default();
1536 let to = to.as_ref().and_then(TxKind::to);
1537 let blob_hashes = blob_versioned_hashes.unwrap_or_default();
1538 let mut base = TxEnv {
1539 caller,
1540 gas_limit,
1541 gas_price,
1542 gas_priority_fee: max_priority_fee_per_gas,
1543 max_fee_per_blob_gas: max_fee_per_blob_gas
1544 .or_else(|| {
1545 if !blob_hashes.is_empty() {
1546 env.evm_env.block_env.blob_gasprice()
1547 } else {
1548 Some(0)
1549 }
1550 })
1551 .unwrap_or_default(),
1552 kind: match to {
1553 Some(addr) => TxKind::Call(*addr),
1554 None => TxKind::Create,
1555 },
1556 tx_type,
1557 value: value.unwrap_or_default(),
1558 data: input.into_input().unwrap_or_default(),
1559 chain_id: Some(chain_id.unwrap_or(self.env.read().evm_env.cfg_env.chain_id)),
1560 access_list: access_list.unwrap_or_default(),
1561 blob_hashes,
1562 ..Default::default()
1563 };
1564 base.set_signed_authorization(authorization_list.unwrap_or_default());
1565 env.tx = OpTransaction { base, ..Default::default() };
1566
1567 if let Some(nonce) = nonce {
1568 env.tx.base.nonce = nonce;
1569 }
1570
1571 if env.evm_env.block_env.basefee == 0 {
1572 env.evm_env.cfg_env.disable_base_fee = true;
1575 }
1576
1577 if let Ok(deposit) = get_deposit_tx_parts(&other) {
1579 env.tx.deposit = deposit;
1580 }
1581
1582 env
1583 }
1584
1585 fn build_inspector(&self) -> AnvilInspector {
1587 let mut inspector = AnvilInspector::default();
1588
1589 if self.print_logs {
1590 inspector = inspector.with_log_collector();
1591 }
1592 if self.print_traces {
1593 inspector = inspector.with_trace_printer();
1594 }
1595
1596 inspector
1597 }
1598
1599 pub async fn simulate(
1601 &self,
1602 request: SimulatePayload,
1603 block_request: Option<BlockRequest>,
1604 ) -> Result<Vec<SimulatedBlock<AnyRpcBlock>>, BlockchainError> {
1605 self.with_database_at(block_request, |state, mut block_env| {
1606 let SimulatePayload {
1607 block_state_calls,
1608 trace_transfers,
1609 validation,
1610 return_full_transactions,
1611 } = request;
1612 let mut cache_db = CacheDB::new(state);
1613 let mut block_res = Vec::with_capacity(block_state_calls.len());
1614
1615 for block in block_state_calls {
1617 let SimBlock { block_overrides, state_overrides, calls } = block;
1618 let mut call_res = Vec::with_capacity(calls.len());
1619 let mut log_index = 0;
1620 let mut gas_used = 0;
1621 let mut transactions = Vec::with_capacity(calls.len());
1622 let mut logs= Vec::new();
1623
1624 if let Some(state_overrides) = state_overrides {
1626 apply_state_overrides(state_overrides, &mut cache_db)?;
1627 }
1628 if let Some(block_overrides) = block_overrides {
1629 cache_db.apply_block_overrides(block_overrides, &mut block_env);
1630 }
1631
1632 for (req_idx, request) in calls.into_iter().enumerate() {
1634 let fee_details = FeeDetails::new(
1635 request.gas_price,
1636 request.max_fee_per_gas,
1637 request.max_priority_fee_per_gas,
1638 request.max_fee_per_blob_gas,
1639 )?
1640 .or_zero_fees();
1641
1642 let mut env = self.build_call_env(
1643 WithOtherFields::new(request.clone()),
1644 fee_details,
1645 block_env.clone(),
1646 );
1647
1648 env.evm_env.cfg_env.disable_eip3607 = true;
1650
1651 if !validation {
1652 env.evm_env.cfg_env.disable_base_fee = !validation;
1653 env.evm_env.block_env.basefee = 0;
1654 }
1655
1656 let mut inspector = self.build_inspector();
1657
1658 let ResultAndState { result, state } = if trace_transfers {
1660 inspector = inspector.with_transfers();
1663 let mut evm= self.new_evm_with_inspector_ref(
1664 &cache_db,
1665 &env,
1666 &mut inspector,
1667 );
1668
1669 trace!(target: "backend", env=?env.evm_env, spec=?env.evm_env.spec_id(),"simulate evm env");
1670 evm.transact(env.tx)?
1671 } else {
1672 let mut evm = self.new_evm_with_inspector_ref(
1673 &cache_db,
1674 &env,
1675 &mut inspector,
1676 );
1677 trace!(target: "backend", env=?env.evm_env, spec=?env.evm_env.spec_id(),"simulate evm env");
1678 evm.transact(env.tx)?
1679 };
1680 trace!(target: "backend", ?result, ?request, "simulate call");
1681
1682 inspector.print_logs();
1683 if self.print_traces {
1684 inspector.into_print_traces(self.call_trace_decoder.clone());
1685 }
1686
1687 cache_db.commit(state);
1689 gas_used += result.gas_used();
1690
1691 let from = request.from.unwrap_or_default();
1693
1694 let mut request = Into::<FoundryTransactionRequest>::into(WithOtherFields::new(request));
1695 request.prep_for_submission();
1696
1697 let typed_tx = request.build_unsigned().map_err(|e| BlockchainError::InvalidTransactionRequest(e.to_string()))?;
1698
1699 let tx = build_typed_transaction(
1700 typed_tx,
1701 Signature::new(Default::default(), Default::default(), false),
1702 )?;
1703 let tx_hash = tx.hash();
1704 let rpc_tx = transaction_build(
1705 None,
1706 MaybeImpersonatedTransaction::impersonated(tx, from),
1707 None,
1708 None,
1709 Some(block_env.basefee),
1710 );
1711 transactions.push(rpc_tx);
1712
1713 let return_data = result.output().cloned().unwrap_or_default();
1714 let sim_res = SimCallResult {
1715 return_data,
1716 gas_used: result.gas_used(),
1717 status: result.is_success(),
1718 error: result.is_success().not().then(|| {
1719 alloy_rpc_types::simulate::SimulateError {
1720 code: -3200,
1721 message: "execution failed".to_string(),
1722 }
1723 }),
1724 logs: result.clone()
1725 .into_logs()
1726 .into_iter()
1727 .enumerate()
1728 .map(|(idx, log)| Log {
1729 inner: log,
1730 block_number: Some(block_env.number.saturating_to()),
1731 block_timestamp: Some(block_env.timestamp.saturating_to()),
1732 transaction_index: Some(req_idx as u64),
1733 log_index: Some((idx + log_index) as u64),
1734 removed: false,
1735
1736 block_hash: None,
1737 transaction_hash: Some(tx_hash),
1738 })
1739 .collect(),
1740 };
1741 logs.extend(sim_res.logs.iter().map(|log| log.inner.clone()));
1742 log_index += sim_res.logs.len();
1743 call_res.push(sim_res);
1744 }
1745
1746 let transactions_envelopes: Vec<AnyTxEnvelope> = transactions
1747 .iter()
1748 .map(|tx| AnyTxEnvelope::from(tx.clone()))
1749 .collect();
1750 let header = Header {
1751 logs_bloom: logs_bloom(logs.iter()),
1752 transactions_root: calculate_transaction_root(&transactions_envelopes),
1753 receipts_root: calculate_receipt_root(&transactions_envelopes),
1754 parent_hash: Default::default(),
1755 beneficiary: block_env.beneficiary,
1756 state_root: Default::default(),
1757 difficulty: Default::default(),
1758 number: block_env.number.saturating_to(),
1759 gas_limit: block_env.gas_limit,
1760 gas_used,
1761 timestamp: block_env.timestamp.saturating_to(),
1762 extra_data: Default::default(),
1763 mix_hash: Default::default(),
1764 nonce: Default::default(),
1765 base_fee_per_gas: Some(block_env.basefee),
1766 withdrawals_root: None,
1767 blob_gas_used: None,
1768 excess_blob_gas: None,
1769 parent_beacon_block_root: None,
1770 requests_hash: None,
1771 ..Default::default()
1772 };
1773 let mut block = alloy_rpc_types::Block {
1774 header: AnyRpcHeader {
1775 hash: header.hash_slow(),
1776 inner: header.into(),
1777 total_difficulty: None,
1778 size: None,
1779 },
1780 uncles: vec![],
1781 transactions: BlockTransactions::Full(transactions),
1782 withdrawals: None,
1783 };
1784
1785 if !return_full_transactions {
1786 block.transactions.convert_to_hashes();
1787 }
1788
1789 for res in &mut call_res {
1790 res.logs.iter_mut().for_each(|log| {
1791 log.block_hash = Some(block.header.hash);
1792 });
1793 }
1794
1795 let simulated_block = SimulatedBlock {
1796 inner: AnyRpcBlock::new(WithOtherFields::new(block)),
1797 calls: call_res,
1798 };
1799
1800 block_env.number += U256::from(1);
1802 block_env.timestamp += U256::from(12);
1803 block_env.basefee = simulated_block
1804 .inner
1805 .header
1806 .next_block_base_fee(self.fees.base_fee_params())
1807 .unwrap_or_default();
1808
1809 block_res.push(simulated_block);
1810 }
1811
1812 Ok(block_res)
1813 })
1814 .await?
1815 }
1816
1817 pub fn call_with_state(
1818 &self,
1819 state: &dyn DatabaseRef,
1820 request: WithOtherFields<TransactionRequest>,
1821 fee_details: FeeDetails,
1822 block_env: BlockEnv,
1823 ) -> Result<(InstructionResult, Option<Output>, u128, State), BlockchainError> {
1824 let mut inspector = self.build_inspector();
1825
1826 let env = self.build_call_env(request, fee_details, block_env);
1827 let mut evm = self.new_evm_with_inspector_ref(state, &env, &mut inspector);
1828 let ResultAndState { result, state } = evm.transact(env.tx)?;
1829 let (exit_reason, gas_used, out) = match result {
1830 ExecutionResult::Success { reason, gas_used, output, .. } => {
1831 (reason.into(), gas_used, Some(output))
1832 }
1833 ExecutionResult::Revert { gas_used, output } => {
1834 (InstructionResult::Revert, gas_used, Some(Output::Call(output)))
1835 }
1836 ExecutionResult::Halt { reason, gas_used } => {
1837 (op_haltreason_to_instruction_result(reason), gas_used, None)
1838 }
1839 };
1840 drop(evm);
1841 inspector.print_logs();
1842
1843 if self.print_traces {
1844 inspector.into_print_traces(self.call_trace_decoder.clone());
1845 }
1846
1847 Ok((exit_reason, out, gas_used as u128, state))
1848 }
1849
1850 pub async fn call_with_tracing(
1851 &self,
1852 request: WithOtherFields<TransactionRequest>,
1853 fee_details: FeeDetails,
1854 block_request: Option<BlockRequest>,
1855 opts: GethDebugTracingCallOptions,
1856 ) -> Result<GethTrace, BlockchainError> {
1857 let GethDebugTracingCallOptions {
1858 tracing_options, block_overrides, state_overrides, ..
1859 } = opts;
1860 let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options;
1861
1862 self.with_database_at(block_request, |state, mut block| {
1863 let block_number = block.number;
1864
1865 let mut cache_db = CacheDB::new(state);
1866 if let Some(state_overrides) = state_overrides {
1867 apply_state_overrides(state_overrides, &mut cache_db)?;
1868 }
1869 if let Some(block_overrides) = block_overrides {
1870 cache_db.apply_block_overrides(block_overrides, &mut block);
1871 }
1872
1873 if let Some(tracer) = tracer {
1874 return match tracer {
1875 GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
1876 GethDebugBuiltInTracerType::CallTracer => {
1877 let call_config = tracer_config
1878 .into_call_config()
1879 .map_err(|e| RpcError::invalid_params(e.to_string()))?;
1880
1881 let mut inspector = self.build_inspector().with_tracing_config(
1882 TracingInspectorConfig::from_geth_call_config(&call_config),
1883 );
1884
1885 let env = self.build_call_env(request, fee_details, block);
1886 let mut evm =
1887 self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector);
1888 let ResultAndState { result, state: _ } = evm.transact(env.tx)?;
1889
1890 drop(evm);
1891
1892 inspector.print_logs();
1893 if self.print_traces {
1894 inspector.print_traces(self.call_trace_decoder.clone());
1895 }
1896
1897 let tracing_inspector = inspector.tracer.expect("tracer disappeared");
1898
1899 Ok(tracing_inspector
1900 .into_geth_builder()
1901 .geth_call_traces(call_config, result.gas_used())
1902 .into())
1903 }
1904 GethDebugBuiltInTracerType::PreStateTracer => {
1905 let pre_state_config = tracer_config
1906 .into_pre_state_config()
1907 .map_err(|e| RpcError::invalid_params(e.to_string()))?;
1908
1909 let mut inspector = TracingInspector::new(
1910 TracingInspectorConfig::from_geth_prestate_config(
1911 &pre_state_config,
1912 ),
1913 );
1914
1915 let env = self.build_call_env(request, fee_details, block);
1916 let mut evm =
1917 self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector);
1918 let result = evm.transact(env.tx)?;
1919
1920 drop(evm);
1921
1922 Ok(inspector
1923 .into_geth_builder()
1924 .geth_prestate_traces(&result, &pre_state_config, cache_db)?
1925 .into())
1926 }
1927 GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()),
1928 GethDebugBuiltInTracerType::FourByteTracer
1929 | GethDebugBuiltInTracerType::MuxTracer
1930 | GethDebugBuiltInTracerType::FlatCallTracer
1931 | GethDebugBuiltInTracerType::Erc7562Tracer => {
1932 Err(RpcError::invalid_params("unsupported tracer type").into())
1933 }
1934 },
1935 #[cfg(not(feature = "js-tracer"))]
1936 GethDebugTracerType::JsTracer(_) => {
1937 Err(RpcError::invalid_params("unsupported tracer type").into())
1938 }
1939 #[cfg(feature = "js-tracer")]
1940 GethDebugTracerType::JsTracer(code) => {
1941 use alloy_evm::IntoTxEnv;
1942 let config = tracer_config.into_json();
1943 let mut inspector =
1944 revm_inspectors::tracing::js::JsInspector::new(code, config)
1945 .map_err(|err| BlockchainError::Message(err.to_string()))?;
1946
1947 let env = self.build_call_env(request, fee_details, block.clone());
1948 let mut evm =
1949 self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector);
1950 let result = evm.transact(env.tx.clone())?;
1951 let res = evm
1952 .inspector_mut()
1953 .json_result(result, &env.tx.into_tx_env(), &block, &cache_db)
1954 .map_err(|err| BlockchainError::Message(err.to_string()))?;
1955
1956 Ok(GethTrace::JS(res))
1957 }
1958 };
1959 }
1960
1961 let mut inspector = self
1963 .build_inspector()
1964 .with_tracing_config(TracingInspectorConfig::from_geth_config(&config));
1965
1966 let env = self.build_call_env(request, fee_details, block);
1967 let mut evm = self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector);
1968 let ResultAndState { result, state: _ } = evm.transact(env.tx)?;
1969
1970 let (exit_reason, gas_used, out) = match result {
1971 ExecutionResult::Success { reason, gas_used, output, .. } => {
1972 (reason.into(), gas_used, Some(output))
1973 }
1974 ExecutionResult::Revert { gas_used, output } => {
1975 (InstructionResult::Revert, gas_used, Some(Output::Call(output)))
1976 }
1977 ExecutionResult::Halt { reason, gas_used } => {
1978 (op_haltreason_to_instruction_result(reason), gas_used, None)
1979 }
1980 };
1981
1982 drop(evm);
1983 let tracing_inspector = inspector.tracer.expect("tracer disappeared");
1984 let return_value = out.as_ref().map(|o| o.data()).cloned().unwrap_or_default();
1985
1986 trace!(target: "backend", ?exit_reason, ?out, %gas_used, %block_number, "trace call");
1987
1988 let res = tracing_inspector
1989 .into_geth_builder()
1990 .geth_traces(gas_used, return_value, config)
1991 .into();
1992
1993 Ok(res)
1994 })
1995 .await?
1996 }
1997
1998 pub fn build_access_list_with_state(
1999 &self,
2000 state: &dyn DatabaseRef,
2001 request: WithOtherFields<TransactionRequest>,
2002 fee_details: FeeDetails,
2003 block_env: BlockEnv,
2004 ) -> Result<(InstructionResult, Option<Output>, u64, AccessList), BlockchainError> {
2005 let mut inspector =
2006 AccessListInspector::new(request.access_list.clone().unwrap_or_default());
2007
2008 let env = self.build_call_env(request, fee_details, block_env);
2009 let mut evm = self.new_evm_with_inspector_ref(state, &env, &mut inspector);
2010 let ResultAndState { result, state: _ } = evm.transact(env.tx)?;
2011 let (exit_reason, gas_used, out) = match result {
2012 ExecutionResult::Success { reason, gas_used, output, .. } => {
2013 (reason.into(), gas_used, Some(output))
2014 }
2015 ExecutionResult::Revert { gas_used, output } => {
2016 (InstructionResult::Revert, gas_used, Some(Output::Call(output)))
2017 }
2018 ExecutionResult::Halt { reason, gas_used } => {
2019 (op_haltreason_to_instruction_result(reason), gas_used, None)
2020 }
2021 };
2022 drop(evm);
2023 let access_list = inspector.access_list();
2024 Ok((exit_reason, out, gas_used, access_list))
2025 }
2026
2027 fn get_receipts(
2029 &self,
2030 tx_hashes: impl IntoIterator<Item = TxHash>,
2031 ) -> Vec<FoundryReceiptEnvelope> {
2032 let storage = self.blockchain.storage.read();
2033 let mut receipts = vec![];
2034
2035 for hash in tx_hashes {
2036 if let Some(tx) = storage.transactions.get(&hash) {
2037 receipts.push(tx.receipt.clone());
2038 }
2039 }
2040
2041 receipts
2042 }
2043
2044 async fn logs_for_block(
2046 &self,
2047 filter: Filter,
2048 hash: B256,
2049 ) -> Result<Vec<Log>, BlockchainError> {
2050 if let Some(block) = self.blockchain.get_block_by_hash(&hash) {
2051 return Ok(self.mined_logs_for_block(filter, block, hash));
2052 }
2053
2054 if let Some(fork) = self.get_fork() {
2055 return Ok(fork.logs(&filter).await?);
2056 }
2057
2058 Ok(Vec::new())
2059 }
2060
2061 fn mined_logs_for_block(&self, filter: Filter, block: Block, block_hash: B256) -> Vec<Log> {
2063 let mut all_logs = Vec::new();
2064 let mut block_log_index = 0u32;
2065
2066 let storage = self.blockchain.storage.read();
2067
2068 for tx in block.body.transactions {
2069 let Some(tx) = storage.transactions.get(&tx.hash()) else {
2070 continue;
2071 };
2072
2073 let logs = tx.receipt.logs();
2074 let transaction_hash = tx.info.transaction_hash;
2075
2076 for log in logs {
2077 if filter.matches(log) {
2078 all_logs.push(Log {
2079 inner: log.clone(),
2080 block_hash: Some(block_hash),
2081 block_number: Some(block.header.number),
2082 block_timestamp: Some(block.header.timestamp),
2083 transaction_hash: Some(transaction_hash),
2084 transaction_index: Some(tx.info.transaction_index),
2085 log_index: Some(block_log_index as u64),
2086 removed: false,
2087 });
2088 }
2089 block_log_index += 1;
2090 }
2091 }
2092 all_logs
2093 }
2094
2095 async fn logs_for_range(
2097 &self,
2098 filter: &Filter,
2099 mut from: u64,
2100 to: u64,
2101 ) -> Result<Vec<Log>, BlockchainError> {
2102 let mut all_logs = Vec::new();
2103
2104 if let Some(fork) = self.get_fork() {
2106 let mut to_on_fork = to;
2107
2108 if !fork.predates_fork(to) {
2109 to_on_fork = fork.block_number();
2111 }
2112
2113 if fork.predates_fork_inclusive(from) {
2114 let filter = filter.clone().from_block(from).to_block(to_on_fork);
2116 all_logs = fork.logs(&filter).await?;
2117
2118 from = fork.block_number() + 1;
2120 }
2121 }
2122
2123 for number in from..=to {
2124 if let Some((block, hash)) = self.get_block_with_hash(number) {
2125 all_logs.extend(self.mined_logs_for_block(filter.clone(), block, hash));
2126 }
2127 }
2128
2129 Ok(all_logs)
2130 }
2131
2132 pub async fn logs(&self, filter: Filter) -> Result<Vec<Log>, BlockchainError> {
2134 trace!(target: "backend", "get logs [{:?}]", filter);
2135 if let Some(hash) = filter.get_block_hash() {
2136 self.logs_for_block(filter, hash).await
2137 } else {
2138 let best = self.best_number();
2139 let to_block =
2140 self.convert_block_number(filter.block_option.get_to_block().copied()).min(best);
2141 let from_block =
2142 self.convert_block_number(filter.block_option.get_from_block().copied());
2143 if from_block > best {
2144 return Err(BlockchainError::BlockOutOfRange(best, from_block));
2145 }
2146
2147 self.logs_for_range(&filter, from_block, to_block).await
2148 }
2149 }
2150
2151 pub async fn block_by_hash(&self, hash: B256) -> Result<Option<AnyRpcBlock>, BlockchainError> {
2152 trace!(target: "backend", "get block by hash {:?}", hash);
2153 if let tx @ Some(_) = self.mined_block_by_hash(hash) {
2154 return Ok(tx);
2155 }
2156
2157 if let Some(fork) = self.get_fork() {
2158 return Ok(fork.block_by_hash(hash).await?);
2159 }
2160
2161 Ok(None)
2162 }
2163
2164 pub async fn block_by_hash_full(
2165 &self,
2166 hash: B256,
2167 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
2168 trace!(target: "backend", "get block by hash {:?}", hash);
2169 if let tx @ Some(_) = self.get_full_block(hash) {
2170 return Ok(tx);
2171 }
2172
2173 if let Some(fork) = self.get_fork() {
2174 return Ok(fork.block_by_hash_full(hash).await?);
2175 }
2176
2177 Ok(None)
2178 }
2179
2180 fn mined_block_by_hash(&self, hash: B256) -> Option<AnyRpcBlock> {
2181 let block = self.blockchain.get_block_by_hash(&hash)?;
2182 Some(self.convert_block_with_hash(block, Some(hash)))
2183 }
2184
2185 pub(crate) async fn mined_transactions_by_block_number(
2186 &self,
2187 number: BlockNumber,
2188 ) -> Option<Vec<AnyRpcTransaction>> {
2189 if let Some(block) = self.get_block(number) {
2190 return self.mined_transactions_in_block(&block);
2191 }
2192 None
2193 }
2194
2195 pub(crate) fn mined_transactions_in_block(
2197 &self,
2198 block: &Block,
2199 ) -> Option<Vec<AnyRpcTransaction>> {
2200 let mut transactions = Vec::with_capacity(block.body.transactions.len());
2201 let base_fee = block.header.base_fee_per_gas;
2202 let storage = self.blockchain.storage.read();
2203 for hash in block.body.transactions.iter().map(|tx| tx.hash()) {
2204 let info = storage.transactions.get(&hash)?.info.clone();
2205 let tx = block.body.transactions.get(info.transaction_index as usize)?.clone();
2206
2207 let tx = transaction_build(Some(hash), tx, Some(block), Some(info), base_fee);
2208 transactions.push(tx);
2209 }
2210 Some(transactions)
2211 }
2212
2213 pub async fn block_by_number(
2214 &self,
2215 number: BlockNumber,
2216 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
2217 trace!(target: "backend", "get block by number {:?}", number);
2218 if let tx @ Some(_) = self.mined_block_by_number(number) {
2219 return Ok(tx);
2220 }
2221
2222 if let Some(fork) = self.get_fork() {
2223 let number = self.convert_block_number(Some(number));
2224 if fork.predates_fork_inclusive(number) {
2225 return Ok(fork.block_by_number(number).await?);
2226 }
2227 }
2228
2229 Ok(None)
2230 }
2231
2232 pub async fn block_by_number_full(
2233 &self,
2234 number: BlockNumber,
2235 ) -> Result<Option<AnyRpcBlock>, BlockchainError> {
2236 trace!(target: "backend", "get block by number {:?}", number);
2237 if let tx @ Some(_) = self.get_full_block(number) {
2238 return Ok(tx);
2239 }
2240
2241 if let Some(fork) = self.get_fork() {
2242 let number = self.convert_block_number(Some(number));
2243 if fork.predates_fork_inclusive(number) {
2244 return Ok(fork.block_by_number_full(number).await?);
2245 }
2246 }
2247
2248 Ok(None)
2249 }
2250
2251 fn get_block_with_hash(&self, id: impl Into<BlockId>) -> Option<(Block, B256)> {
2253 let hash = match id.into() {
2254 BlockId::Hash(hash) => hash.block_hash,
2255 BlockId::Number(number) => {
2256 let storage = self.blockchain.storage.read();
2257 let slots_in_an_epoch = self.slots_in_an_epoch;
2258 match number {
2259 BlockNumber::Latest => storage.best_hash,
2260 BlockNumber::Earliest => storage.genesis_hash,
2261 BlockNumber::Pending => return None,
2262 BlockNumber::Number(num) => *storage.hashes.get(&num)?,
2263 BlockNumber::Safe => {
2264 if storage.best_number > (slots_in_an_epoch) {
2265 *storage.hashes.get(&(storage.best_number - (slots_in_an_epoch)))?
2266 } else {
2267 storage.genesis_hash }
2269 }
2270 BlockNumber::Finalized => {
2271 if storage.best_number > (slots_in_an_epoch * 2) {
2272 *storage.hashes.get(&(storage.best_number - (slots_in_an_epoch * 2)))?
2273 } else {
2274 storage.genesis_hash
2275 }
2276 }
2277 }
2278 }
2279 };
2280 let block = self.get_block_by_hash(hash)?;
2281 Some((block, hash))
2282 }
2283
2284 pub fn get_block(&self, id: impl Into<BlockId>) -> Option<Block> {
2285 self.get_block_with_hash(id).map(|(block, _)| block)
2286 }
2287
2288 pub fn get_block_by_hash(&self, hash: B256) -> Option<Block> {
2289 self.blockchain.get_block_by_hash(&hash)
2290 }
2291
2292 pub fn mined_block_by_number(&self, number: BlockNumber) -> Option<AnyRpcBlock> {
2293 let (block, hash) = self.get_block_with_hash(number)?;
2294 let mut block = self.convert_block_with_hash(block, Some(hash));
2295 block.transactions.convert_to_hashes();
2296 Some(block)
2297 }
2298
2299 pub fn get_full_block(&self, id: impl Into<BlockId>) -> Option<AnyRpcBlock> {
2300 let (block, hash) = self.get_block_with_hash(id)?;
2301 let transactions = self.mined_transactions_in_block(&block)?;
2302 let mut block = self.convert_block_with_hash(block, Some(hash));
2303 block.inner.transactions = BlockTransactions::Full(transactions);
2304 Some(block)
2305 }
2306
2307 pub fn convert_block(&self, block: Block) -> AnyRpcBlock {
2309 self.convert_block_with_hash(block, None)
2310 }
2311
2312 pub fn convert_block_with_hash(&self, block: Block, known_hash: Option<B256>) -> AnyRpcBlock {
2315 let size = U256::from(alloy_rlp::encode(&block).len() as u32);
2316
2317 let header = block.header.clone();
2318 let transactions = block.body.transactions;
2319
2320 let hash = known_hash.unwrap_or_else(|| header.hash_slow());
2321 let Header { number, withdrawals_root, .. } = header;
2322
2323 let block = AlloyBlock {
2324 header: AlloyHeader {
2325 inner: AnyHeader::from(header),
2326 hash,
2327 total_difficulty: Some(self.total_difficulty()),
2328 size: Some(size),
2329 },
2330 transactions: alloy_rpc_types::BlockTransactions::Hashes(
2331 transactions.into_iter().map(|tx| tx.hash()).collect(),
2332 ),
2333 uncles: vec![],
2334 withdrawals: withdrawals_root.map(|_| Default::default()),
2335 };
2336
2337 let mut block = WithOtherFields::new(block);
2338
2339 if is_arbitrum(self.env.read().evm_env.cfg_env.chain_id) {
2341 block.other.insert("l1BlockNumber".to_string(), number.into());
2343 }
2344
2345 AnyRpcBlock::from(block)
2346 }
2347
2348 pub async fn ensure_block_number<T: Into<BlockId>>(
2354 &self,
2355 block_id: Option<T>,
2356 ) -> Result<u64, BlockchainError> {
2357 let current = self.best_number();
2358 let requested =
2359 match block_id.map(Into::into).unwrap_or(BlockId::Number(BlockNumber::Latest)) {
2360 BlockId::Hash(hash) => {
2361 self.block_by_hash(hash.block_hash)
2362 .await?
2363 .ok_or(BlockchainError::BlockNotFound)?
2364 .header
2365 .number
2366 }
2367 BlockId::Number(num) => match num {
2368 BlockNumber::Latest | BlockNumber::Pending => current,
2369 BlockNumber::Earliest => U64::ZERO.to::<u64>(),
2370 BlockNumber::Number(num) => num,
2371 BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch),
2372 BlockNumber::Finalized => current.saturating_sub(self.slots_in_an_epoch * 2),
2373 },
2374 };
2375
2376 if requested > current {
2377 Err(BlockchainError::BlockOutOfRange(current, requested))
2378 } else {
2379 Ok(requested)
2380 }
2381 }
2382
2383 pub fn convert_block_number(&self, block: Option<BlockNumber>) -> u64 {
2384 let current = self.best_number();
2385 match block.unwrap_or(BlockNumber::Latest) {
2386 BlockNumber::Latest | BlockNumber::Pending => current,
2387 BlockNumber::Earliest => 0,
2388 BlockNumber::Number(num) => num,
2389 BlockNumber::Safe => current.saturating_sub(self.slots_in_an_epoch),
2390 BlockNumber::Finalized => current.saturating_sub(self.slots_in_an_epoch * 2),
2391 }
2392 }
2393
2394 pub async fn with_database_at<F, T>(
2396 &self,
2397 block_request: Option<BlockRequest>,
2398 f: F,
2399 ) -> Result<T, BlockchainError>
2400 where
2401 F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockEnv) -> T,
2402 {
2403 let block_number = match block_request {
2404 Some(BlockRequest::Pending(pool_transactions)) => {
2405 let result = self
2406 .with_pending_block(pool_transactions, |state, block| {
2407 let block = block.block;
2408 let block = BlockEnv {
2409 number: U256::from(block.header.number),
2410 beneficiary: block.header.beneficiary,
2411 timestamp: U256::from(block.header.timestamp),
2412 difficulty: block.header.difficulty,
2413 prevrandao: Some(block.header.mix_hash),
2414 basefee: block.header.base_fee_per_gas.unwrap_or_default(),
2415 gas_limit: block.header.gas_limit,
2416 ..Default::default()
2417 };
2418 f(state, block)
2419 })
2420 .await;
2421 return Ok(result);
2422 }
2423 Some(BlockRequest::Number(bn)) => Some(BlockNumber::Number(bn)),
2424 None => None,
2425 };
2426 let block_number = self.convert_block_number(block_number);
2427 let current_number = self.best_number();
2428
2429 if block_number > current_number {
2431 return Err(BlockchainError::BlockOutOfRange(current_number, block_number));
2432 }
2433
2434 if block_number < current_number {
2435 if let Some((block_hash, block)) = self
2436 .block_by_number(BlockNumber::Number(block_number))
2437 .await?
2438 .map(|block| (block.header.hash, block))
2439 {
2440 let read_guard = self.states.upgradable_read();
2441 if let Some(state_db) = read_guard.get_state(&block_hash) {
2442 return Ok(get_block_env(state_db, block_number, block, f));
2443 } else {
2444 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
2445 if let Some(state) = write_guard.get_on_disk_state(&block_hash) {
2446 return Ok(get_block_env(state, block_number, block, f));
2447 }
2448 }
2449 }
2450
2451 warn!(target: "backend", "Not historic state found for block={}", block_number);
2452 return Err(BlockchainError::BlockOutOfRange(current_number, block_number));
2453 }
2454
2455 let db = self.db.read().await;
2456 let block = self.env.read().evm_env.block_env.clone();
2457 Ok(f(Box::new(&**db), block))
2458 }
2459
2460 pub async fn storage_at(
2461 &self,
2462 address: Address,
2463 index: U256,
2464 block_request: Option<BlockRequest>,
2465 ) -> Result<B256, BlockchainError> {
2466 self.with_database_at(block_request, |db, _| {
2467 trace!(target: "backend", "get storage for {:?} at {:?}", address, index);
2468 let val = db.storage_ref(address, index)?;
2469 Ok(val.into())
2470 })
2471 .await?
2472 }
2473
2474 pub async fn get_code(
2479 &self,
2480 address: Address,
2481 block_request: Option<BlockRequest>,
2482 ) -> Result<Bytes, BlockchainError> {
2483 self.with_database_at(block_request, |db, _| self.get_code_with_state(&db, address)).await?
2484 }
2485
2486 pub fn get_code_with_state(
2487 &self,
2488 state: &dyn DatabaseRef,
2489 address: Address,
2490 ) -> Result<Bytes, BlockchainError> {
2491 trace!(target: "backend", "get code for {:?}", address);
2492 let account = state.basic_ref(address)?.unwrap_or_default();
2493 if account.code_hash == KECCAK_EMPTY {
2494 return Ok(Default::default());
2496 }
2497 let code = if let Some(code) = account.code {
2498 code
2499 } else {
2500 state.code_by_hash_ref(account.code_hash)?
2501 };
2502 Ok(code.bytes()[..code.len()].to_vec().into())
2503 }
2504
2505 pub async fn get_balance(
2509 &self,
2510 address: Address,
2511 block_request: Option<BlockRequest>,
2512 ) -> Result<U256, BlockchainError> {
2513 self.with_database_at(block_request, |db, _| self.get_balance_with_state(db, address))
2514 .await?
2515 }
2516
2517 pub async fn get_account_at_block(
2518 &self,
2519 address: Address,
2520 block_request: Option<BlockRequest>,
2521 ) -> Result<TrieAccount, BlockchainError> {
2522 self.with_database_at(block_request, |block_db, _| {
2523 let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
2524 let account = db.get(&address).cloned().unwrap_or_default();
2525 let storage_root = storage_root(&account.storage);
2526 let code_hash = account.info.code_hash;
2527 let balance = account.info.balance;
2528 let nonce = account.info.nonce;
2529 Ok(TrieAccount { balance, nonce, code_hash, storage_root })
2530 })
2531 .await?
2532 }
2533
2534 pub fn get_balance_with_state<D>(
2535 &self,
2536 state: D,
2537 address: Address,
2538 ) -> Result<U256, BlockchainError>
2539 where
2540 D: DatabaseRef,
2541 {
2542 trace!(target: "backend", "get balance for {:?}", address);
2543 Ok(state.basic_ref(address)?.unwrap_or_default().balance)
2544 }
2545
2546 pub async fn get_nonce(
2550 &self,
2551 address: Address,
2552 block_request: BlockRequest,
2553 ) -> Result<u64, BlockchainError> {
2554 if let BlockRequest::Pending(pool_transactions) = &block_request
2555 && let Some(value) = get_pool_transactions_nonce(pool_transactions, address)
2556 {
2557 return Ok(value);
2558 }
2559 let final_block_request = match block_request {
2560 BlockRequest::Pending(_) => BlockRequest::Number(self.best_number()),
2561 BlockRequest::Number(bn) => BlockRequest::Number(bn),
2562 };
2563
2564 self.with_database_at(Some(final_block_request), |db, _| {
2565 trace!(target: "backend", "get nonce for {:?}", address);
2566 Ok(db.basic_ref(address)?.unwrap_or_default().nonce)
2567 })
2568 .await?
2569 }
2570
2571 pub async fn trace_transaction(
2573 &self,
2574 hash: B256,
2575 ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
2576 if let Some(traces) = self.mined_parity_trace_transaction(hash) {
2577 return Ok(traces);
2578 }
2579
2580 if let Some(fork) = self.get_fork() {
2581 return Ok(fork.trace_transaction(hash).await?);
2582 }
2583
2584 Ok(vec![])
2585 }
2586
2587 pub(crate) fn mined_parity_trace_transaction(
2589 &self,
2590 hash: B256,
2591 ) -> Option<Vec<LocalizedTransactionTrace>> {
2592 self.blockchain.storage.read().transactions.get(&hash).map(|tx| tx.parity_traces())
2593 }
2594
2595 pub(crate) fn mined_transaction(&self, hash: B256) -> Option<MinedTransaction> {
2597 self.blockchain.storage.read().transactions.get(&hash).cloned()
2598 }
2599
2600 pub(crate) fn mined_parity_trace_block(
2602 &self,
2603 block: u64,
2604 ) -> Option<Vec<LocalizedTransactionTrace>> {
2605 let block = self.get_block(block)?;
2606 let mut traces = vec![];
2607 let storage = self.blockchain.storage.read();
2608 for tx in block.body.transactions {
2609 traces.extend(storage.transactions.get(&tx.hash())?.parity_traces());
2610 }
2611 Some(traces)
2612 }
2613
2614 pub async fn debug_trace_transaction(
2616 &self,
2617 hash: B256,
2618 opts: GethDebugTracingOptions,
2619 ) -> Result<GethTrace, BlockchainError> {
2620 #[cfg(feature = "js-tracer")]
2621 if let Some(tracer_type) = opts.tracer.as_ref()
2622 && tracer_type.is_js()
2623 {
2624 return self
2625 .trace_tx_with_js_tracer(hash, tracer_type.as_str().to_string(), opts.clone())
2626 .await;
2627 }
2628
2629 if let Some(trace) = self.mined_geth_trace_transaction(hash, opts.clone()).await {
2630 return trace;
2631 }
2632
2633 if let Some(fork) = self.get_fork() {
2634 return Ok(fork.debug_trace_transaction(hash, opts).await?);
2635 }
2636
2637 Ok(GethTrace::Default(Default::default()))
2638 }
2639
2640 fn replay_tx_with_inspector<I, F, T>(
2641 &self,
2642 hash: B256,
2643 mut inspector: I,
2644 f: F,
2645 ) -> Result<T, BlockchainError>
2646 where
2647 for<'a> I: Inspector<EthEvmContext<WrapDatabaseRef<&'a CacheDB<Box<&'a StateDb>>>>>
2648 + Inspector<OpContext<WrapDatabaseRef<&'a CacheDB<Box<&'a StateDb>>>>>
2649 + 'a,
2650 for<'a> F:
2651 FnOnce(ResultAndState<OpHaltReason>, CacheDB<Box<&'a StateDb>>, I, TxEnv, Env) -> T,
2652 {
2653 let block = {
2654 let storage = self.blockchain.storage.read();
2655 let MinedTransaction { block_hash, .. } = storage
2656 .transactions
2657 .get(&hash)
2658 .cloned()
2659 .ok_or(BlockchainError::TransactionNotFound)?;
2660
2661 storage.blocks.get(&block_hash).cloned().ok_or(BlockchainError::BlockNotFound)?
2662 };
2663
2664 let index = block
2665 .body
2666 .transactions
2667 .iter()
2668 .position(|tx| tx.hash() == hash)
2669 .expect("transaction not found in block");
2670
2671 let pool_txs: Vec<Arc<PoolTransaction>> = block.body.transactions[..index]
2672 .iter()
2673 .map(|tx| {
2674 let pending_tx =
2675 PendingTransaction::from_maybe_impersonated(tx.clone()).expect("is valid");
2676 Arc::new(PoolTransaction {
2677 pending_transaction: pending_tx,
2678 requires: vec![],
2679 provides: vec![],
2680 priority: crate::eth::pool::transactions::TransactionPriority(0),
2681 })
2682 })
2683 .collect();
2684
2685 let trace = |parent_state: &StateDb| -> Result<T, BlockchainError> {
2686 let mut cache_db = CacheDB::new(Box::new(parent_state));
2687
2688 let mut env = self.env.read().clone();
2690
2691 env.evm_env.block_env = BlockEnv {
2692 number: U256::from(block.header.number),
2693 beneficiary: block.header.beneficiary,
2694 timestamp: U256::from(block.header.timestamp),
2695 difficulty: block.header.difficulty,
2696 prevrandao: Some(block.header.mix_hash),
2697 basefee: block.header.base_fee_per_gas.unwrap_or_default(),
2698 gas_limit: block.header.gas_limit,
2699 ..Default::default()
2700 };
2701
2702 let executor = TransactionExecutor {
2703 db: &mut cache_db,
2704 validator: self,
2705 pending: pool_txs.into_iter(),
2706 evm_env: env.evm_env.clone(),
2707 parent_hash: block.header.parent_hash,
2708 gas_used: 0,
2709 blob_gas_used: 0,
2710 enable_steps_tracing: self.enable_steps_tracing,
2711 print_logs: self.print_logs,
2712 print_traces: self.print_traces,
2713 call_trace_decoder: self.call_trace_decoder.clone(),
2714 precompile_factory: self.precompile_factory.clone(),
2715 networks: self.env.read().networks,
2716 blob_params: self.blob_params(),
2717 cheats: self.cheats().clone(),
2718 };
2719
2720 let _ = executor.execute();
2721
2722 let target_tx = block.body.transactions[index].clone();
2723 let target_tx = PendingTransaction::from_maybe_impersonated(target_tx)?;
2724 let mut tx_env: OpTransaction<TxEnv> = FromRecoveredTx::from_recovered_tx(
2725 target_tx.transaction.as_ref(),
2726 *target_tx.sender(),
2727 );
2728 if env.networks.is_optimism() {
2729 tx_env.enveloped_tx = Some(target_tx.transaction.encoded_2718().into());
2730 }
2731
2732 let mut evm = self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector);
2733
2734 let result = evm
2735 .transact(tx_env.clone())
2736 .map_err(|err| BlockchainError::Message(err.to_string()))?;
2737
2738 Ok(f(result, cache_db, inspector, tx_env.base, env))
2739 };
2740
2741 let read_guard = self.states.upgradable_read();
2742 if let Some(state) = read_guard.get_state(&block.header.parent_hash) {
2743 trace(state)
2744 } else {
2745 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
2746 let state = write_guard
2747 .get_on_disk_state(&block.header.parent_hash)
2748 .ok_or(BlockchainError::BlockNotFound)?;
2749 trace(state)
2750 }
2751 }
2752
2753 #[cfg(feature = "js-tracer")]
2755 pub async fn trace_tx_with_js_tracer(
2756 &self,
2757 hash: B256,
2758 code: String,
2759 opts: GethDebugTracingOptions,
2760 ) -> Result<GethTrace, BlockchainError> {
2761 let GethDebugTracingOptions { tracer_config, .. } = opts;
2762 let config = tracer_config.into_json();
2763 let inspector = revm_inspectors::tracing::js::JsInspector::new(code, config)
2764 .map_err(|err| BlockchainError::Message(err.to_string()))?;
2765 let trace = self.replay_tx_with_inspector(
2766 hash,
2767 inspector,
2768 |result, cache_db, mut inspector, tx_env, env| {
2769 inspector
2770 .json_result(
2771 result,
2772 &alloy_evm::IntoTxEnv::into_tx_env(tx_env),
2773 &env.evm_env.block_env,
2774 &cache_db,
2775 )
2776 .map_err(|e| BlockchainError::Message(e.to_string()))
2777 },
2778 )??;
2779 Ok(GethTrace::JS(trace))
2780 }
2781
2782 pub async fn debug_code_by_hash(
2784 &self,
2785 code_hash: B256,
2786 block_id: Option<BlockId>,
2787 ) -> Result<Option<Bytes>, BlockchainError> {
2788 if let Ok(code) = self.db.read().await.code_by_hash_ref(code_hash) {
2789 return Ok(Some(code.original_bytes()));
2790 }
2791 if let Some(fork) = self.get_fork() {
2792 return Ok(fork.debug_code_by_hash(code_hash, block_id).await?);
2793 }
2794
2795 Ok(None)
2796 }
2797
2798 pub async fn debug_db_get(&self, key: String) -> Result<Option<Bytes>, BlockchainError> {
2806 let key_bytes = if key.starts_with("0x") {
2807 hex::decode(&key)
2808 .map_err(|_| BlockchainError::Message("Invalid hex key".to_string()))?
2809 } else {
2810 key.into_bytes()
2811 };
2812
2813 if key_bytes.len() != 33 {
2815 return Err(BlockchainError::Message(format!(
2816 "Invalid key length: expected 33 bytes, got {}",
2817 key_bytes.len()
2818 )));
2819 }
2820
2821 if key_bytes[0] != 0x63 {
2823 return Err(BlockchainError::Message(
2824 "Key prefix must be 0x63 for code hash lookups".to_string(),
2825 ));
2826 }
2827
2828 let code_hash = B256::from_slice(&key_bytes[1..33]);
2829
2830 self.debug_code_by_hash(code_hash, None).await
2832 }
2833
2834 fn geth_trace(
2835 &self,
2836 tx: &MinedTransaction,
2837 opts: GethDebugTracingOptions,
2838 ) -> Result<GethTrace, BlockchainError> {
2839 let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts;
2840
2841 if let Some(tracer) = tracer {
2842 match tracer {
2843 GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
2844 GethDebugBuiltInTracerType::FourByteTracer => {
2845 let inspector = FourByteInspector::default();
2846 let res = self.replay_tx_with_inspector(
2847 tx.info.transaction_hash,
2848 inspector,
2849 |_, _, inspector, _, _| FourByteFrame::from(inspector).into(),
2850 )?;
2851 return Ok(res);
2852 }
2853 GethDebugBuiltInTracerType::CallTracer => {
2854 return match tracer_config.into_call_config() {
2855 Ok(call_config) => {
2856 let inspector = TracingInspector::new(
2857 TracingInspectorConfig::from_geth_call_config(&call_config),
2858 );
2859 let frame = self.replay_tx_with_inspector(
2860 tx.info.transaction_hash,
2861 inspector,
2862 |_, _, inspector, _, _| {
2863 inspector
2864 .geth_builder()
2865 .geth_call_traces(
2866 call_config,
2867 tx.receipt.cumulative_gas_used(),
2868 )
2869 .into()
2870 },
2871 )?;
2872 Ok(frame)
2873 }
2874 Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
2875 };
2876 }
2877 GethDebugBuiltInTracerType::PreStateTracer => {
2878 return match tracer_config.into_pre_state_config() {
2879 Ok(pre_state_config) => {
2880 let inspector = TracingInspector::new(
2881 TracingInspectorConfig::from_geth_prestate_config(
2882 &pre_state_config,
2883 ),
2884 );
2885 let frame = self.replay_tx_with_inspector(
2886 tx.info.transaction_hash,
2887 inspector,
2888 |state, db, inspector, _, _| {
2889 inspector.geth_builder().geth_prestate_traces(
2890 &state,
2891 &pre_state_config,
2892 db,
2893 )
2894 },
2895 )??;
2896 Ok(frame.into())
2897 }
2898 Err(e) => Err(RpcError::invalid_params(e.to_string()).into()),
2899 };
2900 }
2901 GethDebugBuiltInTracerType::NoopTracer
2902 | GethDebugBuiltInTracerType::MuxTracer
2903 | GethDebugBuiltInTracerType::Erc7562Tracer
2904 | GethDebugBuiltInTracerType::FlatCallTracer => {}
2905 },
2906 GethDebugTracerType::JsTracer(_code) => {}
2907 }
2908
2909 return Ok(NoopFrame::default().into());
2910 }
2911
2912 Ok(GethTraceBuilder::new(tx.info.traces.clone())
2914 .geth_traces(
2915 tx.receipt.cumulative_gas_used(),
2916 tx.info.out.clone().unwrap_or_default(),
2917 config,
2918 )
2919 .into())
2920 }
2921
2922 async fn mined_geth_trace_transaction(
2923 &self,
2924 hash: B256,
2925 opts: GethDebugTracingOptions,
2926 ) -> Option<Result<GethTrace, BlockchainError>> {
2927 self.blockchain.storage.read().transactions.get(&hash).map(|tx| self.geth_trace(tx, opts))
2928 }
2929
2930 pub async fn trace_block(
2932 &self,
2933 block: BlockNumber,
2934 ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
2935 let number = self.convert_block_number(Some(block));
2936 if let Some(traces) = self.mined_parity_trace_block(number) {
2937 return Ok(traces);
2938 }
2939
2940 if let Some(fork) = self.get_fork()
2941 && fork.predates_fork(number)
2942 {
2943 return Ok(fork.trace_block(number).await?);
2944 }
2945
2946 Ok(vec![])
2947 }
2948
2949 pub async fn trace_replay_block_transactions(
2951 &self,
2952 block: BlockNumber,
2953 trace_types: HashSet<TraceType>,
2954 ) -> Result<Vec<TraceResultsWithTransactionHash>, BlockchainError> {
2955 let block_number = self.convert_block_number(Some(block));
2956
2957 if let Some(results) =
2959 self.mined_parity_trace_replay_block_transactions(block_number, &trace_types)
2960 {
2961 return Ok(results);
2962 }
2963
2964 if let Some(fork) = self.get_fork()
2966 && fork.predates_fork(block_number)
2967 {
2968 return Ok(fork.trace_replay_block_transactions(block_number, trace_types).await?);
2969 }
2970
2971 Ok(vec![])
2972 }
2973
2974 fn mined_parity_trace_replay_block_transactions(
2976 &self,
2977 block_number: u64,
2978 trace_types: &HashSet<TraceType>,
2979 ) -> Option<Vec<TraceResultsWithTransactionHash>> {
2980 let block = self.get_block(block_number)?;
2981
2982 let parent_hash = block.header.parent_hash;
2984 let trace_config = TracingInspectorConfig::from_parity_config(trace_types);
2985
2986 let read_guard = self.states.upgradable_read();
2987 if let Some(state) = read_guard.get_state(&parent_hash) {
2988 self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types)
2989 } else {
2990 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
2991 let state = write_guard.get_on_disk_state(&parent_hash)?;
2992 self.replay_block_transactions_with_inspector(&block, state, trace_config, trace_types)
2993 }
2994 }
2995
2996 fn replay_block_transactions_with_inspector(
2998 &self,
2999 block: &Block,
3000 parent_state: &StateDb,
3001 trace_config: TracingInspectorConfig,
3002 trace_types: &HashSet<TraceType>,
3003 ) -> Option<Vec<TraceResultsWithTransactionHash>> {
3004 let mut cache_db = CacheDB::new(Box::new(parent_state));
3005 let mut results = Vec::new();
3006
3007 let mut env = self.env.read().clone();
3009 env.evm_env.block_env = BlockEnv {
3010 number: U256::from(block.header.number),
3011 beneficiary: block.header.beneficiary,
3012 timestamp: U256::from(block.header.timestamp),
3013 difficulty: block.header.difficulty,
3014 prevrandao: Some(block.header.mix_hash),
3015 basefee: block.header.base_fee_per_gas.unwrap_or_default(),
3016 gas_limit: block.header.gas_limit,
3017 ..Default::default()
3018 };
3019
3020 for tx_envelope in &block.body.transactions {
3022 let tx_hash = tx_envelope.hash();
3023
3024 let mut inspector = TracingInspector::new(trace_config);
3026
3027 let pending_tx =
3029 PendingTransaction::from_maybe_impersonated(tx_envelope.clone()).ok()?;
3030 let mut tx_env: OpTransaction<TxEnv> = FromRecoveredTx::from_recovered_tx(
3031 pending_tx.transaction.as_ref(),
3032 *pending_tx.sender(),
3033 );
3034 if env.networks.is_optimism() {
3035 tx_env.enveloped_tx = Some(pending_tx.transaction.encoded_2718().into());
3036 }
3037
3038 let mut evm = self.new_evm_with_inspector_ref(&cache_db, &env, &mut inspector);
3040 let result = evm.transact(tx_env.clone()).ok()?;
3041
3042 let full_trace = inspector
3044 .into_parity_builder()
3045 .into_trace_results_with_state(&result, trace_types, &cache_db)
3046 .ok()?;
3047
3048 results.push(TraceResultsWithTransactionHash { transaction_hash: tx_hash, full_trace });
3049
3050 cache_db.commit(result.state);
3052 }
3053
3054 Some(results)
3055 }
3056
3057 pub async fn transaction_receipt(
3058 &self,
3059 hash: B256,
3060 ) -> Result<Option<FoundryTxReceipt>, BlockchainError> {
3061 if let Some(receipt) = self.mined_transaction_receipt(hash) {
3062 return Ok(Some(receipt.inner));
3063 }
3064
3065 if let Some(fork) = self.get_fork() {
3066 let receipt = fork.transaction_receipt(hash).await?;
3067 let number = self.convert_block_number(
3068 receipt.clone().and_then(|r| r.block_number()).map(BlockNumber::from),
3069 );
3070
3071 if fork.predates_fork_inclusive(number) {
3072 return Ok(receipt);
3073 }
3074 }
3075
3076 Ok(None)
3077 }
3078
3079 pub async fn trace_filter(
3081 &self,
3082 filter: TraceFilter,
3083 ) -> Result<Vec<LocalizedTransactionTrace>, BlockchainError> {
3084 let matcher = filter.matcher();
3085 let start = filter.from_block.unwrap_or(0);
3086 let end = filter.to_block.unwrap_or_else(|| self.best_number());
3087
3088 if start > end {
3089 return Err(BlockchainError::RpcError(RpcError::invalid_params(
3090 "invalid block range, ensure that to block is greater than from block".to_string(),
3091 )));
3092 }
3093
3094 let dist = end - start;
3095 if dist > 300 {
3096 return Err(BlockchainError::RpcError(RpcError::invalid_params(
3097 "block range too large, currently limited to 300".to_string(),
3098 )));
3099 }
3100
3101 let mut trace_tasks = vec![];
3103 for num in start..=end {
3104 trace_tasks.push(self.trace_block(num.into()));
3105 }
3106
3107 let traces = futures::future::try_join_all(trace_tasks).await?;
3109 let filtered_traces =
3110 traces.into_iter().flatten().filter(|trace| matcher.matches(&trace.trace));
3111
3112 let filtered_traces: Vec<_> = if let Some(after) = filter.after {
3114 filtered_traces.skip(after as usize).collect()
3115 } else {
3116 filtered_traces.collect()
3117 };
3118
3119 let filtered_traces: Vec<_> = if let Some(count) = filter.count {
3120 filtered_traces.into_iter().take(count as usize).collect()
3121 } else {
3122 filtered_traces
3123 };
3124
3125 Ok(filtered_traces)
3126 }
3127
3128 pub fn mined_receipts(&self, hash: B256) -> Option<Vec<FoundryReceiptEnvelope>> {
3130 let block = self.mined_block_by_hash(hash)?;
3131 let mut receipts = Vec::new();
3132 let storage = self.blockchain.storage.read();
3133 for tx in block.transactions.hashes() {
3134 let receipt = storage.transactions.get(&tx)?.receipt.clone();
3135 receipts.push(receipt);
3136 }
3137 Some(receipts)
3138 }
3139
3140 pub fn mined_block_receipts(&self, id: impl Into<BlockId>) -> Option<Vec<FoundryTxReceipt>> {
3142 let mut receipts = Vec::new();
3143 let block = self.get_block(id)?;
3144
3145 for transaction in block.body.transactions {
3146 let receipt = self.mined_transaction_receipt(transaction.hash())?;
3147 receipts.push(receipt.inner);
3148 }
3149
3150 Some(receipts)
3151 }
3152
3153 pub(crate) fn mined_transaction_receipt(&self, hash: B256) -> Option<MinedTransactionReceipt> {
3155 let MinedTransaction { info, receipt: tx_receipt, block_hash, .. } =
3156 self.blockchain.get_transaction_by_hash(&hash)?;
3157
3158 let index = info.transaction_index as usize;
3159 let block = self.blockchain.get_block_by_hash(&block_hash)?;
3160 let transaction = block.body.transactions[index].clone();
3161
3162 let excess_blob_gas = block.header.excess_blob_gas;
3164 let blob_gas_price =
3165 alloy_eips::eip4844::calc_blob_gasprice(excess_blob_gas.unwrap_or_default());
3166 let blob_gas_used = transaction.blob_gas_used();
3167
3168 let effective_gas_price = transaction.effective_gas_price(block.header.base_fee_per_gas);
3169
3170 let receipts = self.get_receipts(block.body.transactions.iter().map(|tx| tx.hash()));
3171 let next_log_index = receipts[..index].iter().map(|r| r.logs().len()).sum::<usize>();
3172
3173 let tx_receipt = tx_receipt.convert_logs_rpc(
3174 BlockNumHash::new(block.header.number, block_hash),
3175 block.header.timestamp,
3176 info.transaction_hash,
3177 info.transaction_index,
3178 next_log_index,
3179 );
3180
3181 let receipt = TransactionReceipt {
3182 inner: tx_receipt,
3183 transaction_hash: info.transaction_hash,
3184 transaction_index: Some(info.transaction_index),
3185 block_number: Some(block.header.number),
3186 gas_used: info.gas_used,
3187 contract_address: info.contract_address,
3188 effective_gas_price,
3189 block_hash: Some(block_hash),
3190 from: info.from,
3191 to: info.to,
3192 blob_gas_price: Some(blob_gas_price),
3193 blob_gas_used,
3194 };
3195
3196 let inner = FoundryTxReceipt::with_timestamp(receipt, block.header.timestamp);
3198 Some(MinedTransactionReceipt { inner, out: info.out })
3199 }
3200
3201 pub async fn block_receipts(
3203 &self,
3204 number: BlockId,
3205 ) -> Result<Option<Vec<FoundryTxReceipt>>, BlockchainError> {
3206 if let Some(receipts) = self.mined_block_receipts(number) {
3207 return Ok(Some(receipts));
3208 }
3209
3210 if let Some(fork) = self.get_fork() {
3211 let number = match self.ensure_block_number(Some(number)).await {
3212 Err(_) => return Ok(None),
3213 Ok(n) => n,
3214 };
3215
3216 if fork.predates_fork_inclusive(number) {
3217 let receipts = fork.block_receipts(number).await?;
3218
3219 return Ok(receipts);
3220 }
3221 }
3222
3223 Ok(None)
3224 }
3225
3226 pub async fn transaction_by_block_number_and_index(
3227 &self,
3228 number: BlockNumber,
3229 index: Index,
3230 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
3231 if let Some(block) = self.mined_block_by_number(number) {
3232 return Ok(self.mined_transaction_by_block_hash_and_index(block.header.hash, index));
3233 }
3234
3235 if let Some(fork) = self.get_fork() {
3236 let number = self.convert_block_number(Some(number));
3237 if fork.predates_fork(number) {
3238 return Ok(fork
3239 .transaction_by_block_number_and_index(number, index.into())
3240 .await?);
3241 }
3242 }
3243
3244 Ok(None)
3245 }
3246
3247 pub async fn transaction_by_block_hash_and_index(
3248 &self,
3249 hash: B256,
3250 index: Index,
3251 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
3252 if let tx @ Some(_) = self.mined_transaction_by_block_hash_and_index(hash, index) {
3253 return Ok(tx);
3254 }
3255
3256 if let Some(fork) = self.get_fork() {
3257 return Ok(fork.transaction_by_block_hash_and_index(hash, index.into()).await?);
3258 }
3259
3260 Ok(None)
3261 }
3262
3263 pub fn mined_transaction_by_block_hash_and_index(
3264 &self,
3265 block_hash: B256,
3266 index: Index,
3267 ) -> Option<AnyRpcTransaction> {
3268 let (info, block, tx) = {
3269 let storage = self.blockchain.storage.read();
3270 let block = storage.blocks.get(&block_hash).cloned()?;
3271 let index: usize = index.into();
3272 let tx = block.body.transactions.get(index)?.clone();
3273 let info = storage.transactions.get(&tx.hash())?.info.clone();
3274 (info, block, tx)
3275 };
3276
3277 Some(transaction_build(
3278 Some(info.transaction_hash),
3279 tx,
3280 Some(&block),
3281 Some(info),
3282 block.header.base_fee_per_gas,
3283 ))
3284 }
3285
3286 pub async fn transaction_by_hash(
3287 &self,
3288 hash: B256,
3289 ) -> Result<Option<AnyRpcTransaction>, BlockchainError> {
3290 trace!(target: "backend", "transaction_by_hash={:?}", hash);
3291 if let tx @ Some(_) = self.mined_transaction_by_hash(hash) {
3292 return Ok(tx);
3293 }
3294
3295 if let Some(fork) = self.get_fork() {
3296 return fork
3297 .transaction_by_hash(hash)
3298 .await
3299 .map_err(BlockchainError::AlloyForkProvider);
3300 }
3301
3302 Ok(None)
3303 }
3304
3305 pub fn mined_transaction_by_hash(&self, hash: B256) -> Option<AnyRpcTransaction> {
3306 let (info, block) = {
3307 let storage = self.blockchain.storage.read();
3308 let MinedTransaction { info, block_hash, .. } =
3309 storage.transactions.get(&hash)?.clone();
3310 let block = storage.blocks.get(&block_hash).cloned()?;
3311 (info, block)
3312 };
3313 let tx = block.body.transactions.get(info.transaction_index as usize)?.clone();
3314
3315 Some(transaction_build(
3316 Some(info.transaction_hash),
3317 tx,
3318 Some(&block),
3319 Some(info),
3320 block.header.base_fee_per_gas,
3321 ))
3322 }
3323
3324 pub fn get_blob_by_tx_hash(&self, hash: B256) -> Result<Option<Vec<alloy_consensus::Blob>>> {
3325 if let Some(tx) = self.mined_transaction_by_hash(hash)
3327 && let Ok(typed_tx) = FoundryTxEnvelope::try_from(tx)
3328 && let Some(sidecar) = typed_tx.sidecar()
3329 {
3330 return Ok(Some(sidecar.sidecar.blobs().to_vec()));
3331 }
3332
3333 Ok(None)
3334 }
3335
3336 pub fn get_blobs_by_block_id(
3337 &self,
3338 id: impl Into<BlockId>,
3339 versioned_hashes: Vec<B256>,
3340 ) -> Result<Option<Vec<alloy_consensus::Blob>>> {
3341 Ok(self.get_block(id).map(|block| {
3342 block
3343 .body
3344 .transactions
3345 .iter()
3346 .filter_map(|tx| tx.as_ref().sidecar())
3347 .flat_map(|sidecar| {
3348 sidecar.sidecar.blobs().iter().zip(sidecar.sidecar.commitments().iter())
3349 })
3350 .filter(|(_, commitment)| {
3351 versioned_hashes.is_empty()
3353 || versioned_hashes.contains(&kzg_to_versioned_hash(commitment.as_slice()))
3354 })
3355 .map(|(blob, _)| *blob)
3356 .collect()
3357 }))
3358 }
3359
3360 pub fn get_blob_by_versioned_hash(&self, hash: B256) -> Result<Option<Blob>> {
3361 let storage = self.blockchain.storage.read();
3362 for block in storage.blocks.values() {
3363 for tx in &block.body.transactions {
3364 let typed_tx = tx.as_ref();
3365 if let Some(sidecar) = typed_tx.sidecar() {
3366 for versioned_hash in sidecar.sidecar.versioned_hashes() {
3367 if versioned_hash == hash
3368 && let Some(index) =
3369 sidecar.sidecar.commitments().iter().position(|commitment| {
3370 kzg_to_versioned_hash(commitment.as_slice()) == *hash
3371 })
3372 && let Some(blob) = sidecar.sidecar.blobs().get(index)
3373 {
3374 return Ok(Some(*blob));
3375 }
3376 }
3377 }
3378 }
3379 }
3380 Ok(None)
3381 }
3382
3383 pub async fn impersonate_signature(
3385 &self,
3386 signature: Bytes,
3387 address: Address,
3388 ) -> Result<(), BlockchainError> {
3389 self.cheats.add_recover_override(signature, address);
3390 Ok(())
3391 }
3392
3393 pub async fn prove_account_at(
3397 &self,
3398 address: Address,
3399 keys: Vec<B256>,
3400 block_request: Option<BlockRequest>,
3401 ) -> Result<AccountProof, BlockchainError> {
3402 let block_number = block_request.as_ref().map(|r| r.block_number());
3403
3404 self.with_database_at(block_request, |block_db, _| {
3405 trace!(target: "backend", "get proof for {:?} at {:?}", address, block_number);
3406 let db = block_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
3407 let account = db.get(&address).cloned().unwrap_or_default();
3408
3409 let mut builder = HashBuilder::default()
3410 .with_proof_retainer(ProofRetainer::new(vec![Nibbles::unpack(keccak256(address))]));
3411
3412 for (key, account) in trie_accounts(db) {
3413 builder.add_leaf(key, &account);
3414 }
3415
3416 let _ = builder.root();
3417
3418 let proof = builder
3419 .take_proof_nodes()
3420 .into_nodes_sorted()
3421 .into_iter()
3422 .map(|(_, v)| v)
3423 .collect();
3424 let (storage_hash, storage_proofs) = prove_storage(&account.storage, &keys);
3425
3426 let account_proof = AccountProof {
3427 address,
3428 balance: account.info.balance,
3429 nonce: account.info.nonce,
3430 code_hash: account.info.code_hash,
3431 storage_hash,
3432 account_proof: proof,
3433 storage_proof: keys
3434 .into_iter()
3435 .zip(storage_proofs)
3436 .map(|(key, proof)| {
3437 let storage_key: U256 = key.into();
3438 let value = account.storage.get(&storage_key).copied().unwrap_or_default();
3439 StorageProof { key: JsonStorageKey::Hash(key), value, proof }
3440 })
3441 .collect(),
3442 };
3443
3444 Ok(account_proof)
3445 })
3446 .await?
3447 }
3448
3449 pub fn new_block_notifications(&self) -> NewBlockNotifications {
3451 let (tx, rx) = unbounded();
3452 self.new_block_listeners.lock().push(tx);
3453 trace!(target: "backed", "added new block listener");
3454 rx
3455 }
3456
3457 fn notify_on_new_block(&self, header: Header, hash: B256) {
3459 self.new_block_listeners.lock().retain(|tx| !tx.is_closed());
3462
3463 let notification = NewBlockNotification { hash, header: Arc::new(header) };
3464
3465 self.new_block_listeners
3466 .lock()
3467 .retain(|tx| tx.unbounded_send(notification.clone()).is_ok());
3468 }
3469
3470 pub async fn reorg(
3477 &self,
3478 depth: u64,
3479 tx_pairs: HashMap<u64, Vec<Arc<PoolTransaction>>>,
3480 common_block: Block,
3481 ) -> Result<(), BlockchainError> {
3482 self.rollback(common_block).await?;
3483 for i in 0..depth {
3485 let to_be_mined = tx_pairs.get(&i).cloned().unwrap_or_else(Vec::new);
3486 let outcome = self.do_mine_block(to_be_mined).await;
3487 node_info!(
3488 " Mined reorg block number {}. With {} valid txs and with invalid {} txs",
3489 outcome.block_number,
3490 outcome.included.len(),
3491 outcome.invalid.len()
3492 );
3493 }
3494
3495 Ok(())
3496 }
3497
3498 pub async fn rollback(&self, common_block: Block) -> Result<(), BlockchainError> {
3503 let common_state = {
3505 let return_state_or_throw_err =
3506 |db: Option<&StateDb>| -> Result<AddressMap<DbAccount>, BlockchainError> {
3507 let state_db = db.ok_or(BlockchainError::DataUnavailable)?;
3508 let db_full =
3509 state_db.maybe_as_full_db().ok_or(BlockchainError::DataUnavailable)?;
3510 Ok(db_full.clone())
3511 };
3512
3513 let hash = &common_block.header.hash_slow();
3514 let read_guard = self.states.upgradable_read();
3515 if let Some(db) = read_guard.get_state(hash) {
3516 return_state_or_throw_err(Some(db))?
3517 } else {
3518 let mut write_guard = RwLockUpgradableReadGuard::upgrade(read_guard);
3519 return_state_or_throw_err(write_guard.get_on_disk_state(hash))?
3520 }
3521 };
3522
3523 {
3524 self.blockchain
3526 .storage
3527 .write()
3528 .unwind_to(common_block.header.number, common_block.header.hash_slow());
3529
3530 let mut env = self.env.write();
3532 env.evm_env.block_env.number = U256::from(common_block.header.number);
3533 env.evm_env.block_env.timestamp = U256::from(common_block.header.timestamp);
3534 env.evm_env.block_env.gas_limit = common_block.header.gas_limit;
3535 env.evm_env.block_env.difficulty = common_block.header.difficulty;
3536 env.evm_env.block_env.prevrandao = Some(common_block.header.mix_hash);
3537
3538 self.time.reset(env.evm_env.block_env.timestamp.saturating_to());
3539 }
3540
3541 {
3542 let block_hashes: Vec<_> = {
3546 let storage = self.blockchain.storage.read();
3547 let min_block = common_block.header.number.saturating_sub(256);
3548 storage
3549 .hashes
3550 .iter()
3551 .filter(|(num, _)| **num >= min_block)
3552 .map(|(&num, &hash)| (num, hash))
3553 .collect()
3554 };
3555
3556 let mut db = self.db.write().await;
3558 db.clear();
3559
3560 for (address, acc) in common_state {
3562 db.insert_account(address, acc.info);
3563 for (key, value) in acc.storage {
3564 db.set_storage_at(address, key.into(), value.into())?;
3565 }
3566 }
3567
3568 for (block_num, hash) in block_hashes {
3571 db.insert_block_hash(U256::from(block_num), hash);
3572 }
3573 }
3574
3575 Ok(())
3576 }
3577}
3578
3579fn get_block_env<F, T>(state: &StateDb, block_number: u64, block: AnyRpcBlock, f: F) -> T
3580where
3581 F: FnOnce(Box<dyn MaybeFullDatabase + '_>, BlockEnv) -> T,
3582{
3583 let block = BlockEnv {
3584 number: U256::from(block_number),
3585 beneficiary: block.header.beneficiary,
3586 timestamp: U256::from(block.header.timestamp),
3587 difficulty: block.header.difficulty,
3588 prevrandao: block.header.mix_hash,
3589 basefee: block.header.base_fee_per_gas.unwrap_or_default(),
3590 gas_limit: block.header.gas_limit,
3591 ..Default::default()
3592 };
3593 f(Box::new(state), block)
3594}
3595
3596fn get_pool_transactions_nonce(
3598 pool_transactions: &[Arc<PoolTransaction>],
3599 address: Address,
3600) -> Option<u64> {
3601 if let Some(highest_nonce) = pool_transactions
3602 .iter()
3603 .filter(|tx| *tx.pending_transaction.sender() == address)
3604 .map(|tx| tx.pending_transaction.nonce())
3605 .max()
3606 {
3607 let tx_count = highest_nonce.saturating_add(1);
3608 return Some(tx_count);
3609 }
3610 None
3611}
3612
3613#[async_trait::async_trait]
3614impl TransactionValidator for Backend {
3615 async fn validate_pool_transaction(
3616 &self,
3617 tx: &PendingTransaction,
3618 ) -> Result<(), BlockchainError> {
3619 let address = *tx.sender();
3620 let account = self.get_account(address).await?;
3621 let env = self.next_env();
3622 Ok(self.validate_pool_transaction_for(tx, &account, &env)?)
3623 }
3624
3625 fn validate_pool_transaction_for(
3626 &self,
3627 pending: &PendingTransaction,
3628 account: &AccountInfo,
3629 env: &Env,
3630 ) -> Result<(), InvalidTransactionError> {
3631 let tx = &pending.transaction;
3632
3633 if let Some(tx_chain_id) = tx.chain_id() {
3634 let chain_id = self.chain_id();
3635 if chain_id.to::<u64>() != tx_chain_id {
3636 if let FoundryTxEnvelope::Legacy(tx) = tx.as_ref() {
3637 if env.evm_env.cfg_env.spec >= SpecId::SPURIOUS_DRAGON
3639 && tx.chain_id().is_none()
3640 {
3641 warn!(target: "backend", ?chain_id, ?tx_chain_id, "incompatible EIP155-based V");
3642 return Err(InvalidTransactionError::IncompatibleEIP155);
3643 }
3644 } else {
3645 warn!(target: "backend", ?chain_id, ?tx_chain_id, "invalid chain id");
3646 return Err(InvalidTransactionError::InvalidChainId);
3647 }
3648 }
3649 }
3650
3651 let is_deposit_tx = matches!(pending.transaction.as_ref(), FoundryTxEnvelope::Deposit(_));
3653 let nonce = tx.nonce();
3654 if nonce < account.nonce && !is_deposit_tx {
3655 warn!(target: "backend", "[{:?}] nonce too low", tx.hash());
3656 return Err(InvalidTransactionError::NonceTooLow);
3657 }
3658
3659 if env.evm_env.cfg_env.spec >= SpecId::CANCUN && tx.is_eip4844() {
3661 let blob_tx = match tx.as_ref() {
3663 FoundryTxEnvelope::Eip4844(tx) => tx.tx(),
3664 _ => unreachable!(),
3665 };
3666
3667 let blob_count = blob_tx.tx().blob_versioned_hashes.len();
3668
3669 if blob_count == 0 {
3671 return Err(InvalidTransactionError::NoBlobHashes);
3672 }
3673
3674 let max_blob_count = self.blob_params().max_blob_count as usize;
3676 if blob_count > max_blob_count {
3677 return Err(InvalidTransactionError::TooManyBlobs(blob_count, max_blob_count));
3678 }
3679
3680 if !self.skip_blob_validation(Some(*pending.sender()))
3682 && let Err(err) = blob_tx.validate(EnvKzgSettings::default().get())
3683 {
3684 return Err(InvalidTransactionError::BlobTransactionValidationError(err));
3685 }
3686 }
3687
3688 if !self.disable_pool_balance_checks {
3690 if tx.gas_limit() < MIN_TRANSACTION_GAS as u64 {
3692 warn!(target: "backend", "[{:?}] gas too low", tx.hash());
3693 return Err(InvalidTransactionError::GasTooLow);
3694 }
3695
3696 if !env.evm_env.cfg_env.disable_block_gas_limit
3698 && tx.gas_limit() > env.evm_env.block_env.gas_limit
3699 {
3700 warn!(target: "backend", "[{:?}] gas too high", tx.hash());
3701 return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
3702 detail: String::from("tx.gas_limit > env.block.gas_limit"),
3703 }));
3704 }
3705
3706 if env.evm_env.cfg_env.tx_gas_limit_cap.is_none()
3708 && tx.gas_limit() > env.evm_env.cfg_env().tx_gas_limit_cap()
3709 {
3710 warn!(target: "backend", "[{:?}] gas too high", tx.hash());
3711 return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
3712 detail: String::from("tx.gas_limit > env.cfg.tx_gas_limit_cap"),
3713 }));
3714 }
3715
3716 if env.evm_env.cfg_env.spec >= SpecId::LONDON {
3718 if tx.max_fee_per_gas() < env.evm_env.block_env.basefee.into() && !is_deposit_tx {
3719 warn!(target: "backend", "max fee per gas={}, too low, block basefee={}", tx.max_fee_per_gas(), env.evm_env.block_env.basefee);
3720 return Err(InvalidTransactionError::FeeCapTooLow);
3721 }
3722
3723 if let (Some(max_priority_fee_per_gas), max_fee_per_gas) =
3724 (tx.as_ref().max_priority_fee_per_gas(), tx.as_ref().max_fee_per_gas())
3725 && max_priority_fee_per_gas > max_fee_per_gas
3726 {
3727 warn!(target: "backend", "max priority fee per gas={}, too high, max fee per gas={}", max_priority_fee_per_gas, max_fee_per_gas);
3728 return Err(InvalidTransactionError::TipAboveFeeCap);
3729 }
3730 }
3731
3732 if env.evm_env.cfg_env.spec >= SpecId::CANCUN
3734 && tx.is_eip4844()
3735 && let Some(max_fee_per_blob_gas) = tx.max_fee_per_blob_gas()
3736 && let Some(blob_gas_and_price) = &env.evm_env.block_env.blob_excess_gas_and_price
3737 && max_fee_per_blob_gas < blob_gas_and_price.blob_gasprice
3738 {
3739 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);
3740 return Err(InvalidTransactionError::BlobFeeCapTooLow(
3741 max_fee_per_blob_gas,
3742 blob_gas_and_price.blob_gasprice,
3743 ));
3744 }
3745
3746 let max_cost =
3747 (tx.gas_limit() as u128).saturating_mul(tx.max_fee_per_gas()).saturating_add(
3748 tx.blob_gas_used()
3749 .map(|g| g as u128)
3750 .unwrap_or(0)
3751 .mul(tx.max_fee_per_blob_gas().unwrap_or(0)),
3752 );
3753 let value = tx.value();
3754 match tx.as_ref() {
3755 FoundryTxEnvelope::Deposit(deposit_tx) => {
3756 if value > account.balance + U256::from(deposit_tx.mint) {
3762 warn!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance + U256::from(deposit_tx.mint), value, *pending.sender());
3763 return Err(InvalidTransactionError::InsufficientFunds);
3764 }
3765 }
3766 _ => {
3767 let req_funds =
3769 max_cost.checked_add(value.saturating_to()).ok_or_else(|| {
3770 warn!(target: "backend", "[{:?}] cost too high", tx.hash());
3771 InvalidTransactionError::InsufficientFunds
3772 })?;
3773 if account.balance < U256::from(req_funds) {
3774 warn!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender());
3775 return Err(InvalidTransactionError::InsufficientFunds);
3776 }
3777 }
3778 }
3779 }
3780 Ok(())
3781 }
3782
3783 fn validate_for(
3784 &self,
3785 tx: &PendingTransaction,
3786 account: &AccountInfo,
3787 env: &Env,
3788 ) -> Result<(), InvalidTransactionError> {
3789 self.validate_pool_transaction_for(tx, account, env)?;
3790 if tx.nonce() > account.nonce {
3791 return Err(InvalidTransactionError::NonceTooHigh);
3792 }
3793 Ok(())
3794 }
3795}
3796
3797pub fn transaction_build(
3799 tx_hash: Option<B256>,
3800 eth_transaction: MaybeImpersonatedTransaction,
3801 block: Option<&Block>,
3802 info: Option<TransactionInfo>,
3803 base_fee: Option<u64>,
3804) -> AnyRpcTransaction {
3805 if let FoundryTxEnvelope::Deposit(deposit_tx) = eth_transaction.as_ref() {
3806 let dep_tx = deposit_tx;
3807
3808 let ser = serde_json::to_value(dep_tx).expect("could not serialize TxDeposit");
3809 let maybe_deposit_fields = OtherFields::try_from(ser);
3810
3811 match maybe_deposit_fields {
3812 Ok(mut fields) => {
3813 fields.insert("v".to_string(), serde_json::to_value("0x0").unwrap());
3816 fields.insert("r".to_string(), serde_json::to_value(B256::ZERO).unwrap());
3817 fields.insert(String::from("s"), serde_json::to_value(B256::ZERO).unwrap());
3818 fields.insert(String::from("nonce"), serde_json::to_value("0x0").unwrap());
3819
3820 let inner = UnknownTypedTransaction {
3821 ty: AnyTxType(DEPOSIT_TX_TYPE_ID),
3822 fields,
3823 memo: Default::default(),
3824 };
3825
3826 let envelope = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
3827 hash: eth_transaction.hash(),
3828 inner,
3829 });
3830
3831 let tx = Transaction {
3832 inner: Recovered::new_unchecked(envelope, deposit_tx.from),
3833 block_hash: block
3834 .as_ref()
3835 .map(|block| B256::from(keccak256(alloy_rlp::encode(&block.header)))),
3836 block_number: block.as_ref().map(|block| block.header.number),
3837 transaction_index: info.as_ref().map(|info| info.transaction_index),
3838 effective_gas_price: None,
3839 };
3840
3841 return AnyRpcTransaction::from(WithOtherFields::new(tx));
3842 }
3843 Err(_) => {
3844 error!(target: "backend", "failed to serialize deposit transaction");
3845 }
3846 }
3847 }
3848
3849 let transaction = eth_transaction.into_rpc_transaction();
3850 let effective_gas_price = transaction.effective_gas_price(base_fee);
3851
3852 let envelope = transaction.inner;
3853 let from = envelope.signer();
3854
3855 let hash = tx_hash.unwrap_or(*envelope.tx_hash());
3861
3862 let envelope = match envelope.into_inner() {
3863 TxEnvelope::Legacy(signed_tx) => {
3864 let (t, sig, _) = signed_tx.into_parts();
3865 let new_signed = Signed::new_unchecked(t, sig, hash);
3866 AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(new_signed))
3867 }
3868 TxEnvelope::Eip1559(signed_tx) => {
3869 let (t, sig, _) = signed_tx.into_parts();
3870 let new_signed = Signed::new_unchecked(t, sig, hash);
3871 AnyTxEnvelope::Ethereum(TxEnvelope::Eip1559(new_signed))
3872 }
3873 TxEnvelope::Eip2930(signed_tx) => {
3874 let (t, sig, _) = signed_tx.into_parts();
3875 let new_signed = Signed::new_unchecked(t, sig, hash);
3876 AnyTxEnvelope::Ethereum(TxEnvelope::Eip2930(new_signed))
3877 }
3878 TxEnvelope::Eip4844(signed_tx) => {
3879 let (t, sig, _) = signed_tx.into_parts();
3880 let new_signed = Signed::new_unchecked(t, sig, hash);
3881 AnyTxEnvelope::Ethereum(TxEnvelope::Eip4844(new_signed))
3882 }
3883 TxEnvelope::Eip7702(signed_tx) => {
3884 let (t, sig, _) = signed_tx.into_parts();
3885 let new_signed = Signed::new_unchecked(t, sig, hash);
3886 AnyTxEnvelope::Ethereum(TxEnvelope::Eip7702(new_signed))
3887 }
3888 };
3889
3890 let tx = Transaction {
3891 inner: Recovered::new_unchecked(envelope, from),
3892 block_hash: block.as_ref().map(|block| block.header.hash_slow()),
3893 block_number: block.as_ref().map(|block| block.header.number),
3894 transaction_index: info.as_ref().map(|info| info.transaction_index),
3895 effective_gas_price: Some(effective_gas_price),
3897 };
3898 AnyRpcTransaction::from(WithOtherFields::new(tx))
3899}
3900
3901pub fn prove_storage(storage: &HashMap<U256, U256>, keys: &[B256]) -> (B256, Vec<Vec<Bytes>>) {
3907 let keys: Vec<_> = keys.iter().map(|key| Nibbles::unpack(keccak256(key))).collect();
3908
3909 let mut builder = HashBuilder::default().with_proof_retainer(ProofRetainer::new(keys.clone()));
3910
3911 for (key, value) in trie_storage(storage) {
3912 builder.add_leaf(key, &value);
3913 }
3914
3915 let root = builder.root();
3916
3917 let mut proofs = Vec::new();
3918 let all_proof_nodes = builder.take_proof_nodes();
3919
3920 for proof_key in keys {
3921 let matching_proof_nodes =
3924 all_proof_nodes.matching_nodes_sorted(&proof_key).into_iter().map(|(_, node)| node);
3925 proofs.push(matching_proof_nodes.collect());
3926 }
3927
3928 (root, proofs)
3929}
3930
3931pub fn is_arbitrum(chain_id: u64) -> bool {
3932 if let Ok(chain) = NamedChain::try_from(chain_id) {
3933 return chain.is_arbitrum();
3934 }
3935 false
3936}
3937
3938pub fn op_haltreason_to_instruction_result(op_reason: OpHaltReason) -> InstructionResult {
3939 match op_reason {
3940 OpHaltReason::Base(eth_h) => eth_h.into(),
3941 OpHaltReason::FailedDeposit => InstructionResult::Stop,
3942 }
3943}
3944
3945#[cfg(test)]
3946mod tests {
3947 use crate::{NodeConfig, spawn};
3948
3949 #[tokio::test]
3950 async fn test_deterministic_block_mining() {
3951 let genesis_timestamp = 1743944919u64;
3953
3954 let config_a = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
3956 let config_b = NodeConfig::test().with_genesis_timestamp(genesis_timestamp.into());
3957
3958 let (api_a, _handle_a) = spawn(config_a).await;
3959 let (api_b, _handle_b) = spawn(config_b).await;
3960
3961 let outcome_a_1 = api_a.backend.mine_block(vec![]).await;
3963 let outcome_b_1 = api_b.backend.mine_block(vec![]).await;
3964
3965 assert_eq!(outcome_a_1.block_number, outcome_b_1.block_number);
3967
3968 let block_a_1 =
3970 api_a.block_by_number(outcome_a_1.block_number.into()).await.unwrap().unwrap();
3971 let block_b_1 =
3972 api_b.block_by_number(outcome_b_1.block_number.into()).await.unwrap().unwrap();
3973
3974 assert_eq!(
3976 block_a_1.header.hash, block_b_1.header.hash,
3977 "Block hashes should be deterministic. Got {} vs {}",
3978 block_a_1.header.hash, block_b_1.header.hash
3979 );
3980
3981 let outcome_a_2 = api_a.backend.mine_block(vec![]).await;
3983 let outcome_b_2 = api_b.backend.mine_block(vec![]).await;
3984
3985 let block_a_2 =
3986 api_a.block_by_number(outcome_a_2.block_number.into()).await.unwrap().unwrap();
3987 let block_b_2 =
3988 api_b.block_by_number(outcome_b_2.block_number.into()).await.unwrap().unwrap();
3989
3990 assert_eq!(
3991 block_a_2.header.hash, block_b_2.header.hash,
3992 "Second block hashes should also be deterministic. Got {} vs {}",
3993 block_a_2.header.hash, block_b_2.header.hash
3994 );
3995
3996 assert_ne!(
3998 block_a_1.header.hash, block_a_2.header.hash,
3999 "Different blocks should have different hashes"
4000 );
4001 }
4002}