From a733d9d7ef0fccd67033bd997be436482391606f Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Tue, 15 Oct 2024 21:10:10 +0200 Subject: [PATCH 01/21] wip --- crates/fuel-gas-price-algorithm/src/v1.rs | 33 ++++++++++--- .../v1/tests/update_l2_block_data_tests.rs | 47 +++++++++++++++++++ 2 files changed, 74 insertions(+), 6 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index a1a5f7dd536..e980f16dc37 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -118,13 +118,8 @@ pub struct AlgorithmUpdaterV1 { /// This is a percentage of the total capacity of the L2 block pub l2_block_fullness_threshold_percent: ClampedPercentage, // DA - /// The gas price for the DA portion of the last block. This can be used to calculate - /// the DA portion of the next block - // pub last_da_gas_price: u64, - /// The gas price (scaled by the `gas_price_factor`) to cover the DA commitment of the next block pub new_scaled_da_gas_price: u64, - /// Scale factor for the gas price. pub gas_price_factor: NonZeroU64, /// The lowest the algorithm allows the da gas price to go @@ -151,11 +146,37 @@ pub struct AlgorithmUpdaterV1 { pub second_to_last_profit: i128, /// The latest known cost per byte for recording blocks on the DA chain pub latest_da_cost_per_byte: u128, - + /// Activity of L2 + pub l2_activity: Activity, /// The unrecorded blocks that are used to calculate the projected cost of recording blocks pub unrecorded_blocks: BTreeMap, } +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] +pub struct Activity { + top: i16, + bottom: i16, + current: i16, +} + +impl Activity { + pub fn new(top: i16, bottom: i16, current: i16) -> Self { + Self { + top, + bottom, + current, + } + } +} + +fn change_percent_with_activity(base: u16, activity: u8, max_change: u16) -> u16 { + let activity = activity as u16; + let max_change = max_change as u16; + let base = base as u16; + let change = activity.saturating_mul(max_change).saturating_div(100); + base.saturating_add(change) +} + /// A value that represents a value between 0 and 100. Higher values are clamped to 100 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] pub struct ClampedPercentage { diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs index 6d5e4cf2ce7..53ceae18c51 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs @@ -610,3 +610,50 @@ fn update_l2_block_data__retains_existing_blocks_and_adds_l2_block_to_unrecorded .contains_key(&preexisting_block.height); assert!(contains_preexisting_block_bytes); } + +#[test] +fn update_l2_block_data__da_gas_price_increase_is_proportional_to_activity_parameter() { + // given + let starting_exec_gas_price = 100; + let starting_da_gas_price = 100; + let starting_cost = u128::MAX; + let latest_gas_per_byte = i32::MAX; // DA is very expensive + let da_p_component = 100; + let da_d_component = 10; + let last_profit = i128::MIN; + let last_last_profit = 0; + let smaller_starting_reward = 0; + let mut updater = UpdaterBuilder::new() + .with_starting_exec_gas_price(starting_exec_gas_price) + .with_starting_da_gas_price(starting_da_gas_price) + .with_da_p_component(da_p_component) + .with_da_d_component(da_d_component) + .with_total_rewards(smaller_starting_reward) + .with_known_total_cost(starting_cost) + .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) + .build(); + let algo = updater.algorithm(); + let old_gas_price = algo.calculate(); + + // when + let height = updater.l2_block_height + 1; + let used = 50; + let capacity = 100u64.try_into().unwrap(); + let block_bytes = 500u64; + let fee = 0; + updater + .update_l2_block_data(height, used, capacity, block_bytes, fee) + .unwrap(); + + // then + let algo = updater.algorithm(); + let new_gas_price = algo.calculate(); + assert!( + new_gas_price > old_gas_price, + "{} !> {}", + new_gas_price, + old_gas_price + ); +} From e4098e856afbfb7f7252c205f7555ecf96297009 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Wed, 16 Oct 2024 11:55:57 +0200 Subject: [PATCH 02/21] Add activity concept with safety mode --- crates/fuel-gas-price-algorithm/src/v1.rs | 91 +++++++++++++++---- .../fuel-gas-price-algorithm/src/v1/tests.rs | 6 +- 2 files changed, 78 insertions(+), 19 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index e980f16dc37..24ee7390ab8 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -147,38 +147,93 @@ pub struct AlgorithmUpdaterV1 { /// The latest known cost per byte for recording blocks on the DA chain pub latest_da_cost_per_byte: u128, /// Activity of L2 - pub l2_activity: Activity, + pub l2_activity: L2ActivityTracker, /// The unrecorded blocks that are used to calculate the projected cost of recording blocks pub unrecorded_blocks: BTreeMap, } +/// Because the DA gas price can increase even when no-one is using the network, there is a potential +/// for a negative feedback loop to occur where the gas price increases, further decreasing activity +/// and increasing the gas price. The `L2ActivityTracker` is used to moderate changes to the DA +/// gas price based on the activity of the L2 chain. +/// +/// For each L2 block, the activity is calculated as a percentage of the block capacity used. If the +/// activity is below a certain threshold, the activity is decreased. The activity exists on a scale +/// between 0 and the sum of the increase, hold, and decrease buffers: +/// +/// |<-- decrease range -->|<-- hold range -->|<-- increase range -->| +/// +/// The current activity determines the behavior of the DA gas price. +/// +/// For healthy behavior, the activity should be in the `increase` range. #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] -pub struct Activity { - top: i16, - bottom: i16, - current: i16, +pub struct L2ActivityTracker { + /// The _size_ of the range of the activity buffer that will increase the gas price + increase_range_size: u16, + /// The _size_ of the range of the activity buffer that will hold the gas price + hold_range_size: u16, + /// The _size_ of the range of the activity buffer that will decrease the gas price + decrease_range_size: u16, + /// The current activity of the L2 chain + activity: u16, + /// The threshold of activity below which the activity will be decreased + block_activity_threshold: ClampedPercentage, } -impl Activity { - pub fn new(top: i16, bottom: i16, current: i16) -> Self { +/// Designates the intended behavior of the DA gas price based on the activity of the L2 chain +pub enum DAGasPriceSafetyMode { + /// Should increase DA gas price freely + Increase, + /// Should not increase the DA gas price + Hold, + /// Should decrease the DA gas price always + Decrease, +} + +impl L2ActivityTracker { + pub fn new_full( + increase_range_size: u16, + hold_range_size: u16, + decrease_range_size: u16, + block_activity_threshold: ClampedPercentage, + ) -> Self { + let activity = decrease_range_size + hold_range_size + increase_range_size; Self { - top, - bottom, - current, + increase_range_size, + hold_range_size, + decrease_range_size, + activity, + block_activity_threshold, } } -} -fn change_percent_with_activity(base: u16, activity: u8, max_change: u16) -> u16 { - let activity = activity as u16; - let max_change = max_change as u16; - let base = base as u16; - let change = activity.saturating_mul(max_change).saturating_div(100); - base.saturating_add(change) + pub fn new_always_increases() -> Self { + Self::new_full(1, 0, 0, ClampedPercentage::new(0)) + } + + pub fn safety_mode(&self) -> DAGasPriceSafetyMode { + if self.activity > self.decrease_range_size + self.hold_range_size { + DAGasPriceSafetyMode::Increase + } else if self.activity > self.decrease_range_size { + DAGasPriceSafetyMode::Hold + } else { + DAGasPriceSafetyMode::Decrease + } + } + + pub fn update(&mut self, block_usage: ClampedPercentage) { + let cap = + self.decrease_range_size + self.hold_range_size + self.increase_range_size; + if block_usage < self.block_activity_threshold { + self.activity = self.activity.saturating_sub(1); + } else { + self.activity = self.activity.saturating_add(1).min(cap); + } + } } /// A value that represents a value between 0 and 100. Higher values are clamped to 100 -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, PartialOrd)] pub struct ClampedPercentage { value: u8, } diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests.rs index 0aca738a3f0..3d23331bc6d 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests.rs @@ -2,7 +2,10 @@ #![allow(clippy::arithmetic_side_effects)] #![allow(clippy::cast_possible_truncation)] -use crate::v1::AlgorithmUpdaterV1; +use crate::v1::{ + AlgorithmUpdaterV1, + L2ActivityTracker, +}; #[cfg(test)] mod algorithm_v1_tests; @@ -190,6 +193,7 @@ impl UpdaterBuilder { .da_gas_price_factor .try_into() .expect("Should never be non-zero"), + l2_activity: L2ActivityTracker::new_always_increases(), } } } From ae5011abe60669130a708886541b2a4c4fb30e9f Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 17 Oct 2024 11:28:20 +0200 Subject: [PATCH 03/21] Add passing tests for hold cases --- crates/fuel-gas-price-algorithm/src/v1.rs | 32 ++++++++- .../fuel-gas-price-algorithm/src/v1/tests.rs | 9 ++- .../v1/tests/update_l2_block_data_tests.rs | 70 +++++++++++++++++-- 3 files changed, 102 insertions(+), 9 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 24ee7390ab8..324b46ee9ee 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -159,9 +159,11 @@ pub struct AlgorithmUpdaterV1 { /// /// For each L2 block, the activity is calculated as a percentage of the block capacity used. If the /// activity is below a certain threshold, the activity is decreased. The activity exists on a scale -/// between 0 and the sum of the increase, hold, and decrease buffers: +/// between 0 and the sum of the increase, hold, and decrease buffers. /// -/// |<-- decrease range -->|<-- hold range -->|<-- increase range -->| +/// e.g. if the decrease range is 50, the hold range is 50, and the increase range is 50: +/// +/// 0<-- decrease range -->50<-- hold range -->100<-- increase range -->150 /// /// The current activity determines the behavior of the DA gas price. /// @@ -207,6 +209,22 @@ impl L2ActivityTracker { } } + pub fn new( + increase_range_size: u16, + hold_range_size: u16, + decrease_range_size: u16, + activity: u16, + block_activity_threshold: ClampedPercentage, + ) -> Self { + Self { + increase_range_size, + hold_range_size, + decrease_range_size, + activity, + block_activity_threshold, + } + } + pub fn new_always_increases() -> Self { Self::new_full(1, 0, 0, ClampedPercentage::new(0)) } @@ -385,7 +403,15 @@ impl AlgorithmUpdaterV1 { fn update_da_gas_price(&mut self) { let p = self.p(); let d = self.d(); - let da_change = self.da_change(p, d); + let maybe_da_change = self.da_change(p, d); + let da_change = if maybe_da_change > 0 { + match self.l2_activity.safety_mode() { + DAGasPriceSafetyMode::Increase => maybe_da_change, + DAGasPriceSafetyMode::Hold | DAGasPriceSafetyMode::Decrease => 0, + } + } else { + maybe_da_change + }; let maybe_new_scaled_da_gas_price = i128::from(self.new_scaled_da_gas_price) .checked_add(da_change) .and_then(|x| u64::try_from(x).ok()) diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests.rs index 3d23331bc6d..efbdb8ec59e 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests.rs @@ -43,6 +43,7 @@ pub struct UpdaterBuilder { last_profit: i128, second_to_last_profit: i128, da_gas_price_factor: u64, + l2_activity: L2ActivityTracker, } impl UpdaterBuilder { @@ -70,6 +71,7 @@ impl UpdaterBuilder { last_profit: 0, second_to_last_profit: 0, da_gas_price_factor: 1, + l2_activity: L2ActivityTracker::new_always_increases(), } } @@ -162,6 +164,11 @@ impl UpdaterBuilder { self } + fn with_activity(mut self, l2_activity: L2ActivityTracker) -> Self { + self.l2_activity = l2_activity; + self + } + fn build(self) -> AlgorithmUpdaterV1 { AlgorithmUpdaterV1 { min_exec_gas_price: self.min_exec_gas_price, @@ -193,7 +200,7 @@ impl UpdaterBuilder { .da_gas_price_factor .try_into() .expect("Should never be non-zero"), - l2_activity: L2ActivityTracker::new_always_increases(), + l2_activity: self.l2_activity, } } } diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs index 53ceae18c51..64e7edfa657 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs @@ -4,6 +4,7 @@ use crate::v1::{ UpdaterBuilder, }, Error, + L2ActivityTracker, }; #[test] @@ -611,8 +612,18 @@ fn update_l2_block_data__retains_existing_blocks_and_adds_l2_block_to_unrecorded assert!(contains_preexisting_block_bytes); } +fn hold_l2_activity() -> L2ActivityTracker { + let increase = 1; + let hold = 100; + let decrease = 1; + let activity = 50; + let threshold = 50.into(); + L2ActivityTracker::new(increase, hold, decrease, activity, threshold) +} + #[test] -fn update_l2_block_data__da_gas_price_increase_is_proportional_to_activity_parameter() { +fn update_l2_block_data__da_gas_price_wants_to_increase_will_hold_if_activity_in_hold_range( +) { // given let starting_exec_gas_price = 100; let starting_da_gas_price = 100; @@ -623,6 +634,7 @@ fn update_l2_block_data__da_gas_price_increase_is_proportional_to_activity_param let last_profit = i128::MIN; let last_last_profit = 0; let smaller_starting_reward = 0; + let hold_activity = hold_l2_activity(); let mut updater = UpdaterBuilder::new() .with_starting_exec_gas_price(starting_exec_gas_price) .with_starting_da_gas_price(starting_da_gas_price) @@ -633,6 +645,7 @@ fn update_l2_block_data__da_gas_price_increase_is_proportional_to_activity_param .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_activity(hold_activity) .build(); let algo = updater.algorithm(); let old_gas_price = algo.calculate(); @@ -650,10 +663,57 @@ fn update_l2_block_data__da_gas_price_increase_is_proportional_to_activity_param // then let algo = updater.algorithm(); let new_gas_price = algo.calculate(); + assert_eq!(new_gas_price, old_gas_price,); +} + +#[test] +fn update_l2_block_data__da_gas_price_wants_to_decrease_will_decrease_if_activity_in_hold_range( +) { + // given + let starting_exec_gas_price = 100; + let last_da_gas_price = 100; + let starting_cost = 500; + let latest_gas_per_byte = 0; // DA is free + let da_p_component = 100; + let da_d_component = 10; + let block_bytes = 500u64; + let last_profit = i128::MAX; + let last_last_profit = 0; + let large_reward = i128::MAX; + let hold_activity = hold_l2_activity(); + let mut updater = UpdaterBuilder::new() + .with_starting_exec_gas_price(starting_exec_gas_price) + .with_da_p_component(da_p_component) + .with_da_d_component(da_d_component) + .with_starting_da_gas_price(last_da_gas_price) + .with_total_rewards(large_reward as u128) + .with_known_total_cost(starting_cost as u128) + .with_projected_total_cost(starting_cost as u128) + .with_da_cost_per_byte(latest_gas_per_byte as u128) + .with_last_profit(last_profit, last_last_profit) + .with_da_max_change_percent(u16::MAX) + .with_exec_gas_price_change_percent(0) + .with_activity(hold_activity) + .build(); + let old_gas_price = updater.algorithm().calculate(); + + // when + updater + .update_l2_block_data( + updater.l2_block_height + 1, + 50, + 100.try_into().unwrap(), + block_bytes, + 200, + ) + .unwrap(); + + // then + let new_gas_price = updater.algorithm().calculate(); assert!( - new_gas_price > old_gas_price, - "{} !> {}", - new_gas_price, - old_gas_price + new_gas_price < old_gas_price, + "{} !< {}", + old_gas_price, + new_gas_price ); } From 27e8e2a6173e1aaf4b2fd50a7df5258b8ce1c6ca Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 17 Oct 2024 15:17:46 +0200 Subject: [PATCH 04/21] Add code for decreasing scenario --- crates/fuel-gas-price-algorithm/src/v1.rs | 30 ++++++---- .../v1/tests/update_l2_block_data_tests.rs | 59 +++++++++++++++++++ 2 files changed, 79 insertions(+), 10 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 324b46ee9ee..4f53cced484 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -404,14 +404,7 @@ impl AlgorithmUpdaterV1 { let p = self.p(); let d = self.d(); let maybe_da_change = self.da_change(p, d); - let da_change = if maybe_da_change > 0 { - match self.l2_activity.safety_mode() { - DAGasPriceSafetyMode::Increase => maybe_da_change, - DAGasPriceSafetyMode::Hold | DAGasPriceSafetyMode::Decrease => 0, - } - } else { - maybe_da_change - }; + let da_change = self.da_change_accounting_for_activity(maybe_da_change); let maybe_new_scaled_da_gas_price = i128::from(self.new_scaled_da_gas_price) .checked_add(da_change) .and_then(|x| u64::try_from(x).ok()) @@ -428,6 +421,18 @@ impl AlgorithmUpdaterV1 { ); } + fn da_change_accounting_for_activity(&self, maybe_da_change: i128) -> i128 { + if maybe_da_change > 0 { + match self.l2_activity.safety_mode() { + DAGasPriceSafetyMode::Increase => maybe_da_change, + DAGasPriceSafetyMode::Hold => 0, + DAGasPriceSafetyMode::Decrease => -self.max_change(), + } + } else { + maybe_da_change + } + } + fn min_scaled_da_gas_price(&self) -> u64 { self.min_da_gas_price .saturating_mul(self.gas_price_factor.into()) @@ -450,14 +455,19 @@ impl AlgorithmUpdaterV1 { fn da_change(&self, p: i128, d: i128) -> i128 { let pd_change = p.saturating_add(d); + let max_change = self.max_change(); + let clamped_change = pd_change.saturating_abs().min(max_change); + pd_change.signum().saturating_mul(clamped_change) + } + + fn max_change(&self) -> i128 { let upcast_percent = self.max_da_gas_price_change_percent.into(); let max_change = self .new_scaled_da_gas_price .saturating_mul(upcast_percent) .saturating_div(100) .into(); - let clamped_change = pd_change.saturating_abs().min(max_change); - pd_change.signum().saturating_mul(clamped_change) + max_change } fn exec_change(&self, principle: u64) -> u64 { diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs index 64e7edfa657..1c799e8c4d4 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs @@ -717,3 +717,62 @@ fn update_l2_block_data__da_gas_price_wants_to_decrease_will_decrease_if_activit new_gas_price ); } + +fn decrease_l2_activity() -> L2ActivityTracker { + let increase = 1; + let hold = 1; + let decrease = 100; + let activity = 50; + let threshold = 50.into(); + L2ActivityTracker::new(increase, hold, decrease, activity, threshold) +} + +#[test] +fn update_l2_block_data__da_gas_price_wants_to_increase_will_decrease_if_activity_in_decrease_range( +) { + // given + let starting_exec_gas_price = 100; + let starting_da_gas_price = 100; + let starting_cost = u128::MAX; + let latest_gas_per_byte = i32::MAX; // DA is very expensive + let da_p_component = 100; + let da_d_component = 10; + let last_profit = i128::MIN; + let last_last_profit = 0; + let smaller_starting_reward = 0; + let decrease_activity = decrease_l2_activity(); + let mut updater = UpdaterBuilder::new() + .with_starting_exec_gas_price(starting_exec_gas_price) + .with_starting_da_gas_price(starting_da_gas_price) + .with_da_p_component(da_p_component) + .with_da_d_component(da_d_component) + .with_total_rewards(smaller_starting_reward) + .with_known_total_cost(starting_cost) + .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_activity(decrease_activity) + .build(); + let algo = updater.algorithm(); + let old_gas_price = algo.calculate(); + + // when + let height = updater.l2_block_height + 1; + let used = 50; + let capacity = 100u64.try_into().unwrap(); + let block_bytes = 500u64; + let fee = 0; + updater + .update_l2_block_data(height, used, capacity, block_bytes, fee) + .unwrap(); + + // then + let algo = updater.algorithm(); + let new_gas_price = algo.calculate(); + assert!( + new_gas_price < old_gas_price, + "{} !> {}", + new_gas_price, + old_gas_price + ); +} From 0ae623e7ff9bb1c27256676a80636b29bf76ee4d Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 17 Oct 2024 16:04:51 +0200 Subject: [PATCH 05/21] Refactor and add more tests --- .../v1/tests/update_l2_block_data_tests.rs | 95 +++++-------------- 1 file changed, 22 insertions(+), 73 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs index 1c799e8c4d4..a5629e4ce0b 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs @@ -266,20 +266,17 @@ fn update_l2_block_data__updates_last_and_last_last_profit() { assert_eq!(actual, expected); } -#[test] -fn update_l2_block_data__positive_profit_decrease_gas_price() { - // given +fn positive_profit_updater_builder() -> UpdaterBuilder { let starting_exec_gas_price = 100; let last_da_gas_price = 100; let starting_cost = 500; let latest_gas_per_byte = 0; // DA is free let da_p_component = 100; let da_d_component = 10; - let block_bytes = 500u64; let last_profit = i128::MAX; let last_last_profit = 0; let large_reward = i128::MAX; - let mut updater = UpdaterBuilder::new() + UpdaterBuilder::new() .with_starting_exec_gas_price(starting_exec_gas_price) .with_da_p_component(da_p_component) .with_da_d_component(da_d_component) @@ -291,10 +288,16 @@ fn update_l2_block_data__positive_profit_decrease_gas_price() { .with_last_profit(last_profit, last_last_profit) .with_da_max_change_percent(u16::MAX) .with_exec_gas_price_change_percent(0) - .build(); +} + +#[test] +fn update_l2_block_data__positive_profit_decrease_gas_price() { + // given + let mut updater = positive_profit_updater_builder().build(); let old_gas_price = updater.algorithm().calculate(); // when + let block_bytes = 500u64; updater .update_l2_block_data( updater.l2_block_height + 1, @@ -497,26 +500,7 @@ fn update_l2_block_data__even_profit_maintains_price() { #[test] fn update_l2_block_data__negative_profit_increase_gas_price() { // given - let starting_exec_gas_price = 100; - let starting_da_gas_price = 100; - let starting_cost = u128::MAX; - let latest_gas_per_byte = i32::MAX; // DA is very expensive - let da_p_component = 100; - let da_d_component = 10; - let last_profit = i128::MIN; - let last_last_profit = 0; - let smaller_starting_reward = 0; - let mut updater = UpdaterBuilder::new() - .with_starting_exec_gas_price(starting_exec_gas_price) - .with_starting_da_gas_price(starting_da_gas_price) - .with_da_p_component(da_p_component) - .with_da_d_component(da_d_component) - .with_total_rewards(smaller_starting_reward) - .with_known_total_cost(starting_cost) - .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) - .build(); + let mut updater = negative_profit_updater_builder().build(); let algo = updater.algorithm(); let old_gas_price = algo.calculate(); @@ -625,26 +609,8 @@ fn hold_l2_activity() -> L2ActivityTracker { fn update_l2_block_data__da_gas_price_wants_to_increase_will_hold_if_activity_in_hold_range( ) { // given - let starting_exec_gas_price = 100; - let starting_da_gas_price = 100; - let starting_cost = u128::MAX; - let latest_gas_per_byte = i32::MAX; // DA is very expensive - let da_p_component = 100; - let da_d_component = 10; - let last_profit = i128::MIN; - let last_last_profit = 0; - let smaller_starting_reward = 0; let hold_activity = hold_l2_activity(); - let mut updater = UpdaterBuilder::new() - .with_starting_exec_gas_price(starting_exec_gas_price) - .with_starting_da_gas_price(starting_da_gas_price) - .with_da_p_component(da_p_component) - .with_da_d_component(da_d_component) - .with_total_rewards(smaller_starting_reward) - .with_known_total_cost(starting_cost) - .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) + let mut updater = negative_profit_updater_builder() .with_activity(hold_activity) .build(); let algo = updater.algorithm(); @@ -670,34 +636,14 @@ fn update_l2_block_data__da_gas_price_wants_to_increase_will_hold_if_activity_in fn update_l2_block_data__da_gas_price_wants_to_decrease_will_decrease_if_activity_in_hold_range( ) { // given - let starting_exec_gas_price = 100; - let last_da_gas_price = 100; - let starting_cost = 500; - let latest_gas_per_byte = 0; // DA is free - let da_p_component = 100; - let da_d_component = 10; - let block_bytes = 500u64; - let last_profit = i128::MAX; - let last_last_profit = 0; - let large_reward = i128::MAX; let hold_activity = hold_l2_activity(); - let mut updater = UpdaterBuilder::new() - .with_starting_exec_gas_price(starting_exec_gas_price) - .with_da_p_component(da_p_component) - .with_da_d_component(da_d_component) - .with_starting_da_gas_price(last_da_gas_price) - .with_total_rewards(large_reward as u128) - .with_known_total_cost(starting_cost as u128) - .with_projected_total_cost(starting_cost as u128) - .with_da_cost_per_byte(latest_gas_per_byte as u128) - .with_last_profit(last_profit, last_last_profit) - .with_da_max_change_percent(u16::MAX) - .with_exec_gas_price_change_percent(0) + let mut updater = positive_profit_updater_builder() .with_activity(hold_activity) .build(); let old_gas_price = updater.algorithm().calculate(); // when + let block_bytes = 500u64; updater .update_l2_block_data( updater.l2_block_height + 1, @@ -727,10 +673,7 @@ fn decrease_l2_activity() -> L2ActivityTracker { L2ActivityTracker::new(increase, hold, decrease, activity, threshold) } -#[test] -fn update_l2_block_data__da_gas_price_wants_to_increase_will_decrease_if_activity_in_decrease_range( -) { - // given +fn negative_profit_updater_builder() -> UpdaterBuilder { let starting_exec_gas_price = 100; let starting_da_gas_price = 100; let starting_cost = u128::MAX; @@ -740,8 +683,7 @@ fn update_l2_block_data__da_gas_price_wants_to_increase_will_decrease_if_activit let last_profit = i128::MIN; let last_last_profit = 0; let smaller_starting_reward = 0; - let decrease_activity = decrease_l2_activity(); - let mut updater = UpdaterBuilder::new() + UpdaterBuilder::new() .with_starting_exec_gas_price(starting_exec_gas_price) .with_starting_da_gas_price(starting_da_gas_price) .with_da_p_component(da_p_component) @@ -751,6 +693,13 @@ fn update_l2_block_data__da_gas_price_wants_to_increase_will_decrease_if_activit .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) +} +#[test] +fn update_l2_block_data__da_gas_price_wants_to_increase_will_decrease_if_activity_in_decrease_range( +) { + // given + let decrease_activity = decrease_l2_activity(); + let mut updater = negative_profit_updater_builder() .with_activity(decrease_activity) .build(); let algo = updater.algorithm(); From 92eff33895876d89fddc39a36cd4355c275ac3a4 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 17 Oct 2024 16:07:28 +0200 Subject: [PATCH 06/21] Add test for decrease in decreas activity --- .../v1/tests/update_l2_block_data_tests.rs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs index a5629e4ce0b..c6df771c05b 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs @@ -725,3 +725,35 @@ fn update_l2_block_data__da_gas_price_wants_to_increase_will_decrease_if_activit old_gas_price ); } + +#[test] +fn update_l2_block_data__da_gas_price_wants_to_decrease_will_decrease_if_activity_in_decrease_range( +) { + // given + let decrease_activity = decrease_l2_activity(); + let mut updater = positive_profit_updater_builder() + .with_activity(decrease_activity) + .build(); + let old_gas_price = updater.algorithm().calculate(); + + // when + let block_bytes = 500u64; + updater + .update_l2_block_data( + updater.l2_block_height + 1, + 50, + 100.try_into().unwrap(), + block_bytes, + 200, + ) + .unwrap(); + + // then + let new_gas_price = updater.algorithm().calculate(); + assert!( + new_gas_price < old_gas_price, + "{} !< {}", + old_gas_price, + new_gas_price + ); +} From 50effd9f4dc9798b977009052f2690a613969321 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 17 Oct 2024 17:03:54 +0200 Subject: [PATCH 07/21] Add test for increasing activity --- crates/fuel-gas-price-algorithm/src/v1.rs | 16 ++++++++++ .../v1/tests/update_l2_block_data_tests.rs | 32 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 4f53cced484..a7834840146 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -248,6 +248,10 @@ impl L2ActivityTracker { self.activity = self.activity.saturating_add(1).min(cap); } } + + pub fn current_activity(&self) -> u16 { + self.activity + } } /// A value that represents a value between 0 and 100. Higher values are clamped to 100 @@ -320,6 +324,9 @@ impl AlgorithmUpdaterV1 { let last_profit = rewards.saturating_sub(projected_total_da_cost); self.update_last_profit(last_profit); + // activity + self.update_activity(used, capacity); + // gas prices self.update_exec_gas_price(used, capacity); self.update_da_gas_price(); @@ -330,6 +337,15 @@ impl AlgorithmUpdaterV1 { } } + fn update_activity(&mut self, used: u64, capacity: NonZeroU64) { + let block_activity = used + .saturating_mul(100) + .checked_div(capacity.into()) + .unwrap_or(100); + let usage = ClampedPercentage::new(block_activity.try_into().unwrap_or(100)); + self.l2_activity.update(usage); + } + fn update_da_rewards(&mut self, fee_wei: u128) { let block_da_reward = self.da_portion_of_fee(fee_wei); self.total_da_rewards_excess = diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs index c6df771c05b..dab0be23dcb 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs @@ -757,3 +757,35 @@ fn update_l2_block_data__da_gas_price_wants_to_decrease_will_decrease_if_activit new_gas_price ); } + +#[test] +fn update_l2_block_data__above_threshold_increase_activity() { + // given + let starting_exec_gas_price = 100; + let exec_gas_price_increase_percent = 10; + let threshold = 50; + let starting_activity = 2; + let activity = L2ActivityTracker::new(1, 1, 1, starting_activity, 50.into()); + let mut updater = UpdaterBuilder::new() + .with_starting_exec_gas_price(starting_exec_gas_price) + .with_exec_gas_price_change_percent(exec_gas_price_increase_percent) + .with_l2_block_capacity_threshold(threshold) + .with_activity(activity) + .build(); + + let height = 1; + let used = 60; + let capacity = 100.try_into().unwrap(); + let block_bytes = 1000; + let fee = 200; + + // when + updater + .update_l2_block_data(height, used, capacity, block_bytes, fee) + .unwrap(); + + // then + let expected = starting_activity + 1; + let actual = updater.l2_activity.current_activity(); + assert_eq!(actual, expected); +} From 6249d48b788d54dd029856cd6e15c34a1e2dbf5e Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 17 Oct 2024 17:05:16 +0200 Subject: [PATCH 08/21] Add test for decreasing activity --- .../v1/tests/update_l2_block_data_tests.rs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs index dab0be23dcb..78df62d1a1b 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs @@ -789,3 +789,35 @@ fn update_l2_block_data__above_threshold_increase_activity() { let actual = updater.l2_activity.current_activity(); assert_eq!(actual, expected); } + +#[test] +fn update_l2_block_data__below_threshold_decrease_activity() { + // given + let starting_exec_gas_price = 100; + let exec_gas_price_increase_percent = 10; + let threshold = 50; + let starting_activity = 2; + let activity = L2ActivityTracker::new(1, 1, 1, starting_activity, 50.into()); + let mut updater = UpdaterBuilder::new() + .with_starting_exec_gas_price(starting_exec_gas_price) + .with_exec_gas_price_change_percent(exec_gas_price_increase_percent) + .with_l2_block_capacity_threshold(threshold) + .with_activity(activity) + .build(); + + let height = 1; + let used = 40; + let capacity = 100.try_into().unwrap(); + let block_bytes = 1000; + let fee = 200; + + // when + updater + .update_l2_block_data(height, used, capacity, block_bytes, fee) + .unwrap(); + + // then + let expected = starting_activity - 1; + let actual = updater.l2_activity.current_activity(); + assert_eq!(actual, expected); +} From 931b36e3397c441a9bac9180cda59c32c5f7196d Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 17 Oct 2024 17:13:54 +0200 Subject: [PATCH 09/21] Appease Clippy-sama --- crates/fuel-gas-price-algorithm/src/v1.rs | 24 ++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index a7834840146..3158fbeacf2 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -199,7 +199,9 @@ impl L2ActivityTracker { decrease_range_size: u16, block_activity_threshold: ClampedPercentage, ) -> Self { - let activity = decrease_range_size + hold_range_size + increase_range_size; + let activity = decrease_range_size + .saturating_add(hold_range_size) + .saturating_add(increase_range_size); Self { increase_range_size, hold_range_size, @@ -230,7 +232,11 @@ impl L2ActivityTracker { } pub fn safety_mode(&self) -> DAGasPriceSafetyMode { - if self.activity > self.decrease_range_size + self.hold_range_size { + if self.activity + > self + .decrease_range_size + .saturating_add(self.hold_range_size) + { DAGasPriceSafetyMode::Increase } else if self.activity > self.decrease_range_size { DAGasPriceSafetyMode::Hold @@ -240,8 +246,10 @@ impl L2ActivityTracker { } pub fn update(&mut self, block_usage: ClampedPercentage) { - let cap = - self.decrease_range_size + self.hold_range_size + self.increase_range_size; + let cap = self + .decrease_range_size + .saturating_add(self.hold_range_size) + .saturating_add(self.increase_range_size); if block_usage < self.block_activity_threshold { self.activity = self.activity.saturating_sub(1); } else { @@ -442,7 +450,7 @@ impl AlgorithmUpdaterV1 { match self.l2_activity.safety_mode() { DAGasPriceSafetyMode::Increase => maybe_da_change, DAGasPriceSafetyMode::Hold => 0, - DAGasPriceSafetyMode::Decrease => -self.max_change(), + DAGasPriceSafetyMode::Decrease => self.max_change().saturating_mul(-1), } } else { maybe_da_change @@ -478,12 +486,10 @@ impl AlgorithmUpdaterV1 { fn max_change(&self) -> i128 { let upcast_percent = self.max_da_gas_price_change_percent.into(); - let max_change = self - .new_scaled_da_gas_price + self.new_scaled_da_gas_price .saturating_mul(upcast_percent) .saturating_div(100) - .into(); - max_change + .into() } fn exec_change(&self, principle: u64) -> u64 { From 0a547c32a637ddff9cca6601bcfa3a0dd1a8440d Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 17 Oct 2024 17:21:51 +0200 Subject: [PATCH 10/21] Add more activity tests --- crates/fuel-gas-price-algorithm/src/v1.rs | 5 ++ .../v1/tests/update_l2_block_data_tests.rs | 73 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 3158fbeacf2..d47c49ecfac 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -218,6 +218,10 @@ impl L2ActivityTracker { activity: u16, block_activity_threshold: ClampedPercentage, ) -> Self { + let max_activity = decrease_range_size + .saturating_add(hold_range_size) + .saturating_add(increase_range_size); + let activity = activity.min(max_activity); Self { increase_range_size, hold_range_size, @@ -227,6 +231,7 @@ impl L2ActivityTracker { } } + #[cfg(test)] pub fn new_always_increases() -> Self { Self::new_full(1, 0, 0, ClampedPercentage::new(0)) } diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs index 78df62d1a1b..860a63b75e8 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs @@ -821,3 +821,76 @@ fn update_l2_block_data__below_threshold_decrease_activity() { let actual = updater.l2_activity.current_activity(); assert_eq!(actual, expected); } + +#[test] +fn update_l2_block_data__if_activity_at_max_will_stop_increasing() { + // given + let starting_exec_gas_price = 100; + let exec_gas_price_increase_percent = 10; + let threshold = 50; + let increase_range = 1; + let hold_range = 1; + let decrease_range = 1; + let starting_activity = increase_range + hold_range + decrease_range; + let activity = L2ActivityTracker::new( + increase_range, + hold_range, + decrease_range, + starting_activity, + 50.into(), + ); + let mut updater = UpdaterBuilder::new() + .with_starting_exec_gas_price(starting_exec_gas_price) + .with_exec_gas_price_change_percent(exec_gas_price_increase_percent) + .with_l2_block_capacity_threshold(threshold) + .with_activity(activity) + .build(); + + let height = 1; + let used = 60; + let capacity = 100.try_into().unwrap(); + let block_bytes = 1000; + let fee = 200; + + // when + updater + .update_l2_block_data(height, used, capacity, block_bytes, fee) + .unwrap(); + + // then + let expected = starting_activity; + let actual = updater.l2_activity.current_activity(); + assert_eq!(actual, expected); +} + +#[test] +fn update_l2_block_data__if_activity_is_zero_will_stop_decreasing() { + // given + let starting_exec_gas_price = 100; + let exec_gas_price_increase_percent = 10; + let threshold = 50; + let starting_activity = 0; + let activity = L2ActivityTracker::new(1, 1, 1, starting_activity, 50.into()); + let mut updater = UpdaterBuilder::new() + .with_starting_exec_gas_price(starting_exec_gas_price) + .with_exec_gas_price_change_percent(exec_gas_price_increase_percent) + .with_l2_block_capacity_threshold(threshold) + .with_activity(activity) + .build(); + + let height = 1; + let used = 40; + let capacity = 100.try_into().unwrap(); + let block_bytes = 1000; + let fee = 200; + + // when + updater + .update_l2_block_data(height, used, capacity, block_bytes, fee) + .unwrap(); + + // then + let expected = starting_activity; + let actual = updater.l2_activity.current_activity(); + assert_eq!(actual, expected); +} From afc76910f518262ca66ddb92e4d10f39858c360c Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 17 Oct 2024 17:23:13 +0200 Subject: [PATCH 11/21] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4e728c2881..51ec318a372 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - [2321](https://github.com/FuelLabs/fuel-core/pull/2321): New metrics for the txpool: "The size of transactions in the txpool" (`txpool_tx_size`), "The time spent by a transaction in the txpool in seconds" (`txpool_tx_time_in_txpool_seconds`), The number of transactions in the txpool (`txpool_number_of_transactions`), "The number of transactions pending verification before entering the txpool" (`txpool_number_of_transactions_pending_verification`), "The number of executable transactions in the txpool" (`txpool_number_of_executable_transactions`), "The time it took to select transactions for inclusion in a block in nanoseconds" (`txpool_select_transaction_time_nanoseconds`), The time it took to insert a transaction in the txpool in milliseconds (`txpool_insert_transaction_time_milliseconds`). +- [2347](https://github.com/FuelLabs/fuel-core/pull/2364): Add activity concept in order to protect against infinitely increasing DA gas price scenarios ## [Version 0.40.0] From dc40e13f4b64fb0bda86efe5cd69cc8fe0a7c409 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 17 Oct 2024 17:25:05 +0200 Subject: [PATCH 12/21] Reorganize --- .../v1/tests/update_l2_block_data_tests.rs | 108 +++++++++--------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs index 860a63b75e8..289e020a38c 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs @@ -7,6 +7,61 @@ use crate::v1::{ L2ActivityTracker, }; +fn decrease_l2_activity() -> L2ActivityTracker { + let increase = 1; + let hold = 1; + let decrease = 100; + let activity = 50; + let threshold = 50.into(); + L2ActivityTracker::new(increase, hold, decrease, activity, threshold) +} + +fn negative_profit_updater_builder() -> UpdaterBuilder { + let starting_exec_gas_price = 100; + let starting_da_gas_price = 100; + let starting_cost = u128::MAX; + let latest_gas_per_byte = i32::MAX; // DA is very expensive + let da_p_component = 100; + let da_d_component = 10; + let last_profit = i128::MIN; + let last_last_profit = 0; + let smaller_starting_reward = 0; + UpdaterBuilder::new() + .with_starting_exec_gas_price(starting_exec_gas_price) + .with_starting_da_gas_price(starting_da_gas_price) + .with_da_p_component(da_p_component) + .with_da_d_component(da_d_component) + .with_total_rewards(smaller_starting_reward) + .with_known_total_cost(starting_cost) + .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) +} + +fn positive_profit_updater_builder() -> UpdaterBuilder { + let starting_exec_gas_price = 100; + let last_da_gas_price = 100; + let starting_cost = 500; + let latest_gas_per_byte = 0; // DA is free + let da_p_component = 100; + let da_d_component = 10; + let last_profit = i128::MAX; + let last_last_profit = 0; + let large_reward = i128::MAX; + UpdaterBuilder::new() + .with_starting_exec_gas_price(starting_exec_gas_price) + .with_da_p_component(da_p_component) + .with_da_d_component(da_d_component) + .with_starting_da_gas_price(last_da_gas_price) + .with_total_rewards(large_reward as u128) + .with_known_total_cost(starting_cost as u128) + .with_projected_total_cost(starting_cost as u128) + .with_da_cost_per_byte(latest_gas_per_byte as u128) + .with_last_profit(last_profit, last_last_profit) + .with_da_max_change_percent(u16::MAX) + .with_exec_gas_price_change_percent(0) +} + #[test] fn update_l2_block_data__updates_l2_block() { // given @@ -266,29 +321,6 @@ fn update_l2_block_data__updates_last_and_last_last_profit() { assert_eq!(actual, expected); } -fn positive_profit_updater_builder() -> UpdaterBuilder { - let starting_exec_gas_price = 100; - let last_da_gas_price = 100; - let starting_cost = 500; - let latest_gas_per_byte = 0; // DA is free - let da_p_component = 100; - let da_d_component = 10; - let last_profit = i128::MAX; - let last_last_profit = 0; - let large_reward = i128::MAX; - UpdaterBuilder::new() - .with_starting_exec_gas_price(starting_exec_gas_price) - .with_da_p_component(da_p_component) - .with_da_d_component(da_d_component) - .with_starting_da_gas_price(last_da_gas_price) - .with_total_rewards(large_reward as u128) - .with_known_total_cost(starting_cost as u128) - .with_projected_total_cost(starting_cost as u128) - .with_da_cost_per_byte(latest_gas_per_byte as u128) - .with_last_profit(last_profit, last_last_profit) - .with_da_max_change_percent(u16::MAX) - .with_exec_gas_price_change_percent(0) -} #[test] fn update_l2_block_data__positive_profit_decrease_gas_price() { @@ -664,36 +696,6 @@ fn update_l2_block_data__da_gas_price_wants_to_decrease_will_decrease_if_activit ); } -fn decrease_l2_activity() -> L2ActivityTracker { - let increase = 1; - let hold = 1; - let decrease = 100; - let activity = 50; - let threshold = 50.into(); - L2ActivityTracker::new(increase, hold, decrease, activity, threshold) -} - -fn negative_profit_updater_builder() -> UpdaterBuilder { - let starting_exec_gas_price = 100; - let starting_da_gas_price = 100; - let starting_cost = u128::MAX; - let latest_gas_per_byte = i32::MAX; // DA is very expensive - let da_p_component = 100; - let da_d_component = 10; - let last_profit = i128::MIN; - let last_last_profit = 0; - let smaller_starting_reward = 0; - UpdaterBuilder::new() - .with_starting_exec_gas_price(starting_exec_gas_price) - .with_starting_da_gas_price(starting_da_gas_price) - .with_da_p_component(da_p_component) - .with_da_d_component(da_d_component) - .with_total_rewards(smaller_starting_reward) - .with_known_total_cost(starting_cost) - .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) -} #[test] fn update_l2_block_data__da_gas_price_wants_to_increase_will_decrease_if_activity_in_decrease_range( ) { From 06780f0ad25d8b5d1c5149e3003732f97c540da1 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 17 Oct 2024 17:29:39 +0200 Subject: [PATCH 13/21] Fix analyzer --- .../gas-price-analysis/src/simulation.rs | 7 +++++- crates/fuel-gas-price-algorithm/src/v1.rs | 25 +++++++++---------- .../fuel-gas-price-algorithm/src/v1/tests.rs | 2 +- .../v1/tests/update_l2_block_data_tests.rs | 15 ++++++----- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation.rs index 94d7c06faa3..590952207ba 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation.rs @@ -1,5 +1,8 @@ use super::*; -use fuel_gas_price_algorithm::v1::AlgorithmUpdaterV1; +use fuel_gas_price_algorithm::v1::{ + AlgorithmUpdaterV1, + L2ActivityTracker, +}; use std::{ collections::BTreeMap, num::NonZeroU64, @@ -83,6 +86,7 @@ impl Simulator { ) -> AlgorithmUpdaterV1 { // Scales the gas price internally, value is arbitrary let gas_price_factor = 100; + let always_normal_activity = L2ActivityTracker::new_always_normal(); let updater = AlgorithmUpdaterV1 { min_exec_gas_price: 10, min_da_gas_price: 10, @@ -109,6 +113,7 @@ impl Simulator { da_d_component, last_profit: 0, second_to_last_profit: 0, + l2_activity: always_normal_activity, }; updater } diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index d47c49ecfac..315d987924d 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -171,7 +171,7 @@ pub struct AlgorithmUpdaterV1 { #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] pub struct L2ActivityTracker { /// The _size_ of the range of the activity buffer that will increase the gas price - increase_range_size: u16, + normal_range_size: u16, /// The _size_ of the range of the activity buffer that will hold the gas price hold_range_size: u16, /// The _size_ of the range of the activity buffer that will decrease the gas price @@ -185,7 +185,7 @@ pub struct L2ActivityTracker { /// Designates the intended behavior of the DA gas price based on the activity of the L2 chain pub enum DAGasPriceSafetyMode { /// Should increase DA gas price freely - Increase, + Normal, /// Should not increase the DA gas price Hold, /// Should decrease the DA gas price always @@ -194,16 +194,16 @@ pub enum DAGasPriceSafetyMode { impl L2ActivityTracker { pub fn new_full( - increase_range_size: u16, + normal_range_size: u16, hold_range_size: u16, decrease_range_size: u16, block_activity_threshold: ClampedPercentage, ) -> Self { let activity = decrease_range_size .saturating_add(hold_range_size) - .saturating_add(increase_range_size); + .saturating_add(normal_range_size); Self { - increase_range_size, + normal_range_size, hold_range_size, decrease_range_size, activity, @@ -212,7 +212,7 @@ impl L2ActivityTracker { } pub fn new( - increase_range_size: u16, + normal_range_size: u16, hold_range_size: u16, decrease_range_size: u16, activity: u16, @@ -220,10 +220,10 @@ impl L2ActivityTracker { ) -> Self { let max_activity = decrease_range_size .saturating_add(hold_range_size) - .saturating_add(increase_range_size); + .saturating_add(normal_range_size); let activity = activity.min(max_activity); Self { - increase_range_size, + normal_range_size, hold_range_size, decrease_range_size, activity, @@ -231,8 +231,7 @@ impl L2ActivityTracker { } } - #[cfg(test)] - pub fn new_always_increases() -> Self { + pub fn new_always_normal() -> Self { Self::new_full(1, 0, 0, ClampedPercentage::new(0)) } @@ -242,7 +241,7 @@ impl L2ActivityTracker { .decrease_range_size .saturating_add(self.hold_range_size) { - DAGasPriceSafetyMode::Increase + DAGasPriceSafetyMode::Normal } else if self.activity > self.decrease_range_size { DAGasPriceSafetyMode::Hold } else { @@ -254,7 +253,7 @@ impl L2ActivityTracker { let cap = self .decrease_range_size .saturating_add(self.hold_range_size) - .saturating_add(self.increase_range_size); + .saturating_add(self.normal_range_size); if block_usage < self.block_activity_threshold { self.activity = self.activity.saturating_sub(1); } else { @@ -453,7 +452,7 @@ impl AlgorithmUpdaterV1 { fn da_change_accounting_for_activity(&self, maybe_da_change: i128) -> i128 { if maybe_da_change > 0 { match self.l2_activity.safety_mode() { - DAGasPriceSafetyMode::Increase => maybe_da_change, + DAGasPriceSafetyMode::Normal => maybe_da_change, DAGasPriceSafetyMode::Hold => 0, DAGasPriceSafetyMode::Decrease => self.max_change().saturating_mul(-1), } diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests.rs index efbdb8ec59e..e7150378428 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests.rs @@ -71,7 +71,7 @@ impl UpdaterBuilder { last_profit: 0, second_to_last_profit: 0, da_gas_price_factor: 1, - l2_activity: L2ActivityTracker::new_always_increases(), + l2_activity: L2ActivityTracker::new_always_normal(), } } diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs index 289e020a38c..d5707d4753e 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs @@ -8,12 +8,12 @@ use crate::v1::{ }; fn decrease_l2_activity() -> L2ActivityTracker { - let increase = 1; + let normal = 1; let hold = 1; let decrease = 100; let activity = 50; let threshold = 50.into(); - L2ActivityTracker::new(increase, hold, decrease, activity, threshold) + L2ActivityTracker::new(normal, hold, decrease, activity, threshold) } fn negative_profit_updater_builder() -> UpdaterBuilder { @@ -321,7 +321,6 @@ fn update_l2_block_data__updates_last_and_last_last_profit() { assert_eq!(actual, expected); } - #[test] fn update_l2_block_data__positive_profit_decrease_gas_price() { // given @@ -629,12 +628,12 @@ fn update_l2_block_data__retains_existing_blocks_and_adds_l2_block_to_unrecorded } fn hold_l2_activity() -> L2ActivityTracker { - let increase = 1; + let normal = 1; let hold = 100; let decrease = 1; let activity = 50; let threshold = 50.into(); - L2ActivityTracker::new(increase, hold, decrease, activity, threshold) + L2ActivityTracker::new(normal, hold, decrease, activity, threshold) } #[test] @@ -830,12 +829,12 @@ fn update_l2_block_data__if_activity_at_max_will_stop_increasing() { let starting_exec_gas_price = 100; let exec_gas_price_increase_percent = 10; let threshold = 50; - let increase_range = 1; + let normal_range = 1; let hold_range = 1; let decrease_range = 1; - let starting_activity = increase_range + hold_range + decrease_range; + let starting_activity = normal_range + hold_range + decrease_range; let activity = L2ActivityTracker::new( - increase_range, + normal_range, hold_range, decrease_range, starting_activity, From 3df911aa27d69da040d41dba53f0c5b1a1b89adf Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Sat, 19 Oct 2024 14:03:25 +0200 Subject: [PATCH 14/21] Precompute thresholds, make other adjustments to comments and naming --- crates/fuel-gas-price-algorithm/src/v1.rs | 85 +++++++++++------------ 1 file changed, 39 insertions(+), 46 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 315d987924d..f9ee531d201 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -152,33 +152,37 @@ pub struct AlgorithmUpdaterV1 { pub unrecorded_blocks: BTreeMap, } +/// The `L2ActivityTracker` tracks the chain activity to determine a safety mode for setting the DA price. +/// /// Because the DA gas price can increase even when no-one is using the network, there is a potential /// for a negative feedback loop to occur where the gas price increases, further decreasing activity /// and increasing the gas price. The `L2ActivityTracker` is used to moderate changes to the DA /// gas price based on the activity of the L2 chain. /// -/// For each L2 block, the activity is calculated as a percentage of the block capacity used. If the -/// activity is below a certain threshold, the activity is decreased. The activity exists on a scale -/// between 0 and the sum of the increase, hold, and decrease buffers. +/// The chain activity is a cumulative measure, updated whenever a new block is processed. +/// For each L2 block, the block usage is a percentage of the block capacity used. If the +/// block usage is below a certain threshold, the chain activity is decreased, if above the threshold, +/// the activity is incresed The chain activity exists on a scale +/// between 0 and the sum of the normal, hold, and decrease buffers. /// -/// e.g. if the decrease range is 50, the hold range is 50, and the increase range is 50: +/// e.g. if the decrease buffer size is 20, the hold buffer size is 60, and the increase buffer size is 40: /// -/// 0<-- decrease range -->50<-- hold range -->100<-- increase range -->150 +/// 0<-- decrease buffer -->20<-- hold buffer -->80<-- normal buffer -->120 /// -/// The current activity determines the behavior of the DA gas price. +/// The current chain activity determines the behavior of the DA gas price. /// -/// For healthy behavior, the activity should be in the `increase` range. +/// For healthy behavior, the activity should be in the `normal` range. #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] pub struct L2ActivityTracker { - /// The _size_ of the range of the activity buffer that will increase the gas price - normal_range_size: u16, - /// The _size_ of the range of the activity buffer that will hold the gas price - hold_range_size: u16, - /// The _size_ of the range of the activity buffer that will decrease the gas price - decrease_range_size: u16, + /// The maximum value the chain activity can hit + max_activity: u16, + /// The threshold if the block activity is below, the DA gas price will be held when it would otherwise be increased + hold_activity_threshold: u16, + /// If the chain activity falls below this value, the DA gas price will be decreased when it would otherwise be increased + decrease_activity_threshold: u16, /// The current activity of the L2 chain - activity: u16, - /// The threshold of activity below which the activity will be decreased + chain_activity: u16, + /// The threshold of block activity below which the chain activity will be decreased block_activity_threshold: ClampedPercentage, } @@ -199,14 +203,16 @@ impl L2ActivityTracker { decrease_range_size: u16, block_activity_threshold: ClampedPercentage, ) -> Self { - let activity = decrease_range_size - .saturating_add(hold_range_size) - .saturating_add(normal_range_size); + let decrease_range_size = decrease_range_size; + let decrease_activity_threshold = decrease_range_size; + let hold_activity_threshold = decrease_range_size.saturating_add(hold_range_size); + let max_activity = hold_activity_threshold.saturating_add(normal_range_size); + let chain_activity = max_activity; Self { - normal_range_size, - hold_range_size, - decrease_range_size, - activity, + max_activity, + hold_activity_threshold, + decrease_activity_threshold, + chain_activity, block_activity_threshold, } } @@ -218,17 +224,14 @@ impl L2ActivityTracker { activity: u16, block_activity_threshold: ClampedPercentage, ) -> Self { - let max_activity = decrease_range_size - .saturating_add(hold_range_size) - .saturating_add(normal_range_size); - let activity = activity.min(max_activity); - Self { + let mut tracker = Self::new_full( normal_range_size, hold_range_size, decrease_range_size, - activity, block_activity_threshold, - } + ); + tracker.chain_activity = activity.min(tracker.max_activity); + tracker } pub fn new_always_normal() -> Self { @@ -236,13 +239,9 @@ impl L2ActivityTracker { } pub fn safety_mode(&self) -> DAGasPriceSafetyMode { - if self.activity - > self - .decrease_range_size - .saturating_add(self.hold_range_size) - { + if self.chain_activity > self.hold_activity_threshold { DAGasPriceSafetyMode::Normal - } else if self.activity > self.decrease_range_size { + } else if self.chain_activity > self.decrease_activity_threshold { DAGasPriceSafetyMode::Hold } else { DAGasPriceSafetyMode::Decrease @@ -250,19 +249,16 @@ impl L2ActivityTracker { } pub fn update(&mut self, block_usage: ClampedPercentage) { - let cap = self - .decrease_range_size - .saturating_add(self.hold_range_size) - .saturating_add(self.normal_range_size); if block_usage < self.block_activity_threshold { - self.activity = self.activity.saturating_sub(1); + self.chain_activity = self.chain_activity.saturating_sub(1); } else { - self.activity = self.activity.saturating_add(1).min(cap); + self.chain_activity = + self.chain_activity.saturating_add(1).min(self.max_activity); } } pub fn current_activity(&self) -> u16 { - self.activity + self.chain_activity } } @@ -350,10 +346,7 @@ impl AlgorithmUpdaterV1 { } fn update_activity(&mut self, used: u64, capacity: NonZeroU64) { - let block_activity = used - .saturating_mul(100) - .checked_div(capacity.into()) - .unwrap_or(100); + let block_activity = used.saturating_mul(100).div(capacity); let usage = ClampedPercentage::new(block_activity.try_into().unwrap_or(100)); self.l2_activity.update(usage); } From 1674f13dfeb750dbb88afebfbb7b1f2983cf2227 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Sat, 19 Oct 2024 14:13:36 +0200 Subject: [PATCH 15/21] Add comment about expected behavior of helper function --- crates/fuel-gas-price-algorithm/src/v1.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index f9ee531d201..62c1fe4e002 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -235,7 +235,17 @@ impl L2ActivityTracker { } pub fn new_always_normal() -> Self { - Self::new_full(1, 0, 0, ClampedPercentage::new(0)) + let normal_range_size = 100; + let hold_range_size = 0; + let decrease_range_size = 0; + let percentage = ClampedPercentage::new(0); + Self::new( + normal_range_size, + hold_range_size, + decrease_range_size, + 100, + percentage, + ) } pub fn safety_mode(&self) -> DAGasPriceSafetyMode { @@ -481,6 +491,7 @@ impl AlgorithmUpdaterV1 { pd_change.signum().saturating_mul(clamped_change) } + // Should always be positive fn max_change(&self) -> i128 { let upcast_percent = self.max_da_gas_price_change_percent.into(); self.new_scaled_da_gas_price From 863678d6037c2908afec41a14f2850934f0c3942 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Sat, 19 Oct 2024 14:14:40 +0200 Subject: [PATCH 16/21] Fix spelling --- crates/fuel-gas-price-algorithm/src/v1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 62c1fe4e002..97b13b39a30 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -162,7 +162,7 @@ pub struct AlgorithmUpdaterV1 { /// The chain activity is a cumulative measure, updated whenever a new block is processed. /// For each L2 block, the block usage is a percentage of the block capacity used. If the /// block usage is below a certain threshold, the chain activity is decreased, if above the threshold, -/// the activity is incresed The chain activity exists on a scale +/// the activity is increased The chain activity exists on a scale /// between 0 and the sum of the normal, hold, and decrease buffers. /// /// e.g. if the decrease buffer size is 20, the hold buffer size is 60, and the increase buffer size is 40: From fdbe6da303a6d7ed03acab2116c8e84230b3650b Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Sat, 19 Oct 2024 14:23:51 +0200 Subject: [PATCH 17/21] Appease Clippy-sama --- crates/fuel-gas-price-algorithm/src/v1.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 97b13b39a30..a7b0e757769 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -203,7 +203,6 @@ impl L2ActivityTracker { decrease_range_size: u16, block_activity_threshold: ClampedPercentage, ) -> Self { - let decrease_range_size = decrease_range_size; let decrease_activity_threshold = decrease_range_size; let hold_activity_threshold = decrease_range_size.saturating_add(hold_range_size); let max_activity = hold_activity_threshold.saturating_add(normal_range_size); From c88b557bc6d8239cefa15097b9944d96ae4e598a Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Mon, 21 Oct 2024 11:03:49 +0300 Subject: [PATCH 18/21] Clarify doc comment --- crates/fuel-gas-price-algorithm/src/v1.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index a7b0e757769..7d658bb9942 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -182,7 +182,8 @@ pub struct L2ActivityTracker { decrease_activity_threshold: u16, /// The current activity of the L2 chain chain_activity: u16, - /// The threshold of block activity below which the chain activity will be decreased + /// The threshold of block activity below which the chain activity will be decreased, + /// above or equal it will always increase block_activity_threshold: ClampedPercentage, } From 12ff4fd742ca3fe66eae95da6df53be90afd9b3b Mon Sep 17 00:00:00 2001 From: Mitchell Turner Date: Mon, 21 Oct 2024 15:42:13 +0300 Subject: [PATCH 19/21] Update crates/fuel-gas-price-algorithm/src/v1.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: MÃ¥rten Blankfors --- crates/fuel-gas-price-algorithm/src/v1.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 7d658bb9942..e56286840a7 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -165,9 +165,10 @@ pub struct AlgorithmUpdaterV1 { /// the activity is increased The chain activity exists on a scale /// between 0 and the sum of the normal, hold, and decrease buffers. /// -/// e.g. if the decrease buffer size is 20, the hold buffer size is 60, and the increase buffer size is 40: +/// e.g. if the decrease activity threshold is 20, the hold activity threshold is 80, and the max activity is 120, +/// we'd have the following ranges: /// -/// 0<-- decrease buffer -->20<-- hold buffer -->80<-- normal buffer -->120 +/// 0 <-- decrease range -->20<-- hold range -->80<-- normal range -->120 /// /// The current chain activity determines the behavior of the DA gas price. /// From 0bb7c3f2b7eeda6c06db64620501debc7305167c Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Tue, 22 Oct 2024 15:30:27 +0300 Subject: [PATCH 20/21] Rename hold and decrease to capped and always decrease --- crates/fuel-gas-price-algorithm/src/v1.rs | 38 +++++++++---------- .../v1/tests/update_l2_block_data_tests.rs | 24 ++++++------ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index e56286840a7..75f91190b53 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -163,12 +163,12 @@ pub struct AlgorithmUpdaterV1 { /// For each L2 block, the block usage is a percentage of the block capacity used. If the /// block usage is below a certain threshold, the chain activity is decreased, if above the threshold, /// the activity is increased The chain activity exists on a scale -/// between 0 and the sum of the normal, hold, and decrease buffers. +/// between 0 and the sum of the normal, capped, and decrease buffers. /// -/// e.g. if the decrease activity threshold is 20, the hold activity threshold is 80, and the max activity is 120, +/// e.g. if the decrease activity threshold is 20, the capped activity threshold is 80, and the max activity is 120, /// we'd have the following ranges: /// -/// 0 <-- decrease range -->20<-- hold range -->80<-- normal range -->120 +/// 0 <-- decrease range -->20<-- capped range -->80<-- normal range -->120 /// /// The current chain activity determines the behavior of the DA gas price. /// @@ -178,7 +178,7 @@ pub struct L2ActivityTracker { /// The maximum value the chain activity can hit max_activity: u16, /// The threshold if the block activity is below, the DA gas price will be held when it would otherwise be increased - hold_activity_threshold: u16, + capped_activity_threshold: u16, /// If the chain activity falls below this value, the DA gas price will be decreased when it would otherwise be increased decrease_activity_threshold: u16, /// The current activity of the L2 chain @@ -193,25 +193,25 @@ pub enum DAGasPriceSafetyMode { /// Should increase DA gas price freely Normal, /// Should not increase the DA gas price - Hold, + Capped, /// Should decrease the DA gas price always - Decrease, + AlwaysDecrease, } impl L2ActivityTracker { pub fn new_full( normal_range_size: u16, - hold_range_size: u16, + capped_range_size: u16, decrease_range_size: u16, block_activity_threshold: ClampedPercentage, ) -> Self { let decrease_activity_threshold = decrease_range_size; - let hold_activity_threshold = decrease_range_size.saturating_add(hold_range_size); - let max_activity = hold_activity_threshold.saturating_add(normal_range_size); + let capped_activity_threshold = decrease_range_size.saturating_add(capped_range_size); + let max_activity = capped_activity_threshold.saturating_add(normal_range_size); let chain_activity = max_activity; Self { max_activity, - hold_activity_threshold, + capped_activity_threshold, decrease_activity_threshold, chain_activity, block_activity_threshold, @@ -220,14 +220,14 @@ impl L2ActivityTracker { pub fn new( normal_range_size: u16, - hold_range_size: u16, + capped_range_size: u16, decrease_range_size: u16, activity: u16, block_activity_threshold: ClampedPercentage, ) -> Self { let mut tracker = Self::new_full( normal_range_size, - hold_range_size, + capped_range_size, decrease_range_size, block_activity_threshold, ); @@ -237,12 +237,12 @@ impl L2ActivityTracker { pub fn new_always_normal() -> Self { let normal_range_size = 100; - let hold_range_size = 0; + let capped_range_size = 0; let decrease_range_size = 0; let percentage = ClampedPercentage::new(0); Self::new( normal_range_size, - hold_range_size, + capped_range_size, decrease_range_size, 100, percentage, @@ -250,12 +250,12 @@ impl L2ActivityTracker { } pub fn safety_mode(&self) -> DAGasPriceSafetyMode { - if self.chain_activity > self.hold_activity_threshold { + if self.chain_activity > self.capped_activity_threshold { DAGasPriceSafetyMode::Normal } else if self.chain_activity > self.decrease_activity_threshold { - DAGasPriceSafetyMode::Hold + DAGasPriceSafetyMode::Capped } else { - DAGasPriceSafetyMode::Decrease + DAGasPriceSafetyMode::AlwaysDecrease } } @@ -457,8 +457,8 @@ impl AlgorithmUpdaterV1 { if maybe_da_change > 0 { match self.l2_activity.safety_mode() { DAGasPriceSafetyMode::Normal => maybe_da_change, - DAGasPriceSafetyMode::Hold => 0, - DAGasPriceSafetyMode::Decrease => self.max_change().saturating_mul(-1), + DAGasPriceSafetyMode::Capped => 0, + DAGasPriceSafetyMode::AlwaysDecrease => self.max_change().saturating_mul(-1), } } else { maybe_da_change diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs index d5707d4753e..5cb56bbde2d 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_l2_block_data_tests.rs @@ -9,11 +9,11 @@ use crate::v1::{ fn decrease_l2_activity() -> L2ActivityTracker { let normal = 1; - let hold = 1; + let capped = 1; let decrease = 100; let activity = 50; let threshold = 50.into(); - L2ActivityTracker::new(normal, hold, decrease, activity, threshold) + L2ActivityTracker::new(normal, capped, decrease, activity, threshold) } fn negative_profit_updater_builder() -> UpdaterBuilder { @@ -627,22 +627,22 @@ fn update_l2_block_data__retains_existing_blocks_and_adds_l2_block_to_unrecorded assert!(contains_preexisting_block_bytes); } -fn hold_l2_activity() -> L2ActivityTracker { +fn capped_l2_activity_tracker() -> L2ActivityTracker { let normal = 1; - let hold = 100; + let capped = 100; let decrease = 1; let activity = 50; let threshold = 50.into(); - L2ActivityTracker::new(normal, hold, decrease, activity, threshold) + L2ActivityTracker::new(normal, capped, decrease, activity, threshold) } #[test] fn update_l2_block_data__da_gas_price_wants_to_increase_will_hold_if_activity_in_hold_range( ) { // given - let hold_activity = hold_l2_activity(); + let capped_activity = capped_l2_activity_tracker(); let mut updater = negative_profit_updater_builder() - .with_activity(hold_activity) + .with_activity(capped_activity) .build(); let algo = updater.algorithm(); let old_gas_price = algo.calculate(); @@ -667,9 +667,9 @@ fn update_l2_block_data__da_gas_price_wants_to_increase_will_hold_if_activity_in fn update_l2_block_data__da_gas_price_wants_to_decrease_will_decrease_if_activity_in_hold_range( ) { // given - let hold_activity = hold_l2_activity(); + let capped_activity = capped_l2_activity_tracker(); let mut updater = positive_profit_updater_builder() - .with_activity(hold_activity) + .with_activity(capped_activity) .build(); let old_gas_price = updater.algorithm().calculate(); @@ -830,12 +830,12 @@ fn update_l2_block_data__if_activity_at_max_will_stop_increasing() { let exec_gas_price_increase_percent = 10; let threshold = 50; let normal_range = 1; - let hold_range = 1; + let capped_range = 1; let decrease_range = 1; - let starting_activity = normal_range + hold_range + decrease_range; + let starting_activity = normal_range + capped_range + decrease_range; let activity = L2ActivityTracker::new( normal_range, - hold_range, + capped_range, decrease_range, starting_activity, 50.into(), From f8d3aca3f8e1d3676501330aab164ed2f63d5480 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Tue, 22 Oct 2024 15:42:43 +0300 Subject: [PATCH 21/21] Add TODO for gas price types --- crates/fuel-gas-price-algorithm/src/v1.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 75f91190b53..9a9da066fee 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -26,6 +26,8 @@ pub enum Error { L2BlockExpectedNotFound(u32), } +// TODO: separate exec gas price and DA gas price into newtypes for clarity +// https://github.com/FuelLabs/fuel-core/issues/2382 #[derive(Debug, Clone, PartialEq)] pub struct AlgorithmV1 { /// The gas price for to cover the execution of the next block @@ -206,7 +208,8 @@ impl L2ActivityTracker { block_activity_threshold: ClampedPercentage, ) -> Self { let decrease_activity_threshold = decrease_range_size; - let capped_activity_threshold = decrease_range_size.saturating_add(capped_range_size); + let capped_activity_threshold = + decrease_range_size.saturating_add(capped_range_size); let max_activity = capped_activity_threshold.saturating_add(normal_range_size); let chain_activity = max_activity; Self { @@ -458,7 +461,9 @@ impl AlgorithmUpdaterV1 { match self.l2_activity.safety_mode() { DAGasPriceSafetyMode::Normal => maybe_da_change, DAGasPriceSafetyMode::Capped => 0, - DAGasPriceSafetyMode::AlwaysDecrease => self.max_change().saturating_mul(-1), + DAGasPriceSafetyMode::AlwaysDecrease => { + self.max_change().saturating_mul(-1) + } } } else { maybe_da_change