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