diff --git a/crates/protocol/src/block_info.rs b/crates/protocol/src/block_info.rs index 94c366b9..79d1d785 100644 --- a/crates/protocol/src/block_info.rs +++ b/crates/protocol/src/block_info.rs @@ -16,15 +16,17 @@ use op_alloy_genesis::{RollupConfig, SystemConfig}; const REGOLITH_SYSTEM_TX_GAS: u64 = 1_000_000; /// The type byte identifier for the L1 scalar format in Ecotone. const L1_SCALAR_ECOTONE: u8 = 1; +/// The type byte identifier for the L1 scalar format in Holocene. +const L1_SCALAR_HOLOCENE: u8 = 2; /// The length of an L1 info transaction in Bedrock. const L1_INFO_TX_LEN_BEDROCK: usize = 4 + 32 * 8; /// The length of an L1 info transaction in Ecotone. const L1_INFO_TX_LEN_ECOTONE: usize = 4 + 32 * 5; /// The length of an L1 info transaction in Holocene. /// -/// The Holocene L1 info transaction size is [L1_INFO_TX_LEN_ECOTONE] + 8 * 2 -/// for the EIP-1559 denominator and elasticity parameters. -const L1_INFO_TX_LEN_HOLOCENE: usize = 4 + 32 * 5 + 8 * 2; +/// The Holocene L1 info transaction size is [L1_INFO_TX_LEN_ECOTONE] + 8 * 3 + 4 +/// for the EIP-1559 denominator and elasticity parameters and the operator fee scalar and constant. +const L1_INFO_TX_LEN_HOLOCENE: usize = 4 + 32 * 5 + 8 * 3 + 4; /// The 4 byte selector of the /// "setL1BlockValues(uint64,uint64,uint256,bytes32,uint64,bytes32,uint256,uint256)" function const L1_INFO_TX_SELECTOR_BEDROCK: [u8; 4] = [0x01, 0x5d, 0x8e, 0xb9]; @@ -148,6 +150,8 @@ pub struct L1BlockInfoEcotone { /// | 32 | BatcherHash | /// | 8 | Eip1559Denominator | /// | 8 | Eip1559Elasticity | +/// | 4 | OperatorFeeScalar | +/// | 8 | OperatorFeeConstant | /// +---------+--------------------------+ #[derive(Debug, Clone, Hash, Eq, PartialEq, Default, Copy)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -174,6 +178,10 @@ pub struct L1BlockInfoHolocene { pub eip_1559_denominator: u64, /// The EIP-1559 elasticity parameter pub eip_1559_elasticity: u64, + /// The operator fee scalar + pub operator_fee_scalar: u32, + /// The operator fee constant + pub operator_fee_constant: u64, } /// An error type for parsing L1 block info transactions. @@ -187,6 +195,10 @@ pub enum BlockInfoError { Eip1559Denominator, /// Failed to parse the EIP-1559 elasticity parameter. Eip1559Elasticity, + /// Failed to parse the Operator Fee Scalar. + OperatorFeeScalar, + /// Failed to parse the Operator Fee Constant. + OperatorFeeConstant, } impl core::fmt::Display for BlockInfoError { @@ -202,6 +214,12 @@ impl core::fmt::Display for BlockInfoError { Self::Eip1559Elasticity => { write!(f, "Failed to parse the EIP-1559 elasticity parameter") } + Self::OperatorFeeScalar => { + write!(f, "Failed to parse the Operator fee scalar parameter") + } + Self::OperatorFeeConstant => { + write!(f, "Failed to parse the Operator fee constant parameter") + } } } } @@ -237,7 +255,7 @@ impl L1BlockInfoTx { ) -> Result { if rollup_config.is_holocene_active(l2_block_time) { let scalar = system_config.scalar.to_be_bytes::<32>(); - let blob_base_fee_scalar = (scalar[0] == L1_SCALAR_ECOTONE) + let blob_base_fee_scalar = (scalar[0] >= L1_SCALAR_ECOTONE) .then(|| { Ok::(u32::from_be_bytes( scalar[24..28] @@ -260,6 +278,20 @@ impl L1BlockInfoTx { .elasticity_multiplier .try_into() .map_err(|_| BlockInfoError::Eip1559Elasticity)?; + let (operator_fee_scalar, operator_fee_constant) = (scalar[0] == L1_SCALAR_HOLOCENE) + .then(|| { + let operator_fee_scalar = u32::from_be_bytes( + scalar[12..16].try_into().map_err(|_| BlockInfoError::OperatorFeeScalar)?, + ); + let operator_fee_constant = u64::from_be_bytes( + scalar[16..24] + .try_into() + .map_err(|_| BlockInfoError::OperatorFeeConstant)?, + ); + Ok::<(u32, u64), BlockInfoError>((operator_fee_scalar, operator_fee_constant)) + }) + .transpose()? + .unwrap_or_default(); return Ok(Self::Holocene(L1BlockInfoHolocene { number: l1_header.number, time: l1_header.timestamp, @@ -272,6 +304,8 @@ impl L1BlockInfoTx { base_fee_scalar, eip_1559_denominator, eip_1559_elasticity, + operator_fee_scalar, + operator_fee_constant, })); } // In the first block of Ecotone, the L1Block contract has not been upgraded yet due to the @@ -584,6 +618,8 @@ impl L1BlockInfoHolocene { buf.extend_from_slice(self.batcher_address.into_word().as_ref()); buf.extend_from_slice(self.eip_1559_denominator.to_be_bytes().as_ref()); buf.extend_from_slice(self.eip_1559_elasticity.to_be_bytes().as_ref()); + buf.extend_from_slice(self.operator_fee_scalar.to_be_bytes().as_ref()); + buf.extend_from_slice(self.operator_fee_constant.to_be_bytes().as_ref()); buf.into() } @@ -627,6 +663,12 @@ impl L1BlockInfoHolocene { let eip_1559_elasticity = u64::from_be_bytes(r[172..180].try_into().map_err(|_| { DecodeError::ParseError("Conversion error for EIP-1559 elasticity".to_string()) })?); + let operator_fee_scalar = u32::from_be_bytes(r[180..184].try_into().map_err(|_| { + DecodeError::ParseError("Conversion error for operator fee scalar".to_string()) + })?); + let operator_fee_constant = u64::from_be_bytes(r[184..192].try_into().map_err(|_| { + DecodeError::ParseError("Conversion error for operator fee constant".to_string()) + })?); Ok(Self { number: l1_block_number, @@ -640,6 +682,8 @@ impl L1BlockInfoHolocene { base_fee_scalar, eip_1559_denominator, eip_1559_elasticity, + operator_fee_scalar, + operator_fee_constant, }) } } @@ -652,7 +696,7 @@ mod test { const RAW_BEDROCK_INFO_TX: [u8; L1_INFO_TX_LEN_BEDROCK] = hex!("015d8eb9000000000000000000000000000000000000000000000000000000000117c4eb0000000000000000000000000000000000000000000000000000000065280377000000000000000000000000000000000000000000000000000000026d05d953392012032675be9f94aae5ab442de73c5f4fb1bf30fa7dd0d2442239899a40fc00000000000000000000000000000000000000000000000000000000000000040000000000000000000000006887246668a3b87f54deb3b94ba47a6f63f3298500000000000000000000000000000000000000000000000000000000000000bc00000000000000000000000000000000000000000000000000000000000a6fe0"); const RAW_ECOTONE_INFO_TX: [u8; L1_INFO_TX_LEN_ECOTONE] = hex!("440a5e2000000558000c5fc5000000000000000500000000661c277300000000012bec20000000000000000000000000000000000000000000000000000000026e9f109900000000000000000000000000000000000000000000000000000000000000011c4c84c50740386c7dc081efddd644405f04cde73e30a2e381737acce9f5add30000000000000000000000006887246668a3b87f54deb3b94ba47a6f63f32985"); - const RAW_HOLOCENE_INFO_TX: [u8; L1_INFO_TX_LEN_HOLOCENE] = hex!("d1fbe15b00000558000c5fc5000000000000000500000000661c277300000000012bec20000000000000000000000000000000000000000000000000000000026e9f109900000000000000000000000000000000000000000000000000000000000000011c4c84c50740386c7dc081efddd644405f04cde73e30a2e381737acce9f5add30000000000000000000000006887246668a3b87f54deb3b94ba47a6f63f3298500000000000012340000000000005678"); + const RAW_HOLOCENE_INFO_TX: [u8; L1_INFO_TX_LEN_HOLOCENE] = hex!("d1fbe15b00000558000c5fc5000000000000000500000000661c277300000000012bec20000000000000000000000000000000000000000000000000000000026e9f109900000000000000000000000000000000000000000000000000000000000000011c4c84c50740386c7dc081efddd644405f04cde73e30a2e381737acce9f5add30000000000000000000000006887246668a3b87f54deb3b94ba47a6f63f32985000000000000123400000000000056780000abcd000000000000dcba"); #[test] fn bedrock_l1_block_info_invalid_len() { @@ -757,6 +801,8 @@ mod test { base_fee_scalar: 1368, eip_1559_denominator: 0x1234, eip_1559_elasticity: 0x5678, + operator_fee_scalar: 0xabcd, + operator_fee_constant: 0xdcba, }; let L1BlockInfoTx::Holocene(decoded) =