Skip to content

Commit 2d76e7b

Browse files
committed
feat: add custom contract size limit settings with activation height
1 parent 9688367 commit 2d76e7b

5 files changed

Lines changed: 157 additions & 36 deletions

File tree

crates/ev-revm/src/factory.rs

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,39 @@ impl MintPrecompileSettings {
7373
}
7474
}
7575

76+
/// Settings for custom contract size limit with activation height.
77+
#[derive(Debug, Clone, Copy)]
78+
pub struct ContractSizeLimitSettings {
79+
limit: usize,
80+
activation_height: u64,
81+
}
82+
83+
impl ContractSizeLimitSettings {
84+
/// Creates a new settings object.
85+
pub const fn new(limit: usize, activation_height: u64) -> Self {
86+
Self {
87+
limit,
88+
activation_height,
89+
}
90+
}
91+
92+
const fn activation_height(&self) -> u64 {
93+
self.activation_height
94+
}
95+
96+
const fn limit(&self) -> usize {
97+
self.limit
98+
}
99+
}
100+
76101
/// Wrapper around an existing `EvmFactory` that produces [`EvEvm`] instances.
77102
#[derive(Debug, Clone)]
78103
pub struct EvEvmFactory<F> {
79104
inner: F,
80105
redirect: Option<BaseFeeRedirectSettings>,
81106
mint_precompile: Option<MintPrecompileSettings>,
82-
/// Maximum contract code size in bytes. When set, overrides the default EIP-170 limit.
83-
limit_contract_code_size: Option<usize>,
107+
/// Custom contract size limit with activation height.
108+
contract_size_limit: Option<ContractSizeLimitSettings>,
84109
}
85110

86111
impl<F> EvEvmFactory<F> {
@@ -89,16 +114,26 @@ impl<F> EvEvmFactory<F> {
89114
inner: F,
90115
redirect: Option<BaseFeeRedirectSettings>,
91116
mint_precompile: Option<MintPrecompileSettings>,
92-
limit_contract_code_size: Option<usize>,
117+
contract_size_limit: Option<ContractSizeLimitSettings>,
93118
) -> Self {
94119
Self {
95120
inner,
96121
redirect,
97122
mint_precompile,
98-
limit_contract_code_size,
123+
contract_size_limit,
99124
}
100125
}
101126

127+
fn contract_size_limit_for_block(&self, block_number: U256) -> Option<usize> {
128+
self.contract_size_limit.and_then(|settings| {
129+
if block_number >= U256::from(settings.activation_height()) {
130+
Some(settings.limit())
131+
} else {
132+
None
133+
}
134+
})
135+
}
136+
102137
fn install_mint_precompile(&self, precompiles: &mut PrecompilesMap, block_number: U256) {
103138
let Some(settings) = self.mint_precompile else {
104139
return;
@@ -148,8 +183,8 @@ impl EvmFactory for EvEvmFactory<EthEvmFactory> {
148183
mut evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
149184
) -> Self::Evm<DB, NoOpInspector> {
150185
let block_number = evm_env.block_env.number;
151-
// Apply custom contract size limit if configured
152-
if let Some(limit) = self.limit_contract_code_size {
186+
// Apply custom contract size limit if configured and active for this block
187+
if let Some(limit) = self.contract_size_limit_for_block(block_number) {
153188
evm_env.cfg_env.limit_contract_code_size = Some(limit);
154189
}
155190
let inner = self.inner.create_evm(db, evm_env);
@@ -168,8 +203,8 @@ impl EvmFactory for EvEvmFactory<EthEvmFactory> {
168203
inspector: I,
169204
) -> Self::Evm<DB, I> {
170205
let block_number = input.block_env.number;
171-
// Apply custom contract size limit if configured
172-
if let Some(limit) = self.limit_contract_code_size {
206+
// Apply custom contract size limit if configured and active for this block
207+
if let Some(limit) = self.contract_size_limit_for_block(block_number) {
173208
input.cfg_env.limit_contract_code_size = Some(limit);
174209
}
175210
let inner = self.inner.create_evm_with_inspector(db, input, inspector);
@@ -187,7 +222,7 @@ pub fn with_ev_handler<ChainSpec>(
187222
config: EthEvmConfig<ChainSpec, EthEvmFactory>,
188223
redirect: Option<BaseFeeRedirectSettings>,
189224
mint_precompile: Option<MintPrecompileSettings>,
190-
limit_contract_code_size: Option<usize>,
225+
contract_size_limit: Option<ContractSizeLimitSettings>,
191226
) -> EthEvmConfig<ChainSpec, EvEvmFactory<EthEvmFactory>> {
192227
let EthEvmConfig {
193228
executor_factory,
@@ -197,7 +232,7 @@ pub fn with_ev_handler<ChainSpec>(
197232
*executor_factory.evm_factory(),
198233
redirect,
199234
mint_precompile,
200-
limit_contract_code_size,
235+
contract_size_limit,
201236
);
202237
let new_executor_factory = EthBlockExecutorFactory::new(
203238
*executor_factory.receipt_builder(),

crates/ev-revm/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,8 @@ pub use api::EvBuilder;
1111
pub use base_fee::{BaseFeeRedirect, BaseFeeRedirectError};
1212
pub use config::{BaseFeeConfig, ConfigError};
1313
pub use evm::{DefaultEvEvm, EvEvm};
14-
pub use factory::{with_ev_handler, BaseFeeRedirectSettings, EvEvmFactory, MintPrecompileSettings};
14+
pub use factory::{
15+
with_ev_handler, BaseFeeRedirectSettings, ContractSizeLimitSettings, EvEvmFactory,
16+
MintPrecompileSettings,
17+
};
1518
pub use handler::EvHandler;

crates/node/src/config.rs

Lines changed: 88 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use alloy_primitives::Address;
22
use reth_chainspec::ChainSpec;
33
use serde::{Deserialize, Serialize};
44

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

88
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
99
struct ChainspecEvolveConfig {
@@ -15,9 +15,12 @@ struct ChainspecEvolveConfig {
1515
pub mint_admin: Option<Address>,
1616
#[serde(default, rename = "mintPrecompileActivationHeight")]
1717
pub mint_precompile_activation_height: Option<u64>,
18-
/// Maximum contract code size in bytes. Defaults to 128KB if not specified.
18+
/// Maximum contract code size in bytes. Defaults to 24KB (EIP-170) if not specified.
1919
#[serde(default, rename = "contractSizeLimit")]
2020
pub contract_size_limit: Option<usize>,
21+
/// Block height at which the custom contract size limit activates.
22+
#[serde(default, rename = "contractSizeLimitActivationHeight")]
23+
pub contract_size_limit_activation_height: Option<u64>,
2124
}
2225

2326
/// Configuration for the Evolve payload builder
@@ -35,9 +38,12 @@ pub struct EvolvePayloadBuilderConfig {
3538
/// Optional activation height for mint precompile; defaults to 0 when admin set.
3639
#[serde(default)]
3740
pub mint_precompile_activation_height: Option<u64>,
38-
/// Maximum contract code size in bytes. Defaults to 128KB.
41+
/// Maximum contract code size in bytes. Defaults to 24KB (EIP-170).
3942
#[serde(default)]
4043
pub contract_size_limit: Option<usize>,
44+
/// Block height at which the custom contract size limit activates.
45+
#[serde(default)]
46+
pub contract_size_limit_activation_height: Option<u64>,
4147
}
4248

4349
impl EvolvePayloadBuilderConfig {
@@ -49,6 +55,7 @@ impl EvolvePayloadBuilderConfig {
4955
base_fee_redirect_activation_height: None,
5056
mint_precompile_activation_height: None,
5157
contract_size_limit: None,
58+
contract_size_limit_activation_height: None,
5259
}
5360
}
5461

@@ -81,13 +88,32 @@ impl EvolvePayloadBuilderConfig {
8188
}
8289

8390
config.contract_size_limit = extras.contract_size_limit;
91+
config.contract_size_limit_activation_height =
92+
extras.contract_size_limit_activation_height;
8493
}
8594
Ok(config)
8695
}
8796

88-
/// Returns the contract size limit, defaulting to 128KB.
89-
pub fn contract_size_limit(&self) -> usize {
90-
self.contract_size_limit
97+
/// Returns the contract size limit settings (limit, `activation_height`) if configured.
98+
/// Returns None if no custom limit is set (uses EIP-170 default).
99+
pub fn contract_size_limit_settings(&self) -> Option<(usize, u64)> {
100+
self.contract_size_limit.map(|limit| {
101+
let activation = self.contract_size_limit_activation_height.unwrap_or(0);
102+
(limit, activation)
103+
})
104+
}
105+
106+
/// Returns the contract size limit for a given block number.
107+
/// Uses the custom limit if configured and active, otherwise returns EIP-170 default.
108+
pub fn contract_size_limit_for_block(&self, block_number: u64) -> usize {
109+
self.contract_size_limit_settings()
110+
.and_then(|(limit, activation)| {
111+
if block_number >= activation {
112+
Some(limit)
113+
} else {
114+
None
115+
}
116+
})
91117
.unwrap_or(DEFAULT_CONTRACT_SIZE_LIMIT)
92118
}
93119

@@ -305,6 +331,7 @@ mod tests {
305331
base_fee_redirect_activation_height: Some(0),
306332
mint_precompile_activation_height: Some(0),
307333
contract_size_limit: None,
334+
contract_size_limit_activation_height: None,
308335
};
309336
assert!(config_with_sink.validate().is_ok());
310337
}
@@ -318,6 +345,7 @@ mod tests {
318345
base_fee_redirect_activation_height: Some(5),
319346
mint_precompile_activation_height: None,
320347
contract_size_limit: None,
348+
contract_size_limit_activation_height: None,
321349
};
322350

323351
assert_eq!(config.base_fee_sink_for_block(4), None);
@@ -354,30 +382,72 @@ mod tests {
354382

355383
#[test]
356384
fn test_contract_size_limit_default() {
357-
// Test default contract size limit (128KB)
385+
// Test default contract size limit (24KB per EIP-170)
358386
let config = EvolvePayloadBuilderConfig::new();
359387
assert_eq!(config.contract_size_limit, None);
360-
assert_eq!(config.contract_size_limit(), DEFAULT_CONTRACT_SIZE_LIMIT);
361-
assert_eq!(config.contract_size_limit(), 128 * 1024);
388+
assert_eq!(config.contract_size_limit_settings(), None);
389+
// When no custom limit is set, use EIP-170 default for any block
390+
assert_eq!(config.contract_size_limit_for_block(0), DEFAULT_CONTRACT_SIZE_LIMIT);
391+
assert_eq!(config.contract_size_limit_for_block(0), 24 * 1024);
362392
}
363393

364394
#[test]
365395
fn test_contract_size_limit_from_chainspec() {
366-
// Test contract size limit from chainspec
396+
// Test contract size limit from chainspec with activation height
397+
let extras = json!({
398+
"contractSizeLimit": 131072,
399+
"contractSizeLimitActivationHeight": 100
400+
});
401+
402+
let chainspec = create_test_chainspec_with_extras(Some(extras));
403+
let config = EvolvePayloadBuilderConfig::from_chain_spec(&chainspec).unwrap();
404+
405+
assert_eq!(config.contract_size_limit, Some(131072));
406+
assert_eq!(config.contract_size_limit_activation_height, Some(100));
407+
assert_eq!(config.contract_size_limit_settings(), Some((131072, 100)));
408+
}
409+
410+
#[test]
411+
fn test_contract_size_limit_respects_activation_height() {
412+
// Test that contract size limit respects activation height
413+
let extras = json!({
414+
"contractSizeLimit": 131072,
415+
"contractSizeLimitActivationHeight": 100
416+
});
417+
418+
let chainspec = create_test_chainspec_with_extras(Some(extras));
419+
let config = EvolvePayloadBuilderConfig::from_chain_spec(&chainspec).unwrap();
420+
421+
// Before activation: use EIP-170 default
422+
assert_eq!(config.contract_size_limit_for_block(0), DEFAULT_CONTRACT_SIZE_LIMIT);
423+
assert_eq!(config.contract_size_limit_for_block(99), DEFAULT_CONTRACT_SIZE_LIMIT);
424+
425+
// At and after activation: use custom limit
426+
assert_eq!(config.contract_size_limit_for_block(100), 131072);
427+
assert_eq!(config.contract_size_limit_for_block(1000), 131072);
428+
}
429+
430+
#[test]
431+
fn test_contract_size_limit_defaults_activation_to_zero() {
432+
// Test that activation height defaults to 0 when limit is set but height is not
367433
let extras = json!({
368-
"contractSizeLimit": 256000
434+
"contractSizeLimit": 131072
369435
});
370436

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

374-
assert_eq!(config.contract_size_limit, Some(256000));
375-
assert_eq!(config.contract_size_limit(), 256000);
440+
assert_eq!(config.contract_size_limit, Some(131072));
441+
assert_eq!(config.contract_size_limit_activation_height, None);
442+
// Settings method defaults activation to 0
443+
assert_eq!(config.contract_size_limit_settings(), Some((131072, 0)));
444+
// Limit is active from block 0
445+
assert_eq!(config.contract_size_limit_for_block(0), 131072);
376446
}
377447

378448
#[test]
379449
fn test_contract_size_limit_not_set_uses_default() {
380-
// Test that missing contractSizeLimit uses default
450+
// Test that missing contractSizeLimit uses EIP-170 default
381451
let extras = json!({
382452
"baseFeeSink": "0x0000000000000000000000000000000000000001"
383453
});
@@ -386,6 +456,8 @@ mod tests {
386456
let config = EvolvePayloadBuilderConfig::from_chain_spec(&chainspec).unwrap();
387457

388458
assert_eq!(config.contract_size_limit, None);
389-
assert_eq!(config.contract_size_limit(), DEFAULT_CONTRACT_SIZE_LIMIT);
459+
assert_eq!(config.contract_size_limit_settings(), None);
460+
assert_eq!(config.contract_size_limit_for_block(0), DEFAULT_CONTRACT_SIZE_LIMIT);
461+
assert_eq!(config.contract_size_limit_for_block(1000000), DEFAULT_CONTRACT_SIZE_LIMIT);
390462
}
391463
}

crates/node/src/executor.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
33
use alloy_evm::eth::{spec::EthExecutorSpec, EthEvmFactory};
44
use ev_revm::{
5-
with_ev_handler, BaseFeeRedirect, BaseFeeRedirectSettings, EvEvmFactory, MintPrecompileSettings,
5+
with_ev_handler, BaseFeeRedirect, BaseFeeRedirectSettings, ContractSizeLimitSettings,
6+
EvEvmFactory, MintPrecompileSettings,
67
};
78
use reth_chainspec::ChainSpec;
89
use reth_ethereum::{
@@ -51,12 +52,17 @@ where
5152
.mint_precompile_settings()
5253
.map(|(admin, activation)| MintPrecompileSettings::new(admin, activation));
5354

54-
let contract_size_limit = Some(evolve_config.contract_size_limit());
55-
info!(
56-
target = "ev-reth::executor",
57-
limit_bytes = contract_size_limit,
58-
"Contract size limit configured"
59-
);
55+
let contract_size_limit = evolve_config
56+
.contract_size_limit_settings()
57+
.map(|(limit, activation)| {
58+
info!(
59+
target = "ev-reth::executor",
60+
limit_bytes = limit,
61+
activation_height = activation,
62+
"Custom contract size limit enabled"
63+
);
64+
ContractSizeLimitSettings::new(limit, activation)
65+
});
6066

6167
Ok(with_ev_handler(
6268
base_config,

crates/tests/src/common.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use std::sync::Arc;
88
use alloy_consensus::{transaction::SignerRecoverable, TxLegacy, TypedTransaction};
99
use alloy_genesis::Genesis;
1010
use alloy_primitives::{Address, Bytes, ChainId, Signature, TxKind, B256, U256};
11-
use ev_revm::{with_ev_handler, BaseFeeRedirect, BaseFeeRedirectSettings, MintPrecompileSettings};
11+
use ev_revm::{
12+
with_ev_handler, BaseFeeRedirect, BaseFeeRedirectSettings, ContractSizeLimitSettings,
13+
MintPrecompileSettings,
14+
};
1215
use eyre::Result;
1316
use reth_chainspec::{ChainSpec, ChainSpecBuilder};
1417
use reth_ethereum_primitives::TransactionSigned;
@@ -137,7 +140,9 @@ impl EvolveTestFixture {
137140
let mint_precompile = config
138141
.mint_precompile_settings()
139142
.map(|(admin, activation)| MintPrecompileSettings::new(admin, activation));
140-
let contract_size_limit = Some(config.contract_size_limit());
143+
let contract_size_limit = config
144+
.contract_size_limit_settings()
145+
.map(|(limit, activation)| ContractSizeLimitSettings::new(limit, activation));
141146
let wrapped_evm = with_ev_handler(
142147
evm_config,
143148
base_fee_redirect,

0 commit comments

Comments
 (0)