From ffa4fdb6468e57c99e190c90ec8b1b1424274478 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Wed, 25 Sep 2024 16:02:31 +0200 Subject: [PATCH 01/17] Fix DA tests to expect unrecorded blocks to exist and to take the bytes from there --- .../v1/tests/update_da_record_data_tests.txt | 8 + crates/fuel-gas-price-algorithm/src/v1.rs | 38 ++-- .../fuel-gas-price-algorithm/src/v1/tests.rs | 6 +- .../v1/tests/update_da_record_data_tests.rs | 178 +++++++++++------- .../v1/tests/update_l2_block_data_tests.rs | 13 +- 5 files changed, 145 insertions(+), 98 deletions(-) create mode 100644 crates/fuel-gas-price-algorithm/proptest-regressions/v1/tests/update_da_record_data_tests.txt diff --git a/crates/fuel-gas-price-algorithm/proptest-regressions/v1/tests/update_da_record_data_tests.txt b/crates/fuel-gas-price-algorithm/proptest-regressions/v1/tests/update_da_record_data_tests.txt new file mode 100644 index 00000000000..43794a5b9f6 --- /dev/null +++ b/crates/fuel-gas-price-algorithm/proptest-regressions/v1/tests/update_da_record_data_tests.txt @@ -0,0 +1,8 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc cc5569d588ba605fce71abb30155cf5234665a1fd8526128275b6d0f34db8a17 # shrinks to (cost, reward, blocks) = (0, 74140008300, [RecordedBlock { height: 1, block_cost: 74140008300 }]) +cc 15ea0d6f70d5b90b95085b130c444d0896286306336859b3e0199ff134b178ff # shrinks to (cost, reward, blocks) = (74140008300, 0, [RecordedBlock { height: 1, block_cost: 74140008300 }]) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index ca3b58f22f9..3daf62fe9cb 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -1,11 +1,11 @@ +use crate::utils::cumulative_percentage_change; use std::{ cmp::max, + collections::HashMap, num::NonZeroU64, ops::Div, }; -use crate::utils::cumulative_percentage_change; - #[cfg(test)] mod tests; @@ -19,6 +19,8 @@ pub enum Error { CouldNotCalculateCostPerByte { bytes: u64, cost: u64 }, #[error("Failed to include L2 block data: {0}")] FailedTooIncludeL2BlockData(String), + #[error("L2 block expected but not found in unrecorded blocks: {0}")] + L2BlockExpectedNotFound(u32), } #[derive(Debug, Clone, PartialEq)] @@ -94,6 +96,8 @@ impl AlgorithmV1 { /// The DA portion also uses a moving average of the profits over the last `avg_window` blocks /// instead of the actual profit. Setting the `avg_window` to 1 will effectively disable the /// moving average. +type Height = u32; +type Bytes = u64; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] pub struct AlgorithmUpdaterV1 { // Execution @@ -144,8 +148,9 @@ 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, + /// The unrecorded blocks that are used to calculate the projected cost of recording blocks - pub unrecorded_blocks: Vec, + pub unrecorded_blocks: HashMap, } /// A value that represents a value between 0 and 100. Higher values are clamped to 100 @@ -179,7 +184,7 @@ impl core::ops::Deref for ClampedPercentage { #[derive(Debug, Clone)] pub struct RecordedBlock { pub height: u32, - pub block_bytes: u64, + // pub block_bytes: u64, pub block_cost: u64, } @@ -195,7 +200,7 @@ impl AlgorithmUpdaterV1 { blocks: &[RecordedBlock], ) -> Result<(), Error> { for block in blocks { - self.da_block_update(block.height, block.block_bytes, block.block_cost)?; + self.da_block_update(block.height, block.block_cost)?; } self.recalculate_projected_cost(); self.normalize_rewards_and_costs(); @@ -234,10 +239,7 @@ impl AlgorithmUpdaterV1 { self.update_da_gas_price(); // metadata - self.unrecorded_blocks.push(BlockBytes { - height, - block_bytes, - }); + self.unrecorded_blocks.insert(height, block_bytes); Ok(()) } } @@ -372,12 +374,7 @@ impl AlgorithmUpdaterV1 { .saturating_div(100) } - fn da_block_update( - &mut self, - height: u32, - block_bytes: u64, - block_cost: u64, - ) -> Result<(), Error> { + fn da_block_update(&mut self, height: u32, block_cost: u64) -> Result<(), Error> { let expected = self.da_recorded_block_height.saturating_add(1); if height != expected { Err(Error::SkippedDABlock { @@ -385,6 +382,10 @@ impl AlgorithmUpdaterV1 { got: height, }) } else { + let block_bytes = self + .unrecorded_blocks + .remove(&height) + .ok_or(Error::L2BlockExpectedNotFound(height))?; let new_cost_per_byte: u128 = (block_cost as u128) .checked_div(block_bytes as u128) .ok_or(Error::CouldNotCalculateCostPerByte { @@ -402,15 +403,12 @@ impl AlgorithmUpdaterV1 { } fn recalculate_projected_cost(&mut self) { - // remove all blocks that have been recorded - self.unrecorded_blocks - .retain(|block| block.height > self.da_recorded_block_height); // add the cost of the remaining blocks let projection_portion: u128 = self .unrecorded_blocks .iter() - .map(|block| { - (block.block_bytes as u128).saturating_mul(self.latest_da_cost_per_byte) + .map(|(_, &bytes)| { + (bytes as u128).saturating_mul(self.latest_da_cost_per_byte) }) .sum(); self.projected_total_da_cost = self diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests.rs index 707173baca4..00622b921e0 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests.rs @@ -175,7 +175,11 @@ impl UpdaterBuilder { latest_da_cost_per_byte: self.da_cost_per_byte, projected_total_da_cost: self.project_total_cost, latest_known_total_da_cost_excess: self.latest_known_total_cost, - unrecorded_blocks: self.unrecorded_blocks, + unrecorded_blocks: self + .unrecorded_blocks + .iter() + .map(|b| (b.height, b.block_bytes)) + .collect(), last_profit: self.last_profit, second_to_last_profit: self.second_to_last_profit, min_da_gas_price: self.min_da_gas_price, diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs index 8df873854f3..ee884ae269e 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs @@ -15,25 +15,33 @@ use rand::SeedableRng; fn update_da_record_data__increases_block() { // given let da_recorded_block_height = 0; - let mut updater = UpdaterBuilder::new() - .with_da_recorded_block_height(da_recorded_block_height) - .build(); - - let blocks = vec![ + let recorded_blocks = vec![ RecordedBlock { height: 1, - block_bytes: 1000, block_cost: 100, }, RecordedBlock { height: 2, - block_bytes: 1000, block_cost: 100, }, ]; + let unrecorded_blocks = vec![ + BlockBytes { + height: 1, + block_bytes: 1000, + }, + BlockBytes { + height: 2, + block_bytes: 2000, + }, + ]; + let mut updater = UpdaterBuilder::new() + .with_da_recorded_block_height(da_recorded_block_height) + .with_unrecorded_blocks(unrecorded_blocks) + .build(); // when - updater.update_da_record_data(&blocks).unwrap(); + updater.update_da_record_data(&recorded_blocks).unwrap(); // then let expected = 2; @@ -45,22 +53,24 @@ fn update_da_record_data__increases_block() { fn update_da_record_data__throws_error_if_out_of_order() { // given let da_recorded_block_height = 0; - let mut updater = UpdaterBuilder::new() - .with_da_recorded_block_height(da_recorded_block_height) - .build(); - let blocks = vec![ RecordedBlock { height: 1, - block_bytes: 1000, block_cost: 100, }, RecordedBlock { height: 3, - block_bytes: 1000, block_cost: 100, }, ]; + let unrecorded_blocks = vec![BlockBytes { + height: 1, + block_bytes: 1000, + }]; + let mut updater = UpdaterBuilder::new() + .with_da_recorded_block_height(da_recorded_block_height) + .with_unrecorded_blocks(unrecorded_blocks) + .build(); // when let actual_error = updater.update_da_record_data(&blocks).unwrap_err(); @@ -77,16 +87,20 @@ fn update_da_record_data__throws_error_if_out_of_order() { fn update_da_record_data__updates_cost_per_byte() { // given let da_cost_per_byte = 20; + let block_bytes = 1000; + let unrecorded_blocks = vec![BlockBytes { + height: 1, + block_bytes, + }]; let mut updater = UpdaterBuilder::new() .with_da_cost_per_byte(da_cost_per_byte) + .with_unrecorded_blocks(unrecorded_blocks) .build(); - let block_bytes = 1000; let new_cost_per_byte = 100; let block_cost = block_bytes * new_cost_per_byte; let blocks = vec![RecordedBlock { height: 1, - block_bytes, block_cost, }]; // when @@ -106,30 +120,41 @@ fn update_da_record_data__updates_known_total_cost() { let l2_block_height = 15; let projected_total_cost = 2000; let known_total_cost = 1500; + let unrecorded_blocks = vec![ + BlockBytes { + height: 11, + block_bytes: 1000, + }, + BlockBytes { + height: 12, + block_bytes: 2000, + }, + BlockBytes { + height: 13, + block_bytes: 1500, + }, + ]; let mut updater = UpdaterBuilder::new() .with_da_cost_per_byte(da_cost_per_byte) .with_da_recorded_block_height(da_recorded_block_height) .with_l2_block_height(l2_block_height) .with_projected_total_cost(projected_total_cost) .with_known_total_cost(known_total_cost) + .with_unrecorded_blocks(unrecorded_blocks) .build(); - let block_bytes = 1000; let block_cost = 100; let blocks = vec![ RecordedBlock { height: 11, - block_bytes, block_cost, }, RecordedBlock { height: 12, - block_bytes, block_cost, }, RecordedBlock { height: 13, - block_bytes, block_cost, }, ]; @@ -184,17 +209,14 @@ fn update_da_record_data__if_da_height_matches_l2_height_prjected_and_known_matc let blocks = vec![ RecordedBlock { height: 11, - block_bytes, block_cost, }, RecordedBlock { height: 12, - block_bytes, block_cost, }, RecordedBlock { height: 13, - block_bytes, block_cost, }, ]; @@ -216,7 +238,8 @@ fn update_da_record_data__da_block_updates_projected_total_cost_with_known_and_g let da_cost_per_byte = 20; let da_recorded_block_height = 10; let l2_block_height = 15; - let known_total_cost = 1500; + let original_known_total_cost = 1500; + let block_bytes = 1000; let mut unrecorded_blocks = vec![ BlockBytes { height: 11, @@ -235,54 +258,48 @@ fn update_da_record_data__da_block_updates_projected_total_cost_with_known_and_g let remaining = vec![ BlockBytes { height: 14, - block_bytes: 1200, + block_bytes, }, BlockBytes { height: 15, - block_bytes: 3000, + block_bytes, }, ]; + let to_be_removed = unrecorded_blocks.clone(); unrecorded_blocks.extend(remaining.clone()); let guessed_cost: u64 = unrecorded_blocks .iter() .map(|block| block.block_bytes * da_cost_per_byte) .sum(); - let projected_total_cost = known_total_cost + guessed_cost; + 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_recorded_block_height(da_recorded_block_height) .with_l2_block_height(l2_block_height) .with_projected_total_cost(projected_total_cost as u128) - .with_known_total_cost(known_total_cost as u128) + .with_known_total_cost(original_known_total_cost as u128) .with_unrecorded_blocks(unrecorded_blocks) .build(); - let block_bytes = 1000; let new_cost_per_byte = 100; - let block_cost = block_bytes * new_cost_per_byte; - let blocks = vec![ - RecordedBlock { - height: 11, - block_bytes, - block_cost, - }, - RecordedBlock { - height: 12, - block_bytes, - block_cost, - }, - RecordedBlock { - height: 13, - block_bytes, - block_cost, - }, - ]; + let recorded_blocks = to_be_removed + .iter() + .map(|block| RecordedBlock { + height: block.height, + block_cost: block.block_bytes * new_cost_per_byte, + }) + .collect::>(); + // when - updater.update_da_record_data(&blocks).unwrap(); + updater.update_da_record_data(&recorded_blocks).unwrap(); // then let actual = updater.projected_total_da_cost; - let new_known_total_cost = known_total_cost + 3 * block_cost; + let new_known_total_cost = original_known_total_cost + + recorded_blocks + .iter() + .map(|block| block.block_cost) + .sum::(); let guessed_part: u64 = remaining .iter() .map(|block| block.block_bytes * new_cost_per_byte) @@ -292,40 +309,46 @@ fn update_da_record_data__da_block_updates_projected_total_cost_with_known_and_g } prop_compose! { - fn arb_vec_of_da_blocks()(last_da_block: u32, count in 1..123usize, rng_seed: u64) -> Vec { + fn arb_vec_of_da_blocks()(last_da_block: u32, count in 1..123usize, rng_seed: u64) -> (Vec, Vec) { let rng = &mut rand::rngs::StdRng::seed_from_u64(rng_seed); - let mut blocks = Vec::with_capacity(count); + let mut unrecorded_blocks = Vec::with_capacity(count); + let mut recorded_blocks = Vec::with_capacity(count); for i in 0..count { + let height = last_da_block + 1 + i as u32; let block_bytes = rng.gen_range(100..131_072); let cost_per_byte = rng.gen_range(1..1000000); let block_cost = block_bytes * cost_per_byte; - blocks.push(RecordedBlock { - height: last_da_block + 1 + i as u32, + unrecorded_blocks.push(BlockBytes { + height, block_bytes, + }); + recorded_blocks.push(RecordedBlock { + height, block_cost, }); } - blocks + (unrecorded_blocks, recorded_blocks) } } prop_compose! { - fn reward_greater_than_cost_with_da_blocks()(cost: u64, extra: u64, blocks in arb_vec_of_da_blocks()) -> (u128, u128, Vec) { - let cost_from_blocks = blocks.iter().map(|block| block.block_cost as u128).sum::(); + fn reward_greater_than_cost_with_da_blocks()(cost: u64, extra: u64, (unrecorded_blocks, recorded_blocks) in arb_vec_of_da_blocks()) -> (u128, u128, Vec, Vec) { + let cost_from_blocks = recorded_blocks.iter().map(|block| block.block_cost as u128).sum::(); let reward = cost as u128 + cost_from_blocks + extra as u128; - (cost as u128, reward, blocks) + (cost as u128, reward, unrecorded_blocks, recorded_blocks) } } proptest! { #[test] fn update_da_record_data__when_reward_is_greater_than_cost_will_zero_cost_and_subtract_from_reward( - (cost, reward, blocks) in reward_greater_than_cost_with_da_blocks() + (cost, reward, unrecorded_blocks, recorded_blocks) in reward_greater_than_cost_with_da_blocks() ) { _update_da_record_data__when_reward_is_greater_than_cost_will_zero_cost_and_subtract_from_reward( cost, reward, - blocks + unrecorded_blocks, + recorded_blocks, ) } } @@ -333,11 +356,12 @@ proptest! { fn _update_da_record_data__when_reward_is_greater_than_cost_will_zero_cost_and_subtract_from_reward( known_total_cost: u128, total_rewards: u128, - blocks: Vec, + unrecorded_blocks: Vec, + recorded_blocks: Vec, ) { // given let da_cost_per_byte = 20; - let da_recorded_block_height = blocks.first().unwrap().height - 1; + let da_recorded_block_height = recorded_blocks.first().unwrap().height - 1; let l2_block_height = 15; let mut updater = UpdaterBuilder::new() .with_da_cost_per_byte(da_cost_per_byte) @@ -345,12 +369,16 @@ fn _update_da_record_data__when_reward_is_greater_than_cost_will_zero_cost_and_s .with_l2_block_height(l2_block_height) .with_known_total_cost(known_total_cost) .with_total_rewards(total_rewards) + .with_unrecorded_blocks(unrecorded_blocks) .build(); - let new_costs = blocks.iter().map(|block| block.block_cost).sum::(); + let new_costs = recorded_blocks + .iter() + .map(|block| block.block_cost) + .sum::(); // when - updater.update_da_record_data(&blocks).unwrap(); + updater.update_da_record_data(&recorded_blocks).unwrap(); // then let expected = total_rewards - new_costs as u128 - known_total_cost; @@ -363,22 +391,23 @@ fn _update_da_record_data__when_reward_is_greater_than_cost_will_zero_cost_and_s } prop_compose! { - fn cost_greater_than_reward_with_da_blocks()(reward: u64, extra: u64, blocks in arb_vec_of_da_blocks()) -> (u128, u128, Vec) { - let cost_from_blocks = blocks.iter().map(|block| block.block_cost as u128).sum::(); + fn cost_greater_than_reward_with_da_blocks()(reward: u64, extra: u64, (unrecorded_blocks, recorded_blocks) in arb_vec_of_da_blocks()) -> (u128, u128, Vec, Vec) { + let cost_from_blocks = recorded_blocks.iter().map(|block| block.block_cost as u128).sum::(); let cost = reward as u128 + cost_from_blocks + extra as u128; - (cost, reward as u128, blocks) + (cost, reward as u128, unrecorded_blocks, recorded_blocks) } } proptest! { #[test] fn update_da_record_data__when_cost_is_greater_than_reward_will_zero_reward_and_subtract_from_cost( - (cost, reward, blocks) in cost_greater_than_reward_with_da_blocks() + (cost, reward, unrecorded_blocks, recorded_blocks) in cost_greater_than_reward_with_da_blocks() ) { _update_da_record_data__when_cost_is_greater_than_reward_will_zero_reward_and_subtract_from_cost( cost, reward, - blocks + unrecorded_blocks, + recorded_blocks, ) } } @@ -386,11 +415,12 @@ proptest! { fn _update_da_record_data__when_cost_is_greater_than_reward_will_zero_reward_and_subtract_from_cost( known_total_cost: u128, total_rewards: u128, - blocks: Vec, + unrecorded_blocks: Vec, + recorded_blocks: Vec, ) { // given let da_cost_per_byte = 20; - let da_recorded_block_height = blocks.first().unwrap().height - 1; + let da_recorded_block_height = recorded_blocks.first().unwrap().height - 1; let l2_block_height = 15; let mut updater = UpdaterBuilder::new() .with_da_cost_per_byte(da_cost_per_byte) @@ -398,12 +428,16 @@ fn _update_da_record_data__when_cost_is_greater_than_reward_will_zero_reward_and .with_l2_block_height(l2_block_height) .with_known_total_cost(known_total_cost) .with_total_rewards(total_rewards) + .with_unrecorded_blocks(unrecorded_blocks) .build(); - let new_costs = blocks.iter().map(|block| block.block_cost).sum::(); + let new_costs = recorded_blocks + .iter() + .map(|block| block.block_cost) + .sum::(); // when - updater.update_da_record_data(&blocks).unwrap(); + updater.update_da_record_data(&recorded_blocks).unwrap(); // then let expected = 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 1ad9a1aacad..9d6faf7ac25 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 @@ -563,8 +563,9 @@ fn update_l2_block_data__adds_l2_block_to_unrecorded_blocks() { height, block_bytes, }; - let contains_block_bytes = updater.unrecorded_blocks.contains(&block_bytes); - assert!(contains_block_bytes); + let expected = block_bytes.block_bytes; + let actual = updater.unrecorded_blocks.get(&block_bytes.height).unwrap(); + assert_eq!(expected, *actual); } #[test] @@ -598,10 +599,12 @@ fn update_l2_block_data__retains_existing_blocks_and_adds_l2_block_to_unrecorded height, block_bytes, }; - let contains_block_bytes = updater.unrecorded_blocks.contains(&block_bytes); + let contains_block_bytes = + updater.unrecorded_blocks.contains_key(&block_bytes.height); assert!(contains_block_bytes); - let contains_preexisting_block_bytes = - updater.unrecorded_blocks.contains(&preexisting_block); + let contains_preexisting_block_bytes = updater + .unrecorded_blocks + .contains_key(&preexisting_block.height); assert!(contains_preexisting_block_bytes); } From fc3cf91ee4b61444db59fa6f35caee0066dc63e5 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Wed, 25 Sep 2024 16:08:27 +0200 Subject: [PATCH 02/17] Remove regression for unimportant failure mode --- .../v1/tests/update_da_record_data_tests.txt | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 crates/fuel-gas-price-algorithm/proptest-regressions/v1/tests/update_da_record_data_tests.txt diff --git a/crates/fuel-gas-price-algorithm/proptest-regressions/v1/tests/update_da_record_data_tests.txt b/crates/fuel-gas-price-algorithm/proptest-regressions/v1/tests/update_da_record_data_tests.txt deleted file mode 100644 index 43794a5b9f6..00000000000 --- a/crates/fuel-gas-price-algorithm/proptest-regressions/v1/tests/update_da_record_data_tests.txt +++ /dev/null @@ -1,8 +0,0 @@ -# Seeds for failure cases proptest has generated in the past. It is -# automatically read and these particular cases re-run before any -# novel cases are generated. -# -# It is recommended to check this file in to source control so that -# everyone who runs the test benefits from these saved cases. -cc cc5569d588ba605fce71abb30155cf5234665a1fd8526128275b6d0f34db8a17 # shrinks to (cost, reward, blocks) = (0, 74140008300, [RecordedBlock { height: 1, block_cost: 74140008300 }]) -cc 15ea0d6f70d5b90b95085b130c444d0896286306336859b3e0199ff134b178ff # shrinks to (cost, reward, blocks) = (74140008300, 0, [RecordedBlock { height: 1, block_cost: 74140008300 }]) From 8cd972f4abec95a59e59fdff6795b70e722fe439 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Wed, 25 Sep 2024 16:16:09 +0200 Subject: [PATCH 03/17] Move struct that is only used in tests --- crates/fuel-gas-price-algorithm/src/v1.rs | 6 ------ crates/fuel-gas-price-algorithm/src/v1/tests.rs | 11 +++++++---- .../src/v1/tests/update_da_record_data_tests.rs | 6 ++++-- .../src/v1/tests/update_l2_block_data_tests.rs | 6 ++++-- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 577d761e86d..d5b0f318c4a 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -188,12 +188,6 @@ pub struct RecordedBlock { pub block_cost: u64, } -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] -pub struct BlockBytes { - pub height: u32, - pub block_bytes: u64, -} - impl AlgorithmUpdaterV1 { pub fn update_da_record_data( &mut self, diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests.rs index 00622b921e0..0aca738a3f0 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests.rs @@ -2,10 +2,7 @@ #![allow(clippy::arithmetic_side_effects)] #![allow(clippy::cast_possible_truncation)] -use crate::v1::{ - AlgorithmUpdaterV1, - BlockBytes, -}; +use crate::v1::AlgorithmUpdaterV1; #[cfg(test)] mod algorithm_v1_tests; @@ -14,6 +11,12 @@ mod update_da_record_data_tests; #[cfg(test)] mod update_l2_block_data_tests; +#[derive(Debug, Clone)] +pub struct BlockBytes { + pub height: u32, + pub block_bytes: u64, +} + pub struct UpdaterBuilder { min_exec_gas_price: u64, min_da_gas_price: u64, diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs index 377782ab51b..15e0d3f03f9 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs @@ -1,6 +1,8 @@ use crate::v1::{ - tests::UpdaterBuilder, - BlockBytes, + tests::{ + BlockBytes, + UpdaterBuilder, + }, Error, RecordedBlock, }; 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 9d6faf7ac25..c32a48728ed 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 @@ -1,6 +1,8 @@ use crate::v1::{ - tests::UpdaterBuilder, - BlockBytes, + tests::{ + BlockBytes, + UpdaterBuilder, + }, Error, }; From 8fd66166cdd45c94b884b6e7e2a6a254b15935c5 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 26 Sep 2024 10:33:55 +0200 Subject: [PATCH 04/17] Remove unused struct --- crates/fuel-gas-price-algorithm/src/v0.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v0.rs b/crates/fuel-gas-price-algorithm/src/v0.rs index 494c9045a88..418e6619c7e 100644 --- a/crates/fuel-gas-price-algorithm/src/v0.rs +++ b/crates/fuel-gas-price-algorithm/src/v0.rs @@ -66,12 +66,6 @@ pub struct AlgorithmUpdaterV0 { pub l2_block_fullness_threshold_percent: u64, } -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] -pub struct BlockBytes { - pub height: u32, - pub block_bytes: u64, -} - impl AlgorithmUpdaterV0 { pub fn new( new_exec_price: u64, From 49e521e6e7bd9bbf566e9c827e4fd0d40d85da79 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 26 Sep 2024 11:39:09 +0200 Subject: [PATCH 05/17] Change interface of `update_da_record_data` --- crates/fuel-gas-price-algorithm/src/v1.rs | 68 +++++--- .../v1/tests/update_da_record_data_tests.rs | 160 ++++++++---------- 2 files changed, 119 insertions(+), 109 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index d5b0f318c4a..48ac87b945d 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -3,7 +3,10 @@ use std::{ cmp::max, collections::HashMap, num::NonZeroU64, - ops::Div, + ops::{ + Div, + Range, + }, }; #[cfg(test)] @@ -16,7 +19,7 @@ pub enum Error { #[error("Skipped DA block update: expected {expected:?}, got {got:?}")] SkippedDABlock { expected: u32, got: u32 }, #[error("Could not calculate cost per byte: {bytes:?} bytes, {cost:?} cost")] - CouldNotCalculateCostPerByte { bytes: u64, cost: u64 }, + CouldNotCalculateCostPerByte { bytes: u128, cost: u128 }, #[error("Failed to include L2 block data: {0}")] FailedTooIncludeL2BlockData(String), #[error("L2 block expected but not found in unrecorded blocks: {0}")] @@ -191,13 +194,14 @@ pub struct RecordedBlock { impl AlgorithmUpdaterV1 { pub fn update_da_record_data( &mut self, - blocks: &[RecordedBlock], + height_range: Range, + range_cost: u128, ) -> Result<(), Error> { - for block in blocks { - self.da_block_update(block.height, block.block_cost)?; + if height_range.len() != 0 { + self.da_block_update(height_range, range_cost)?; + self.recalculate_projected_cost(); + self.normalize_rewards_and_costs(); } - self.recalculate_projected_cost(); - self.normalize_rewards_and_costs(); Ok(()) } @@ -368,34 +372,52 @@ impl AlgorithmUpdaterV1 { .saturating_div(100) } - fn da_block_update(&mut self, height: u32, block_cost: u64) -> Result<(), Error> { + fn da_block_update( + &mut self, + height_range: Range, + range_cost: u128, + ) -> Result<(), Error> { let expected = self.da_recorded_block_height.saturating_add(1); - if height != expected { + let first = height_range.start; + if first != expected { Err(Error::SkippedDABlock { - expected: self.da_recorded_block_height.saturating_add(1), - got: height, + expected, + got: first, }) } else { - let block_bytes = self - .unrecorded_blocks - .remove(&height) - .ok_or(Error::L2BlockExpectedNotFound(height))?; - let new_cost_per_byte: u128 = (block_cost as u128) - .checked_div(block_bytes as u128) - .ok_or(Error::CouldNotCalculateCostPerByte { - bytes: block_bytes, - cost: block_cost, - })?; - self.da_recorded_block_height = height; + let last = height_range.end - 1; + let range_bytes = self.drain_l2_block_bytes_for_range(height_range)?; + let new_cost_per_byte: u128 = range_cost.checked_div(range_bytes).ok_or( + Error::CouldNotCalculateCostPerByte { + bytes: range_bytes, + cost: range_cost, + }, + )?; + self.da_recorded_block_height = last; let new_block_cost = self .latest_known_total_da_cost_excess - .saturating_add(block_cost as u128); + .saturating_add(range_cost); self.latest_known_total_da_cost_excess = new_block_cost; self.latest_da_cost_per_byte = new_cost_per_byte; Ok(()) } } + fn drain_l2_block_bytes_for_range( + &mut self, + height_range: Range, + ) -> Result { + let mut total: u128 = 0; + for height in height_range { + let bytes = self + .unrecorded_blocks + .remove(&height) + .ok_or(Error::L2BlockExpectedNotFound(height))?; + total = total.saturating_add(bytes as u128); + } + Ok(total) + } + fn recalculate_projected_cost(&mut self) { // add the cost of the remaining blocks let projection_portion: u128 = self diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs index 15e0d3f03f9..cf01368b82d 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs @@ -17,16 +17,8 @@ use rand::SeedableRng; fn update_da_record_data__increases_block() { // given let da_recorded_block_height = 0; - let recorded_blocks = vec![ - RecordedBlock { - height: 1, - block_cost: 100, - }, - RecordedBlock { - height: 2, - block_cost: 100, - }, - ]; + let recorded_range = 1u32..3; + let recorded_cost = 200; let unrecorded_blocks = vec![ BlockBytes { height: 1, @@ -43,28 +35,22 @@ fn update_da_record_data__increases_block() { .build(); // when - updater.update_da_record_data(&recorded_blocks).unwrap(); + updater + .update_da_record_data(recorded_range, recorded_cost) + .unwrap(); // then let expected = 2; let actual = updater.da_recorded_block_height; - assert_eq!(actual, expected); + assert_eq!(expected, actual); } #[test] fn update_da_record_data__throws_error_if_out_of_order() { // given let da_recorded_block_height = 0; - let blocks = vec![ - RecordedBlock { - height: 1, - block_cost: 100, - }, - RecordedBlock { - height: 3, - block_cost: 100, - }, - ]; + let bad_recorded_range = 2u32..4; + let recorded_cost = 200; let unrecorded_blocks = vec![BlockBytes { height: 1, block_bytes: 1000, @@ -75,12 +61,14 @@ fn update_da_record_data__throws_error_if_out_of_order() { .build(); // when - let actual_error = updater.update_da_record_data(&blocks).unwrap_err(); + let actual_error = updater + .update_da_record_data(bad_recorded_range, recorded_cost) + .unwrap_err(); // then let expected_error = Error::SkippedDABlock { - expected: 2, - got: 3, + expected: 1, + got: 2, }; assert_eq!(actual_error, expected_error); } @@ -100,13 +88,12 @@ fn update_da_record_data__updates_cost_per_byte() { .build(); let new_cost_per_byte = 100; - let block_cost = block_bytes * new_cost_per_byte; - let blocks = vec![RecordedBlock { - height: 1, - block_cost, - }]; + let recorded_cost = (block_bytes * new_cost_per_byte) as u128; + let recorded_range = 1u32..2; // when - updater.update_da_record_data(&blocks).unwrap(); + updater + .update_da_record_data(recorded_range, recorded_cost) + .unwrap(); // then let expected = new_cost_per_byte as u128; @@ -145,27 +132,16 @@ fn update_da_record_data__updates_known_total_cost() { .with_unrecorded_blocks(unrecorded_blocks) .build(); - let block_cost = 100; - let blocks = vec![ - RecordedBlock { - height: 11, - block_cost, - }, - RecordedBlock { - height: 12, - block_cost, - }, - RecordedBlock { - height: 13, - block_cost, - }, - ]; + let recorded_range = 11u32..14; + let recorded_cost = 300; // when - updater.update_da_record_data(&blocks).unwrap(); + updater + .update_da_record_data(recorded_range, recorded_cost) + .unwrap(); // then let actual = updater.latest_known_total_da_cost_excess; - let expected = known_total_cost + (3 * block_cost as u128); + let expected = known_total_cost + recorded_cost; assert_eq!(actual, expected); } @@ -208,22 +184,13 @@ fn update_da_record_data__if_da_height_matches_l2_height_projected_and_known_mat let block_bytes = 1000; let new_cost_per_byte = 100; let block_cost = block_bytes * new_cost_per_byte; - let blocks = vec![ - RecordedBlock { - height: 11, - block_cost, - }, - RecordedBlock { - height: 12, - block_cost, - }, - RecordedBlock { - height: 13, - block_cost, - }, - ]; + + let recorded_range = 11u32..14; + let recorded_cost = block_cost * 3; // when - updater.update_da_record_data(&blocks).unwrap(); + updater + .update_da_record_data(recorded_range, recorded_cost) + .unwrap(); // then assert_eq!(updater.l2_block_height, updater.da_recorded_block_height); @@ -284,24 +251,25 @@ fn update_da_record_data__da_block_updates_projected_total_cost_with_known_and_g .build(); let new_cost_per_byte = 100; - let recorded_blocks = to_be_removed - .iter() - .map(|block| RecordedBlock { - height: block.height, - block_cost: block.block_bytes * new_cost_per_byte, - }) - .collect::>(); + let (recorded_heights, recorded_cost) = + to_be_removed + .iter() + .fold((vec![], 0), |(mut range, cost), block| { + range.push(block.height); + (range, cost + block.block_bytes * new_cost_per_byte) + }); + let min = recorded_heights.iter().min().unwrap(); + let max = recorded_heights.iter().max().unwrap(); + let recorded_range = *min..(max + 1); // when - updater.update_da_record_data(&recorded_blocks).unwrap(); + updater + .update_da_record_data(recorded_range, recorded_cost as u128) + .unwrap(); // then let actual = updater.projected_total_da_cost; - let new_known_total_cost = original_known_total_cost - + recorded_blocks - .iter() - .map(|block| block.block_cost) - .sum::(); + let new_known_total_cost = original_known_total_cost + recorded_cost; let guessed_part: u64 = remaining .iter() .map(|block| block.block_bytes * new_cost_per_byte) @@ -374,16 +342,26 @@ fn _update_da_record_data__when_reward_is_greater_than_cost_will_zero_cost_and_s .with_unrecorded_blocks(unrecorded_blocks) .build(); - let new_costs = recorded_blocks + let recorded_heights = recorded_blocks .iter() - .map(|block| block.block_cost) - .sum::(); + .map(|block| block.height) + .collect::>(); + let min = recorded_heights.iter().min().unwrap(); + let max = recorded_heights.iter().max().unwrap(); + let recorded_range = *min..(max + 1); + + let recorded_cost = recorded_blocks + .iter() + .map(|block| block.block_cost as u128) + .sum(); // when - updater.update_da_record_data(&recorded_blocks).unwrap(); + updater + .update_da_record_data(recorded_range, recorded_cost) + .unwrap(); // then - let expected = total_rewards - new_costs as u128 - known_total_cost; + let expected = total_rewards - recorded_cost - known_total_cost; let actual = updater.total_da_rewards_excess; assert_eq!(actual, expected); @@ -433,20 +411,30 @@ fn _update_da_record_data__when_cost_is_greater_than_reward_will_zero_reward_and .with_unrecorded_blocks(unrecorded_blocks) .build(); - let new_costs = recorded_blocks + let recorded_heights = recorded_blocks + .iter() + .map(|block| block.height) + .collect::>(); + let min = recorded_heights.iter().min().unwrap(); + let max = recorded_heights.iter().max().unwrap(); + let recorded_range = *min..(max + 1); + + let recorded_cost = recorded_blocks .iter() - .map(|block| block.block_cost) - .sum::(); + .map(|block| block.block_cost as u128) + .sum(); // when - updater.update_da_record_data(&recorded_blocks).unwrap(); + updater + .update_da_record_data(recorded_range, recorded_cost) + .unwrap(); // then let expected = 0; let actual = updater.total_da_rewards_excess; assert_eq!(actual, expected); - let expected = known_total_cost + new_costs as u128 - total_rewards; + let expected = known_total_cost + recorded_cost - total_rewards; let actual = updater.latest_known_total_da_cost_excess; assert_eq!(actual, expected); } From cc28922cdf93762be915418a0856cf95bb9b437a Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 26 Sep 2024 11:47:58 +0200 Subject: [PATCH 06/17] Move `RecordedBlock` into tests --- crates/fuel-gas-price-algorithm/src/v1.rs | 7 -- .../v1/tests/update_da_record_data_tests.rs | 85 +++++++++---------- 2 files changed, 40 insertions(+), 52 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 48ac87b945d..81098d3b8bd 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -184,13 +184,6 @@ impl core::ops::Deref for ClampedPercentage { } } -#[derive(Debug, Clone)] -pub struct RecordedBlock { - pub height: u32, - // pub block_bytes: u64, - pub block_cost: u64, -} - impl AlgorithmUpdaterV1 { pub fn update_da_record_data( &mut self, diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs index cf01368b82d..b75fe3db2ef 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs @@ -4,7 +4,6 @@ use crate::v1::{ UpdaterBuilder, }, Error, - RecordedBlock, }; use proptest::{ prelude::Rng, @@ -12,6 +11,7 @@ use proptest::{ proptest, }; use rand::SeedableRng; +use std::ops::Range; #[test] fn update_da_record_data__increases_block() { @@ -278,8 +278,15 @@ fn update_da_record_data__da_block_updates_projected_total_cost_with_known_and_g assert_eq!(actual, expected as u128); } +#[derive(Debug, Clone)] +pub struct RecordedBlock { + pub height: u32, + // pub block_bytes: u64, + pub block_cost: u64, +} + prop_compose! { - fn arb_vec_of_da_blocks()(last_da_block: u32, count in 1..123usize, rng_seed: u64) -> (Vec, Vec) { + fn arb_vec_of_da_blocks()(last_da_block: u32, count in 1..123usize, rng_seed: u64) -> (Vec, (Range, u128)) { let rng = &mut rand::rngs::StdRng::seed_from_u64(rng_seed); let mut unrecorded_blocks = Vec::with_capacity(count); let mut recorded_blocks = Vec::with_capacity(count); @@ -297,28 +304,40 @@ prop_compose! { block_cost, }); } - (unrecorded_blocks, recorded_blocks) + let recorded_heights = recorded_blocks + .iter() + .map(|block| block.height) + .collect::>(); + let min = recorded_heights.iter().min().unwrap(); + let max = recorded_heights.iter().max().unwrap(); + let recorded_range = *min..(max + 1); + let recorded_cost = recorded_blocks + .iter() + .map(|block| block.block_cost as u128) + .sum(); + + (unrecorded_blocks, (recorded_range, recorded_cost)) } } prop_compose! { - fn reward_greater_than_cost_with_da_blocks()(cost: u64, extra: u64, (unrecorded_blocks, recorded_blocks) in arb_vec_of_da_blocks()) -> (u128, u128, Vec, Vec) { - let cost_from_blocks = recorded_blocks.iter().map(|block| block.block_cost as u128).sum::(); - let reward = cost as u128 + cost_from_blocks + extra as u128; - (cost as u128, reward, unrecorded_blocks, recorded_blocks) + fn reward_greater_than_cost_with_da_blocks()(cost: u64, extra: u64, (unrecorded_blocks, (recorded_range, recorded_cost)) in arb_vec_of_da_blocks()) -> (u128, u128, Vec, (Range, u128)) { + let reward = cost as u128 + recorded_cost + extra as u128; + (cost as u128, reward, unrecorded_blocks, (recorded_range, recorded_cost)) } } proptest! { #[test] fn update_da_record_data__when_reward_is_greater_than_cost_will_zero_cost_and_subtract_from_reward( - (cost, reward, unrecorded_blocks, recorded_blocks) in reward_greater_than_cost_with_da_blocks() + (cost, reward, unrecorded_blocks, (recorded_range, recorded_cost)) in reward_greater_than_cost_with_da_blocks() ) { _update_da_record_data__when_reward_is_greater_than_cost_will_zero_cost_and_subtract_from_reward( cost, reward, unrecorded_blocks, - recorded_blocks, + recorded_range, + recorded_cost ) } } @@ -327,11 +346,12 @@ fn _update_da_record_data__when_reward_is_greater_than_cost_will_zero_cost_and_s known_total_cost: u128, total_rewards: u128, unrecorded_blocks: Vec, - recorded_blocks: Vec, + recorded_range: Range, + recorded_cost: u128, ) { // given let da_cost_per_byte = 20; - let da_recorded_block_height = recorded_blocks.first().unwrap().height - 1; + let da_recorded_block_height = recorded_range.start - 1; let l2_block_height = 15; let mut updater = UpdaterBuilder::new() .with_da_cost_per_byte(da_cost_per_byte) @@ -342,19 +362,6 @@ fn _update_da_record_data__when_reward_is_greater_than_cost_will_zero_cost_and_s .with_unrecorded_blocks(unrecorded_blocks) .build(); - let recorded_heights = recorded_blocks - .iter() - .map(|block| block.height) - .collect::>(); - let min = recorded_heights.iter().min().unwrap(); - let max = recorded_heights.iter().max().unwrap(); - let recorded_range = *min..(max + 1); - - let recorded_cost = recorded_blocks - .iter() - .map(|block| block.block_cost as u128) - .sum(); - // when updater .update_da_record_data(recorded_range, recorded_cost) @@ -371,23 +378,23 @@ fn _update_da_record_data__when_reward_is_greater_than_cost_will_zero_cost_and_s } prop_compose! { - fn cost_greater_than_reward_with_da_blocks()(reward: u64, extra: u64, (unrecorded_blocks, recorded_blocks) in arb_vec_of_da_blocks()) -> (u128, u128, Vec, Vec) { - let cost_from_blocks = recorded_blocks.iter().map(|block| block.block_cost as u128).sum::(); - let cost = reward as u128 + cost_from_blocks + extra as u128; - (cost, reward as u128, unrecorded_blocks, recorded_blocks) + fn cost_greater_than_reward_with_da_blocks()(reward: u64, extra: u64, (unrecorded_blocks, (recorded_range, recorded_cost)) in arb_vec_of_da_blocks()) -> (u128, u128, Vec, (Range, u128)) { + let cost = reward as u128 + recorded_cost + extra as u128; + (cost, reward as u128, unrecorded_blocks, (recorded_range, recorded_cost)) } } proptest! { #[test] fn update_da_record_data__when_cost_is_greater_than_reward_will_zero_reward_and_subtract_from_cost( - (cost, reward, unrecorded_blocks, recorded_blocks) in cost_greater_than_reward_with_da_blocks() + (cost, reward, unrecorded_blocks, (recorded_range, recorded_cost)) in cost_greater_than_reward_with_da_blocks() ) { _update_da_record_data__when_cost_is_greater_than_reward_will_zero_reward_and_subtract_from_cost( cost, reward, unrecorded_blocks, - recorded_blocks, + recorded_range, + recorded_cost ) } } @@ -396,11 +403,12 @@ fn _update_da_record_data__when_cost_is_greater_than_reward_will_zero_reward_and known_total_cost: u128, total_rewards: u128, unrecorded_blocks: Vec, - recorded_blocks: Vec, + recorded_range: Range, + recorded_cost: u128, ) { // given let da_cost_per_byte = 20; - let da_recorded_block_height = recorded_blocks.first().unwrap().height - 1; + let da_recorded_block_height = recorded_range.start - 1; let l2_block_height = 15; let mut updater = UpdaterBuilder::new() .with_da_cost_per_byte(da_cost_per_byte) @@ -411,19 +419,6 @@ fn _update_da_record_data__when_cost_is_greater_than_reward_will_zero_reward_and .with_unrecorded_blocks(unrecorded_blocks) .build(); - let recorded_heights = recorded_blocks - .iter() - .map(|block| block.height) - .collect::>(); - let min = recorded_heights.iter().min().unwrap(); - let max = recorded_heights.iter().max().unwrap(); - let recorded_range = *min..(max + 1); - - let recorded_cost = recorded_blocks - .iter() - .map(|block| block.block_cost as u128) - .sum(); - // when updater .update_da_record_data(recorded_range, recorded_cost) From 93a6e986d45a13aa59bc68f23bb4a1b563329821 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 26 Sep 2024 11:53:58 +0200 Subject: [PATCH 07/17] Use BTreeMap instead of HashMap --- crates/fuel-gas-price-algorithm/src/v1.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 81098d3b8bd..f48b3f96e08 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -1,7 +1,7 @@ use crate::utils::cumulative_percentage_change; use std::{ cmp::max, - collections::HashMap, + collections::BTreeMap, num::NonZeroU64, ops::{ Div, @@ -153,7 +153,7 @@ pub struct AlgorithmUpdaterV1 { pub latest_da_cost_per_byte: u128, /// The unrecorded blocks that are used to calculate the projected cost of recording blocks - pub unrecorded_blocks: HashMap, + pub unrecorded_blocks: BTreeMap, } /// A value that represents a value between 0 and 100. Higher values are clamped to 100 From 41c26f08e1e66c872be349362dc165bfa067e26b Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 26 Sep 2024 12:08:17 +0200 Subject: [PATCH 08/17] Use `pop_first` instead of `remove` --- crates/fuel-gas-price-algorithm/src/v1.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index f48b3f96e08..001631f9285 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -401,11 +401,14 @@ impl AlgorithmUpdaterV1 { height_range: Range, ) -> Result { let mut total: u128 = 0; - for height in height_range { - let bytes = self + for expected_height in height_range { + let (actual_height, bytes) = self .unrecorded_blocks - .remove(&height) - .ok_or(Error::L2BlockExpectedNotFound(height))?; + .pop_first() + .ok_or(Error::L2BlockExpectedNotFound(expected_height))?; + if actual_height != expected_height { + return Err(Error::L2BlockExpectedNotFound(expected_height)); + } total = total.saturating_add(bytes as u128); } Ok(total) From 364ce4a6f06d10c5f6c6d703e74c61296eae476e Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 26 Sep 2024 12:17:48 +0200 Subject: [PATCH 09/17] Fix compilation errors --- crates/fuel-gas-price-algorithm/src/v1.rs | 4 ++-- .../services/gas_price_service/src/fuel_gas_price_updater.rs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 001631f9285..aa3c2efcffc 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -190,7 +190,7 @@ impl AlgorithmUpdaterV1 { height_range: Range, range_cost: u128, ) -> Result<(), Error> { - if height_range.len() != 0 { + if !height_range.is_empty() { self.da_block_update(height_range, range_cost)?; self.recalculate_projected_cost(); self.normalize_rewards_and_costs(); @@ -378,7 +378,7 @@ impl AlgorithmUpdaterV1 { got: first, }) } else { - let last = height_range.end - 1; + let last = height_range.end.saturating_sub(1); let range_bytes = self.drain_l2_block_bytes_for_range(height_range)?; let new_cost_per_byte: u128 = range_cost.checked_div(range_bytes).ok_or( Error::CouldNotCalculateCostPerByte { diff --git a/crates/services/gas_price_service/src/fuel_gas_price_updater.rs b/crates/services/gas_price_service/src/fuel_gas_price_updater.rs index b5fde86f2e5..2818fce0dd3 100644 --- a/crates/services/gas_price_service/src/fuel_gas_price_updater.rs +++ b/crates/services/gas_price_service/src/fuel_gas_price_updater.rs @@ -14,7 +14,6 @@ pub use fuel_gas_price_algorithm::{ v1::{ AlgorithmUpdaterV1, AlgorithmV1, - RecordedBlock, }, }; From e40a857ee4a5ffe66b046fcf6e18c7be61e2efd9 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 26 Sep 2024 17:58:41 +0200 Subject: [PATCH 10/17] Kinda fix the analyzer I think --- .../gas-price-analysis/src/simulation.rs | 58 ++++++++++++------- crates/fuel-gas-price-algorithm/src/v1.rs | 28 +++++---- 2 files changed, 53 insertions(+), 33 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 6ad906cc41f..6dd35157c21 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,10 +1,10 @@ -use fuel_gas_price_algorithm::v1::{ - AlgorithmUpdaterV1, - RecordedBlock, -}; -use std::num::NonZeroU64; - use super::*; +use fuel_gas_price_algorithm::v1::AlgorithmUpdaterV1; +use std::{ + collections::BTreeMap, + num::NonZeroU64, + ops::Range, +}; pub mod da_cost_per_byte; @@ -90,7 +90,7 @@ impl Simulator { latest_da_cost_per_byte: 0, projected_total_da_cost: 0, latest_known_total_da_cost_excess: 0, - unrecorded_blocks: vec![], + unrecorded_blocks: BTreeMap::new(), da_p_component, da_d_component, last_profit: 0, @@ -105,7 +105,7 @@ impl Simulator { max_block_bytes: u64, fullness_and_bytes: Vec<(u64, u64)>, // blocks: Enumerate, Iter>>>>, - blocks: impl Iterator>))>, + blocks: impl Iterator, u128)>))>, mut updater: AlgorithmUpdaterV1, ) -> SimulationResults { let mut gas_prices = vec![]; @@ -121,7 +121,10 @@ impl Simulator { da_gas_prices.push(updater.new_scaled_da_gas_price); let gas_price = updater.algorithm().calculate(); gas_prices.push(gas_price); - let total_fee = gas_price * fullness; + println!("height: {}", height); + println!("gas_price {}, fullness {}", gas_price, fullness); + println!("last_profit: {}", updater.last_profit); + let total_fee = gas_price as u128 * fullness as u128; updater .update_l2_block_data( height, @@ -137,13 +140,15 @@ impl Simulator { projected_cost_totals.push(updater.projected_total_da_cost); // Update DA blocks on the occasion there is one - if let Some(da_blocks) = &da_block { - let mut total_cost = updater.latest_known_total_da_cost_excess; - for block in da_blocks { - total_cost += block.block_cost as u128; + if let Some((range, cost)) = da_block { + println!("############ cost: {}", cost); + for height in range.to_owned() { + let total_cost = updater.latest_known_total_da_cost_excess + cost; actual_costs.push(total_cost); + updater + .update_da_record_data(height..(height + 1), *cost) + .unwrap(); } - updater.update_da_record_data(&da_blocks).unwrap(); } } let (fullness_without_capacity, bytes): (Vec<_>, Vec<_>) = @@ -187,7 +192,7 @@ impl Simulator { da_recording_rate: usize, da_finalization_rate: usize, fullness_and_bytes: &Vec<(u64, u64)>, - ) -> Vec>> { + ) -> Vec, u128)>> { let l2_blocks_with_no_da_blocks = std::iter::repeat(None).take(da_finalization_rate); let (_, da_blocks) = fullness_and_bytes @@ -199,12 +204,13 @@ impl Simulator { |(mut delayed, mut recorded), (index, ((_fullness, bytes), cost_per_byte))| { let total_cost = *bytes * cost_per_byte; + + println!( + "@@@@@@@ bytes {}, cost_per_byte: {}, total_cost: {}", + bytes, cost_per_byte, total_cost + ); let height = index as u32 + 1; - let converted = RecordedBlock { - height, - block_bytes: *bytes, - block_cost: total_cost as u64, - }; + let converted = (height, bytes, total_cost); delayed.push(converted); if delayed.len() == da_recording_rate { recorded.push(Some(delayed)); @@ -215,7 +221,17 @@ impl Simulator { } }, ); - l2_blocks_with_no_da_blocks.chain(da_blocks).collect() + let da_block_ranges = da_blocks.into_iter().map(|maybe_recorded_blocks| { + maybe_recorded_blocks.map(|list| { + let heights_iter = list.iter().map(|(height, _, _)| *height); + let min = heights_iter.clone().min().unwrap(); + let max = heights_iter.max().unwrap(); + let cost: u128 = list.iter().map(|(_, _, cost)| *cost as u128).sum(); + println!("&&&&&&&&&& cost: {}", cost); + (min..(max + 1), cost) + }) + }); + l2_blocks_with_no_da_blocks.chain(da_block_ranges).collect() } } diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index aa3c2efcffc..65390be92d2 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -204,7 +204,7 @@ impl AlgorithmUpdaterV1 { used: u64, capacity: NonZeroU64, block_bytes: u64, - fee_wei: u64, + fee_wei: u128, ) -> Result<(), Error> { let expected = self.l2_block_height.saturating_add(1); if height != expected { @@ -235,33 +235,36 @@ impl AlgorithmUpdaterV1 { } } - fn update_rewards(&mut self, fee_wei: u64) { + fn update_rewards(&mut self, fee_wei: u128) { let block_da_reward = self.da_portion_of_fee(fee_wei); - self.total_da_rewards_excess = self - .total_da_rewards_excess - .saturating_add(block_da_reward.into()); + self.total_da_rewards_excess = + self.total_da_rewards_excess.saturating_add(block_da_reward); } fn update_projected_cost(&mut self, block_bytes: u64) { let block_projected_da_cost = (block_bytes as u128).saturating_mul(self.latest_da_cost_per_byte); + println!("bytes: {}", block_bytes); + println!("latest_da_cost_per_byte: {}", self.latest_da_cost_per_byte); + println!("block_projected_da_cost: {}", block_projected_da_cost); self.projected_total_da_cost = self .projected_total_da_cost .saturating_add(block_projected_da_cost); } // Take the `fee_wei` and return the portion of the fee that should be used for paying DA costs - fn da_portion_of_fee(&self, fee_wei: u64) -> u64 { + fn da_portion_of_fee(&self, fee_wei: u128) -> u128 { // fee_wei * (da_price / (exec_price + da_price)) - let numerator = fee_wei.saturating_mul(self.descaled_da_price()); - let denominator = self - .descaled_exec_price() - .saturating_add(self.descaled_da_price()); - if denominator == 0 { + let numerator = fee_wei.saturating_mul(self.descaled_da_price() as u128); + let denominator = (self.descaled_exec_price() as u128) + .saturating_add(self.descaled_da_price() as u128); + let reward = if denominator == 0 { 0 } else { numerator.div_ceil(denominator) - } + }; + println!("reward: {}", reward); + reward } fn clamped_projected_cost_as_i128(&self) -> i128 { @@ -380,6 +383,7 @@ impl AlgorithmUpdaterV1 { } else { let last = height_range.end.saturating_sub(1); let range_bytes = self.drain_l2_block_bytes_for_range(height_range)?; + println!("range_bytes: {}", range_bytes); let new_cost_per_byte: u128 = range_cost.checked_div(range_bytes).ok_or( Error::CouldNotCalculateCostPerByte { bytes: range_bytes, From ea5469fc31287307d8bb828b6f280823e728521d Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Thu, 26 Sep 2024 18:01:28 +0200 Subject: [PATCH 11/17] Cleanup prints --- .../gas-price-analysis/src/simulation.rs | 9 --------- crates/fuel-gas-price-algorithm/src/v1.rs | 5 ----- 2 files changed, 14 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 6dd35157c21..2a24bce3481 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 @@ -121,9 +121,6 @@ impl Simulator { da_gas_prices.push(updater.new_scaled_da_gas_price); let gas_price = updater.algorithm().calculate(); gas_prices.push(gas_price); - println!("height: {}", height); - println!("gas_price {}, fullness {}", gas_price, fullness); - println!("last_profit: {}", updater.last_profit); let total_fee = gas_price as u128 * fullness as u128; updater .update_l2_block_data( @@ -141,7 +138,6 @@ impl Simulator { // Update DA blocks on the occasion there is one if let Some((range, cost)) = da_block { - println!("############ cost: {}", cost); for height in range.to_owned() { let total_cost = updater.latest_known_total_da_cost_excess + cost; actual_costs.push(total_cost); @@ -205,10 +201,6 @@ impl Simulator { (index, ((_fullness, bytes), cost_per_byte))| { let total_cost = *bytes * cost_per_byte; - println!( - "@@@@@@@ bytes {}, cost_per_byte: {}, total_cost: {}", - bytes, cost_per_byte, total_cost - ); let height = index as u32 + 1; let converted = (height, bytes, total_cost); delayed.push(converted); @@ -227,7 +219,6 @@ impl Simulator { let min = heights_iter.clone().min().unwrap(); let max = heights_iter.max().unwrap(); let cost: u128 = list.iter().map(|(_, _, cost)| *cost as u128).sum(); - println!("&&&&&&&&&& cost: {}", cost); (min..(max + 1), cost) }) }); diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 65390be92d2..3cf740a1dab 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -244,9 +244,6 @@ impl AlgorithmUpdaterV1 { fn update_projected_cost(&mut self, block_bytes: u64) { let block_projected_da_cost = (block_bytes as u128).saturating_mul(self.latest_da_cost_per_byte); - println!("bytes: {}", block_bytes); - println!("latest_da_cost_per_byte: {}", self.latest_da_cost_per_byte); - println!("block_projected_da_cost: {}", block_projected_da_cost); self.projected_total_da_cost = self .projected_total_da_cost .saturating_add(block_projected_da_cost); @@ -263,7 +260,6 @@ impl AlgorithmUpdaterV1 { } else { numerator.div_ceil(denominator) }; - println!("reward: {}", reward); reward } @@ -383,7 +379,6 @@ impl AlgorithmUpdaterV1 { } else { let last = height_range.end.saturating_sub(1); let range_bytes = self.drain_l2_block_bytes_for_range(height_range)?; - println!("range_bytes: {}", range_bytes); let new_cost_per_byte: u128 = range_cost.checked_div(range_bytes).ok_or( Error::CouldNotCalculateCostPerByte { bytes: range_bytes, From bfafd03b5714a238c99943bebd9e879a40a8fe03 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Fri, 27 Sep 2024 10:57:57 +0200 Subject: [PATCH 12/17] Fix test compilation --- crates/fuel-gas-price-algorithm/src/v1.rs | 5 ++--- .../src/v1/tests/update_l2_block_data_tests.rs | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 3cf740a1dab..fa88f6b0a55 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -255,12 +255,11 @@ impl AlgorithmUpdaterV1 { let numerator = fee_wei.saturating_mul(self.descaled_da_price() as u128); let denominator = (self.descaled_exec_price() as u128) .saturating_add(self.descaled_da_price() as u128); - let reward = if denominator == 0 { + if denominator == 0 { 0 } else { numerator.div_ceil(denominator) - }; - reward + } } fn clamped_projected_cost_as_i128(&self) -> i128 { 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 c32a48728ed..6d5e4cf2ce7 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 @@ -106,10 +106,10 @@ fn update_l2_block_data__updates_the_total_reward_value() { .unwrap(); // then - let expected = (fee * starting_da_gas_price) - .div_ceil(starting_da_gas_price + starting_exec_gas_price); + let expected = (fee * starting_da_gas_price as u128) + .div_ceil(starting_da_gas_price as u128 + starting_exec_gas_price as u128); let actual = updater.total_da_rewards_excess; - assert_eq!(actual, expected as u128); + assert_eq!(actual, expected); } #[test] @@ -482,7 +482,7 @@ fn update_l2_block_data__even_profit_maintains_price() { 50, 100.try_into().unwrap(), block_bytes, - total_fee, + total_fee.into(), ) .unwrap(); let algo = updater.algorithm(); From 97fcc32d97084fbee6d377e8f7af2767e13f7649 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Fri, 27 Sep 2024 11:28:32 +0200 Subject: [PATCH 13/17] Fix profit chart length --- .../gas-price-analysis/src/charts.rs | 10 ++++++++-- .../gas-price-analysis/src/simulation.rs | 1 - 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/charts.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/charts.rs index 32194a716a2..0023c06206c 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/charts.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/charts.rs @@ -264,6 +264,12 @@ pub fn draw_profit( pessimistic_block_costs: &[u128], title: &str, ) -> anyhow::Result<()> { + println!("projected_profit len: {}", projected_profit.len()); + println!("actual_profit len: {}", actual_profit.len()); + println!( + "pessimistic_block_costs len: {}", + pessimistic_block_costs.len() + ); const ACTUAL_PROFIT_COLOR: RGBColor = BLACK; const PROJECTED_PROFIT_COLOR: RGBColor = RED; const PESSIMISTIC_BLOCK_COST_COLOR: RGBColor = BLUE; @@ -307,10 +313,10 @@ pub fn draw_profit( .x_label_area_size(40) .y_label_area_size(100) .right_y_label_area_size(100) - .build_cartesian_2d(0..actual_profit_gwei.len(), min..max) + .build_cartesian_2d(0..projected_profit.len(), min..max) .unwrap() .set_secondary_coord( - 0..actual_profit_gwei.len(), + 0..projected_profit.len(), 0..*pessimistic_block_costs_gwei.iter().max().unwrap(), ); 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 2a24bce3481..cae93f0c1fc 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 @@ -104,7 +104,6 @@ impl Simulator { capacity: u64, max_block_bytes: u64, fullness_and_bytes: Vec<(u64, u64)>, - // blocks: Enumerate, Iter>>>>, blocks: impl Iterator, u128)>))>, mut updater: AlgorithmUpdaterV1, ) -> SimulationResults { From a51a32c5e2bfc3675cfad118f1cd03341253bd5b Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Fri, 27 Sep 2024 18:17:52 +0200 Subject: [PATCH 14/17] Remove normalization function to fix simulation --- .../gas-price-analysis/src/charts.rs | 6 ------ .../gas-price-analysis/src/simulation.rs | 3 +-- crates/fuel-gas-price-algorithm/src/v1.rs | 9 +++++++-- .../src/v1/tests/update_da_record_data_tests.rs | 4 ++++ 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/charts.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/charts.rs index 0023c06206c..6b4c82f09ff 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/charts.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/charts.rs @@ -264,12 +264,6 @@ pub fn draw_profit( pessimistic_block_costs: &[u128], title: &str, ) -> anyhow::Result<()> { - println!("projected_profit len: {}", projected_profit.len()); - println!("actual_profit len: {}", actual_profit.len()); - println!( - "pessimistic_block_costs len: {}", - pessimistic_block_costs.len() - ); const ACTUAL_PROFIT_COLOR: RGBColor = BLACK; const PROJECTED_PROFIT_COLOR: RGBColor = RED; const PESSIMISTIC_BLOCK_COST_COLOR: RGBColor = BLUE; 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 cae93f0c1fc..99fe567e2e6 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 @@ -138,11 +138,10 @@ impl Simulator { // Update DA blocks on the occasion there is one if let Some((range, cost)) = da_block { for height in range.to_owned() { - let total_cost = updater.latest_known_total_da_cost_excess + cost; - actual_costs.push(total_cost); updater .update_da_record_data(height..(height + 1), *cost) .unwrap(); + actual_costs.push(updater.latest_known_total_da_cost_excess) } } } diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index fa88f6b0a55..598c8fb864b 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -193,7 +193,7 @@ impl AlgorithmUpdaterV1 { if !height_range.is_empty() { self.da_block_update(height_range, range_cost)?; self.recalculate_projected_cost(); - self.normalize_rewards_and_costs(); + // self.normalize_rewards_and_costs(); } Ok(()) } @@ -222,6 +222,8 @@ impl AlgorithmUpdaterV1 { // costs self.update_projected_cost(block_bytes); let projected_total_da_cost = self.clamped_projected_cost_as_i128(); + + // profit let last_profit = rewards.saturating_sub(projected_total_da_cost); self.update_last_profit(last_profit); @@ -444,9 +446,12 @@ impl AlgorithmUpdaterV1 { } } + // TODO: This breaks our simulation now that we are using an extended finalization period. We + // can either keep this function and fix the simulation, or we might decide to remove it. + // https://github.com/FuelLabs/fuel-core/issues/2264 // We only need to track the difference between the rewards and costs after we have true DA data // Normalize, or zero out the lower value and subtract it from the higher value - fn normalize_rewards_and_costs(&mut self) { + fn _normalize_rewards_and_costs(&mut self) { let (excess, projected_cost_excess) = if self.total_da_rewards_excess > self.latest_known_total_da_cost_excess { ( diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs index b75fe3db2ef..218ea6353e1 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs @@ -328,6 +328,8 @@ prop_compose! { } proptest! { + // https://github.com/FuelLabs/fuel-core/issues/2264 + #[ignore] #[test] fn update_da_record_data__when_reward_is_greater_than_cost_will_zero_cost_and_subtract_from_reward( (cost, reward, unrecorded_blocks, (recorded_range, recorded_cost)) in reward_greater_than_cost_with_da_blocks() @@ -385,6 +387,8 @@ prop_compose! { } proptest! { + // https://github.com/FuelLabs/fuel-core/issues/2264 + #[ignore] #[test] fn update_da_record_data__when_cost_is_greater_than_reward_will_zero_reward_and_subtract_from_cost( (cost, reward, unrecorded_blocks, (recorded_range, recorded_cost)) in cost_greater_than_reward_with_da_blocks() From dd8a252f715488b6fee17c05912d9929d8afc398 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Mon, 30 Sep 2024 13:58:10 +0200 Subject: [PATCH 15/17] Remove comment, add todo --- crates/fuel-gas-price-algorithm/src/v1.rs | 1 + .../src/v1/tests/update_da_record_data_tests.rs | 1 - 2 files 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 598c8fb864b..0496820f93a 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -193,6 +193,7 @@ impl AlgorithmUpdaterV1 { if !height_range.is_empty() { self.da_block_update(height_range, range_cost)?; self.recalculate_projected_cost(); + // TODO: https://github.com/FuelLabs/fuel-core/issues/2264 // self.normalize_rewards_and_costs(); } Ok(()) diff --git a/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs b/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs index 218ea6353e1..1c1628be60a 100644 --- a/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs +++ b/crates/fuel-gas-price-algorithm/src/v1/tests/update_da_record_data_tests.rs @@ -281,7 +281,6 @@ fn update_da_record_data__da_block_updates_projected_total_cost_with_known_and_g #[derive(Debug, Clone)] pub struct RecordedBlock { pub height: u32, - // pub block_bytes: u64, pub block_cost: u64, } From 5848462f51d0eadd238ffc8416af5d6ab3f9e073 Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Wed, 2 Oct 2024 19:14:27 +0200 Subject: [PATCH 16/17] clean up function signature --- .../gas-price-analysis/src/simulation.rs | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 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 99fe567e2e6..bbbc1529ad3 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 @@ -24,6 +24,13 @@ pub struct Simulator { da_cost_per_byte: Vec, } +// (usize, ((u64, u64), &'a Option<(Range, u128) +struct BlockData { + fullness: u64, + bytes: u64, + maybe_da_block: Option<(Range, u128)>, +} + impl Simulator { pub fn new(da_cost_per_byte: Vec) -> Self { Simulator { da_cost_per_byte } @@ -49,7 +56,14 @@ impl Simulator { &fullness_and_bytes, ); - let blocks = l2_blocks.zip(da_blocks.iter()).enumerate(); + let blocks = l2_blocks + .zip(da_blocks.iter()) + .map(|((fullness, bytes), maybe_da_block)| BlockData { + fullness, + bytes, + maybe_da_block: maybe_da_block.clone(), + }) + .enumerate(); let updater = self.build_updater(da_p_component, da_d_component); @@ -104,7 +118,7 @@ impl Simulator { capacity: u64, max_block_bytes: u64, fullness_and_bytes: Vec<(u64, u64)>, - blocks: impl Iterator, u128)>))>, + blocks: impl Iterator, mut updater: AlgorithmUpdaterV1, ) -> SimulationResults { let mut gas_prices = vec![]; @@ -114,7 +128,12 @@ impl Simulator { let mut projected_cost_totals = vec![]; let mut actual_costs = vec![]; let mut pessimistic_costs = vec![]; - for (index, ((fullness, bytes), da_block)) in blocks { + for (index, block_data) in blocks { + let BlockData { + fullness, + bytes, + maybe_da_block: da_block, + } = block_data; let height = index as u32 + 1; exec_gas_prices.push(updater.new_scaled_exec_price); da_gas_prices.push(updater.new_scaled_da_gas_price); @@ -139,7 +158,7 @@ impl Simulator { if let Some((range, cost)) = da_block { for height in range.to_owned() { updater - .update_da_record_data(height..(height + 1), *cost) + .update_da_record_data(height..(height + 1), cost) .unwrap(); actual_costs.push(updater.latest_known_total_da_cost_excess) } From 8ec9c18cdd26f064a6734bb97b07b8c1d3d3d2aa Mon Sep 17 00:00:00 2001 From: Mitch Turner Date: Wed, 2 Oct 2024 19:19:14 +0200 Subject: [PATCH 17/17] revert file inclusion from botched merge --- .../src/fuel_gas_price_updater.rs | 354 ------------------ 1 file changed, 354 deletions(-) delete mode 100644 crates/services/gas_price_service/src/fuel_gas_price_updater.rs diff --git a/crates/services/gas_price_service/src/fuel_gas_price_updater.rs b/crates/services/gas_price_service/src/fuel_gas_price_updater.rs deleted file mode 100644 index b5fde86f2e5..00000000000 --- a/crates/services/gas_price_service/src/fuel_gas_price_updater.rs +++ /dev/null @@ -1,354 +0,0 @@ -use crate::{ - ports::MetadataStorage, - GasPriceAlgorithm, - UpdateAlgorithm, -}; -use anyhow::anyhow; -use core::num::NonZeroU64; -use fuel_core_types::fuel_types::BlockHeight; -pub use fuel_gas_price_algorithm::{ - v0::{ - AlgorithmUpdaterV0, - AlgorithmV0, - }, - v1::{ - AlgorithmUpdaterV1, - AlgorithmV1, - RecordedBlock, - }, -}; - -#[cfg(test)] -mod tests; - -pub mod fuel_core_storage_adapter; - -pub mod algorithm_updater; -pub mod da_source_adapter; - -pub struct FuelGasPriceUpdater { - inner: AlgorithmUpdater, - l2_block_source: L2, - metadata_storage: Metadata, - #[allow(dead_code)] - da_block_costs: DaBlockCosts, -} - -#[derive(Debug, Clone, PartialEq)] -pub enum AlgorithmUpdater { - V0(AlgorithmUpdaterV0), - V1(AlgorithmUpdaterV1), -} - -impl AlgorithmUpdater { - pub fn algorithm(&self) -> Algorithm { - match self { - AlgorithmUpdater::V0(v0) => Algorithm::V0(v0.algorithm()), - AlgorithmUpdater::V1(v1) => Algorithm::V1(v1.algorithm()), - } - } - - pub fn l2_block_height(&self) -> BlockHeight { - match self { - AlgorithmUpdater::V0(v0) => v0.l2_block_height.into(), - AlgorithmUpdater::V1(v1) => v1.l2_block_height.into(), - } - } -} - -impl FuelGasPriceUpdater { - pub fn new( - inner: AlgorithmUpdater, - l2_block_source: L2, - metadata_storage: Metadata, - da_block_costs: DaBlockCosts, - ) -> Self { - Self { - inner, - l2_block_source, - metadata_storage, - da_block_costs, - } - } -} - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Failed to find L2 block at height {block_height:?}: {source_error:?}")] - CouldNotFetchL2Block { - block_height: BlockHeight, - source_error: anyhow::Error, - }, - #[error("Failed to find DA records: {0:?}")] - CouldNotFetchDARecord(anyhow::Error), - #[error("Failed to retrieve updater metadata: {source_error:?}")] - CouldNotFetchMetadata { source_error: anyhow::Error }, - #[error( - "Failed to set updater metadata at height {block_height:?}: {source_error:?}" - )] - CouldNotSetMetadata { - block_height: BlockHeight, - source_error: anyhow::Error, - }, - #[error("Failed to initialize updater: {0:?}")] - CouldNotInitUpdater(anyhow::Error), -} - -pub type Result = core::result::Result; - -// Info required about the l2 block for the gas price algorithm -#[derive(Debug, Clone, PartialEq)] -pub enum BlockInfo { - // The genesis block of the L2 chain - GenesisBlock, - // A normal block in the L2 chain - Block { - // Block height - height: u32, - // Gas used in the block - gas_used: u64, - // Total gas capacity of the block - block_gas_capacity: u64, - }, -} -#[async_trait::async_trait] -pub trait L2BlockSource: Send + Sync { - async fn get_l2_block(&mut self, height: BlockHeight) -> Result; -} - -#[derive(Debug, Default, Clone, Eq, Hash, PartialEq)] -pub struct DaBlockCosts { - pub l2_block_range: core::ops::Range, - pub blob_size_bytes: u32, - pub blob_cost_wei: u128, -} - -pub trait GetDaBlockCosts: Send + Sync { - fn get(&self) -> Result>; -} - -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] -pub enum UpdaterMetadata { - V0(V0Metadata), -} - -impl UpdaterMetadata { - pub fn l2_block_height(&self) -> BlockHeight { - match self { - UpdaterMetadata::V0(v1) => v1.l2_block_height.into(), - } - } -} - -impl From for AlgorithmUpdater { - fn from(metadata: UpdaterMetadata) -> Self { - match metadata { - UpdaterMetadata::V0(v1_no_da) => { - let V0Metadata { - new_exec_price, - min_exec_gas_price, - exec_gas_price_change_percent, - l2_block_height, - l2_block_fullness_threshold_percent, - } = v1_no_da; - let updater = AlgorithmUpdaterV0 { - new_exec_price, - min_exec_gas_price, - exec_gas_price_change_percent, - l2_block_height, - l2_block_fullness_threshold_percent, - }; - AlgorithmUpdater::V0(updater) - } - } - } -} - -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] -pub struct V0Metadata { - /// The gas price to cover the execution of the next block - pub new_exec_price: u64, - // Execution - /// The lowest the algorithm allows the exec gas price to go - pub min_exec_gas_price: u64, - /// The Percentage the execution gas price will change in a single block, either increase or decrease - /// based on the fullness of the last L2 block - pub exec_gas_price_change_percent: u64, - /// The height for which the `new_exec_price` is calculated, which should be the _next_ block - pub l2_block_height: u32, - /// The threshold of gas usage above and below which the gas price will increase or decrease - /// This is a percentage of the total capacity of the L2 block - pub l2_block_fullness_threshold_percent: u64, -} - -impl From for UpdaterMetadata { - fn from(updater: AlgorithmUpdater) -> Self { - match updater { - AlgorithmUpdater::V0(v0) => { - let metadata = V0Metadata { - new_exec_price: v0.new_exec_price, - min_exec_gas_price: v0.min_exec_gas_price, - exec_gas_price_change_percent: v0.exec_gas_price_change_percent, - l2_block_height: v0.l2_block_height, - l2_block_fullness_threshold_percent: v0 - .l2_block_fullness_threshold_percent, - }; - UpdaterMetadata::V0(metadata) - } - AlgorithmUpdater::V1(_v1) => { - unimplemented!() // https://github.com/FuelLabs/fuel-core/issues/2140 - } - } - } -} - -impl FuelGasPriceUpdater -where - Metadata: MetadataStorage, - DaBlockCosts: GetDaBlockCosts, -{ - pub fn init( - target_block_height: BlockHeight, - l2_block_source: L2, - metadata_storage: Metadata, - da_block_costs: DaBlockCosts, - min_exec_gas_price: u64, - exec_gas_price_change_percent: u64, - l2_block_fullness_threshold_percent: u64, - ) -> Result { - let old_metadata = metadata_storage - .get_metadata(&target_block_height) - .map_err(|err| Error::CouldNotInitUpdater(anyhow::anyhow!(err)))? - .ok_or(Error::CouldNotInitUpdater(anyhow::anyhow!( - "No metadata found for block height: {:?}", - target_block_height - )))?; - let inner = match old_metadata { - UpdaterMetadata::V0(old) => { - let v0 = AlgorithmUpdaterV0::new( - old.new_exec_price, - min_exec_gas_price, - exec_gas_price_change_percent, - old.l2_block_height, - l2_block_fullness_threshold_percent, - ); - AlgorithmUpdater::V0(v0) - } - }; - let updater = Self { - inner, - l2_block_source, - metadata_storage, - da_block_costs, - }; - Ok(updater) - } - - fn validate_block_gas_capacity( - &self, - block_gas_capacity: u64, - ) -> anyhow::Result { - NonZeroU64::new(block_gas_capacity) - .ok_or_else(|| anyhow!("Block gas capacity must be non-zero")) - } - - async fn set_metadata(&mut self) -> anyhow::Result<()> { - let metadata = self.inner.clone().into(); - self.metadata_storage - .set_metadata(&metadata) - .map_err(|err| anyhow!(err)) - } - - async fn handle_normal_block( - &mut self, - height: u32, - gas_used: u64, - block_gas_capacity: u64, - ) -> anyhow::Result<()> { - let capacity = self.validate_block_gas_capacity(block_gas_capacity)?; - - match &mut self.inner { - AlgorithmUpdater::V0(updater) => { - updater.update_l2_block_data(height, gas_used, capacity)?; - } - AlgorithmUpdater::V1(_) => { - return Err(anyhow!("V1 of the gas price algo has not been enabled yet")) - // TODO(#2139): update the DA record data with data received from the source - // updater.update_da_record_data(vec![])?; - } - } - - self.set_metadata().await?; - Ok(()) - } - - async fn apply_block_info_to_gas_algorithm( - &mut self, - l2_block: BlockInfo, - ) -> anyhow::Result<()> { - match l2_block { - BlockInfo::GenesisBlock => { - self.set_metadata().await?; - } - BlockInfo::Block { - height, - gas_used, - block_gas_capacity, - } => { - self.handle_normal_block(height, gas_used, block_gas_capacity) - .await?; - } - } - Ok(()) - } -} - -#[async_trait::async_trait] -impl UpdateAlgorithm - for FuelGasPriceUpdater -where - L2: L2BlockSource, - Metadata: MetadataStorage, - DaBlockCosts: GetDaBlockCosts, -{ - type Algorithm = Algorithm; - - fn start(&self, _for_block: BlockHeight) -> Self::Algorithm { - self.inner.algorithm() - } - - async fn next(&mut self) -> anyhow::Result { - let l2_block_res = self - .l2_block_source - .get_l2_block(self.inner.l2_block_height()) - .await; - tracing::info!("Received L2 block result: {:?}", l2_block_res); - let l2_block = l2_block_res?; - - self.apply_block_info_to_gas_algorithm(l2_block).await?; - - Ok(self.inner.algorithm()) - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Algorithm { - V0(AlgorithmV0), - V1(AlgorithmV1), -} - -impl GasPriceAlgorithm for Algorithm { - fn next_gas_price(&self) -> u64 { - match self { - Algorithm::V0(v0) => v0.calculate(), - Algorithm::V1(v1) => v1.calculate(), - } - } - - fn worst_case_gas_price(&self, height: BlockHeight) -> u64 { - match self { - Algorithm::V0(v0) => v0.worst_case(height.into()), - Algorithm::V1(v1) => v1.worst_case(height.into()), - } - } -}