diff --git a/bins/revme/src/statetest/models/spec.rs b/bins/revme/src/statetest/models/spec.rs index cf24caaca2..cb29fd225f 100644 --- a/bins/revme/src/statetest/models/spec.rs +++ b/bins/revme/src/statetest/models/spec.rs @@ -23,6 +23,7 @@ pub enum SpecName { Merge, //done #[serde(alias = "Merge+3540+3670")] MergeEOF, + Shanghai, } impl SpecName { @@ -41,6 +42,7 @@ impl SpecName { Self::London | Self::BerlinToLondonAt5 => SpecId::LONDON, Self::Merge => SpecId::MERGE, Self::MergeEOF => SpecId::MERGE_EOF, + Self::Shanghai => SpecId::SHANGHAI, Self::ByzantiumToConstantinopleAt5 | Self::Constantinople => { panic!("Overriden with PETERSBURG") } //_ => panic!("Conversion failed"), diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 5b864e015c..dd1ecb92fd 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -169,6 +169,7 @@ pub fn evm_inner<'a, DB: Database, const INSPECT: bool>( } SpecId::MERGE => create_evm!(MergeSpec, db, env, insp), SpecId::MERGE_EOF => create_evm!(MergeSpec, db, env, insp), + SpecId::SHANGHAI => create_evm!(ShanghaiSpec, db, env, insp), SpecId::LATEST => create_evm!(LatestSpec, db, env, insp), } } diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs index ba57871c57..f18f81e1e7 100644 --- a/crates/revm/src/evm_impl.rs +++ b/crates/revm/src/evm_impl.rs @@ -794,6 +794,22 @@ impl<'a, GSPEC: Spec, DB: Database + 'a, const INSPECT: bool> Host .ok() } + fn tload( + &mut self, + address: H160, + index: U256, + ) -> U256 { + self.data + .journaled_state + .tload(address, index) + } + + fn tstore(&mut self, address: H160, index: U256, value: U256) { + self.data + .journaled_state + .tstore(address, index, value) + } + fn log(&mut self, address: H160, topics: Vec, data: Bytes) { if INSPECT { self.inspector.log(&mut self.data, &address, &topics, &data); @@ -881,6 +897,10 @@ pub trait Host { index: U256, value: U256, ) -> Option<(U256, U256, U256, bool)>; + + fn tload(&mut self, address: H160, index: U256) -> U256; + fn tstore(&mut self, address: H160, index: U256, value: U256); + /// Create a log owned by address with given topics and data. fn log(&mut self, address: H160, topics: Vec, data: Bytes); /// Mark an address to be deleted, with funds transferred to target. diff --git a/crates/revm/src/instructions.rs b/crates/revm/src/instructions.rs index 296f46e57f..60dcc0687e 100644 --- a/crates/revm/src/instructions.rs +++ b/crates/revm/src/instructions.rs @@ -229,6 +229,8 @@ pub fn eval(opcode: u8, interp: &mut Interpreter, host: &mut H opcode::GASLIMIT => host_env::gaslimit(interp, host), opcode::SLOAD => host::sload::(interp, host), opcode::SSTORE => host::sstore::(interp, host), + opcode::TSTORE => host::tstore::(interp, host), + opcode::TLOAD => host::tload::(interp, host), opcode::GAS => system::gas(interp), opcode::LOG0 => host::log::(interp, 0, host), opcode::LOG1 => host::log::(interp, 1, host), diff --git a/crates/revm/src/instructions/host.rs b/crates/revm/src/instructions/host.rs index 4f482ee6f8..f037fc685f 100644 --- a/crates/revm/src/instructions/host.rs +++ b/crates/revm/src/instructions/host.rs @@ -159,6 +159,26 @@ pub fn sstore(interp: &mut Interpreter, host: &mut H) -> Re interp.add_next_gas_block(interp.program_counter() - 1) } +pub fn tstore(interp: &mut Interpreter, host: &mut H) -> Return { + check!(!SPEC::IS_STATIC_CALL); + gas!(interp, gas::WARM_STORAGE_READ_COST); + + pop!(interp, index, value); + + host.tstore(interp.contract.address, index, value); + Return::Continue +} + +pub fn tload(interp: &mut Interpreter, host: &mut H) -> Return { + gas!(interp, gas::WARM_STORAGE_READ_COST); + + pop!(interp, index); + + let value = host.tload(interp.contract.address, index); + push!(interp, value); + Return::Continue +} + pub fn log(interp: &mut Interpreter, n: u8, host: &mut H) -> Return { check!(!SPEC::IS_STATIC_CALL); diff --git a/crates/revm/src/instructions/opcode.rs b/crates/revm/src/instructions/opcode.rs index 3d02c5c545..ee19268555 100644 --- a/crates/revm/src/instructions/opcode.rs +++ b/crates/revm/src/instructions/opcode.rs @@ -142,6 +142,8 @@ pub const LOG1: u8 = 0xa1; pub const LOG2: u8 = 0xa2; pub const LOG3: u8 = 0xa3; pub const LOG4: u8 = 0xa4; +pub const TLOAD: u8 = 0xb3; +pub const TSTORE: u8 = 0xb4; pub const CREATE: u8 = 0xf0; pub const CREATE2: u8 = 0xf5; pub const CALL: u8 = 0xf1; @@ -473,8 +475,18 @@ macro_rules! gas_opcodee { /* 0xb0 */ OpInfo::none(), /* 0xb1 */ OpInfo::none(), /* 0xb2 */ OpInfo::none(), - /* 0xb3 */ OpInfo::none(), - /* 0xb4 */ OpInfo::none(), + /* 0xb3 TLOAD */ + OpInfo::gas(if SpecId::enabled($spec_id, SpecId::SHANGHAI) { + gas::WARM_STORAGE_READ_COST + } else { + 0 + }), + /* 0xb4 TSTORE */ + OpInfo::gas(if SpecId::enabled($spec_id, SpecId::SHANGHAI) { + gas::WARM_STORAGE_READ_COST + } else { + 0 + }), /* 0xb5 */ OpInfo::none(), /* 0xb6 */ OpInfo::none(), /* 0xb7 */ OpInfo::none(), @@ -620,6 +632,10 @@ pub const fn spec_opcode_gas(spec_id: SpecId) -> &'static [OpInfo; 256] { gas_opcodee!(MERGE, SpecId::MERGE); MERGE } + SpecId::SHANGHAI => { + gas_opcodee!(SHANGHAI, SpecId::SHANGHAI); + SHANGHAI + } SpecId::MERGE_EOF => { gas_opcodee!(MERGE_EOF, SpecId::MERGE_EOF); MERGE_EOF @@ -811,8 +827,8 @@ pub const OPCODE_JUMPMAP: [Option<&'static str>; 256] = [ /* 0xb0 */ None, /* 0xb1 */ None, /* 0xb2 */ None, - /* 0xb3 */ None, - /* 0xb4 */ None, + /* 0xb3 */ Some("TLOAD"), + /* 0xb4 */ Some("TSTORE"), /* 0xb5 */ None, /* 0xb6 */ None, /* 0xb7 */ None, diff --git a/crates/revm/src/interpreter.rs b/crates/revm/src/interpreter.rs index 9e83e94beb..0b6d684fce 100644 --- a/crates/revm/src/interpreter.rs +++ b/crates/revm/src/interpreter.rs @@ -2,11 +2,13 @@ pub mod bytecode; mod contract; pub(crate) mod memory; mod stack; +mod transient; pub use bytecode::{Bytecode, BytecodeLocked, BytecodeState}; pub use contract::Contract; pub use memory::Memory; pub use stack::Stack; +pub use transient::TransientStorage; use crate::{ instructions::{eval, Return}, diff --git a/crates/revm/src/interpreter/transient.rs b/crates/revm/src/interpreter/transient.rs new file mode 100644 index 0000000000..513f82d0e0 --- /dev/null +++ b/crates/revm/src/interpreter/transient.rs @@ -0,0 +1,49 @@ +use std::collections::HashMap; +use ruint::aliases::U256; +use primitive_types::H160; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct TransientStorage { + data: HashMap> +} + +impl Default for TransientStorage { + fn default() -> Self { + Self::new() + } +} + +impl TransientStorage { + pub fn new() -> Self { + Self { + data: HashMap::default() + } + } + + pub fn set(&mut self, address: H160, key: U256, value: U256) { + match self.data.get_mut(&address) { + Some(storage) => { + let _ = storage.insert(key, value); + return + } + None => { + let mut storage: HashMap = HashMap::default(); + let _ = storage.insert(key, value); + self.data.insert(address, storage); + return + } + } + } + + pub fn get(&self, address: H160, key: U256) -> U256 { + match self.data.get(&address) { + Some(storage) => { + match storage.get(&key) { + Some(value) => *value, + None => U256::default(), + } + } + None => U256::default() + } + } +} diff --git a/crates/revm/src/journaled_state.rs b/crates/revm/src/journaled_state.rs index ff5cc74691..d2fcf4b081 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/revm/src/journaled_state.rs @@ -1,4 +1,10 @@ -use crate::{interpreter::bytecode::Bytecode, models::SelfDestructResult, Return, KECCAK_EMPTY}; +use crate::{ + interpreter::{ + bytecode::Bytecode, + TransientStorage + }, + models::SelfDestructResult, Return, KECCAK_EMPTY +}; use alloc::{vec, vec::Vec}; use core::mem::{self}; use hashbrown::{hash_map::Entry, HashMap as Map}; @@ -12,6 +18,8 @@ use crate::{db::Database, AccountInfo, Log}; pub struct JournaledState { /// Current state. pub state: State, + /// EIP 1153 transient storage + pub transient_storage: TransientStorage, /// logs pub logs: Vec, /// how deep are we in call stack. @@ -147,6 +155,13 @@ pub enum JournalEntry { key: U256, had_value: Option, //if none, storage slot was cold loaded from db and needs to be removed }, + + TransientStorageChange { + address: H160, + key: U256, + had_value: Option, + }, + /// Code changed /// Action: Account code changed /// Revert: Revert to previous bytecode. @@ -163,6 +178,7 @@ impl JournaledState { pub fn new(num_of_precompiles: usize) -> JournaledState { Self { state: Map::new(), + transient_storage: TransientStorage::new(), logs: Vec::new(), journal: vec![vec![]], depth: 0, @@ -342,6 +358,7 @@ impl JournaledState { fn journal_revert( state: &mut State, + transient_storage: &mut TransientStorage, journal_entries: Vec, is_spurious_dragon_enabled: bool, ) { @@ -396,6 +413,13 @@ impl JournaledState { storage.remove(&key); } } + JournalEntry::TransientStorageChange { + address, + key, + had_value, + } => { + transient_storage.set(address, key, had_value.unwrap()); + } JournalEntry::CodeChange { address, had_code } => { let acc = state.get_mut(&address).unwrap(); acc.info.code_hash = had_code.hash(); @@ -422,6 +446,7 @@ impl JournaledState { pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) { let is_spurious_dragon_enabled = !self.is_before_spurious_dragon; let state = &mut self.state; + let transient_storage = &mut self.transient_storage; self.depth -= 1; // iterate over last N journals sets and revert our global state let leng = self.journal.len(); @@ -429,7 +454,7 @@ impl JournaledState { .iter_mut() .rev() .take(leng - checkpoint.journal_i) - .for_each(|cs| Self::journal_revert(state, mem::take(cs), is_spurious_dragon_enabled)); + .for_each(|cs| Self::journal_revert(state, transient_storage, mem::take(cs), is_spurious_dragon_enabled)); self.logs.truncate(checkpoint.log_i); self.journal.truncate(checkpoint.journal_i); @@ -608,6 +633,38 @@ impl JournaledState { Ok((slot.original_value, present, new, is_cold)) } + pub fn tload( + &mut self, + address: H160, + key: U256, + ) -> U256 { + self.transient_storage.get(address, key) + } + + pub fn tstore( + &mut self, + address: H160, + key: U256, + new: U256, + ) { + let present = self.tload(address, key); + + if present == new { + return + } + + self.journal + .last_mut() + .unwrap() + .push(JournalEntry::TransientStorageChange { + address, + key, + had_value: Some(present), + }); + + self.transient_storage.set(address, key, new) + } + /// push log into subroutine pub fn log(&mut self, log: Log) { self.logs.push(log); diff --git a/crates/revm/src/specification.rs b/crates/revm/src/specification.rs index 0ddc87776e..41c660186f 100644 --- a/crates/revm/src/specification.rs +++ b/crates/revm/src/specification.rs @@ -26,7 +26,8 @@ pub enum SpecId { GRAY_GLACIER = 14, // Gray Glacier 15050000 MERGE = 15, // Paris/Merge TBD (Depends on difficulty) MERGE_EOF = 16, // Merge+EOF TBD - LATEST = 17, + SHANGHAI = 17, // Shanghai TBD + LATEST = 18, } impl SpecId { @@ -37,7 +38,7 @@ impl SpecId { } BYZANTIUM | CONSTANTINOPLE | PETERSBURG => PrecompileId::BYZANTIUM, ISTANBUL | MUIR_GLACIER => PrecompileId::ISTANBUL, - BERLIN | LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE | MERGE_EOF | LATEST => { + BERLIN | LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE | MERGE_EOF | SHANGHAI | LATEST => { PrecompileId::BERLIN } } @@ -66,6 +67,7 @@ impl From<&str> for SpecId { "London" => SpecId::LONDON, "Merge" => SpecId::MERGE, "MergeEOF" => SpecId::MERGE_EOF, + "Shanghai" => SpecId::SHANGHAI, _ => SpecId::LATEST, } } @@ -147,6 +149,7 @@ pub(crate) mod spec_impl { // ARROW_GLACIER no EVM spec change // GRAT_GLACIER no EVM spec change spec!(MERGE); + spec!(SHANGHAI); spec!(LATEST); } @@ -158,6 +161,7 @@ pub use spec_impl::ISTANBUL::SpecImpl as IstanbulSpec; pub use spec_impl::LATEST::SpecImpl as LatestSpec; pub use spec_impl::LONDON::SpecImpl as LondonSpec; pub use spec_impl::MERGE::SpecImpl as MergeSpec; +pub use spec_impl::SHANGHAI::SpecImpl as ShanghaiSpec; pub use spec_impl::PETERSBURG::SpecImpl as PetersburgSpec; pub use spec_impl::SPURIOUS_DRAGON::SpecImpl as SpuriousDragonSpec; pub use spec_impl::TANGERINE::SpecImpl as TangerineSpec;