diff --git a/core/lib/types/src/fee_model.rs b/core/lib/types/src/fee_model.rs index 38d785113e5f..6f5985d46108 100644 --- a/core/lib/types/src/fee_model.rs +++ b/core/lib/types/src/fee_model.rs @@ -202,7 +202,6 @@ pub struct FeeModelConfigV2 { /// The maximum amount of pubdata that can be used by the batch. Note that if the calldata is used as pubdata, this variable should not exceed 128kb. pub max_pubdata_per_batch: u64, } - impl Default for FeeModelConfig { /// Config with all zeroes is not a valid config (since for instance having 0 max gas per batch may incur division by zero), /// so we implement a sensible default config here. diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 00d804de6c81..66a1c07a1c64 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -34,13 +34,13 @@ pub trait BatchFeeModelInputProvider: fmt::Debug + 'static + Send + Sync { params, l1_gas_price_scale_factor, )), - FeeParams::V2(params) => { - BatchFeeInput::PubdataIndependent(compute_batch_fee_model_input_v2( + FeeParams::V2(params) => BatchFeeInput::PubdataIndependent( + clip_batch_fee_model_input_v2(compute_batch_fee_model_input_v2( params, l1_gas_price_scale_factor, l1_pubdata_price_scale_factor, - )) - } + )), + ), }) } @@ -191,7 +191,7 @@ fn compute_batch_fee_model_input_v2( let l1_batch_overhead_wei = U256::from(l1_gas_price) * U256::from(batch_overhead_l1_gas); let fair_l2_gas_price = { - // Firstly, we calculate which part of the overall overhead overhead each unit of L2 gas should cover. + // Firstly, we calculate which part of the overall overhead each unit of L2 gas should cover. let l1_batch_overhead_per_gas = ceil_div_u256(l1_batch_overhead_wei, U256::from(max_gas_per_batch)); @@ -206,7 +206,7 @@ fn compute_batch_fee_model_input_v2( }; let fair_pubdata_price = { - // Firstly, we calculate which part of the overall overhead overhead each pubdata byte should cover. + // Firstly, we calculate which part of the overall overhead each pubdata byte should cover. let l1_batch_overhead_per_pubdata = ceil_div_u256(l1_batch_overhead_wei, U256::from(max_pubdata_per_batch)); @@ -227,6 +227,43 @@ fn compute_batch_fee_model_input_v2( } } +/// Bootloader places limitations on fair_l2_gas_price and fair_pubdata_price. +/// (MAX_ALLOWED_FAIR_L2_GAS_PRICE and MAX_ALLOWED_FAIR_PUBDATA_PRICE in bootloader code respectively) +/// Server needs to clip this prices in order to allow chain continues operation at a loss. The alternative +/// would be to stop accepting the transactions until the conditions improve. +/// TODO (PE-153): to be removed when bootloader limitation is removed +fn clip_batch_fee_model_input_v2( + fee_model: PubdataIndependentBatchFeeModelInput, +) -> PubdataIndependentBatchFeeModelInput { + /// MAX_ALLOWED_FAIR_L2_GAS_PRICE + const MAXIMUM_L2_GAS_PRICE: u64 = 10_000_000_000_000; + /// MAX_ALLOWED_FAIR_PUBDATA_PRICE + const MAXIMUM_PUBDATA_PRICE: u64 = 1_000_000_000_000_000; + PubdataIndependentBatchFeeModelInput { + l1_gas_price: fee_model.l1_gas_price, + fair_l2_gas_price: if fee_model.fair_l2_gas_price < MAXIMUM_L2_GAS_PRICE { + fee_model.fair_l2_gas_price + } else { + tracing::warn!( + "Fair l2 gas price {} exceeds maximum. Limitting to {}", + fee_model.fair_l2_gas_price, + MAXIMUM_L2_GAS_PRICE + ); + MAXIMUM_L2_GAS_PRICE + }, + fair_pubdata_price: if fee_model.fair_pubdata_price < MAXIMUM_PUBDATA_PRICE { + fee_model.fair_pubdata_price + } else { + tracing::warn!( + "Fair pubdata price {} exceeds maximum. Limitting to {}", + fee_model.fair_pubdata_price, + MAXIMUM_PUBDATA_PRICE + ); + MAXIMUM_PUBDATA_PRICE + }, + } +} + /// Mock [`BatchFeeModelInputProvider`] implementation that returns a constant value. /// Intended to be used in tests only. #[derive(Debug)] @@ -259,9 +296,10 @@ mod tests { // To test that overflow never happens, we'll use giant L1 gas price, i.e. // almost realistic very large value of 100k gwei. Since it is so large, we'll also // use it for the L1 pubdata price. - const GIANT_L1_GAS_PRICE: u64 = 100_000_000_000_000; + const GWEI: u64 = 1_000_000_000; + const GIANT_L1_GAS_PRICE: u64 = 100_000 * GWEI; - // As a small small L2 gas price we'll use the value of 1 wei. + // As a small L2 gas price we'll use the value of 1 wei. const SMALL_L1_GAS_PRICE: u64 = 1; #[test] @@ -314,7 +352,8 @@ mod tests { BaseTokenConversionRatio::default(), ); - let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); + let input = + clip_batch_fee_model_input_v2(compute_batch_fee_model_input_v2(params, 1.0, 1.0)); assert_eq!(input.l1_gas_price, SMALL_L1_GAS_PRICE); assert_eq!(input.fair_l2_gas_price, SMALL_L1_GAS_PRICE); @@ -340,7 +379,8 @@ mod tests { BaseTokenConversionRatio::default(), ); - let input = compute_batch_fee_model_input_v2(params, 1.0, 1.0); + let input = + clip_batch_fee_model_input_v2(compute_batch_fee_model_input_v2(params, 1.0, 1.0)); assert_eq!(input.l1_gas_price, GIANT_L1_GAS_PRICE); // The fair L2 gas price is identical to the minimal one. assert_eq!(input.fair_l2_gas_price, 100_000_000_000); @@ -491,6 +531,64 @@ mod tests { ); } + #[test] + fn test_compute_batch_fee_model_input_v2_gas_price_over_limit_due_to_l1_gas() { + // In this test we check the gas price limit works as expected + let config = FeeModelConfigV2 { + minimal_l2_gas_price: 100 * GWEI, + compute_overhead_part: 0.5, + pubdata_overhead_part: 0.5, + batch_overhead_l1_gas: 700_000, + max_gas_per_batch: 500_000_000, + max_pubdata_per_batch: 100_000, + }; + + let l1_gas_price = 1_000_000_000 * GWEI; + let params = FeeParamsV2::new( + config, + l1_gas_price, + GIANT_L1_GAS_PRICE, + BaseTokenConversionRatio::default(), + ); + + let input = + clip_batch_fee_model_input_v2(compute_batch_fee_model_input_v2(params, 1.0, 1.0)); + assert_eq!(input.l1_gas_price, l1_gas_price); + // The fair L2 gas price is identical to the maximum + assert_eq!(input.fair_l2_gas_price, 10_000 * GWEI); + assert_eq!(input.fair_pubdata_price, 1_000_000 * GWEI); + } + + #[test] + fn test_compute_batch_fee_model_input_v2_gas_price_over_limit_due_to_conversion_rate() { + // In this test we check the gas price limit works as expected + let config = FeeModelConfigV2 { + minimal_l2_gas_price: GWEI, + compute_overhead_part: 0.5, + pubdata_overhead_part: 0.5, + batch_overhead_l1_gas: 700_000, + max_gas_per_batch: 500_000_000, + max_pubdata_per_batch: 100_000, + }; + + let params = FeeParamsV2::new( + config, + GWEI, + 2 * GWEI, + BaseTokenConversionRatio { + numerator: NonZeroU64::new(3_000_000).unwrap(), + denominator: NonZeroU64::new(1).unwrap(), + }, + ); + + let input = + clip_batch_fee_model_input_v2(compute_batch_fee_model_input_v2(params, 1.0, 1.0)); + assert_eq!(input.l1_gas_price, 3_000_000 * GWEI); + // The fair L2 gas price is identical to the maximum + assert_eq!(input.fair_l2_gas_price, 10_000 * GWEI); + assert_eq!(input.fair_pubdata_price, 1_000_000 * GWEI); + } + #[tokio::test] async fn test_get_fee_model_params() { struct TestCase { @@ -544,7 +642,7 @@ mod tests { expected_l1_pubdata_price: 3000, }, TestCase { - name: "Large conversion - 1 ETH = 1_000 BaseToken", + name: "Large conversion - 1 ETH = 1_000_000 BaseToken", conversion_ratio: BaseTokenConversionRatio { numerator: NonZeroU64::new(1_000_000).unwrap(), denominator: NonZeroU64::new(1).unwrap(), diff --git a/docs/guides/advanced/07_fee_model.md b/docs/guides/advanced/07_fee_model.md index 3e6473d3ab96..4592c5007ea2 100644 --- a/docs/guides/advanced/07_fee_model.md +++ b/docs/guides/advanced/07_fee_model.md @@ -171,7 +171,7 @@ proportional to the part of the batch you are using. | gas_limit | `<= max_allowed_l2_tx_gas_limit` | The limit (4G gas) is set in the `StateKeeper` config; it's the limit for the entire L1 batch. | | gas_limit | `<= MAX_GAS_PER_TRANSACTION` | This limit (80M) is set in bootloader. | | gas_limit | `> l2_tx_intrinsic_gas` | This limit (around 14k gas) is hardcoded to ensure that the transaction has enough gas to start. | -| max_fee_per_gas | `<= fair_l2_gas_price` | Fair L2 gas price (0.25 Gwei) is set in the `StateKeeper` config | +| max_fee_per_gas | `>= fair_l2_gas_price` | Fair L2 gas price (0.1 Gwei on Era) is set in the `StateKeeper` config | | | `<=validation_computational_gas_limit` | There is an additional, stricter limit (300k gas) on the amount of gas that a transaction can use during validation. | ### Why do we have two limits: 80M and 4G