Skip to content

Commit

Permalink
fix(executor): Holocene EIP-1559 params in Header (#622)
Browse files Browse the repository at this point in the history
* fix(executor): Holocene EIP-1559 params in Header

* throw if params are missing post-holocene

* fix attributes builder + fmt

* tests

* sent `B64::ZERO` for the first holocene block

lin

* simplify branch
  • Loading branch information
clabby authored Oct 4, 2024
1 parent a9dd0e7 commit 44ad84c
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 40 deletions.
71 changes: 63 additions & 8 deletions crates/derive/src/attributes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,17 +196,32 @@ where
gas_limit: Some(u64::from_be_bytes(
alloy_primitives::U64::from(sys_config.gas_limit).to_be_bytes(),
)),
eip_1559_params: eip_1559_params_from_system_config(&sys_config),
eip_1559_params: eip_1559_params_from_system_config(
&self.rollup_cfg,
l2_parent.block_info.timestamp,
next_l2_time,
&sys_config,
),
})
}
}

/// Returns the eip1559 parameters from a [SystemConfig] encoded as a [B64].
fn eip_1559_params_from_system_config(sys_config: &SystemConfig) -> Option<B64> {
if sys_config.eip1559_denominator.is_none() && sys_config.eip1559_elasticity.is_none() {
None
fn eip_1559_params_from_system_config(
rollup_config: &RollupConfig,
parent_timestamp: u64,
next_timestamp: u64,
sys_config: &SystemConfig,
) -> Option<B64> {
let is_holocene = rollup_config.is_holocene_active(next_timestamp);

// For the first holocene block, a zero'd out B64 is returned to signal the
// execution layer to use the canyon base fee parameters. Else, the system
// config's eip1559 parameters are encoded as a B64.
if is_holocene && !rollup_config.is_holocene_active(parent_timestamp) {
Some(B64::ZERO)
} else {
Some(B64::from_slice(
is_holocene.then_some(B64::from_slice(
&[
sys_config.eip1559_denominator.unwrap_or_default().to_be_bytes(),
sys_config.eip1559_elasticity.unwrap_or_default().to_be_bytes(),
Expand Down Expand Up @@ -313,30 +328,70 @@ mod tests {

#[test]
fn test_eip_1559_params_from_system_config_none() {
let rollup_config = RollupConfig::default();
let sys_config = SystemConfig::default();
assert_eq!(eip_1559_params_from_system_config(&sys_config), None);
assert_eq!(eip_1559_params_from_system_config(&rollup_config, 0, 0, &sys_config), None);
}

#[test]
fn test_eip_1559_params_from_system_config_some() {
let rollup_config = RollupConfig { holocene_time: Some(0), ..Default::default() };
let sys_config = SystemConfig {
eip1559_denominator: Some(1),
eip1559_elasticity: None,
..Default::default()
};
let expected = Some(B64::from_slice(&[1u32.to_be_bytes(), 0u32.to_be_bytes()].concat()));
assert_eq!(eip_1559_params_from_system_config(&sys_config), expected);
assert_eq!(eip_1559_params_from_system_config(&rollup_config, 0, 0, &sys_config), expected);
}

#[test]
fn test_eip_1559_params_from_system_config() {
let rollup_config = RollupConfig { holocene_time: Some(0), ..Default::default() };
let sys_config = SystemConfig {
eip1559_denominator: Some(1),
eip1559_elasticity: Some(2),
..Default::default()
};
let expected = Some(B64::from_slice(&[1u32.to_be_bytes(), 2u32.to_be_bytes()].concat()));
assert_eq!(eip_1559_params_from_system_config(&sys_config), expected);
assert_eq!(eip_1559_params_from_system_config(&rollup_config, 0, 0, &sys_config), expected);
}

#[test]
fn test_default_eip_1559_params_from_system_config() {
let rollup_config = RollupConfig { holocene_time: Some(0), ..Default::default() };
let sys_config = SystemConfig {
eip1559_denominator: None,
eip1559_elasticity: None,
..Default::default()
};
let expected = Some(B64::ZERO);
assert_eq!(eip_1559_params_from_system_config(&rollup_config, 0, 0, &sys_config), expected);
}

#[test]
fn test_default_eip_1559_params_from_system_config_pre_holocene() {
let rollup_config = RollupConfig::default();
let sys_config = SystemConfig {
eip1559_denominator: Some(1),
eip1559_elasticity: Some(2),
..Default::default()
};
assert_eq!(eip_1559_params_from_system_config(&rollup_config, 0, 0, &sys_config), None);
}

#[test]
fn test_default_eip_1559_params_first_block_holocene() {
let rollup_config = RollupConfig { holocene_time: Some(2), ..Default::default() };
let sys_config = SystemConfig {
eip1559_denominator: Some(1),
eip1559_elasticity: Some(2),
..Default::default()
};
assert_eq!(
eip_1559_params_from_system_config(&rollup_config, 0, 2, &sys_config),
Some(B64::ZERO)
);
}

#[tokio::test]
Expand Down
3 changes: 3 additions & 0 deletions crates/executor/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ pub enum ExecutorError {
/// Missing gas limit in the payload attributes.
#[error("Gas limit not provided in payload attributes")]
MissingGasLimit,
/// Missing EIP-1559 parameters in execution payload post-Holocene.
#[error("Missing EIP-1559 parameters in execution payload post-Holocene")]
MissingEIP1559Params,
/// Missing parent beacon block root in the payload attributes.
#[error("Parent beacon block root not provided in payload attributes")]
MissingParentBeaconBlockRoot,
Expand Down
93 changes: 61 additions & 32 deletions crates/executor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,12 @@ where
payload: OptimismPayloadAttributes,
) -> ExecutorResult<&Header> {
// Prepare the `revm` environment.
let base_fee_params = Self::active_base_fee_params(self.config, &payload)?;
let initialized_block_env = Self::prepare_block_env(
self.revm_spec_id(payload.payload_attributes.timestamp),
self.config,
self.trie_db.parent_block_header(),
&payload,
&base_fee_params,
);
let initialized_cfg = self.evm_cfg_env(payload.payload_attributes.timestamp);
let block_number = initialized_block_env.number.to::<u64>();
Expand Down Expand Up @@ -305,6 +306,21 @@ where
})
.unwrap_or_default();

let encoded_base_fee_params = self
.config
.is_holocene_active(payload.payload_attributes.timestamp)
.then(|| {
let mut encoded_params = B64::ZERO;
encoded_params[0..4].copy_from_slice(
(base_fee_params.max_change_denominator as u32).to_be_bytes().as_ref(),
);
encoded_params[4..8].copy_from_slice(
(base_fee_params.elasticity_multiplier as u32).to_be_bytes().as_ref(),
);
encoded_params
})
.unwrap_or_default();

// Construct the new header.
let header = Header {
parent_hash: state.database.parent_block_header().seal(),
Expand All @@ -322,12 +338,7 @@ where
gas_used: cumulative_gas_used,
timestamp: payload.payload_attributes.timestamp,
mix_hash: payload.payload_attributes.prev_randao,
nonce: self
.config
.is_holocene_active(payload.payload_attributes.timestamp)
.then_some(payload.eip_1559_params)
.flatten()
.unwrap_or_default(),
nonce: encoded_base_fee_params,
base_fee_per_gas: base_fee.try_into().ok(),
blob_gas_used,
excess_blob_gas: excess_blob_gas.and_then(|x| x.try_into().ok()),
Expand Down Expand Up @@ -502,41 +513,22 @@ where
/// Prepares a [BlockEnv] with the given [OptimismPayloadAttributes].
///
/// ## Takes
/// - `payload`: The payload to prepare the environment for.
/// - `env`: The block environment to prepare.
/// - `spec_id`: The [SpecId] to prepare the environment for.
/// - `parent_header`: The parent header of the block to be executed.
/// - `payload_attrs`: The payload to prepare the environment for.
/// - `base_fee_params`: The active base fee parameters for the block.
fn prepare_block_env(
spec_id: SpecId,
config: &RollupConfig,
parent_header: &Header,
payload_attrs: &OptimismPayloadAttributes,
base_fee_params: &BaseFeeParams,
) -> BlockEnv {
let blob_excess_gas_and_price = parent_header
.next_block_excess_blob_gas()
.or_else(|| spec_id.is_enabled_in(SpecId::ECOTONE).then_some(0))
.map(BlobExcessGasAndPrice::new);
// If the payload attribute timestamp is past canyon activation,
// use the canyon base fee params from the rollup config.
let base_fee_params = if config
.is_holocene_active(payload_attrs.payload_attributes.timestamp)
{
// If the parent header nonce is zero, use the default base fee params.
if parent_header.nonce == B64::ZERO {
config.canyon_base_fee_params
} else {
let denominator = u32::from_be_bytes(parent_header.nonce[0..4].try_into().unwrap());
let elasticity = u32::from_be_bytes(parent_header.nonce[4..8].try_into().unwrap());
BaseFeeParams {
max_change_denominator: denominator as u128,
elasticity_multiplier: elasticity as u128,
}
}
} else if config.is_canyon_active(payload_attrs.payload_attributes.timestamp) {
config.canyon_base_fee_params
} else {
config.base_fee_params
};
let next_block_base_fee =
parent_header.next_block_base_fee(base_fee_params).unwrap_or_default();
parent_header.next_block_base_fee(*base_fee_params).unwrap_or_default();

BlockEnv {
number: U256::from(parent_header.number + 1),
Expand All @@ -550,6 +542,43 @@ where
}
}

/// Returns the active base fee parameters for the given payload attributes.
///
/// ## Takes
/// - `config`: The rollup config to use for the computation.
/// - `payload_attrs`: The payload attributes to use for the computation.
fn active_base_fee_params(
config: &RollupConfig,
payload_attrs: &OptimismPayloadAttributes,
) -> ExecutorResult<BaseFeeParams> {
// If the payload attribute timestamp is past canyon activation,
// use the canyon base fee params from the rollup config.
let base_fee_params =
if config.is_holocene_active(payload_attrs.payload_attributes.timestamp) {
let params =
payload_attrs.eip_1559_params.ok_or(ExecutorError::MissingEIP1559Params)?;

// If the parameters sent are equal to `0`, use the canyon base fee params.
// Otherwise, use the EIP-1559 parameters sent through the payload.
if params == B64::ZERO {
config.canyon_base_fee_params
} else {
let denominator = u32::from_be_bytes(params[0..4].try_into().unwrap());
let elasticity = u32::from_be_bytes(params[4..8].try_into().unwrap());
BaseFeeParams {
max_change_denominator: denominator as u128,
elasticity_multiplier: elasticity as u128,
}
}
} else if config.is_canyon_active(payload_attrs.payload_attributes.timestamp) {
config.canyon_base_fee_params
} else {
config.base_fee_params
};

Ok(base_fee_params)
}

/// Prepares a [TxEnv] with the given [OpTxEnvelope].
///
/// ## Takes
Expand Down

0 comments on commit 44ad84c

Please sign in to comment.