diff --git a/constants.testing.toml b/constants.testing.toml index 589b5d030f..018e490071 100644 --- a/constants.testing.toml +++ b/constants.testing.toml @@ -37,6 +37,8 @@ EVM_MAX_FEE_CHECK_HEIGHT = 0 # After this evm block height, RPC receipts derive effectiveGasPrice # from the actual fee charged by the Sovereign gas meter. EVM_RECEIPT_ACTUAL_FEE_HEIGHT = 0 +# Before this rollup height, custom EVM precompiles are disabled without reading their enabled set. +ENABLE_EVM_CUSTOM_PRECOMPILES_AT = 0 # How many rollup blocks to wait before terminating setup mode. While setup mode is enabled, # the rollup does not charge gas. This is useful when initializing the rollup with a bridged gas token. SETUP_MODE_TERMINATION_HEIGHT = 0 # Disable setup mode entirely by default. diff --git a/constants.toml b/constants.toml index ddd114dd1d..513b9714ab 100644 --- a/constants.toml +++ b/constants.toml @@ -60,6 +60,8 @@ EVM_MAX_FEE_CHECK_HEIGHT = 0 # After this evm block height, RPC receipts derive effectiveGasPrice # from the actual fee charged by the Sovereign gas meter. EVM_RECEIPT_ACTUAL_FEE_HEIGHT = 0 +# Before this rollup height, custom EVM precompiles are disabled without reading their enabled set. +ENABLE_EVM_CUSTOM_PRECOMPILES_AT = 0 # How many rollup blocks to wait before terminating setup mode. While setup mode is enabled, # the rollup does not charge gas. This is useful when initializing the rollup with a bridged gas token. SETUP_MODE_TERMINATION_HEIGHT = 0 # Disable setup mode entirely by default. diff --git a/crates/module-system/module-implementations/sov-evm/src/lib.rs b/crates/module-system/module-implementations/sov-evm/src/lib.rs index cd6c0c3542..0fb077c6a1 100644 --- a/crates/module-system/module-implementations/sov-evm/src/lib.rs +++ b/crates/module-system/module-implementations/sov-evm/src/lib.rs @@ -52,6 +52,7 @@ pub use revm::primitives::hardfork::SpecId; use serde::Serialize; use sov_address::{EthereumAddress, FromVmAddress}; use sov_bank::Amount; +use sov_modules_api::macros::config_value; use sov_modules_api::{ err_detail, AccessoryStateMap, AccessoryStateValue, Context, CoreModuleError, DaSpec, ErrorContext, ErrorDetail, GenesisState, Module, ModuleId, ModuleInfo, Spec, StateMap, @@ -304,9 +305,16 @@ where state: &mut Reader, ) -> Result, E> where - Reader: StateReader, + Reader: StateReader + VersionReader, { - let enabled_custom_precompiles = self.enabled_custom_precompile_addresses(state)?; + let activation_height: u64 = config_value!("ENABLE_EVM_CUSTOM_PRECOMPILES_AT"); + // The enabled-set read is metered. Existing rollups that add their first custom precompile + // must preserve the old no-read execution path until a coordinated activation height. + let enabled_custom_precompiles = enabled_custom_precompiles_at_height( + state.rollup_height_to_access().get(), + activation_height, + || self.enabled_custom_precompile_addresses(state), + )?; Ok(precompiles::SovPrecompileProvider::new( P::default(), context, @@ -337,6 +345,17 @@ where } } +fn enabled_custom_precompiles_at_height( + rollup_height: u64, + activation_height: u64, + load_enabled: impl FnOnce() -> Result, E>, +) -> Result, E> { + if rollup_height < activation_height { + return Ok(BTreeSet::new()); + } + load_enabled() +} + pub(crate) fn to_rollup_address(address: Address) -> S::Address where S::Address: FromVmAddress, @@ -352,3 +371,30 @@ pub(crate) fn to_rollup_balance(balance: U256) -> Amount { .unwrap_or_else(|_| panic!("The impossible happened: Balance overflowed")); Amount::new(bank_amount) } + +#[cfg(test)] +mod custom_precompile_activation_tests { + use std::convert::Infallible; + + use super::enabled_custom_precompiles_at_height; + + #[test] + fn enabled_set_is_not_loaded_before_activation() { + let enabled = enabled_custom_precompiles_at_height::(41, 42, || { + panic!("enabled precompile set must not be read before activation") + }) + .unwrap(); + assert!(enabled.is_empty()); + } + + #[test] + fn enabled_set_is_loaded_at_activation() { + let mut load_called = false; + enabled_custom_precompiles_at_height::(42, 42, || { + load_called = true; + Ok(Default::default()) + }) + .unwrap(); + assert!(load_called); + } +}