mod analysis;
mod pretty_print;
use alloy_primitives::Address;
pub use analysis::SnapshotAnalysis;
use std::{
ops::{Deref, DerefMut},
sync::Arc,
};
use edb_common::types::ExecutionFrameId;
use revm::{database::CacheDB, Database, DatabaseCommit, DatabaseRef};
use serde::{Deserialize, Serialize};
use tracing::error;
use crate::{HookSnapshot, HookSnapshots, OpcodeSnapshot, OpcodeSnapshots, USID};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Snapshot<DB>
where
DB: Database + DatabaseCommit + DatabaseRef + Clone,
<CacheDB<DB> as Database>::Error: Clone,
<DB as Database>::Error: Clone,
{
id: usize,
frame_id: ExecutionFrameId,
next_id: Option<usize>,
prev_id: Option<usize>,
detail: SnapshotDetail<DB>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SnapshotDetail<DB>
where
DB: Database + DatabaseCommit + DatabaseRef + Clone,
<CacheDB<DB> as Database>::Error: Clone,
<DB as Database>::Error: Clone,
{
Opcode(OpcodeSnapshot<DB>),
Hook(HookSnapshot<DB>),
}
impl<DB> Snapshot<DB>
where
DB: Database + DatabaseCommit + DatabaseRef + Clone,
<CacheDB<DB> as Database>::Error: Clone,
<DB as Database>::Error: Clone,
{
pub fn new_opcode(id: usize, frame_id: ExecutionFrameId, detail: OpcodeSnapshot<DB>) -> Self {
Self { id, frame_id, next_id: None, prev_id: None, detail: SnapshotDetail::Opcode(detail) }
}
pub fn new_hook(id: usize, frame_id: ExecutionFrameId, detail: HookSnapshot<DB>) -> Self {
Self { id, frame_id, next_id: None, prev_id: None, detail: SnapshotDetail::Hook(detail) }
}
pub fn set_next_id(&mut self, id: usize) {
self.next_id = Some(id);
}
pub fn next_id(&self) -> Option<usize> {
self.next_id
}
pub fn set_prev_id(&mut self, id: usize) {
self.prev_id = Some(id);
}
pub fn prev_id(&self) -> Option<usize> {
self.prev_id
}
pub fn id(&self) -> usize {
self.id
}
pub fn frame_id(&self) -> ExecutionFrameId {
self.frame_id
}
pub fn detail(&self) -> &SnapshotDetail<DB> {
&self.detail
}
pub fn detail_mut(&mut self) -> &mut SnapshotDetail<DB> {
&mut self.detail
}
pub fn usid(&self) -> Option<USID> {
match &self.detail {
SnapshotDetail::Opcode(_) => None,
SnapshotDetail::Hook(snapshot) => Some(snapshot.usid),
}
}
pub fn db(&self) -> Arc<CacheDB<DB>> {
match &self.detail {
SnapshotDetail::Opcode(snapshot) => snapshot.database.clone(),
SnapshotDetail::Hook(snapshot) => snapshot.database.clone(),
}
}
pub fn bytecode_address(&self) -> Address {
match &self.detail {
SnapshotDetail::Opcode(snapshot) => snapshot.bytecode_address,
SnapshotDetail::Hook(snapshot) => snapshot.bytecode_address,
}
}
pub fn target_address(&self) -> Address {
match &self.detail {
SnapshotDetail::Opcode(snapshot) => snapshot.target_address,
SnapshotDetail::Hook(snapshot) => snapshot.target_address,
}
}
pub fn is_hook(&self) -> bool {
matches!(self.detail, SnapshotDetail::Hook(_))
}
pub fn is_opcode(&self) -> bool {
matches!(self.detail, SnapshotDetail::Opcode(_))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Snapshots<DB>
where
DB: Database + DatabaseCommit + DatabaseRef + Clone,
<CacheDB<DB> as Database>::Error: Clone,
<DB as Database>::Error: Clone,
{
inner: Vec<(ExecutionFrameId, Snapshot<DB>)>,
}
impl<DB> Deref for Snapshots<DB>
where
DB: Database + DatabaseCommit + DatabaseRef + Clone,
<CacheDB<DB> as Database>::Error: Clone,
<DB as Database>::Error: Clone,
{
type Target = [(ExecutionFrameId, Snapshot<DB>)];
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<DB> DerefMut for Snapshots<DB>
where
DB: Database + DatabaseCommit + DatabaseRef + Clone,
<CacheDB<DB> as Database>::Error: Clone,
<DB as Database>::Error: Clone,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<DB> IntoIterator for Snapshots<DB>
where
DB: Database + DatabaseCommit + DatabaseRef + Clone,
<CacheDB<DB> as Database>::Error: Clone,
<DB as Database>::Error: Clone,
{
type Item = (ExecutionFrameId, Snapshot<DB>);
type IntoIter = std::vec::IntoIter<(ExecutionFrameId, Snapshot<DB>)>;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl<'a, DB> IntoIterator for &'a Snapshots<DB>
where
DB: Database + DatabaseCommit + DatabaseRef + Clone,
<CacheDB<DB> as Database>::Error: Clone,
<DB as Database>::Error: Clone,
{
type Item = &'a (ExecutionFrameId, Snapshot<DB>);
type IntoIter = std::slice::Iter<'a, (ExecutionFrameId, Snapshot<DB>)>;
fn into_iter(self) -> Self::IntoIter {
self.inner.iter()
}
}
impl<'a, DB> IntoIterator for &'a mut Snapshots<DB>
where
DB: Database + DatabaseCommit + DatabaseRef + Clone,
<CacheDB<DB> as Database>::Error: Clone,
<DB as Database>::Error: Clone,
{
type Item = &'a mut (ExecutionFrameId, Snapshot<DB>);
type IntoIter = std::slice::IterMut<'a, (ExecutionFrameId, Snapshot<DB>)>;
fn into_iter(self) -> Self::IntoIter {
self.inner.iter_mut()
}
}
impl<DB> Default for Snapshots<DB>
where
DB: Database + DatabaseCommit + DatabaseRef + Clone,
<CacheDB<DB> as Database>::Error: Clone,
<DB as Database>::Error: Clone,
{
fn default() -> Self {
Self::new()
}
}
impl<DB> Snapshots<DB>
where
DB: Database + DatabaseCommit + DatabaseRef + Clone,
<CacheDB<DB> as Database>::Error: Clone,
<DB as Database>::Error: Clone,
{
pub fn new() -> Self {
Self { inner: Vec::new() }
}
pub fn merge(
mut opcode_snapshots: OpcodeSnapshots<DB>,
hook_snapshots: HookSnapshots<DB>,
) -> Self {
let mut inner = Vec::new();
for (frame_id, snapshot_opt) in hook_snapshots {
match snapshot_opt {
Some(snapshot) => {
inner.push((frame_id, Snapshot::new_hook(inner.len(), frame_id, snapshot)));
}
None => {
if let Some(opcode_frame_snapshots) = opcode_snapshots.remove(&frame_id) {
for opcode_snapshot in opcode_frame_snapshots {
inner.push((
frame_id,
Snapshot::new_opcode(inner.len(), frame_id, opcode_snapshot),
));
}
}
}
}
}
if opcode_snapshots.values().any(|snapshots| !snapshots.is_empty()) {
error!(
"There are still opcode snapshots left after merging: {:?}",
opcode_snapshots.keys().collect::<Vec<_>>()
);
}
Self { inner }
}
pub fn get_frame_snapshots(&self, frame_id: ExecutionFrameId) -> Vec<&Snapshot<DB>> {
self.inner
.iter()
.filter_map(|(id, snapshot)| if *id == frame_id { Some(snapshot) } else { None })
.collect()
}
pub fn get_frame_ids(&self) -> Vec<ExecutionFrameId> {
let mut frame_ids: Vec<_> = self.inner.iter().map(|(id, _)| *id).collect();
frame_ids.dedup();
frame_ids
}
pub fn total_snapshot_count(&self) -> usize {
self.inner.len()
}
pub fn frame_count(&self) -> usize {
self.get_frame_ids().len()
}
}