From 5068df637ee8fe6b9d0d6b72ee1775bad1c7cded Mon Sep 17 00:00:00 2001 From: mandreyel Date: Thu, 27 Apr 2023 14:07:56 +0400 Subject: [PATCH] feat: replace DIFFICULTY with PREVRANDAO since merge (EIP-4399) --- benches/loop.rs | 1 + core/src/error.rs | 10 ++++++--- runtime/src/eval/mod.rs | 15 +++++++++++-- runtime/src/eval/system.rs | 31 ++++++++++++++++++++++++-- runtime/src/handler.rs | 2 ++ runtime/src/lib.rs | 40 ++++++++++++++++++++++++++++++++-- src/backend/memory.rs | 8 +++++++ src/backend/mod.rs | 2 ++ src/executor/stack/executor.rs | 3 +++ src/executor/stack/memory.rs | 3 +++ 10 files changed, 106 insertions(+), 9 deletions(-) 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..acbc0e83 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -127,9 +127,6 @@ 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), /// An opcode accesses external information, but the request is off offset /// limit (runtime). @@ -154,6 +151,13 @@ pub enum ExitError { /// Other normal errors. #[cfg_attr(feature = "with-codec", codec(index = 13))] Other(Cow<'static, str>), + + /// Randomness was not configured for the PREVRANDAO opcode + #[cfg_attr(feature = "with-codec", codec(index = 14))] + MissingRandomness, + /// 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), } impl From for ExitReason { diff --git a/runtime/src/eval/mod.rs b/runtime/src/eval/mod.rs index 855ff9a9..567f8c48 100644 --- a/runtime/src/eval/mod.rs +++ b/runtime/src/eval/mod.rs @@ -14,7 +14,11 @@ pub enum Control { Exit(ExitReason), } -fn handle_other(state: &mut Runtime<'_>, opcode: Opcode, handler: &mut H) -> Control { +fn handle_other( + state: &mut Runtime<'_>, + opcode: Opcode, + handler: &mut H, +) -> Control { match handler.other(opcode, &mut state.machine) { Ok(()) => Control::Continue, Err(e) => Control::Exit(e.into()), @@ -40,7 +44,14 @@ 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::difficulty(state, handler) + if state.config().has_prevrandao { + system::prevrandao(state, handler) + } else { + system::difficulty(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 bf673648..fbee313d 100644 --- a/runtime/src/eval/system.rs +++ b/runtime/src/eval/system.rs @@ -185,6 +185,25 @@ pub fn difficulty(runtime: &mut Runtime<'_>, handler: &H) -> Control Control::Continue } +pub fn prevrandao(runtime: &mut Runtime, handler: &H) -> Control { + match handler.block_randomness() { + Some(rand) => { + // Convert between U256 and H256. U256 is in little-endian but since H256 is just + // a string-like byte array, it's big endian (MSB is the first element of the array). + // + // Byte order here is important because this opcode has the same value as DIFFICULTY + // (0x44), and so for older forks of Ethereum, the threshold value of 2^64 is used to + // distinguish between the two: if it's below, the value corresponds to the DIFFICULTY + // opcode, otherwise to the PREVRANDAO opcode. + let mut buf = [0u8; 32]; + rand.to_big_endian(&mut buf); + push!(runtime, H256(buf)); + Control::Continue + } + None => Control::Exit(ExitError::MissingRandomness.into()), + } +} + pub fn gaslimit(runtime: &mut Runtime<'_>, handler: &H) -> Control { push_u256!(runtime, handler.block_gas_limit()); Control::Continue @@ -265,7 +284,11 @@ pub fn suicide(runtime: &mut Runtime<'_>, handler: &mut H) -> Contro Control::Exit(ExitSucceed::Suicided.into()) } -pub fn create(runtime: &mut Runtime<'_>, is_create2: bool, handler: &mut H) -> Control { +pub fn create( + runtime: &mut Runtime<'_>, + is_create2: bool, + handler: &mut H, +) -> Control { runtime.return_data_buffer = Vec::new(); pop_u256!(runtime, value, code_offset, len); @@ -305,7 +328,11 @@ pub fn create(runtime: &mut Runtime<'_>, is_create2: bool, handler: } } -pub fn call(runtime: &mut Runtime<'_>, scheme: CallScheme, handler: &mut H) -> Control { +pub fn call( + runtime: &mut Runtime<'_>, + scheme: CallScheme, + handler: &mut H, +) -> Control { runtime.return_data_buffer = Vec::new(); pop_u256!(runtime, gas); diff --git a/runtime/src/handler.rs b/runtime/src/handler.rs index e7e25182..5c97bdd3 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 7d8bff7c..95600ea0 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -114,7 +114,7 @@ pub struct Runtime<'config> { return_data_len: U256, return_data_offset: U256, context: Context, - _config: &'config Config, + config: &'config Config, } impl<'config> Runtime<'config> { @@ -132,7 +132,7 @@ impl<'config> Runtime<'config> { return_data_len: U256::zero(), return_data_offset: U256::zero(), context, - _config: config, + config, } } @@ -146,6 +146,11 @@ impl<'config> Runtime<'config> { &self.context } + /// Get a reference to the runtime config. + pub fn config(&self) -> &'config Config { + self.config + } + /// Step the runtime. pub fn step<'a, H: Handler>( &'a mut self, @@ -287,6 +292,9 @@ pub struct Config { pub has_base_fee: bool, /// Has PUSH0 opcode. See [EIP-3855](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3855.md) pub has_push0: bool, + /// Has PREVRANDAO opcode, replacing the DIFFICULTY opcode. + /// See [EIP-4399](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4399.md) + pub has_prevrandao: bool, /// Whether the gasometer is running in estimate mode. pub estimate: bool, } @@ -342,6 +350,7 @@ impl Config { has_ext_code_hash: false, has_base_fee: false, has_push0: false, + has_prevrandao: false, estimate: false, } } @@ -396,6 +405,7 @@ impl Config { has_ext_code_hash: true, has_base_fee: false, has_push0: false, + has_prevrandao: false, estimate: false, } } @@ -410,6 +420,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()) @@ -423,6 +438,7 @@ impl Config { decrease_clears_refund, has_base_fee, has_push0, + has_prevrandao, disallow_executable_format, warm_coinbase_address, max_initcode_size, @@ -488,6 +504,7 @@ impl Config { has_ext_code_hash: true, has_base_fee, has_push0, + has_prevrandao, estimate: false, } } @@ -502,6 +519,7 @@ struct DerivedConfigInputs { decrease_clears_refund: bool, has_base_fee: bool, has_push0: bool, + has_prevrandao: bool, disallow_executable_format: bool, warm_coinbase_address: bool, max_initcode_size: Option, @@ -516,6 +534,7 @@ impl DerivedConfigInputs { decrease_clears_refund: false, has_base_fee: false, has_push0: false, + has_prevrandao: false, disallow_executable_format: false, warm_coinbase_address: false, max_initcode_size: None, @@ -530,6 +549,22 @@ impl DerivedConfigInputs { decrease_clears_refund: true, has_base_fee: true, has_push0: false, + has_prevrandao: false, + disallow_executable_format: true, + warm_coinbase_address: false, + max_initcode_size: None, + } + } + + 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, + has_prevrandao: true, disallow_executable_format: true, warm_coinbase_address: false, max_initcode_size: None, @@ -544,6 +579,7 @@ impl DerivedConfigInputs { decrease_clears_refund: true, has_base_fee: true, has_push0: true, + has_prevrandao: true, disallow_executable_format: true, warm_coinbase_address: true, // 2 * 24576 as per EIP-3860 diff --git a/src/backend/memory.rs b/src/backend/memory.rs index 7382a5e2..93f001ad 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. @@ -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 } diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 1a503cbc..200e71f0 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -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 ad27688e..946c912f 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -1101,6 +1101,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..5803b52b 100644 --- a/src/executor/stack/memory.rs +++ b/src/executor/stack/memory.rs @@ -421,6 +421,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() }