diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 717e02d1..a3afec52 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -34,7 +34,7 @@ jobs: - uses: actions/checkout@v2 with: repository: "rust-blockchain/evm-tests" - submodules: true + submodules: recursive - name: Submodules run: | cd evm diff --git a/benches/loop.rs b/benches/loop.rs index 447c8a79..b9ca7ae7 100644 --- a/benches/loop.rs +++ b/benches/loop.rs @@ -19,6 +19,7 @@ fn run_loop_contract() { block_gas_limit: Default::default(), chain_id: U256::one(), block_base_fee_per_gas: U256::zero(), + block_randomness: None, }; let mut state = BTreeMap::new(); diff --git a/core/src/error.rs b/core/src/error.rs index 510d6df4..9c6dd34e 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -127,6 +127,7 @@ pub enum ExitError { /// Create init code exceeds limit (runtime). #[cfg_attr(feature = "with-codec", codec(index = 7))] CreateContractLimit, + /// Invalid opcode during execution or starting byte is 0xef. See [EIP-3541](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3541.md). #[cfg_attr(feature = "with-codec", codec(index = 15))] InvalidCode(Opcode), @@ -154,6 +155,11 @@ pub enum ExitError { /// Other normal errors. #[cfg_attr(feature = "with-codec", codec(index = 13))] Other(Cow<'static, str>), + + /// Nonce reached maximum value of 2^64-1 + /// https://eips.ethereum.org/EIPS/eip-2681 + #[cfg_attr(feature = "with-codec", codec(index = 14))] + MaxNonce, } impl From for ExitReason { diff --git a/runtime/src/eval/mod.rs b/runtime/src/eval/mod.rs index c65370fc..b2965335 100644 --- a/runtime/src/eval/mod.rs +++ b/runtime/src/eval/mod.rs @@ -40,7 +40,7 @@ pub fn eval(state: &mut Runtime, opcode: Opcode, handler: &mut H) -> Opcode::COINBASE => system::coinbase(state, handler), Opcode::TIMESTAMP => system::timestamp(state, handler), Opcode::NUMBER => system::number(state, handler), - Opcode::DIFFICULTY => system::difficulty(state, handler), + Opcode::DIFFICULTY => system::prevrandao(state, handler), Opcode::GASLIMIT => system::gaslimit(state, handler), Opcode::SLOAD => system::sload(state, handler), Opcode::SSTORE => system::sstore(state, handler), diff --git a/runtime/src/eval/system.rs b/runtime/src/eval/system.rs index 57435543..e7ef5036 100644 --- a/runtime/src/eval/system.rs +++ b/runtime/src/eval/system.rs @@ -185,6 +185,15 @@ pub fn difficulty(runtime: &mut Runtime, handler: &H) -> Control Control::Continue } +pub fn prevrandao(runtime: &mut Runtime, handler: &H) -> Control { + if let Some(rand) = handler.block_randomness() { + push!(runtime, rand); + Control::Continue + } else { + difficulty(runtime, handler) + } +} + pub fn gaslimit(runtime: &mut Runtime, handler: &H) -> Control { push_u256!(runtime, handler.block_gas_limit()); Control::Continue diff --git a/runtime/src/handler.rs b/runtime/src/handler.rs index e7e25182..31e894fb 100644 --- a/runtime/src/handler.rs +++ b/runtime/src/handler.rs @@ -54,6 +54,8 @@ pub trait Handler { fn block_timestamp(&self) -> U256; /// Get environmental block difficulty. fn block_difficulty(&self) -> U256; + /// Get environmental block randomness. + fn block_randomness(&self) -> Option; /// Get environmental gas limit. fn block_gas_limit(&self) -> U256; /// Environmental block base fee. diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a5d3a54b..8809e58f 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -409,6 +409,11 @@ impl Config { Self::config_with_derived_values(DerivedConfigInputs::london()) } + /// The Merge (Paris) hard fork configuration. + pub const fn merge() -> Config { + Self::config_with_derived_values(DerivedConfigInputs::merge()) + } + /// Shanghai hard fork configuration. pub const fn shanghai() -> Config { Self::config_with_derived_values(DerivedConfigInputs::shanghai()) @@ -535,6 +540,20 @@ impl DerivedConfigInputs { } } + const fn merge() -> Self { + Self { + gas_storage_read_warm: 100, + gas_sload_cold: 2100, + gas_access_list_storage_key: 1900, + decrease_clears_refund: true, + has_base_fee: true, + has_push0: false, + disallow_executable_format: true, + warm_coinbase_address: false, + max_initcode_size: None, + } + } + const fn shanghai() -> Self { Self { gas_storage_read_warm: 100, diff --git a/src/backend/memory.rs b/src/backend/memory.rs index 7382a5e2..4910e628 100644 --- a/src/backend/memory.rs +++ b/src/backend/memory.rs @@ -31,6 +31,11 @@ pub struct MemoryVicinity { pub block_gas_limit: U256, /// Environmental base fee per gas. pub block_base_fee_per_gas: U256, + /// Environmental randomness. + /// + /// In Ethereum, this is the randomness beacon provided by the beacon + /// chain and is only enabled post Merge. + pub block_randomness: Option, } /// Account information of a memory backend. @@ -42,7 +47,7 @@ pub struct MemoryVicinity { #[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] pub struct MemoryAccount { /// Account nonce. - pub nonce: U256, + pub nonce: u64, /// Account balance. pub balance: U256, /// Full account storage. @@ -110,6 +115,9 @@ impl<'vicinity> Backend for MemoryBackend<'vicinity> { fn block_difficulty(&self) -> U256 { self.vicinity.block_difficulty } + fn block_randomness(&self) -> Option { + self.vicinity.block_randomness + } fn block_gas_limit(&self) -> U256 { self.vicinity.block_gas_limit } @@ -202,8 +210,7 @@ impl<'vicinity> ApplyBackend for MemoryBackend<'vicinity> { } account.balance == U256::zero() - && account.nonce == U256::zero() - && account.code.is_empty() + && account.nonce == 0 && account.code.is_empty() }; if is_empty && delete_empty { diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 1a503cbc..c442958c 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -20,7 +20,7 @@ pub struct Basic { /// Account balance. pub balance: U256, /// Account nonce. - pub nonce: U256, + pub nonce: u64, } pub use ethereum::Log; @@ -66,6 +66,8 @@ pub trait Backend { fn block_timestamp(&self) -> U256; /// Environmental block difficulty. fn block_difficulty(&self) -> U256; + /// Get environmental block randomness. + fn block_randomness(&self) -> Option; /// Environmental block gas limit. fn block_gas_limit(&self) -> U256; /// Environmental block base fee. diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index b50e68af..4f6f52cd 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -204,7 +204,7 @@ pub trait StackState<'config>: Backend { fn is_cold(&self, address: H160) -> bool; fn is_storage_cold(&self, address: H160, key: H256) -> bool; - fn inc_nonce(&mut self, address: H160); + fn inc_nonce(&mut self, address: H160) -> Result<(), ExitError>; fn set_storage(&mut self, address: H160, key: H256, value: H256); fn reset_storage(&mut self, address: H160); fn log(&mut self, address: H160, topics: Vec, data: Vec); @@ -304,7 +304,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> /// Execute using Runtimes on the call_stack until it returns. fn execute_with_call_stack( &mut self, - call_stack: &mut Vec, + call_stack: &mut Vec>, ) -> (ExitReason, Option, Vec) { // This `interrupt_runtime` is used to pass the runtime obtained from the // `Capture::Trap` branch in the match below back to the top of the call stack. @@ -568,7 +568,9 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> self.initialize_with_access_list(access_list); } - self.state.inc_nonce(caller); + if let Err(e) = self.state.inc_nonce(caller) { + return (e.into(), Vec::new()); + } let context = Context { caller, @@ -616,7 +618,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> } /// Get account nonce. - pub fn nonce(&self, address: H160) -> U256 { + pub fn nonce(&self, address: H160) -> u64 { self.state.basic(address).nonce } @@ -702,6 +704,10 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> return Capture::Exit((ExitError::OutOfFund.into(), None, Vec::new())); } + if let Err(e) = self.state.inc_nonce(caller) { + return Capture::Exit((e.into(), None, Vec::new())); + } + let after_gas = if take_l64 && self.config.call_l64_after_gas { if self.config.estimate { let initial_after_gas = self.state.metadata().gasometer.gas(); @@ -720,8 +726,6 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> let gas_limit = min(after_gas, target_gas); try_or_fail!(self.state.metadata_mut().gasometer.record_cost(gas_limit)); - self.state.inc_nonce(caller); - self.enter_substate(gas_limit, false); { @@ -730,7 +734,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> return Capture::Exit((ExitError::CreateCollision.into(), None, Vec::new())); } - if self.nonce(address) > U256::zero() { + if self.nonce(address) > 0 { let _ = self.exit_substate(StackExitKind::Failed); return Capture::Exit((ExitError::CreateCollision.into(), None, Vec::new())); } @@ -757,7 +761,9 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> } if self.config.create_increase_nonce { - self.state.inc_nonce(address); + if let Err(e) = self.state.inc_nonce(address) { + return Capture::Exit((e.into(), None, Vec::new())); + } } let runtime = Runtime::new( @@ -1108,6 +1114,9 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler fn block_difficulty(&self) -> U256 { self.state.block_difficulty() } + fn block_randomness(&self) -> Option { + self.state.block_randomness() + } fn block_gas_limit(&self) -> U256 { self.state.block_gas_limit() } diff --git a/src/executor/stack/memory.rs b/src/executor/stack/memory.rs index ca04885c..d66c1033 100644 --- a/src/executor/stack/memory.rs +++ b/src/executor/stack/memory.rs @@ -198,14 +198,14 @@ impl<'config> MemoryStackSubstate<'config> { return Some(false); } - if account.basic.nonce != U256::zero() { + if account.basic.nonce != 0 { return Some(false); } if let Some(code) = &account.code { return Some( account.basic.balance == U256::zero() - && account.basic.nonce == U256::zero() + && account.basic.nonce == 0 && code.is_empty(), ); } @@ -301,8 +301,10 @@ impl<'config> MemoryStackSubstate<'config> { .expect("New account was just inserted") } - pub fn inc_nonce(&mut self, address: H160, backend: &B) { - self.account_mut(address, backend).basic.nonce += U256::one(); + pub fn inc_nonce(&mut self, address: H160, backend: &B) -> Result<(), ExitError> { + let nonce = &mut self.account_mut(address, backend).basic.nonce; + *nonce = nonce.checked_add(1).ok_or(ExitError::MaxNonce)?; + Ok(()) } pub fn set_storage(&mut self, address: H160, key: H256, value: H256) { @@ -421,6 +423,9 @@ impl<'backend, 'config, B: Backend> Backend for MemoryStackState<'backend, 'conf fn block_difficulty(&self) -> U256 { self.backend.block_difficulty() } + fn block_randomness(&self) -> Option { + self.backend.block_randomness() + } fn block_gas_limit(&self) -> U256 { self.backend.block_gas_limit() } @@ -494,7 +499,7 @@ impl<'backend, 'config, B: Backend> StackState<'config> for MemoryStackState<'ba } self.backend.basic(address).balance == U256::zero() - && self.backend.basic(address).nonce == U256::zero() + && self.backend.basic(address).nonce == 0 && self.backend.code(address).len() == 0 } @@ -510,8 +515,8 @@ impl<'backend, 'config, B: Backend> StackState<'config> for MemoryStackState<'ba self.substate.is_storage_cold(address, key) } - fn inc_nonce(&mut self, address: H160) { - self.substate.inc_nonce(address, self.backend); + fn inc_nonce(&mut self, address: H160) -> Result<(), ExitError> { + self.substate.inc_nonce(address, self.backend) } fn set_storage(&mut self, address: H160, key: H256, value: H256) {