Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/fuel-gas-price-algorithm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
#![deny(clippy::cast_possible_truncation)]
#![deny(warnings)]

mod utils;
pub mod v0;
pub mod v1;
20 changes: 20 additions & 0 deletions crates/fuel-gas-price-algorithm/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#[allow(clippy::cast_possible_truncation)]
pub(crate) fn cumulative_percentage_change(
new_exec_price: u64,
for_height: u32,
percentage: u64,
height: u32,
) -> u64 {
let blocks = height.saturating_sub(for_height) as f64;
let percentage_as_decimal = percentage as f64 / 100.0;
let multiple = (1.0f64 + percentage_as_decimal).powf(blocks);
let mut approx = new_exec_price as f64 * multiple;
// account for rounding errors and take a slightly higher value
const ROUNDING_ERROR_CUTOFF: f64 = 16948547188989277.0;
if approx > ROUNDING_ERROR_CUTOFF {
const ROUNDING_ERROR_COMPENSATION: f64 = 2000.0;
approx += ROUNDING_ERROR_COMPENSATION;
}
// `f64` over `u64::MAX` are cast to `u64::MAX`
approx.ceil() as u64
}
23 changes: 8 additions & 15 deletions crates/fuel-gas-price-algorithm/src/v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use std::{
num::NonZeroU64,
};

use crate::utils::cumulative_percentage_change;

#[cfg(test)]
mod tests;

Expand All @@ -27,22 +29,13 @@ impl AlgorithmV0 {
self.new_exec_price
}

#[allow(clippy::cast_possible_truncation)]
pub fn worst_case(&self, height: u32) -> u64 {
let price = self.new_exec_price as f64;
let blocks = height.saturating_sub(self.for_height) as f64;
let percentage = self.percentage as f64;
let percentage_as_decimal = percentage / 100.0;
let multiple = (1.0f64 + percentage_as_decimal).powf(blocks);
let mut approx = price * multiple;
// account for rounding errors and take a slightly higher value
const ARB_CUTOFF: f64 = 16948547188989277.0;
if approx > ARB_CUTOFF {
const ARB_ADDITION: f64 = 2000.0;
approx += ARB_ADDITION;
}
// `f64` over `u64::MAX` are cast to `u64::MAX`
approx.ceil() as u64
cumulative_percentage_change(
self.new_exec_price,
self.for_height,
self.percentage,
height,
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ proptest! {
}

#[test]
fn worst_case__same_block_gives_new_exec_price() {
fn worst_case__same_block_gives_the_same_value_as_calculate() {
// given
let new_exec_price = 1000;
let for_height = 10;
Expand All @@ -100,6 +100,6 @@ fn worst_case__same_block_gives_new_exec_price() {
let actual = algorithm.worst_case(target_height);

// then
let expected = new_exec_price;
let expected = algorithm.calculate();
assert_eq!(expected, actual);
}
43 changes: 36 additions & 7 deletions crates/fuel-gas-price-algorithm/src/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use std::{
ops::Div,
};

use crate::utils::cumulative_percentage_change;

#[cfg(test)]
mod tests;

Expand All @@ -21,13 +23,37 @@ pub enum Error {

#[derive(Debug, Clone, PartialEq)]
pub struct AlgorithmV1 {
/// the combined Execution and DA gas prices
combined_gas_price: u64,
/// The gas price for to cover the execution of the next block
new_exec_price: u64,
/// The change percentage per block
exec_price_percentage: u64,
/// The gas price for to cover DA commitment
new_da_gas_price: u64,
/// The change percentage per block
da_gas_price_percentage: u64,
/// The block height of the next L2 block
for_height: u32,
}

impl AlgorithmV1 {
pub fn calculate(&self) -> u64 {
self.combined_gas_price
self.new_exec_price.saturating_add(self.new_da_gas_price)
}

pub fn worst_case(&self, height: u32) -> u64 {
let exec = cumulative_percentage_change(
self.new_exec_price,
self.for_height,
self.exec_price_percentage,
height,
);
let da = cumulative_percentage_change(
self.new_da_gas_price,
self.for_height,
self.da_gas_price_percentage,
height,
);
exec.saturating_add(da)
}
}

Expand Down Expand Up @@ -370,10 +396,13 @@ impl AlgorithmUpdaterV1 {
}

pub fn algorithm(&self) -> AlgorithmV1 {
let combined_gas_price = self
.descaled_exec_price()
.saturating_add(self.descaled_da_price());
AlgorithmV1 { combined_gas_price }
AlgorithmV1 {
new_exec_price: self.descaled_exec_price(),
exec_price_percentage: self.exec_gas_price_change_percent as u64,
new_da_gas_price: self.descaled_da_price(),
da_gas_price_percentage: self.max_da_gas_price_change_percent as u64,
for_height: self.l2_block_height,
}
}

// We only need to track the difference between the rewards and costs after we have true DA data
Expand Down
117 changes: 116 additions & 1 deletion crates/fuel-gas-price-algorithm/src/v1/tests/algorithm_v1_tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::v1::tests::UpdaterBuilder;
use crate::v1::{
tests::UpdaterBuilder,
AlgorithmV1,
};
use proptest::prelude::*;

#[test]
fn calculate__returns_sum_of_da_and_exec_gas_prices() {
Expand Down Expand Up @@ -28,3 +32,114 @@ fn calculate__returns_sum_of_da_and_exec_gas_prices() {
let expected = starting_exec_gas_price + starting_da_gas_price;
assert_eq!(expected, actual);
}

fn _worst_case__correctly_calculates_value(
new_exec_price: u64,
new_da_gas_price: u64,
for_height: u32,
block_horizon: u32,
exec_price_percentage: u64,
da_gas_price_percentage: u64,
) {
// given
let algorithm = AlgorithmV1 {
new_exec_price,
exec_price_percentage,
new_da_gas_price,
da_gas_price_percentage,
for_height,
};

// when
let target_height = for_height.saturating_add(block_horizon);
let actual = algorithm.worst_case(target_height);

// then
let mut expected_exec_price = new_exec_price;
let mut expected_da_gas_price = new_da_gas_price;

for _ in 0..block_horizon {
let change_amount = expected_exec_price
.saturating_mul(exec_price_percentage)
.saturating_div(100);
expected_exec_price = expected_exec_price.saturating_add(change_amount);

let change_amount = expected_da_gas_price
.saturating_mul(da_gas_price_percentage)
.saturating_div(100);
expected_da_gas_price = expected_da_gas_price.saturating_add(change_amount);
}

let expected = expected_exec_price.saturating_add(expected_da_gas_price);

dbg!(actual, expected);
assert!(actual >= expected);
}

proptest! {
#[test]
fn worst_case__correctly_calculates_value(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add another test to check for overflows. See the V0 test and this convo:
#2025 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test added in 6cb3161

exec_price: u64,
da_price: u64,
starting_height: u32,
block_horizon in 0..10_000u32,
exec_percentage: u64,
da_percentage: u64,
) {
_worst_case__correctly_calculates_value(exec_price, da_price, starting_height, block_horizon, exec_percentage, da_percentage);
}
}

proptest! {
#[test]
fn worst_case__never_overflows(
exec_price: u64,
da_price: u64,
starting_height: u32,
block_horizon in 0..10_000u32,
exec_percentage: u64,
da_percentage: u64,
) {
// given
let algorithm = AlgorithmV1 {
new_exec_price: exec_price,
exec_price_percentage: exec_percentage,
new_da_gas_price: da_price,
da_gas_price_percentage: da_percentage,
for_height: starting_height,
};

// when
let target_height = starting_height.saturating_add(block_horizon);
let _ = algorithm.worst_case(target_height);

// then
// doesn't panic with an overflow
}
}

#[test]
fn worst_case__same_block_gives_the_same_value_as_calculate() {
// given
let new_exec_price = 1000;
let exec_price_percentage = 10;
let new_da_gas_price = 1000;
let da_gas_price_percentage = 10;
let for_height = 10;
let algorithm = AlgorithmV1 {
new_exec_price,
exec_price_percentage,
new_da_gas_price,
da_gas_price_percentage,
for_height,
};

// when
let delta = 0;
let target_height = for_height + delta;
let actual = algorithm.worst_case(target_height);

// then
let expected = algorithm.calculate();
assert_eq!(expected, actual);
}
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,7 @@ impl GasPriceAlgorithm for Algorithm {
fn worst_case_gas_price(&self, height: BlockHeight) -> u64 {
match self {
Algorithm::V0(v0) => v0.worst_case(height.into()),
// TODO: Add worst_case calculation https://github.com/FuelLabs/fuel-core/issues/2203
Algorithm::V1(v1) => v1.calculate(),
Algorithm::V1(v1) => v1.worst_case(height.into()),
}
}
}