From cbc6dee4a39561f33a2d456d685fffbcb1d9a29b Mon Sep 17 00:00:00 2001 From: rymnc <43716372+rymnc@users.noreply.github.com> Date: Mon, 30 Dec 2024 23:42:46 +0530 Subject: [PATCH 01/24] chore(gas_price_service_v1): strictly ensure last_recorded_height is set, to avoid initial poll of da source --- .../block_committer_costs.rs | 85 ++----------------- .../gas_price_service/src/v1/service.rs | 7 ++ .../src/v1/uninitialized_task.rs | 6 ++ 3 files changed, 20 insertions(+), 78 deletions(-) diff --git a/crates/services/gas_price_service/src/v1/da_source_service/block_committer_costs.rs b/crates/services/gas_price_service/src/v1/da_source_service/block_committer_costs.rs index fb6077472c4..de932a8edce 100644 --- a/crates/services/gas_price_service/src/v1/da_source_service/block_committer_costs.rs +++ b/crates/services/gas_price_service/src/v1/da_source_service/block_committer_costs.rs @@ -20,8 +20,6 @@ use std::ops::Deref; #[async_trait::async_trait] pub trait BlockCommitterApi: Send + Sync { - /// Used on first run to get the latest costs and seqno - async fn get_latest_costs(&self) -> DaBlockCostsResult>; /// Used to get the costs for a specific seqno async fn get_costs_by_l2_block_number( &self, @@ -103,7 +101,10 @@ where .get_costs_by_l2_block_number(*next_height.deref()) .await? } - None => self.client.get_latest_costs().await?.into_iter().collect(), + None => { + // prevent calling the latest endpoint + return Err(anyhow!("last_recorded_height is None")); + } }; tracing::info!("raw_da_block_costs: {:?}", raw_da_block_costs); @@ -158,23 +159,6 @@ impl BlockCommitterApi for BlockCommitterHttpApi { Ok(vec![]) } } - - async fn get_latest_costs(&self) -> DaBlockCostsResult> { - // Latest: http://localhost:8080/v1/costs?variant=latest&limit=5 - if let Some(url) = &self.url { - tracing::info!("getting latest costs"); - let formatted_url = format!("{url}/v1/costs?variant=latest&limit=1"); - tracing::info!("Formatted URL: {:?}", formatted_url); - let response = self.client.get(formatted_url).send().await?; - tracing::info!("response: {:?}", response); - let raw_da_block_costs = response.json::>().await?; - tracing::info!("Parsed: {:?}", raw_da_block_costs); - // only take the first element, since we are only looking for the most recent - Ok(raw_da_block_costs.first().cloned()) - } else { - Ok(None) - } - } } #[cfg(test)] @@ -290,56 +274,6 @@ mod test_block_committer_http_api { // then assert_eq!(actual, expected); } - - #[test] - fn get_latest_costs__when_url_is_none__then_returns_none() { - let rt = tokio::runtime::Runtime::new().unwrap(); - - // given - let block_committer = BlockCommitterHttpApi::new(None); - - // when - let actual = - rt.block_on(async { block_committer.get_latest_costs().await.unwrap() }); - - // then - assert_eq!(actual, None); - } - - #[test] - fn get_latest_costs__when_url_is_some__then_returns_expected_costs() { - let rt = tokio::runtime::Runtime::new().unwrap(); - let mut mock = FakeServer::new(); - let url = mock.url(); - - // given - let block_committer = BlockCommitterHttpApi::new(Some(url)); - let not_expected = RawDaBlockCosts { - id: 1, - start_height: 1, - end_height: 10, - da_block_height: 1u64.into(), - cost: 1, - size: 1, - }; - mock.add_response(not_expected); - let expected = RawDaBlockCosts { - id: 2, - start_height: 11, - end_height: 20, - da_block_height: 2u64.into(), - cost: 2, - size: 2, - }; - mock.add_response(expected.clone()); - - // when - let actual = - rt.block_on(async { block_committer.get_latest_costs().await.unwrap() }); - - // then - assert_eq!(actual, Some(expected)); - } } #[cfg(any(test, feature = "test-helpers"))] pub mod fake_server { @@ -471,9 +405,6 @@ mod tests { #[async_trait::async_trait] impl BlockCommitterApi for MockBlockCommitterApi { - async fn get_latest_costs(&self) -> DaBlockCostsResult> { - Ok(self.value.clone()) - } async fn get_costs_by_l2_block_number( &self, l2_block_number: u32, @@ -504,19 +435,17 @@ mod tests { } #[tokio::test] - async fn request_da_block_cost__when_last_value_is_none__then_get_latest_costs_is_called( - ) { + async fn request_da_block_cost__when_last_value_is_none__then_error_is_thrown() { // given let da_block_costs = test_da_block_costs(); - let expected = vec![(&da_block_costs).into()]; let mock_api = MockBlockCommitterApi::new(Some(da_block_costs)); let mut block_committer = BlockCommitterDaBlockCosts::new(mock_api, None); // when - let actual = block_committer.request_da_block_costs().await.unwrap(); + let actual = block_committer.request_da_block_costs().await; // then - assert_eq!(actual, expected); + assert!(actual.is_err()); } #[tokio::test] diff --git a/crates/services/gas_price_service/src/v1/service.rs b/crates/services/gas_price_service/src/v1/service.rs index 602b086539b..5efad6d497d 100644 --- a/crates/services/gas_price_service/src/v1/service.rs +++ b/crates/services/gas_price_service/src/v1/service.rs @@ -182,6 +182,13 @@ where storage_tx .set_recorded_height(recorded_height) .map_err(|err| anyhow!(err))?; + } else { + // we default to the l2 block height + // this is done so that we poll the da with a specific height + // to avoid initial loss + storage_tx + .set_recorded_height(height.into()) + .map_err(|err| anyhow!(err))?; } let fee_in_wei = u128::from(block_fees).saturating_mul(1_000_000_000); diff --git a/crates/services/gas_price_service/src/v1/uninitialized_task.rs b/crates/services/gas_price_service/src/v1/uninitialized_task.rs index db77274ccca..fb8e3740879 100644 --- a/crates/services/gas_price_service/src/v1/uninitialized_task.rs +++ b/crates/services/gas_price_service/src/v1/uninitialized_task.rs @@ -160,7 +160,13 @@ where if let Some(last_recorded_height) = self.gas_price_db.get_recorded_height()? { self.da_source.set_last_value(last_recorded_height).await?; tracing::info!("Set last recorded height to {}", last_recorded_height); + } else { + tracing::info!("No recorded height found"); + self.da_source + .set_last_value(latest_block_height.into()) + .await?; } + let poll_duration = self .config .da_poll_interval From dba77da57cd0636931d3e9621dd8e05f2549880f Mon Sep 17 00:00:00 2001 From: rymnc <43716372+rymnc@users.noreply.github.com> Date: Tue, 31 Dec 2024 01:54:30 +0530 Subject: [PATCH 02/24] fix --- tests/tests/gas_price.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/tests/gas_price.rs b/tests/tests/gas_price.rs index a86c5d5a67d..b64f65871f9 100644 --- a/tests/tests/gas_price.rs +++ b/tests/tests/gas_price.rs @@ -612,9 +612,9 @@ fn produce_block__l1_committed_block_affects_gas_price() { let mut mock = FakeServer::new(); let url = mock.url(); let costs = RawDaBlockCosts { - id: 1, - start_height: 1, - end_height: 1, + id: 2, + start_height: 2, + end_height: 2, da_block_height: DaBlockHeight(100), cost: 100, size: 100, @@ -634,7 +634,7 @@ fn produce_block__l1_committed_block_affects_gas_price() { ]); // when - let new_gas_price = rt + let new_gas_price: u64 = rt .block_on(async { let driver = FuelCoreDriver::spawn_with_directory(temp_dir, &args) .await From 3b60422222846b547e17ee011fdc0c581b3bf7ee Mon Sep 17 00:00:00 2001 From: rymnc <43716372+rymnc@users.noreply.github.com> Date: Tue, 31 Dec 2024 03:25:22 +0530 Subject: [PATCH 03/24] chore: add test for gas price service --- .../src/common/fuel_core_storage_adapter.rs | 2 +- .../services/gas_price_service/src/ports.rs | 2 +- .../src/v1/da_source_service/service.rs | 1 - .../gas_price_service/src/v1/service.rs | 77 +++++++++++++++++++ .../gas_price_service/src/v1/tests.rs | 1 + 5 files changed, 80 insertions(+), 3 deletions(-) diff --git a/crates/services/gas_price_service/src/common/fuel_core_storage_adapter.rs b/crates/services/gas_price_service/src/common/fuel_core_storage_adapter.rs index 14b0675d762..6ba2904c57d 100644 --- a/crates/services/gas_price_service/src/common/fuel_core_storage_adapter.rs +++ b/crates/services/gas_price_service/src/common/fuel_core_storage_adapter.rs @@ -119,7 +119,7 @@ impl GasPriceServiceAtomicStorage for Storage where Storage: 'static, Storage: GetMetadataStorage + GetLatestRecordedHeight, - Storage: KeyValueInspect + Modifiable + Send + Sync, + Storage: KeyValueInspect + Modifiable + Send + Sync + Clone, { type Transaction<'a> = StorageTransaction<&'a mut Storage> where Self: 'a; diff --git a/crates/services/gas_price_service/src/ports.rs b/crates/services/gas_price_service/src/ports.rs index 7670b3e0eab..bd8cb4918c0 100644 --- a/crates/services/gas_price_service/src/ports.rs +++ b/crates/services/gas_price_service/src/ports.rs @@ -44,7 +44,7 @@ pub trait GetLatestRecordedHeight: Send + Sync { fn get_recorded_height(&self) -> Result>; } -pub trait GasPriceServiceAtomicStorage +pub trait GasPriceServiceAtomicStorage: Clone where Self: 'static, Self: Send + Sync, diff --git a/crates/services/gas_price_service/src/v1/da_source_service/service.rs b/crates/services/gas_price_service/src/v1/da_source_service/service.rs index 675d82cda55..90e5acdb7ed 100644 --- a/crates/services/gas_price_service/src/v1/da_source_service/service.rs +++ b/crates/services/gas_price_service/src/v1/da_source_service/service.rs @@ -112,7 +112,6 @@ where /// This function polls the source according to a polling interval /// described by the DaBlockCostsService async fn run(&mut self, state_watcher: &mut StateWatcher) -> TaskNextAction { - tracing::debug!("111111111111111111111111111111111"); tokio::select! { biased; _ = state_watcher.while_started() => { diff --git a/crates/services/gas_price_service/src/v1/service.rs b/crates/services/gas_price_service/src/v1/service.rs index 5efad6d497d..bd69fa2d45c 100644 --- a/crates/services/gas_price_service/src/v1/service.rs +++ b/crates/services/gas_price_service/src/v1/service.rs @@ -101,6 +101,10 @@ where self.apply_block_info_to_gas_algorithm(block).await?; Ok(()) } + + fn storage(&self) -> AtomicStorage { + self.storage_tx_provider.clone() + } } impl GasPriceServiceV1 @@ -389,6 +393,7 @@ mod tests { }, }, ports::{ + GetLatestRecordedHeight, GetMetadataStorage, SetMetadataStorage, }, @@ -801,4 +806,76 @@ mod tests { service.shutdown().await.unwrap(); } + + #[tokio::test] + async fn run__stores_l2_height_as_recorded_if_previous_doesnt_exist() { + // given + let block_height = 1; + let l2_block = BlockInfo::Block { + height: block_height, + gas_used: 60, + block_gas_capacity: 100, + block_bytes: 100, + block_fees: 100, + }; + + let (l2_block_sender, l2_block_receiver) = mpsc::channel(1); + let l2_block_source = FakeL2BlockSource { + l2_block: l2_block_receiver, + }; + + let metadata_storage = FakeMetadata::empty(); + let l2_block_height = 0; + let config = V1AlgorithmConfig { + new_exec_gas_price: 100, + min_exec_gas_price: 50, + exec_gas_price_change_percent: 20, + l2_block_fullness_threshold_percent: 20, + gas_price_factor: NonZeroU64::new(10).unwrap(), + min_da_gas_price: 10, + max_da_gas_price_change_percent: 20, + da_p_component: 4, + da_d_component: 2, + normal_range_size: 10, + capped_range_size: 100, + decrease_range_size: 4, + block_activity_threshold: 20, + da_poll_interval: None, + }; + let inner = database(); + let (algo_updater, shared_algo) = + initialize_algorithm(&config, l2_block_height, &metadata_storage).unwrap(); + + let notifier = Arc::new(tokio::sync::Notify::new()); + let dummy_da_source = DaSourceService::new( + DummyDaBlockCosts::new( + Err(anyhow::anyhow!("unused at the moment")), + notifier.clone(), + ), + None, + ); + let da_service_runner = ServiceRunner::new(dummy_da_source); + da_service_runner.start_and_await().await.unwrap(); + + let mut service = GasPriceServiceV1::new( + l2_block_source, + shared_algo, + algo_updater, + da_service_runner, + inner, + ); + let read_algo = service.next_block_algorithm(); + let mut watcher = StateWatcher::started(); + let initial_price = read_algo.next_gas_price(); + + // when + l2_block_sender.send(l2_block).await.unwrap(); + service.run(&mut watcher).await; + let storage_provider = service.storage_tx_provider().clone(); + service.shutdown().await.unwrap(); + + // then + let recorded_height = storage_provider.get_recorded_height().unwrap().unwrap(); + assert_eq!(recorded_height, BlockHeight::from(block_height)); + } } diff --git a/crates/services/gas_price_service/src/v1/tests.rs b/crates/services/gas_price_service/src/v1/tests.rs index f43671b3420..ddc9d23be0d 100644 --- a/crates/services/gas_price_service/src/v1/tests.rs +++ b/crates/services/gas_price_service/src/v1/tests.rs @@ -151,6 +151,7 @@ impl GetMetadataStorage for FakeMetadata { } } +#[derive(Clone)] struct ErroringPersistedData; impl SetMetadataStorage for ErroringPersistedData { From b151207e5ba165a56e8d660b7ae0d591d9ab3980 Mon Sep 17 00:00:00 2001 From: rymnc <43716372+rymnc@users.noreply.github.com> Date: Tue, 31 Dec 2024 13:32:09 +0530 Subject: [PATCH 04/24] fix: remove unnecessary storage set, add test for uninit task instead --- .../services/gas_price_service/src/ports.rs | 2 +- .../gas_price_service/src/v1/service.rs | 83 ------------------- .../gas_price_service/src/v1/tests.rs | 49 ++++++++++- 3 files changed, 49 insertions(+), 85 deletions(-) diff --git a/crates/services/gas_price_service/src/ports.rs b/crates/services/gas_price_service/src/ports.rs index bd8cb4918c0..7670b3e0eab 100644 --- a/crates/services/gas_price_service/src/ports.rs +++ b/crates/services/gas_price_service/src/ports.rs @@ -44,7 +44,7 @@ pub trait GetLatestRecordedHeight: Send + Sync { fn get_recorded_height(&self) -> Result>; } -pub trait GasPriceServiceAtomicStorage: Clone +pub trait GasPriceServiceAtomicStorage where Self: 'static, Self: Send + Sync, diff --git a/crates/services/gas_price_service/src/v1/service.rs b/crates/services/gas_price_service/src/v1/service.rs index bd69fa2d45c..cec290cedd4 100644 --- a/crates/services/gas_price_service/src/v1/service.rs +++ b/crates/services/gas_price_service/src/v1/service.rs @@ -101,10 +101,6 @@ where self.apply_block_info_to_gas_algorithm(block).await?; Ok(()) } - - fn storage(&self) -> AtomicStorage { - self.storage_tx_provider.clone() - } } impl GasPriceServiceV1 @@ -186,13 +182,6 @@ where storage_tx .set_recorded_height(recorded_height) .map_err(|err| anyhow!(err))?; - } else { - // we default to the l2 block height - // this is done so that we poll the da with a specific height - // to avoid initial loss - storage_tx - .set_recorded_height(height.into()) - .map_err(|err| anyhow!(err))?; } let fee_in_wei = u128::from(block_fees).saturating_mul(1_000_000_000); @@ -806,76 +795,4 @@ mod tests { service.shutdown().await.unwrap(); } - - #[tokio::test] - async fn run__stores_l2_height_as_recorded_if_previous_doesnt_exist() { - // given - let block_height = 1; - let l2_block = BlockInfo::Block { - height: block_height, - gas_used: 60, - block_gas_capacity: 100, - block_bytes: 100, - block_fees: 100, - }; - - let (l2_block_sender, l2_block_receiver) = mpsc::channel(1); - let l2_block_source = FakeL2BlockSource { - l2_block: l2_block_receiver, - }; - - let metadata_storage = FakeMetadata::empty(); - let l2_block_height = 0; - let config = V1AlgorithmConfig { - new_exec_gas_price: 100, - min_exec_gas_price: 50, - exec_gas_price_change_percent: 20, - l2_block_fullness_threshold_percent: 20, - gas_price_factor: NonZeroU64::new(10).unwrap(), - min_da_gas_price: 10, - max_da_gas_price_change_percent: 20, - da_p_component: 4, - da_d_component: 2, - normal_range_size: 10, - capped_range_size: 100, - decrease_range_size: 4, - block_activity_threshold: 20, - da_poll_interval: None, - }; - let inner = database(); - let (algo_updater, shared_algo) = - initialize_algorithm(&config, l2_block_height, &metadata_storage).unwrap(); - - let notifier = Arc::new(tokio::sync::Notify::new()); - let dummy_da_source = DaSourceService::new( - DummyDaBlockCosts::new( - Err(anyhow::anyhow!("unused at the moment")), - notifier.clone(), - ), - None, - ); - let da_service_runner = ServiceRunner::new(dummy_da_source); - da_service_runner.start_and_await().await.unwrap(); - - let mut service = GasPriceServiceV1::new( - l2_block_source, - shared_algo, - algo_updater, - da_service_runner, - inner, - ); - let read_algo = service.next_block_algorithm(); - let mut watcher = StateWatcher::started(); - let initial_price = read_algo.next_gas_price(); - - // when - l2_block_sender.send(l2_block).await.unwrap(); - service.run(&mut watcher).await; - let storage_provider = service.storage_tx_provider().clone(); - service.shutdown().await.unwrap(); - - // then - let recorded_height = storage_provider.get_recorded_height().unwrap().unwrap(); - assert_eq!(recorded_height, BlockHeight::from(block_height)); - } } diff --git a/crates/services/gas_price_service/src/v1/tests.rs b/crates/services/gas_price_service/src/v1/tests.rs index ddc9d23be0d..85643a964ad 100644 --- a/crates/services/gas_price_service/src/v1/tests.rs +++ b/crates/services/gas_price_service/src/v1/tests.rs @@ -151,7 +151,6 @@ impl GetMetadataStorage for FakeMetadata { } } -#[derive(Clone)] struct ErroringPersistedData; impl SetMetadataStorage for ErroringPersistedData { @@ -794,3 +793,51 @@ async fn uninitialized_task__init__if_metadata_behind_l2_height_then_sync() { assert_eq!(on_chain_height, algo_updater_height); } + +#[tokio::test] +async fn uninitialized_task__init__sets_block_height_for_da_source_before_starting() { + // given + let metadata_height = 100; + let l2_height = 200; + let config = zero_threshold_arbitrary_config(); + + let metadata = V1Metadata { + new_scaled_exec_price: 100, + l2_block_height: metadata_height, + new_scaled_da_gas_price: 0, + gas_price_factor: NonZeroU64::new(100).unwrap(), + total_da_rewards_excess: 0, + latest_known_total_da_cost_excess: 0, + last_profit: 0, + second_to_last_profit: 0, + latest_da_cost_per_byte: 0, + unrecorded_block_bytes: 0, + }; + let gas_price_db = gas_price_database_with_metadata(&metadata); + let mut onchain_db = FakeOnChainDb::new(l2_height); + for height in 1..=l2_height { + let block = arb_block(); + onchain_db.blocks.insert(BlockHeight::from(height), block); + } + let da_source = FakeDABlockCost::never_returns(); + let latest_received_height_arc = da_source.latest_received_height.clone(); + + let service = UninitializedTask::new( + config, + Some(metadata_height.into()), + 0.into(), + FakeSettings::default(), + empty_block_stream(), + gas_price_db, + da_source, + onchain_db.clone(), + ) + .unwrap(); + + // when + let _ = service.init(&StateWatcher::started()).await.unwrap(); + + // then + let latest_received_height = (*latest_received_height_arc.lock().unwrap()).unwrap(); + assert_eq!(latest_received_height, l2_height.into()); +} From 24569eb64ac67223a1335dc39fbcaadfbb2a0a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Tue, 31 Dec 2024 15:52:08 +0100 Subject: [PATCH 05/24] Adopt simulator to the latest changes in types --- crates/fuel-gas-price-algorithm/src/v1.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index 7fe26ce10e3..b53403c8644 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -3,10 +3,7 @@ use std::{ cmp::max, collections::BTreeMap, num::NonZeroU64, - ops::{ - Div, - RangeInclusive, - }, + ops::Div, }; #[cfg(test)] @@ -352,7 +349,7 @@ impl core::ops::Deref for ClampedPercentage { impl AlgorithmUpdaterV1 { pub fn update_da_record_data( &mut self, - heights: RangeInclusive, + heights: &[u32], recorded_bytes: u32, recording_cost: u128, unrecorded_blocks: &mut U, @@ -587,7 +584,7 @@ impl AlgorithmUpdaterV1 { fn da_block_update( &mut self, - heights: RangeInclusive, + heights: &[u32], recorded_bytes: u128, recording_cost: u128, unrecorded_blocks: &mut U, @@ -616,7 +613,7 @@ impl AlgorithmUpdaterV1 { // Always remove the blocks from the unrecorded blocks so they don't build up indefinitely fn update_unrecorded_block_bytes( &mut self, - heights: RangeInclusive, + heights: &[u32], unrecorded_blocks: &mut U, ) -> Result<(), Error> { let mut total: u128 = 0; From b553b2c74a0042c26085c106b8ac4eeae8587cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Tue, 31 Dec 2024 17:14:55 +0100 Subject: [PATCH 06/24] Add new command --- .../gas-price-analysis/src/main.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs index 998f508151b..6973c4bd742 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs @@ -66,15 +66,30 @@ enum Mode { #[derive(Subcommand)] enum Source { + /// Generate arbitrary blocks Generated { + /// Number of blocks to generate size: usize, }, + /// Use predefined L2 block data from file (legacy format) Predefined { + /// Path to the file containing predefined blocks (.csv file with the following + /// columns: block_number, excess_blob_gas, blob_gas_used, blob_fee_wei, + /// blob_fee_wei_for_1_blob, blob_fee_wei_for_2_blobs, blob_fee_wei_for_3_blobs) file_path: String, - /// The number of L2 blocks to include from source + /// The number of blocks to include from source #[arg(short, long)] sample_size: Option, }, + /// Use predefined L1 and L2 block data from a file + Predefined2 { + /// Path to the file containing predefined blocks (.csv file with the following + /// columns: L1_block_number, L1_blob_fee_wei, L2_block_number, L2_fulness, L2_size) + file_path: String, + /// The number of L2 blocks per single L1 blob + #[arg(short, long)] + l2_blocks_per_blob: u16, + }, } #[tokio::main] From c74f4e6fb3bb4f2b91aaa096fcdcd7716d51013e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Tue, 31 Dec 2024 17:15:13 +0100 Subject: [PATCH 07/24] Use reflection to get fields --- .../gas-price-analysis/Cargo.toml | 1 + .../src/simulation/da_cost_per_byte.rs | 96 ++++++++++++++++--- 2 files changed, 83 insertions(+), 14 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml b/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml index 200977ff82a..edc96e79959 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml @@ -16,4 +16,5 @@ plotters = "0.3.5" rand = "0.8.5" rand_distr = "0.4.3" serde = { version = "1.0.209", features = ["derive"] } +serde-reflection = "0.5.0" tokio = { version = "1.40.0", features = ["macros", "rt", "rt-multi-thread"] } diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs index 911dcf713d8..68a024f1ac9 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs @@ -3,8 +3,32 @@ use crate::{ simulation::gen_noisy_signal, Source, }; +use serde_reflection::{ + Samples, + Tracer, + TracerConfig, +}; use std::iter; +const PREDEFINED_CSV_COLUMNS: &[&str] = &[ + "block_number", + "excess_blob_gas", + "blob_gas_used", + "blob_fee_wei", + "blob_fee_wei_for_1_blob", + "blob_fee_wei_for_2_blobs", + "blob_fee_wei_for_3_blobs", +]; +const PREDEFINED_L2_BLOCKS_PER_L1_BLOCK: usize = 12; + +const PREDEFINED_2_CSV_COLUMNS: &[&str] = &[ + "L1_block_number", + "L1_blob_fee_wei", + "L2_block_number", + "L2_fulness", + "L2_size", +]; + pub fn get_da_cost_per_byte_from_source( source: Source, update_period: usize, @@ -21,12 +45,22 @@ pub fn get_da_cost_per_byte_from_source( .flat_map(|x| iter::repeat(x).take(update_period)) .collect() } + Source::Predefined2 { + file_path, + l2_blocks_per_blob, + } => { + let original = get_costs_from_csv_file(&file_path, None); + todo!() + } } } +trait HasBlobFee { + fn blob_fee_wei(&self) -> u64; +} #[allow(dead_code)] -#[derive(Debug, serde::Deserialize)] -struct Record { +#[derive(Debug, serde::Deserialize, Default, serde::Serialize)] +struct PredefinedRecord { block_number: u64, excess_blob_gas: u64, blob_gas_used: u64, @@ -36,30 +70,64 @@ struct Record { blob_fee_wei_for_3_blobs: u64, } +impl HasBlobFee for PredefinedRecord { + fn blob_fee_wei(&self) -> u64 { + self.blob_fee_wei + } +} + +#[allow(dead_code)] +#[derive(Debug, serde::Deserialize)] +struct Predefined2Record { + l1_block_number: u64, + l1_blob_fee_wei: u64, + l2_block_number: u64, + l2_fulness: u64, + l2_size: u64, +} + +impl HasBlobFee for Predefined2Record { + fn blob_fee_wei(&self) -> u64 { + self.l1_blob_fee_wei + } +} + +fn fields_of_struct_in_order() -> Vec +where + T: serde::de::DeserializeOwned, +{ + let mut tracer = Tracer::new(TracerConfig::default()); + let samples = Samples::new(); + tracer.trace_type::(&samples).unwrap(); + let type_name = std::any::type_name::().split("::").last().unwrap(); + let registry = tracer.registry().unwrap(); + let Some(serde_reflection::ContainerFormat::Struct(fields)) = registry.get(type_name) + else { + panic!("No fields?") + }; + + fields.iter().map(|f| f.name.clone()).collect() +} + fn get_costs_from_csv_file(file_path: &str, sample_size: Option) -> Vec { let mut rdr = csv::ReaderBuilder::new() .has_headers(true) .from_path(file_path) .unwrap(); let mut costs = vec![]; - let headers = csv::StringRecord::from(vec![ - "block_number", - "excess_blob_gas", - "blob_gas_used", - "blob_fee_wei", - "blob_fee_wei_for_1_blob", - "blob_fee_wei_for_2_blobs", - "blob_fee_wei_for_3_blobs", - ]); + + let headers = + csv::StringRecord::from(fields_of_struct_in_order::()); + let mut max_cost = 0; for record in rdr.records().skip(1) { - const L2_BLOCKS_PER_L1_BLOCK: usize = 12; if let Some(size) = sample_size { - if costs.len() >= size / L2_BLOCKS_PER_L1_BLOCK { + if costs.len() >= size / PREDEFINED_L2_BLOCKS_PER_L1_BLOCK { break; } }; - let record: Record = record.unwrap().deserialize(Some(&headers)).unwrap(); + let record: PredefinedRecord = + record.unwrap().deserialize(Some(&headers)).unwrap(); let cost = record.blob_fee_wei; if cost > max_cost { max_cost = cost; From 6f9d5953dda2219a6a8b8f9f1d1fe18589325fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Tue, 31 Dec 2024 17:20:03 +0100 Subject: [PATCH 08/24] Properly retrieve `blob_fee_wei()` --- .../src/simulation/da_cost_per_byte.rs | 38 ++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs index 68a024f1ac9..a45e5bd93b9 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs @@ -10,25 +10,8 @@ use serde_reflection::{ }; use std::iter; -const PREDEFINED_CSV_COLUMNS: &[&str] = &[ - "block_number", - "excess_blob_gas", - "blob_gas_used", - "blob_fee_wei", - "blob_fee_wei_for_1_blob", - "blob_fee_wei_for_2_blobs", - "blob_fee_wei_for_3_blobs", -]; const PREDEFINED_L2_BLOCKS_PER_L1_BLOCK: usize = 12; -const PREDEFINED_2_CSV_COLUMNS: &[&str] = &[ - "L1_block_number", - "L1_blob_fee_wei", - "L2_block_number", - "L2_fulness", - "L2_size", -]; - pub fn get_da_cost_per_byte_from_source( source: Source, update_period: usize, @@ -39,7 +22,8 @@ pub fn get_da_cost_per_byte_from_source( file_path, sample_size, } => { - let original = get_costs_from_csv_file(&file_path, sample_size); + let original = + get_costs_from_csv_file::(&file_path, sample_size); original .into_iter() .flat_map(|x| iter::repeat(x).take(update_period)) @@ -49,7 +33,7 @@ pub fn get_da_cost_per_byte_from_source( file_path, l2_blocks_per_blob, } => { - let original = get_costs_from_csv_file(&file_path, None); + let original = get_costs_from_csv_file::(&file_path, None); todo!() } } @@ -109,26 +93,28 @@ where fields.iter().map(|f| f.name.clone()).collect() } -fn get_costs_from_csv_file(file_path: &str, sample_size: Option) -> Vec { +fn get_costs_from_csv_file(file_path: &str, sample_size: Option) -> Vec +where + T: serde::de::DeserializeOwned + HasBlobFee, +{ let mut rdr = csv::ReaderBuilder::new() .has_headers(true) .from_path(file_path) .unwrap(); let mut costs = vec![]; - let headers = - csv::StringRecord::from(fields_of_struct_in_order::()); + let headers = csv::StringRecord::from(fields_of_struct_in_order::()); let mut max_cost = 0; - for record in rdr.records().skip(1) { + for record in rdr.records() { if let Some(size) = sample_size { if costs.len() >= size / PREDEFINED_L2_BLOCKS_PER_L1_BLOCK { break; } }; - let record: PredefinedRecord = - record.unwrap().deserialize(Some(&headers)).unwrap(); - let cost = record.blob_fee_wei; + let record: T = record.unwrap().deserialize(Some(&headers)).unwrap(); + let cost = record.blob_fee_wei(); + dbg!(&cost); if cost > max_cost { max_cost = cost; } From dbaceb8145bec805b43cfa807a7540850a95f109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Thu, 2 Jan 2025 10:39:27 +0100 Subject: [PATCH 09/24] Properly fetch L1 fee for both algorithms --- .../gas-price-analysis/src/main.rs | 6 +- .../src/simulation/da_cost_per_byte.rs | 68 ++++++++++--------- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs index 6973c4bd742..e2602380285 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs @@ -77,7 +77,9 @@ enum Source { /// columns: block_number, excess_blob_gas, blob_gas_used, blob_fee_wei, /// blob_fee_wei_for_1_blob, blob_fee_wei_for_2_blobs, blob_fee_wei_for_3_blobs) file_path: String, - /// The number of blocks to include from source + /// The number of blocks to include from source, specified in L1 blocks. + /// This algorithm assumes that there are 12 L2 blocks per L1 block, so if you + /// pass 1200 here, it will pull 100 L1 blocks from the source file. #[arg(short, long)] sample_size: Option, }, @@ -88,7 +90,7 @@ enum Source { file_path: String, /// The number of L2 blocks per single L1 blob #[arg(short, long)] - l2_blocks_per_blob: u16, + l2_blocks_per_blob: usize, }, } diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs index a45e5bd93b9..3b4f0b99cc9 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs @@ -21,22 +21,23 @@ pub fn get_da_cost_per_byte_from_source( Source::Predefined { file_path, sample_size, - } => { - let original = - get_costs_from_csv_file::(&file_path, sample_size); - original - .into_iter() - .flat_map(|x| iter::repeat(x).take(update_period)) - .collect() - } + } => get_costs_from_csv_file::( + &file_path, + sample_size, + PREDEFINED_L2_BLOCKS_PER_L1_BLOCK, + ), Source::Predefined2 { file_path, l2_blocks_per_blob, - } => { - let original = get_costs_from_csv_file::(&file_path, None); - todo!() - } + } => get_costs_from_csv_file::( + &file_path, + None, + l2_blocks_per_blob, + ), } + .into_iter() + .flat_map(|x| iter::repeat(x).take(update_period)) + .collect() } trait HasBlobFee { @@ -93,7 +94,11 @@ where fields.iter().map(|f| f.name.clone()).collect() } -fn get_costs_from_csv_file(file_path: &str, sample_size: Option) -> Vec +fn get_costs_from_csv_file( + file_path: &str, + sample_size: Option, + l2_blocks_per_blob: usize, +) -> Vec where T: serde::de::DeserializeOwned + HasBlobFee, { @@ -101,29 +106,28 @@ where .has_headers(true) .from_path(file_path) .unwrap(); - let mut costs = vec![]; - let headers = csv::StringRecord::from(fields_of_struct_in_order::()); - let mut max_cost = 0; - for record in rdr.records() { - if let Some(size) = sample_size { - if costs.len() >= size / PREDEFINED_L2_BLOCKS_PER_L1_BLOCK { - break; - } - }; - let record: T = record.unwrap().deserialize(Some(&headers)).unwrap(); - let cost = record.blob_fee_wei(); - dbg!(&cost); - if cost > max_cost { - max_cost = cost; - } - costs.push(cost); - } - println!("Max cost: {}", prettify_number(max_cost)); + let costs: Vec<_> = rdr + .records() + .step_by(l2_blocks_per_blob as usize) + .take( + sample_size + .map(|size| size / l2_blocks_per_blob) + .unwrap_or(usize::MAX), + ) + .map(|record| { + let record: T = record.unwrap().deserialize(Some(&headers)).unwrap(); + record.blob_fee_wei() + }) + .collect(); + + println!( + "Max cost: {}", + prettify_number(costs.iter().max_by(|a, b| a.cmp(b)).unwrap()) + ); costs } - fn noisy_eth_price>(input: T) -> f64 where >::Error: core::fmt::Debug, From c909cb400a049faa17b89c9f0aa1487c546c068e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Thu, 2 Jan 2025 10:50:26 +0100 Subject: [PATCH 10/24] Add some clarifying comments --- .../src/simulation/da_cost_per_byte.rs | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs index 3b4f0b99cc9..44f8d1a8cad 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs @@ -21,7 +21,7 @@ pub fn get_da_cost_per_byte_from_source( Source::Predefined { file_path, sample_size, - } => get_costs_from_csv_file::( + } => get_l1_costs_from_csv_file::( &file_path, sample_size, PREDEFINED_L2_BLOCKS_PER_L1_BLOCK, @@ -29,7 +29,7 @@ pub fn get_da_cost_per_byte_from_source( Source::Predefined2 { file_path, l2_blocks_per_blob, - } => get_costs_from_csv_file::( + } => get_l1_costs_from_csv_file::( &file_path, None, l2_blocks_per_blob, @@ -94,7 +94,24 @@ where fields.iter().map(|f| f.name.clone()).collect() } -fn get_costs_from_csv_file( +// This function is used to read the CSV file and extract the blob fee from it. +// The `sample_size` and `l2_blocks_per_blob` parameters are used to tailor the +// behavior to the slightly different approaches used in the two different CSV files. +// +// "Predefined" CSV file format (aka legacy): +// 1. `sample_size` represents the number of L1 blocks we want to simulate. +// 2. we assume that there are 12 L2 blocks per L1 block. +// 3. because each row in the file is a different L1 block. +// 4. we need to pull `sample_size` / 12 records from the CSV file +// 5. for example, for sample size 1200, algorithm assumes 1200 L1 blocks and pulls 100 L1 blocks from the file +// +// "Predefined2" CSV file format: +// 1. `sample_size` is not used - we always pull all data from the input file +// 2. `l2_blocks_per_blob` is used to determine how many L2 blocks are in a single L1 blob (no assumptions) +// 3. each row in the file is a different L2 block, hence L1 entries are repeated +// 4. so we need to skip `l2_blocks_per_blob` records to get to the next L1 block +// 5. for example, for l2_blocks_per_blob = 12, algorithm takes 1 value, skips 11, takes another value, etc. +fn get_l1_costs_from_csv_file( file_path: &str, sample_size: Option, l2_blocks_per_blob: usize, From 37f76119a4c99c159bf9ab8e2a1d8245fc2a84af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Thu, 2 Jan 2025 11:03:57 +0100 Subject: [PATCH 11/24] Calculate fullness and bytes pre simulation --- .../gas-price-analysis/src/main.rs | 99 +++++++++++++++++-- .../gas-price-analysis/src/optimisation.rs | 10 +- .../gas-price-analysis/src/simulation.rs | 59 +---------- 3 files changed, 103 insertions(+), 65 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs index e2602380285..8abfec3042f 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs @@ -94,43 +94,122 @@ enum Source { }, } +fn fullness_and_bytes_per_block(size: usize, capacity: u64) -> Vec<(u64, u32)> { + let mut rng = StdRng::seed_from_u64(888); + + let fullness_noise: Vec<_> = std::iter::repeat(()) + .take(size) + .map(|_| rng.gen_range(-0.5..0.5)) + // .map(|val| val * capacity as f64) + .collect(); + + const ROUGH_GAS_TO_BYTE_RATIO: f64 = 0.01; + let bytes_scale: Vec<_> = std::iter::repeat(()) + .take(size) + .map(|_| rng.gen_range(0.5..1.0)) + .map(|x| x * ROUGH_GAS_TO_BYTE_RATIO) + .collect(); + + (0usize..size) + .map(|val| val as f64) + .map(noisy_fullness) + .map(|signal| (0.01 * signal + 0.01) * capacity as f64) // Scale and shift so it's between 0 and capacity + .zip(fullness_noise) + .map(|(fullness, noise)| fullness + noise) + .map(|x| f64::min(x, capacity as f64)) + .map(|x| f64::max(x, 5.0)) + .zip(bytes_scale) + .map(|(fullness, bytes_scale)| { + let bytes = fullness * bytes_scale; + (fullness, bytes) + }) + .map(|(fullness, bytes)| (fullness as u64, std::cmp::max(bytes as u32, 1))) + .collect() +} + +// Naive Fourier series +fn gen_noisy_signal(input: f64, components: &[f64]) -> f64 { + components + .iter() + .fold(0f64, |acc, &c| acc + f64::sin(input / c)) + / components.len() as f64 +} + +fn noisy_fullness>(input: T) -> f64 +where + >::Error: core::fmt::Debug, +{ + const COMPONENTS: &[f64] = &[-30.0, 40.0, 700.0, -340.0, 400.0]; + let input = input.try_into().unwrap(); + gen_noisy_signal(input, COMPONENTS) +} + +struct L1FullnessData { + da_cost_per_byte: Vec, + fullness_and_bytes: Vec<(u64, u32)>, +} + +fn fulness_and_bytes_from_source( + source: Source, + capacity: u64, + update_period: usize, +) -> L1FullnessData { + let da_cost_per_byte = get_da_cost_per_byte_from_source(source, update_period); + let size = da_cost_per_byte.len(); + L1FullnessData { + da_cost_per_byte, + fullness_and_bytes: fullness_and_bytes_per_block(size, capacity), + } +} + #[tokio::main] async fn main() -> anyhow::Result<()> { let args = Arg::parse(); const UPDATE_PERIOD: usize = 12; + const CAPACITY: u64 = 30_000_000; let da_finalization_period = args.da_finalization_period; let (results, (p_comp, d_comp)) = match args.mode { Mode::WithValues { p, d, source } => { - let da_cost_per_byte = - get_da_cost_per_byte_from_source(source, UPDATE_PERIOD); - let size = da_cost_per_byte.len(); + let L1FullnessData { + da_cost_per_byte, + fullness_and_bytes, + } = fulness_and_bytes_from_source(source, CAPACITY, UPDATE_PERIOD); + println!( "Running simulation with P: {}, D: {}, {} blocks and {} da_finalization_period", prettify_number(p), prettify_number(d), - prettify_number(size), + prettify_number(da_cost_per_byte.len()), prettify_number(da_finalization_period) ); let simulator = Simulator::new(da_cost_per_byte); - let result = - simulator.run_simulation(p, d, UPDATE_PERIOD, da_finalization_period); + let result = simulator.run_simulation( + p, + d, + UPDATE_PERIOD, + &fullness_and_bytes, + da_finalization_period, + ); (result, (p, d)) } Mode::Optimization { iterations, source } => { - let da_cost_per_byte = - get_da_cost_per_byte_from_source(source, UPDATE_PERIOD); - let size = da_cost_per_byte.len(); + let L1FullnessData { + da_cost_per_byte, + fullness_and_bytes, + } = fulness_and_bytes_from_source(source, CAPACITY, UPDATE_PERIOD); println!( - "Running optimization with {iterations} iterations and {size} blocks" + "Running optimization with {iterations} iterations and {} blocks", + da_cost_per_byte.len() ); let simulator = Simulator::new(da_cost_per_byte); let (results, (p, d)) = naive_optimisation( &simulator, iterations as usize, UPDATE_PERIOD, + &fullness_and_bytes, da_finalization_period, ) .await; diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/optimisation.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/optimisation.rs index 4943091fb84..17af6d31df3 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/optimisation.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/optimisation.rs @@ -16,15 +16,23 @@ pub async fn naive_optimisation( simulator: &Simulator, iterations: usize, update_period: usize, + fullness_and_bytes: &[(u64, u32)], da_recording_rate: usize, ) -> (SimulationResults, (i64, i64)) { let tasks = da_pid_factors(iterations) .into_iter() .map(|(p, d)| { + let cloned_fullness_and_bytes = fullness_and_bytes.to_vec(); let new_simulator = simulator.clone(); let f = move || { ( - new_simulator.run_simulation(p, d, update_period, da_recording_rate), + new_simulator.run_simulation( + p, + d, + update_period, + &cloned_fullness_and_bytes, + da_recording_rate, + ), (p, d), ) }; 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 0080aa1a837..8281dab1c62 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 @@ -52,13 +52,14 @@ impl Simulator { da_p_component: i64, da_d_component: i64, update_period: usize, + fullness_and_bytes: &[(u64, u32)], da_finalization_rate: usize, ) -> SimulationResults { let capacity = 30_000_000; let gas_per_byte = 63; let max_block_bytes = capacity / gas_per_byte; let size = self.da_cost_per_byte.len(); - let fullness_and_bytes = fullness_and_bytes_per_block(size, capacity); + let l2_blocks = fullness_and_bytes.clone().into_iter(); let da_blocks = self.calculate_da_blocks( update_period, @@ -69,8 +70,8 @@ impl Simulator { let blocks = l2_blocks .zip(da_blocks.iter()) .map(|((fullness, bytes), maybe_da_block)| BlockData { - fullness, - bytes, + fullness: *fullness, + bytes: *bytes, maybe_da_bundle: maybe_da_block.clone(), }) .enumerate(); @@ -127,7 +128,7 @@ impl Simulator { &self, capacity: u64, max_block_bytes: u64, - fullness_and_bytes: Vec<(u64, u32)>, + fullness_and_bytes: &[(u64, u32)], blocks: impl Iterator, mut updater: AlgorithmUpdaterV1, ) -> SimulationResults { @@ -267,53 +268,3 @@ impl Simulator { l2_blocks_with_no_da_blocks.chain(da_block_ranges).collect() } } - -// Naive Fourier series -fn gen_noisy_signal(input: f64, components: &[f64]) -> f64 { - components - .iter() - .fold(0f64, |acc, &c| acc + f64::sin(input / c)) - / components.len() as f64 -} - -fn noisy_fullness>(input: T) -> f64 -where - >::Error: core::fmt::Debug, -{ - const COMPONENTS: &[f64] = &[-30.0, 40.0, 700.0, -340.0, 400.0]; - let input = input.try_into().unwrap(); - gen_noisy_signal(input, COMPONENTS) -} - -fn fullness_and_bytes_per_block(size: usize, capacity: u64) -> Vec<(u64, u32)> { - let mut rng = StdRng::seed_from_u64(888); - - let fullness_noise: Vec<_> = std::iter::repeat(()) - .take(size) - .map(|_| rng.gen_range(-0.5..0.5)) - // .map(|val| val * capacity as f64) - .collect(); - - const ROUGH_GAS_TO_BYTE_RATIO: f64 = 0.01; - let bytes_scale: Vec<_> = std::iter::repeat(()) - .take(size) - .map(|_| rng.gen_range(0.5..1.0)) - .map(|x| x * ROUGH_GAS_TO_BYTE_RATIO) - .collect(); - - (0usize..size) - .map(|val| val as f64) - .map(noisy_fullness) - .map(|signal| (0.01 * signal + 0.01) * capacity as f64) // Scale and shift so it's between 0 and capacity - .zip(fullness_noise) - .map(|(fullness, noise)| fullness + noise) - .map(|x| f64::min(x, capacity as f64)) - .map(|x| f64::max(x, 5.0)) - .zip(bytes_scale) - .map(|(fullness, bytes_scale)| { - let bytes = fullness * bytes_scale; - (fullness, bytes) - }) - .map(|(fullness, bytes)| (fullness as u64, std::cmp::max(bytes as u32, 1))) - .collect() -} From e52014f3f17912bcb470540d2239c5520930495d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Thu, 2 Jan 2025 11:36:47 +0100 Subject: [PATCH 12/24] Read L2 block data from file --- .../gas-price-analysis/src/charts.rs | 14 +++ .../gas-price-analysis/src/main.rs | 111 ++++++++++++++++-- .../src/simulation/da_cost_per_byte.rs | 74 ++---------- 3 files changed, 124 insertions(+), 75 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 aa3f401ddcc..8972e9c5bc0 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 @@ -1,3 +1,17 @@ +use plotters::{ + chart::ChartBuilder, + prelude::{ + BitMapBackend, + DrawingArea, + IntoDrawingArea, + PathElement, + }, + series::LineSeries, + style::{ + Color, IntoFont, RGBColor, BLACK, BLUE, RED, WHITE + }, +}; + use super::*; use std::{ fs, diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs index 8abfec3042f..9b27eac0d38 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs @@ -1,8 +1,9 @@ +use std::path::Path; + use crate::{ charts::draw_chart, simulation::da_cost_per_byte::get_da_cost_per_byte_from_source, }; -use plotters::prelude::*; use rand::{ rngs::StdRng, Rng, @@ -10,6 +11,11 @@ use rand::{ }; use plotters::coord::Shift; +use serde_reflection::{ + Samples, + Tracer, + TracerConfig, +}; use crate::{ optimisation::naive_optimisation, @@ -94,7 +100,61 @@ enum Source { }, } -fn fullness_and_bytes_per_block(size: usize, capacity: u64) -> Vec<(u64, u32)> { +trait HasBlobFee { + fn blob_fee_wei(&self) -> u64; +} +#[allow(dead_code)] +#[derive(Debug, serde::Deserialize, Default, serde::Serialize)] +struct PredefinedRecord { + block_number: u64, + excess_blob_gas: u64, + blob_gas_used: u64, + blob_fee_wei: u64, + blob_fee_wei_for_1_blob: u64, + blob_fee_wei_for_2_blobs: u64, + blob_fee_wei_for_3_blobs: u64, +} + +impl HasBlobFee for PredefinedRecord { + fn blob_fee_wei(&self) -> u64 { + self.blob_fee_wei + } +} + +#[allow(dead_code)] +#[derive(Debug, serde::Deserialize)] +struct Predefined2Record { + l1_block_number: u64, + l1_blob_fee_wei: u64, + l2_block_number: u64, + l2_fullness: u64, + l2_size: u64, +} + +impl HasBlobFee for Predefined2Record { + fn blob_fee_wei(&self) -> u64 { + self.l1_blob_fee_wei + } +} + +fn fields_of_struct_in_order() -> Vec +where + T: serde::de::DeserializeOwned, +{ + let mut tracer = Tracer::new(TracerConfig::default()); + let samples = Samples::new(); + tracer.trace_type::(&samples).unwrap(); + let type_name = std::any::type_name::().split("::").last().unwrap(); + let registry = tracer.registry().unwrap(); + let Some(serde_reflection::ContainerFormat::Struct(fields)) = registry.get(type_name) + else { + panic!("No fields?") + }; + + fields.iter().map(|f| f.name.clone()).collect() +} + +fn arb_l2_fullness_and_bytes_per_block(size: usize, capacity: u64) -> Vec<(u64, u32)> { let mut rng = StdRng::seed_from_u64(888); let fullness_noise: Vec<_> = std::iter::repeat(()) @@ -144,21 +204,48 @@ where gen_noisy_signal(input, COMPONENTS) } -struct L1FullnessData { +fn get_l2_costs_from_csv_file>(file_path: P) -> Vec<(u64, u32)> { + let mut rdr = csv::ReaderBuilder::new() + .has_headers(true) + .from_path(file_path) + .unwrap(); + let headers = + csv::StringRecord::from(fields_of_struct_in_order::()); + + rdr.records() + .map(|record| { + let record: Predefined2Record = + record.unwrap().deserialize(Some(&headers)).unwrap(); + (record.l2_fullness, record.l2_size as u32) + }) + .collect() +} + +struct L1L2BlockData { da_cost_per_byte: Vec, fullness_and_bytes: Vec<(u64, u32)>, } -fn fulness_and_bytes_from_source( +fn l1_l2_block_data_from_source( source: Source, capacity: u64, update_period: usize, -) -> L1FullnessData { - let da_cost_per_byte = get_da_cost_per_byte_from_source(source, update_period); +) -> L1L2BlockData { + let da_cost_per_byte = get_da_cost_per_byte_from_source(&source, update_period); let size = da_cost_per_byte.len(); - L1FullnessData { + + let fullness_and_bytes = match source { + Source::Generated { .. } | Source::Predefined { .. } => { + arb_l2_fullness_and_bytes_per_block(size, capacity) + } + Source::Predefined2 { file_path, .. } => get_l2_costs_from_csv_file(file_path), + }; + + dbg!(&fullness_and_bytes); + + L1L2BlockData { da_cost_per_byte, - fullness_and_bytes: fullness_and_bytes_per_block(size, capacity), + fullness_and_bytes, } } @@ -173,10 +260,10 @@ async fn main() -> anyhow::Result<()> { let (results, (p_comp, d_comp)) = match args.mode { Mode::WithValues { p, d, source } => { - let L1FullnessData { + let L1L2BlockData { da_cost_per_byte, fullness_and_bytes, - } = fulness_and_bytes_from_source(source, CAPACITY, UPDATE_PERIOD); + } = l1_l2_block_data_from_source(source, CAPACITY, UPDATE_PERIOD); println!( "Running simulation with P: {}, D: {}, {} blocks and {} da_finalization_period", @@ -196,10 +283,10 @@ async fn main() -> anyhow::Result<()> { (result, (p, d)) } Mode::Optimization { iterations, source } => { - let L1FullnessData { + let L1L2BlockData { da_cost_per_byte, fullness_and_bytes, - } = fulness_and_bytes_from_source(source, CAPACITY, UPDATE_PERIOD); + } = l1_l2_block_data_from_source(source, CAPACITY, UPDATE_PERIOD); println!( "Running optimization with {iterations} iterations and {} blocks", da_cost_per_byte.len() diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs index 44f8d1a8cad..e3f9a5f28c6 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs @@ -1,29 +1,31 @@ use crate::{ + fields_of_struct_in_order, prettify_number, simulation::gen_noisy_signal, Source, }; -use serde_reflection::{ - Samples, - Tracer, - TracerConfig, -}; use std::iter; +use super::{ + HasBlobFee, + Predefined2Record, + PredefinedRecord, +}; + const PREDEFINED_L2_BLOCKS_PER_L1_BLOCK: usize = 12; pub fn get_da_cost_per_byte_from_source( - source: Source, + source: &Source, update_period: usize, ) -> Vec { match source { - Source::Generated { size } => arbitrary_cost_per_byte(size, update_period), + Source::Generated { size } => arbitrary_cost_per_byte(*size, update_period), Source::Predefined { file_path, sample_size, } => get_l1_costs_from_csv_file::( &file_path, - sample_size, + *sample_size, PREDEFINED_L2_BLOCKS_PER_L1_BLOCK, ), Source::Predefined2 { @@ -32,7 +34,7 @@ pub fn get_da_cost_per_byte_from_source( } => get_l1_costs_from_csv_file::( &file_path, None, - l2_blocks_per_blob, + *l2_blocks_per_blob, ), } .into_iter() @@ -40,60 +42,6 @@ pub fn get_da_cost_per_byte_from_source( .collect() } -trait HasBlobFee { - fn blob_fee_wei(&self) -> u64; -} -#[allow(dead_code)] -#[derive(Debug, serde::Deserialize, Default, serde::Serialize)] -struct PredefinedRecord { - block_number: u64, - excess_blob_gas: u64, - blob_gas_used: u64, - blob_fee_wei: u64, - blob_fee_wei_for_1_blob: u64, - blob_fee_wei_for_2_blobs: u64, - blob_fee_wei_for_3_blobs: u64, -} - -impl HasBlobFee for PredefinedRecord { - fn blob_fee_wei(&self) -> u64 { - self.blob_fee_wei - } -} - -#[allow(dead_code)] -#[derive(Debug, serde::Deserialize)] -struct Predefined2Record { - l1_block_number: u64, - l1_blob_fee_wei: u64, - l2_block_number: u64, - l2_fulness: u64, - l2_size: u64, -} - -impl HasBlobFee for Predefined2Record { - fn blob_fee_wei(&self) -> u64 { - self.l1_blob_fee_wei - } -} - -fn fields_of_struct_in_order() -> Vec -where - T: serde::de::DeserializeOwned, -{ - let mut tracer = Tracer::new(TracerConfig::default()); - let samples = Samples::new(); - tracer.trace_type::(&samples).unwrap(); - let type_name = std::any::type_name::().split("::").last().unwrap(); - let registry = tracer.registry().unwrap(); - let Some(serde_reflection::ContainerFormat::Struct(fields)) = registry.get(type_name) - else { - panic!("No fields?") - }; - - fields.iter().map(|f| f.name.clone()).collect() -} - // This function is used to read the CSV file and extract the blob fee from it. // The `sample_size` and `l2_blocks_per_blob` parameters are used to tailor the // behavior to the slightly different approaches used in the two different CSV files. From 97ce5dd64fa6a5da8dc6156a56cd2b2afafbbc76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Thu, 2 Jan 2025 11:48:42 +0100 Subject: [PATCH 13/24] Clean up code structure --- .../gas-price-analysis/src/block_data.rs | 99 ++++++++++++++ .../gas-price-analysis/src/main.rs | 123 ++---------------- .../gas-price-analysis/src/simulation.rs | 20 +-- .../src/simulation/da_cost_per_byte.rs | 14 +- .../gas-price-analysis/src/utils.rs | 30 +++++ 5 files changed, 151 insertions(+), 135 deletions(-) create mode 100644 crates/fuel-gas-price-algorithm/gas-price-analysis/src/block_data.rs create mode 100644 crates/fuel-gas-price-algorithm/gas-price-analysis/src/utils.rs diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/block_data.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/block_data.rs new file mode 100644 index 00000000000..473a04d0460 --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/block_data.rs @@ -0,0 +1,99 @@ +use rand::{ + rngs::StdRng, + Rng, + SeedableRng, +}; + +use crate::utils::gen_noisy_signal; + +pub(super) trait HasBlobFee { + fn blob_fee_wei(&self) -> u64; +} +#[allow(dead_code)] +#[derive(Debug, serde::Deserialize, Default, serde::Serialize)] +pub(super) struct PredefinedRecord { + block_number: u64, + excess_blob_gas: u64, + blob_gas_used: u64, + blob_fee_wei: u64, + blob_fee_wei_for_1_blob: u64, + blob_fee_wei_for_2_blobs: u64, + blob_fee_wei_for_3_blobs: u64, +} + +impl HasBlobFee for PredefinedRecord { + fn blob_fee_wei(&self) -> u64 { + self.blob_fee_wei + } +} + +#[allow(dead_code)] +#[derive(Debug, serde::Deserialize)] +pub(super) struct Predefined2Record { + l1_block_number: u64, + l1_blob_fee_wei: u64, + l2_block_number: u64, + l2_fullness: u64, + l2_size: u64, +} + +impl Predefined2Record { + pub(super) fn l2_fullness(&self) -> u64 { + self.l2_fullness + } + + pub(super) fn l2_size(&self) -> u64 { + self.l2_size + } +} + +impl HasBlobFee for Predefined2Record { + fn blob_fee_wei(&self) -> u64 { + self.l1_blob_fee_wei + } +} + +pub(super) fn arb_l2_fullness_and_bytes_per_block( + size: usize, + capacity: u64, +) -> Vec<(u64, u32)> { + let mut rng = StdRng::seed_from_u64(888); + + let fullness_noise: Vec<_> = std::iter::repeat(()) + .take(size) + .map(|_| rng.gen_range(-0.5..0.5)) + // .map(|val| val * capacity as f64) + .collect(); + + const ROUGH_GAS_TO_BYTE_RATIO: f64 = 0.01; + let bytes_scale: Vec<_> = std::iter::repeat(()) + .take(size) + .map(|_| rng.gen_range(0.5..1.0)) + .map(|x| x * ROUGH_GAS_TO_BYTE_RATIO) + .collect(); + + (0usize..size) + .map(|val| val as f64) + .map(noisy_fullness) + .map(|signal| (0.01 * signal + 0.01) * capacity as f64) // Scale and shift so it's between 0 and capacity + .zip(fullness_noise) + .map(|(fullness, noise)| fullness + noise) + .map(|x| f64::min(x, capacity as f64)) + .map(|x| f64::max(x, 5.0)) + .zip(bytes_scale) + .map(|(fullness, bytes_scale)| { + let bytes = fullness * bytes_scale; + (fullness, bytes) + }) + .map(|(fullness, bytes)| (fullness as u64, std::cmp::max(bytes as u32, 1))) + .collect() +} + +fn noisy_fullness>(input: T) -> f64 +where + >::Error: core::fmt::Debug, +{ + const COMPONENTS: &[f64] = &[-30.0, 40.0, 700.0, -340.0, 400.0]; + let input = input.try_into().unwrap(); + gen_noisy_signal(input, COMPONENTS) +} diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs index 9b27eac0d38..ce14867ecd3 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs @@ -4,6 +4,10 @@ use crate::{ charts::draw_chart, simulation::da_cost_per_byte::get_da_cost_per_byte_from_source, }; +use block_data::{ + arb_l2_fullness_and_bytes_per_block, + Predefined2Record, +}; use rand::{ rngs::StdRng, Rng, @@ -11,11 +15,7 @@ use rand::{ }; use plotters::coord::Shift; -use serde_reflection::{ - Samples, - Tracer, - TracerConfig, -}; +use utils::fields_of_struct_in_order; use crate::{ optimisation::naive_optimisation, @@ -25,10 +25,11 @@ use crate::{ }, }; +mod block_data; +mod charts; mod optimisation; mod simulation; - -mod charts; +mod utils; use clap::{ Parser, @@ -100,110 +101,6 @@ enum Source { }, } -trait HasBlobFee { - fn blob_fee_wei(&self) -> u64; -} -#[allow(dead_code)] -#[derive(Debug, serde::Deserialize, Default, serde::Serialize)] -struct PredefinedRecord { - block_number: u64, - excess_blob_gas: u64, - blob_gas_used: u64, - blob_fee_wei: u64, - blob_fee_wei_for_1_blob: u64, - blob_fee_wei_for_2_blobs: u64, - blob_fee_wei_for_3_blobs: u64, -} - -impl HasBlobFee for PredefinedRecord { - fn blob_fee_wei(&self) -> u64 { - self.blob_fee_wei - } -} - -#[allow(dead_code)] -#[derive(Debug, serde::Deserialize)] -struct Predefined2Record { - l1_block_number: u64, - l1_blob_fee_wei: u64, - l2_block_number: u64, - l2_fullness: u64, - l2_size: u64, -} - -impl HasBlobFee for Predefined2Record { - fn blob_fee_wei(&self) -> u64 { - self.l1_blob_fee_wei - } -} - -fn fields_of_struct_in_order() -> Vec -where - T: serde::de::DeserializeOwned, -{ - let mut tracer = Tracer::new(TracerConfig::default()); - let samples = Samples::new(); - tracer.trace_type::(&samples).unwrap(); - let type_name = std::any::type_name::().split("::").last().unwrap(); - let registry = tracer.registry().unwrap(); - let Some(serde_reflection::ContainerFormat::Struct(fields)) = registry.get(type_name) - else { - panic!("No fields?") - }; - - fields.iter().map(|f| f.name.clone()).collect() -} - -fn arb_l2_fullness_and_bytes_per_block(size: usize, capacity: u64) -> Vec<(u64, u32)> { - let mut rng = StdRng::seed_from_u64(888); - - let fullness_noise: Vec<_> = std::iter::repeat(()) - .take(size) - .map(|_| rng.gen_range(-0.5..0.5)) - // .map(|val| val * capacity as f64) - .collect(); - - const ROUGH_GAS_TO_BYTE_RATIO: f64 = 0.01; - let bytes_scale: Vec<_> = std::iter::repeat(()) - .take(size) - .map(|_| rng.gen_range(0.5..1.0)) - .map(|x| x * ROUGH_GAS_TO_BYTE_RATIO) - .collect(); - - (0usize..size) - .map(|val| val as f64) - .map(noisy_fullness) - .map(|signal| (0.01 * signal + 0.01) * capacity as f64) // Scale and shift so it's between 0 and capacity - .zip(fullness_noise) - .map(|(fullness, noise)| fullness + noise) - .map(|x| f64::min(x, capacity as f64)) - .map(|x| f64::max(x, 5.0)) - .zip(bytes_scale) - .map(|(fullness, bytes_scale)| { - let bytes = fullness * bytes_scale; - (fullness, bytes) - }) - .map(|(fullness, bytes)| (fullness as u64, std::cmp::max(bytes as u32, 1))) - .collect() -} - -// Naive Fourier series -fn gen_noisy_signal(input: f64, components: &[f64]) -> f64 { - components - .iter() - .fold(0f64, |acc, &c| acc + f64::sin(input / c)) - / components.len() as f64 -} - -fn noisy_fullness>(input: T) -> f64 -where - >::Error: core::fmt::Debug, -{ - const COMPONENTS: &[f64] = &[-30.0, 40.0, 700.0, -340.0, 400.0]; - let input = input.try_into().unwrap(); - gen_noisy_signal(input, COMPONENTS) -} - fn get_l2_costs_from_csv_file>(file_path: P) -> Vec<(u64, u32)> { let mut rdr = csv::ReaderBuilder::new() .has_headers(true) @@ -216,7 +113,7 @@ fn get_l2_costs_from_csv_file>(file_path: P) -> Vec<(u64, u32)> { .map(|record| { let record: Predefined2Record = record.unwrap().deserialize(Some(&headers)).unwrap(); - (record.l2_fullness, record.l2_size as u32) + (record.l2_fullness(), record.l2_size() as u32) }) .collect() } @@ -241,8 +138,6 @@ fn l1_l2_block_data_from_source( Source::Predefined2 { file_path, .. } => get_l2_costs_from_csv_file(file_path), }; - dbg!(&fullness_and_bytes); - L1L2BlockData { da_cost_per_byte, fullness_and_bytes, 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 8281dab1c62..aa915b76316 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 @@ -52,22 +52,18 @@ impl Simulator { da_p_component: i64, da_d_component: i64, update_period: usize, - fullness_and_bytes: &[(u64, u32)], + l2_blocks: &[(u64, u32)], da_finalization_rate: usize, ) -> SimulationResults { let capacity = 30_000_000; let gas_per_byte = 63; let max_block_bytes = capacity / gas_per_byte; - let size = self.da_cost_per_byte.len(); - let l2_blocks = fullness_and_bytes.clone().into_iter(); - let da_blocks = self.calculate_da_blocks( - update_period, - da_finalization_rate, - &fullness_and_bytes, - ); + let da_blocks = + self.calculate_da_blocks(update_period, da_finalization_rate, l2_blocks); let blocks = l2_blocks + .iter() .zip(da_blocks.iter()) .map(|((fullness, bytes), maybe_da_block)| BlockData { fullness: *fullness, @@ -78,13 +74,7 @@ impl Simulator { let updater = self.build_updater(da_p_component, da_d_component); - self.execute_simulation( - capacity, - max_block_bytes, - fullness_and_bytes, - blocks, - updater, - ) + self.execute_simulation(capacity, max_block_bytes, l2_blocks, blocks, updater) } fn build_updater( diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs index e3f9a5f28c6..4e4fc181050 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs @@ -1,15 +1,17 @@ use crate::{ fields_of_struct_in_order, prettify_number, - simulation::gen_noisy_signal, + utils::gen_noisy_signal, Source, }; use std::iter; use super::{ - HasBlobFee, + block_data::{ + HasBlobFee, + PredefinedRecord, + }, Predefined2Record, - PredefinedRecord, }; const PREDEFINED_L2_BLOCKS_PER_L1_BLOCK: usize = 12; @@ -24,7 +26,7 @@ pub fn get_da_cost_per_byte_from_source( file_path, sample_size, } => get_l1_costs_from_csv_file::( - &file_path, + file_path, *sample_size, PREDEFINED_L2_BLOCKS_PER_L1_BLOCK, ), @@ -32,7 +34,7 @@ pub fn get_da_cost_per_byte_from_source( file_path, l2_blocks_per_blob, } => get_l1_costs_from_csv_file::( - &file_path, + file_path, None, *l2_blocks_per_blob, ), @@ -75,7 +77,7 @@ where let costs: Vec<_> = rdr .records() - .step_by(l2_blocks_per_blob as usize) + .step_by(l2_blocks_per_blob) .take( sample_size .map(|size| size / l2_blocks_per_blob) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/utils.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/utils.rs new file mode 100644 index 00000000000..0746186ad8c --- /dev/null +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/utils.rs @@ -0,0 +1,30 @@ +use serde_reflection::{ + Samples, + Tracer, + TracerConfig, +}; + +pub(crate) fn fields_of_struct_in_order() -> Vec +where + T: serde::de::DeserializeOwned, +{ + let mut tracer = Tracer::new(TracerConfig::default()); + let samples = Samples::new(); + tracer.trace_type::(&samples).unwrap(); + let type_name = std::any::type_name::().split("::").last().unwrap(); + let registry = tracer.registry().unwrap(); + let Some(serde_reflection::ContainerFormat::Struct(fields)) = registry.get(type_name) + else { + panic!("No fields?") + }; + + fields.iter().map(|f| f.name.clone()).collect() +} + +// Naive Fourier series +pub(crate) fn gen_noisy_signal(input: f64, components: &[f64]) -> f64 { + components + .iter() + .fold(0f64, |acc, &c| acc + f64::sin(input / c)) + / components.len() as f64 +} From 9ebe8038ab9a07d074011b94e224726912269152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Thu, 2 Jan 2025 14:11:00 +0100 Subject: [PATCH 14/24] Clean up some comments --- .../gas-price-analysis/src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs index ce14867ecd3..9b924c0f37c 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs @@ -161,10 +161,10 @@ async fn main() -> anyhow::Result<()> { } = l1_l2_block_data_from_source(source, CAPACITY, UPDATE_PERIOD); println!( - "Running simulation with P: {}, D: {}, {} blocks and {} da_finalization_period", + "Running simulation with P: {}, D: {}, {} L2 blocks and {} da_finalization_period", prettify_number(p), prettify_number(d), - prettify_number(da_cost_per_byte.len()), + prettify_number(fullness_and_bytes.len()), prettify_number(da_finalization_period) ); let simulator = Simulator::new(da_cost_per_byte); @@ -183,8 +183,8 @@ async fn main() -> anyhow::Result<()> { fullness_and_bytes, } = l1_l2_block_data_from_source(source, CAPACITY, UPDATE_PERIOD); println!( - "Running optimization with {iterations} iterations and {} blocks", - da_cost_per_byte.len() + "Running optimization with {iterations} iterations and {} L2 blocks", + fullness_and_bytes.len() ); let simulator = Simulator::new(da_cost_per_byte); let (results, (p, d)) = naive_optimisation( From a0464f16e2d355dc6d877cd80f0cb72bb370ed9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Thu, 2 Jan 2025 14:44:05 +0100 Subject: [PATCH 15/24] Update generation of L1 data --- .../gas-price-analysis/src/main.rs | 16 ++++++++++++---- .../gas-price-analysis/src/simulation.rs | 4 +++- .../src/simulation/da_cost_per_byte.rs | 18 ++++++++++++------ 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs index 9b924c0f37c..352e6f1ac2a 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs @@ -124,7 +124,7 @@ struct L1L2BlockData { } fn l1_l2_block_data_from_source( - source: Source, + source: &Source, capacity: u64, update_period: usize, ) -> L1L2BlockData { @@ -158,7 +158,7 @@ async fn main() -> anyhow::Result<()> { let L1L2BlockData { da_cost_per_byte, fullness_and_bytes, - } = l1_l2_block_data_from_source(source, CAPACITY, UPDATE_PERIOD); + } = l1_l2_block_data_from_source(&source, CAPACITY, UPDATE_PERIOD); println!( "Running simulation with P: {}, D: {}, {} L2 blocks and {} da_finalization_period", @@ -168,10 +168,18 @@ async fn main() -> anyhow::Result<()> { prettify_number(da_finalization_period) ); let simulator = Simulator::new(da_cost_per_byte); + + let update_period = match source { + Source::Generated { .. } | Source::Predefined { .. } => UPDATE_PERIOD, + Source::Predefined2 { + l2_blocks_per_blob, .. + } => l2_blocks_per_blob, + }; + let result = simulator.run_simulation( p, d, - UPDATE_PERIOD, + update_period, &fullness_and_bytes, da_finalization_period, ); @@ -181,7 +189,7 @@ async fn main() -> anyhow::Result<()> { let L1L2BlockData { da_cost_per_byte, fullness_and_bytes, - } = l1_l2_block_data_from_source(source, CAPACITY, UPDATE_PERIOD); + } = l1_l2_block_data_from_source(&source, CAPACITY, UPDATE_PERIOD); println!( "Running optimization with {iterations} iterations and {} L2 blocks", fullness_and_bytes.len() 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 aa915b76316..74b0a3e4bd1 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 @@ -255,6 +255,8 @@ impl Simulator { } }) }); - l2_blocks_with_no_da_blocks.chain(da_block_ranges).collect() + let x = l2_blocks_with_no_da_blocks.chain(da_block_ranges).collect(); + dbg!(&x); + x } } diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs index 4e4fc181050..4528157f56a 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/simulation/da_cost_per_byte.rs @@ -21,7 +21,10 @@ pub fn get_da_cost_per_byte_from_source( update_period: usize, ) -> Vec { match source { - Source::Generated { size } => arbitrary_cost_per_byte(*size, update_period), + Source::Generated { size } => arbitrary_cost_per_byte(*size, update_period) + .into_iter() + .flat_map(|x| iter::repeat(x).take(update_period)) + .collect(), Source::Predefined { file_path, sample_size, @@ -29,7 +32,10 @@ pub fn get_da_cost_per_byte_from_source( file_path, *sample_size, PREDEFINED_L2_BLOCKS_PER_L1_BLOCK, - ), + ) + .into_iter() + .flat_map(|x| iter::repeat(x).take(update_period)) + .collect(), Source::Predefined2 { file_path, l2_blocks_per_blob, @@ -37,11 +43,11 @@ pub fn get_da_cost_per_byte_from_source( file_path, None, *l2_blocks_per_blob, - ), + ) + .into_iter() + .flat_map(|x| iter::repeat(x).take(*l2_blocks_per_blob)) + .collect(), } - .into_iter() - .flat_map(|x| iter::repeat(x).take(update_period)) - .collect() } // This function is used to read the CSV file and extract the blob fee from it. From 8d07bfc7a76865c0a1708a5921b48d53f1c8d1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Thu, 2 Jan 2025 16:56:46 +0100 Subject: [PATCH 16/24] Fix typo --- crates/client/src/client/schema/tx.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/client/src/client/schema/tx.rs b/crates/client/src/client/schema/tx.rs index 01f9184ff8d..c906a7a65b7 100644 --- a/crates/client/src/client/schema/tx.rs +++ b/crates/client/src/client/schema/tx.rs @@ -310,7 +310,7 @@ impl TryFrom for TransactionExecutionResult { } } DryRunTransactionStatus::Unknown => { - return Err(Self::Error::UnknownVariant("DryRuynTxStatus")) + return Err(Self::Error::UnknownVariant("DryRunTransactionStatus")) } }) } From 602403b25cfe0abdf25e0ab5719b57a3782fe656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Thu, 2 Jan 2025 16:57:06 +0100 Subject: [PATCH 17/24] Introduce `L2BlockData` --- .../gas-price-analysis/src/main.rs | 20 +++++++- .../gas-price-analysis/src/optimisation.rs | 2 +- .../gas-price-analysis/src/simulation.rs | 47 ++++++++++++++----- 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs index 352e6f1ac2a..03ef64604d9 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs @@ -118,9 +118,18 @@ fn get_l2_costs_from_csv_file>(file_path: P) -> Vec<(u64, u32)> { .collect() } +// TODO[RC]: Rename, to be more descriptive (not confusing with L1L2BlockData and BlockData) +#[derive(Clone)] +struct L2BlockData { + fullness: u64, + size: u32, + height: usize, +} + +// TODO[RC]: Rename, to be more descriptive (not confusing with L2BlockData and BlockData) struct L1L2BlockData { da_cost_per_byte: Vec, - fullness_and_bytes: Vec<(u64, u32)>, + fullness_and_bytes: Vec, } fn l1_l2_block_data_from_source( @@ -136,7 +145,14 @@ fn l1_l2_block_data_from_source( arb_l2_fullness_and_bytes_per_block(size, capacity) } Source::Predefined2 { file_path, .. } => get_l2_costs_from_csv_file(file_path), - }; + } + .iter() + .map(|(fullness, size)| L2BlockData { + fullness: *fullness, + size: *size, + height: 100000, + }) + .collect(); L1L2BlockData { da_cost_per_byte, diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/optimisation.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/optimisation.rs index 17af6d31df3..1f181cae419 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/optimisation.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/optimisation.rs @@ -16,7 +16,7 @@ pub async fn naive_optimisation( simulator: &Simulator, iterations: usize, update_period: usize, - fullness_and_bytes: &[(u64, u32)], + fullness_and_bytes: &[L2BlockData], da_recording_rate: usize, ) -> (SimulationResults, (i64, i64)) { let tasks = da_pid_factors(iterations) 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 74b0a3e4bd1..ca583b61652 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 @@ -52,7 +52,7 @@ impl Simulator { da_p_component: i64, da_d_component: i64, update_period: usize, - l2_blocks: &[(u64, u32)], + l2_blocks: &[L2BlockData], da_finalization_rate: usize, ) -> SimulationResults { let capacity = 30_000_000; @@ -65,11 +65,20 @@ impl Simulator { let blocks = l2_blocks .iter() .zip(da_blocks.iter()) - .map(|((fullness, bytes), maybe_da_block)| BlockData { - fullness: *fullness, - bytes: *bytes, - maybe_da_bundle: maybe_da_block.clone(), - }) + .map( + |( + L2BlockData { + fullness, + size, + height, + }, + maybe_da_block, + )| BlockData { + fullness: *fullness, + bytes: *size, + maybe_da_bundle: maybe_da_block.clone(), + }, + ) .enumerate(); let updater = self.build_updater(da_p_component, da_d_component); @@ -118,7 +127,7 @@ impl Simulator { &self, capacity: u64, max_block_bytes: u64, - fullness_and_bytes: &[(u64, u32)], + fullness_and_bytes: &[L2BlockData], blocks: impl Iterator, mut updater: AlgorithmUpdaterV1, ) -> SimulationResults { @@ -178,8 +187,10 @@ impl Simulator { } } } - let (fullness_without_capacity, bytes): (Vec<_>, Vec<_>) = - fullness_and_bytes.iter().cloned().unzip(); + let (fullness_without_capacity, bytes): (Vec<_>, Vec<_>) = fullness_and_bytes + .iter() + .map(|l2_block_data| (l2_block_data.fullness, l2_block_data.size)) + .unzip(); let fullness: Vec<_> = fullness_without_capacity .iter() .map(|&fullness| (fullness, capacity)) @@ -218,7 +229,7 @@ impl Simulator { &self, da_recording_rate: usize, da_finalization_rate: usize, - fullness_and_bytes: &[(u64, u32)], + fullness_and_bytes: &[L2BlockData], ) -> Vec> { let l2_blocks_with_no_da_blocks = std::iter::repeat(None).take(da_finalization_rate); @@ -229,10 +240,20 @@ impl Simulator { .fold( (vec![], vec![]), |(mut delayed, mut recorded), - (index, ((_fullness, bytes), cost_per_byte))| { - let total_cost = *bytes as u64 * cost_per_byte; + ( + index, + ( + L2BlockData { + fullness, + size, + height, + }, + cost_per_byte, + ), + )| { + let total_cost = *size as u64 * cost_per_byte; let height = index as u32 + 1; - let converted = (height, bytes, total_cost); + let converted = (height, size, total_cost); delayed.push(converted); if delayed.len() == da_recording_rate { recorded.push(Some(delayed)); From 18b9ae1df26da9e29e4dc85260645398b4246f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Thu, 2 Jan 2025 17:12:09 +0100 Subject: [PATCH 18/24] Read L2 block heights from the csv file --- .../gas-price-analysis/src/block_data.rs | 7 ++- .../gas-price-analysis/src/main.rs | 19 +++++-- .../gas-price-analysis/src/simulation.rs | 57 ++++++++----------- 3 files changed, 43 insertions(+), 40 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/block_data.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/block_data.rs index 473a04d0460..4eaa6d9503c 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/block_data.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/block_data.rs @@ -45,6 +45,10 @@ impl Predefined2Record { pub(super) fn l2_size(&self) -> u64 { self.l2_size } + + pub(super) fn l2_block_number(&self) -> u64 { + self.l2_block_number + } } impl HasBlobFee for Predefined2Record { @@ -56,7 +60,7 @@ impl HasBlobFee for Predefined2Record { pub(super) fn arb_l2_fullness_and_bytes_per_block( size: usize, capacity: u64, -) -> Vec<(u64, u32)> { +) -> Vec<(u64, u32, u64)> { let mut rng = StdRng::seed_from_u64(888); let fullness_noise: Vec<_> = std::iter::repeat(()) @@ -86,6 +90,7 @@ pub(super) fn arb_l2_fullness_and_bytes_per_block( (fullness, bytes) }) .map(|(fullness, bytes)| (fullness as u64, std::cmp::max(bytes as u32, 1))) + .enumerate().map(|(height, (fullness, bytes))| (fullness, bytes, height as u64)) .collect() } diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs index 03ef64604d9..ddc2d259ea6 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs @@ -101,7 +101,7 @@ enum Source { }, } -fn get_l2_costs_from_csv_file>(file_path: P) -> Vec<(u64, u32)> { +fn get_l2_costs_from_csv_file>(file_path: P) -> Vec<(u64, u32, u64)> { let mut rdr = csv::ReaderBuilder::new() .has_headers(true) .from_path(file_path) @@ -113,17 +113,22 @@ fn get_l2_costs_from_csv_file>(file_path: P) -> Vec<(u64, u32)> { .map(|record| { let record: Predefined2Record = record.unwrap().deserialize(Some(&headers)).unwrap(); - (record.l2_fullness(), record.l2_size() as u32) + ( + record.l2_fullness(), + record.l2_size() as u32, + record.l2_block_number(), + ) }) .collect() } // TODO[RC]: Rename, to be more descriptive (not confusing with L1L2BlockData and BlockData) -#[derive(Clone)] +// TODO[RC]: Into `BlockData` instead of manual mapping in `run_simulation()` +#[derive(Debug, Clone)] struct L2BlockData { + height: u64, fullness: u64, size: u32, - height: usize, } // TODO[RC]: Rename, to be more descriptive (not confusing with L2BlockData and BlockData) @@ -147,10 +152,10 @@ fn l1_l2_block_data_from_source( Source::Predefined2 { file_path, .. } => get_l2_costs_from_csv_file(file_path), } .iter() - .map(|(fullness, size)| L2BlockData { + .map(|(fullness, size, height)| L2BlockData { fullness: *fullness, size: *size, - height: 100000, + height: *height, }) .collect(); @@ -176,6 +181,8 @@ async fn main() -> anyhow::Result<()> { fullness_and_bytes, } = l1_l2_block_data_from_source(&source, CAPACITY, UPDATE_PERIOD); + dbg!(&fullness_and_bytes); + println!( "Running simulation with P: {}, D: {}, {} L2 blocks and {} da_finalization_period", prettify_number(p), 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 ca583b61652..1bbdcda857d 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 @@ -30,6 +30,7 @@ pub struct Simulator { // (usize, ((u64, u64), &'a Option<(Range, u128) struct BlockData { + height: u64, fullness: u64, bytes: u32, maybe_da_bundle: Option, @@ -62,24 +63,23 @@ impl Simulator { let da_blocks = self.calculate_da_blocks(update_period, da_finalization_rate, l2_blocks); - let blocks = l2_blocks - .iter() - .zip(da_blocks.iter()) - .map( - |( - L2BlockData { - fullness, - size, - height, - }, - maybe_da_block, - )| BlockData { - fullness: *fullness, - bytes: *size, - maybe_da_bundle: maybe_da_block.clone(), + dbg!(&da_blocks); + + let blocks = l2_blocks.iter().zip(da_blocks.iter()).map( + |( + L2BlockData { + fullness, + size, + height, }, - ) - .enumerate(); + maybe_da_block, + )| BlockData { + height: *height, + fullness: *fullness, + bytes: *size, + maybe_da_bundle: maybe_da_block.clone(), + }, + ); let updater = self.build_updater(da_p_component, da_d_component); @@ -128,7 +128,7 @@ impl Simulator { capacity: u64, max_block_bytes: u64, fullness_and_bytes: &[L2BlockData], - blocks: impl Iterator, + blocks: impl Iterator, mut updater: AlgorithmUpdaterV1, ) -> SimulationResults { let mut gas_prices = vec![]; @@ -139,13 +139,14 @@ impl Simulator { let mut actual_costs = vec![]; let mut pessimistic_costs = vec![]; let mut unrecorded_blocks = BTreeMap::new(); - for (index, block_data) in blocks { + for block_data in blocks { let BlockData { fullness, bytes, maybe_da_bundle, + height, } = block_data; - let height = index as u32 + 1; + let height = height as u32 + 1; exec_gas_prices.push(updater.new_scaled_exec_price); da_gas_prices.push(updater.new_scaled_da_gas_price); let gas_price = updater.algorithm().calculate(); @@ -236,23 +237,13 @@ impl Simulator { let (_, da_blocks) = fullness_and_bytes .iter() .zip(self.da_cost_per_byte.iter()) - .enumerate() .fold( (vec![], vec![]), |(mut delayed, mut recorded), - ( - index, - ( - L2BlockData { - fullness, - size, - height, - }, - cost_per_byte, - ), - )| { + + (L2BlockData { size, height, .. }, cost_per_byte)| { let total_cost = *size as u64 * cost_per_byte; - let height = index as u32 + 1; + let height = *height as u32 + 1; let converted = (height, size, total_cost); delayed.push(converted); if delayed.len() == da_recording_rate { From c6ce17d56f386751e54301253422d9686f2a9faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Thu, 2 Jan 2025 17:27:36 +0100 Subject: [PATCH 19/24] Initialize updater with proper block height --- .../gas-price-analysis/src/simulation.rs | 14 ++++++++++---- 1 file changed, 10 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 1bbdcda857d..fc51f00ee5d 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 @@ -63,8 +63,6 @@ impl Simulator { let da_blocks = self.calculate_da_blocks(update_period, da_finalization_rate, l2_blocks); - dbg!(&da_blocks); - let blocks = l2_blocks.iter().zip(da_blocks.iter()).map( |( L2BlockData { @@ -81,7 +79,14 @@ impl Simulator { }, ); - let updater = self.build_updater(da_p_component, da_d_component); + let updater = self.build_updater( + da_p_component, + da_d_component, + l2_blocks + .first() + .expect("should have at least 1 l2 block") + .height as u32, + ); self.execute_simulation(capacity, max_block_bytes, l2_blocks, blocks, updater) } @@ -90,6 +95,7 @@ impl Simulator { &self, da_p_component: i64, da_d_component: i64, + l2_block_height: u32, ) -> AlgorithmUpdaterV1 { // Scales the gas price internally, value is arbitrary let gas_price_factor = 100; @@ -102,7 +108,7 @@ impl Simulator { // Change to adjust where the da gas price starts on block 0 new_scaled_da_gas_price: 10_000_000 * gas_price_factor, gas_price_factor: NonZeroU64::new(gas_price_factor).unwrap(), - l2_block_height: 0, + l2_block_height, // Choose the ideal fullness percentage for the L2 block l2_block_fullness_threshold_percent: 50u8.into(), // Increase to make the exec price change faster From 0853b2e330c5e89cca68315a3ab891d528616170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Fri, 3 Jan 2025 10:32:57 +0100 Subject: [PATCH 20/24] Update X-axis labels on charts --- .../gas-price-analysis/src/charts.rs | 40 +++++++++++++++---- .../gas-price-analysis/src/simulation.rs | 7 ++++ 2 files changed, 40 insertions(+), 7 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 8972e9c5bc0..74ef050b525 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 @@ -8,7 +8,13 @@ use plotters::{ }, series::LineSeries, style::{ - Color, IntoFont, RGBColor, BLACK, BLUE, RED, WHITE + Color, + IntoFont, + RGBColor, + BLACK, + BLUE, + RED, + WHITE, }, }; @@ -36,6 +42,7 @@ pub fn draw_chart( actual_profit, projected_profit, pessimistic_costs, + first_height, } = results; let plot_width = 640 * 2 * 2; @@ -58,9 +65,14 @@ pub fn draw_chart( let (window_two, new_lower) = lower.split_vertically(plot_height / 4); let (window_three, window_four) = new_lower.split_vertically(plot_height / 4); - draw_fullness(&window_one, &fullness, "Fullness")?; + draw_fullness(&window_one, &fullness, "Fullness", first_height)?; - draw_bytes_and_cost_per_block(&window_two, &bytes_and_costs, "Bytes Per Block")?; + draw_bytes_and_cost_per_block( + &window_two, + &bytes_and_costs, + "Bytes Per Block", + first_height, + )?; draw_profit( &window_three, @@ -73,6 +85,7 @@ pub fn draw_chart( prettify_number(d_comp), prettify_number(da_finalization_period) ), + first_height, )?; draw_gas_prices( &window_four, @@ -80,6 +93,7 @@ pub fn draw_chart( &exec_gas_prices, &da_gas_prices, "Gas Prices", + first_height, )?; root.present()?; @@ -93,6 +107,7 @@ pub fn draw_gas_prices( _exec_gas_prices: &[u64], da_gas_prices: &[u64], title: &str, + first_height: u32, ) -> anyhow::Result<()> { let gas_prices_gwei: Vec<_> = gas_prices.iter().map(|x| x / ONE_GWEI).collect(); let _exec_gas_prices_gwei: Vec<_> = @@ -115,7 +130,9 @@ pub fn draw_gas_prices( chart .configure_mesh() .y_desc("DA Gas Price") - .x_desc("Block") + .x_desc("L2 Block") + .x_labels(da_gas_prices.len()) + .x_label_formatter(&|x| format!("{}", x + first_height as usize)) .draw()?; // chart @@ -161,6 +178,7 @@ pub fn draw_fullness( drawing_area: &DrawingArea, fullness: &[(u64, u64)], title: &str, + first_height: u32, ) -> anyhow::Result<()> { const FULLNESS_COLOR: RGBColor = BLACK; @@ -178,7 +196,9 @@ pub fn draw_fullness( chart .configure_mesh() .y_desc("Fullness Percentage") - .x_desc("Block") + .x_desc("L2 Block") + .x_labels(fullness.len()) + .x_label_formatter(&|x| format!("{}", x + first_height as usize)) .draw()?; chart @@ -207,6 +227,7 @@ pub fn draw_bytes_and_cost_per_block( drawing_area: &DrawingArea, bytes_and_costs_per_block: &[(u32, u64)], title: &str, + first_height: u32, ) -> anyhow::Result<()> { const BYTES_PER_BLOCK_COLOR: RGBColor = BLACK; let (bytes, costs): (Vec, Vec) = @@ -230,7 +251,9 @@ pub fn draw_bytes_and_cost_per_block( chart .configure_mesh() .y_desc("Bytes Per Block") - .x_desc("Block") + .x_desc("L2 Block") + .x_labels(bytes_and_costs_per_block.len()) + .x_label_formatter(&|x| format!("{}", x + first_height as usize)) .draw() .unwrap(); @@ -284,6 +307,7 @@ pub fn draw_profit( projected_profit: &[i128], pessimistic_block_costs: &[u128], title: &str, + first_height: u32, ) -> anyhow::Result<()> { const ACTUAL_PROFIT_COLOR: RGBColor = BLACK; const PROJECTED_PROFIT_COLOR: RGBColor = RED; @@ -336,7 +360,9 @@ pub fn draw_profit( chart .configure_mesh() .y_desc("Profit (Gwei)") - .x_desc("Block") + .x_desc("L2 Block") + .x_labels(actual_profit.len()) + .x_label_formatter(&|x| format!("{}", x + first_height as usize)) .draw()?; chart 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 fc51f00ee5d..ae1314b129b 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 @@ -21,6 +21,7 @@ pub struct SimulationResults { pub actual_profit: Vec, pub projected_profit: Vec, pub pessimistic_costs: Vec, + pub first_height: u32, } #[derive(Clone, Debug)] @@ -220,6 +221,11 @@ impl Simulator { .map(|(cost, reward)| *reward as i128 - *cost as i128) .collect(); + let first_height = fullness_and_bytes + .first() + .expect("should have at least 1 l2 block") + .height as u32; + SimulationResults { gas_prices, exec_gas_prices, @@ -229,6 +235,7 @@ impl Simulator { actual_profit, projected_profit, pessimistic_costs, + first_height, } } From ffb1bb05da22e7efaecdee9e7956df93a8100427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Fri, 3 Jan 2025 10:39:38 +0100 Subject: [PATCH 21/24] Fix fullness drawing --- .../gas-price-analysis/src/charts.rs | 12 ++++++------ .../gas-price-analysis/src/simulation.rs | 9 +++++---- 2 files changed, 11 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 74ef050b525..1a9f366b8c3 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 @@ -17,6 +17,10 @@ use plotters::{ WHITE, }, }; +use simulation::{ + Capacity, + Fullness, +}; use super::*; use std::{ @@ -176,7 +180,7 @@ pub fn draw_gas_prices( pub fn draw_fullness( drawing_area: &DrawingArea, - fullness: &[(u64, u64)], + fullness: &[(Fullness, Capacity)], title: &str, first_height: u32, ) -> anyhow::Result<()> { @@ -203,11 +207,7 @@ pub fn draw_fullness( chart .draw_series(LineSeries::new( - fullness - .iter() - .map(|(x, y)| (*x as f64 / *y as f64) * 100.) - .map(|x| x as i32) - .enumerate(), + fullness.iter().map(|(x, _)| *x as i32).enumerate(), FULLNESS_COLOR, )) .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 ae1314b129b..eec85a0c225 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 @@ -12,11 +12,14 @@ use super::*; pub mod da_cost_per_byte; +pub(crate) type Fullness = u64; +pub(crate) type Capacity = u64; + pub struct SimulationResults { pub gas_prices: Vec, pub exec_gas_prices: Vec, pub da_gas_prices: Vec, - pub fullness: Vec<(u64, u64)>, + pub fullness: Vec<(Fullness, Capacity)>, pub bytes_and_costs: Vec<(u32, u64)>, pub actual_profit: Vec, pub projected_profit: Vec, @@ -280,8 +283,6 @@ impl Simulator { } }) }); - let x = l2_blocks_with_no_da_blocks.chain(da_block_ranges).collect(); - dbg!(&x); - x + l2_blocks_with_no_da_blocks.chain(da_block_ranges).collect() } } From d27d74ce84c270f090c2ee318c31b1d64ec1a0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Fri, 3 Jan 2025 11:06:25 +0100 Subject: [PATCH 22/24] Add tracing subscriber so we can see logs from the algorithm --- crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml | 1 + crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml b/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml index edc96e79959..c9bc1e6d31b 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml @@ -18,3 +18,4 @@ rand_distr = "0.4.3" serde = { version = "1.0.209", features = ["derive"] } serde-reflection = "0.5.0" tokio = { version = "1.40.0", features = ["macros", "rt", "rt-multi-thread"] } +tracing-subscriber = "0.3" \ No newline at end of file diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs index ddc2d259ea6..56b6c8fa9ef 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs @@ -169,6 +169,8 @@ fn l1_l2_block_data_from_source( async fn main() -> anyhow::Result<()> { let args = Arg::parse(); + tracing_subscriber::fmt::init(); + const UPDATE_PERIOD: usize = 12; const CAPACITY: u64 = 30_000_000; From 61236ff15b69d07f99c543629fd1e242a2641164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Fri, 3 Jan 2025 13:51:46 +0100 Subject: [PATCH 23/24] Track capacity and max size per block --- .../gas-price-analysis/Cargo.toml | 3 +- .../gas-price-analysis/src/block_data.rs | 31 ++++++--- .../gas-price-analysis/src/main.rs | 40 ++++++----- .../gas-price-analysis/src/simulation.rs | 69 +++++++++++++------ crates/fuel-gas-price-algorithm/src/v1.rs | 56 +++++++++++++-- .../gas_price_service/src/common/utils.rs | 19 +++++ .../gas_price_service/src/v1/service.rs | 2 + 7 files changed, 166 insertions(+), 54 deletions(-) diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml b/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml index c9bc1e6d31b..116339adfee 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/Cargo.toml @@ -18,4 +18,5 @@ rand_distr = "0.4.3" serde = { version = "1.0.209", features = ["derive"] } serde-reflection = "0.5.0" tokio = { version = "1.40.0", features = ["macros", "rt", "rt-multi-thread"] } -tracing-subscriber = "0.3" \ No newline at end of file +tracing = "0.1.41" +tracing-subscriber = "0.3" diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/block_data.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/block_data.rs index 4eaa6d9503c..e02eaae54e7 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/block_data.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/block_data.rs @@ -33,22 +33,32 @@ pub(super) struct Predefined2Record { l1_block_number: u64, l1_blob_fee_wei: u64, l2_block_number: u64, - l2_fullness: u64, - l2_size: u64, + l2_gas_fullness: u64, + l2_gas_capacity: u64, + l2_byte_size: u64, + l2_byte_capacity: u64, } impl Predefined2Record { - pub(super) fn l2_fullness(&self) -> u64 { - self.l2_fullness + pub(super) fn l2_gas_fullness(&self) -> u64 { + self.l2_gas_fullness } - pub(super) fn l2_size(&self) -> u64 { - self.l2_size + pub(super) fn l2_byte_size(&self) -> u64 { + self.l2_byte_size } pub(super) fn l2_block_number(&self) -> u64 { self.l2_block_number } + + pub(super) fn l2_gas_capacity(&self) -> u64 { + self.l2_gas_capacity + } + + pub(super) fn l2_byte_capacity(&self) -> u64 { + self.l2_byte_capacity + } } impl HasBlobFee for Predefined2Record { @@ -59,10 +69,13 @@ impl HasBlobFee for Predefined2Record { pub(super) fn arb_l2_fullness_and_bytes_per_block( size: usize, - capacity: u64, -) -> Vec<(u64, u32, u64)> { +) -> Vec<(u64, u32, u64, u64, u64)> { let mut rng = StdRng::seed_from_u64(888); + let capacity = 30_000_000; + let gas_per_byte = 63; + let max_block_bytes = capacity / gas_per_byte; + let fullness_noise: Vec<_> = std::iter::repeat(()) .take(size) .map(|_| rng.gen_range(-0.5..0.5)) @@ -90,7 +103,7 @@ pub(super) fn arb_l2_fullness_and_bytes_per_block( (fullness, bytes) }) .map(|(fullness, bytes)| (fullness as u64, std::cmp::max(bytes as u32, 1))) - .enumerate().map(|(height, (fullness, bytes))| (fullness, bytes, height as u64)) + .enumerate().map(|(height, (fullness, bytes))| (fullness, bytes, height as u64, capacity, max_block_bytes)) .collect() } diff --git a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs index 56b6c8fa9ef..9222fd12fd9 100644 --- a/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs +++ b/crates/fuel-gas-price-algorithm/gas-price-analysis/src/main.rs @@ -101,7 +101,10 @@ enum Source { }, } -fn get_l2_costs_from_csv_file>(file_path: P) -> Vec<(u64, u32, u64)> { +// TODO[RC]: Just return `Predefined2Record` instead of crazy tuple? :-) +fn get_l2_costs_from_csv_file>( + file_path: P, +) -> Vec<(u64, u32, u64, u64, u64)> { let mut rdr = csv::ReaderBuilder::new() .has_headers(true) .from_path(file_path) @@ -114,9 +117,11 @@ fn get_l2_costs_from_csv_file>(file_path: P) -> Vec<(u64, u32, u6 let record: Predefined2Record = record.unwrap().deserialize(Some(&headers)).unwrap(); ( - record.l2_fullness(), - record.l2_size() as u32, + record.l2_gas_fullness(), + record.l2_byte_size() as u32, record.l2_block_number(), + record.l2_gas_capacity(), + record.l2_byte_capacity(), ) }) .collect() @@ -128,7 +133,9 @@ fn get_l2_costs_from_csv_file>(file_path: P) -> Vec<(u64, u32, u6 struct L2BlockData { height: u64, fullness: u64, + gas_capacity: u64, size: u32, + size_capacity: u64, } // TODO[RC]: Rename, to be more descriptive (not confusing with L2BlockData and BlockData) @@ -137,26 +144,26 @@ struct L1L2BlockData { fullness_and_bytes: Vec, } -fn l1_l2_block_data_from_source( - source: &Source, - capacity: u64, - update_period: usize, -) -> L1L2BlockData { +fn l1_l2_block_data_from_source(source: &Source, update_period: usize) -> L1L2BlockData { let da_cost_per_byte = get_da_cost_per_byte_from_source(&source, update_period); let size = da_cost_per_byte.len(); let fullness_and_bytes = match source { Source::Generated { .. } | Source::Predefined { .. } => { - arb_l2_fullness_and_bytes_per_block(size, capacity) + arb_l2_fullness_and_bytes_per_block(size) } Source::Predefined2 { file_path, .. } => get_l2_costs_from_csv_file(file_path), } .iter() - .map(|(fullness, size, height)| L2BlockData { - fullness: *fullness, - size: *size, - height: *height, - }) + .map( + |(fullness, size, height, gas_capacity, size_capacity)| L2BlockData { + fullness: *fullness, + size: *size, + height: *height, + gas_capacity: *gas_capacity, + size_capacity: *size_capacity, + }, + ) .collect(); L1L2BlockData { @@ -172,7 +179,6 @@ async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); const UPDATE_PERIOD: usize = 12; - const CAPACITY: u64 = 30_000_000; let da_finalization_period = args.da_finalization_period; @@ -181,7 +187,7 @@ async fn main() -> anyhow::Result<()> { let L1L2BlockData { da_cost_per_byte, fullness_and_bytes, - } = l1_l2_block_data_from_source(&source, CAPACITY, UPDATE_PERIOD); + } = l1_l2_block_data_from_source(&source, UPDATE_PERIOD); dbg!(&fullness_and_bytes); @@ -214,7 +220,7 @@ async fn main() -> anyhow::Result<()> { let L1L2BlockData { da_cost_per_byte, fullness_and_bytes, - } = l1_l2_block_data_from_source(&source, CAPACITY, UPDATE_PERIOD); + } = l1_l2_block_data_from_source(&source, UPDATE_PERIOD); println!( "Running optimization with {iterations} iterations and {} L2 blocks", fullness_and_bytes.len() 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 eec85a0c225..03550ca0b18 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 @@ -36,7 +36,9 @@ pub struct Simulator { struct BlockData { height: u64, fullness: u64, + gas_capacity: u64, bytes: u32, + bytes_capacity: u64, maybe_da_bundle: Option, } @@ -60,10 +62,6 @@ impl Simulator { l2_blocks: &[L2BlockData], da_finalization_rate: usize, ) -> SimulationResults { - let capacity = 30_000_000; - let gas_per_byte = 63; - let max_block_bytes = capacity / gas_per_byte; - let da_blocks = self.calculate_da_blocks(update_period, da_finalization_rate, l2_blocks); @@ -73,6 +71,8 @@ impl Simulator { fullness, size, height, + gas_capacity, + size_capacity, }, maybe_da_block, )| BlockData { @@ -80,6 +80,8 @@ impl Simulator { fullness: *fullness, bytes: *size, maybe_da_bundle: maybe_da_block.clone(), + gas_capacity: *gas_capacity, + bytes_capacity: *size_capacity, }, ); @@ -92,7 +94,7 @@ impl Simulator { .height as u32, ); - self.execute_simulation(capacity, max_block_bytes, l2_blocks, blocks, updater) + self.execute_simulation(l2_blocks, blocks, updater) } fn build_updater( @@ -104,12 +106,13 @@ impl Simulator { // Scales the gas price internally, value is arbitrary let gas_price_factor = 100; let always_normal_activity = L2ActivityTracker::new_always_normal(); - AlgorithmUpdaterV1 { + + let updater = AlgorithmUpdaterV1 { min_exec_gas_price: 10_000_000, min_da_gas_price: 100_000_000, - // Change to adjust where the exec gas price starts on block 0 + // Change to adjust where the exec gas price starts on first block new_scaled_exec_price: 10_000_000 * gas_price_factor, - // Change to adjust where the da gas price starts on block 0 + // Change to adjust where the da gas price starts on first block new_scaled_da_gas_price: 10_000_000 * gas_price_factor, gas_price_factor: NonZeroU64::new(gas_price_factor).unwrap(), l2_block_height, @@ -120,7 +123,7 @@ impl Simulator { // Increase to make the da price change faster max_da_gas_price_change_percent: 15, total_da_rewards_excess: 0, - // Change to adjust the cost per byte of the DA on block 0 + // Change to adjust the cost per byte of the DA on first block latest_da_cost_per_byte: 0, projected_total_da_cost: 0, latest_known_total_da_cost_excess: 0, @@ -130,13 +133,15 @@ impl Simulator { second_to_last_profit: 0, l2_activity: always_normal_activity, unrecorded_blocks_bytes: 0, - } + }; + + tracing::trace!(?updater, "Built updater"); + + updater } fn execute_simulation( &self, - capacity: u64, - max_block_bytes: u64, fullness_and_bytes: &[L2BlockData], blocks: impl Iterator, mut updater: AlgorithmUpdaterV1, @@ -150,30 +155,49 @@ impl Simulator { let mut pessimistic_costs = vec![]; let mut unrecorded_blocks = BTreeMap::new(); for block_data in blocks { + tracing::trace!(%block_data.height, + "Processing L2 block with{} DA bundle", + if block_data.maybe_da_bundle.is_some() { + "" + } else { + "out" + } + ); let BlockData { fullness, bytes, maybe_da_bundle, height, + gas_capacity, + bytes_capacity, } = block_data; let height = height as u32 + 1; - exec_gas_prices.push(updater.new_scaled_exec_price); - da_gas_prices.push(updater.new_scaled_da_gas_price); + let new_scaled_exec_price = updater.new_scaled_exec_price; + exec_gas_prices.push(new_scaled_exec_price); + let new_scaled_da_gas_price = updater.new_scaled_da_gas_price; + da_gas_prices.push(new_scaled_da_gas_price); let gas_price = updater.algorithm().calculate(); gas_prices.push(gas_price); let total_fee = gas_price as u128 * fullness as u128; + tracing::trace!( + "New scaled exec price: {}, New scaled da price: {}, Gas price: {}, Fullness: {}, Total fee: {}", + new_scaled_exec_price, + new_scaled_da_gas_price, + gas_price,fullness, + total_fee + ); updater .update_l2_block_data( height, fullness, - capacity.try_into().unwrap(), + gas_capacity.try_into().unwrap(), bytes as u64, total_fee, &mut unrecorded_blocks, ) .unwrap(); pessimistic_costs - .push(max_block_bytes as u128 * updater.latest_da_cost_per_byte); + .push(bytes_capacity as u128 * updater.latest_da_cost_per_byte); actual_reward_totals.push(updater.total_da_rewards_excess); projected_cost_totals.push(updater.projected_total_da_cost); @@ -198,14 +222,15 @@ impl Simulator { } } } - let (fullness_without_capacity, bytes): (Vec<_>, Vec<_>) = fullness_and_bytes + let (fullness, bytes): (Vec<_>, Vec<_>) = fullness_and_bytes .iter() - .map(|l2_block_data| (l2_block_data.fullness, l2_block_data.size)) + .map(|l2_block_data| { + ( + (l2_block_data.fullness, l2_block_data.gas_capacity), + l2_block_data.size, + ) + }) .unzip(); - let fullness: Vec<_> = fullness_without_capacity - .iter() - .map(|&fullness| (fullness, capacity)) - .collect(); let bytes_and_costs: Vec<_> = bytes .iter() .zip(self.da_cost_per_byte.iter()) diff --git a/crates/fuel-gas-price-algorithm/src/v1.rs b/crates/fuel-gas-price-algorithm/src/v1.rs index b53403c8644..357b8688463 100644 --- a/crates/fuel-gas-price-algorithm/src/v1.rs +++ b/crates/fuel-gas-price-algorithm/src/v1.rs @@ -41,6 +41,16 @@ pub struct AlgorithmV1 { for_height: u32, } +impl core::fmt::Display for AlgorithmV1 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "AlgorithmV1: new_exec_price: {}, exec_price_percentage: {}, new_da_gas_price: {}, da_gas_price_percentage: {}, for_height: {}", + self.new_exec_price, self.exec_price_percentage, self.new_da_gas_price, self.da_gas_price_percentage, self.for_height + ) + } +} + impl AlgorithmV1 { pub fn calculate(&self) -> u64 { self.new_exec_price.saturating_add(self.new_da_gas_price) @@ -70,6 +80,8 @@ pub trait UnrecordedBlocks { fn insert(&mut self, height: Height, bytes: Bytes) -> Result<(), String>; fn remove(&mut self, height: &Height) -> Result, String>; + + fn len(&self) -> usize; } impl UnrecordedBlocks for BTreeMap { @@ -82,6 +94,10 @@ impl UnrecordedBlocks for BTreeMap { let value = self.remove(height); Ok(value) } + + fn len(&self) -> usize { + self.len() + } } /// The state of the algorithm used to update the gas price algorithm for each block @@ -383,6 +399,15 @@ impl AlgorithmUpdaterV1 { got: height, }) } else { + tracing::info!( + height, + total_fee_wei = fee_wei, + fullness_used = used, + capacity, + block_bytes, + unrecorded_blocks_len = unrecorded_blocks.len(), + "Updating L2 block data for height" + ); self.l2_block_height = height; // rewards @@ -390,7 +415,6 @@ impl AlgorithmUpdaterV1 { let rewards = self.clamped_rewards_as_i128(); // costs - tracing::info!("Block bytes: {}", block_bytes); self.update_projected_da_cost(block_bytes); let projected_total_da_cost = self.clamped_projected_cost_as_i128(); @@ -417,17 +441,23 @@ impl AlgorithmUpdaterV1 { } fn update_activity(&mut self, used: u64, capacity: NonZeroU64) { + tracing::info!(fullness_used = used, capacity, "Updating L2 activity, PRE"); let block_activity = used.saturating_mul(100).div(capacity); let usage = ClampedPercentage::new(block_activity.try_into().unwrap_or(100)); + tracing::info!(block_activity, ?usage, "Updating L2 activity, POST"); self.l2_activity.update(usage); } fn update_da_rewards(&mut self, fee_wei: u128) { - tracing::info!("Fee: {}", fee_wei); let block_da_reward = self.da_portion_of_fee(fee_wei); - tracing::info!("DA reward: {}", block_da_reward); self.total_da_rewards_excess = self.total_da_rewards_excess.saturating_add(block_da_reward); + tracing::info!( + fee_wei, + block_da_reward, + total_da_rewards_excess = self.total_da_rewards_excess, + "Updating DA rewards" + ); } fn update_projected_da_cost(&mut self, block_bytes: u64) { @@ -436,6 +466,12 @@ impl AlgorithmUpdaterV1 { self.projected_total_da_cost = self .projected_total_da_cost .saturating_add(block_projected_da_cost); + tracing::info!( + latest_da_cost_per_byte = self.latest_da_cost_per_byte, + block_projected_da_cost, + projected_total_da_cost = self.projected_total_da_cost, + "Updating projected DA cost" + ) } // Take the `fee_wei` and return the portion of the fee that should be used for paying DA costs @@ -460,6 +496,14 @@ impl AlgorithmUpdaterV1 { } fn update_last_profit(&mut self, new_profit: i128) { + tracing::info!( + "Updating profit: second_to_last_profit ({} -> {}), last_profit ({} -> {})", + self.second_to_last_profit, + self.last_profit, + self.last_profit, + new_profit + ); + self.second_to_last_profit = self.last_profit; self.last_profit = new_profit; } @@ -654,12 +698,14 @@ impl AlgorithmUpdaterV1 { } pub fn algorithm(&self) -> AlgorithmV1 { - AlgorithmV1 { + let algorithm = AlgorithmV1 { new_exec_price: self.descaled_exec_price(), exec_price_percentage: self.exec_gas_price_change_percent as u64, new_da_gas_price: self.descaled_da_price(), da_gas_price_percentage: self.max_da_gas_price_change_percent as u64, for_height: self.l2_block_height, - } + }; + tracing::trace!(%algorithm); + algorithm } } diff --git a/crates/services/gas_price_service/src/common/utils.rs b/crates/services/gas_price_service/src/common/utils.rs index b57fc2608a2..4ac08fde38c 100644 --- a/crates/services/gas_price_service/src/common/utils.rs +++ b/crates/services/gas_price_service/src/common/utils.rs @@ -44,3 +44,22 @@ pub enum BlockInfo { block_fees: u64, }, } + +impl core::fmt::Display for BlockInfo { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + BlockInfo::GenesisBlock => write!(f, "Genesis Block"), + BlockInfo::Block { + height, + gas_used, + block_gas_capacity, + block_bytes, + block_fees, + } => write!( + f, + "Block: height: {}, gas_used: {}, block_gas_capacity: {}, block_bytes: {}, block_fees: {}", + height, gas_used, block_gas_capacity, block_bytes, block_fees + ), + } + } +} diff --git a/crates/services/gas_price_service/src/v1/service.rs b/crates/services/gas_price_service/src/v1/service.rs index cec290cedd4..d9f4e228964 100644 --- a/crates/services/gas_price_service/src/v1/service.rs +++ b/crates/services/gas_price_service/src/v1/service.rs @@ -214,6 +214,7 @@ where ) -> anyhow::Result<()> { match l2_block { BlockInfo::GenesisBlock => { + tracing::trace!(%l2_block, "genesis"); let metadata: UpdaterMetadata = self.algorithm_updater.clone().into(); let mut tx = self.storage_tx_provider.begin_transaction()?; tx.set_metadata(&metadata).map_err(|err| anyhow!(err))?; @@ -228,6 +229,7 @@ where block_bytes, block_fees, } => { + tracing::trace!(%l2_block, "normal"); self.handle_normal_block( height, gas_used, From 12fc51e6d55f8d196898649f76e39f8276d255eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Chabowski?= Date: Fri, 3 Jan 2025 13:57:01 +0100 Subject: [PATCH 24/24] Draw fullness not as percentage --- .../gas-price-analysis/src/charts.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 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 1a9f366b8c3..d8ef10c83eb 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 @@ -187,7 +187,11 @@ pub fn draw_fullness( const FULLNESS_COLOR: RGBColor = BLACK; let min = 0; - let max = 100; + let max = fullness + .iter() + .map(|(fullness, _)| *fullness) + .max() + .unwrap() as i32; let mut chart = ChartBuilder::on(drawing_area) .caption(title, ("sans-serif", 50).into_font()) @@ -199,7 +203,7 @@ pub fn draw_fullness( chart .configure_mesh() - .y_desc("Fullness Percentage") + .y_desc("Fullness") .x_desc("L2 Block") .x_labels(fullness.len()) .x_label_formatter(&|x| format!("{}", x + first_height as usize))