Skip to content

Commit

Permalink
feat(mempool): impl calculate-gas-price according to EIP 1559
Browse files Browse the repository at this point in the history
  • Loading branch information
MohammadNassar1 committed Sep 2, 2024
1 parent 5c782b6 commit 780f288
Showing 1 changed file with 72 additions and 0 deletions.
72 changes: 72 additions & 0 deletions crates/blockifier/src/blockifier/block.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::cmp::max;
use std::num::NonZeroU128;

use log::warn;
Expand All @@ -16,6 +17,14 @@ use crate::versioned_constants::VersionedConstants;
#[path = "block_test.rs"]
pub mod block_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;
const MIN_GAS_PRICE: u64 = 100000; // In fri.
// TODO(Mohammad): Check the exact value for maximum block size in StarkNet.
const MAX_BLOCK_SIZE: u64 = 4000000000; // In gas units. It's equivalent to 40M gas steps, with 100 gas units per step.

#[derive(Clone, Debug)]
pub struct BlockInfo {
pub block_number: BlockNumber,
Expand All @@ -27,6 +36,69 @@ pub struct BlockInfo {
pub use_kzg_da: bool,
}

impl BlockInfo {
/// Calculate the base gas price for the next block according to EIP-1559.
///
/// # Parameters
/// - `price`: The base fee 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 the gas limit).
pub fn calculate_next_base_gas_price(price: u64, gas_used: u64, gas_target: u64) -> u64 {
// 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,
"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,
"The gas price must be at least the minimum to prevent precision loss during \
multiplication and division."
);

// We use unsigned integers (u64 and u128) to avoid overflow issues, as the input values are
// naturally unsigned and i256 is unstable in Rust. This approach allows safe handling of
// all inputs using u128 for intermediate calculations.

// The absolute difference between gas_used and gas_target is always u64.
let gas_delta = gas_used.abs_diff(gas_target);
// Convert to u128 to prevent overflow, as a product of two u64 fits inside a u128.
let price_u128 = u128::from(price);
let gas_delta_u128 = u128::from(gas_delta);
let gas_target_u128 = u128::from(gas_target);

// Calculate the gas change as u128 to handle potential overflow during multiplication.
let gas_delta_cost =
price_u128.checked_mul(gas_delta_u128).expect("Both variables originate from 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;

// 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
// is bounded by half the maximum block size. Therefore, after dividing by the gas target
// (which is half the maximum block size), the result is guaranteed to fit within a u64.
let price_change = u64::try_from(price_change_u128)
.expect("Result fits u64 after division of a bounded gas delta");

let adjusted_price =
if gas_used > gas_target { price + price_change } else { price - price_change };

assert!(
gas_used > gas_target && adjusted_price >= price
|| gas_used <= gas_target && adjusted_price <= price
);

max(adjusted_price, MIN_GAS_PRICE)
}
}

#[derive(Clone, Debug)]
pub struct GasPrices {
eth_l1_gas_price: NonZeroU128, // In wei.
Expand Down

0 comments on commit 780f288

Please sign in to comment.