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, ExecutedPoolTransactions, PoolTransactionHooks, 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 pool::transactions::PoolTransaction,
20};
21use alloy_consensus::{
22 BlockHeader, Transaction as _, constants::EMPTY_ROOT_HASH, transaction::Recovered,
23};
24use alloy_evm::{
25 Database, Evm, EvmEnv, EvmFactory, RecoveredTx,
26 block::{BlockExecutionError, BlockExecutionResult, BlockExecutor, StateDB},
27};
28use alloy_monad_evm::{MonadContext, MonadEvm, MonadEvmFactory};
29use alloy_network::{BlockResponse, Network};
30use alloy_primitives::{B256, U256};
31use alloy_rpc_types::{AccessList, BlockNumberOrTag as BlockNumber, BlockTransactions};
32use anvil_core::eth::{
33 block::Block,
34 transaction::{MaybeImpersonatedTransaction, PendingTransaction},
35};
36use eyre::{Context, Result};
37use foundry_evm::{
38 backend::DatabaseError,
39 core::{
40 FoundryChain, FromAnyRpcTransaction,
41 evm::{
42 EvmEnvFor, FoundryEvmFactory, MonadBlockParticipants, MonadEvmNetwork,
43 monad_block_participants, monad_context_from_participants, protocol_system_call,
44 },
45 },
46 hardfork::FoundryHardfork,
47 utils::get_blob_params,
48};
49use foundry_primitives::{FoundryReceiptEnvelope, FoundryTxEnvelope};
50use monad_revm::{MonadChainContext, MonadHardfork, instructions::monad_gas_params};
51use revm::{
52 Inspector,
53 context::{Transaction, TxEnv},
54 context_interface::{
55 result::{HaltReason, InvalidTransaction, ResultAndState},
56 transaction::AuthorizationTr,
57 },
58 database_interface::WrapDatabaseRef,
59 primitives::hardfork::SpecId,
60 state::AccountInfo,
61};
62use std::sync::Arc;
63use tracing::debug;
64
65pub(super) struct PreparedExecution {
66 pub(super) context: Option<MonadChainContext>,
67 pub(super) kind: EnvelopeExecutionKind,
68 pub(super) hardfork: MonadHardfork,
69}
70
71pub(super) struct ForkReplay {
72 hardfork: FoundryHardfork,
73 context: Option<MonadChainContext>,
74 participants: MonadBlockParticipants,
75 execution_chain_id: u64,
76 source_chain_id: u64,
77 timestamp: u64,
78 inferred_hardfork: bool,
79}
80
81impl ForkReplay {
82 pub(super) const fn hardfork(&self) -> FoundryHardfork {
83 self.hardfork
84 }
85
86 pub(super) const fn take_context(&mut self) -> Option<MonadChainContext> {
87 self.context.take()
88 }
89
90 pub(super) fn store_metadata<N: Network>(
91 &mut self,
92 storage: &mut BlockchainStorage<N>,
93 block_hash: B256,
94 ) {
95 store_block_metadata(
96 storage,
97 block_hash,
98 std::mem::take(&mut self.participants),
99 self.execution_chain_id,
100 self.hardfork,
101 );
102 }
103}
104
105pub(super) fn normalize_access_list(
106 mut access_list: AccessList,
107 hardfork: MonadHardfork,
108) -> AccessList {
109 if MonadHardfork::MonadTen.is_enabled_in(hardfork) {
110 for item in &mut access_list.0 {
111 item.storage_keys.sort_unstable();
112 item.storage_keys.dedup_by_key(|slot| {
113 monad_revm::page::page_index(U256::from_be_slice(slot.as_slice()))
114 });
115 }
116 }
117 access_list
118}
119
120pub(super) async fn cache_fork_context(fork: &ClientFork) -> Result<(), BlockchainError> {
122 let block =
123 fork.block_by_hash_full(fork.block_hash()).await?.ok_or(BlockchainError::BlockNotFound)?;
124 let parent_hash = block.header().parent_hash();
125 if !parent_hash.is_zero() {
126 fork.block_by_hash_full(parent_hash).await?.ok_or(BlockchainError::BlockNotFound)?;
127 }
128 Ok(())
129}
130
131fn append_transaction(chain: &mut MonadChainContext, tx: &TxEnv) {
133 chain.current_tx_index = chain.current_block_senders.len();
134 chain.current_block_senders.push(tx.caller());
135 chain.current_block_authorities.push(
136 tx.authorization_list().filter_map(|authorization| authorization.authority()).collect(),
137 );
138}
139
140pub(super) fn prepare_transaction<DB: alloy_evm::Database>(
141 evm: &mut MonadEvm<DB, AnvilInspector>,
142 tx: &TxEnv,
143) {
144 append_transaction(&mut evm.ctx_mut().chain, tx);
145}
146
147pub(super) fn resolve_execution_context(
148 context: Option<MonadExecutionContext<'_>>,
149 tx: &TxEnv,
150) -> Option<MonadChainContext> {
151 match context {
152 Some(MonadExecutionContext::Exact(context)) => Some(*context),
153 Some(MonadExecutionContext::Next(context)) => {
154 append_transaction(context, tx);
155 Some(context.clone())
156 }
157 None => None,
158 }
159}
160
161pub(super) fn advance_block_context(context: &mut Option<MonadChainContext>) {
162 let Some(context) = context else { return };
163 let current = context
164 .current_block_senders
165 .iter()
166 .copied()
167 .chain(context.current_block_authorities.iter().flatten().copied())
168 .collect();
169 context.grandparent_senders_and_authorities =
170 std::mem::replace(&mut context.parent_senders_and_authorities, current);
171 context.current_block_senders.clear();
172 context.current_block_authorities.clear();
173 context.current_tx_index = 0;
174}
175
176pub(super) fn rollback_transaction<DB: alloy_evm::Database>(
178 evm: &mut MonadEvm<DB, AnvilInspector>,
179) {
180 let chain = &mut evm.ctx_mut().chain;
181 chain.current_block_senders.pop();
182 chain.current_block_authorities.pop();
183 chain.current_tx_index = chain.current_block_senders.len();
184}
185
186impl<N: Network> Backend<N> {
187 pub(super) async fn prepare_monad_fork_replay(
189 &self,
190 source_chain_id: u64,
191 execution_chain_id: u64,
192 timestamp: u64,
193 parent_hash: B256,
194 transactions: &[HistoricalReplayTransaction],
195 ) -> Result<Option<ForkReplay>> {
196 if !self.is_monad() {
197 return Ok(None);
198 }
199
200 let explicit_hardfork = self.node_config.read().await.hardfork;
201 let hardfork = explicit_hardfork
204 .or_else(|| {
205 MonadHardfork::from_chain_and_timestamp(source_chain_id, timestamp)
206 .map(FoundryHardfork::Monad)
207 })
208 .unwrap_or_else(|| self.hardfork());
209 let context = self.monad_context_for_child_of_block_hash(parent_hash).await?;
210 let participants =
211 monad_block_participants(&self.monad_historical_replay_tx_envs(transactions));
212
213 Ok(Some(ForkReplay {
214 hardfork,
215 context: Some(context),
216 participants,
217 execution_chain_id,
218 source_chain_id,
219 timestamp,
220 inferred_hardfork: explicit_hardfork.is_none(),
221 }))
222 }
223
224 pub(super) fn finalize_monad_fork_replay(&self, replay: &ForkReplay, evm_env: &mut EvmEnv) {
226 let spec_id = SpecId::from(replay.hardfork);
227 evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec_id);
228 self.fees.set_execution_rules(
229 spec_id,
230 self.networks.base_fee_params(replay.timestamp),
231 None,
232 );
233 self.fees.set_blob_params(get_blob_params(replay.source_chain_id, replay.timestamp));
234
235 if replay.inferred_hardfork {
236 *self.hardfork.write() = replay.hardfork;
237 if let Some(fork) = self.fork.read().clone() {
238 fork.config.write().hardfork = Some(replay.hardfork);
239 }
240 }
241 }
242
243 pub(super) const fn validate_monad_transaction_type(
245 &self,
246 tx: &FoundryTxEnvelope,
247 ) -> Result<(), InvalidTransactionError> {
248 if self.is_monad() && tx.is_eip4844() {
249 return Err(InvalidTransactionError::MonadBlobTransactionUnsupported);
250 }
251 Ok(())
252 }
253
254 pub(super) fn validate_monad_transaction_funds(
258 &self,
259 pending: &PendingTransaction<FoundryTxEnvelope>,
260 account: &AccountInfo,
261 evm_env: &EvmEnv,
262 ) -> Result<bool, InvalidTransactionError> {
263 if !self.is_monad() {
264 return Ok(false);
265 }
266
267 let tx = &pending.transaction;
268 let effective_gas_price = tx.effective_gas_price(Some(evm_env.block_env.basefee));
269 let required = U256::from(tx.gas_limit()) * U256::from(effective_gas_price);
270 if account.balance < required {
271 debug!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance, required, *pending.sender());
272 return Err(InvalidTransactionError::InsufficientFunds);
273 }
274 Ok(true)
275 }
276
277 pub(super) fn validate_monad_mining_pool_transaction_for(
281 &self,
282 pool_tx: &PoolTransaction<FoundryTxEnvelope>,
283 account: &AccountInfo,
284 evm_env: &EvmEnv,
285 ) -> Result<bool, InvalidTransactionError> {
286 if !self.is_monad() || !pool_tx.is_replay {
287 return Ok(false);
288 }
289
290 let tx_env: TxEnv = build_tx_env_for_pending(&pool_tx.pending_transaction, self.cheats());
291 let Some(system_call) = protocol_system_call(&tx_env).map_err(|err| {
292 InvalidTransactionError::Revm(InvalidTransaction::Str(err.to_string().into()))
293 })?
294 else {
295 return Ok(false);
296 };
297 if system_call.chain_id.is_some_and(|chain_id| chain_id != evm_env.cfg_env.chain_id) {
298 return Err(InvalidTransactionError::InvalidChainId);
299 }
300 if system_call.nonce < account.nonce {
301 return Err(InvalidTransactionError::NonceTooLow);
302 }
303 if system_call.nonce > account.nonce {
304 return Err(InvalidTransactionError::NonceTooHigh);
305 }
306 if system_call.nonce == u64::MAX {
307 return Err(InvalidTransactionError::NonceMaxValue);
308 }
309 Ok(true)
310 }
311
312 #[allow(clippy::too_many_arguments, clippy::type_complexity)]
314 pub(super) fn execute_with_monad_block_executor<DB>(
315 &self,
316 db: DB,
317 evm_env: &EvmEnv,
318 parent_hash: B256,
319 spec_id: SpecId,
320 hardfork: FoundryHardfork,
321 pool_transactions: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
322 gas_config: &PoolTxGasConfig,
323 inspector_tx_config: &crate::mem::inspector::InspectorTxConfig,
324 validator: &dyn Fn(
325 &PoolTransaction<FoundryTxEnvelope>,
326 &AccountInfo,
327 ) -> Result<(), InvalidTransactionError>,
328 ) -> Result<
329 (ExecutedPoolTransactions<FoundryTxEnvelope>, BlockExecutionResult<FoundryReceiptEnvelope>),
330 BlockchainError,
331 >
332 where
333 DB: StateDB<Error = DatabaseError>,
334 {
335 let hardfork = MonadHardfork::from(hardfork);
336 let monad_env = Self::build_monad_evm_env(evm_env, hardfork);
337 let inspector = self.build_mining_inspector();
338 let mut evm =
339 MonadEvmFactory::default().create_evm_with_inspector(db, monad_env, inspector);
340 let transaction_context = self
341 .monad_context_for_child_of(parent_hash)
342 .expect("Monad ancestor context must be available before block execution");
343 evm.ctx_mut().chain = transaction_context;
344 self.inject_precompiles(evm.precompiles_mut(), evm_env);
345
346 let mut executor = AnvilBlockExecutor::new(evm, parent_hash, spec_id, None)
347 .with_max_blob_gas_per_block(gas_config.max_blob_gas_per_block);
348 executor
349 .apply_pre_execution_changes()
350 .map_err(|err| BlockchainError::Internal(err.to_string()))?;
351 let mut hooks = PoolTransactionHooks {
352 before_transaction: prepare_transaction,
353 execute_transaction: |executor: &mut AnvilBlockExecutor<_>,
354 tx_env: TxEnv,
355 recovered: Recovered<FoundryTxEnvelope>,
356 is_replay: bool| {
357 if !is_replay {
358 return executor.execute_transaction_without_commit((tx_env, recovered));
359 }
360 match protocol_system_call(&tx_env) {
361 Ok(None) => {
362 return executor.execute_transaction_without_commit((tx_env, recovered));
363 }
364 Ok(Some(_)) => {}
365 Err(err) => return Err(BlockExecutionError::msg(err)),
366 }
367 executor.execute_transaction_without_commit_with(
368 (tx_env, recovered),
369 |evm, tx_env, transaction_hash| {
370 MonadEvmFactory::default()
371 .try_transact_system_replay(evm, &tx_env)
372 .map_err(|err| {
373 BlockExecutionError::msg(format!(
374 "failed to replay Monad transaction {transaction_hash}: {err}"
375 ))
376 })?
377 .ok_or_else(|| {
378 BlockExecutionError::msg(format!(
379 "Monad transaction {transaction_hash} is not a canonical replay \
380 envelope"
381 ))
382 })
383 },
384 )
385 },
386 on_execution_error: rollback_transaction,
387 };
388 let pool_result = execute_pool_transactions(
389 &mut executor,
390 pool_transactions,
391 gas_config,
392 inspector_tx_config,
393 self.cheats(),
394 validator,
395 &mut hooks,
396 );
397 let (evm, block_result) =
398 executor.finish().map_err(|err| BlockchainError::Internal(err.to_string()))?;
399 drop(evm);
400 Ok((pool_result, block_result))
401 }
402
403 #[allow(clippy::too_many_arguments)]
405 pub(super) fn execute_with_monad_replay_block_executor<DB>(
406 &self,
407 db: DB,
408 evm_env: &EvmEnv,
409 parent_hash: B256,
410 hardfork: FoundryHardfork,
411 transactions: &[HistoricalReplayTransaction],
412 inspector_tx_config: &crate::mem::inspector::InspectorTxConfig,
413 transaction_context: Option<MonadChainContext>,
414 ) -> Result<ExecutedHistoricalReplay>
415 where
416 DB: StateDB<Error = DatabaseError>,
417 {
418 let hardfork = MonadHardfork::from(hardfork);
419 let monad_env = Self::build_monad_evm_env(evm_env, hardfork);
420 let inspector = self.build_mining_inspector();
421 let mut evm =
422 MonadEvmFactory::default().create_evm_with_inspector(db, monad_env, inspector);
423 evm.ctx_mut().chain = transaction_context
424 .ok_or_else(|| eyre::eyre!("Monad replay ancestor context is unavailable"))?;
425 self.inject_precompiles(evm.precompiles_mut(), evm_env);
426
427 let mut executor = AnvilBlockExecutor::new(evm, parent_hash, *evm_env.spec_id(), None)
428 .with_state_changes();
429 executor
430 .apply_pre_execution_changes()
431 .wrap_err("failed to apply replay block-start transitions")?;
432 let (transactions, transaction_infos) = execute_historical_replay_with(
433 &mut executor,
434 transactions,
435 inspector_tx_config,
436 |evm, tx_env, _transaction_hash| {
437 prepare_transaction(evm, &tx_env);
438 let result = match MonadEvmFactory::default()
439 .try_transact_system_replay(evm, &tx_env)
440 .map_err(BlockExecutionError::msg)
441 {
442 Ok(Some(result)) => Ok(result),
443 Ok(None) => evm.transact(tx_env).map_err(BlockExecutionError::msg),
444 Err(err) => Err(err),
445 };
446 if result.is_err() {
447 rollback_transaction(evm);
448 }
449 result
450 },
451 )?;
452 let state_changes = executor.take_state_changes();
453 let (evm, block_result) =
454 executor.finish().wrap_err("failed to finish replay block execution")?;
455 drop(evm);
456 Ok(ExecutedHistoricalReplay {
457 block_result,
458 transactions,
459 transaction_infos,
460 state_changes,
461 })
462 }
463
464 pub(super) fn monad_pending_mined_transaction_from_storage(
466 storage: &BlockchainStorage<N>,
467 transaction: MaybeImpersonatedTransaction<FoundryTxEnvelope>,
468 ) -> Result<PendingTransaction<FoundryTxEnvelope>, BlockchainError> {
469 let transaction_hash = transaction.hash();
470 let mined =
471 storage.transactions.get(&transaction_hash).ok_or(BlockchainError::DataUnavailable)?;
472 Ok(PendingTransaction::with_sender(transaction, mined.info.from))
473 }
474
475 fn monad_tx_envs_from_storage(
477 &self,
478 storage: &BlockchainStorage<N>,
479 transactions: &[MaybeImpersonatedTransaction<FoundryTxEnvelope>],
480 ) -> Result<Vec<TxEnv>, BlockchainError> {
481 transactions
482 .iter()
483 .map(|transaction| {
484 let pending = Self::monad_pending_mined_transaction_from_storage(
485 storage,
486 transaction.clone(),
487 )?;
488 Ok(build_tx_env_for_pending::<FoundryTxEnvelope, TxEnv>(&pending, self.cheats()))
489 })
490 .collect()
491 }
492
493 pub(super) fn monad_historical_replay_tx_envs(
495 &self,
496 transactions: &[HistoricalReplayTransaction],
497 ) -> Vec<TxEnv> {
498 transactions
499 .iter()
500 .map(|replay| {
501 let pending = PendingTransaction::with_sender(
502 MaybeImpersonatedTransaction::new(replay.transaction.tx().clone()),
503 replay.transaction.signer(),
504 );
505 build_tx_env_for_pending::<FoundryTxEnvelope, TxEnv>(&pending, self.cheats())
506 })
507 .collect()
508 }
509
510 fn monad_block_participants_from_storage(
512 &self,
513 storage: &BlockchainStorage<N>,
514 hash: B256,
515 ) -> Result<(u64, B256, MonadBlockParticipants), BlockchainError> {
516 if let Some(block) = storage.blocks.get(&hash) {
517 let participants = match storage.monad_block_participants.get(&hash) {
518 Some(participants) => participants.clone(),
519 None if block.body.transactions.is_empty()
520 && block.header.transactions_root() != EMPTY_ROOT_HASH =>
521 {
522 return Err(BlockchainError::DataUnavailable);
523 }
524 None => monad_block_participants(
525 &self.monad_tx_envs_from_storage(storage, &block.body.transactions)?,
526 ),
527 };
528 return Ok((block.header.number(), block.header.parent_hash, participants));
529 }
530
531 let fork = self.get_fork().ok_or(BlockchainError::BlockNotFound)?;
532 let block = fork
533 .storage
534 .read()
535 .blocks
536 .get(&hash)
537 .cloned()
538 .ok_or(BlockchainError::DataUnavailable)?;
539 let BlockTransactions::Full(transactions) = block.transactions() else {
540 return Err(BlockchainError::DataUnavailable);
541 };
542 let tx_envs = transactions
543 .iter()
544 .map(TxEnv::from_any_rpc_transaction)
545 .collect::<eyre::Result<Vec<_>>>()?;
546 Ok((
547 block.header().number(),
548 block.header().parent_hash(),
549 monad_block_participants(&tx_envs),
550 ))
551 }
552
553 fn monad_block_participants(
554 &self,
555 hash: B256,
556 ) -> Result<(u64, B256, MonadBlockParticipants), BlockchainError> {
557 self.monad_block_participants_from_storage(&self.blockchain.storage.read(), hash)
558 }
559
560 pub(super) fn rebuild_monad_block_participant_cache(
562 &self,
563 storage: &mut BlockchainStorage<N>,
564 ) -> Result<(), BlockchainError> {
565 let participants = storage
566 .blocks
567 .iter()
568 .filter(|(hash, block)| {
569 !storage.monad_block_participants.contains_key(*hash)
570 && (!block.body.transactions.is_empty()
571 || block.header.transactions_root() == EMPTY_ROOT_HASH)
572 })
573 .map(|(hash, block)| (*hash, block.body.transactions.clone()))
574 .map(|(hash, transactions)| {
575 let tx_envs = self.monad_tx_envs_from_storage(storage, &transactions)?;
576 Ok((hash, monad_block_participants(&tx_envs)))
577 })
578 .collect::<Result<Vec<_>, BlockchainError>>()?;
579
580 for (hash, participants) in participants {
581 if storage.blocks.contains_key(&hash) {
582 storage.monad_block_participants.insert(hash, participants);
583 }
584 }
585 Ok(())
586 }
587
588 pub(super) fn monad_context_for_child_of(
590 &self,
591 parent_hash: B256,
592 ) -> Result<MonadChainContext, BlockchainError> {
593 self.monad_context_for_child_of_in_storage(&self.blockchain.storage.read(), parent_hash)
594 }
595
596 pub(super) fn monad_context_for_child_of_in_storage(
598 &self,
599 storage: &BlockchainStorage<N>,
600 parent_hash: B256,
601 ) -> Result<MonadChainContext, BlockchainError> {
602 let (_, grandparent_hash, parent) =
603 self.monad_block_participants_from_storage(storage, parent_hash)?;
604 let grandparent = if grandparent_hash.is_zero() {
605 MonadBlockParticipants::default()
606 } else {
607 self.monad_block_participants_from_storage(storage, grandparent_hash)?.2
608 };
609 Ok(monad_context_from_participants(grandparent, parent, &[], 0))
610 }
611
612 async fn monad_context_for_child_of_block(
613 &self,
614 block: alloy_network::AnyRpcBlock,
615 block_hash: B256,
616 ) -> Result<MonadChainContext, BlockchainError> {
617 let parent_hash = block.header().parent_hash();
618 if !parent_hash.is_zero() {
619 self.block_by_hash_full(parent_hash).await?.ok_or(BlockchainError::DataUnavailable)?;
620 }
621 self.monad_context_for_child_of(block_hash)
622 }
623
624 pub(super) async fn monad_context_for_child_of_block_number(
626 &self,
627 block_number: u64,
628 ) -> Result<MonadChainContext, BlockchainError> {
629 let block = self
630 .block_by_number_full(BlockNumber::Number(block_number))
631 .await?
632 .ok_or(BlockchainError::BlockNotFound)?;
633 let block_hash = block.header().hash;
634 self.monad_context_for_child_of_block(block, block_hash).await
635 }
636
637 pub(super) async fn monad_context_for_child_of_block_hash(
639 &self,
640 block_hash: B256,
641 ) -> Result<MonadChainContext, BlockchainError> {
642 let block =
643 self.block_by_hash_full(block_hash).await?.ok_or(BlockchainError::BlockNotFound)?;
644 self.monad_context_for_child_of_block(block, block_hash).await
645 }
646
647 fn monad_context_for_mined_transactions(
648 &self,
649 block: &Block,
650 current: &[TxEnv],
651 current_tx_index: usize,
652 ) -> Result<MonadChainContext, BlockchainError> {
653 let (_, grandparent_hash, parent) =
654 self.monad_block_participants(block.header.parent_hash)?;
655 let grandparent = if grandparent_hash.is_zero() {
656 MonadBlockParticipants::default()
657 } else {
658 self.monad_block_participants(grandparent_hash)?.2
659 };
660 Ok(monad_context_from_participants(grandparent, parent, current, current_tx_index))
661 }
662
663 fn monad_context_for_mined_block(
664 &self,
665 block: &Block,
666 ) -> Result<MonadChainContext, BlockchainError> {
667 let current = self.monad_tx_envs_from_storage(
668 &self.blockchain.storage.read(),
669 &block.body.transactions,
670 )?;
671 self.monad_context_for_mined_transactions(block, ¤t, 0)
672 }
673
674 pub(super) fn active_monad_context_for_mined_block(
675 &self,
676 block: &Block,
677 ) -> Result<Option<MonadChainContext>, BlockchainError> {
678 self.is_monad().then(|| self.monad_context_for_mined_block(block)).transpose()
679 }
680
681 fn monad_context_before_mined_transaction(
682 &self,
683 block: &Block,
684 current_tx_index: usize,
685 ) -> Result<MonadChainContext, BlockchainError> {
686 let current = self.monad_tx_envs_from_storage(
687 &self.blockchain.storage.read(),
688 &block.body.transactions,
689 )?;
690 if current_tx_index > current.len() {
691 return Err(BlockchainError::DataUnavailable);
692 }
693 self.monad_context_for_mined_transactions(
694 block,
695 ¤t[..current_tx_index],
696 current_tx_index,
697 )
698 }
699
700 pub(super) fn active_monad_context_before_mined_transaction(
701 &self,
702 block: &Block,
703 current_tx_index: usize,
704 ) -> Result<Option<MonadChainContext>, BlockchainError> {
705 self.is_monad()
706 .then(|| self.monad_context_before_mined_transaction(block, current_tx_index))
707 .transpose()
708 }
709
710 pub(super) fn build_monad_evm_env(
712 evm_env: &EvmEnv,
713 hardfork: MonadHardfork,
714 ) -> EvmEnvFor<MonadEvmNetwork> {
715 EvmEnv::new(
716 evm_env.cfg_env.clone().with_spec_and_gas_params(hardfork, monad_gas_params(hardfork)),
717 evm_env.block_env.clone(),
718 )
719 }
720
721 pub(super) fn transact_monad_with_inspector_ref<'db, I, DB>(
723 &self,
724 db: &'db DB,
725 evm_env: &EvmEnv,
726 inspector: &mut I,
727 tx_env: TxEnv,
728 execution: PreparedExecution,
729 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
730 where
731 DB: DatabaseRef + ?Sized,
732 I: Inspector<MonadContext<WrapDatabaseRef<&'db DB>>>,
733 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
734 {
735 let monad_env = Self::build_monad_evm_env(evm_env, execution.hardfork);
736 let factory = MonadEvmFactory::default();
737 let context =
738 execution.context.unwrap_or_else(|| MonadChainContext::for_transaction(&tx_env));
739 let mut evm = factory.create_evm_with_inspector(WrapDatabaseRef(db), monad_env, inspector);
740 evm.ctx_mut().chain = context;
741 self.inject_configured_precompiles(evm.precompiles_mut(), evm_env);
742 match execution.kind {
743 EnvelopeExecutionKind::Transaction => Ok(evm.transact(tx_env)?),
744 EnvelopeExecutionKind::Replay => {
745 if let Some(result) = factory.try_transact_system_replay(&mut evm, &tx_env)? {
746 Ok(result)
747 } else {
748 Ok(evm.transact(tx_env)?)
749 }
750 }
751 }
752 }
753}
754
755pub(super) fn store_block_metadata<N: Network>(
756 storage: &mut BlockchainStorage<N>,
757 block_hash: B256,
758 participants: MonadBlockParticipants,
759 execution_chain_id: u64,
760 hardfork: FoundryHardfork,
761) {
762 storage.monad_block_participants.insert(block_hash, participants);
763 storage.monad_block_replay_profiles.insert(
764 block_hash,
765 MonadBlockReplayProfile { execution_chain_id, hardfork: MonadHardfork::from(hardfork) },
766 );
767}