diff --git a/core/src/eval/misc.rs b/core/src/eval/misc.rs index 8271d5f6c..c4e27e29e 100644 --- a/core/src/eval/misc.rs +++ b/core/src/eval/misc.rs @@ -110,6 +110,28 @@ pub fn mstore8(state: &mut Machine) -> Control { } } +#[inline] +pub fn mcopy(state: &mut Machine) -> Control { + pop_u256!(state, dst, src, len); + + try_or_fail!(state.memory.resize_offset(dst, len)); + + if len == U256::zero() { + return Control::Continue(1); + } + + let src_usize = as_usize_or_fail!(src); + let len_usize = as_usize_or_fail!(len); + let data = state.memory.get(src_usize, len_usize); + + // NB: using `set` would be more straighforward but it lacks + // of internal checks. + match state.memory.copy_large(dst, U256::zero(), len, &data) { + Ok(()) => Control::Continue(1), + Err(e) => Control::Exit(e.into()), + } +} + #[inline] pub fn jump(state: &mut Machine) -> Control { pop_u256!(state, dest); diff --git a/core/src/eval/mod.rs b/core/src/eval/mod.rs index 519f5d925..fb6752344 100644 --- a/core/src/eval/mod.rs +++ b/core/src/eval/mod.rs @@ -156,6 +156,10 @@ fn eval_mstore8(state: &mut Machine, _opcode: Opcode, _position: usize) -> Contr self::misc::mstore8(state) } +fn eval_mcopy(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { + self::misc::mcopy(state) +} + fn eval_jump(state: &mut Machine, _opcode: Opcode, _position: usize) -> Control { self::misc::jump(state) } @@ -492,6 +496,7 @@ pub fn eval(state: &mut Machine, opcode: Opcode, position: usize) -> Control { table[Opcode::MLOAD.as_usize()] = eval_mload as _; table[Opcode::MSTORE.as_usize()] = eval_mstore as _; table[Opcode::MSTORE8.as_usize()] = eval_mstore8 as _; + table[Opcode::MCOPY.as_usize()] = eval_mcopy as _; table[Opcode::JUMP.as_usize()] = eval_jump as _; table[Opcode::JUMPI.as_usize()] = eval_jumpi as _; table[Opcode::PC.as_usize()] = eval_pc as _; diff --git a/core/src/opcode.rs b/core/src/opcode.rs index 15814b0ac..65c90cf5f 100644 --- a/core/src/opcode.rs +++ b/core/src/opcode.rs @@ -83,6 +83,8 @@ impl Opcode { pub const MSTORE: Opcode = Opcode(0x52); /// `MSTORE8` pub const MSTORE8: Opcode = Opcode(0x53); + /// `MCOPY` + pub const MCOPY: Opcode = Opcode(0x5e); /// `JUMP` pub const JUMP: Opcode = Opcode(0x56); /// `JUMPI` @@ -189,6 +191,8 @@ impl Opcode { pub const SELFBALANCE: Opcode = Opcode(0x47); /// `BASEFEE` pub const BASEFEE: Opcode = Opcode(0x48); + /// `BLOBBASEFEE` + pub const BLOBBASEFEE: Opcode = Opcode(0x4a); /// `ORIGIN` pub const ORIGIN: Opcode = Opcode(0x32); /// `CALLER` @@ -223,6 +227,10 @@ impl Opcode { pub const SLOAD: Opcode = Opcode(0x54); /// `SSTORE` pub const SSTORE: Opcode = Opcode(0x55); + /// `TLOAD` + pub const TLOAD: Opcode = Opcode(0x5c); + /// `TSTORE` + pub const TSTORE: Opcode = Opcode(0x5d); /// `GAS` pub const GAS: Opcode = Opcode(0x5a); /// `LOGn` diff --git a/runtime/src/eval/mod.rs b/runtime/src/eval/mod.rs index b2965335f..00d8569a0 100644 --- a/runtime/src/eval/mod.rs +++ b/runtime/src/eval/mod.rs @@ -44,6 +44,8 @@ pub fn eval(state: &mut Runtime, opcode: Opcode, handler: &mut H) -> Opcode::GASLIMIT => system::gaslimit(state, handler), Opcode::SLOAD => system::sload(state, handler), Opcode::SSTORE => system::sstore(state, handler), + Opcode::TLOAD => system::tload(state, handler), + Opcode::TSTORE => system::tstore(state, handler), Opcode::GAS => system::gas(state, handler), Opcode::LOG0 => system::log(state, 0, handler), Opcode::LOG1 => system::log(state, 1, handler), @@ -59,6 +61,7 @@ pub fn eval(state: &mut Runtime, opcode: Opcode, handler: &mut H) -> Opcode::STATICCALL => system::call(state, CallScheme::StaticCall, handler), Opcode::CHAINID => system::chainid(state, handler), Opcode::BASEFEE => system::base_fee(state, handler), + Opcode::BLOBBASEFEE => system::blob_base_fee(state, handler), _ => handle_other(state, opcode, handler), } } diff --git a/runtime/src/eval/system.rs b/runtime/src/eval/system.rs index e7ef50365..73342a135 100644 --- a/runtime/src/eval/system.rs +++ b/runtime/src/eval/system.rs @@ -90,6 +90,14 @@ pub fn base_fee(runtime: &mut Runtime, handler: &H) -> Control { Control::Continue } +pub fn blob_base_fee(runtime: &mut Runtime, handler: &H) -> Control { + let mut ret = H256::default(); + handler.block_blob_base_fee().to_big_endian(&mut ret[..]); + push!(runtime, ret); + + Control::Continue +} + pub fn extcodesize(runtime: &mut Runtime, handler: &H) -> Control { pop!(runtime, address); push_u256!(runtime, handler.code_size(address.into())); @@ -228,6 +236,27 @@ pub fn sstore(runtime: &mut Runtime, handler: &mut H) -> Control } } +pub fn tload(runtime: &mut Runtime, handler: &H) -> Control { + pop!(runtime, index); + let value = handler.transient_storage(index); + push!(runtime, value); + + event!(TLoad { index, value }); + + Control::Continue +} + +pub fn tstore(runtime: &mut Runtime, handler: &mut H) -> Control { + pop!(runtime, index, value); + + event!(TStore { index, value }); + + match handler.set_transient_storage(index, value) { + Ok(()) => Control::Continue, + Err(e) => Control::Exit(e.into()), + } +} + pub fn gas(runtime: &mut Runtime, handler: &H) -> Control { push_u256!(runtime, handler.gas_left()); diff --git a/runtime/src/handler.rs b/runtime/src/handler.rs index 31e894fbe..0a94d42ac 100644 --- a/runtime/src/handler.rs +++ b/runtime/src/handler.rs @@ -35,6 +35,8 @@ pub trait Handler { fn code(&self, address: H160) -> Vec; /// Get storage value of address at index. fn storage(&self, address: H160, index: H256) -> H256; + /// Get transient storage value at index. + fn transient_storage(&self, index: H256) -> H256; /// Get original storage value of address at index. fn original_storage(&self, address: H160, index: H256) -> H256; @@ -60,6 +62,8 @@ pub trait Handler { fn block_gas_limit(&self) -> U256; /// Environmental block base fee. fn block_base_fee_per_gas(&self) -> U256; + /// Returns the value of the blob base-fee of the current block it is executing in. + fn block_blob_base_fee(&self) -> U256; /// Get environmental chain ID. fn chain_id(&self) -> U256; @@ -77,6 +81,8 @@ pub trait Handler { /// Set storage value of address at index. fn set_storage(&mut self, address: H160, index: H256, value: H256) -> Result<(), ExitError>; + /// Set transient storage value at index. + fn set_transient_storage(&mut self, index: H256, value: H256) -> Result<(), ExitError>; /// Create a log owned by address with given topics and data. fn log(&mut self, address: H160, topics: Vec, data: Vec) -> Result<(), ExitError>; /// Mark an address to be deleted, with funds transferred to target. diff --git a/runtime/src/tracing.rs b/runtime/src/tracing.rs index f9fed1961..62f7e974a 100644 --- a/runtime/src/tracing.rs +++ b/runtime/src/tracing.rs @@ -32,6 +32,14 @@ pub enum Event<'a> { index: H256, value: H256, }, + TLoad { + index: H256, + value: H256, + }, + TStore { + index: H256, + value: H256, + }, } // Expose `listener::with` to the crate only. diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 294c8a2ca..f4bb7ab08 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.68.2" +channel = "1.70.0" profile = "minimal" -components = [ "rustfmt", "clippy" ] +components = ["rustfmt", "clippy"] diff --git a/src/backend/memory.rs b/src/backend/memory.rs index 37e7718b1..2fd94ba2c 100644 --- a/src/backend/memory.rs +++ b/src/backend/memory.rs @@ -31,6 +31,8 @@ pub struct MemoryVicinity { pub block_gas_limit: U256, /// Environmental base fee per gas. pub block_base_fee_per_gas: U256, + /// Environmental blob base fee. + pub block_blob_base_fee: U256, /// Environmental randomness. /// /// In Ethereum, this is the randomness beacon provided by the beacon @@ -61,15 +63,21 @@ pub struct MemoryAccount { pub struct MemoryBackend<'vicinity> { vicinity: &'vicinity MemoryVicinity, state: BTreeMap, + transient_state: BTreeMap, logs: Vec, } impl<'vicinity> MemoryBackend<'vicinity> { /// Create a new memory backend. - pub fn new(vicinity: &'vicinity MemoryVicinity, state: BTreeMap) -> Self { + pub fn new( + vicinity: &'vicinity MemoryVicinity, + state: BTreeMap, + transient_state: BTreeMap, + ) -> Self { Self { vicinity, state, + transient_state, logs: Vec::new(), } } @@ -125,6 +133,10 @@ impl<'vicinity> Backend for MemoryBackend<'vicinity> { self.vicinity.block_base_fee_per_gas } + fn block_blob_base_fee(&self) -> U256 { + self.vicinity.block_blob_base_fee + } + fn chain_id(&self) -> U256 { self.vicinity.chain_id } @@ -157,6 +169,13 @@ impl<'vicinity> Backend for MemoryBackend<'vicinity> { .unwrap_or_default() } + fn transient_storage(&self, index: H256) -> H256 { + self.transient_state + .get(&index) + .cloned() + .unwrap_or_default() + } + fn original_storage(&self, address: H160, index: H256) -> Option { Some(self.storage(address, index)) } diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 7cda7fd1d..916cf9b45 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -72,6 +72,8 @@ pub trait Backend { fn block_gas_limit(&self) -> U256; /// Environmental block base fee. fn block_base_fee_per_gas(&self) -> U256; + /// Returns the value of the blob base-fee of the current block it is executing in. + fn block_blob_base_fee(&self) -> U256; /// Environmental chain ID. fn chain_id(&self) -> U256; @@ -83,6 +85,8 @@ pub trait Backend { fn code(&self, address: H160) -> Vec; /// Get storage value of address at index. fn storage(&self, address: H160, index: H256) -> H256; + /// Get storage value at index. + fn transient_storage(&self, index: H256) -> H256; /// Get original storage value of address at index, if available. fn original_storage(&self, address: H160, index: H256) -> Option; } diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 869296a64..428be1ce6 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -206,6 +206,7 @@ pub trait StackState<'config>: Backend { fn inc_nonce(&mut self, address: H160) -> Result<(), ExitError>; fn set_storage(&mut self, address: H160, key: H256, value: H256); + fn set_transient_storage(&mut self, key: H256, value: H256); fn reset_storage(&mut self, address: H160); fn log(&mut self, address: H160, topics: Vec, data: Vec); fn set_deleted(&mut self, address: H160); @@ -1047,6 +1048,10 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler self.state.storage(address, index) } + fn transient_storage(&self, index: H256) -> H256 { + self.state.transient_storage(index) + } + fn original_storage(&self, address: H160, index: H256) -> H256 { self.state .original_storage(address, index) @@ -1121,6 +1126,9 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler fn block_base_fee_per_gas(&self) -> U256 { self.state.block_base_fee_per_gas() } + fn block_blob_base_fee(&self) -> U256 { + self.state.block_blob_base_fee() + } fn chain_id(&self) -> U256 { self.state.chain_id() } @@ -1134,6 +1142,11 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler Ok(()) } + fn set_transient_storage(&mut self, index: H256, value: H256) -> Result<(), ExitError> { + self.state.set_transient_storage(index, value); + Ok(()) + } + fn log(&mut self, address: H160, topics: Vec, data: Vec) -> Result<(), ExitError> { self.state.log(address, topics, data); Ok(()) diff --git a/src/executor/stack/memory.rs b/src/executor/stack/memory.rs index 957b2b3d5..a51127367 100644 --- a/src/executor/stack/memory.rs +++ b/src/executor/stack/memory.rs @@ -23,6 +23,7 @@ pub struct MemoryStackSubstate<'config> { logs: Vec, accounts: BTreeMap, storages: BTreeMap<(H160, H256), H256>, + transient_storages: BTreeMap, deletes: BTreeSet, } @@ -34,6 +35,7 @@ impl<'config> MemoryStackSubstate<'config> { logs: Vec::new(), accounts: BTreeMap::new(), storages: BTreeMap::new(), + transient_storages: BTreeMap::new(), deletes: BTreeSet::new(), } } @@ -119,6 +121,7 @@ impl<'config> MemoryStackSubstate<'config> { logs: Vec::new(), accounts: BTreeMap::new(), storages: BTreeMap::new(), + transient_storages: BTreeMap::new(), deletes: BTreeSet::new(), }; mem::swap(&mut entering, self); @@ -231,6 +234,17 @@ impl<'config> MemoryStackSubstate<'config> { None } + pub fn known_transient_storage(&self, key: H256) -> Option { + if let Some(value) = self.transient_storages.get(&key) { + return Some(*value); + } + + if let Some(parent) = self.parent.as_ref() { + return parent.known_transient_storage(key); + } + + None + } pub fn known_original_storage(&self, address: H160) -> Option { if let Some(account) = self.accounts.get(&address) { @@ -314,6 +328,10 @@ impl<'config> MemoryStackSubstate<'config> { self.storages.insert((address, key), value); } + pub fn set_transient_storage(&mut self, key: H256, value: H256) { + self.transient_storages.insert(key, value); + } + pub fn reset_storage(&mut self, address: H160, backend: &B) { let mut removing = Vec::new(); @@ -435,6 +453,9 @@ impl<'backend, 'config, B: Backend> Backend for MemoryStackState<'backend, 'conf fn block_base_fee_per_gas(&self) -> U256 { self.backend.block_base_fee_per_gas() } + fn block_blob_base_fee(&self) -> U256 { + self.backend.block_blob_base_fee() + } fn chain_id(&self) -> U256 { self.backend.chain_id() @@ -462,6 +483,12 @@ impl<'backend, 'config, B: Backend> Backend for MemoryStackState<'backend, 'conf .unwrap_or_else(|| self.backend.storage(address, key)) } + fn transient_storage(&self, key: H256) -> H256 { + self.substate + .known_transient_storage(key) + .unwrap_or_else(|| self.backend.transient_storage(key)) + } + fn original_storage(&self, address: H160, key: H256) -> Option { if let Some(value) = self.substate.known_original_storage(address) { return Some(value); @@ -526,6 +553,10 @@ impl<'backend, 'config, B: Backend> StackState<'config> for MemoryStackState<'ba self.substate.set_storage(address, key, value) } + fn set_transient_storage(&mut self, key: H256, value: H256) { + self.substate.set_transient_storage(key, value) + } + fn reset_storage(&mut self, address: H160) { self.substate.reset_storage(address, self.backend); }