1use std::{collections::HashMap, marker::PhantomData, mem, sync::Arc};
2
3use alloy::{
4 consensus::{BlockHeader, TxType},
5 eips::{eip1898::RpcBlockHash, BlockId},
6 network::TransactionBuilder,
7 rpc::types::{state::StateOverride, Block, Header, Transaction, TransactionRequest},
8};
9use eyre::Result;
10use revm::{
11 context::{result::ExecutionResult, BlockEnv, CfgEnv, ContextTr, TxEnv},
12 context_interface::block::BlobExcessGasAndPrice,
13 primitives::{eip7825, hardfork::SpecId, Address, U256},
14 Context, ExecuteEvm, MainBuilder, MainContext,
15};
16use tracing::debug;
17
18use helios_common::{
19 execution_provider::ExecutionProvider,
20 fork_schedule::ForkSchedule,
21 types::{Account, EvmError},
22};
23use helios_core::execution::errors::ExecutionError;
24use helios_revm_utils::proof_db::ProofDB;
25use phos_precompiles::DataNetworkPrecompiles;
26
27use crate::spec::DataNetwork;
28
29pub struct DataNetworkEvm<E: ExecutionProvider<DataNetwork>> {
30 execution: Arc<E>,
31 chain_id: u64,
32 block_id: BlockId,
33 fork_schedule: ForkSchedule,
34 phantom: PhantomData<DataNetwork>,
35}
36
37impl<E: ExecutionProvider<DataNetwork>> DataNetworkEvm<E> {
38 pub fn new(
39 execution: Arc<E>,
40 chain_id: u64,
41 fork_schedule: ForkSchedule,
42 block_id: BlockId,
43 ) -> Self {
44 Self {
45 execution,
46 chain_id,
47 block_id,
48 fork_schedule,
49 phantom: PhantomData,
50 }
51 }
52
53 pub async fn transact_inner(
54 &mut self,
55 tx: &TransactionRequest,
56 validate_tx: bool,
57 state_overrides: Option<StateOverride>,
58 ) -> Result<(ExecutionResult, HashMap<Address, Account>), EvmError> {
59 let block = self
60 .execution
61 .get_block(self.block_id, false)
62 .await
63 .map_err(|err| EvmError::Generic(err.to_string()))?
64 .ok_or(ExecutionError::BlockNotFound(self.block_id))
65 .map_err(|err| EvmError::Generic(err.to_string()))?;
66
67 let pinned_block: RpcBlockHash = block.header.hash.into();
69
70 let mut db = ProofDB::new(pinned_block, self.execution.clone(), state_overrides);
71 _ = db.state.prefetch_state(tx, validate_tx).await;
72
73 let mut iteration: u32 = 0;
75
76 let tx_res = loop {
77 iteration += 1;
78
79 if db.state.needs_update() {
81 debug!(
82 "evm cache miss (iteration {}): {:?}",
83 iteration,
84 db.state.access.as_ref().unwrap()
85 );
86 db.state
87 .update_state()
88 .await
89 .map_err(|e| EvmError::Generic(e.to_string()))?;
90 }
91
92 let context = self.get_context(tx, &block, validate_tx);
94
95 let (result, needs_update) = {
97 let mut evm = context
98 .with_db(&mut db)
99 .build_mainnet()
100 .with_precompiles(DataNetworkPrecompiles::default());
101 let res = evm.replay();
102 let needs_update = evm.db_mut().state.needs_update();
103 (res, needs_update)
104 };
105
106 if result.is_ok() || !needs_update {
107 break result.map(|res| (res.result, mem::take(&mut db.state.accounts)));
108 }
109 };
110
111 tx_res.map_err(|err| EvmError::Generic(format!("generic: {err}")))
112 }
113
114 fn get_context(
115 &self,
116 tx: &TransactionRequest,
117 block: &Block<Transaction>,
118 validate_tx: bool,
119 ) -> Context {
120 let spec = get_spec_id_for_block_timestamp(block.header.timestamp, &self.fork_schedule);
121 let mut tx_env = Self::tx_env(tx, spec);
122
123 if <TxType as Into<u8>>::into(
124 <TransactionRequest as TransactionBuilder<DataNetwork>>::output_tx_type(tx),
125 ) == 0u8
126 {
127 tx_env.chain_id = None;
128 } else {
129 tx_env.chain_id = Some(self.chain_id);
130 }
131
132 let mut cfg = CfgEnv::default();
133 cfg.spec = get_spec_id_for_block_timestamp(block.header.timestamp, &self.fork_schedule);
134 cfg.chain_id = self.chain_id;
135 cfg.disable_block_gas_limit = !validate_tx;
136 cfg.disable_eip3607 = !validate_tx;
137 cfg.disable_base_fee = !validate_tx;
138 cfg.disable_nonce_check = !validate_tx;
139
140 Context::mainnet()
141 .with_tx(tx_env)
142 .with_block(Self::block_env(block, &self.fork_schedule))
143 .with_cfg(cfg)
144 }
145
146 fn tx_env(tx: &TransactionRequest, spec: SpecId) -> TxEnv {
147 let default_gas_limit = if spec >= SpecId::OSAKA {
148 eip7825::TX_GAS_LIMIT_CAP
149 } else {
150 u64::MAX
151 };
152 TxEnv {
153 tx_type: tx.transaction_type.unwrap_or_default(),
154 caller: tx.from.unwrap_or_default(),
155 gas_limit: <TransactionRequest as TransactionBuilder<DataNetwork>>::gas_limit(tx)
156 .unwrap_or(default_gas_limit),
157 gas_price: <TransactionRequest as TransactionBuilder<DataNetwork>>::gas_price(tx)
158 .unwrap_or_default(),
159 kind: tx.to.unwrap_or_default(),
160 value: tx.value.unwrap_or_default(),
161 data: <TransactionRequest as TransactionBuilder<DataNetwork>>::input(tx)
162 .unwrap_or_default()
163 .clone(),
164 nonce: <TransactionRequest as TransactionBuilder<DataNetwork>>::nonce(tx)
165 .unwrap_or_default(),
166 chain_id: <TransactionRequest as TransactionBuilder<DataNetwork>>::chain_id(tx),
167 access_list: <TransactionRequest as TransactionBuilder<DataNetwork>>::access_list(tx)
168 .cloned()
169 .unwrap_or_default(),
170 gas_priority_fee:
171 <TransactionRequest as TransactionBuilder<DataNetwork>>::max_priority_fee_per_gas(
172 tx,
173 ),
174 max_fee_per_blob_gas: tx.max_fee_per_blob_gas.unwrap_or_default(),
175 blob_hashes: tx
176 .blob_versioned_hashes
177 .as_ref()
178 .map(|v| v.to_vec())
179 .unwrap_or_default(),
180 authorization_list: vec![],
181 }
182 }
183
184 fn block_env(block: &Block<Transaction, Header>, fork_schedule: &ForkSchedule) -> BlockEnv {
185 let blob_base_fee_update_fraction =
187 fork_schedule.get_blob_base_fee_update_fraction(block.header.timestamp());
188
189 let blob_excess_gas_and_price = block
190 .header
191 .excess_blob_gas()
192 .map(|v| BlobExcessGasAndPrice::new(v, blob_base_fee_update_fraction))
193 .unwrap_or_else(|| BlobExcessGasAndPrice::new(0, blob_base_fee_update_fraction));
194
195 BlockEnv {
196 number: U256::from(block.header.number()),
197 beneficiary: block.header.beneficiary(),
198 timestamp: U256::from(block.header.timestamp()),
199 gas_limit: block.header.gas_limit(),
200 basefee: block.header.base_fee_per_gas().unwrap_or_default(),
201 difficulty: block.header.difficulty(),
202 prevrandao: block.header.mix_hash(),
203 blob_excess_gas_and_price: Some(blob_excess_gas_and_price),
204 }
205 }
206}
207
208pub fn get_spec_id_for_block_timestamp(timestamp: u64, fork_schedule: &ForkSchedule) -> SpecId {
209 if timestamp >= fork_schedule.osaka_timestamp {
210 SpecId::OSAKA
211 } else if timestamp >= fork_schedule.prague_timestamp {
212 SpecId::PRAGUE
213 } else if timestamp >= fork_schedule.cancun_timestamp {
214 SpecId::CANCUN
215 } else if timestamp >= fork_schedule.shanghai_timestamp {
216 SpecId::SHANGHAI
217 } else if timestamp >= fork_schedule.paris_timestamp {
218 SpecId::MERGE
219 } else if timestamp >= fork_schedule.gray_glacier_timestamp {
220 SpecId::GRAY_GLACIER
221 } else if timestamp >= fork_schedule.arrow_glacier_timestamp {
222 SpecId::ARROW_GLACIER
223 } else if timestamp >= fork_schedule.london_timestamp {
224 SpecId::LONDON
225 } else if timestamp >= fork_schedule.berlin_timestamp {
226 SpecId::BERLIN
227 } else if timestamp >= fork_schedule.muir_glacier_timestamp {
228 SpecId::MUIR_GLACIER
229 } else if timestamp >= fork_schedule.istanbul_timestamp {
230 SpecId::ISTANBUL
231 } else if timestamp >= fork_schedule.petersburg_timestamp {
232 SpecId::PETERSBURG
233 } else if timestamp >= fork_schedule.constantinople_timestamp {
234 SpecId::CONSTANTINOPLE
235 } else if timestamp >= fork_schedule.byzantium_timestamp {
236 SpecId::BYZANTIUM
237 } else if timestamp >= fork_schedule.spurious_dragon_timestamp {
238 SpecId::SPURIOUS_DRAGON
239 } else if timestamp >= fork_schedule.tangerine_timestamp {
240 SpecId::TANGERINE
241 } else if timestamp >= fork_schedule.dao_timestamp {
242 SpecId::DAO_FORK
243 } else if timestamp >= fork_schedule.homestead_timestamp {
244 SpecId::HOMESTEAD
245 } else if timestamp >= fork_schedule.frontier_timestamp {
246 SpecId::FRONTIER
247 } else {
248 SpecId::default()
249 }
250}