Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 60 additions & 4 deletions crates/ev-revm/src/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,39 @@ impl MintPrecompileSettings {
}
}

/// Settings for custom contract size limit with activation height.
#[derive(Debug, Clone, Copy)]
pub struct ContractSizeLimitSettings {
limit: usize,
activation_height: u64,
}

impl ContractSizeLimitSettings {
/// Creates a new settings object.
pub const fn new(limit: usize, activation_height: u64) -> Self {
Self {
limit,
activation_height,
}
}

const fn activation_height(&self) -> u64 {
self.activation_height
}

const fn limit(&self) -> usize {
self.limit
}
}

/// Wrapper around an existing `EvmFactory` that produces [`EvEvm`] instances.
#[derive(Debug, Clone)]
pub struct EvEvmFactory<F> {
inner: F,
redirect: Option<BaseFeeRedirectSettings>,
mint_precompile: Option<MintPrecompileSettings>,
/// Custom contract size limit with activation height.
Comment thread
tac0turtle marked this conversation as resolved.
Outdated
contract_size_limit: Option<ContractSizeLimitSettings>,
}

impl<F> EvEvmFactory<F> {
Expand All @@ -87,14 +114,26 @@ impl<F> EvEvmFactory<F> {
inner: F,
redirect: Option<BaseFeeRedirectSettings>,
mint_precompile: Option<MintPrecompileSettings>,
contract_size_limit: Option<ContractSizeLimitSettings>,
) -> Self {
Self {
inner,
redirect,
mint_precompile,
contract_size_limit,
}
}

fn contract_size_limit_for_block(&self, block_number: U256) -> Option<usize> {
self.contract_size_limit.and_then(|settings| {
if block_number >= U256::from(settings.activation_height()) {
Some(settings.limit())
} else {
None
}
})
}

fn install_mint_precompile(&self, precompiles: &mut PrecompilesMap, block_number: U256) {
let Some(settings) = self.mint_precompile else {
return;
Expand Down Expand Up @@ -141,9 +180,13 @@ impl EvmFactory for EvEvmFactory<EthEvmFactory> {
fn create_evm<DB: Database>(
&self,
db: DB,
evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
mut evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
) -> Self::Evm<DB, NoOpInspector> {
let block_number = evm_env.block_env.number;
// Apply custom contract size limit if configured and active for this block
if let Some(limit) = self.contract_size_limit_for_block(block_number) {
evm_env.cfg_env.limit_contract_code_size = Some(limit);
}
let inner = self.inner.create_evm(db, evm_env);
let mut evm = EvEvm::from_inner(inner, self.redirect_for_block(block_number), false);
{
Expand All @@ -156,10 +199,14 @@ impl EvmFactory for EvEvmFactory<EthEvmFactory> {
fn create_evm_with_inspector<DB: Database, I: Inspector<Self::Context<DB>>>(
&self,
db: DB,
input: EvmEnv<Self::Spec, Self::BlockEnv>,
mut input: EvmEnv<Self::Spec, Self::BlockEnv>,
inspector: I,
) -> Self::Evm<DB, I> {
let block_number = input.block_env.number;
// Apply custom contract size limit if configured and active for this block
if let Some(limit) = self.contract_size_limit_for_block(block_number) {
input.cfg_env.limit_contract_code_size = Some(limit);
}
let inner = self.inner.create_evm_with_inspector(db, input, inspector);
let mut evm = EvEvm::from_inner(inner, self.redirect_for_block(block_number), true);
{
Expand All @@ -175,13 +222,18 @@ pub fn with_ev_handler<ChainSpec>(
config: EthEvmConfig<ChainSpec, EthEvmFactory>,
redirect: Option<BaseFeeRedirectSettings>,
mint_precompile: Option<MintPrecompileSettings>,
contract_size_limit: Option<ContractSizeLimitSettings>,
) -> EthEvmConfig<ChainSpec, EvEvmFactory<EthEvmFactory>> {
let EthEvmConfig {
executor_factory,
block_assembler,
} = config;
let wrapped_factory =
EvEvmFactory::new(*executor_factory.evm_factory(), redirect, mint_precompile);
let wrapped_factory = EvEvmFactory::new(
*executor_factory.evm_factory(),
redirect,
mint_precompile,
contract_size_limit,
);
let new_executor_factory = EthBlockExecutorFactory::new(
*executor_factory.receipt_builder(),
executor_factory.spec().clone(),
Expand Down Expand Up @@ -265,6 +317,7 @@ mod tests {
alloy_evm::eth::EthEvmFactory::default(),
Some(BaseFeeRedirectSettings::new(redirect, 0)),
None,
None,
)
.create_evm(state, evm_env.clone());

Expand Down Expand Up @@ -355,6 +408,7 @@ mod tests {
alloy_evm::eth::EthEvmFactory::default(),
None,
Some(MintPrecompileSettings::new(contract, 0)),
None,
)
.create_evm(state, evm_env);

Expand Down Expand Up @@ -395,6 +449,7 @@ mod tests {
alloy_evm::eth::EthEvmFactory::default(),
Some(BaseFeeRedirectSettings::new(BaseFeeRedirect::new(sink), 5)),
None,
None,
);

let mut before_env: alloy_evm::EvmEnv<SpecId, BlockEnv> = EvmEnv::default();
Expand Down Expand Up @@ -461,6 +516,7 @@ mod tests {
alloy_evm::eth::EthEvmFactory::default(),
None,
Some(MintPrecompileSettings::new(contract, 3)),
None,
);

let tx_env = || crate::factory::TxEnv {
Expand Down
5 changes: 4 additions & 1 deletion crates/ev-revm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@ pub use api::EvBuilder;
pub use base_fee::{BaseFeeRedirect, BaseFeeRedirectError};
pub use config::{BaseFeeConfig, ConfigError};
pub use evm::{DefaultEvEvm, EvEvm};
pub use factory::{with_ev_handler, BaseFeeRedirectSettings, EvEvmFactory, MintPrecompileSettings};
pub use factory::{
with_ev_handler, BaseFeeRedirectSettings, ContractSizeLimitSettings, EvEvmFactory,
MintPrecompileSettings,
};
pub use handler::EvHandler;
130 changes: 130 additions & 0 deletions crates/node/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ use alloy_primitives::Address;
use reth_chainspec::ChainSpec;
use serde::{Deserialize, Serialize};

/// Default contract size limit in bytes (24KB per EIP-170).
pub const DEFAULT_CONTRACT_SIZE_LIMIT: usize = 24 * 1024;

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
struct ChainspecEvolveConfig {
#[serde(default, rename = "baseFeeSink")]
Expand All @@ -12,6 +15,12 @@ struct ChainspecEvolveConfig {
pub mint_admin: Option<Address>,
#[serde(default, rename = "mintPrecompileActivationHeight")]
pub mint_precompile_activation_height: Option<u64>,
/// Maximum contract code size in bytes. Defaults to 24KB (EIP-170) if not specified.
#[serde(default, rename = "contractSizeLimit")]
pub contract_size_limit: Option<usize>,
/// Block height at which the custom contract size limit activates.
#[serde(default, rename = "contractSizeLimitActivationHeight")]
pub contract_size_limit_activation_height: Option<u64>,
}

/// Configuration for the Evolve payload builder
Expand All @@ -29,6 +38,12 @@ pub struct EvolvePayloadBuilderConfig {
/// Optional activation height for mint precompile; defaults to 0 when admin set.
#[serde(default)]
pub mint_precompile_activation_height: Option<u64>,
/// Maximum contract code size in bytes. Defaults to 24KB (EIP-170).
#[serde(default)]
pub contract_size_limit: Option<usize>,
/// Block height at which the custom contract size limit activates.
#[serde(default)]
pub contract_size_limit_activation_height: Option<u64>,
}

impl EvolvePayloadBuilderConfig {
Expand All @@ -39,6 +54,8 @@ impl EvolvePayloadBuilderConfig {
mint_admin: None,
base_fee_redirect_activation_height: None,
mint_precompile_activation_height: None,
contract_size_limit: None,
contract_size_limit_activation_height: None,
}
}

Expand Down Expand Up @@ -69,10 +86,37 @@ impl EvolvePayloadBuilderConfig {
if config.mint_admin.is_some() && config.mint_precompile_activation_height.is_none() {
config.mint_precompile_activation_height = Some(0);
}

config.contract_size_limit = extras.contract_size_limit;
config.contract_size_limit_activation_height =
extras.contract_size_limit_activation_height;
}
Ok(config)
}

/// Returns the contract size limit settings (limit, `activation_height`) if configured.
/// Returns None if no custom limit is set (uses EIP-170 default).
pub fn contract_size_limit_settings(&self) -> Option<(usize, u64)> {
self.contract_size_limit.map(|limit| {
let activation = self.contract_size_limit_activation_height.unwrap_or(0);
(limit, activation)
})
}

/// Returns the contract size limit for a given block number.
/// Uses the custom limit if configured and active, otherwise returns EIP-170 default.
pub fn contract_size_limit_for_block(&self, block_number: u64) -> usize {
self.contract_size_limit_settings()
.and_then(|(limit, activation)| {
if block_number >= activation {
Some(limit)
} else {
None
}
})
.unwrap_or(DEFAULT_CONTRACT_SIZE_LIMIT)
}

/// Validates the configuration
pub const fn validate(&self) -> Result<(), ConfigError> {
Ok(())
Expand Down Expand Up @@ -272,6 +316,7 @@ mod tests {
assert_eq!(config.mint_admin, None);
assert_eq!(config.base_fee_redirect_activation_height, None);
assert_eq!(config.mint_precompile_activation_height, None);
assert_eq!(config.contract_size_limit, None);
}

#[test]
Expand All @@ -285,6 +330,8 @@ mod tests {
mint_admin: Some(address!("00000000000000000000000000000000000000aa")),
base_fee_redirect_activation_height: Some(0),
mint_precompile_activation_height: Some(0),
contract_size_limit: None,
contract_size_limit_activation_height: None,
};
assert!(config_with_sink.validate().is_ok());
}
Expand All @@ -297,6 +344,8 @@ mod tests {
mint_admin: None,
base_fee_redirect_activation_height: Some(5),
mint_precompile_activation_height: None,
contract_size_limit: None,
contract_size_limit_activation_height: None,
};

assert_eq!(config.base_fee_sink_for_block(4), None);
Expand Down Expand Up @@ -330,4 +379,85 @@ mod tests {
assert_eq!(config.base_fee_sink, None);
assert_eq!(config.mint_admin, None);
}

#[test]
fn test_contract_size_limit_default() {
// Test default contract size limit (24KB per EIP-170)
let config = EvolvePayloadBuilderConfig::new();
assert_eq!(config.contract_size_limit, None);
assert_eq!(config.contract_size_limit_settings(), None);
// When no custom limit is set, use EIP-170 default for any block
assert_eq!(config.contract_size_limit_for_block(0), DEFAULT_CONTRACT_SIZE_LIMIT);
assert_eq!(config.contract_size_limit_for_block(0), 24 * 1024);
}

#[test]
fn test_contract_size_limit_from_chainspec() {
// Test contract size limit from chainspec with activation height
let extras = json!({
"contractSizeLimit": 131072,
"contractSizeLimitActivationHeight": 100
});

let chainspec = create_test_chainspec_with_extras(Some(extras));
let config = EvolvePayloadBuilderConfig::from_chain_spec(&chainspec).unwrap();

assert_eq!(config.contract_size_limit, Some(131072));
assert_eq!(config.contract_size_limit_activation_height, Some(100));
assert_eq!(config.contract_size_limit_settings(), Some((131072, 100)));
}

#[test]
fn test_contract_size_limit_respects_activation_height() {
// Test that contract size limit respects activation height
let extras = json!({
"contractSizeLimit": 131072,
"contractSizeLimitActivationHeight": 100
});

let chainspec = create_test_chainspec_with_extras(Some(extras));
let config = EvolvePayloadBuilderConfig::from_chain_spec(&chainspec).unwrap();

// Before activation: use EIP-170 default
assert_eq!(config.contract_size_limit_for_block(0), DEFAULT_CONTRACT_SIZE_LIMIT);
assert_eq!(config.contract_size_limit_for_block(99), DEFAULT_CONTRACT_SIZE_LIMIT);

// At and after activation: use custom limit
assert_eq!(config.contract_size_limit_for_block(100), 131072);
assert_eq!(config.contract_size_limit_for_block(1000), 131072);
}

#[test]
fn test_contract_size_limit_defaults_activation_to_zero() {
// Test that activation height defaults to 0 when limit is set but height is not
let extras = json!({
"contractSizeLimit": 131072
});

let chainspec = create_test_chainspec_with_extras(Some(extras));
let config = EvolvePayloadBuilderConfig::from_chain_spec(&chainspec).unwrap();

assert_eq!(config.contract_size_limit, Some(131072));
assert_eq!(config.contract_size_limit_activation_height, None);
// Settings method defaults activation to 0
assert_eq!(config.contract_size_limit_settings(), Some((131072, 0)));
// Limit is active from block 0
assert_eq!(config.contract_size_limit_for_block(0), 131072);
}

#[test]
fn test_contract_size_limit_not_set_uses_default() {
// Test that missing contractSizeLimit uses EIP-170 default
let extras = json!({
"baseFeeSink": "0x0000000000000000000000000000000000000001"
});

let chainspec = create_test_chainspec_with_extras(Some(extras));
let config = EvolvePayloadBuilderConfig::from_chain_spec(&chainspec).unwrap();

assert_eq!(config.contract_size_limit, None);
assert_eq!(config.contract_size_limit_settings(), None);
assert_eq!(config.contract_size_limit_for_block(0), DEFAULT_CONTRACT_SIZE_LIMIT);
assert_eq!(config.contract_size_limit_for_block(1000000), DEFAULT_CONTRACT_SIZE_LIMIT);
}
}
22 changes: 20 additions & 2 deletions crates/node/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

use alloy_evm::eth::{spec::EthExecutorSpec, EthEvmFactory};
use ev_revm::{
with_ev_handler, BaseFeeRedirect, BaseFeeRedirectSettings, EvEvmFactory, MintPrecompileSettings,
with_ev_handler, BaseFeeRedirect, BaseFeeRedirectSettings, ContractSizeLimitSettings,
EvEvmFactory, MintPrecompileSettings,
};
use reth_chainspec::ChainSpec;
use reth_ethereum::{
Expand Down Expand Up @@ -51,7 +52,24 @@ where
.mint_precompile_settings()
.map(|(admin, activation)| MintPrecompileSettings::new(admin, activation));

Ok(with_ev_handler(base_config, redirect, mint_precompile))
let contract_size_limit = evolve_config
.contract_size_limit_settings()
.map(|(limit, activation)| {
info!(
target = "ev-reth::executor",
limit_bytes = limit,
activation_height = activation,
"Custom contract size limit enabled"
);
ContractSizeLimitSettings::new(limit, activation)
});

Ok(with_ev_handler(
base_config,
redirect,
mint_precompile,
contract_size_limit,
))
}

/// Thin wrapper so we can plug the EV executor into the node components builder.
Expand Down
Loading
Loading