1use std::{
2 sync::{Arc, Mutex},
3 time::Duration,
4};
5
6use ethrex_common::{
7 H256,
8 tracing::{CallTrace, OpcodeTraceResult, PrestateResult},
9 types::Block,
10};
11use ethrex_crypto::NativeCrypto;
12use ethrex_storage::Store;
13use ethrex_vm::tracing::OpcodeTracerConfig;
14use ethrex_vm::{Evm, EvmError};
15
16use crate::{Blockchain, error::ChainError, vm::StoreVmDatabase};
17
18impl Blockchain {
19 pub async fn trace_transaction_calls(
22 &self,
23 tx_hash: H256,
24 reexec: u32,
25 timeout: Duration,
26 only_top_call: bool,
27 with_log: bool,
28 ) -> Result<CallTrace, ChainError> {
29 let Some((_, block_hash, tx_index)) =
31 self.storage.get_transaction_location(tx_hash).await?
32 else {
33 return Err(ChainError::Custom("Transaction not Found".to_string()));
34 };
35 let tx_index = tx_index as usize;
36 let Some(block) = self.storage.get_block_by_hash(block_hash).await? else {
37 return Err(ChainError::Custom("Block not Found".to_string()));
38 };
39 let mut vm = self
41 .rebuild_parent_state(block.header.parent_hash, reexec)
42 .await?;
43 vm.rerun_block(&block, Some(tx_index))?;
45 timeout_trace_operation(timeout, move || {
47 vm.trace_tx_calls(&block, tx_index, only_top_call, with_log)
48 })
49 .await
50 }
51
52 pub async fn trace_block_calls(
56 &self,
57 block: Block,
59 reexec: u32,
60 timeout: Duration,
61 only_top_call: bool,
62 with_log: bool,
63 ) -> Result<Vec<(H256, CallTrace)>, ChainError> {
64 let mut vm = self
66 .rebuild_parent_state(block.header.parent_hash, reexec)
67 .await?;
68 vm.rerun_block(&block, Some(0))?;
70 let vm = Arc::new(Mutex::new(vm));
73 let block = Arc::new(block);
74 let mut call_traces = vec![];
75 for index in 0..block.body.transactions.len() {
76 let block = block.clone();
78 let vm = vm.clone();
79 let tx_hash = block.as_ref().body.transactions[index].hash(&NativeCrypto);
80 let call_trace = timeout_trace_operation(timeout, move || {
81 vm.lock()
82 .map_err(|_| EvmError::Custom("Unexpected Runtime Error".to_string()))?
83 .trace_tx_calls(block.as_ref(), index, only_top_call, with_log)
84 })
85 .await?;
86 call_traces.push((tx_hash, call_trace));
87 }
88 Ok(call_traces)
89 }
90
91 pub async fn trace_transaction_prestate(
96 &self,
97 tx_hash: H256,
98 reexec: u32,
99 timeout: Duration,
100 diff_mode: bool,
101 include_empty: bool,
102 ) -> Result<PrestateResult, ChainError> {
103 let Some((_, block_hash, tx_index)) =
104 self.storage.get_transaction_location(tx_hash).await?
105 else {
106 return Err(ChainError::Custom("Transaction not Found".to_string()));
107 };
108 let tx_index = tx_index as usize;
109 let Some(block) = self.storage.get_block_by_hash(block_hash).await? else {
110 return Err(ChainError::Custom("Block not Found".to_string()));
111 };
112 let mut vm = self
113 .rebuild_parent_state(block.header.parent_hash, reexec)
114 .await?;
115 vm.rerun_block(&block, Some(tx_index))?;
117 timeout_trace_operation(timeout, move || {
119 vm.trace_tx_prestate(&block, tx_index, diff_mode, include_empty)
120 })
121 .await
122 }
123
124 pub async fn trace_block_prestate(
130 &self,
131 block: Block,
132 reexec: u32,
133 timeout: Duration,
134 diff_mode: bool,
135 include_empty: bool,
136 ) -> Result<Vec<(H256, PrestateResult)>, ChainError> {
137 let mut vm = self
138 .rebuild_parent_state(block.header.parent_hash, reexec)
139 .await?;
140 vm.rerun_block(&block, Some(0))?;
142 let vm = Arc::new(Mutex::new(vm));
145 let block = Arc::new(block);
146 let mut traces = vec![];
147 for index in 0..block.body.transactions.len() {
148 let block = block.clone();
149 let vm = vm.clone();
150 let tx_hash = block.as_ref().body.transactions[index].hash(&NativeCrypto);
151 let result = timeout_trace_operation(timeout, move || {
152 vm.lock()
153 .map_err(|_| EvmError::Custom("Unexpected Runtime Error".to_string()))?
154 .trace_tx_prestate(block.as_ref(), index, diff_mode, include_empty)
155 })
156 .await?;
157 traces.push((tx_hash, result));
158 }
159 Ok(traces)
160 }
161
162 pub async fn trace_transaction_opcodes(
165 &self,
166 tx_hash: H256,
167 reexec: u32,
168 timeout: Duration,
169 cfg: OpcodeTracerConfig,
170 ) -> Result<OpcodeTraceResult, ChainError> {
171 let Some((_, block_hash, tx_index)) =
172 self.storage.get_transaction_location(tx_hash).await?
173 else {
174 return Err(ChainError::Custom("Transaction not Found".to_string()));
175 };
176 let tx_index = tx_index as usize;
177 let Some(block) = self.storage.get_block_by_hash(block_hash).await? else {
178 return Err(ChainError::Custom("Block not Found".to_string()));
179 };
180 let mut vm = self
181 .rebuild_parent_state(block.header.parent_hash, reexec)
182 .await?;
183 vm.rerun_block(&block, Some(tx_index))?;
184 timeout_trace_operation(timeout, move || vm.trace_tx_opcodes(&block, tx_index, cfg)).await
185 }
186
187 pub async fn trace_block_opcodes(
193 &self,
194 block: Block,
195 reexec: u32,
196 timeout: Duration,
197 cfg: OpcodeTracerConfig,
198 ) -> Result<Vec<(H256, OpcodeTraceResult)>, ChainError> {
199 let mut vm = self
200 .rebuild_parent_state(block.header.parent_hash, reexec)
201 .await?;
202 vm.rerun_block(&block, Some(0))?;
203 let vm = Arc::new(Mutex::new(vm));
204 let block = Arc::new(block);
205 let mut traces = vec![];
206 for index in 0..block.body.transactions.len() {
207 let block = block.clone();
208 let vm = vm.clone();
209 let tx_hash = block.as_ref().body.transactions[index].hash(&NativeCrypto);
210 let cfg = cfg.clone();
211 let result = timeout_trace_operation(timeout, move || {
212 vm.lock()
213 .map_err(|_| EvmError::Custom("Unexpected Runtime Error".to_string()))?
214 .trace_tx_opcodes(block.as_ref(), index, cfg)
215 })
216 .await?;
217 traces.push((tx_hash, result));
218 }
219 Ok(traces)
220 }
221
222 async fn rebuild_parent_state(
225 &self,
226 parent_hash: H256,
227 reexec: u32,
228 ) -> Result<Evm, ChainError> {
229 let blocks_to_re_execute =
231 get_missing_state_parents(parent_hash, &self.storage, reexec).await?;
232 let parent_hash = blocks_to_re_execute
234 .last()
235 .map(|b| b.header.parent_hash)
236 .unwrap_or(parent_hash);
237 let block_hash_cache = blocks_to_re_execute
239 .iter()
240 .map(|b| (b.header.number, b.hash()))
241 .collect();
242 let parent_header = self
243 .storage
244 .get_block_header_by_hash(parent_hash)?
245 .ok_or(ChainError::ParentNotFound)?;
246 let vm_db = StoreVmDatabase::new_with_block_hash_cache(
247 self.storage.clone(),
248 parent_header,
249 block_hash_cache,
250 )?;
251 let mut vm = self.new_evm(vm_db)?;
252 for block in blocks_to_re_execute.iter().rev() {
254 vm.rerun_block(block, None)?;
255 }
256 Ok(vm)
257 }
258}
259
260async fn get_missing_state_parents(
265 mut parent_hash: H256,
266 store: &Store,
267 reexec: u32,
268) -> Result<Vec<Block>, ChainError> {
269 let mut missing_state_parents = Vec::new();
270 loop {
271 if missing_state_parents.len() > reexec as usize {
272 return Err(ChainError::Custom(
273 "Exceeded max amount of blocks to re-execute for tracing".to_string(),
274 ));
275 }
276 let Some(parent_block) = store.get_block_by_hash(parent_hash).await? else {
277 return Err(ChainError::Custom("Parent Block not Found".to_string()));
278 };
279 if store.has_state_root(parent_block.header.state_root)? {
280 break;
281 }
282 parent_hash = parent_block.header.parent_hash;
283 missing_state_parents.push(parent_block);
285 }
286 Ok(missing_state_parents)
287}
288
289async fn timeout_trace_operation<O, T>(timeout: Duration, operation: O) -> Result<T, ChainError>
291where
292 O: FnOnce() -> Result<T, EvmError> + Send + 'static,
293 T: Send + 'static,
294{
295 Ok(
296 tokio::time::timeout(timeout, tokio::task::spawn_blocking(operation))
297 .await
298 .map_err(|_| ChainError::Custom("Tracing Timeout".to_string()))?
299 .map_err(|_| ChainError::Custom("Unexpected Runtime Error".to_string()))??,
300 )
301}