diff --git a/CHANGELOG.md b/CHANGELOG.md index fcb4089c3e3..3bb97d51da1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added -- [1972](https://github.com/FuelLabs/fuel-core/pull/1972): Implement `AlgorithmUpdater` for `GasPriceService` +- [#1972](https://github.com/FuelLabs/fuel-core/pull/1972): Implement `AlgorithmUpdater` for `GasPriceService` - [#1948](https://github.com/FuelLabs/fuel-core/pull/1948): Add new `AlgorithmV1` and `AlgorithmUpdaterV1` for the gas price. Include tools for analysis +### Changed + +#### Breaking +- [#1989](https://github.com/FuelLabs/fuel-core/pull/1989): Extract `HistoricalView` trait from the `AtomicView`. + ## [Version 0.30.0] ### Added diff --git a/bin/fuel-core/chainspec/local-testnet/state_transition_bytecode.wasm b/bin/fuel-core/chainspec/local-testnet/state_transition_bytecode.wasm index 8e610b6f1ba..9b14f5112ce 100755 Binary files a/bin/fuel-core/chainspec/local-testnet/state_transition_bytecode.wasm and b/bin/fuel-core/chainspec/local-testnet/state_transition_bytecode.wasm differ diff --git a/crates/fuel-core/src/coins_query.rs b/crates/fuel-core/src/coins_query.rs index 8120d723ff9..c0adc94101a 100644 --- a/crates/fuel-core/src/coins_query.rs +++ b/crates/fuel-core/src/coins_query.rs @@ -343,7 +343,7 @@ mod tests { asset, base_asset_id, None, - &db.view(), + &db.test_view(), )) .map(|coins| { coins @@ -503,7 +503,7 @@ mod tests { db: &ServiceDatabase, ) -> Result, CoinsQueryError> { let coins = random_improve( - &db.view(), + &db.test_view(), &SpendQuery::new(owner, &query_per_asset, None, base_asset_id)?, ); @@ -701,7 +701,8 @@ mod tests { Some(excluded_ids), base_asset_id, )?; - let coins = random_improve(&db.service_database().view(), &spend_query); + let coins = + random_improve(&db.service_database().test_view(), &spend_query); // Transform result for convenience coins.map(|coins| { @@ -859,7 +860,7 @@ mod tests { } let coins = random_improve( - &db.service_database().view(), + &db.service_database().test_view(), &SpendQuery::new( owner, &[AssetSpendTarget { @@ -1008,7 +1009,7 @@ mod tests { pub fn owned_coins(&self, owner: &Address) -> Vec { use crate::query::CoinQueryData; let query = self.service_database(); - let query = query.view(); + let query = query.test_view(); query .owned_coins_ids(owner, None, IterDirection::Forward) .map(|res| res.map(|id| query.coin(id).unwrap())) @@ -1019,7 +1020,7 @@ mod tests { pub fn owned_messages(&self, owner: &Address) -> Vec { use crate::query::MessageQueryData; let query = self.service_database(); - let query = query.view(); + let query = query.test_view(); query .owned_message_ids(owner, None, IterDirection::Forward) .map(|res| res.map(|id| query.message(&id).unwrap())) diff --git a/crates/fuel-core/src/database.rs b/crates/fuel-core/src/database.rs index e46ce1a12de..a3777c8914b 100644 --- a/crates/fuel-core/src/database.rs +++ b/crates/fuel-core/src/database.rs @@ -49,13 +49,7 @@ use fuel_core_storage::{ StorageInspect, StorageMutate, }; -use fuel_core_types::{ - blockchain::{ - block::CompressedBlock, - primitives::DaBlockHeight, - }, - fuel_types::BlockHeight, -}; +use fuel_core_types::blockchain::block::CompressedBlock; use itertools::Itertools; use std::{ fmt::Debug, @@ -66,8 +60,10 @@ pub use fuel_core_database::Error; pub type Result = core::result::Result; // TODO: Extract `Database` and all belongs into `fuel-core-database`. +use crate::database::database_description::DatabaseHeight; #[cfg(feature = "rocksdb")] use crate::state::rocks_db::RocksDb; +use fuel_core_storage::transactional::HistoricalView; #[cfg(feature = "rocksdb")] use std::path::Path; @@ -294,60 +290,32 @@ where } } -impl AtomicView for Database { - type View = Self; - - type Height = BlockHeight; - - fn latest_height(&self) -> Option { - *self.stage.height.lock() - } - - fn view_at(&self, _: &BlockHeight) -> StorageResult { - // TODO: Unimplemented until of the https://github.com/FuelLabs/fuel-core/issues/451 - Ok(self.latest_view()) - } +impl AtomicView for Database +where + Description: DatabaseDescription, +{ + type LatestView = Self; - fn latest_view(&self) -> Self::View { + fn latest_view(&self) -> StorageResult { // TODO: https://github.com/FuelLabs/fuel-core/issues/1581 - self.clone() + Ok(self.clone()) } } -impl AtomicView for Database { - type View = Self; - - type Height = BlockHeight; +impl HistoricalView for Database +where + Description: DatabaseDescription, +{ + type Height = Description::Height; + type ViewAtHeight = Self; fn latest_height(&self) -> Option { *self.stage.height.lock() } - fn view_at(&self, _: &BlockHeight) -> StorageResult { + fn view_at(&self, _: &Self::Height) -> StorageResult { // TODO: Unimplemented until of the https://github.com/FuelLabs/fuel-core/issues/451 - Ok(self.latest_view()) - } - - fn latest_view(&self) -> Self::View { - // TODO: https://github.com/FuelLabs/fuel-core/issues/1581 - self.clone() - } -} - -impl AtomicView for Database { - type View = Self; - type Height = DaBlockHeight; - - fn latest_height(&self) -> Option { - *self.stage.height.lock() - } - - fn view_at(&self, _: &Self::Height) -> StorageResult { - Ok(self.latest_view()) - } - - fn latest_view(&self) -> Self::View { - self.clone() + Ok(self.clone()) } } @@ -409,33 +377,6 @@ impl Modifiable for GenesisDatabase { } } -trait DatabaseHeight: Sized { - fn as_u64(&self) -> u64; - - fn advance_height(&self) -> Option; -} - -impl DatabaseHeight for BlockHeight { - fn as_u64(&self) -> u64 { - let height: u32 = (*self).into(); - height as u64 - } - - fn advance_height(&self) -> Option { - self.succ() - } -} - -impl DatabaseHeight for DaBlockHeight { - fn as_u64(&self) -> u64 { - self.0 - } - - fn advance_height(&self) -> Option { - self.0.checked_add(1).map(Into::into) - } -} - fn commit_changes_with_height_update( database: &mut Database, changes: Changes, @@ -620,7 +561,7 @@ mod tests { .unwrap(); // Then - assert_eq!(AtomicView::latest_height(&database), None); + assert_eq!(HistoricalView::latest_height(&database), None); } #[test] @@ -780,7 +721,7 @@ mod tests { .unwrap(); // Then - assert_eq!(AtomicView::latest_height(&database), None); + assert_eq!(HistoricalView::latest_height(&database), None); } #[test] @@ -903,6 +844,7 @@ mod tests { }; use fuel_core_relayer::storage::EventsHistory; use fuel_core_storage::transactional::WriteTransaction; + use fuel_core_types::blockchain::primitives::DaBlockHeight; #[test] fn column_keys_not_exceed_count_test() { @@ -945,7 +887,7 @@ mod tests { .unwrap(); // Then - assert_eq!(AtomicView::latest_height(&database), None); + assert_eq!(HistoricalView::latest_height(&database), None); } #[test] diff --git a/crates/fuel-core/src/database/database_description.rs b/crates/fuel-core/src/database/database_description.rs index f62c6a11695..8e5f8ab3e7a 100644 --- a/crates/fuel-core/src/database/database_description.rs +++ b/crates/fuel-core/src/database/database_description.rs @@ -1,16 +1,47 @@ use core::fmt::Debug; use fuel_core_storage::kv_store::StorageColumn; +use fuel_core_types::{ + blockchain::primitives::DaBlockHeight, + fuel_types::BlockHeight, +}; pub mod off_chain; pub mod on_chain; pub mod relayer; +pub trait DatabaseHeight: PartialEq + Default + Copy + Send + Sync { + fn as_u64(&self) -> u64; + + fn advance_height(&self) -> Option; +} + +impl DatabaseHeight for BlockHeight { + fn as_u64(&self) -> u64 { + let height: u32 = (*self).into(); + height as u64 + } + + fn advance_height(&self) -> Option { + self.succ() + } +} + +impl DatabaseHeight for DaBlockHeight { + fn as_u64(&self) -> u64 { + self.0 + } + + fn advance_height(&self) -> Option { + self.0.checked_add(1).map(Into::into) + } +} + /// The description of the database that makes it unique. pub trait DatabaseDescription: 'static + Clone + Debug + Send + Sync { /// The type of the column used by the database. type Column: StorageColumn + strum::EnumCount + enum_iterator::Sequence; /// The type of the height of the database used to track commits. - type Height: Default + Copy; + type Height: DatabaseHeight; /// Returns the expected version of the database. fn version() -> u32; diff --git a/crates/fuel-core/src/executor.rs b/crates/fuel-core/src/executor.rs index 1d2d4bbd560..7a1bf096799 100644 --- a/crates/fuel-core/src/executor.rs +++ b/crates/fuel-core/src/executor.rs @@ -162,19 +162,10 @@ mod tests { } impl AtomicView for DisabledRelayer { - type View = Self; - type Height = DaBlockHeight; + type LatestView = Self; - fn latest_height(&self) -> Option { - Some(0u64.into()) - } - - fn view_at(&self, _: &Self::Height) -> StorageResult { - Ok(self.latest_view()) - } - - fn latest_view(&self) -> Self::View { - self.clone() + fn latest_view(&self) -> StorageResult { + Ok(self.clone()) } } @@ -508,6 +499,7 @@ mod tests { } = producer .storage_view_provider .latest_view() + .unwrap() .contract_balances(recipient, None, IterDirection::Forward) .next() .unwrap() @@ -598,6 +590,7 @@ mod tests { } = producer .storage_view_provider .latest_view() + .unwrap() .contract_balances(recipient, None, IterDirection::Forward) .next() .unwrap() @@ -707,6 +700,7 @@ mod tests { } = validator .storage_view_provider .latest_view() + .unwrap() .contract_balances(recipient, None, IterDirection::Forward) .next() .unwrap() @@ -2303,7 +2297,7 @@ mod tests { }; let mut exec = make_executor(&messages); - let view = exec.storage_view_provider.latest_view(); + let view = exec.storage_view_provider.latest_view().unwrap(); assert!(view.message_exists(message_coin.nonce()).unwrap()); assert!(view.message_exists(message_data.nonce()).unwrap()); @@ -2314,7 +2308,7 @@ mod tests { assert_eq!(skipped_transactions.len(), 0); // Successful execution consumes `message_coin` and `message_data`. - let view = exec.storage_view_provider.latest_view(); + let view = exec.storage_view_provider.latest_view().unwrap(); assert!(!view.message_exists(message_coin.nonce()).unwrap()); assert!(!view.message_exists(message_data.nonce()).unwrap()); assert_eq!( @@ -2350,7 +2344,7 @@ mod tests { }; let mut exec = make_executor(&messages); - let view = exec.storage_view_provider.latest_view(); + let view = exec.storage_view_provider.latest_view().unwrap(); assert!(view.message_exists(message_coin.nonce()).unwrap()); assert!(view.message_exists(message_data.nonce()).unwrap()); @@ -2361,7 +2355,7 @@ mod tests { assert_eq!(skipped_transactions.len(), 0); // We should spend only `message_coin`. The `message_data` should be unspent. - let view = exec.storage_view_provider.latest_view(); + let view = exec.storage_view_provider.latest_view().unwrap(); assert!(!view.message_exists(message_coin.nonce()).unwrap()); assert!(view.message_exists(message_data.nonce()).unwrap()); assert_eq!(*view.coin(&UtxoId::new(tx_id, 0)).unwrap().amount(), amount); diff --git a/crates/fuel-core/src/graphql_api/api_service.rs b/crates/fuel-core/src/graphql_api/api_service.rs index 179a62f6ed0..88eaf3da23a 100644 --- a/crates/fuel-core/src/graphql_api/api_service.rs +++ b/crates/fuel-core/src/graphql_api/api_service.rs @@ -190,10 +190,10 @@ pub fn new_service( request_timeout: Duration, ) -> anyhow::Result where - OnChain: AtomicView + 'static, - OffChain: AtomicView + 'static, - OnChain::View: OnChainDatabase, - OffChain::View: OffChainDatabase, + OnChain: AtomicView + 'static, + OffChain: AtomicView + 'static, + OnChain::LatestView: OnChainDatabase, + OffChain::LatestView: OffChainDatabase, { let network_addr = config.addr; let combined_read_database = diff --git a/crates/fuel-core/src/graphql_api/database.rs b/crates/fuel-core/src/graphql_api/database.rs index 5ac6e9b5589..3ff3e9f70bf 100644 --- a/crates/fuel-core/src/graphql_api/database.rs +++ b/crates/fuel-core/src/graphql_api/database.rs @@ -82,9 +82,9 @@ pub struct ReadDatabase { /// The height of the genesis block. genesis_height: BlockHeight, /// The on-chain database view provider. - on_chain: Box>, + on_chain: Box>, /// The off-chain database view provider. - off_chain: Box>, + off_chain: Box>, } impl ReadDatabase { @@ -95,10 +95,10 @@ impl ReadDatabase { off_chain: OffChain, ) -> Self where - OnChain: AtomicView + 'static, - OffChain: AtomicView + 'static, - OnChain::View: OnChainDatabase, - OffChain::View: OffChainDatabase, + OnChain: AtomicView + 'static, + OffChain: AtomicView + 'static, + OnChain::LatestView: OnChainDatabase, + OffChain::LatestView: OffChainDatabase, { Self { genesis_height, @@ -108,15 +108,20 @@ impl ReadDatabase { } /// Creates a consistent view of the database. - pub fn view(&self) -> ReadView { + pub fn view(&self) -> StorageResult { // TODO: Use the same height for both views to guarantee consistency. // It is not possible to implement until `view_at` is implemented for the `AtomicView`. // https://github.com/FuelLabs/fuel-core/issues/1582 - ReadView { + Ok(ReadView { genesis_height: self.genesis_height, - on_chain: self.on_chain.latest_view(), - off_chain: self.off_chain.latest_view(), - } + on_chain: self.on_chain.latest_view()?, + off_chain: self.off_chain.latest_view()?, + }) + } + + #[cfg(feature = "test-helpers")] + pub fn test_view(&self) -> ReadView { + self.view().expect("The latest view always should exist") } } diff --git a/crates/fuel-core/src/graphql_api/database/arc_wrapper.rs b/crates/fuel-core/src/graphql_api/database/arc_wrapper.rs index 5355bde48d8..0fc88ec8c70 100644 --- a/crates/fuel-core/src/graphql_api/database/arc_wrapper.rs +++ b/crates/fuel-core/src/graphql_api/database/arc_wrapper.rs @@ -30,46 +30,26 @@ impl ArcWrapper { } } -impl AtomicView for ArcWrapper +impl AtomicView for ArcWrapper where - Provider: AtomicView, + Provider: AtomicView, View: OnChainDatabase + 'static, { - type View = OnChainView; - type Height = Height; + type LatestView = OnChainView; - fn latest_height(&self) -> Option { - self.inner.latest_height() - } - - fn view_at(&self, height: &Height) -> StorageResult { - let view = self.inner.view_at(height)?; - Ok(Arc::new(view)) - } - - fn latest_view(&self) -> Self::View { - Arc::new(self.inner.latest_view()) + fn latest_view(&self) -> StorageResult { + Ok(Arc::new(self.inner.latest_view()?)) } } -impl AtomicView for ArcWrapper +impl AtomicView for ArcWrapper where - Provider: AtomicView, + Provider: AtomicView, View: OffChainDatabase + 'static, { - type View = OffChainView; - type Height = Height; - - fn latest_height(&self) -> Option { - self.inner.latest_height() - } - - fn view_at(&self, height: &Height) -> StorageResult { - let view = self.inner.view_at(height)?; - Ok(Arc::new(view)) - } + type LatestView = OffChainView; - fn latest_view(&self) -> Self::View { - Arc::new(self.inner.latest_view()) + fn latest_view(&self) -> StorageResult { + Ok(Arc::new(self.inner.latest_view()?)) } } diff --git a/crates/fuel-core/src/graphql_api/view_extension.rs b/crates/fuel-core/src/graphql_api/view_extension.rs index ca482fe9878..48c666ed189 100644 --- a/crates/fuel-core/src/graphql_api/view_extension.rs +++ b/crates/fuel-core/src/graphql_api/view_extension.rs @@ -6,7 +6,9 @@ use async_graphql::{ ExtensionFactory, NextPrepareRequest, }, + Pos, Request, + ServerError, ServerResult, }; use std::sync::Arc; @@ -37,7 +39,16 @@ impl Extension for ViewExtension { next: NextPrepareRequest<'_>, ) -> ServerResult { let database: &ReadDatabase = ctx.data_unchecked(); - let view = database.view(); + let view = database.view().map_err(|e| { + let (line, column) = (line!(), column!()); + ServerError::new( + e.to_string(), + Some(Pos { + line: line as usize, + column: column as usize, + }), + ) + })?; let request = request.data(view); next.run(ctx, request).await } diff --git a/crates/fuel-core/src/schema/dap.rs b/crates/fuel-core/src/schema/dap.rs index 3a247fa8706..6c61603df16 100644 --- a/crates/fuel-core/src/schema/dap.rs +++ b/crates/fuel-core/src/schema/dap.rs @@ -19,6 +19,7 @@ use async_graphql::{ use fuel_core_storage::{ not_found, transactional::{ + AtomicView, IntoTransaction, StorageTransaction, }, @@ -109,7 +110,7 @@ impl ConcreteStorage { &mut self, txs: &[Script], params: Arc, - storage: Database, + storage: &Database, ) -> anyhow::Result { let id = Uuid::new_v4(); let id = ID::from(id); @@ -156,7 +157,7 @@ impl ConcreteStorage { &mut self, id: &ID, params: Arc, - storage: Database, + storage: &Database, ) -> anyhow::Result<()> { let vm_database = Self::vm_database(storage)?; let tx = self @@ -202,13 +203,14 @@ impl ConcreteStorage { .ok_or_else(|| anyhow::anyhow!("The VM instance was not found")) } - fn vm_database(storage: Database) -> anyhow::Result { - let block = storage + fn vm_database(storage: &Database) -> anyhow::Result { + let view = storage.latest_view()?; + let block = view .get_current_block()? .ok_or(not_found!("Block for VMDatabase"))?; let vm_database = VmStorage::new( - storage.into_transaction(), + view.into_transaction(), block.header().consensus(), block.header().application(), // TODO: Use a real coinbase address @@ -313,11 +315,11 @@ impl DapMutation { .data_unchecked::() .latest_consensus_params(); - let id = ctx.data_unchecked::().lock().await.init( - &[], - params, - db.clone(), - )?; + let id = + ctx.data_unchecked::() + .lock() + .await + .init(&[], params, db)?; debug!("Session {:?} initialized", id); @@ -346,11 +348,10 @@ impl DapMutation { .data_unchecked::() .latest_consensus_params(); - ctx.data_unchecked::().lock().await.reset( - &id, - params, - db.clone(), - )?; + ctx.data_unchecked::() + .lock() + .await + .reset(&id, params, db)?; debug!("Session {:?} was reset", id); diff --git a/crates/fuel-core/src/service/adapters/producer.rs b/crates/fuel-core/src/service/adapters/producer.rs index 3aab12b5776..36470d8f951 100644 --- a/crates/fuel-core/src/service/adapters/producer.rs +++ b/crates/fuel-core/src/service/adapters/producer.rs @@ -190,6 +190,11 @@ fn get_gas_cost_for_height( } impl fuel_core_producer::ports::BlockProducerDatabase for Database { + fn latest_height(&self) -> Option { + use fuel_core_storage::transactional::HistoricalView; + HistoricalView::latest_height(self) + } + fn get_block(&self, height: &BlockHeight) -> StorageResult> { self.storage::() .get(height)? diff --git a/crates/fuel-core/src/service/adapters/relayer.rs b/crates/fuel-core/src/service/adapters/relayer.rs index c776146ec0f..ba08cc9d392 100644 --- a/crates/fuel-core/src/service/adapters/relayer.rs +++ b/crates/fuel-core/src/service/adapters/relayer.rs @@ -4,7 +4,7 @@ use crate::database::{ }; use fuel_core_relayer::ports::Transactional; use fuel_core_storage::transactional::{ - AtomicView, + HistoricalView, IntoTransaction, StorageTransaction, }; @@ -18,6 +18,6 @@ impl Transactional for Database { } fn latest_da_height(&self) -> Option { - AtomicView::latest_height(self) + HistoricalView::latest_height(self) } } diff --git a/crates/services/consensus_module/src/block_verifier.rs b/crates/services/consensus_module/src/block_verifier.rs index e7c931383e6..bd47ec5d3b2 100644 --- a/crates/services/consensus_module/src/block_verifier.rs +++ b/crates/services/consensus_module/src/block_verifier.rs @@ -44,7 +44,7 @@ impl Verifier { impl Verifier where V: AtomicView, - V::View: PoAVerifierDatabase, + V::LatestView: PoAVerifierDatabase, { /// Verifies **all** fields of the block based on used consensus to produce a block. /// @@ -65,7 +65,7 @@ where ) } Consensus::PoA(_) => { - let view = self.view_provider.latest_view(); + let view = self.view_provider.latest_view()?; fuel_core_poa::verifier::verify_block_fields(&view, block) } _ => Err(anyhow::anyhow!("Unsupported consensus: {:?}", consensus)), diff --git a/crates/services/executor/src/executor.rs b/crates/services/executor/src/executor.rs index 647e95e9469..fd5fe13de3a 100644 --- a/crates/services/executor/src/executor.rs +++ b/crates/services/executor/src/executor.rs @@ -229,6 +229,14 @@ where R: RelayerPort, D: KeyValueInspect, { + pub fn new(relayer: R, database: D, options: ExecutionOptions) -> Self { + Self { + relayer, + database, + options, + } + } + #[tracing::instrument(skip_all)] pub fn produce_without_commit( self, diff --git a/crates/services/importer/src/importer.rs b/crates/services/importer/src/importer.rs index 7bc451f16f0..850b5b90f30 100644 --- a/crates/services/importer/src/importer.rs +++ b/crates/services/importer/src/importer.rs @@ -3,6 +3,7 @@ use crate::{ BlockVerifier, DatabaseTransaction, ImporterDatabase, + Transactional, Validator, }, Config, @@ -177,7 +178,7 @@ impl Importer { impl Importer where - D: ImporterDatabase, + D: ImporterDatabase + Transactional, { /// The method commits the result of the block execution attaching the consensus data. /// It expects that the `UncommittedResult` contains the result of the block @@ -434,7 +435,7 @@ where impl Importer where - IDatabase: ImporterDatabase + 'static, + IDatabase: ImporterDatabase + Transactional + 'static, E: Validator + 'static, V: BlockVerifier + 'static, { diff --git a/crates/services/importer/src/ports.rs b/crates/services/importer/src/ports.rs index cd5cbe02c42..ee1952b335c 100644 --- a/crates/services/importer/src/ports.rs +++ b/crates/services/importer/src/ports.rs @@ -62,7 +62,7 @@ pub trait Transactional { } /// The alias port used by the block importer. -pub trait ImporterDatabase: Transactional + Send + Sync { +pub trait ImporterDatabase: Send + Sync { /// Returns the latest block height. fn latest_block_height(&self) -> StorageResult>; diff --git a/crates/services/p2p/src/service.rs b/crates/services/p2p/src/service.rs index f6a29e41579..34727f1c209 100644 --- a/crates/services/p2p/src/service.rs +++ b/crates/services/p2p/src/service.rs @@ -411,7 +411,7 @@ fn convert_peer_id(peer_id: &PeerId) -> anyhow::Result { impl RunnableService for UninitializedTask where V: AtomicView + 'static, - V::View: P2pDb, + V::LatestView: P2pDb, { const NAME: &'static str = "P2P"; @@ -437,7 +437,7 @@ where config, } = self; - let view = view_provider.latest_view(); + let view = view_provider.latest_view()?; let genesis = view.get_genesis()?; let config = config.init(genesis)?; let Config { @@ -492,7 +492,7 @@ impl RunnableTask for Task where P: TaskP2PService + 'static, V: AtomicView + 'static, - V::View: P2pDb, + V::LatestView: P2pDb, B: Broadcast + 'static, { async fn run(&mut self, watcher: &mut StateWatcher) -> anyhow::Result { @@ -578,7 +578,7 @@ where Some(FuelP2PEvent::InboundRequestMessage { request_message, request_id }) => { match request_message { RequestMessage::Transactions(range) => { - let view = self.view_provider.latest_view(); + let view = self.view_provider.latest_view()?; match view.get_transactions(range.clone()) { Ok(response) => { let _ = self.p2p_service.send_response_msg(request_id, ResponseMessage::Transactions(response)); @@ -599,7 +599,7 @@ where let response = None; let _ = self.p2p_service.send_response_msg(request_id, ResponseMessage::SealedHeaders(response)); } else { - let view = self.view_provider.latest_view(); + let view = self.view_provider.latest_view()?; match view.get_sealed_headers(range.clone()) { Ok(headers) => { let response = Some(headers); @@ -801,7 +801,7 @@ pub fn new_service( ) -> Service where V: AtomicView + 'static, - V::View: P2pDb, + V::LatestView: P2pDb, B: BlockHeightImporter, { let task = @@ -867,20 +867,10 @@ pub mod tests { struct FakeDb; impl AtomicView for FakeDb { - type View = Self; + type LatestView = Self; - type Height = BlockHeight; - - fn latest_height(&self) -> Option { - Some(BlockHeight::default()) - } - - fn view_at(&self, _: &BlockHeight) -> StorageResult { - unimplemented!() - } - - fn latest_view(&self) -> Self::View { - self.clone() + fn latest_view(&self) -> StorageResult { + Ok(self.clone()) } } @@ -992,20 +982,10 @@ pub mod tests { struct FakeDB; impl AtomicView for FakeDB { - type View = Self; - - type Height = BlockHeight; - - fn latest_height(&self) -> Option { - Some(BlockHeight::default()) - } - - fn view_at(&self, _: &BlockHeight) -> StorageResult { - unimplemented!() - } + type LatestView = Self; - fn latest_view(&self) -> Self::View { - self.clone() + fn latest_view(&self) -> StorageResult { + Ok(self.clone()) } } diff --git a/crates/services/producer/src/block_producer.rs b/crates/services/producer/src/block_producer.rs index d3f34c4add2..476b8685fc9 100644 --- a/crates/services/producer/src/block_producer.rs +++ b/crates/services/producer/src/block_producer.rs @@ -91,8 +91,8 @@ pub struct Producer Producer where - ViewProvider: AtomicView + 'static, - ViewProvider::View: BlockProducerDatabase, + ViewProvider: AtomicView + 'static, + ViewProvider::LatestView: BlockProducerDatabase, GasPriceProvider: GasPriceProviderConstraint, ConsensusProvider: ConsensusParametersProvider, { @@ -165,8 +165,8 @@ where impl Producer where - ViewProvider: AtomicView + 'static, - ViewProvider::View: BlockProducerDatabase, + ViewProvider: AtomicView + 'static, + ViewProvider::LatestView: BlockProducerDatabase, TxPool: ports::TxPool + 'static, Executor: ports::BlockProducer + 'static, GasPriceProvider: GasPriceProviderConstraint, @@ -188,8 +188,8 @@ where impl Producer where - ViewProvider: AtomicView + 'static, - ViewProvider::View: BlockProducerDatabase, + ViewProvider: AtomicView + 'static, + ViewProvider::LatestView: BlockProducerDatabase, Executor: ports::BlockProducer> + 'static, GasPriceProvider: GasPriceProviderConstraint, ConsensusProvider: ConsensusParametersProvider, @@ -209,8 +209,8 @@ where impl Producer where - ViewProvider: AtomicView + 'static, - ViewProvider::View: BlockProducerDatabase, + ViewProvider: AtomicView + 'static, + ViewProvider::LatestView: BlockProducerDatabase, Executor: ports::DryRunner + 'static, GasPriceProvider: GasPriceProviderConstraint, ConsensusProvider: ConsensusParametersProvider, @@ -226,9 +226,9 @@ where utxo_validation: Option, gas_price: Option, ) -> anyhow::Result> { + let view = self.view_provider.latest_view()?; let height = height.unwrap_or_else(|| { - self.view_provider - .latest_height() + view.latest_height() .unwrap_or_default() .succ() .expect("It is impossible to overflow the current block height") @@ -283,8 +283,8 @@ pub const NO_NEW_DA_HEIGHT_FOUND: &str = "No new da_height found"; impl Producer where - ViewProvider: AtomicView + 'static, - ViewProvider::View: BlockProducerDatabase, + ViewProvider: AtomicView + 'static, + ViewProvider::LatestView: BlockProducerDatabase, ConsensusProvider: ConsensusParametersProvider, { /// Create the header for a new block at the provided height @@ -356,7 +356,7 @@ where height: BlockHeight, block_time: Tai64, ) -> anyhow::Result { - let view = self.view_provider.latest_view(); + let view = self.view_provider.latest_view()?; let previous_block_info = self.previous_block_info(height, &view)?; let consensus_parameters_version = view.latest_consensus_parameters_version()?; let state_transition_bytecode_version = @@ -381,10 +381,11 @@ where fn previous_block_info( &self, height: BlockHeight, - view: &ViewProvider::View, + view: &ViewProvider::LatestView, ) -> anyhow::Result { let latest_height = self .view_provider + .latest_view()? .latest_height() .ok_or(Error::NoGenesisBlock)?; // block 0 is reserved for genesis diff --git a/crates/services/producer/src/mocks.rs b/crates/services/producer/src/mocks.rs index 3c4a86f9855..95fe741c34c 100644 --- a/crates/services/producer/src/mocks.rs +++ b/crates/services/producer/src/mocks.rs @@ -213,26 +213,20 @@ pub struct MockDb { } impl AtomicView for MockDb { - type View = Self; + type LatestView = Self; - type Height = BlockHeight; + fn latest_view(&self) -> StorageResult { + Ok(self.clone()) + } +} +impl BlockProducerDatabase for MockDb { fn latest_height(&self) -> Option { let blocks = self.blocks.lock().unwrap(); blocks.keys().max().cloned() } - fn view_at(&self, _: &BlockHeight) -> StorageResult { - Ok(self.latest_view()) - } - - fn latest_view(&self) -> Self::View { - self.clone() - } -} - -impl BlockProducerDatabase for MockDb { fn get_block(&self, height: &BlockHeight) -> StorageResult> { let blocks = self.blocks.lock().unwrap(); blocks diff --git a/crates/services/producer/src/ports.rs b/crates/services/producer/src/ports.rs index 66c4f77d235..a50b2ebe33c 100644 --- a/crates/services/producer/src/ports.rs +++ b/crates/services/producer/src/ports.rs @@ -29,6 +29,9 @@ use fuel_core_types::{ use std::borrow::Cow; pub trait BlockProducerDatabase: Send + Sync { + /// Returns the latest block height. + fn latest_height(&self) -> Option; + /// Gets the committed block at the `height`. fn get_block(&self, height: &BlockHeight) -> StorageResult>; diff --git a/crates/services/txpool/src/mock_db.rs b/crates/services/txpool/src/mock_db.rs index a7b8406b485..d6a4ff8d9c1 100644 --- a/crates/services/txpool/src/mock_db.rs +++ b/crates/services/txpool/src/mock_db.rs @@ -16,10 +16,7 @@ use fuel_core_types::{ ContractId, UtxoId, }, - fuel_types::{ - BlockHeight, - Nonce, - }, + fuel_types::Nonce, }; use std::{ collections::{ @@ -89,19 +86,9 @@ impl TxPoolDb for MockDb { pub struct MockDBProvider(pub MockDb); impl AtomicView for MockDBProvider { - type View = MockDb; - - type Height = BlockHeight; - - fn latest_height(&self) -> Option { - Some(BlockHeight::default()) - } - - fn view_at(&self, _: &BlockHeight) -> StorageResult { - Ok(self.latest_view()) - } + type LatestView = MockDb; - fn latest_view(&self) -> Self::View { - self.0.clone() + fn latest_view(&self) -> StorageResult { + Ok(self.0.clone()) } } diff --git a/crates/services/txpool/src/service.rs b/crates/services/txpool/src/service.rs index 565cad0525c..afa90ca6ba3 100644 --- a/crates/services/txpool/src/service.rs +++ b/crates/services/txpool/src/service.rs @@ -161,7 +161,7 @@ impl RunnableS for Task where P2P: PeerToPeer, - ViewProvider: AtomicView, + ViewProvider: AtomicView, View: TxPoolDb, GasPriceProvider: GasPriceProviderConstraint + Send + Sync, ConsensusProvider: ConsensusParametersProvider + Send + Sync, @@ -193,7 +193,7 @@ impl RunnableT for Task where P2P: PeerToPeer, - ViewProvider: AtomicView, + ViewProvider: AtomicView, View: TxPoolDb, GasPriceProvider: GasPriceProviderConstraint + Send + Sync, ConsensusProvider: ConsensusParametersProvider + Send + Sync, @@ -387,7 +387,7 @@ impl SharedState where P2P: PeerToPeer, - ViewProvider: AtomicView, + ViewProvider: AtomicView, View: TxPoolDb, GasPriceProvider: GasPriceProviderConstraint + Send + Sync, ConsensusProvider: ConsensusParametersProvider, @@ -503,7 +503,7 @@ where Importer: BlockImporter, P2P: PeerToPeer + 'static, ViewProvider: AtomicView, - ViewProvider::View: TxPoolDb, + ViewProvider::LatestView: TxPoolDb, GasPriceProvider: GasPriceProviderConstraint + Send + Sync, ConsensusProvider: ConsensusParametersProvider + Send + Sync, MP: MemoryPool + Send + Sync, diff --git a/crates/services/txpool/src/txpool.rs b/crates/services/txpool/src/txpool.rs index 736a6b17bcb..eab09da10ac 100644 --- a/crates/services/txpool/src/txpool.rs +++ b/crates/services/txpool/src/txpool.rs @@ -334,7 +334,7 @@ impl TxPool { impl TxPool where - ViewProvider: AtomicView, + ViewProvider: AtomicView, View: TxPoolDb, { #[cfg(test)] @@ -342,7 +342,7 @@ where &mut self, tx: Checked, ) -> Result { - let view = self.database.latest_view(); + let view = self.database.latest_view().unwrap(); self.insert_inner(tx, &view) } @@ -432,7 +432,10 @@ where // Check if that data is okay (witness match input/output, and if recovered signatures ara valid). // should be done before transaction comes to txpool, or before it enters RwLocked region. let mut res = Vec::new(); - let view = self.database.latest_view(); + let view = match self.database.latest_view() { + Ok(view) => view, + Err(e) => return vec![Err(Error::Other(e.to_string()))], + }; for tx in txs.into_iter() { res.push(self.insert_inner(tx, &view)); diff --git a/crates/services/upgradable-executor/src/executor.rs b/crates/services/upgradable-executor/src/executor.rs index a6065f18516..12acb9489c5 100644 --- a/crates/services/upgradable-executor/src/executor.rs +++ b/crates/services/upgradable-executor/src/executor.rs @@ -16,14 +16,17 @@ use fuel_core_storage::{ transactional::{ AtomicView, Changes, + HistoricalView, Modifiable, }, }; use fuel_core_types::{ blockchain::{ block::Block, - header::StateTransitionBytecodeVersion, - primitives::DaBlockHeight, + header::{ + StateTransitionBytecodeVersion, + LATEST_STATE_TRANSITION_VERSION, + }, }, fuel_tx::Transaction, fuel_types::BlockHeight, @@ -34,6 +37,7 @@ use fuel_core_types::{ ExecutionResult, Result as ExecutorResult, TransactionExecutionStatus, + ValidationResult, }, Uncommitted, }, @@ -54,10 +58,6 @@ use fuel_core_storage::{ use fuel_core_types::blockchain::block::PartialFuelBlock; #[cfg(any(test, feature = "test-helpers"))] use fuel_core_types::services::executor::UncommittedResult; -use fuel_core_types::{ - blockchain::header::LATEST_STATE_TRANSITION_VERSION, - services::executor::ValidationResult, -}; #[cfg(feature = "wasm-executor")] enum ExecutionStrategy { @@ -206,12 +206,13 @@ impl Executor { } } -impl Executor +impl Executor where - R: AtomicView, - R::View: RelayerPort + Send + Sync + 'static, - D: AtomicView + Modifiable, - D::View: KeyValueInspect + Send + Sync + 'static, + S: HistoricalView + Modifiable, + S::LatestView: KeyValueInspect + Send + Sync + 'static, + S::ViewAtHeight: KeyValueInspect + Send + Sync + 'static, + R: AtomicView, + R::LatestView: RelayerPort + Send + Sync + 'static, { #[cfg(any(test, feature = "test-helpers"))] /// Executes the block and commits the result of the execution into the inner `Database`. @@ -240,10 +241,11 @@ where #[cfg(any(test, feature = "test-helpers"))] impl Executor where - S: AtomicView, - S::View: KeyValueInspect + Send + Sync + 'static, - R: AtomicView, - R::View: RelayerPort + Send + Sync + 'static, + S: HistoricalView, + S::LatestView: KeyValueInspect + Send + Sync + 'static, + S::ViewAtHeight: KeyValueInspect + Send + Sync + 'static, + R: AtomicView, + R::LatestView: RelayerPort + Send + Sync + 'static, { /// Executes the block and returns the result of the execution with storage changes. pub fn produce_without_commit( @@ -287,10 +289,11 @@ where impl Executor where - S: AtomicView, - S::View: KeyValueInspect + Send + Sync + 'static, - R: AtomicView, - R::View: RelayerPort + Send + Sync + 'static, + S: HistoricalView, + S::LatestView: KeyValueInspect + Send + Sync + 'static, + S::ViewAtHeight: KeyValueInspect + Send + Sync + 'static, + R: AtomicView, + R::LatestView: RelayerPort + Send + Sync + 'static, { /// Produces the block and returns the result of the execution without committing the changes. pub fn produce_without_commit_with_source( @@ -479,13 +482,24 @@ where gas_price, }; - let storage = self.storage_view_provider.latest_view(); - let relayer = self.relayer_view_provider.latest_view(); + let previous_block_height = block.header_to_produce.height().pred(); + + let instance_without_input = + crate::instance::Instance::new(&self.engine).add_source(source)?; + + let instance_without_input = if let Some(previous_block_height) = + previous_block_height + { + let storage = self.storage_view_provider.view_at(&previous_block_height)?; + instance_without_input.add_storage(storage)? + } else { + let storage = self.storage_view_provider.latest_view()?; + instance_without_input.add_storage(storage)? + }; + + let relayer = self.relayer_view_provider.latest_view()?; + let instance_without_input = instance_without_input.add_relayer(relayer)?; - let instance_without_input = crate::instance::Instance::new(&self.engine) - .add_source(source)? - .add_storage(storage)? - .add_relayer(relayer)?; let instance = if dry_run { instance_without_input.add_dry_run_input_data(block, options)? } else { @@ -509,12 +523,20 @@ where self.trace_block_version_warning( block.header().state_transition_bytecode_version, ); - let storage = self.storage_view_provider.latest_view(); - let relayer = self.relayer_view_provider.latest_view(); + let previous_block_height = block.header().height().pred(); + + let instance = crate::instance::Instance::new(&self.engine).no_source()?; + + let instance = if let Some(previous_block_height) = previous_block_height { + let storage = self.storage_view_provider.view_at(&previous_block_height)?; + instance.add_storage(storage)? + } else { + let storage = self.storage_view_provider.latest_view()?; + instance.add_storage(storage)? + }; - let instance = crate::instance::Instance::new(&self.engine) - .no_source()? - .add_storage(storage)? + let relayer = self.relayer_view_provider.latest_view()?; + let instance = instance .add_relayer(relayer)? .add_validation_input_data(block, options)?; @@ -536,8 +558,18 @@ where where TxSource: TransactionsSource + Send + Sync + 'static, { - let instance = self.new_native_executor_instance(options); - instance.produce_without_commit(block, dry_run) + let previous_block_height = block.header_to_produce.height().pred(); + let relayer = self.relayer_view_provider.latest_view()?; + + if let Some(previous_block_height) = previous_block_height { + let database = self.storage_view_provider.view_at(&previous_block_height)?; + ExecutionInstance::new(relayer, database, options) + .produce_without_commit(block, dry_run) + } else { + let database = self.storage_view_provider.latest_view()?; + ExecutionInstance::new(relayer, database, options) + .produce_without_commit(block, dry_run) + } } fn native_validate_inner( @@ -545,21 +577,17 @@ where block: &Block, options: ExecutionOptions, ) -> ExecutorResult> { - let instance = self.new_native_executor_instance(options); - instance.validate_without_commit(block) - } + let previous_block_height = block.header().height().pred(); + let relayer = self.relayer_view_provider.latest_view()?; - fn new_native_executor_instance( - &self, - options: ExecutionOptions, - ) -> ExecutionInstance<::View, ::View> { - let relayer = self.relayer_view_provider.latest_view(); - let storage = self.storage_view_provider.latest_view(); - - ExecutionInstance { - relayer, - database: storage, - options, + if let Some(previous_block_height) = previous_block_height { + let database = self.storage_view_provider.view_at(&previous_block_height)?; + ExecutionInstance::new(relayer, database, options) + .validate_without_commit(block) + } else { + let database = self.storage_view_provider.latest_view()?; + ExecutionInstance::new(relayer, database, options) + .validate_without_commit(block) } } @@ -578,7 +606,7 @@ where } drop(guard); - let view = StructuredStorage::new(self.storage_view_provider.latest_view()); + let view = StructuredStorage::new(self.storage_view_provider.latest_view()?); let bytecode_root = *view .storage::() .get(&version)? @@ -637,7 +665,10 @@ mod test { PartialBlockHeader, StateTransitionBytecodeVersion, }, - primitives::Empty, + primitives::{ + DaBlockHeight, + Empty, + }, }, fuel_tx::{ AssetId, @@ -656,19 +687,23 @@ mod test { struct Storage(InMemoryStorage); impl AtomicView for Storage { - type View = InMemoryStorage; + type LatestView = InMemoryStorage; + + fn latest_view(&self) -> StorageResult { + Ok(self.0.clone()) + } + } + + impl HistoricalView for Storage { type Height = BlockHeight; + type ViewAtHeight = Self::LatestView; fn latest_height(&self) -> Option { None } - fn view_at(&self, _: &Self::Height) -> StorageResult { - unimplemented!() - } - - fn latest_view(&self) -> Self::View { - self.0.clone() + fn view_at(&self, _: &Self::Height) -> StorageResult { + self.latest_view() } } @@ -700,19 +735,10 @@ mod test { } impl AtomicView for DisabledRelayer { - type View = Self; - type Height = DaBlockHeight; - - fn latest_height(&self) -> Option { - None - } - - fn view_at(&self, _: &Self::Height) -> StorageResult { - unimplemented!() - } + type LatestView = Self; - fn latest_view(&self) -> Self::View { - *self + fn latest_view(&self) -> StorageResult { + Ok(*self) } } diff --git a/crates/storage/src/lib.rs b/crates/storage/src/lib.rs index 5bd81711ce9..6f3e6b9faca 100644 --- a/crates/storage/src/lib.rs +++ b/crates/storage/src/lib.rs @@ -68,6 +68,13 @@ pub enum Error { Other(anyhow::Error), } +#[cfg(feature = "test-helpers")] +impl PartialEq for Error { + fn eq(&self, other: &Self) -> bool { + self.to_string().eq(&other.to_string()) + } +} + impl From for anyhow::Error { fn from(error: Error) -> Self { anyhow::Error::msg(error) diff --git a/crates/storage/src/structured_storage.rs b/crates/storage/src/structured_storage.rs index 11fb9fa986a..8f0c83290d7 100644 --- a/crates/storage/src/structured_storage.rs +++ b/crates/storage/src/structured_storage.rs @@ -84,6 +84,11 @@ impl StructuredStorage { pub fn new(storage: S) -> Self { Self { inner: storage } } + + /// Returns the inner storage. + pub fn into_inner(self) -> S { + self.inner + } } impl AsRef for StructuredStorage { diff --git a/crates/storage/src/transactional.rs b/crates/storage/src/transactional.rs index f997488295e..7fc569f28e5 100644 --- a/crates/storage/src/transactional.rs +++ b/crates/storage/src/transactional.rs @@ -33,23 +33,30 @@ use crate::{ kv_store::KVItem, }; -/// Provides a view of the storage at the given height. -/// It guarantees to be atomic, meaning the view is immutable to outside modifications. +/// Provides an atomic view of the storage at the latest height at +/// the moment of view instantiation. All modifications to the storage +/// after this point will not affect the view. pub trait AtomicView: Send + Sync { - /// The type of the storage view. - type View; + /// The type of the latest storage view. + type LatestView; + /// Returns current the view of the storage. + fn latest_view(&self) -> StorageResult; +} + +/// Provides an atomic view of the storage at the given height. +/// The view is guaranteed to be unmodifiable for the given height. +pub trait HistoricalView: AtomicView { /// The type used by the storage to track the commitments at a specific height. type Height; + /// The type of the storage view at `Self::Height`. + type ViewAtHeight; - /// Returns the latest block height. + /// Returns the latest height. fn latest_height(&self) -> Option; /// Returns the view of the storage at the given `height`. - fn view_at(&self, height: &Self::Height) -> StorageResult; - - /// Returns the view of the storage for the latest block height. - fn latest_view(&self) -> Self::View; + fn view_at(&self, height: &Self::Height) -> StorageResult; } /// Storage transaction on top of the storage.