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
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 53 additions & 18 deletions crates/context/src/journal/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub trait JournalEntryTr {
fn account_destroyed(
address: Address,
target: Address,
was_destroyed: bool,
destroyed_status: SelfdestructionRevertStatus,
had_balance: U256,
) -> Self;

Expand All @@ -39,7 +39,7 @@ pub trait JournalEntryTr {
fn nonce_changed(address: Address) -> Self;

/// Creates a journal entry for when a new account is created
fn account_created(address: Address) -> Self;
fn account_created(address: Address, is_created_globaly: bool) -> Self;

/// Creates a journal entry for when a storage slot is modified
/// Records the previous value for reverting
Expand Down Expand Up @@ -87,6 +87,24 @@ pub trait JournalEntryTr {
);
}

/// Status of selfdestruction revert.
///
/// Global selfdestruction means that selfdestruct is called for first time in global scope.
///
/// Locally selfdesturction that selfdestruct is called for first time in one transaction scope.
///
/// Repeated selfdestruction means local selfdesturction was already called in one transaction scope.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SelfdestructionRevertStatus {
/// Selfdestruct is called for first time in global scope.
GloballySelfdestroyed,
/// Selfdestruct is called for first time in one transaction scope.
LocallySelfdestroyed,
/// Selfdestruct is called again in one transaction scope.
RepeatedSelfdestruction,
}

/// Journal entries that are used to track changes to the state and are used to revert it.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Expand All @@ -108,8 +126,8 @@ pub enum JournalEntry {
address: Address,
/// Address of account that received the balance.
target: Address,
/// Whether the account had already been destroyed before this journal entry.
was_destroyed: bool,
/// Status of selfdestruction revert.
destroyed_status: SelfdestructionRevertStatus,
},
/// Loading account does not mean that account will need to be added to MerkleTree (touched).
/// Only when account is called (to execute contract or transfer balance) only then account is made touched.
Expand Down Expand Up @@ -154,6 +172,8 @@ pub enum JournalEntry {
/// Address of account that is created.
/// On revert, this account will be set to empty.
address: Address,
/// If account is created globally for first time.
is_created_globally: bool,
},
/// Entry used to track storage changes
/// Action: Storage change
Expand Down Expand Up @@ -202,13 +222,13 @@ impl JournalEntryTr for JournalEntry {
fn account_destroyed(
address: Address,
target: Address,
was_destroyed: bool, // if account had already been destroyed before this journal entry
had_balance: U256,
destroyed_status: SelfdestructionRevertStatus,
had_balance: StorageValue,
) -> Self {
JournalEntry::AccountDestroyed {
address,
target,
was_destroyed,
destroyed_status,
had_balance,
}
}
Expand All @@ -228,8 +248,11 @@ impl JournalEntryTr for JournalEntry {
JournalEntry::BalanceTransfer { from, to, balance }
}

fn account_created(address: Address) -> Self {
JournalEntry::AccountCreated { address }
fn account_created(address: Address, is_created_globally: bool) -> Self {
JournalEntry::AccountCreated {
address,
is_created_globally,
}
}

fn storage_changed(address: Address, key: StorageKey, had_value: StorageValue) -> Self {
Expand Down Expand Up @@ -284,19 +307,24 @@ impl JournalEntryTr for JournalEntry {
JournalEntry::AccountDestroyed {
address,
target,
was_destroyed,
destroyed_status,
had_balance,
} => {
let account = state.get_mut(&address).unwrap();
// set previous state of selfdestructed flag, as there could be multiple
// selfdestructs in one transaction.
if was_destroyed {
// flag is still selfdestructed
account.mark_selfdestruct();
} else {
// flag that is not selfdestructed
account.unmark_selfdestruct();
match destroyed_status {
SelfdestructionRevertStatus::GloballySelfdestroyed => {
account.unmark_selfdestruct();
account.unmark_selfdestructed_locally();
}
SelfdestructionRevertStatus::LocallySelfdestroyed => {
account.unmark_selfdestructed_locally();
}
// do nothing on repeated selfdestruction
SelfdestructionRevertStatus::RepeatedSelfdestruction => (),
}

account.info.balance += had_balance;

if address != target {
Expand All @@ -321,9 +349,16 @@ impl JournalEntryTr for JournalEntry {
JournalEntry::NonceChange { address } => {
state.get_mut(&address).unwrap().info.nonce -= 1;
}
JournalEntry::AccountCreated { address } => {
JournalEntry::AccountCreated {
address,
is_created_globally,
} => {
let account = &mut state.get_mut(&address).unwrap();
account.unmark_created();
account.unmark_created_locally();
if is_created_globally {
account.unmark_created();
}
// only account that have nonce == 0 can be created so it is safe to set it to 0.
account.info.nonce = 0;
}
JournalEntry::StorageWarmed { address, key } => {
Expand Down
39 changes: 31 additions & 8 deletions crates/context/src/journal/inner.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
//! Module containing the [`JournalInner`] that is part of [`crate::Journal`].
use crate::entry::SelfdestructionRevertStatus;

use super::JournalEntryTr;
use bytecode::Bytecode;
use context_interface::{
Expand Down Expand Up @@ -419,10 +421,10 @@ impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
}

// set account status to create.
target_acc.mark_created();
let is_created_globaly = target_acc.mark_created_locally();

// this entry will revert set nonce.
last_journal.push(ENTRY::account_created(target_address));
last_journal.push(ENTRY::account_created(target_address, is_created_globaly));
target_acc.info.code = None;
// EIP-161: State trie clearing (invariant-preserving alternative)
if spec_id.is_enabled_in(SPURIOUS_DRAGON) {
Expand Down Expand Up @@ -520,17 +522,25 @@ impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {

let acc = self.state.get_mut(&address).unwrap();
let balance = acc.info.balance;
let previously_destroyed = acc.is_selfdestructed();

let destroyed_status = if !acc.is_selfdestructed() {
SelfdestructionRevertStatus::GloballySelfdestroyed
} else if !acc.is_selfdestructed_locally() {
SelfdestructionRevertStatus::LocallySelfdestroyed
} else {
SelfdestructionRevertStatus::RepeatedSelfdestruction
};

let is_cancun_enabled = spec.is_enabled_in(CANCUN);

// EIP-6780 (Cancun hard-fork): selfdestruct only if contract is created in the same tx
let journal_entry = if acc.is_created() || !is_cancun_enabled {
acc.mark_selfdestruct();
let journal_entry = if acc.is_created_locally() || !is_cancun_enabled {
acc.mark_selfdestructed_locally();
acc.info.balance = U256::ZERO;
Some(ENTRY::account_destroyed(
address,
target,
previously_destroyed,
destroyed_status,
balance,
))
} else if address != target {
Expand All @@ -552,7 +562,8 @@ impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
data: SelfDestructResult {
had_value: !balance.is_zero(),
target_exists: !is_empty,
previously_destroyed,
previously_destroyed: destroyed_status
== SelfdestructionRevertStatus::RepeatedSelfdestruction,
},
is_cold,
})
Expand Down Expand Up @@ -631,7 +642,18 @@ impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
let load = match self.state.entry(address) {
Entry::Occupied(entry) => {
let account = entry.into_mut();
let is_cold = account.mark_warm();
let is_cold = account.mark_warm_with_transaction_id(self.transaction_id);
// if it is colad loaded we need to clear local flags that can interact with selfdestruct
if is_cold {
// if it is cold loaded and we have selfdestructed locally it means that
// account was selfdestructed in previous transaction and we need to clear its information and storage.
if account.is_selfdestructed_locally() {
account.selfdestruct();
account.unmark_selfdestructed_locally();
}
// unmark locally created
account.unmark_created_locally();
}
StateLoad {
data: account,
is_cold,
Expand All @@ -653,6 +675,7 @@ impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
}
}
};

// journal loading of cold account.
if load.is_cold {
self.journal.push(ENTRY::account_warmed(address));
Expand Down
41 changes: 41 additions & 0 deletions crates/context/src/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,47 @@ impl TxEnv {
pub fn builder() -> TxEnvBuilder {
TxEnvBuilder::new()
}

/// Create a new builder for constructing a [`TxEnv`] with benchmark-specific values.
pub fn builder_for_bench() -> TxEnvBuilder {
TxEnv::new_bench().modify()
}

/// Modify the [`TxEnv`] by using builder pattern.
pub fn modify(self) -> TxEnvBuilder {
let TxEnv {
tx_type,
caller,
gas_limit,
gas_price,
kind,
value,
data,
nonce,
chain_id,
access_list,
gas_priority_fee,
blob_hashes,
max_fee_per_blob_gas,
authorization_list,
} = self;

TxEnvBuilder::new()
.tx_type(Some(tx_type))
.caller(caller)
.gas_limit(gas_limit)
.gas_price(gas_price)
.kind(kind)
.value(value)
.data(data)
.nonce(nonce)
.chain_id(chain_id)
.access_list(access_list)
.gas_priority_fee(gas_priority_fee)
.blob_hashes(blob_hashes)
.max_fee_per_blob_gas(max_fee_per_blob_gas)
.authorization_list(authorization_list)
}
}

#[cfg(test)]
Expand Down
2 changes: 2 additions & 0 deletions crates/op-revm/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,8 @@ where

let old_balance = acc.info.balance;

// decrement transaction id as it was incremented when we discarded the tx.
acc.transaction_id -= acc.transaction_id;
acc.info.nonce = acc.info.nonce.saturating_add(1);
acc.info.balance = acc
.info
Expand Down
7 changes: 6 additions & 1 deletion crates/revm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ precompile.workspace = true
primitives.workspace = true
state.workspace = true

[dev-dependencies]
serde_json = { workspace = true, features = ["alloc", "preserve_order"] }
serde = { workspace = true, features = ["derive"] }

[features]
default = ["std", "c-kzg", "secp256k1", "portable", "blst"]
default = ["std", "c-kzg", "secp256k1", "portable", "blst", "tracer"]
std = [
"interpreter/std",
"precompile/std",
Expand All @@ -50,6 +53,8 @@ std = [
"inspector/std",
"primitives/std",
"state/std",
"serde/std",
"serde_json/std",
]
hashbrown = ["interpreter/hashbrown", "precompile/hashbrown"]
serde = [
Expand Down
Loading
Loading