1mod analysis;
32mod pretty_print;
33
34use alloy_primitives::Address;
35pub use analysis::SnapshotAnalysis;
36
37use std::{
38 ops::{Deref, DerefMut},
39 sync::Arc,
40};
41
42use edb_common::types::ExecutionFrameId;
43use revm::{database::CacheDB, Database, DatabaseCommit, DatabaseRef};
44use serde::{Deserialize, Serialize};
45use tracing::error;
46
47use crate::{HookSnapshot, HookSnapshots, OpcodeSnapshot, OpcodeSnapshots, USID};
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct Snapshot<DB>
57where
58 DB: Database + DatabaseCommit + DatabaseRef + Clone,
59 <CacheDB<DB> as Database>::Error: Clone,
60 <DB as Database>::Error: Clone,
61{
62 id: usize,
63 frame_id: ExecutionFrameId,
64 next_id: Option<usize>,
65 prev_id: Option<usize>,
66
67 detail: SnapshotDetail<DB>,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
78pub enum SnapshotDetail<DB>
79where
80 DB: Database + DatabaseCommit + DatabaseRef + Clone,
81 <CacheDB<DB> as Database>::Error: Clone,
82 <DB as Database>::Error: Clone,
83{
84 Opcode(OpcodeSnapshot<DB>),
86 Hook(HookSnapshot<DB>),
88}
89
90impl<DB> Snapshot<DB>
91where
92 DB: Database + DatabaseCommit + DatabaseRef + Clone,
93 <CacheDB<DB> as Database>::Error: Clone,
94 <DB as Database>::Error: Clone,
95{
96 pub fn new_opcode(id: usize, frame_id: ExecutionFrameId, detail: OpcodeSnapshot<DB>) -> Self {
98 Self { id, frame_id, next_id: None, prev_id: None, detail: SnapshotDetail::Opcode(detail) }
99 }
100
101 pub fn new_hook(id: usize, frame_id: ExecutionFrameId, detail: HookSnapshot<DB>) -> Self {
103 Self { id, frame_id, next_id: None, prev_id: None, detail: SnapshotDetail::Hook(detail) }
104 }
105
106 pub fn set_next_id(&mut self, id: usize) {
108 self.next_id = Some(id);
109 }
110
111 pub fn next_id(&self) -> Option<usize> {
113 self.next_id
114 }
115
116 pub fn set_prev_id(&mut self, id: usize) {
118 self.prev_id = Some(id);
119 }
120
121 pub fn prev_id(&self) -> Option<usize> {
123 self.prev_id
124 }
125
126 pub fn id(&self) -> usize {
128 self.id
129 }
130
131 pub fn frame_id(&self) -> ExecutionFrameId {
133 self.frame_id
134 }
135
136 pub fn detail(&self) -> &SnapshotDetail<DB> {
138 &self.detail
139 }
140
141 pub fn detail_mut(&mut self) -> &mut SnapshotDetail<DB> {
143 &mut self.detail
144 }
145
146 pub fn usid(&self) -> Option<USID> {
148 match &self.detail {
149 SnapshotDetail::Opcode(_) => None,
150 SnapshotDetail::Hook(snapshot) => Some(snapshot.usid),
151 }
152 }
153
154 pub fn db(&self) -> Arc<CacheDB<DB>> {
156 match &self.detail {
157 SnapshotDetail::Opcode(snapshot) => snapshot.database.clone(),
158 SnapshotDetail::Hook(snapshot) => snapshot.database.clone(),
159 }
160 }
161
162 pub fn bytecode_address(&self) -> Address {
164 match &self.detail {
165 SnapshotDetail::Opcode(snapshot) => snapshot.bytecode_address,
166 SnapshotDetail::Hook(snapshot) => snapshot.bytecode_address,
167 }
168 }
169
170 pub fn target_address(&self) -> Address {
172 match &self.detail {
173 SnapshotDetail::Opcode(snapshot) => snapshot.target_address,
174 SnapshotDetail::Hook(snapshot) => snapshot.target_address,
175 }
176 }
177
178 pub fn is_hook(&self) -> bool {
180 matches!(self.detail, SnapshotDetail::Hook(_))
181 }
182
183 pub fn is_opcode(&self) -> bool {
185 matches!(self.detail, SnapshotDetail::Opcode(_))
186 }
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct Snapshots<DB>
201where
202 DB: Database + DatabaseCommit + DatabaseRef + Clone,
203 <CacheDB<DB> as Database>::Error: Clone,
204 <DB as Database>::Error: Clone,
205{
206 inner: Vec<(ExecutionFrameId, Snapshot<DB>)>,
208}
209
210impl<DB> Deref for Snapshots<DB>
211where
212 DB: Database + DatabaseCommit + DatabaseRef + Clone,
213 <CacheDB<DB> as Database>::Error: Clone,
214 <DB as Database>::Error: Clone,
215{
216 type Target = [(ExecutionFrameId, Snapshot<DB>)];
217
218 fn deref(&self) -> &Self::Target {
219 &self.inner
220 }
221}
222
223impl<DB> DerefMut for Snapshots<DB>
224where
225 DB: Database + DatabaseCommit + DatabaseRef + Clone,
226 <CacheDB<DB> as Database>::Error: Clone,
227 <DB as Database>::Error: Clone,
228{
229 fn deref_mut(&mut self) -> &mut Self::Target {
230 &mut self.inner
231 }
232}
233
234impl<DB> IntoIterator for Snapshots<DB>
236where
237 DB: Database + DatabaseCommit + DatabaseRef + Clone,
238 <CacheDB<DB> as Database>::Error: Clone,
239 <DB as Database>::Error: Clone,
240{
241 type Item = (ExecutionFrameId, Snapshot<DB>);
242 type IntoIter = std::vec::IntoIter<(ExecutionFrameId, Snapshot<DB>)>;
243 fn into_iter(self) -> Self::IntoIter {
244 self.inner.into_iter()
245 }
246}
247
248impl<'a, DB> IntoIterator for &'a Snapshots<DB>
250where
251 DB: Database + DatabaseCommit + DatabaseRef + Clone,
252 <CacheDB<DB> as Database>::Error: Clone,
253 <DB as Database>::Error: Clone,
254{
255 type Item = &'a (ExecutionFrameId, Snapshot<DB>);
256 type IntoIter = std::slice::Iter<'a, (ExecutionFrameId, Snapshot<DB>)>;
257 fn into_iter(self) -> Self::IntoIter {
258 self.inner.iter()
259 }
260}
261
262impl<'a, DB> IntoIterator for &'a mut Snapshots<DB>
264where
265 DB: Database + DatabaseCommit + DatabaseRef + Clone,
266 <CacheDB<DB> as Database>::Error: Clone,
267 <DB as Database>::Error: Clone,
268{
269 type Item = &'a mut (ExecutionFrameId, Snapshot<DB>);
270 type IntoIter = std::slice::IterMut<'a, (ExecutionFrameId, Snapshot<DB>)>;
271 fn into_iter(self) -> Self::IntoIter {
272 self.inner.iter_mut()
273 }
274}
275
276impl<DB> Default for Snapshots<DB>
277where
278 DB: Database + DatabaseCommit + DatabaseRef + Clone,
279 <CacheDB<DB> as Database>::Error: Clone,
280 <DB as Database>::Error: Clone,
281{
282 fn default() -> Self {
283 Self::new()
284 }
285}
286
287impl<DB> Snapshots<DB>
288where
289 DB: Database + DatabaseCommit + DatabaseRef + Clone,
290 <CacheDB<DB> as Database>::Error: Clone,
291 <DB as Database>::Error: Clone,
292{
293 pub fn new() -> Self {
295 Self { inner: Vec::new() }
296 }
297
298 pub fn merge(
308 mut opcode_snapshots: OpcodeSnapshots<DB>,
309 hook_snapshots: HookSnapshots<DB>,
310 ) -> Self {
311 let mut inner = Vec::new();
312
313 for (frame_id, snapshot_opt) in hook_snapshots {
315 match snapshot_opt {
316 Some(snapshot) => {
317 inner.push((frame_id, Snapshot::new_hook(inner.len(), frame_id, snapshot)));
319 }
320 None => {
321 if let Some(opcode_frame_snapshots) = opcode_snapshots.remove(&frame_id) {
324 for opcode_snapshot in opcode_frame_snapshots {
326 inner.push((
327 frame_id,
328 Snapshot::new_opcode(inner.len(), frame_id, opcode_snapshot),
329 ));
330 }
331 }
332 }
333 }
334 }
335
336 if opcode_snapshots.values().any(|snapshots| !snapshots.is_empty()) {
338 error!(
339 "There are still opcode snapshots left after merging: {:?}",
340 opcode_snapshots.keys().collect::<Vec<_>>()
341 );
342 }
343
344 Self { inner }
345 }
346
347 pub fn get_frame_snapshots(&self, frame_id: ExecutionFrameId) -> Vec<&Snapshot<DB>> {
349 self.inner
350 .iter()
351 .filter_map(|(id, snapshot)| if *id == frame_id { Some(snapshot) } else { None })
352 .collect()
353 }
354
355 pub fn get_frame_ids(&self) -> Vec<ExecutionFrameId> {
357 let mut frame_ids: Vec<_> = self.inner.iter().map(|(id, _)| *id).collect();
358 frame_ids.dedup();
359 frame_ids
360 }
361
362 pub fn total_snapshot_count(&self) -> usize {
364 self.inner.len()
365 }
366
367 pub fn frame_count(&self) -> usize {
369 self.get_frame_ids().len()
370 }
371}