Skip to content

Commit

Permalink
feat(gas_price_service_v1): include max_da_gas_price to control vol…
Browse files Browse the repository at this point in the history
…atility (#2541)

## Linked Issues/PRs
<!-- List of related issues/PRs -->
- #2469

## Description
<!-- List of detailed changes -->
Adds a new config param `max_da_gas_price`, which
`new_scaled_da_gas_price` depends upon while mutating it.

## Checklist
- [ ] Breaking changes are clearly marked as such in the PR description
and changelog
- [ ] New behavior is reflected in tests
- [ ] [The specification](https://github.com/FuelLabs/fuel-specs/)
matches the implemented behavior (link update PR if changes are needed)

### Before requesting review
- [ ] I have reviewed the code myself
- [ ] I have created follow-up issues caused by this PR and linked them
here


### After merging, notify other teams

[Add or remove entries as needed]

- [ ] [Rust SDK](https://github.com/FuelLabs/fuels-rs/)
- [ ] [Sway compiler](https://github.com/FuelLabs/sway/)
- [ ] [Platform
documentation](https://github.com/FuelLabs/devrel-requests/issues/new?assignees=&labels=new+request&projects=&template=NEW-REQUEST.yml&title=%5BRequest%5D%3A+)
(for out-of-organization contributors, the person merging the PR will do
this)
- [ ] Someone else?

---------

Co-authored-by: Brandon Kite <brandonkite92@gmail.com>
Co-authored-by: Mitchell Turner <james.mitchell.turner@gmail.com>
  • Loading branch information
3 people authored Jan 9, 2025
1 parent 6ea6a5c commit ae40ee4
Show file tree
Hide file tree
Showing 12 changed files with 181 additions and 4 deletions.
7 changes: 7 additions & 0 deletions bin/fuel-core/src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ pub struct Command {
#[arg(long = "min-da-gas-price", default_value = "10000000", env)]
pub min_da_gas_price: u64,

/// Maximum DA gas price
// DEV: ensure that the max_da_gas_price default is > then the min_da_gas_price default
#[arg(long = "max-da-gas-price", default_value = "10000001", env)]
pub max_da_gas_price: u64,

/// P component of DA gas price calculation
/// **NOTE**: This is the **inverse** gain of a typical P controller.
/// Increasing this value will reduce gas price fluctuations.
Expand Down Expand Up @@ -339,6 +344,7 @@ impl Command {
min_gas_price,
gas_price_threshold_percent,
min_da_gas_price,
max_da_gas_price,
da_p_component,
da_d_component,
max_da_gas_price_change_percent,
Expand Down Expand Up @@ -660,6 +666,7 @@ impl Command {
memory_pool_size,
da_gas_price_factor: NonZeroU64::new(100).expect("100 is not zero"),
min_da_gas_price,
max_da_gas_price,
max_da_gas_price_change_percent,
da_p_component,
da_d_component,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ impl From<Config> for GasPriceServiceConfig {
value.exec_gas_price_threshold_percent,
value.da_gas_price_factor,
value.min_da_gas_price,
value.max_da_gas_price,
value.max_da_gas_price_change_percent,
value.da_p_component,
value.da_d_component,
Expand Down
2 changes: 2 additions & 0 deletions crates/fuel-core/src/service/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub struct Config {
pub memory_pool_size: usize,
pub da_gas_price_factor: NonZeroU64,
pub min_da_gas_price: u64,
pub max_da_gas_price: u64,
pub max_da_gas_price_change_percent: u16,
pub da_p_component: i64,
pub da_d_component: i64,
Expand Down Expand Up @@ -216,6 +217,7 @@ impl Config {
memory_pool_size: 4,
da_gas_price_factor: NonZeroU64::new(100).expect("100 is not zero"),
min_da_gas_price: 0,
max_da_gas_price: 1,
max_da_gas_price_change_percent: 0,
da_p_component: 0,
da_d_component: 0,
Expand Down
22 changes: 18 additions & 4 deletions crates/fuel-gas-price-algorithm/src/v1.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::utils::cumulative_percentage_change;
use std::{
cmp::max,
cmp::{
max,
min,
},
collections::BTreeMap,
num::NonZeroU64,
ops::{
Expand Down Expand Up @@ -147,6 +150,8 @@ pub struct AlgorithmUpdaterV1 {
pub gas_price_factor: NonZeroU64,
/// The lowest the algorithm allows the da gas price to go
pub min_da_gas_price: u64,
/// The highest the algorithm allows the da gas price to go
pub max_da_gas_price: u64,
/// The maximum percentage that the DA portion of the gas price can change in a single block
/// Using `u16` because it can go above 100% and possibly over 255%
pub max_da_gas_price_change_percent: u16,
Expand Down Expand Up @@ -514,9 +519,12 @@ impl AlgorithmUpdaterV1 {
scaled_da_change,
maybe_new_scaled_da_gas_price
);
self.new_scaled_da_gas_price = max(
self.min_scaled_da_gas_price(),
maybe_new_scaled_da_gas_price,
self.new_scaled_da_gas_price = min(
max(
self.min_scaled_da_gas_price(),
maybe_new_scaled_da_gas_price,
),
self.max_scaled_da_gas_price(),
);
}

Expand All @@ -540,6 +548,12 @@ impl AlgorithmUpdaterV1 {
.saturating_mul(self.gas_price_factor.into())
}

fn max_scaled_da_gas_price(&self) -> u64 {
// note: here we make sure that a correct maximum is used; hacky :(
max(self.max_da_gas_price, self.min_da_gas_price)
.saturating_mul(self.gas_price_factor.into())
}

fn p(&self) -> i128 {
let upcast_p = i128::from(self.da_p_component);
let checked_p = self.last_profit.checked_div(upcast_p);
Expand Down
8 changes: 8 additions & 0 deletions crates/fuel-gas-price-algorithm/src/v1/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub struct BlockBytes {
pub struct UpdaterBuilder {
min_exec_gas_price: u64,
min_da_gas_price: u64,
max_da_gas_price: u64,
starting_exec_gas_price: u64,
starting_da_gas_price: u64,
exec_gas_price_change_percent: u16,
Expand Down Expand Up @@ -53,6 +54,7 @@ impl UpdaterBuilder {
Self {
min_exec_gas_price: 0,
min_da_gas_price: 0,
max_da_gas_price: 1,
starting_exec_gas_price: 1,
starting_da_gas_price: 1,
exec_gas_price_change_percent: 0,
Expand Down Expand Up @@ -86,6 +88,11 @@ impl UpdaterBuilder {
self
}

fn with_max_da_gas_price(mut self, max_price: u64) -> Self {
self.max_da_gas_price = max_price;
self
}

fn with_starting_exec_gas_price(mut self, starting_da_gas_price: u64) -> Self {
self.starting_exec_gas_price = starting_da_gas_price;
self
Expand Down Expand Up @@ -192,6 +199,7 @@ impl UpdaterBuilder {
last_profit: self.last_profit,
second_to_last_profit: self.second_to_last_profit,
min_da_gas_price: self.min_da_gas_price,
max_da_gas_price: self.max_da_gas_price,
gas_price_factor: self
.da_gas_price_factor
.try_into()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ fn update_da_record_data__da_block_lowers_da_gas_price() {
fn update_da_record_data__da_block_increases_da_gas_price() {
// given
let da_cost_per_byte = 40;
let max_da_gas_price = u64::MAX;
let l2_block_height = 11;
let original_known_total_cost = 150;
let mut unrecorded_blocks: BTreeMap<_, _> = [(11, 3000)].into_iter().collect();
Expand All @@ -320,6 +321,7 @@ fn update_da_record_data__da_block_increases_da_gas_price() {
.with_projected_total_cost(projected_total_cost as u128)
.with_known_total_cost(original_known_total_cost as u128)
.with_unrecorded_blocks(&unrecorded_blocks)
.with_max_da_gas_price(max_da_gas_price)
.build();

let new_cost_per_byte = 100;
Expand Down Expand Up @@ -355,6 +357,126 @@ fn update_da_record_data__da_block_increases_da_gas_price() {
assert_ne!(old_da_gas_price, new_da_gas_price);
}

#[test]
fn update_da_record_data__da_block_increases_da_gas_price_within_the_min_max_range() {
// given
let min_da_gas_price = 0;
let max_da_gas_price = 5;
let da_cost_per_byte = 40;
let l2_block_height = 11;
let original_known_total_cost = 150;
let mut unrecorded_blocks: BTreeMap<_, _> = [(11, 3000)].into_iter().collect();
let da_p_component = 2;
let guessed_cost: u64 = unrecorded_blocks
.values()
.map(|bytes| bytes * da_cost_per_byte)
.sum();
let projected_total_cost = original_known_total_cost + guessed_cost;

let mut updater = UpdaterBuilder::new()
.with_da_cost_per_byte(da_cost_per_byte as u128)
.with_da_p_component(da_p_component)
.with_last_profit(-10, 0)
.with_l2_block_height(l2_block_height)
.with_projected_total_cost(projected_total_cost as u128)
.with_known_total_cost(original_known_total_cost as u128)
.with_unrecorded_blocks(&unrecorded_blocks)
.with_min_da_gas_price(min_da_gas_price)
.with_max_da_gas_price(max_da_gas_price)
.build();

let new_cost_per_byte = 100;
let (recorded_heights, recorded_cost) = unrecorded_blocks.iter().fold(
(vec![], 0),
|(mut range, cost), (height, bytes)| {
range.push(height);
(range, cost + bytes * new_cost_per_byte)
},
);

let min = *recorded_heights.iter().min().unwrap();
let max = *recorded_heights.iter().max().unwrap();
let recorded_range = *min..=*max;
let recorded_bytes = 500;

let old_da_gas_price = updater.new_scaled_da_gas_price;

// when
updater
.update_da_record_data(
recorded_range,
recorded_bytes,
recorded_cost as u128,
&mut unrecorded_blocks,
)
.unwrap();

// then
let new_da_gas_price = updater.new_scaled_da_gas_price;
// because the profit is -10 and the da_p_component is 2, the new da gas price should be greater than the previous one.
assert_eq!(new_da_gas_price, max_da_gas_price);
assert_ne!(old_da_gas_price, new_da_gas_price);
}

#[test]
fn update_da_record_data__sets_da_gas_price_to_min_da_gas_price_when_max_lt_min() {
// given
let min_da_gas_price = 1;
let max_da_gas_price = 0;
let da_cost_per_byte = 40;
let l2_block_height = 11;
let original_known_total_cost = 150;
let mut unrecorded_blocks: BTreeMap<_, _> = [(11, 3000)].into_iter().collect();
let da_p_component = 2;
let guessed_cost: u64 = unrecorded_blocks
.values()
.map(|bytes| bytes * da_cost_per_byte)
.sum();
let projected_total_cost = original_known_total_cost + guessed_cost;

let mut updater = UpdaterBuilder::new()
.with_da_cost_per_byte(da_cost_per_byte as u128)
.with_da_p_component(da_p_component)
.with_last_profit(-10, 0)
.with_l2_block_height(l2_block_height)
.with_projected_total_cost(projected_total_cost as u128)
.with_known_total_cost(original_known_total_cost as u128)
.with_unrecorded_blocks(&unrecorded_blocks)
.with_min_da_gas_price(min_da_gas_price)
.with_max_da_gas_price(max_da_gas_price)
.build();

let new_cost_per_byte = 100;
let (recorded_heights, recorded_cost) = unrecorded_blocks.iter().fold(
(vec![], 0),
|(mut range, cost), (height, bytes)| {
range.push(height);
(range, cost + bytes * new_cost_per_byte)
},
);

let min = *recorded_heights.iter().min().unwrap();
let max = *recorded_heights.iter().max().unwrap();
let recorded_range = *min..=*max;
let recorded_bytes = 500;

// when
updater
.update_da_record_data(
recorded_range,
recorded_bytes,
recorded_cost as u128,
&mut unrecorded_blocks,
)
.unwrap();

// then
let new_da_gas_price = updater.new_scaled_da_gas_price;

// because max_da_gas_price = 0 and < min_da_gas_price = 1, the new da gas price should be min_da_gas_price
assert_eq!(new_da_gas_price, min_da_gas_price);
}

#[test]
fn update_da_record_data__da_block_will_not_change_da_gas_price() {
// given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ fn negative_profit_updater_builder() -> UpdaterBuilder {
let starting_da_gas_price = 100;
let starting_cost = u128::MAX;
let latest_gas_per_byte = i32::MAX; // DA is very expensive
let max_da_gas_price = u64::MAX;
let da_p_component = 100;
let da_d_component = 10;
let last_profit = i128::MIN;
Expand All @@ -39,6 +40,7 @@ fn negative_profit_updater_builder() -> UpdaterBuilder {
.with_projected_total_cost(starting_cost)
.with_da_cost_per_byte(latest_gas_per_byte as u128)
.with_last_profit(last_profit, last_last_profit)
.with_max_da_gas_price(max_da_gas_price)
}

fn positive_profit_updater_builder() -> UpdaterBuilder {
Expand Down Expand Up @@ -399,6 +401,7 @@ fn update_l2_block_data__price_does_not_decrease_more_than_max_percent() {
let last_profit = i128::MAX; // Large, positive profit to decrease da price
let last_last_profit = 0;
let max_da_change_percent = 5;
let max_da_gas_price = u64::MAX;
let large_starting_reward = i128::MAX;
let mut updater = UpdaterBuilder::new()
.with_starting_exec_gas_price(starting_exec_gas_price)
Expand All @@ -411,6 +414,7 @@ fn update_l2_block_data__price_does_not_decrease_more_than_max_percent() {
.with_da_cost_per_byte(latest_gas_per_byte as u128)
.with_last_profit(last_profit, last_last_profit)
.with_da_max_change_percent(max_da_change_percent)
.with_max_da_gas_price(max_da_gas_price)
.build();
let unrecorded_blocks = &mut empty_unrecorded_blocks();

Expand Down Expand Up @@ -446,6 +450,7 @@ fn update_l2_block_data__da_price_does_not_increase_more_than_max_percent() {
let last_profit = i128::MIN; // Large, negative profit to increase da price
let last_last_profit = 0;
let max_da_change_percent = 5;
let max_da_gas_price = u64::MAX;
let large_starting_reward = 0;
let unrecorded_blocks = &mut empty_unrecorded_blocks();
let mut updater = UpdaterBuilder::new()
Expand All @@ -459,6 +464,7 @@ fn update_l2_block_data__da_price_does_not_increase_more_than_max_percent() {
.with_da_cost_per_byte(latest_gas_per_byte)
.with_last_profit(last_profit, last_last_profit)
.with_da_max_change_percent(max_da_change_percent)
.with_max_da_gas_price(max_da_gas_price)
.build();

// when
Expand Down Expand Up @@ -487,6 +493,7 @@ fn update_l2_block_data__never_drops_below_minimum_da_gas_price() {
let starting_exec_gas_price = 0;
let last_da_gas_price = 100;
let min_da_gas_price = 100;
let max_da_gas_price = min_da_gas_price + 1;
let starting_cost = 0;
let latest_gas_per_byte = 0; // DA is free
let da_p_component = 100;
Expand All @@ -507,6 +514,7 @@ fn update_l2_block_data__never_drops_below_minimum_da_gas_price() {
.with_da_cost_per_byte(latest_gas_per_byte as u128)
.with_last_profit(last_profit, avg_window)
.with_min_da_gas_price(min_da_gas_price)
.with_max_da_gas_price(max_da_gas_price)
.build();

// when
Expand Down Expand Up @@ -534,6 +542,7 @@ fn update_l2_block_data__even_profit_maintains_price() {
// given
let starting_exec_gas_price = 100;
let starting_da_gas_price = 100;
let max_da_gas_price = u64::MAX;
let starting_cost = 500;
let latest_cost_per_byte = 10;
let da_gas_price_denominator = 1;
Expand All @@ -548,6 +557,7 @@ fn update_l2_block_data__even_profit_maintains_price() {
.with_known_total_cost(starting_cost as u128)
.with_projected_total_cost(starting_cost as u128)
.with_da_cost_per_byte(latest_cost_per_byte as u128)
.with_max_da_gas_price(max_da_gas_price)
.build();

// when
Expand Down
2 changes: 2 additions & 0 deletions crates/services/gas_price_service/src/ports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ impl GasPriceServiceConfig {
l2_block_fullness_threshold_percent: u8,
gas_price_factor: NonZeroU64,
min_da_gas_price: u64,
max_da_gas_price: u64,
max_da_gas_price_change_percent: u16,
da_p_component: i64,
da_d_component: i64,
Expand All @@ -114,6 +115,7 @@ impl GasPriceServiceConfig {
l2_block_fullness_threshold_percent,
gas_price_factor,
min_da_gas_price,
max_da_gas_price,
max_da_gas_price_change_percent,
da_p_component,
da_d_component,
Expand Down
3 changes: 3 additions & 0 deletions crates/services/gas_price_service/src/v1/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ pub struct V1AlgorithmConfig {
// https://github.com/FuelLabs/fuel-core/issues/2481
pub gas_price_factor: NonZeroU64,
pub min_da_gas_price: u64,
pub max_da_gas_price: u64,
pub max_da_gas_price_change_percent: u16,
pub da_p_component: i64,
pub da_d_component: i64,
Expand Down Expand Up @@ -108,6 +109,7 @@ pub fn updater_from_config(value: &V1AlgorithmConfig) -> AlgorithmUpdaterV1 {
.l2_block_fullness_threshold_percent
.into(),
min_da_gas_price: value.min_da_gas_price,
max_da_gas_price: value.max_da_gas_price,
max_da_gas_price_change_percent: value.max_da_gas_price_change_percent,
da_p_component: value.da_p_component,
da_d_component: value.da_d_component,
Expand Down Expand Up @@ -166,6 +168,7 @@ pub fn v1_algorithm_from_metadata(
.l2_block_fullness_threshold_percent
.into(),
min_da_gas_price: config.min_da_gas_price,
max_da_gas_price: config.max_da_gas_price,
max_da_gas_price_change_percent: config.max_da_gas_price_change_percent,
da_p_component: config.da_p_component,
da_d_component: config.da_d_component,
Expand Down
Loading

0 comments on commit ae40ee4

Please sign in to comment.