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