diff --git a/CHANGELOG.md b/CHANGELOG.md index 570a313067..8e41f666a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,6 +86,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - [2304](https://github.com/FuelLabs/fuel-core/pull/2304): Add initialization for the genesis base asset contract. +### Added +- [2288](https://github.com/FuelLabs/fuel-core/pull/2288): Specify `V1Metadata` for `GasPriceServiceV1`. + ## [Version 0.37.0] ### Added diff --git a/Cargo.lock b/Cargo.lock index d3f7c31535..def920c335 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3790,7 +3790,7 @@ version = "0.40.0" dependencies = [ "anyhow", "async-trait", - "derive_more", + "derive_more 0.99.18", "fuel-core-metrics", "fuel-core-services", "fuel-core-storage", diff --git a/crates/fuel-core/src/service/adapters/gas_price_adapters.rs b/crates/fuel-core/src/service/adapters/gas_price_adapters.rs index 014dc98456..857c6ded98 100644 --- a/crates/fuel-core/src/service/adapters/gas_price_adapters.rs +++ b/crates/fuel-core/src/service/adapters/gas_price_adapters.rs @@ -64,9 +64,9 @@ impl GasPriceData for Database { impl From for GasPriceServiceConfig { fn from(value: Config) -> Self { - GasPriceServiceConfig::new( - value.min_gas_price, + GasPriceServiceConfig::new_v0( value.starting_gas_price, + value.min_gas_price, value.gas_price_change_percent, value.gas_price_threshold_percent, ) diff --git a/crates/fuel-core/src/service/sub_services.rs b/crates/fuel-core/src/service/sub_services.rs index 13c9f6a9d0..342783c2ba 100644 --- a/crates/fuel-core/src/service/sub_services.rs +++ b/crates/fuel-core/src/service/sub_services.rs @@ -48,6 +48,7 @@ use fuel_core_poa::{ }; use fuel_core_storage::{ self, + structured_storage::StructuredStorage, transactional::AtomicView, }; #[cfg(feature = "relayer")] @@ -181,6 +182,7 @@ pub fn init_sub_services( let genesis_block_height = *genesis_block.header().height(); let settings = consensus_parameters_provider.clone(); let block_stream = importer_adapter.events_shared_result(); + let metadata = StructuredStorage::new(database.gas_price().clone()); let gas_price_service_v0 = new_gas_price_service_v0( config.clone().into(), @@ -188,6 +190,7 @@ pub fn init_sub_services( settings, block_stream, database.gas_price().clone(), + metadata, database.on_chain().clone(), )?; diff --git a/crates/fuel-gas-price-algorithm/src/v0.rs b/crates/fuel-gas-price-algorithm/src/v0.rs index 418e6619c7..126d70a366 100644 --- a/crates/fuel-gas-price-algorithm/src/v0.rs +++ b/crates/fuel-gas-price-algorithm/src/v0.rs @@ -74,8 +74,9 @@ impl AlgorithmUpdaterV0 { l2_block_height: u32, l2_block_fullness_threshold_percent: u64, ) -> Self { + let new_exec_price_corrected = max(new_exec_price, min_exec_gas_price); Self { - new_exec_price, + new_exec_price: new_exec_price_corrected, min_exec_gas_price, exec_gas_price_change_percent, l2_block_height, diff --git a/crates/services/gas_price_service/src/common/updater_metadata.rs b/crates/services/gas_price_service/src/common/updater_metadata.rs index b12fa32cf8..6ce274ea1a 100644 --- a/crates/services/gas_price_service/src/common/updater_metadata.rs +++ b/crates/services/gas_price_service/src/common/updater_metadata.rs @@ -1,16 +1,25 @@ -use crate::v0::metadata::V0Metadata; +use crate::{ + common::utils::Error, + v0::metadata::V0Metadata, + v1::metadata::V1Metadata, +}; use fuel_core_types::fuel_types::BlockHeight; -use fuel_gas_price_algorithm::v0::AlgorithmUpdaterV0; +use fuel_gas_price_algorithm::{ + v0::AlgorithmUpdaterV0, + v1::AlgorithmUpdaterV1, +}; #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] pub enum UpdaterMetadata { V0(V0Metadata), + V1(V1Metadata), } impl UpdaterMetadata { pub fn l2_block_height(&self) -> BlockHeight { match self { - UpdaterMetadata::V0(v1) => v1.l2_block_height.into(), + UpdaterMetadata::V0(v0) => v0.l2_block_height.into(), + UpdaterMetadata::V1(v1) => v1.l2_block_height.into(), } } } @@ -20,3 +29,31 @@ impl From for UpdaterMetadata { Self::V0(updater.into()) } } + +impl From for UpdaterMetadata { + fn from(updater: AlgorithmUpdaterV1) -> Self { + Self::V1(updater.into()) + } +} + +impl TryFrom for V0Metadata { + type Error = Error; + + fn try_from(metadata: UpdaterMetadata) -> Result { + match metadata { + UpdaterMetadata::V0(v0) => Ok(v0), + _ => Err(Error::CouldNotConvertMetadata), + } + } +} + +impl TryFrom for V1Metadata { + type Error = Error; + + fn try_from(metadata: UpdaterMetadata) -> Result { + match metadata { + UpdaterMetadata::V1(v1) => Ok(v1), + _ => Err(Error::CouldNotConvertMetadata), + } + } +} diff --git a/crates/services/gas_price_service/src/common/utils.rs b/crates/services/gas_price_service/src/common/utils.rs index 2ee96cecec..712e288adc 100644 --- a/crates/services/gas_price_service/src/common/utils.rs +++ b/crates/services/gas_price_service/src/common/utils.rs @@ -17,6 +17,8 @@ pub enum Error { }, #[error("Failed to initialize updater: {0:?}")] CouldNotInitUpdater(anyhow::Error), + #[error("Failed to convert metadata to concrete type. THere is no migration path for this metadata version")] + CouldNotConvertMetadata, // todo(https://github.com/FuelLabs/fuel-core/issues/2286) } pub type Result = core::result::Result; diff --git a/crates/services/gas_price_service/src/ports.rs b/crates/services/gas_price_service/src/ports.rs index cefa85f4e0..cb829ab21f 100644 --- a/crates/services/gas_price_service/src/ports.rs +++ b/crates/services/gas_price_service/src/ports.rs @@ -1,6 +1,10 @@ -use crate::common::{ - updater_metadata::UpdaterMetadata, - utils::Result, +use crate::{ + common::{ + updater_metadata::UpdaterMetadata, + utils::Result, + }, + v0::metadata::V0AlgorithmConfig, + v1::metadata::V1AlgorithmConfig, }; use fuel_core_storage::Result as StorageResult; use fuel_core_types::{ @@ -30,25 +34,57 @@ pub trait GasPriceData: Send + Sync { fn latest_height(&self) -> Option; } -pub struct GasPriceServiceConfig { - pub min_gas_price: u64, - pub starting_gas_price: u64, - pub gas_price_change_percent: u64, - pub gas_price_threshold_percent: u64, +pub enum GasPriceServiceConfig { + V0(V0AlgorithmConfig), + V1(V1AlgorithmConfig), } impl GasPriceServiceConfig { - pub fn new( - min_gas_price: u64, + pub fn new_v0( starting_gas_price: u64, + min_gas_price: u64, gas_price_change_percent: u64, gas_price_threshold_percent: u64, ) -> Self { - Self { - min_gas_price, + Self::V0(V0AlgorithmConfig { starting_gas_price, + min_gas_price, gas_price_change_percent, gas_price_threshold_percent, + }) + } + + pub fn new_v1(metadata: V1AlgorithmConfig) -> Self { + Self::V1(metadata) + } + + /// Extract V0AlgorithmConfig if it is of V0 version + pub fn v0(self) -> Option { + if let GasPriceServiceConfig::V0(v0) = self { + Some(v0) + } else { + None } } + + /// Extract V1AlgorithmConfig if it is of V1 version + pub fn v1(self) -> Option { + if let GasPriceServiceConfig::V1(v1) = self { + Some(v1) + } else { + None + } + } +} + +impl From for GasPriceServiceConfig { + fn from(value: V0AlgorithmConfig) -> Self { + GasPriceServiceConfig::V0(value) + } +} + +impl From for GasPriceServiceConfig { + fn from(value: V1AlgorithmConfig) -> Self { + GasPriceServiceConfig::V1(value) + } } diff --git a/crates/services/gas_price_service/src/v0/algorithm.rs b/crates/services/gas_price_service/src/v0/algorithm.rs index 34209eb4f0..e5d4c79668 100644 --- a/crates/services/gas_price_service/src/v0/algorithm.rs +++ b/crates/services/gas_price_service/src/v0/algorithm.rs @@ -1,4 +1,7 @@ -use crate::common::gas_price_algorithm::GasPriceAlgorithm; +use crate::common::gas_price_algorithm::{ + GasPriceAlgorithm, + SharedGasPriceAlgo, +}; use fuel_core_types::fuel_types::BlockHeight; use fuel_gas_price_algorithm::v0::AlgorithmV0; @@ -11,3 +14,5 @@ impl GasPriceAlgorithm for AlgorithmV0 { self.worst_case(block_height.into()) } } + +pub type SharedV0Algorithm = SharedGasPriceAlgo; diff --git a/crates/services/gas_price_service/src/v0/metadata.rs b/crates/services/gas_price_service/src/v0/metadata.rs index 0d21b7b06e..f42e33e041 100644 --- a/crates/services/gas_price_service/src/v0/metadata.rs +++ b/crates/services/gas_price_service/src/v0/metadata.rs @@ -4,41 +4,22 @@ use fuel_gas_price_algorithm::v0::AlgorithmUpdaterV0; pub struct V0Metadata { /// The gas price to cover the execution of the next block pub new_exec_price: u64, - // Execution - /// The lowest the algorithm allows the exec gas price to go - pub min_exec_gas_price: u64, - /// The Percentage the execution gas price will change in a single block, either increase or decrease - /// based on the fullness of the last L2 block - pub exec_gas_price_change_percent: u64, /// The height for which the `new_exec_price` is calculated, which should be the _next_ block pub l2_block_height: u32, - /// The threshold of gas usage above and below which the gas price will increase or decrease - /// This is a percentage of the total capacity of the L2 block - pub l2_block_fullness_threshold_percent: u64, } -impl From for AlgorithmUpdaterV0 { - fn from(metadata: V0Metadata) -> Self { - Self { - new_exec_price: metadata.new_exec_price, - min_exec_gas_price: metadata.min_exec_gas_price, - exec_gas_price_change_percent: metadata.exec_gas_price_change_percent, - l2_block_height: metadata.l2_block_height, - l2_block_fullness_threshold_percent: metadata - .l2_block_fullness_threshold_percent, - } - } +pub struct V0AlgorithmConfig { + pub starting_gas_price: u64, + pub min_gas_price: u64, + pub gas_price_change_percent: u64, + pub gas_price_threshold_percent: u64, } impl From for V0Metadata { fn from(updater: AlgorithmUpdaterV0) -> Self { Self { new_exec_price: updater.new_exec_price, - min_exec_gas_price: updater.min_exec_gas_price, - exec_gas_price_change_percent: updater.exec_gas_price_change_percent, l2_block_height: updater.l2_block_height, - l2_block_fullness_threshold_percent: updater - .l2_block_fullness_threshold_percent, } } } diff --git a/crates/services/gas_price_service/src/v0/service.rs b/crates/services/gas_price_service/src/v0/service.rs index 04254f67cb..b8d95650cd 100644 --- a/crates/services/gas_price_service/src/v0/service.rs +++ b/crates/services/gas_price_service/src/v0/service.rs @@ -5,7 +5,7 @@ use crate::{ utils::BlockInfo, }, ports::MetadataStorage, - v0::uninitialized_task::SharedV0Algorithm, + v0::algorithm::SharedV0Algorithm, }; use anyhow::anyhow; use async_trait::async_trait; @@ -165,12 +165,10 @@ mod tests { }, ports::MetadataStorage, v0::{ - metadata::V0Metadata, + algorithm::SharedV0Algorithm, + metadata::V0AlgorithmConfig, service::GasPriceServiceV0, - uninitialized_task::{ - initialize_algorithm, - SharedV0Algorithm, - }, + uninitialized_task::initialize_algorithm, }, }; use fuel_core_services::{ @@ -262,15 +260,15 @@ mod tests { l2_block: l2_block_receiver, }; let metadata_storage = FakeMetadata::empty(); - let starting_metadata = V0Metadata { - min_exec_gas_price: 10, - exec_gas_price_change_percent: 10, - new_exec_price: 100, - l2_block_fullness_threshold_percent: 0, - l2_block_height: 0, + let l2_block_height = 0; + let config = V0AlgorithmConfig { + starting_gas_price: 100, + min_gas_price: 10, + gas_price_change_percent: 10, + gas_price_threshold_percent: 0, }; let (algo_updater, shared_algo) = - initialize_algorithm(starting_metadata.clone(), &metadata_storage).unwrap(); + initialize_algorithm(&config, l2_block_height, &metadata_storage).unwrap(); let service = GasPriceServiceV0::new( l2_block_source, diff --git a/crates/services/gas_price_service/src/v0/tests.rs b/crates/services/gas_price_service/src/v0/tests.rs index 9c5128371f..a43f4bca46 100644 --- a/crates/services/gas_price_service/src/v0/tests.rs +++ b/crates/services/gas_price_service/src/v0/tests.rs @@ -2,6 +2,10 @@ use crate::{ common::{ + fuel_core_storage_adapter::{ + GasPriceSettings, + GasPriceSettingsProvider, + }, l2_block_source::L2BlockSource, updater_metadata::UpdaterMetadata, utils::{ @@ -10,20 +14,50 @@ use crate::{ Result as GasPriceResult, }, }, - ports::MetadataStorage, + ports::{ + GasPriceData, + L2Data, + MetadataStorage, + }, v0::{ - metadata::V0Metadata, + metadata::{ + V0AlgorithmConfig, + V0Metadata, + }, service::GasPriceServiceV0, - uninitialized_task::initialize_algorithm, + uninitialized_task::{ + initialize_algorithm, + UninitializedTask, + }, }, }; use anyhow::anyhow; use fuel_core_services::{ + stream::{ + BoxStream, + IntoBoxStream, + }, Service, ServiceRunner, }; -use fuel_core_types::fuel_types::BlockHeight; +use fuel_core_storage::{ + transactional::AtomicView, + Result as StorageResult, +}; +use fuel_core_types::{ + blockchain::{ + block::Block, + header::ConsensusParametersVersion, + }, + fuel_tx::Transaction, + fuel_types::BlockHeight, + services::block_importer::{ + ImportResult, + SharedImportResult, + }, +}; use std::{ + ops::Deref, sync::Arc, time::Duration, }; @@ -41,15 +75,6 @@ impl L2BlockSource for FakeL2BlockSource { } } -struct PendingL2BlockSource; - -#[async_trait::async_trait] -impl L2BlockSource for PendingL2BlockSource { - async fn get_l2_block(&mut self) -> GasPriceResult { - futures::future::pending().await - } -} - struct FakeMetadata { inner: Arc>>, } @@ -91,27 +116,28 @@ impl MetadataStorage for ErroringMetadata { } } -fn arb_metadata() -> V0Metadata { - V0Metadata { - // set values - exec_gas_price_change_percent: 10, - new_exec_price: 100, - // unset values - l2_block_height: 0, - l2_block_fullness_threshold_percent: 0, - min_exec_gas_price: 0, +fn arbitrary_config() -> V0AlgorithmConfig { + V0AlgorithmConfig { + starting_gas_price: 100, + min_gas_price: 0, + gas_price_change_percent: 10, + gas_price_threshold_percent: 0, } } -fn different_arb_metadata() -> V0Metadata { +fn arbitrary_metadata() -> V0Metadata { V0Metadata { - // set values - exec_gas_price_change_percent: 20, new_exec_price: 100, - // unset values l2_block_height: 0, - l2_block_fullness_threshold_percent: 0, - min_exec_gas_price: 0, + } +} + +fn different_arb_config() -> V0AlgorithmConfig { + V0AlgorithmConfig { + starting_gas_price: 200, + min_gas_price: 0, + gas_price_change_percent: 20, + gas_price_threshold_percent: 0, } } @@ -129,9 +155,10 @@ async fn next_gas_price__affected_by_new_l2_block() { }; let metadata_storage = FakeMetadata::empty(); - let starting_metadata = arb_metadata(); + let config = arbitrary_config(); + let height = 0; let (algo_updater, shared_algo) = - initialize_algorithm(starting_metadata.clone(), &metadata_storage).unwrap(); + initialize_algorithm(&config, height, &metadata_storage).unwrap(); let service = GasPriceServiceV0::new( l2_block_source, metadata_storage, @@ -170,9 +197,10 @@ async fn next__new_l2_block_saves_old_metadata() { inner: metadata_inner.clone(), }; - let starting_metadata = arb_metadata(); + let config = arbitrary_config(); + let height = 0; let (algo_updater, shared_algo) = - initialize_algorithm(starting_metadata.clone(), &metadata_storage).unwrap(); + initialize_algorithm(&config, height, &metadata_storage).unwrap(); let service = GasPriceServiceV0::new( l2_block_source, @@ -183,64 +211,152 @@ async fn next__new_l2_block_saves_old_metadata() { // when let service = ServiceRunner::new(service); - let shared = service.shared.clone(); - let start = shared.next_gas_price(); service.start_and_await().await.unwrap(); l2_block_sender.send(l2_block).await.unwrap(); tokio::time::sleep(Duration::from_millis(10)).await; // then - let new = shared.next_gas_price(); - assert_ne!(start, new); + assert!(metadata_inner.lock().unwrap().is_some()); +} + +#[derive(Clone)] +struct FakeSettings; + +impl GasPriceSettingsProvider for FakeSettings { + fn settings( + &self, + _param_version: &ConsensusParametersVersion, + ) -> GasPriceResult { + unimplemented!() + } +} + +#[derive(Clone)] +struct FakeGasPriceDb; + +// GasPriceData + Modifiable + KeyValueInspect +impl GasPriceData for FakeGasPriceDb { + fn latest_height(&self) -> Option { + unimplemented!() + } +} + +#[derive(Clone)] +struct FakeOnChainDb { + height: BlockHeight, +} + +impl FakeOnChainDb { + fn new(height: u32) -> Self { + Self { + height: height.into(), + } + } +} + +struct FakeL2Data { + height: BlockHeight, +} + +impl FakeL2Data { + fn new(height: BlockHeight) -> Self { + Self { height } + } +} + +impl L2Data for FakeL2Data { + fn latest_height(&self) -> StorageResult { + Ok(self.height) + } + + fn get_block( + &self, + _height: &BlockHeight, + ) -> StorageResult>> { + unimplemented!() + } +} +impl AtomicView for FakeOnChainDb { + type LatestView = FakeL2Data; + + fn latest_view(&self) -> StorageResult { + Ok(FakeL2Data::new(self.height)) + } +} + +fn empty_block_stream() -> BoxStream { + let blocks: Vec + Send + Sync>> = vec![]; + tokio_stream::iter(blocks).into_boxed() } #[tokio::test] -async fn new__if_exists_already_reload_old_values_with_overrides() { +async fn uninitialized_task__new__if_exists_already_reload_old_values_with_overrides() { // given - let original = UpdaterMetadata::V0(arb_metadata()); + let original_metadata = arbitrary_metadata(); + let original = UpdaterMetadata::V0(original_metadata.clone()); let metadata_inner = Arc::new(std::sync::Mutex::new(Some(original.clone()))); let metadata_storage = FakeMetadata { inner: metadata_inner, }; - let l2_block_source = PendingL2BlockSource; - let new_exec_gas_price = 100; - let new_min_exec_gas_price = 99; - let new_exec_gas_price_change_percent = 88; - let new_l2_block_fullness_threshold_percent = 77; - let new_metadata = V0Metadata { - exec_gas_price_change_percent: new_exec_gas_price_change_percent, - new_exec_price: new_exec_gas_price, - l2_block_fullness_threshold_percent: new_l2_block_fullness_threshold_percent, - min_exec_gas_price: new_min_exec_gas_price, - l2_block_height: original.l2_block_height().into(), - }; - let (algo_updater, shared_algo) = - initialize_algorithm(new_metadata, &metadata_storage).unwrap(); + + let different_config = different_arb_config(); + assert_ne!( + different_config.starting_gas_price, + original_metadata.new_exec_price + ); + let different_l2_block = 1231; + assert_ne!(different_l2_block, original_metadata.l2_block_height); + let settings = FakeSettings; + let block_stream = empty_block_stream(); + let gas_price_db = FakeGasPriceDb; + let on_chain_db = FakeOnChainDb::new(different_l2_block); // when - let service = GasPriceServiceV0::new( - l2_block_source, + let service = UninitializedTask::new( + different_config, + 0.into(), + settings, + block_stream, + gas_price_db, metadata_storage, - shared_algo, - algo_updater, - ); + on_chain_db, + ) + .unwrap(); // then - let expected = original; - let actual = service.algorithm_updater().clone().into(); - assert_ne!(expected, actual); + let V0Metadata { + new_exec_price, + l2_block_height, + } = original_metadata; + let UninitializedTask { algo_updater, .. } = service; + assert_eq!(algo_updater.new_exec_price, new_exec_price); + assert_eq!(algo_updater.l2_block_height, l2_block_height); } #[tokio::test] -async fn initialize_algorithm__should_fail_if_cannot_fetch_metadata() { +async fn uninitialized_task__new__should_fail_if_cannot_fetch_metadata() { // given + let config = arbitrary_config(); + let different_l2_block = 1231; let metadata_storage = ErroringMetadata; + let settings = FakeSettings; + let block_stream = empty_block_stream(); + let gas_price_db = FakeGasPriceDb; + let on_chain_db = FakeOnChainDb::new(different_l2_block); // when - let metadata = different_arb_metadata(); - let res = initialize_algorithm(metadata, &metadata_storage); + let res = UninitializedTask::new( + config, + 0.into(), + settings, + block_stream, + gas_price_db, + metadata_storage, + on_chain_db, + ); // then - assert!(matches!(res, Err(GasPriceError::CouldNotInitUpdater(_)))); + let is_err = res.is_err(); + assert!(is_err); } diff --git a/crates/services/gas_price_service/src/v0/uninitialized_task.rs b/crates/services/gas_price_service/src/v0/uninitialized_task.rs index bd58db174c..569739605c 100644 --- a/crates/services/gas_price_service/src/v0/uninitialized_task.rs +++ b/crates/services/gas_price_service/src/v0/uninitialized_task.rs @@ -2,7 +2,6 @@ use crate::{ common::{ fuel_core_storage_adapter::{ get_block_info, - storage::GasPriceColumn, GasPriceSettings, GasPriceSettingsProvider, }, @@ -22,7 +21,11 @@ use crate::{ MetadataStorage, }, v0::{ - metadata::V0Metadata, + algorithm::SharedV0Algorithm, + metadata::{ + V0AlgorithmConfig, + V0Metadata, + }, service::GasPriceServiceV0, }, }; @@ -33,13 +36,8 @@ use fuel_core_services::{ StateWatcher, }; use fuel_core_storage::{ - kv_store::KeyValueInspect, not_found, - structured_storage::StructuredStorage, - transactional::{ - AtomicView, - Modifiable, - }, + transactional::AtomicView, }; use fuel_core_types::{ fuel_types::BlockHeight, @@ -49,48 +47,34 @@ use fuel_gas_price_algorithm::v0::AlgorithmUpdaterV0; pub use fuel_gas_price_algorithm::v0::AlgorithmV0; -pub type SharedV0Algorithm = SharedGasPriceAlgo; - -pub struct UninitializedTask { - pub config: GasPriceServiceConfig, +pub struct UninitializedTask { + pub config: V0AlgorithmConfig, pub genesis_block_height: BlockHeight, pub settings: SettingsProvider, pub gas_price_db: GasPriceStore, pub on_chain_db: L2DataStoreView, pub block_stream: BoxStream, - shared_algo: SharedV0Algorithm, - algo_updater: AlgorithmUpdaterV0, - metadata_storage: StructuredStorage, + pub(crate) shared_algo: SharedV0Algorithm, + pub(crate) algo_updater: AlgorithmUpdaterV0, + pub(crate) metadata_storage: Metadata, } -fn get_default_metadata( - config: &GasPriceServiceConfig, - latest_block_height: u32, -) -> V0Metadata { - V0Metadata { - new_exec_price: config.starting_gas_price.max(config.min_gas_price), - min_exec_gas_price: config.min_gas_price, - exec_gas_price_change_percent: config.gas_price_change_percent, - l2_block_height: latest_block_height, - l2_block_fullness_threshold_percent: config.gas_price_threshold_percent, - } -} - -impl - UninitializedTask +impl + UninitializedTask where L2DataStore: L2Data, L2DataStoreView: AtomicView, - GasPriceStore: - GasPriceData + Modifiable + KeyValueInspect + Clone, + GasPriceStore: GasPriceData, + Metadata: MetadataStorage, SettingsProvider: GasPriceSettingsProvider, { pub fn new( - config: GasPriceServiceConfig, + config: V0AlgorithmConfig, genesis_block_height: BlockHeight, settings: SettingsProvider, block_stream: BoxStream, gas_price_db: GasPriceStore, + metadata_storage: Metadata, on_chain_db: L2DataStoreView, ) -> anyhow::Result { let latest_block_height: u32 = on_chain_db @@ -99,10 +83,8 @@ where .unwrap_or(genesis_block_height) .into(); - let metadata_storage = StructuredStorage::new(gas_price_db.clone()); - let starting_metadata = get_default_metadata(&config, latest_block_height); let (algo_updater, shared_algo) = - initialize_algorithm(starting_metadata, &metadata_storage)?; + initialize_algorithm(&config, latest_block_height, &metadata_storage)?; let task = Self { config, @@ -120,12 +102,8 @@ where pub fn init( mut self, - ) -> anyhow::Result< - GasPriceServiceV0< - FuelL2BlockSource, - StructuredStorage, - >, - > { + ) -> anyhow::Result, Metadata>> + { let mut first_run = false; let latest_block_height: u32 = self .on_chain_db @@ -181,21 +159,19 @@ where } #[async_trait::async_trait] -impl RunnableService - for UninitializedTask +impl + RunnableService + for UninitializedTask where L2DataStore: L2Data, L2DataStoreView: AtomicView, - GasPriceStore: - GasPriceData + Modifiable + KeyValueInspect + Clone, + GasPriceStore: GasPriceData, + Metadata: MetadataStorage, SettingsProvider: GasPriceSettingsProvider, { const NAME: &'static str = "GasPriceServiceV0"; type SharedData = SharedV0Algorithm; - type Task = GasPriceServiceV0< - FuelL2BlockSource, - StructuredStorage, - >; + type Task = GasPriceServiceV0, Metadata>; type TaskParams = (); fn shared_data(&self) -> Self::SharedData { @@ -212,40 +188,36 @@ where } pub fn initialize_algorithm( - starting_metadata: V0Metadata, + config: &V0AlgorithmConfig, + latest_block_height: u32, metadata_storage: &Metadata, ) -> GasPriceResult<(AlgorithmUpdaterV0, SharedV0Algorithm)> where Metadata: MetadataStorage, { - let V0Metadata { - min_exec_gas_price, - exec_gas_price_change_percent, - new_exec_price, - l2_block_fullness_threshold_percent, - l2_block_height, - } = starting_metadata; + let min_exec_gas_price = config.min_gas_price; + let exec_gas_price_change_percent = config.gas_price_change_percent; + let l2_block_fullness_threshold_percent = config.gas_price_threshold_percent; let algorithm_updater; - if let Some(old_metadata) = metadata_storage - .get_metadata(&l2_block_height.into()) + if let Some(updater_metadata) = metadata_storage + .get_metadata(&latest_block_height.into()) .map_err(|err| GasPriceError::CouldNotInitUpdater(anyhow::anyhow!(err)))? { - algorithm_updater = match old_metadata { - UpdaterMetadata::V0(old) => AlgorithmUpdaterV0::new( - old.new_exec_price, - min_exec_gas_price, - exec_gas_price_change_percent, - old.l2_block_height, - l2_block_fullness_threshold_percent, - ), - }; + let previous_metadata: V0Metadata = updater_metadata.try_into()?; + algorithm_updater = AlgorithmUpdaterV0::new( + previous_metadata.new_exec_price, + min_exec_gas_price, + exec_gas_price_change_percent, + previous_metadata.l2_block_height, + l2_block_fullness_threshold_percent, + ); } else { algorithm_updater = AlgorithmUpdaterV0::new( - new_exec_price, + config.starting_gas_price, min_exec_gas_price, exec_gas_price_change_percent, - l2_block_height, + latest_block_height, l2_block_fullness_threshold_percent, ); } @@ -259,11 +231,11 @@ where fn sync_gas_price_db_with_on_chain_storage< L2DataStore, L2DataStoreView, - GasPriceStore, + Metadata, SettingsProvider, >( settings: &SettingsProvider, - metadata_storage: &mut StructuredStorage, + metadata_storage: &mut Metadata, on_chain_db: &L2DataStoreView, metadata_height: u32, latest_block_height: u32, @@ -271,16 +243,26 @@ fn sync_gas_price_db_with_on_chain_storage< where L2DataStore: L2Data, L2DataStoreView: AtomicView, - GasPriceStore: GasPriceData + Modifiable + KeyValueInspect, + Metadata: MetadataStorage, SettingsProvider: GasPriceSettingsProvider, { - let UpdaterMetadata::V0(metadata) = metadata_storage + let metadata = metadata_storage .get_metadata(&metadata_height.into())? .ok_or(anyhow::anyhow!( "Expected metadata to exist for height: {metadata_height}" ))?; - let mut algo_updater = metadata.into(); + let mut algo_updater = if let UpdaterMetadata::V0(metadata) = metadata { + Ok(AlgorithmUpdaterV0::new( + metadata.new_exec_price, + 0, + 0, + metadata.l2_block_height, + 0, + )) + } else { + Err(anyhow::anyhow!("Expected V0 metadata")) + }?; sync_v0_metadata( settings, @@ -294,18 +276,18 @@ where Ok(()) } -fn sync_v0_metadata( +fn sync_v0_metadata( settings: &SettingsProvider, on_chain_db: &L2DataStoreView, metadata_height: u32, latest_block_height: u32, updater: &mut AlgorithmUpdaterV0, - metadata_storage: &mut StructuredStorage, + metadata_storage: &mut Metadata, ) -> anyhow::Result<()> where L2DataStore: L2Data, L2DataStoreView: AtomicView, - GasPriceStore: GasPriceData + Modifiable + KeyValueInspect, + Metadata: MetadataStorage, SettingsProvider: GasPriceSettingsProvider, { let first = metadata_height.saturating_add(1); @@ -342,6 +324,7 @@ pub fn new_gas_price_service_v0< L2DataStore, L2DataStoreView, GasPriceStore, + Metadata, SettingsProvider, >( config: GasPriceServiceConfig, @@ -349,25 +332,29 @@ pub fn new_gas_price_service_v0< settings: SettingsProvider, block_stream: BoxStream, gas_price_db: GasPriceStore, + metadata: Metadata, on_chain_db: L2DataStoreView, ) -> anyhow::Result< - ServiceRunner>, + ServiceRunner< + UninitializedTask, + >, > where L2DataStore: L2Data, L2DataStoreView: AtomicView, - GasPriceStore: - GasPriceData + Modifiable + KeyValueInspect + Clone, + GasPriceStore: GasPriceData, SettingsProvider: GasPriceSettingsProvider, + Metadata: MetadataStorage, { - // lazy initialization of gas price service, delegate to parent - let gas_price_uninit = UninitializedTask::new( - config, + let v0_config = config.v0().ok_or(anyhow::anyhow!("Expected V0 config"))?; + let gas_price_init = UninitializedTask::new( + v0_config, genesis_block_height, settings, block_stream, gas_price_db, + metadata, on_chain_db, )?; - Ok(ServiceRunner::new(gas_price_uninit)) + Ok(ServiceRunner::new(gas_price_init)) } diff --git a/crates/services/gas_price_service/src/v1.rs b/crates/services/gas_price_service/src/v1.rs index c0f23f5100..4770112762 100644 --- a/crates/services/gas_price_service/src/v1.rs +++ b/crates/services/gas_price_service/src/v1.rs @@ -1,2 +1,3 @@ pub mod algorithm; pub mod da_source_service; +pub mod metadata; diff --git a/crates/services/gas_price_service/src/v1/metadata.rs b/crates/services/gas_price_service/src/v1/metadata.rs new file mode 100644 index 0000000000..422cdf4250 --- /dev/null +++ b/crates/services/gas_price_service/src/v1/metadata.rs @@ -0,0 +1,140 @@ +use crate::v0::metadata::V0Metadata; +use fuel_gas_price_algorithm::v1::{ + AlgorithmUpdaterV1, + ClampedPercentage, + L2ActivityTracker, +}; +use std::num::NonZeroU64; + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] +pub struct V1Metadata { + // Execution + /// The gas price (scaled by the `gas_price_factor`) to cover the execution of the next block + pub new_scaled_exec_price: u64, + /// The height of the next L2 block + pub l2_block_height: u32, + // DA + /// The gas price (scaled by the `gas_price_factor`) to cover the DA commitment of the next block + pub new_scaled_da_gas_price: u64, + /// Scale factor for the gas price. + pub gas_price_factor: NonZeroU64, + /// The cumulative reward from the DA portion of the gas price + pub total_da_rewards_excess: u128, + /// The height of the last L2 block recorded on the DA chain + pub da_recorded_block_height: u32, + /// The cumulative cost of recording L2 blocks on the DA chain as of the last recorded block + pub latest_known_total_da_cost_excess: u128, + /// The predicted cost of recording L2 blocks on the DA chain as of the last L2 block + /// (This value is added on top of the `latest_known_total_da_cost` if the L2 height is higher) + pub projected_total_da_cost: u128, + /// The last profit + pub last_profit: i128, + /// The profit before last + pub second_to_last_profit: i128, + /// The latest known cost per byte for recording blocks on the DA chain + pub latest_da_cost_per_byte: u128, + /// List of (height, size) for l2 blocks that have not been recorded on the DA chain (that we know), + /// but have been used to estimate the cost of recording blocks on the DA chain + pub unrecorded_blocks: Vec<(u32, u64)>, +} + +impl V1Metadata { + pub fn construct_from_v0_metadata( + v0_metadata: V0Metadata, + config: V1AlgorithmConfig, + ) -> anyhow::Result { + let metadata = Self { + new_scaled_exec_price: v0_metadata + .new_exec_price + .saturating_mul(config.gas_price_factor.get()), + l2_block_height: v0_metadata.l2_block_height, + new_scaled_da_gas_price: config + .min_da_gas_price + .saturating_mul(config.gas_price_factor.get()), + gas_price_factor: config.gas_price_factor, + total_da_rewards_excess: 0, + // TODO: Set to `None` after: + // https://github.com/FuelLabs/fuel-core/issues/2397 + da_recorded_block_height: 0, + latest_known_total_da_cost_excess: 0, + projected_total_da_cost: 0, + last_profit: 0, + second_to_last_profit: 0, + latest_da_cost_per_byte: 0, + unrecorded_blocks: vec![], + }; + Ok(metadata) + } +} + +pub struct V1AlgorithmConfig { + new_exec_gas_price: u64, + min_exec_gas_price: u64, + exec_gas_price_change_percent: u16, + l2_block_fullness_threshold_percent: u8, + gas_price_factor: NonZeroU64, + min_da_gas_price: u64, + max_da_gas_price_change_percent: u16, + da_p_component: i64, + da_d_component: i64, + normal_range_size: u16, + capped_range_size: u16, + decrease_range_size: u16, + block_activity_threshold: u8, +} + +impl From for V1Metadata { + fn from(updater: AlgorithmUpdaterV1) -> Self { + Self { + new_scaled_exec_price: updater.new_scaled_exec_price, + l2_block_height: updater.l2_block_height, + new_scaled_da_gas_price: updater.new_scaled_da_gas_price, + gas_price_factor: updater.gas_price_factor, + total_da_rewards_excess: updater.total_da_rewards_excess, + da_recorded_block_height: updater.da_recorded_block_height, + latest_known_total_da_cost_excess: updater.latest_known_total_da_cost_excess, + projected_total_da_cost: updater.projected_total_da_cost, + last_profit: updater.last_profit, + second_to_last_profit: updater.second_to_last_profit, + latest_da_cost_per_byte: updater.latest_da_cost_per_byte, + unrecorded_blocks: updater.unrecorded_blocks.into_iter().collect(), + } + } +} + +pub fn v1_algorithm_from_metadata( + metadata: V1Metadata, + config: V1AlgorithmConfig, +) -> AlgorithmUpdaterV1 { + let l2_activity = L2ActivityTracker::new_full( + config.normal_range_size, + config.capped_range_size, + config.decrease_range_size, + config.block_activity_threshold.into(), + ); + let unrecorded_blocks = metadata.unrecorded_blocks.into_iter().collect(); + AlgorithmUpdaterV1 { + new_scaled_exec_price: metadata.new_scaled_exec_price, + l2_block_height: metadata.l2_block_height, + new_scaled_da_gas_price: metadata.new_scaled_da_gas_price, + gas_price_factor: metadata.gas_price_factor, + total_da_rewards_excess: metadata.total_da_rewards_excess, + da_recorded_block_height: metadata.da_recorded_block_height, + latest_known_total_da_cost_excess: metadata.latest_known_total_da_cost_excess, + projected_total_da_cost: metadata.projected_total_da_cost, + last_profit: metadata.last_profit, + second_to_last_profit: metadata.second_to_last_profit, + latest_da_cost_per_byte: metadata.latest_da_cost_per_byte, + l2_activity, + min_exec_gas_price: config.min_exec_gas_price, + exec_gas_price_change_percent: config.exec_gas_price_change_percent, + l2_block_fullness_threshold_percent: config + .l2_block_fullness_threshold_percent + .into(), + min_da_gas_price: config.min_da_gas_price, + max_da_gas_price_change_percent: config.max_da_gas_price_change_percent, + da_p_component: config.da_p_component, + da_d_component: config.da_d_component, + unrecorded_blocks, + } +} diff --git a/tests/tests/gas_price.rs b/tests/tests/gas_price.rs index 18507785ee..acd8b7d2ad 100644 --- a/tests/tests/gas_price.rs +++ b/tests/tests/gas_price.rs @@ -20,10 +20,7 @@ use fuel_core_client::client::{ FuelClient, }; use fuel_core_gas_price_service::{ - common::{ - fuel_core_storage_adapter::storage::GasPriceMetadata, - updater_metadata::UpdaterMetadata, - }, + common::fuel_core_storage_adapter::storage::GasPriceMetadata, v0::metadata::V0Metadata, }; use fuel_core_poa::Trigger; @@ -412,15 +409,8 @@ async fn startup__can_override_gas_price_values_by_changing_config() { .deref() .clone(); - let UpdaterMetadata::V0(V0Metadata { - min_exec_gas_price, - exec_gas_price_change_percent, - l2_block_height, - l2_block_fullness_threshold_percent, - .. - }) = new_metadata; - assert_eq!(exec_gas_price_change_percent, 11); - assert_eq!(l2_block_fullness_threshold_percent, 22); - assert_eq!(min_exec_gas_price, 33); + let V0Metadata { + l2_block_height, .. + } = new_metadata.try_into().unwrap(); assert_eq!(l2_block_height, new_height); }