1use super::Backend;
4use crate::eth::{
5 backend::{
6 db::{AnvilCacheDB, Db, StateRef},
7 executor::{BlockExecutionKind, ExecutedPoolTransactions},
8 time::{PendingBlockTimestamp, TimeManager},
9 },
10 error::BlockchainError,
11 pool::transactions::PoolTransaction,
12};
13use alloy_eips::{Decodable2718, Encodable2718};
14use alloy_evm::{EthEvmFactory, Evm, EvmEnv, EvmFactory};
15use alloy_network::Network;
16use alloy_primitives::{Address, B256, TxKind, U256};
17use anvil_core::eth::transaction::PendingTransaction;
18use base_common_chains::ChainConfig;
19use base_common_consensus::Predeploys;
20use base_common_evm::{
21 BaseContext, BaseEvmFactory, BaseHaltReason, BaseSpecId, BaseTime, BaseTransaction,
22 BaseUpgrade, L1BlockInfo,
23};
24use base_common_genesis::RollupConfig;
25use base_common_rpc_types::EIP8130_PRE_ZENITH_RPC_ERROR;
26use base_consensus_upgrades::Jovian;
27use base_protocol::{BaseTimeUpdateTx, L1BlockInfoJovian, L1BlockInfoTx};
28use foundry_evm::{backend::DatabaseError, hardfork::FoundryHardfork};
29use foundry_primitives::FoundryTxEnvelope;
30use revm::{
31 DatabaseRef, Inspector,
32 context::{
33 TxEnv,
34 result::{ExecutionResult, HaltReason, Output, ResultAndState},
35 },
36 database::EmptyDB,
37 database_interface::WrapDatabaseRef,
38};
39use std::sync::Arc;
40
41impl<N: Network> Backend<N> {
42 pub(super) fn transact_base_with_inspector_ref<'db, I>(
44 &self,
45 db: &'db dyn super::DatabaseRef,
46 evm_env: &EvmEnv,
47 inspector: &mut I,
48 tx: BaseTransaction<TxEnv>,
49 ) -> Result<ResultAndState<HaltReason>, BlockchainError>
50 where
51 I: Inspector<BaseContext<WrapDatabaseRef<&'db dyn super::DatabaseRef>>>,
52 {
53 let upgrade = self.base_upgrade_at_timestamp(evm_env.block_env.timestamp.saturating_to());
54 if tx.eip8130.is_some() && upgrade < BaseUpgrade::Zenith {
55 return Err(BlockchainError::InvalidTransactionRequest(
56 EIP8130_PRE_ZENITH_RPC_ERROR.to_string(),
57 ));
58 }
59 let base_env = EvmEnv::new(
60 evm_env.cfg_env.clone().with_spec_and_mainnet_gas_params(BaseSpecId::new(upgrade)),
61 evm_env.block_env.clone(),
62 );
63 let activation_admin = self.base_activation_admin().or_else(|| {
64 ChainConfig::activation_admin_address_for_upgrade_by_chain_id(
65 base_env.cfg_env.chain_id,
66 upgrade,
67 )
68 });
69 let factory = BaseEvmFactory::new(activation_admin);
70 let mut evm = factory.create_evm_with_inspector(WrapDatabaseRef(db), base_env, inspector);
71 evm.ctx_mut().cfg.tx_chain_id_check = true;
72 self.inject_precompiles(evm.precompiles_mut(), evm_env);
73 let result = Evm::transact_raw(&mut evm, tx)?;
74 Ok(ResultAndState {
75 result: result.result.map_haltreason(|halt| match halt {
76 BaseHaltReason::Base(eth) => eth,
77 BaseHaltReason::FailedDeposit => HaltReason::PrecompileError,
78 }),
79 state: result.state,
80 })
81 }
82
83 pub(super) fn ensure_fork_accepts_system_transactions(
88 &self,
89 db: &dyn Db,
90 parent_env: &EvmEnv,
91 parent_hash: B256,
92 hardfork: FoundryHardfork,
93 ) -> Result<(), DatabaseError> {
94 let mut evm_env = parent_env.clone();
95 evm_env.block_env.number = evm_env.block_env.number.saturating_add(U256::from(1));
96 let transactions = system_transactions(
97 db,
98 evm_env.block_env.number.saturating_to(),
99 evm_env.block_env.timestamp.saturating_to(),
100 parent_hash,
101 true,
102 )?;
103 let mut candidate_db = AnvilCacheDB::<StateRef<'_>>::new(db, *evm_env.spec_id());
104 self.execute_with_block_executor(
105 &mut candidate_db,
106 &evm_env,
107 parent_hash,
108 hardfork,
109 Some(B256::ZERO),
110 BlockExecutionKind::Complete,
111 &transactions,
112 &self.pool_tx_gas_config(&evm_env),
113 &self.inspector_tx_config(),
114 &|_, _| Ok(()),
115 )
116 .and_then(|(executed, _)| validate_system_transactions(&transactions, &executed))
117 .map_err(|err| {
118 DatabaseError::AnyRequest(Arc::new(eyre::eyre!(
119 "Denim system deposits fail on this fork; its L1Block likely predates Jovian (fork \
120 at or after the Jovian upgrade, or use an earlier --hardfork): {err}"
121 )))
122 })
123 }
124}
125
126fn next_millis_part(db: &dyn DatabaseRef<Error = DatabaseError>) -> Result<u16, DatabaseError> {
128 let parent = BaseTime::fetch_timestamp_millis_part(&mut WrapDatabaseRef(db))?;
129 if !BaseTimeUpdateTx::is_valid_timestamp_millis_part(parent) {
130 return Err(DatabaseError::AnyRequest(Arc::new(eyre::eyre!(
131 "invalid parent BaseTime millisecond component: {parent}"
132 ))));
133 }
134 Ok(parent + BaseTimeUpdateTx::BLOCK_INTERVAL_MILLIS)
135}
136
137pub(super) fn prepare_block_timestamp(
139 db: &dyn DatabaseRef<Error = DatabaseError>,
140 time: &TimeManager,
141 parent_timestamp: u64,
142) -> Result<PendingBlockTimestamp, DatabaseError> {
143 let carry = u64::from(next_millis_part(db)? >= 1_000);
144 Ok(time.prepare_next_timestamp_with_increment(carry, parent_timestamp.saturating_add(carry)))
145}
146
147pub(super) fn system_transactions(
152 db: &dyn DatabaseRef<Error = DatabaseError>,
153 block_number: u64,
154 timestamp: u64,
155 parent_hash: B256,
156 fork: bool,
157) -> Result<Vec<Arc<PoolTransaction<FoundryTxEnvelope>>>, DatabaseError> {
158 let mut state = WrapDatabaseRef(db);
159 let fees = L1BlockInfo::try_fetch(
160 &mut state,
161 U256::from(block_number),
162 BaseSpecId::new(BaseUpgrade::Denim),
163 )?;
164 let origin = db.storage_ref(Predeploys::L1_BLOCK_INFO, U256::ZERO)?.to_be_bytes::<32>();
165 let sequence_slot =
166 db.storage_ref(Predeploys::L1_BLOCK_INFO, U256::from(3))?.to_be_bytes::<32>();
167 let sequence = u64::from_be_bytes(sequence_slot[24..32].try_into().unwrap());
168 let batcher = db.storage_ref(Predeploys::L1_BLOCK_INFO, U256::from(4))?;
169 let (number, origin_timestamp, origin_hash, sequence) = if fork {
170 (
171 u64::from_be_bytes(origin[24..32].try_into().unwrap()),
172 u64::from_be_bytes(origin[16..24].try_into().unwrap()),
173 B256::from(db.storage_ref(Predeploys::L1_BLOCK_INFO, U256::from(2))?),
174 sequence.checked_add(1).ok_or_else(|| {
175 DatabaseError::AnyRequest(Arc::new(eyre::eyre!("L1-info sequence number overflow")))
176 })?,
177 )
178 } else {
179 (block_number, timestamp, parent_hash, block_number)
180 };
181 let l1_info = L1BlockInfoTx::Jovian(L1BlockInfoJovian::new(
182 number,
183 origin_timestamp,
184 fees.l1_base_fee.saturating_to(),
185 origin_hash,
186 sequence,
187 Address::from_word(B256::from(batcher)),
188 fees.l1_blob_base_fee.unwrap_or_default().saturating_to(),
189 fees.l1_blob_base_fee_scalar.unwrap_or_default().saturating_to(),
190 fees.l1_base_fee_scalar.saturating_to(),
191 fees.operator_fee_scalar.unwrap_or_default().saturating_to(),
192 fees.operator_fee_constant.unwrap_or_default().saturating_to(),
193 fees.da_footprint_gas_scalar.unwrap_or_default(),
194 ));
195 let mut upgrades = RollupConfig::default();
196 upgrades.set_upgrade_activation_timestamp(BaseUpgrade::Regolith, 0);
197 let l1_info = l1_info.into_deposit_tx(&upgrades, timestamp);
198 let base_time = BaseTimeUpdateTx::new(next_millis_part(db)? % 1_000)
199 .expect("the parent BaseTime phase was validated")
200 .into_deposit_tx(block_number);
201 Ok([l1_info, base_time]
202 .into_iter()
203 .map(|deposit| {
204 let envelope = FoundryTxEnvelope::decode_2718(&mut deposit.encoded_2718().as_slice())
205 .expect("Base and OP deposits share the canonical encoding");
206 let pending = PendingTransaction::new(envelope).expect("deposit sender is explicit");
207 Arc::new(PoolTransaction::new(pending))
208 })
209 .collect())
210}
211
212pub(super) fn ensure_l1_block_predeploy(db: &mut dyn Db) -> Result<(), DatabaseError> {
217 if db.basic(Predeploys::L1_BLOCK_INFO)?.is_some_and(|info| !info.is_empty_code_hash()) {
218 return Ok(());
219 }
220 let mut evm = EthEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default());
221 let result = evm
222 .transact_raw(TxEnv {
223 kind: TxKind::Create,
224 gas_limit: 1_000_000,
225 data: Jovian::l1_block_deployment_bytecode(),
226 ..Default::default()
227 })
228 .map_err(|err| DatabaseError::AnyRequest(Arc::new(eyre::eyre!(err))))?;
229 let ExecutionResult::Success { output: Output::Create(code, _), .. } = result.result else {
230 return Err(DatabaseError::AnyRequest(Arc::new(eyre::eyre!(
231 "failed to initialize the L1Block runtime"
232 ))));
233 };
234 db.set_code(Predeploys::L1_BLOCK_INFO, code)
235}
236
237pub(super) fn validate_system_transactions(
239 expected: &[Arc<PoolTransaction<FoundryTxEnvelope>>],
240 executed: &ExecutedPoolTransactions<FoundryTxEnvelope>,
241) -> Result<(), BlockchainError> {
242 for (index, transaction) in expected.iter().enumerate() {
243 if executed.included.get(index).is_none_or(|included| included.hash() != transaction.hash())
244 || executed.tx_info.get(index).is_none_or(|info| !info.exit.is_ok())
245 {
246 return Err(BlockchainError::Internal(format!(
247 "required Base system deposit at transaction index {index} failed"
248 )));
249 }
250 }
251 Ok(())
252}