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