From 99d31992dbb250019e0e08a533becb93c3e2f690 Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Tue, 10 Jun 2025 21:48:44 +0100 Subject: [PATCH 1/9] feat: rollup-fee updated for spec=feynman --- src/l1block.rs | 86 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/src/l1block.rs b/src/l1block.rs index c52c422..0bbacba 100644 --- a/src/l1block.rs +++ b/src/l1block.rs @@ -42,6 +42,12 @@ const L1_COMMIT_SCALAR_SLOT: U256 = U256::from_limbs([6u64, 0, 0, 0]); /// The L1 blob scalar storage slot. const L1_BLOB_SCALAR_SLOT: U256 = U256::from_limbs([7u64, 0, 0, 0]); +/// The L1 compression scalar storage slot. +const L1_COMPRESSION_SCALAR_SLOT: U256 = U256::from_limbs([8u64, 0, 0, 0]); + +/// The L1 proof verification scalar storage slot. +const L1_VERIFICATION_SCALAR_SLOT: U256 = U256::from_limbs([9u64, 0, 0, 0]); + // L1 BLOCK INFO // ================================================================================================ @@ -65,6 +71,10 @@ pub struct L1BlockInfo { pub l1_blob_scalar: Option, /// The current call data gas (l1_blob_scalar * l1_base_fee), None if before Curie. pub calldata_gas: Option, + /// The current L1 DA compression scalar, None if before Feynman. + pub l1_compression_scalar: Option, + /// The current L1 proof verification scalar, None if before Feynman. + pub l1_verification_scalar: Option, } impl L1BlockInfo { @@ -92,6 +102,25 @@ impl L1BlockInfo { let l1_blob_scalar = db.storage(L1_GAS_PRICE_ORACLE_ADDRESS, L1_BLOB_SCALAR_SLOT)?; let calldata_gas = l1_commit_scalar.saturating_mul(l1_base_fee); + // If Feynman is not enabled, return the L1 block info without Feynman fields. + if !spec_id.is_enabled_in(ScrollSpecId::FEYNMAN) { + return Ok(L1BlockInfo { + l1_base_fee, + l1_fee_overhead, + l1_base_fee_scalar, + l1_blob_base_fee: Some(l1_blob_base_fee), + l1_commit_scalar: Some(l1_commit_scalar), + l1_blob_scalar: Some(l1_blob_scalar), + calldata_gas: Some(calldata_gas), + ..Default::default() + }); + } + + let l1_compression_scalar = + db.storage(L1_GAS_PRICE_ORACLE_ADDRESS, L1_COMPRESSION_SCALAR_SLOT)?; + let l1_verification_scalar = + db.storage(L1_GAS_PRICE_ORACLE_ADDRESS, L1_VERIFICATION_SCALAR_SLOT)?; + Ok(L1BlockInfo { l1_base_fee, l1_fee_overhead, @@ -100,6 +129,8 @@ impl L1BlockInfo { l1_commit_scalar: Some(l1_commit_scalar), l1_blob_scalar: Some(l1_blob_scalar), calldata_gas: Some(calldata_gas), + l1_compression_scalar: Some(l1_compression_scalar), + l1_verification_scalar: Some(l1_verification_scalar), }) } @@ -136,12 +167,65 @@ impl L1BlockInfo { self.calldata_gas.unwrap().saturating_add(blob_gas).wrapping_div(TX_L1_FEE_PRECISION) } + fn calculate_tx_l1_cost_feynman(&self, input: &[u8], spec_id: ScrollSpecId) -> U256 { + // rollup_fee(tx) = compression_ratio(tx) * size(tx) * (component_exec + component_blob) + // + // - compression_ratio(tx): estimated compression ratio of RLP-encoded signed tx data, + // derived by plugging in this tx into the previous finalised batch. This gives us an idea + // as to how compressible is the zstd-encoding of the data in this tx. + // + // - size(tx): denotes the size of the RLP-encoded signed tx data. + // + // - component_exec: The component that accounts towards commiting this tx as part of a L2 + // batch as well as gas costs for the eventual on-chain proof verification. + // => (compression_scalar + commit_scalar + verification_scalar) * l1_base_fee + // + // - component_blob: The component that accounts the costs associated with data + // availability, i.e. the costs of posting this tx's data in the EIP-4844 blob. + // => (compression_scalar + blob_scalar) * l1_blob_base_fee + let component_exec = { + let compression_scalar = self + .l1_compression_scalar + .unwrap_or_else(|| panic!("compression scalar in spec_id={:?}", spec_id)); + let commit_scalar = self + .l1_commit_scalar + .unwrap_or_else(|| panic!("l1 commit scalar in spec_id={:?}", spec_id)); + let verification_scalar = self + .l1_verification_scalar + .unwrap_or_else(|| panic!("verification scalar in spec_id={:?}", spec_id)); + compression_scalar.saturating_add(commit_scalar).saturating_add(verification_scalar) + }; + let component_blob = { + let compression_scalar = self + .l1_compression_scalar + .unwrap_or_else(|| panic!("compression scalar in spec_id={:?}", spec_id)); + let blob_scalar = self + .l1_blob_scalar + .unwrap_or_else(|| panic!("l1 blob scalar in spec_id={:?}", spec_id)); + compression_scalar.saturating_add(blob_scalar) + }; + + // Assume compression_ratio = 1 until we have specification for estimating compression + // ratio based on previous finalised batches. + let compression_ratio = |_input: &[u8]| -> U256 { U256::ONE }; + + // size(tx) is just the length of the RLP-encoded signed tx data. + let tx_size = |input: &[u8]| -> U256 { U256::from(input.len()) }; + + compression_ratio(input) + .saturating_mul(tx_size(input)) + .saturating_mul(component_exec.saturating_add(component_blob)) + .wrapping_div(TX_L1_FEE_PRECISION) + } + /// Calculate the gas cost of a transaction based on L1 block data posted on L2. pub fn calculate_tx_l1_cost(&self, input: &[u8], spec_id: ScrollSpecId) -> U256 { if !spec_id.is_enabled_in(ScrollSpecId::CURIE) { self.calculate_tx_l1_cost_shanghai(input, spec_id) - } else { + } else if !spec_id.is_enabled_in(ScrollSpecId::FEYNMAN) { self.calculate_tx_l1_cost_curie(input, spec_id) + } else { + self.calculate_tx_l1_cost_feynman(input, spec_id) } } } From 4b74b246e694dd335732ca8483ce0dbcb644b80b Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Wed, 11 Jun 2025 09:54:39 +0100 Subject: [PATCH 2/9] fix: re-use old slots for exec and blob scalar --- src/l1block.rs | 59 +++++++++++++------------------------------------- 1 file changed, 15 insertions(+), 44 deletions(-) diff --git a/src/l1block.rs b/src/l1block.rs index 0bbacba..a032ad4 100644 --- a/src/l1block.rs +++ b/src/l1block.rs @@ -37,17 +37,13 @@ const L1_SCALAR_SLOT: U256 = U256::from_limbs([3u64, 0, 0, 0]); const L1_BLOB_BASE_FEE_SLOT: U256 = U256::from_limbs([5u64, 0, 0, 0]); /// The L1 commit scalar storage slot. +/// +/// Post-FEYNMAN this represents the exec_scalar. const L1_COMMIT_SCALAR_SLOT: U256 = U256::from_limbs([6u64, 0, 0, 0]); /// The L1 blob scalar storage slot. const L1_BLOB_SCALAR_SLOT: U256 = U256::from_limbs([7u64, 0, 0, 0]); -/// The L1 compression scalar storage slot. -const L1_COMPRESSION_SCALAR_SLOT: U256 = U256::from_limbs([8u64, 0, 0, 0]); - -/// The L1 proof verification scalar storage slot. -const L1_VERIFICATION_SCALAR_SLOT: U256 = U256::from_limbs([9u64, 0, 0, 0]); - // L1 BLOCK INFO // ================================================================================================ @@ -71,10 +67,6 @@ pub struct L1BlockInfo { pub l1_blob_scalar: Option, /// The current call data gas (l1_blob_scalar * l1_base_fee), None if before Curie. pub calldata_gas: Option, - /// The current L1 DA compression scalar, None if before Feynman. - pub l1_compression_scalar: Option, - /// The current L1 proof verification scalar, None if before Feynman. - pub l1_verification_scalar: Option, } impl L1BlockInfo { @@ -102,25 +94,6 @@ impl L1BlockInfo { let l1_blob_scalar = db.storage(L1_GAS_PRICE_ORACLE_ADDRESS, L1_BLOB_SCALAR_SLOT)?; let calldata_gas = l1_commit_scalar.saturating_mul(l1_base_fee); - // If Feynman is not enabled, return the L1 block info without Feynman fields. - if !spec_id.is_enabled_in(ScrollSpecId::FEYNMAN) { - return Ok(L1BlockInfo { - l1_base_fee, - l1_fee_overhead, - l1_base_fee_scalar, - l1_blob_base_fee: Some(l1_blob_base_fee), - l1_commit_scalar: Some(l1_commit_scalar), - l1_blob_scalar: Some(l1_blob_scalar), - calldata_gas: Some(calldata_gas), - ..Default::default() - }); - } - - let l1_compression_scalar = - db.storage(L1_GAS_PRICE_ORACLE_ADDRESS, L1_COMPRESSION_SCALAR_SLOT)?; - let l1_verification_scalar = - db.storage(L1_GAS_PRICE_ORACLE_ADDRESS, L1_VERIFICATION_SCALAR_SLOT)?; - Ok(L1BlockInfo { l1_base_fee, l1_fee_overhead, @@ -129,8 +102,6 @@ impl L1BlockInfo { l1_commit_scalar: Some(l1_commit_scalar), l1_blob_scalar: Some(l1_blob_scalar), calldata_gas: Some(calldata_gas), - l1_compression_scalar: Some(l1_compression_scalar), - l1_verification_scalar: Some(l1_verification_scalar), }) } @@ -179,30 +150,29 @@ impl L1BlockInfo { // - component_exec: The component that accounts towards commiting this tx as part of a L2 // batch as well as gas costs for the eventual on-chain proof verification. // => (compression_scalar + commit_scalar + verification_scalar) * l1_base_fee + // => (new_commit_scalar) * l1_base_fee // // - component_blob: The component that accounts the costs associated with data // availability, i.e. the costs of posting this tx's data in the EIP-4844 blob. // => (compression_scalar + blob_scalar) * l1_blob_base_fee + // => (new_blob_scalar) * l1_blob_base_fee + // + // Note that the same slots for L1_COMMIT_SCALAR_SLOT and L1_BLOB_SCALAR_SLOT are + // re-used/updated for the new values post-FEYNMAN. let component_exec = { - let compression_scalar = self - .l1_compression_scalar - .unwrap_or_else(|| panic!("compression scalar in spec_id={:?}", spec_id)); - let commit_scalar = self + let exec_scalar = self .l1_commit_scalar - .unwrap_or_else(|| panic!("l1 commit scalar in spec_id={:?}", spec_id)); - let verification_scalar = self - .l1_verification_scalar - .unwrap_or_else(|| panic!("verification scalar in spec_id={:?}", spec_id)); - compression_scalar.saturating_add(commit_scalar).saturating_add(verification_scalar) + .unwrap_or_else(|| panic!("exec scalar in spec_id={:?}", spec_id)); + exec_scalar.saturating_mul(self.l1_base_fee) }; let component_blob = { - let compression_scalar = self - .l1_compression_scalar - .unwrap_or_else(|| panic!("compression scalar in spec_id={:?}", spec_id)); let blob_scalar = self .l1_blob_scalar .unwrap_or_else(|| panic!("l1 blob scalar in spec_id={:?}", spec_id)); - compression_scalar.saturating_add(blob_scalar) + let blob_base_fee = self + .l1_blob_base_fee + .unwrap_or_else(|| panic!("l1 blob base fee in spec_id={:?}", spec_id)); + blob_scalar.saturating_mul(blob_base_fee) }; // Assume compression_ratio = 1 until we have specification for estimating compression @@ -216,6 +186,7 @@ impl L1BlockInfo { .saturating_mul(tx_size(input)) .saturating_mul(component_exec.saturating_add(component_blob)) .wrapping_div(TX_L1_FEE_PRECISION) + .wrapping_div(TX_L1_FEE_PRECISION) } /// Calculate the gas cost of a transaction based on L1 block data posted on L2. From 01b55cce18fed43bf93a3a883515c1ca8a10b350 Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Wed, 11 Jun 2025 15:15:30 +0100 Subject: [PATCH 3/9] chore: resolve comments --- src/l1block.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/l1block.rs b/src/l1block.rs index a032ad4..ef9aaa0 100644 --- a/src/l1block.rs +++ b/src/l1block.rs @@ -141,16 +141,16 @@ impl L1BlockInfo { fn calculate_tx_l1_cost_feynman(&self, input: &[u8], spec_id: ScrollSpecId) -> U256 { // rollup_fee(tx) = compression_ratio(tx) * size(tx) * (component_exec + component_blob) // - // - compression_ratio(tx): estimated compression ratio of RLP-encoded signed tx data, - // derived by plugging in this tx into the previous finalised batch. This gives us an idea - // as to how compressible is the zstd-encoding of the data in this tx. + // - compression_ratio(tx): estimated compressibility of the signed tx data. The tx is + // eventually a part of a L2 batch that should likely result in a better compression ratio, + // however a conservative estimate is the size of zstd-encoding of the signed tx. // - // - size(tx): denotes the size of the RLP-encoded signed tx data. + // - size(tx): denotes the size of the signed tx. // // - component_exec: The component that accounts towards commiting this tx as part of a L2 // batch as well as gas costs for the eventual on-chain proof verification. // => (compression_scalar + commit_scalar + verification_scalar) * l1_base_fee - // => (new_commit_scalar) * l1_base_fee + // => (exec_scalar) * l1_base_fee // // - component_blob: The component that accounts the costs associated with data // availability, i.e. the costs of posting this tx's data in the EIP-4844 blob. @@ -162,22 +162,25 @@ impl L1BlockInfo { let component_exec = { let exec_scalar = self .l1_commit_scalar - .unwrap_or_else(|| panic!("exec scalar in spec_id={:?}", spec_id)); + .unwrap_or_else(|| panic!("missing exec scalar in spec_id={:?}", spec_id)); exec_scalar.saturating_mul(self.l1_base_fee) }; let component_blob = { let blob_scalar = self .l1_blob_scalar - .unwrap_or_else(|| panic!("l1 blob scalar in spec_id={:?}", spec_id)); + .unwrap_or_else(|| panic!("missing l1 blob scalar in spec_id={:?}", spec_id)); let blob_base_fee = self .l1_blob_base_fee - .unwrap_or_else(|| panic!("l1 blob base fee in spec_id={:?}", spec_id)); + .unwrap_or_else(|| panic!("missing l1 blob base fee in spec_id={:?}", spec_id)); blob_scalar.saturating_mul(blob_base_fee) }; // Assume compression_ratio = 1 until we have specification for estimating compression // ratio based on previous finalised batches. - let compression_ratio = |_input: &[u8]| -> U256 { U256::ONE }; + // + // We use the `TX_L1_FEE_PRECISION` to allow fractions. We then divide the overall product + // by the precision value as well. + let compression_ratio = |_input: &[u8]| -> U256 { TX_L1_FEE_PRECISION }; // size(tx) is just the length of the RLP-encoded signed tx data. let tx_size = |input: &[u8]| -> U256 { U256::from(input.len()) }; From daca1e671aa2dcde14fcb814cc104ad74c9214ea Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Fri, 13 Jun 2025 13:02:53 +0100 Subject: [PATCH 4/9] chore(doc): fix naming related to compression ratio v factor --- src/l1block.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/l1block.rs b/src/l1block.rs index de36c59..23b8769 100644 --- a/src/l1block.rs +++ b/src/l1block.rs @@ -141,9 +141,10 @@ impl L1BlockInfo { } fn calculate_tx_l1_cost_feynman(&self, input: &[u8], spec_id: ScrollSpecId) -> U256 { - // rollup_fee(tx) = compression_ratio(tx) * size(tx) * (component_exec + component_blob) + // rollup_fee(tx) = compression_factor(tx) * size(tx) * (component_exec + component_blob) // - // - compression_ratio(tx): estimated compressibility of the signed tx data. The tx is + // - compression_factor(tx): compression_factor = 1 / compression_ratio, where + // compression_ratio is the estimated compressibility of the signed tx data. The tx is // eventually a part of a L2 batch that should likely result in a better compression ratio, // however a conservative estimate is the size of zstd-encoding of the signed tx. // @@ -177,17 +178,17 @@ impl L1BlockInfo { blob_scalar.saturating_mul(blob_base_fee) }; - // Assume compression_ratio = 1 until we have specification for estimating compression + // Assume compression_factor = 1 until we have specification for estimating compression // ratio based on previous finalised batches. // // We use the `TX_L1_FEE_PRECISION` to allow fractions. We then divide the overall product // by the precision value as well. - let compression_ratio = |_input: &[u8]| -> U256 { TX_L1_FEE_PRECISION }; + let compression_factor = |_input: &[u8]| -> U256 { TX_L1_FEE_PRECISION }; // size(tx) is just the length of the RLP-encoded signed tx data. let tx_size = |input: &[u8]| -> U256 { U256::from(input.len()) }; - compression_ratio(input) + compression_factor(input) .saturating_mul(tx_size(input)) .saturating_mul(component_exec.saturating_add(component_blob)) .wrapping_div(TX_L1_FEE_PRECISION) From c11f2b4d3cb9444b69b51f017ee14cc0cd73e443 Mon Sep 17 00:00:00 2001 From: frisitano Date: Wed, 18 Jun 2025 14:15:06 +0100 Subject: [PATCH 5/9] feat: feynman compression --- src/handler.rs | 10 ++++++++-- src/l1block.rs | 23 ++++++++++++++++++----- src/transaction.rs | 29 +++++++++++++++++++++++++---- 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/handler.rs b/src/handler.rs index f7dbe55..139a297 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -113,8 +113,10 @@ where "[SCROLL] Failed to load transaction rlp_bytes.".to_string(), )); }; + // Deduct l1 fee from caller. - let tx_l1_cost = l1_block_info.calculate_tx_l1_cost(rlp_bytes, spec); + let tx_l1_cost = + l1_block_info.calculate_tx_l1_cost(rlp_bytes, spec, ctx.tx().compression_factor()); let caller_account = ctx.journal().load_account(caller)?; if tx_l1_cost.gt(&caller_account.info.balance) { return Err(InvalidTransaction::LackOfFundForMaxFee { @@ -225,7 +227,11 @@ where "[SCROLL] Failed to load transaction rlp_bytes.".to_string(), )); }; - let l1_cost = l1_block_info.calculate_tx_l1_cost(rlp_bytes, ctx.cfg().spec()); + let l1_cost = l1_block_info.calculate_tx_l1_cost( + rlp_bytes, + ctx.cfg().spec(), + ctx.tx().compression_factor(), + ); // reward the beneficiary with the gas fee including the L1 cost of the transaction and mark // the account as touched. diff --git a/src/l1block.rs b/src/l1block.rs index 23b8769..ee682da 100644 --- a/src/l1block.rs +++ b/src/l1block.rs @@ -140,7 +140,12 @@ impl L1BlockInfo { self.calldata_gas.unwrap().saturating_add(blob_gas).wrapping_div(TX_L1_FEE_PRECISION) } - fn calculate_tx_l1_cost_feynman(&self, input: &[u8], spec_id: ScrollSpecId) -> U256 { + fn calculate_tx_l1_cost_feynman( + &self, + input: &[u8], + spec_id: ScrollSpecId, + compression_factor: U256, + ) -> U256 { // rollup_fee(tx) = compression_factor(tx) * size(tx) * (component_exec + component_blob) // // - compression_factor(tx): compression_factor = 1 / compression_ratio, where @@ -183,12 +188,12 @@ impl L1BlockInfo { // // We use the `TX_L1_FEE_PRECISION` to allow fractions. We then divide the overall product // by the precision value as well. - let compression_factor = |_input: &[u8]| -> U256 { TX_L1_FEE_PRECISION }; + // let compression_factor = |_input: &[u8]| -> U256 { TX_L1_FEE_PRECISION }; // size(tx) is just the length of the RLP-encoded signed tx data. let tx_size = |input: &[u8]| -> U256 { U256::from(input.len()) }; - compression_factor(input) + compression_factor .saturating_mul(tx_size(input)) .saturating_mul(component_exec.saturating_add(component_blob)) .wrapping_div(TX_L1_FEE_PRECISION) @@ -196,13 +201,21 @@ impl L1BlockInfo { } /// Calculate the gas cost of a transaction based on L1 block data posted on L2. - pub fn calculate_tx_l1_cost(&self, input: &[u8], spec_id: ScrollSpecId) -> U256 { + pub fn calculate_tx_l1_cost( + &self, + input: &[u8], + spec_id: ScrollSpecId, + compression_factor: Option, + ) -> U256 { let l1_cost = if !spec_id.is_enabled_in(ScrollSpecId::CURIE) { self.calculate_tx_l1_cost_shanghai(input, spec_id) } else if !spec_id.is_enabled_in(ScrollSpecId::FEYNMAN) { self.calculate_tx_l1_cost_curie(input, spec_id) } else { - self.calculate_tx_l1_cost_feynman(input, spec_id) + let compression_factor = compression_factor.unwrap_or_else(|| { + panic!("compression factor should be set in spec_id={:?}", spec_id) + }); + self.calculate_tx_l1_cost_feynman(input, spec_id, compression_factor) }; l1_cost.min(U64_MAX) } diff --git a/src/transaction.rs b/src/transaction.rs index b112fa6..9f4eccd 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -12,26 +12,43 @@ pub trait ScrollTxTr: Transaction { /// The RLP encoded transaction bytes which are used to calculate the cost associated with /// posting the transaction on L1. fn rlp_bytes(&self) -> Option<&Bytes>; + + /// The compression factor of the transaction which is used to calculate the cost associated + /// with posting the transaction on L1. + fn compression_factor(&self) -> Option; } /// A Scroll transaction. Wraps around a base transaction and provides the optional RLPed bytes for /// the l1 fee computation. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ScrollTransaction { pub base: T, pub rlp_bytes: Option, + pub compression_factor: Option, +} + +// We implement PartialEq and Eq for ScrollTransaction to allow comparing transactions +// based on their base transaction and RLP bytes. It should be noted that this comparison does not +// take into account the `compression_factor`. This is because `f64` does not implement `PartialEq` +// or `Eq`, so we cannot use it directly in the trait bounds. +impl PartialEq for ScrollTransaction { + fn eq(&self, other: &Self) -> bool { + self.base == other.base && self.rlp_bytes == other.rlp_bytes + } } +impl Eq for ScrollTransaction {} + impl ScrollTransaction { - pub fn new(base: T, rlp_bytes: Option) -> Self { - Self { base, rlp_bytes } + pub fn new(base: T, rlp_bytes: Option, compression_factor: Option) -> Self { + Self { base, rlp_bytes, compression_factor } } } impl Default for ScrollTransaction { fn default() -> Self { - Self { base: TxEnv::default(), rlp_bytes: None } + Self { base: TxEnv::default(), rlp_bytes: None, compression_factor: None } } } @@ -114,4 +131,8 @@ impl ScrollTxTr for ScrollTransaction { fn rlp_bytes(&self) -> Option<&Bytes> { self.rlp_bytes.as_ref() } + + fn compression_factor(&self) -> Option { + self.compression_factor + } } From b3df03f6013fa6f241e3b35d0503a9250ee7ec51 Mon Sep 17 00:00:00 2001 From: frisitano Date: Wed, 18 Jun 2025 14:17:01 +0100 Subject: [PATCH 6/9] add compression factor assertions --- src/l1block.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/l1block.rs b/src/l1block.rs index ee682da..b12f915 100644 --- a/src/l1block.rs +++ b/src/l1block.rs @@ -167,6 +167,11 @@ impl L1BlockInfo { // // Note that the same slots for L1_COMMIT_SCALAR_SLOT and L1_BLOB_SCALAR_SLOT are // re-used/updated for the new values post-FEYNMAN. + assert!( + compression_factor < U256::from(1), + "transaction compression factor must be less than 1" + ); + let component_exec = { let exec_scalar = self .l1_commit_scalar From 0d8e72e674e732f0e8971b7a96eafb884a4476a0 Mon Sep 17 00:00:00 2001 From: frisitano Date: Wed, 18 Jun 2025 14:18:23 +0100 Subject: [PATCH 7/9] remove manual impl of PartialEq and Eq on ScrollTransaction --- src/transaction.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/transaction.rs b/src/transaction.rs index 9f4eccd..c46aed4 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -20,7 +20,7 @@ pub trait ScrollTxTr: Transaction { /// A Scroll transaction. Wraps around a base transaction and provides the optional RLPed bytes for /// the l1 fee computation. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ScrollTransaction { pub base: T, @@ -28,18 +28,6 @@ pub struct ScrollTransaction { pub compression_factor: Option, } -// We implement PartialEq and Eq for ScrollTransaction to allow comparing transactions -// based on their base transaction and RLP bytes. It should be noted that this comparison does not -// take into account the `compression_factor`. This is because `f64` does not implement `PartialEq` -// or `Eq`, so we cannot use it directly in the trait bounds. -impl PartialEq for ScrollTransaction { - fn eq(&self, other: &Self) -> bool { - self.base == other.base && self.rlp_bytes == other.rlp_bytes - } -} - -impl Eq for ScrollTransaction {} - impl ScrollTransaction { pub fn new(base: T, rlp_bytes: Option, compression_factor: Option) -> Self { Self { base, rlp_bytes, compression_factor } From 76faa00fdeebf2b99ba8bee82a89dbe778585edc Mon Sep 17 00:00:00 2001 From: frisitano Date: Tue, 24 Jun 2025 16:37:00 +0100 Subject: [PATCH 8/9] update compression ratio --- src/l1block.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/l1block.rs b/src/l1block.rs index b12f915..941403c 100644 --- a/src/l1block.rs +++ b/src/l1block.rs @@ -18,7 +18,8 @@ const NON_ZERO_BYTE_COST: u64 = 16; const TX_L1_COMMIT_EXTRA_COST: U256 = U256::from_limbs([64u64, 0, 0, 0]); /// The precision used for L1 fee calculations. -const TX_L1_FEE_PRECISION: U256 = U256::from_limbs([1_000_000_000u64, 0, 0, 0]); +pub const TX_L1_FEE_PRECISION: u64 = 1_000_000_000u64; +pub const TX_L1_FEE_PRECISION_U256: U256 = U256::from_limbs([TX_L1_FEE_PRECISION, 0, 0, 0]); /// The L1 gas price oracle address. pub const L1_GAS_PRICE_ORACLE_ADDRESS: Address = @@ -130,21 +131,21 @@ impl L1BlockInfo { tx_l1_gas .saturating_mul(self.l1_base_fee) .saturating_mul(self.l1_base_fee_scalar) - .wrapping_div(TX_L1_FEE_PRECISION) + .wrapping_div(TX_L1_FEE_PRECISION_U256) } fn calculate_tx_l1_cost_curie(&self, input: &[u8], spec_id: ScrollSpecId) -> U256 { // "commitScalar * l1BaseFee + blobScalar * _data.length * l1BlobBaseFee" let blob_gas = self.data_gas(input, spec_id); - self.calldata_gas.unwrap().saturating_add(blob_gas).wrapping_div(TX_L1_FEE_PRECISION) + self.calldata_gas.unwrap().saturating_add(blob_gas).wrapping_div(TX_L1_FEE_PRECISION_U256) } fn calculate_tx_l1_cost_feynman( &self, input: &[u8], spec_id: ScrollSpecId, - compression_factor: U256, + compression_ratio: U256, ) -> U256 { // rollup_fee(tx) = compression_factor(tx) * size(tx) * (component_exec + component_blob) // @@ -168,8 +169,8 @@ impl L1BlockInfo { // Note that the same slots for L1_COMMIT_SCALAR_SLOT and L1_BLOB_SCALAR_SLOT are // re-used/updated for the new values post-FEYNMAN. assert!( - compression_factor < U256::from(1), - "transaction compression factor must be less than 1" + compression_ratio >= TX_L1_FEE_PRECISION_U256, + "transaction compression ratio must be greater or equal to {TX_L1_FEE_PRECISION_U256:?} - compression ratio: {compression_ratio:?}" ); let component_exec = { @@ -198,11 +199,11 @@ impl L1BlockInfo { // size(tx) is just the length of the RLP-encoded signed tx data. let tx_size = |input: &[u8]| -> U256 { U256::from(input.len()) }; - compression_factor - .saturating_mul(tx_size(input)) + tx_size(input) .saturating_mul(component_exec.saturating_add(component_blob)) - .wrapping_div(TX_L1_FEE_PRECISION) - .wrapping_div(TX_L1_FEE_PRECISION) + .saturating_mul(TX_L1_FEE_PRECISION_U256) + .wrapping_div(compression_ratio) + .wrapping_div(TX_L1_FEE_PRECISION_U256) } /// Calculate the gas cost of a transaction based on L1 block data posted on L2. From c47878bc4847f50d621aed3437362535e6195b35 Mon Sep 17 00:00:00 2001 From: frisitano Date: Wed, 25 Jun 2025 10:50:48 +0100 Subject: [PATCH 9/9] update compression factor to compression ratio --- src/handler.rs | 4 ++-- src/l1block.rs | 8 ++++---- src/transaction.rs | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/handler.rs b/src/handler.rs index 139a297..5cc8a67 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -116,7 +116,7 @@ where // Deduct l1 fee from caller. let tx_l1_cost = - l1_block_info.calculate_tx_l1_cost(rlp_bytes, spec, ctx.tx().compression_factor()); + l1_block_info.calculate_tx_l1_cost(rlp_bytes, spec, ctx.tx().compression_ratio()); let caller_account = ctx.journal().load_account(caller)?; if tx_l1_cost.gt(&caller_account.info.balance) { return Err(InvalidTransaction::LackOfFundForMaxFee { @@ -230,7 +230,7 @@ where let l1_cost = l1_block_info.calculate_tx_l1_cost( rlp_bytes, ctx.cfg().spec(), - ctx.tx().compression_factor(), + ctx.tx().compression_ratio(), ); // reward the beneficiary with the gas fee including the L1 cost of the transaction and mark diff --git a/src/l1block.rs b/src/l1block.rs index 941403c..a0ce313 100644 --- a/src/l1block.rs +++ b/src/l1block.rs @@ -211,17 +211,17 @@ impl L1BlockInfo { &self, input: &[u8], spec_id: ScrollSpecId, - compression_factor: Option, + compression_ratio: Option, ) -> U256 { let l1_cost = if !spec_id.is_enabled_in(ScrollSpecId::CURIE) { self.calculate_tx_l1_cost_shanghai(input, spec_id) } else if !spec_id.is_enabled_in(ScrollSpecId::FEYNMAN) { self.calculate_tx_l1_cost_curie(input, spec_id) } else { - let compression_factor = compression_factor.unwrap_or_else(|| { - panic!("compression factor should be set in spec_id={:?}", spec_id) + let compression_ratio = compression_ratio.unwrap_or_else(|| { + panic!("compression ratio should be set in spec_id={:?}", spec_id) }); - self.calculate_tx_l1_cost_feynman(input, spec_id, compression_factor) + self.calculate_tx_l1_cost_feynman(input, spec_id, compression_ratio) }; l1_cost.min(U64_MAX) } diff --git a/src/transaction.rs b/src/transaction.rs index c46aed4..b3121af 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -13,9 +13,9 @@ pub trait ScrollTxTr: Transaction { /// posting the transaction on L1. fn rlp_bytes(&self) -> Option<&Bytes>; - /// The compression factor of the transaction which is used to calculate the cost associated + /// The compression ratio of the transaction which is used to calculate the cost associated /// with posting the transaction on L1. - fn compression_factor(&self) -> Option; + fn compression_ratio(&self) -> Option; } /// A Scroll transaction. Wraps around a base transaction and provides the optional RLPed bytes for @@ -25,18 +25,18 @@ pub trait ScrollTxTr: Transaction { pub struct ScrollTransaction { pub base: T, pub rlp_bytes: Option, - pub compression_factor: Option, + pub compression_ratio: Option, } impl ScrollTransaction { - pub fn new(base: T, rlp_bytes: Option, compression_factor: Option) -> Self { - Self { base, rlp_bytes, compression_factor } + pub fn new(base: T, rlp_bytes: Option, compression_ratio: Option) -> Self { + Self { base, rlp_bytes, compression_ratio } } } impl Default for ScrollTransaction { fn default() -> Self { - Self { base: TxEnv::default(), rlp_bytes: None, compression_factor: None } + Self { base: TxEnv::default(), rlp_bytes: None, compression_ratio: None } } } @@ -120,7 +120,7 @@ impl ScrollTxTr for ScrollTransaction { self.rlp_bytes.as_ref() } - fn compression_factor(&self) -> Option { - self.compression_factor + fn compression_ratio(&self) -> Option { + self.compression_ratio } }