1use super::{
4 AnvilInspector, Backend, ClientFork, DatabaseRef, EnvelopeExecutionKind, MonadExecutionContext,
5 storage::BlockchainStorage,
6};
7use crate::eth::{
8 backend::{
9 db::MonadBlockReplayProfile,
10 executor::{
11 AnvilBlockExecutor, AnvilTxResult, ExecutedPoolTransactions, PoolTxGasConfig,
12 build_tx_env_for_pending, execute_pool_transactions,
13 },
14 replay::{
15 ExecutedHistoricalReplay, HistoricalReplayTransaction, execute_historical_replay_with,
16 },
17 },
18 error::{BlockchainError, InvalidTransactionError},
19 fees::FeeManager,
20 pool::transactions::PoolTransaction,
21};
22use alloy_consensus::{
23 BlockHeader, Header, Transaction as _, constants::EMPTY_ROOT_HASH, transaction::Recovered,
24};
25use alloy_eips::eip1559::BaseFeeParams;
26use alloy_evm::{
27 Evm, EvmEnv, EvmFactory, RecoveredTx,
28 block::{
29 BalIndexedDatabase, BlockExecutionError, BlockExecutionResult, BlockExecutor, StateDB,
30 },
31};
32use alloy_monad_evm::{MonadContext, MonadEvm, MonadEvmFactory};
33use alloy_network::{BlockResponse, Network};
34use alloy_primitives::{B256, U256};
35use alloy_rpc_types::{AccessList, BlockNumberOrTag as BlockNumber, BlockTransactions};
36use anvil_core::eth::{
37 block::Block,
38 transaction::{MaybeImpersonatedTransaction, PendingTransaction},
39};
40use eyre::{Context, Result};
41use foundry_evm::{
42 backend::DatabaseError,
43 core::{
44 FoundryChain, FromAnyRpcTransaction,
45 evm::{
46 MonadBlockParticipants, monad_block_participants, monad_context_from_participants,
47 protocol_system_call, try_transact_monad_system_replay,
48 },
49 },
50 hardfork::FoundryHardfork,
51 utils::get_blob_params,
52};
53use foundry_primitives::{FoundryReceiptEnvelope, FoundryTxEnvelope};
54use monad_revm::{MonadChainContext, MonadHardfork, instructions::monad_gas_params};
55use revm::{
56 Inspector,
57 context::{Transaction, TxEnv},
58 context_interface::{
59 block::BlobExcessGasAndPrice,
60 result::{HaltReason, InvalidTransaction, ResultAndState},
61 transaction::AuthorizationTr,
62 },
63 database_interface::WrapDatabaseRef,
64 primitives::hardfork::SpecId,
65 state::AccountInfo,
66};
67use std::sync::Arc;
68use tracing::debug;
69
70pub(super) struct PreparedExecution {
72 pub(super) context: MonadChainContext,
73 pub(super) kind: EnvelopeExecutionKind,
74 pub(super) hardfork: MonadHardfork,
75}
76
77pub(super) struct ForkReplay {
78 hardfork: FoundryHardfork,
79 context: Option<MonadChainContext>,
80 participants: MonadBlockParticipants,
81 execution_chain_id: u64,
82 source_chain_id: u64,
83 timestamp: u64,
84 inferred_hardfork: bool,
85}
86
87impl ForkReplay {
88 pub(super) const fn hardfork(&self) -> FoundryHardfork {
89 self.hardfork
90 }
91
92 pub(super) const fn take_context(&mut self) -> Option<MonadChainContext> {
93 self.context.take()
94 }
95
96 pub(super) fn store_metadata<N: Network>(
97 &mut self,
98 storage: &mut BlockchainStorage<N>,
99 block_hash: B256,
100 ) {
101 store_block_metadata(
102 storage,
103 block_hash,
104 std::mem::take(&mut self.participants),
105 self.execution_chain_id,
106 self.hardfork,
107 );
108 }
109}
110
111pub(super) struct MonadRollbackProfile {
113 hardfork: FoundryHardfork,
114 fees: FeeManager,
115 block_blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
116 publish_inferred_hardfork: bool,
117}
118
119impl MonadRollbackProfile {
120 pub(super) fn apply_env(&self, env: &mut EvmEnv) {
122 env.cfg_env.set_spec_and_mainnet_gas_params(SpecId::from(self.hardfork));
123 env.block_env.blob_excess_gas_and_price = self.block_blob_excess_gas_and_price;
124 }
125}
126
127pub(super) fn normalize_access_list(
128 mut access_list: AccessList,
129 hardfork: MonadHardfork,
130) -> AccessList {
131 if MonadHardfork::MonadTen.is_enabled_in(hardfork) {
132 for item in &mut access_list.0 {
133 item.storage_keys.sort_unstable();
134 item.storage_keys.dedup_by_key(|slot| {
135 monad_revm::page::page_index(U256::from_be_slice(slot.as_slice()))
136 });
137 }
138 }
139 access_list
140}
141
142pub(super) async fn cache_fork_context(fork: &ClientFork) -> Result<(), BlockchainError> {
144 let block =
145 fork.block_by_hash_full(fork.block_hash()).await?.ok_or(BlockchainError::BlockNotFound)?;
146 let parent_hash = block.header().parent_hash();
147 if !parent_hash.is_zero() {
148 fork.block_by_hash_full(parent_hash).await?.ok_or(BlockchainError::BlockNotFound)?;
149 }
150 Ok(())
151}
152
153fn execute_pool_transaction<DB>(
155 executor: &mut AnvilBlockExecutor<MonadEvm<DB, AnvilInspector>>,
156 tx_env: TxEnv,
157 recovered: Recovered<FoundryTxEnvelope>,
158 is_replay: bool,
159) -> Result<AnvilTxResult<HaltReason>, BlockExecutionError>
160where
161 DB: StateDB<Error = DatabaseError> + BalIndexedDatabase,
162{
163 prepare_transaction(executor.evm_mut(), &tx_env);
164 let result = (|| {
165 if !is_replay {
166 return executor.execute_transaction_without_commit((tx_env, recovered));
167 }
168 match protocol_system_call(&tx_env) {
169 Ok(None) => return executor.execute_transaction_without_commit((tx_env, recovered)),
170 Ok(Some(_)) => {}
171 Err(err) => return Err(BlockExecutionError::msg(err)),
172 }
173 executor.execute_transaction_without_commit_with(
174 (tx_env, recovered),
175 |evm, tx_env, transaction_hash| {
176 try_transact_monad_system_replay(evm, &tx_env)
177 .map_err(|err| {
178 BlockExecutionError::msg(format!(
179 "failed to replay Monad transaction {transaction_hash}: {err}"
180 ))
181 })?
182 .ok_or_else(|| {
183 BlockExecutionError::msg(format!(
184 "Monad transaction {transaction_hash} is not a canonical replay envelope"
185 ))
186 })
187 },
188 )
189 })();
190 if result.is_err() {
191 rollback_transaction(executor.evm_mut());
192 }
193 result
194}
195
196fn append_transaction(chain: &mut MonadChainContext, tx: &TxEnv) {
198 chain.current_tx_index = chain.current_block_senders.len();
199 chain.current_block_senders.push(tx.caller());
200 chain.current_block_authorities.push(
201 tx.authorization_list().filter_map(|authorization| authorization.authority()).collect(),
202 );
203}
204
205pub(super) fn prepare_transaction<DB: alloy_evm::Database>(
206 evm: &mut MonadEvm<DB, AnvilInspector>,
207 tx: &TxEnv,
208) {
209 append_transaction(&mut evm.ctx_mut().chain, tx);
210}
211
212pub(super) fn resolve_execution_context(
213 context: Option<MonadExecutionContext<'_>>,
214 tx: &TxEnv,
215) -> MonadChainContext {
216 match context {
217 Some(MonadExecutionContext::Exact(context)) => *context,
218 Some(MonadExecutionContext::Next(context)) => {
219 append_transaction(context, tx);
220 context.clone()
221 }
222 None => MonadChainContext::for_transaction(tx),
223 }
224}
225
226pub(super) fn advance_block_context(context: &mut Option<MonadChainContext>) {
227 let Some(context) = context else { return };
228 let current = context
229 .current_block_senders
230 .iter()
231 .copied()
232 .chain(context.current_block_authorities.iter().flatten().copied())
233 .collect();
234 context.grandparent_senders_and_authorities =
235 std::mem::replace(&mut context.parent_senders_and_authorities, current);
236 context.current_block_senders.clear();
237 context.current_block_authorities.clear();
238 context.current_tx_index = 0;
239}
240
241pub(super) fn rollback_transaction<DB: alloy_evm::Database>(
243 evm: &mut MonadEvm<DB, AnvilInspector>,
244) {
245 let chain = &mut evm.ctx_mut().chain;
246 chain.current_block_senders.pop();
247 chain.current_block_authorities.pop();
248 chain.current_tx_index = chain.current_block_senders.len();
249}
250
251impl<N: Network> Backend<N> {
252 pub(crate) async fn monad_rollback_block(
254 &self,
255 number: u64,
256 ) -> Result<Option<Block>, BlockchainError> {
257 let Some(fork) = self.get_fork().filter(|fork| {
258 fork.transaction_hash().is_some() && fork.predates_fork_inclusive(number)
259 }) else {
260 return Ok(None);
261 };
262 let Some(block) = fork.block_by_number(number).await? else {
263 return Ok(None);
264 };
265 let header = Header::try_from(block.header().inner.clone())
266 .map_err(|err| BlockchainError::Internal(err.to_string()))?;
267 Ok(Some(Block { header: header.into(), body: Default::default() }))
268 }
269
270 pub(super) async fn prepare_monad_fork_replay(
272 &self,
273 source_chain_id: u64,
274 execution_chain_id: u64,
275 timestamp: u64,
276 parent_hash: B256,
277 transactions: &[HistoricalReplayTransaction],
278 ) -> Result<Option<ForkReplay>> {
279 if !self.is_monad() {
280 return Ok(None);
281 }
282
283 let explicit_hardfork = self.node_config.read().await.hardfork;
284 let hardfork = explicit_hardfork
287 .or_else(|| {
288 MonadHardfork::from_chain_and_timestamp(source_chain_id, timestamp)
289 .map(FoundryHardfork::Monad)
290 })
291 .unwrap_or_else(|| self.hardfork());
292 let context = self.monad_context_for_child_of_block_hash(parent_hash).await?;
293 let participants =
294 monad_block_participants(&self.monad_historical_replay_tx_envs(transactions));
295
296 Ok(Some(ForkReplay {
297 hardfork,
298 context: Some(context),
299 participants,
300 execution_chain_id,
301 source_chain_id,
302 timestamp,
303 inferred_hardfork: explicit_hardfork.is_none(),
304 }))
305 }
306
307 pub(super) fn finalize_monad_fork_replay(&self, replay: &ForkReplay, evm_env: &mut EvmEnv) {
309 let spec_id = SpecId::from(replay.hardfork());
310 evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec_id);
311 self.fees.set_execution_rules(
312 spec_id,
313 self.networks.base_fee_params(replay.timestamp),
314 None,
315 );
316 self.fees.set_blob_params(get_blob_params(replay.source_chain_id, replay.timestamp));
317
318 if replay.inferred_hardfork {
319 *self.hardfork.write() = replay.hardfork();
320 if let Some(fork) = self.fork.read().clone() {
321 fork.config.write().hardfork = Some(replay.hardfork());
322 }
323 }
324 }
325
326 pub(super) async fn prepare_monad_rollback_profile(
328 &self,
329 common_block: &Block,
330 ) -> Option<MonadRollbackProfile> {
331 let fork = self.get_fork()?;
333
334 let explicit_hardfork = self.node_config.read().await.hardfork;
335 let block_hash = common_block.header.hash_slow();
336 let stored_hardfork = self
337 .blockchain
338 .storage
339 .read()
340 .monad_block_replay_profiles
341 .get(&block_hash)
342 .map(|profile| profile.hardfork);
343 let endpoint_hardfork = {
344 let config = fork.config.read();
345 if config.block_number != common_block.header.number()
346 || config.block_hash != block_hash
347 {
348 None
349 } else {
350 match config.endpoint_identity.hardfork {
351 Some(FoundryHardfork::Monad(hardfork)) => Some(hardfork),
352 _ => None,
353 }
354 }
355 };
356 let source_chain_id = fork.chain_id();
357 let scheduled_hardfork = MonadHardfork::from_chain_and_timestamp(
358 source_chain_id,
359 common_block.header.timestamp(),
360 );
361 let hardfork = explicit_hardfork.unwrap_or_else(|| {
362 FoundryHardfork::Monad(
363 stored_hardfork
364 .or(endpoint_hardfork)
365 .or(scheduled_hardfork)
366 .unwrap_or_else(|| self.monad_hardfork()),
367 )
368 });
369 let spec_id = SpecId::from(hardfork);
370 let timestamp = common_block.header.timestamp();
371 let blob_params = get_blob_params(source_chain_id, timestamp);
372 let block_base_fee = common_block.header.base_fee_per_gas.unwrap_or_default();
373 let is_fork_root = fork.block_number() == common_block.header.number();
374
375 let fees = self.fees.detached();
376 fees.set_execution_rules(spec_id, BaseFeeParams::ethereum(), None);
377 fees.set_blob_params(blob_params);
378 fees.set_base_fee(block_base_fee);
382 let (next_block_base_fee, next_block_excess_blob_gas) = if is_fork_root {
383 if let Some(child) = self.get_block(common_block.header.number().saturating_add(1)) {
384 (
385 child.header.base_fee_per_gas().unwrap_or_default(),
386 child.header.excess_blob_gas().unwrap_or_default(),
387 )
388 } else {
389 let snapshot = self.fees.snapshot();
390 (snapshot.base_fee, snapshot.blob_excess_gas_and_price.excess_blob_gas)
391 }
392 } else {
393 (
394 fees.get_next_block_base_fee_from_header(&common_block.header),
395 blob_params.next_block_excess_blob_gas_osaka(
396 common_block.header.excess_blob_gas.unwrap_or_default(),
397 common_block.header.blob_gas_used.unwrap_or_default(),
398 block_base_fee,
399 ),
400 )
401 };
402 fees.set_base_fee(next_block_base_fee);
403 fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new(
404 next_block_excess_blob_gas,
405 blob_params.update_fraction as u64,
406 ));
407
408 Some(MonadRollbackProfile {
409 hardfork,
410 fees,
411 block_blob_excess_gas_and_price: common_block.header.excess_blob_gas.map(|excess| {
412 BlobExcessGasAndPrice::new(excess, blob_params.update_fraction as u64)
413 }),
414 publish_inferred_hardfork: explicit_hardfork.is_none(),
415 })
416 }
417
418 pub(crate) async fn monad_rollback_fee_defaults(
420 &self,
421 common_block: &Block,
422 suggested_tip: u128,
423 ) -> Option<(u128, u128)> {
424 let profile = self.prepare_monad_rollback_profile(common_block).await?;
425 let gas_price = if profile.fees.is_eip1559() {
426 let base_fee = profile.fees.base_fee() as u128;
427 if profile.fees.is_min_priority_fee_enforced() {
428 base_fee.saturating_add(suggested_tip)
429 } else {
430 base_fee
431 }
432 } else {
433 profile.fees.raw_gas_price()
434 };
435 Some((gas_price, profile.fees.get_next_block_blob_base_fee_per_gas()))
436 }
437
438 pub(crate) async fn rollback_monad(
440 &self,
441 common_block: Block,
442 mining_guard: &tokio::sync::MutexGuard<'_, ()>,
443 ) -> Result<(), BlockchainError>
444 where
445 N: Network<TxEnvelope = FoundryTxEnvelope, ReceiptEnvelope = FoundryReceiptEnvelope>,
446 {
447 let profile = self.prepare_monad_rollback_profile(&common_block).await;
448 self.rollback(common_block, mining_guard).await?;
449 if let Some(profile) = profile {
450 profile.apply_env(&mut self.evm_env.write());
451 self.publish_monad_rollback_profile(profile);
452 }
453 Ok(())
454 }
455
456 pub(super) fn publish_monad_rollback_profile(&self, profile: MonadRollbackProfile) {
458 self.fees.replace_from(&profile.fees);
459 if profile.publish_inferred_hardfork {
460 *self.hardfork.write() = profile.hardfork;
461 if let Some(fork) = self.fork.read().clone() {
462 fork.config.write().hardfork = Some(profile.hardfork);
463 }
464 }
465 }
466
467 pub(super) const fn validate_monad_transaction_type(
469 &self,
470 tx: &FoundryTxEnvelope,
471 ) -> Result<(), InvalidTransactionError> {
472 if self.is_monad() && tx.is_eip4844() {
473 return Err(InvalidTransactionError::MonadBlobTransactionUnsupported);
474 }
475 Ok(())
476 }
477
478 pub(super) fn validate_monad_transaction_funds(
482 &self,
483 pending: &PendingTransaction<FoundryTxEnvelope>,
484 account: &AccountInfo,
485 evm_env: &EvmEnv,
486 ) -> Result<bool, InvalidTransactionError> {
487 if !self.is_monad() {
488 return Ok(false);
489 }
490
491 let tx = &pending.transaction;
492 let effective_gas_price = tx.effective_gas_price(Some(evm_env.block_env.basefee));
493 let required = U256::from(tx.gas_limit()) * U256::from(effective_gas_price);
494 if account.balance < required {
495 debug!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance, required, *pending.sender());
496 return Err(InvalidTransactionError::InsufficientFunds);
497 }
498 Ok(true)
499 }
500
501 pub(super) fn validate_monad_mining_pool_transaction_for(
505 &self,
506 pool_tx: &PoolTransaction<FoundryTxEnvelope>,
507 account: &AccountInfo,
508 evm_env: &EvmEnv,
509 ) -> Result<bool, InvalidTransactionError> {
510 if !self.is_monad() || !pool_tx.is_replay {
511 return Ok(false);
512 }
513
514 let tx_env: TxEnv = build_tx_env_for_pending(&pool_tx.pending_transaction, self.cheats());
515 let Some(system_call) = protocol_system_call(&tx_env).map_err(|err| {
516 InvalidTransactionError::Revm(InvalidTransaction::Str(err.to_string().into()))
517 })?
518 else {
519 return Ok(false);
520 };
521 if system_call.chain_id.is_some_and(|chain_id| chain_id != evm_env.cfg_env.chain_id) {
522 return Err(InvalidTransactionError::InvalidChainId);
523 }
524 if system_call.nonce < account.nonce {
525 return Err(InvalidTransactionError::NonceTooLow);
526 }
527 if system_call.nonce > account.nonce {
528 return Err(InvalidTransactionError::NonceTooHigh);
529 }
530 if system_call.nonce == u64::MAX {
531 return Err(InvalidTransactionError::NonceMaxValue);
532 }
533 Ok(true)
534 }
535
536 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
538 pub(super) fn execute_with_monad_block_executor<DB>(
539 &self,
540 db: DB,
541 evm_env: &EvmEnv,
542 parent_hash: B256,
543 spec_id: SpecId,
544 hardfork: MonadHardfork,
545 pool_transactions: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
546 gas_config: &PoolTxGasConfig,
547 inspector_tx_config: &crate::mem::inspector::InspectorTxConfig,
548 validator: &dyn Fn(
549 &PoolTransaction<FoundryTxEnvelope>,
550 &AccountInfo,
551 ) -> Result<(), InvalidTransactionError>,
552 ) -> Result<
553 (ExecutedPoolTransactions<FoundryTxEnvelope>, BlockExecutionResult<FoundryReceiptEnvelope>),
554 BlockchainError,
555 >
556 where
557 DB: StateDB<Error = DatabaseError> + BalIndexedDatabase,
558 {
559 let monad_env = Self::build_monad_evm_env(evm_env, hardfork);
560 let inspector = self.build_mining_inspector();
561 let mut evm =
562 MonadEvmFactory::default().create_evm_with_inspector(db, monad_env, inspector);
563 let transaction_context = self
564 .monad_context_for_child_of(parent_hash)
565 .expect("Monad ancestor context must be available before block execution");
566 evm.ctx_mut().chain = transaction_context;
567 self.inject_precompiles(evm.precompiles_mut(), evm_env);
568
569 let mut executor = AnvilBlockExecutor::new(evm, parent_hash, spec_id, None)
570 .with_max_blob_gas_per_block(gas_config.max_blob_gas_per_block);
571 executor
572 .apply_pre_execution_changes()
573 .map_err(|err| BlockchainError::Internal(err.to_string()))?;
574 let pool_result = execute_pool_transactions(
575 &mut executor,
576 pool_transactions,
577 gas_config,
578 inspector_tx_config,
579 self.cheats(),
580 validator,
581 &mut execute_pool_transaction,
582 );
583 let (evm, block_result) =
584 executor.finish().map_err(|err| BlockchainError::Internal(err.to_string()))?;
585 drop(evm);
586 Ok((pool_result, block_result))
587 }
588
589 #[allow(clippy::too_many_arguments)]
591 pub(super) fn execute_with_monad_replay_block_executor<DB>(
592 &self,
593 db: DB,
594 evm_env: &EvmEnv,
595 parent_hash: B256,
596 hardfork: MonadHardfork,
597 transactions: &[HistoricalReplayTransaction],
598 inspector_tx_config: &crate::mem::inspector::InspectorTxConfig,
599 transaction_context: MonadChainContext,
600 ) -> Result<ExecutedHistoricalReplay>
601 where
602 DB: StateDB<Error = DatabaseError> + BalIndexedDatabase,
603 {
604 let monad_env = Self::build_monad_evm_env(evm_env, hardfork);
605 let inspector = self.build_mining_inspector();
606 let mut evm =
607 MonadEvmFactory::default().create_evm_with_inspector(db, monad_env, inspector);
608 evm.ctx_mut().chain = transaction_context;
609 self.inject_precompiles(evm.precompiles_mut(), evm_env);
610
611 let mut executor = AnvilBlockExecutor::new(evm, parent_hash, *evm_env.spec_id(), None)
612 .with_state_changes();
613 executor
614 .apply_pre_execution_changes()
615 .wrap_err("failed to apply replay block-start transitions")?;
616 let (transactions, transaction_infos) = execute_historical_replay_with(
617 &mut executor,
618 transactions,
619 inspector_tx_config,
620 |evm, tx_env, _transaction_hash| {
621 prepare_transaction(evm, &tx_env);
622 let result = match try_transact_monad_system_replay(evm, &tx_env)
623 .map_err(BlockExecutionError::msg)
624 {
625 Ok(Some(result)) => Ok(result),
626 Ok(None) => evm.transact(tx_env).map_err(BlockExecutionError::msg),
627 Err(err) => Err(err),
628 };
629 if result.is_err() {
630 rollback_transaction(evm);
631 }
632 result
633 },
634 )?;
635 let state_changes = executor.take_state_changes();
636 let (evm, block_result) =
637 executor.finish().wrap_err("failed to finish replay block execution")?;
638 drop(evm);
639 Ok(ExecutedHistoricalReplay {
640 block_result,
641 transactions,
642 transaction_infos,
643 state_changes,
644 })
645 }
646
647 pub(super) fn monad_pending_mined_transaction_from_storage(
649 storage: &BlockchainStorage<N>,
650 transaction: MaybeImpersonatedTransaction<FoundryTxEnvelope>,
651 ) -> Result<PendingTransaction<FoundryTxEnvelope>, BlockchainError> {
652 let transaction_hash = transaction.hash();
653 let mined =
654 storage.transactions.get(&transaction_hash).ok_or(BlockchainError::DataUnavailable)?;
655 Ok(PendingTransaction::with_sender(transaction, mined.info.from))
656 }
657
658 fn monad_tx_envs_from_storage(
660 &self,
661 storage: &BlockchainStorage<N>,
662 transactions: &[MaybeImpersonatedTransaction<FoundryTxEnvelope>],
663 ) -> Result<Vec<TxEnv>, BlockchainError> {
664 transactions
665 .iter()
666 .map(|transaction| {
667 let pending = Self::monad_pending_mined_transaction_from_storage(
668 storage,
669 transaction.clone(),
670 )?;
671 Ok(build_tx_env_for_pending::<FoundryTxEnvelope, TxEnv>(&pending, self.cheats()))
672 })
673 .collect()
674 }
675
676 pub(super) fn monad_historical_replay_tx_envs(
678 &self,
679 transactions: &[HistoricalReplayTransaction],
680 ) -> Vec<TxEnv> {
681 transactions
682 .iter()
683 .map(|replay| {
684 let pending = PendingTransaction::with_sender(
685 MaybeImpersonatedTransaction::new(replay.transaction.tx().clone()),
686 replay.transaction.signer(),
687 );
688 build_tx_env_for_pending::<FoundryTxEnvelope, TxEnv>(&pending, self.cheats())
689 })
690 .collect()
691 }
692
693 fn monad_block_participants_from_storage(
695 &self,
696 storage: &BlockchainStorage<N>,
697 hash: B256,
698 ) -> Result<(u64, B256, MonadBlockParticipants), BlockchainError> {
699 if let Some(block) = storage.blocks.get(&hash) {
700 let participants = match storage.monad_block_participants.get(&hash) {
701 Some(participants) => participants.clone(),
702 None if block.body.transactions.is_empty()
703 && block.header.transactions_root() != EMPTY_ROOT_HASH =>
704 {
705 return Err(BlockchainError::DataUnavailable);
706 }
707 None => monad_block_participants(
708 &self.monad_tx_envs_from_storage(storage, &block.body.transactions)?,
709 ),
710 };
711 return Ok((block.header.number(), block.header.parent_hash, participants));
712 }
713
714 let fork = self.get_fork().ok_or(BlockchainError::BlockNotFound)?;
715 let block = fork
716 .storage
717 .read()
718 .blocks
719 .get(&hash)
720 .cloned()
721 .ok_or(BlockchainError::DataUnavailable)?;
722 let BlockTransactions::Full(transactions) = block.transactions() else {
723 return Err(BlockchainError::DataUnavailable);
724 };
725 let tx_envs = transactions
726 .iter()
727 .map(TxEnv::from_any_rpc_transaction)
728 .collect::<eyre::Result<Vec<_>>>()?;
729 Ok((
730 block.header().number(),
731 block.header().parent_hash(),
732 monad_block_participants(&tx_envs),
733 ))
734 }
735
736 fn monad_block_participants(
737 &self,
738 hash: B256,
739 ) -> Result<(u64, B256, MonadBlockParticipants), BlockchainError> {
740 self.monad_block_participants_from_storage(&self.blockchain.storage.read(), hash)
741 }
742
743 pub(super) fn rebuild_monad_block_participant_cache(
745 &self,
746 storage: &mut BlockchainStorage<N>,
747 ) -> Result<(), BlockchainError> {
748 let participants = storage
749 .blocks
750 .iter()
751 .filter(|(hash, block)| {
752 !storage.monad_block_participants.contains_key(*hash)
753 && (!block.body.transactions.is_empty()
754 || block.header.transactions_root() == EMPTY_ROOT_HASH)
755 })
756 .map(|(hash, block)| (*hash, block.body.transactions.clone()))
757 .map(|(hash, transactions)| {
758 let tx_envs = self.monad_tx_envs_from_storage(storage, &transactions)?;
759 Ok((hash, monad_block_participants(&tx_envs)))
760 })
761 .collect::<Result<Vec<_>, BlockchainError>>()?;
762
763 for (hash, participants) in participants {
764 if storage.blocks.contains_key(&hash) {
765 storage.monad_block_participants.insert(hash, participants);
766 }
767 }
768 Ok(())
769 }
770
771 pub(super) fn monad_context_for_child_of(
773 &self,
774 parent_hash: B256,
775 ) -> Result<MonadChainContext, BlockchainError> {
776 self.monad_context_for_child_of_in_storage(&self.blockchain.storage.read(), parent_hash)
777 }
778
779 pub(super) fn monad_context_for_child_of_in_storage(
781 &self,
782 storage: &BlockchainStorage<N>,
783 parent_hash: B256,
784 ) -> Result<MonadChainContext, BlockchainError> {
785 let (_, grandparent_hash, parent) =
786 self.monad_block_participants_from_storage(storage, parent_hash)?;
787 let grandparent = if grandparent_hash.is_zero() {
788 MonadBlockParticipants::default()
789 } else {
790 self.monad_block_participants_from_storage(storage, grandparent_hash)?.2
791 };
792 Ok(monad_context_from_participants(grandparent, parent, &[], 0))
793 }
794
795 async fn monad_context_for_child_of_block(
796 &self,
797 block: alloy_network::AnyRpcBlock,
798 block_hash: B256,
799 ) -> Result<MonadChainContext, BlockchainError> {
800 let parent_hash = block.header().parent_hash();
801 if !parent_hash.is_zero() {
802 self.block_by_hash_full(parent_hash).await?.ok_or(BlockchainError::DataUnavailable)?;
803 }
804 self.monad_context_for_child_of(block_hash)
805 }
806
807 pub(super) async fn monad_context_for_child_of_block_number(
809 &self,
810 block_number: u64,
811 ) -> Result<MonadChainContext, BlockchainError> {
812 let block = self
813 .block_by_number_full(BlockNumber::Number(block_number))
814 .await?
815 .ok_or(BlockchainError::BlockNotFound)?;
816 let block_hash = block.header().hash;
817 self.monad_context_for_child_of_block(block, block_hash).await
818 }
819
820 pub(super) async fn monad_context_for_child_of_block_hash(
822 &self,
823 block_hash: B256,
824 ) -> Result<MonadChainContext, BlockchainError> {
825 let block =
826 self.block_by_hash_full(block_hash).await?.ok_or(BlockchainError::BlockNotFound)?;
827 self.monad_context_for_child_of_block(block, block_hash).await
828 }
829
830 fn monad_context_for_mined_transactions(
831 &self,
832 block: &Block,
833 current: &[TxEnv],
834 current_tx_index: usize,
835 ) -> Result<MonadChainContext, BlockchainError> {
836 let (_, grandparent_hash, parent) =
837 self.monad_block_participants(block.header.parent_hash)?;
838 let grandparent = if grandparent_hash.is_zero() {
839 MonadBlockParticipants::default()
840 } else {
841 self.monad_block_participants(grandparent_hash)?.2
842 };
843 Ok(monad_context_from_participants(grandparent, parent, current, current_tx_index))
844 }
845
846 fn monad_context_for_mined_block(
847 &self,
848 block: &Block,
849 ) -> Result<MonadChainContext, BlockchainError> {
850 let current = self.monad_tx_envs_from_storage(
851 &self.blockchain.storage.read(),
852 &block.body.transactions,
853 )?;
854 self.monad_context_for_mined_transactions(block, ¤t, 0)
855 }
856
857 pub(super) fn active_monad_context_for_mined_block(
858 &self,
859 block: &Block,
860 ) -> Result<Option<MonadChainContext>, BlockchainError> {
861 self.is_monad().then(|| self.monad_context_for_mined_block(block)).transpose()
862 }
863
864 fn monad_context_before_mined_transaction(
865 &self,
866 block: &Block,
867 current_tx_index: usize,
868 ) -> Result<MonadChainContext, BlockchainError> {
869 let current = self.monad_tx_envs_from_storage(
870 &self.blockchain.storage.read(),
871 &block.body.transactions,
872 )?;
873 if current_tx_index > current.len() {
874 return Err(BlockchainError::DataUnavailable);
875 }
876 self.monad_context_for_mined_transactions(
877 block,
878 ¤t[..current_tx_index],
879 current_tx_index,
880 )
881 }
882
883 pub(super) fn active_monad_context_before_mined_transaction(
884 &self,
885 block: &Block,
886 current_tx_index: usize,
887 ) -> Result<Option<MonadChainContext>, BlockchainError> {
888 self.is_monad()
889 .then(|| self.monad_context_before_mined_transaction(block, current_tx_index))
890 .transpose()
891 }
892
893 pub(super) fn build_monad_evm_env(
895 evm_env: &EvmEnv,
896 hardfork: MonadHardfork,
897 ) -> EvmEnv<MonadHardfork> {
898 EvmEnv::new(
899 evm_env.cfg_env.clone().with_spec_and_gas_params(hardfork, monad_gas_params(hardfork)),
900 evm_env.block_env.clone(),
901 )
902 }
903
904 pub(super) fn transact_monad_with_inspector_ref<'db, I>(
906 &self,
907 db: &'db dyn DatabaseRef,
908 evm_env: &EvmEnv,
909 inspector: &mut I,
910 tx_env: TxEnv,
911 execution: PreparedExecution,
912 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
913 where
914 I: Inspector<MonadContext<WrapDatabaseRef<&'db dyn DatabaseRef>>>,
915 {
916 let monad_env = Self::build_monad_evm_env(evm_env, execution.hardfork);
917 let factory = MonadEvmFactory::default();
918 let mut evm = factory.create_evm_with_inspector(WrapDatabaseRef(db), monad_env, inspector);
919 evm.ctx_mut().chain = execution.context;
920 self.inject_configured_precompiles(evm.precompiles_mut(), evm_env);
921 match execution.kind {
922 EnvelopeExecutionKind::Transaction => Ok(evm.transact(tx_env)?),
923 EnvelopeExecutionKind::Replay => {
924 if let Some(result) = try_transact_monad_system_replay(&mut evm, &tx_env)? {
925 Ok(result)
926 } else {
927 Ok(evm.transact(tx_env)?)
928 }
929 }
930 }
931 }
932}
933
934pub(super) fn store_block_metadata<N: Network>(
935 storage: &mut BlockchainStorage<N>,
936 block_hash: B256,
937 participants: MonadBlockParticipants,
938 execution_chain_id: u64,
939 hardfork: FoundryHardfork,
940) {
941 storage.monad_block_participants.insert(block_hash, participants);
942 storage.monad_block_replay_profiles.insert(
943 block_hash,
944 MonadBlockReplayProfile { execution_chain_id, hardfork: MonadHardfork::from(hardfork) },
945 );
946}