Skip to content

Commit

Permalink
refactor(consensus): use versioned constants in fee market, consensus
Browse files Browse the repository at this point in the history
context
  • Loading branch information
ayeletstarkware committed Jan 15, 2025
1 parent d3ad325 commit e17763e
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,38 +1,31 @@
use std::cmp::max;

use crate::versioned_constants;

#[cfg(test)]
mod test;

// This constant is used to calculate the base gas price for the next block according to EIP-1559
// and serves as a sensitivity parameter that limits the maximum rate of change of the gas price
// between consecutive blocks.
const GAS_PRICE_MAX_CHANGE_DENOMINATOR: u128 = 48;
/// The minimum gas price in fri.
pub const MIN_GAS_PRICE: u64 = 100000;
// TODO(Mohammad): Check the exact value for maximum block size in StarkNet.
/// The maximum block size in gas units: 40M gas steps * 100 units/step.
pub const MAX_BLOCK_SIZE: u64 = 4000000000;

/// Calculate the base gas price for the next block according to EIP-1559.
///
/// # Parameters
/// - `price`: The base gas price per unit (in fri) of the current block.
/// - `gas_used`: The total gas used in the current block.
/// - `gas_target`: The target gas usage per block (usually half of a block's gas limit).
pub fn calculate_next_base_gas_price(price: u64, gas_used: u64, gas_target: u64) -> u64 {
let versioned_constants = versioned_constants::VersionedConstants::latest_constants();
// Setting the target at 50% of the max block size balances the rate of gas price changes,
// helping to prevent sudden spikes, particularly during increases, for a better user
// experience.
assert_eq!(
gas_target,
MAX_BLOCK_SIZE / 2,
versioned_constants.max_block_size / 2,
"Gas target must be 50% of max block size to balance price changes."
);
// To prevent precision loss during multiplication and division, we set a minimum gas price.
// Additionally, a minimum gas price is established to prevent prolonged periods before the
// price reaches a higher value.
assert!(
price >= MIN_GAS_PRICE,
price >= versioned_constants.min_gas_price,
"The gas price must be at least the minimum to prevent precision loss during \
multiplication and division."
);
Expand All @@ -54,7 +47,8 @@ pub fn calculate_next_base_gas_price(price: u64, gas_used: u64, gas_target: u64)
// Calculate the price change, maintaining precision by dividing after scaling up.
// This avoids significant precision loss that would occur if dividing before
// multiplication.
let price_change_u128 = gas_delta_cost / gas_target_u128 / GAS_PRICE_MAX_CHANGE_DENOMINATOR;
let price_change_u128 =
gas_delta_cost / gas_target_u128 / versioned_constants.gas_price_max_change_denominator;

// Convert back to u64, as the price change should fit within the u64 range.
// Since the target is half the maximum block size (which fits within a u64), the gas delta
Expand All @@ -71,5 +65,5 @@ pub fn calculate_next_base_gas_price(price: u64, gas_used: u64, gas_target: u64)
|| gas_used <= gas_target && adjusted_price <= price
);

max(adjusted_price, MIN_GAS_PRICE)
max(adjusted_price, versioned_constants.min_gas_price)
}
Original file line number Diff line number Diff line change
@@ -1,62 +1,65 @@
use crate::fee_market::{
calculate_next_base_gas_price,
GAS_PRICE_MAX_CHANGE_DENOMINATOR,
MAX_BLOCK_SIZE,
MIN_GAS_PRICE,
};
use crate::fee_market::calculate_next_base_gas_price;
use crate::versioned_constants::VersionedConstants;

#[test]
fn test_price_calculation_snapshot() {
// Setup: using realistic arbitrary values.
const INIT_PRICE: u64 = 1_000_000;
const GAS_TARGET: u64 = MAX_BLOCK_SIZE / 2;
const HIGH_CONGESTION_GAS_USED: u64 = MAX_BLOCK_SIZE * 3 / 4;
const LOW_CONGESTION_GAS_USED: u64 = MAX_BLOCK_SIZE / 4;
const STABLE_CONGESTION_GAS_USED: u64 = GAS_TARGET;
let init_price: u64 = 1_000_000;
let versioned_constants = VersionedConstants::latest_constants();
let max_block_size = versioned_constants.max_block_size;
let get_target: u64 = max_block_size / 2;
let high_congestion_gas_used: u64 = max_block_size * 3 / 4;
let low_congestion_gas_used: u64 = max_block_size / 4;
let stable_congestion_gas_used: u64 = get_target;

// Fixed expected output values.
let increased_price = 1000000 + 10416; // 1000000 + (1000000 * 1 / 4 * MAX_BLOCK_SIZE) / (0.5 * MAX_BLOCK_SIZE * 48);
let decreased_price = 1000000 - 10416; // 1000000 - (1000000 * 1 / 4 * MAX_BLOCK_SIZE) / (0.5 * MAX_BLOCK_SIZE * 48);
let increased_price = 1000000 + 10416; // 1000000 + (1000000 * 1 / 4 * max_block_size) / (0.5 * max_block_size * 48);
let decreased_price = 1000000 - 10416; // 1000000 - (1000000 * 1 / 4 * max_block_size) / (0.5 * max_block_size * 48);

// Assert.
assert_eq!(
calculate_next_base_gas_price(INIT_PRICE, HIGH_CONGESTION_GAS_USED, GAS_TARGET),
calculate_next_base_gas_price(init_price, high_congestion_gas_used, get_target),
increased_price
);
assert_eq!(
calculate_next_base_gas_price(INIT_PRICE, LOW_CONGESTION_GAS_USED, GAS_TARGET),
calculate_next_base_gas_price(init_price, low_congestion_gas_used, get_target),
decreased_price
);
assert_eq!(
calculate_next_base_gas_price(INIT_PRICE, STABLE_CONGESTION_GAS_USED, GAS_TARGET),
INIT_PRICE
calculate_next_base_gas_price(init_price, stable_congestion_gas_used, get_target),
init_price
);
}

#[test]
// This test ensures that the gas price calculation does not overflow with extreme values,
fn test_gas_price_with_extreme_values() {
let price = MIN_GAS_PRICE;
let gas_target = MAX_BLOCK_SIZE / 2;
let versioned_constants = VersionedConstants::latest_constants();
let max_block_size = versioned_constants.max_block_size;
let min_gas_price = versioned_constants.min_gas_price;
let gas_price_max_change_denominator = versioned_constants.gas_price_max_change_denominator;

let price = min_gas_price;
let gas_target = max_block_size / 2;
let gas_used = 0;
assert_eq!(calculate_next_base_gas_price(price, gas_used, gas_target), MIN_GAS_PRICE);
assert_eq!(calculate_next_base_gas_price(price, gas_used, gas_target), min_gas_price);

let price = MIN_GAS_PRICE;
let gas_target = MAX_BLOCK_SIZE / 2;
let gas_used = MAX_BLOCK_SIZE;
assert!(calculate_next_base_gas_price(price, gas_used, gas_target) > MIN_GAS_PRICE);
let price = min_gas_price;
let gas_target = max_block_size / 2;
let gas_used = max_block_size;
assert!(calculate_next_base_gas_price(price, gas_used, gas_target) > min_gas_price);

let price = u64::MAX;
let gas_target = MAX_BLOCK_SIZE / 2;
let gas_target = max_block_size / 2;
let gas_used = 0;
calculate_next_base_gas_price(price, gas_used, gas_target); // Should not panic.

// To avoid overflow when updating the price, the value is set below a certain threshold so that
// the new price does not exceed u64::MAX.
let max_u128 = u128::from(u64::MAX);
let price_u128 =
max_u128 * GAS_PRICE_MAX_CHANGE_DENOMINATOR / (GAS_PRICE_MAX_CHANGE_DENOMINATOR + 1);
let gas_target = MAX_BLOCK_SIZE / 2;
let gas_used = MAX_BLOCK_SIZE;
max_u128 * gas_price_max_change_denominator / (gas_price_max_change_denominator + 1);
let gas_target = max_block_size / 2;
let gas_used = max_block_size;
calculate_next_base_gas_price(u64::try_from(price_u128).unwrap(), gas_used, gas_target); // Should not panic.
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ use tokio_util::task::AbortOnDropHandle;
use tracing::{debug, error_span, info, instrument, trace, warn, Instrument};

use crate::cende::{BlobParameters, CendeContext};
use crate::fee_market::{calculate_next_base_gas_price, MAX_BLOCK_SIZE, MIN_GAS_PRICE};
use crate::fee_market::calculate_next_base_gas_price;
use crate::versioned_constants::VersionedConstants;

// TODO(Dan, Matan): Remove this once and replace with real gas prices.
const TEMPORARY_GAS_PRICES: GasPrices = GasPrices {
Expand Down Expand Up @@ -169,7 +170,7 @@ impl SequencerConsensusContext {
queued_proposals: BTreeMap::new(),
chain_id,
cende_ambassador,
l2_gas_price: MIN_GAS_PRICE,
l2_gas_price: VersionedConstants::latest_constants().min_gas_price,
}
}

Expand Down Expand Up @@ -384,8 +385,11 @@ impl ConsensusContext for SequencerConsensusContext {
})
.await;

self.l2_gas_price =
calculate_next_base_gas_price(self.l2_gas_price, l2_gas_used.0, MAX_BLOCK_SIZE / 2);
self.l2_gas_price = calculate_next_base_gas_price(
self.l2_gas_price,
l2_gas_used.0,
VersionedConstants::latest_constants().max_block_size / 2,
);

Ok(())
}
Expand Down

0 comments on commit e17763e

Please sign in to comment.