Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bins/revme/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ revm = { workspace = true, features = [
"c-kzg",
"blst",
"serde-json",
"hashbrown",
] }
primitives.workspace = true
database.workspace = true
Expand Down
22 changes: 20 additions & 2 deletions crates/context/interface/src/journaled_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use state::{
Account, Bytecode,
};

/// Trait that contains database and journal of all changes that were made to the state.
pub trait JournalTr {
type Database: Database;
type FinalOutput;
Expand Down Expand Up @@ -55,44 +56,55 @@ pub trait JournalTr {
target: Address,
) -> Result<StateLoad<SelfDestructResult>, <Self::Database as Database>::Error>;

/// Warms the account and storage.
fn warm_account_and_storage(
&mut self,
address: Address,
storage_keys: impl IntoIterator<Item = U256>,
) -> Result<(), <Self::Database as Database>::Error>;

/// Warms the account.
fn warm_account(&mut self, address: Address);

/// Warms the precompiles.
fn warm_precompiles(&mut self, addresses: HashSet<Address>);

/// Returns the addresses of the precompiles.
fn precompile_addresses(&self) -> &HashSet<Address>;

/// Sets the spec id.
fn set_spec_id(&mut self, spec_id: SpecId);

/// Touches the account.
fn touch_account(&mut self, address: Address);

/// Transfers the balance from one account to another.
fn transfer(
&mut self,
from: &Address,
to: &Address,
from: Address,
to: Address,
balance: U256,
) -> Result<Option<TransferError>, <Self::Database as Database>::Error>;

/// Increments the nonce of the account.
fn inc_account_nonce(
&mut self,
address: Address,
) -> Result<Option<u64>, <Self::Database as Database>::Error>;

/// Loads the account.
fn load_account(
&mut self,
address: Address,
) -> Result<StateLoad<&mut Account>, <Self::Database as Database>::Error>;

/// Loads the account code.
fn load_account_code(
&mut self,
address: Address,
) -> Result<StateLoad<&mut Account>, <Self::Database as Database>::Error>;

/// Loads the account delegated.
fn load_account_delegated(
&mut self,
address: Address,
Expand Down Expand Up @@ -158,12 +170,17 @@ pub trait JournalTr {
/// Called at the end of the transaction to clean all residue data from journal.
fn clear(&mut self);

/// Creates a checkpoint of the current state. State can be revert to this point
/// if needed.
fn checkpoint(&mut self) -> JournalCheckpoint;

/// Commits the changes made since the last checkpoint.
fn checkpoint_commit(&mut self);

/// Reverts the changes made since the last checkpoint.
fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint);

/// Creates a checkpoint of the account creation.
fn create_account_checkpoint(
&mut self,
caller: Address,
Expand All @@ -172,6 +189,7 @@ pub trait JournalTr {
spec_id: SpecId,
) -> Result<JournalCheckpoint, TransferError>;

/// Returns the depth of the journal.
fn depth(&self) -> usize;

/// Does cleanup and returns modified state.
Expand Down
8 changes: 5 additions & 3 deletions crates/context/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{block::BlockEnv, cfg::CfgEnv, journaled_state::Journal, tx::TxEnv};
use crate::{block::BlockEnv, cfg::CfgEnv, journal::Journal, tx::TxEnv};
use context_interface::{
context::{ContextError, ContextSetters},
Block, Cfg, ContextTr, JournalTr, Transaction,
Expand Down Expand Up @@ -157,12 +157,14 @@ where
}

/// Creates a new context with a new database type.
///
/// This will create a new [`Journal`] object.
pub fn with_db<ODB: Database>(
self,
db: ODB,
) -> Context<BLOCK, TX, CFG, ODB, Journal<ODB>, CHAIN> {
let spec = self.cfg.spec().into();
let mut journaled_state = Journal::new(spec, db);
let mut journaled_state = Journal::new(db);
journaled_state.set_spec_id(spec);
Context {
tx: self.tx,
Expand All @@ -180,7 +182,7 @@ where
db: ODB,
) -> Context<BLOCK, TX, CFG, WrapDatabaseRef<ODB>, Journal<WrapDatabaseRef<ODB>>, CHAIN> {
let spec = self.cfg.spec().into();
let mut journaled_state = Journal::new(spec, WrapDatabaseRef(db));
let mut journaled_state = Journal::new(WrapDatabaseRef(db));
journaled_state.set_spec_id(spec);
Context {
tx: self.tx,
Expand Down
256 changes: 256 additions & 0 deletions crates/context/src/journal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
pub mod entry;
pub mod inner;

pub use entry::{JournalEntry, JournalEntryTr};
pub use inner::JournalInner;

use bytecode::Bytecode;
use context_interface::{
context::{SStoreResult, SelfDestructResult, StateLoad},
journaled_state::{AccountLoad, JournalCheckpoint, JournalTr, TransferError},
};
use core::ops::{Deref, DerefMut};
use database_interface::Database;
use primitives::{hardfork::SpecId, Address, HashSet, Log, B256, U256};
use state::{Account, EvmState};
use std::vec::Vec;

/// A journal of state changes internal to the EVM
///
/// On each additional call, the depth of the journaled state is increased (`depth`) and a new journal is added.
///
/// The journal contains every state change that happens within that call, making it possible to revert changes made in a specific call.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Journal<DB, ENTRY = JournalEntry>
where
ENTRY: JournalEntryTr,
{
/// Database
pub database: DB,
/// Inner journal state.
pub inner: JournalInner<ENTRY>,
}

impl<DB, ENTRY> Deref for Journal<DB, ENTRY>
where
ENTRY: JournalEntryTr,
{
type Target = JournalInner<ENTRY>;

fn deref(&self) -> &Self::Target {
&self.inner
}
}

impl<DB, ENTRY> DerefMut for Journal<DB, ENTRY>
where
ENTRY: JournalEntryTr,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}

impl<DB, ENTRY: JournalEntryTr> Journal<DB, ENTRY> {
/// Creates a new JournaledState by copying state data from a JournalInit and provided database.
/// This allows reusing the state, logs, and other data from a previous execution context while
/// connecting it to a different database backend.
pub fn new_with_inner(database: DB, inner: JournalInner<ENTRY>) -> Self {
Self { database, inner }
}

/// Consumes the [`Journal`] and returns [`JournalInner`].
///
/// If you need to preserve the original journal, use [`Self::to_inner`] instead which clones the state.
pub fn into_init(self) -> JournalInner<ENTRY> {
self.inner
}
}

impl<DB, ENTRY: JournalEntryTr + Clone> Journal<DB, ENTRY> {
/// Creates a new [`JournalInner`] by cloning all internal state data (state, storage, logs, etc)
/// This allows creating a new journaled state with the same state data but without
/// carrying over the original database.
///
/// This is useful when you want to reuse the current state for a new transaction or
/// execution context, but want to start with a fresh database.
pub fn to_inner(&self) -> JournalInner<ENTRY> {
self.inner.clone()
}
}

/// Output of the journal after finalizing.
pub struct JournalOutput {
/// Changes or touched accounts that loads, created or changed in the journal.
pub state: EvmState,
/// Logs that were emitted by contract calls.
pub logs: Vec<Log>,
}

impl<DB: Database, ENTRY: JournalEntryTr> JournalTr for Journal<DB, ENTRY> {
type Database = DB;
type FinalOutput = JournalOutput;

fn new(database: DB) -> Journal<DB, ENTRY> {
Self {
inner: JournalInner::new(SpecId::default()),
database,
}
}

fn db_ref(&self) -> &Self::Database {
&self.database
}

fn db(&mut self) -> &mut Self::Database {
&mut self.database
}

fn sload(
&mut self,
address: Address,
key: U256,
) -> Result<StateLoad<U256>, <Self::Database as Database>::Error> {
self.inner.sload(&mut self.database, address, key)
}

fn sstore(
&mut self,
address: Address,
key: U256,
value: U256,
) -> Result<StateLoad<SStoreResult>, <Self::Database as Database>::Error> {
self.inner.sstore(&mut self.database, address, key, value)
}

fn tload(&mut self, address: Address, key: U256) -> U256 {
self.inner.tload(address, key)
}

fn tstore(&mut self, address: Address, key: U256, value: U256) {
self.inner.tstore(address, key, value)
}

fn log(&mut self, log: Log) {
self.inner.log(log)
}

fn selfdestruct(
&mut self,
address: Address,
target: Address,
) -> Result<StateLoad<SelfDestructResult>, DB::Error> {
self.inner.selfdestruct(&mut self.database, address, target)
}

fn warm_account(&mut self, address: Address) {
self.inner.warm_preloaded_addresses.insert(address);
}

fn warm_precompiles(&mut self, address: HashSet<Address>) {
self.inner.precompiles = address;
self.inner
.warm_preloaded_addresses
.extend(self.inner.precompiles.iter());
}

#[inline]
fn precompile_addresses(&self) -> &HashSet<Address> {
&self.inner.precompiles
}

/// Returns call depth.
#[inline]
fn depth(&self) -> usize {
self.inner.depth
}

fn warm_account_and_storage(
&mut self,
address: Address,
storage_keys: impl IntoIterator<Item = U256>,
) -> Result<(), <Self::Database as Database>::Error> {
self.inner
.initial_account_load(&mut self.database, address, storage_keys)?;
Ok(())
}

fn set_spec_id(&mut self, spec_id: SpecId) {
self.inner.spec = spec_id;
}

fn transfer(
&mut self,
from: Address,
to: Address,
balance: U256,
) -> Result<Option<TransferError>, DB::Error> {
self.inner.transfer(&mut self.database, from, to, balance)
}

fn touch_account(&mut self, address: Address) {
self.inner.touch(address);
}

fn inc_account_nonce(&mut self, address: Address) -> Result<Option<u64>, DB::Error> {
Ok(self.inner.inc_nonce(address))
}

fn load_account(&mut self, address: Address) -> Result<StateLoad<&mut Account>, DB::Error> {
self.inner.load_account(&mut self.database, address)
}

fn load_account_code(
&mut self,
address: Address,
) -> Result<StateLoad<&mut Account>, DB::Error> {
self.inner.load_code(&mut self.database, address)
}

fn load_account_delegated(
&mut self,
address: Address,
) -> Result<StateLoad<AccountLoad>, DB::Error> {
self.inner
.load_account_delegated(&mut self.database, address)
}

fn checkpoint(&mut self) -> JournalCheckpoint {
self.inner.checkpoint()
}

fn checkpoint_commit(&mut self) {
self.inner.checkpoint_commit()
}

fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
self.inner.checkpoint_revert(checkpoint)
}

fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
self.inner.set_code_with_hash(address, code, hash);
}

fn clear(&mut self) {
// Clears the inner journal state. Preserving only the spec.
let spec = self.inner.spec;
self.inner = JournalInner::new(spec);
}

fn create_account_checkpoint(
&mut self,
caller: Address,
address: Address,
balance: U256,
spec_id: SpecId,
) -> Result<JournalCheckpoint, TransferError> {
// Ignore error.
self.inner
.create_account_checkpoint(caller, address, balance, spec_id)
}

fn finalize(&mut self) -> Self::FinalOutput {
self.inner.take_output_and_clear()
}
}
Loading
Loading