From 6e7c7bfae1e22ba0012343d0627c690c1c2af08b Mon Sep 17 00:00:00 2001 From: Green Baneling Date: Fri, 19 Jan 2024 13:14:13 -0500 Subject: [PATCH] Use `BlockHeight` as a primary key for the `FuelsBlock` table (#1587) Closes https://github.com/FuelLabs/fuel-core/issues/1580 --- CHANGELOG.md | 1 + crates/fuel-core/src/database.rs | 36 +++--- crates/fuel-core/src/database/block.rs | 116 +++++++++--------- crates/fuel-core/src/database/coin.rs | 6 +- crates/fuel-core/src/database/contracts.rs | 6 +- crates/fuel-core/src/database/message.rs | 14 +-- crates/fuel-core/src/database/sealed_block.rs | 56 +++------ crates/fuel-core/src/database/transactions.rs | 22 ++-- crates/fuel-core/src/graphql_api/database.rs | 25 ++-- crates/fuel-core/src/graphql_api/ports.rs | 19 +-- crates/fuel-core/src/query/block.rs | 46 +++---- crates/fuel-core/src/query/message.rs | 12 +- crates/fuel-core/src/query/message/test.rs | 35 ++++-- crates/fuel-core/src/schema/block.rs | 19 +-- crates/fuel-core/src/schema/dap.rs | 3 +- crates/fuel-core/src/schema/message.rs | 11 +- crates/fuel-core/src/schema/tx/types.rs | 7 +- .../src/service/adapters/block_importer.rs | 13 +- .../service/adapters/graphql_api/on_chain.rs | 33 ++--- crates/fuel-core/src/service/adapters/p2p.rs | 2 +- .../src/service/adapters/producer.rs | 3 +- crates/fuel-core/src/service/genesis.rs | 7 +- .../storage/src/structured_storage/blocks.rs | 4 +- .../src/structured_storage/sealed_block.rs | 6 +- crates/storage/src/tables.rs | 8 +- tests/tests/blocks.rs | 8 +- tests/tests/poa.rs | 7 +- 27 files changed, 265 insertions(+), 260 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80f96ab1272..201435abcaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Description of the upcoming release here. ### Changed - [#1591](https://github.com/FuelLabs/fuel-core/pull/1591): Simplify libp2p dependencies and not depend on all sub modules directly. +- [#1587](https://github.com/FuelLabs/fuel-core/pull/1587): Use `BlockHeight` as a primary key for the `FuelsBlock` table. - [#1585](https://github.com/FuelLabs/fuel-core/pull/1585): Let `NetworkBehaviour` macro generate `FuelBehaviorEvent` in p2p - [#1579](https://github.com/FuelLabs/fuel-core/pull/1579): The change extracts the off-chain-related logic from the executor and moves it to the GraphQL off-chain worker. It creates two new concepts - Off-chain and On-chain databases where the GraphQL worker has exclusive ownership of the database and may modify it without intersecting with the On-chain database. - [#1577](https://github.com/FuelLabs/fuel-core/pull/1577): Moved insertion of sealed blocks into the `BlockImporter` instead of the executor. diff --git a/crates/fuel-core/src/database.rs b/crates/fuel-core/src/database.rs index 8d4538b2d32..913bc445f16 100644 --- a/crates/fuel-core/src/database.rs +++ b/crates/fuel-core/src/database.rs @@ -13,7 +13,11 @@ use fuel_core_chain_config::{ }; use fuel_core_storage::{ blueprint::Blueprint, - codec::Decode, + codec::{ + Decode, + Encode, + Encoder, + }, iter::IterDirection, kv_store::{ BatchOperations, @@ -253,7 +257,7 @@ impl BatchOperations for DataSource { /// Read-only methods. impl Database { - fn iter_all( + pub(crate) fn iter_all( &self, direction: Option, ) -> impl Iterator> + '_ @@ -261,10 +265,10 @@ impl Database { M: Mappable + TableWithBlueprint, M::Blueprint: Blueprint, { - self.iter_all_filtered::, Vec>(None, None, direction) + self.iter_all_filtered::(None, None, direction) } - fn iter_all_by_prefix( + pub(crate) fn iter_all_by_prefix( &self, prefix: Option

, ) -> impl Iterator> + '_ @@ -273,40 +277,44 @@ impl Database { M::Blueprint: Blueprint, P: AsRef<[u8]>, { - self.iter_all_filtered::(prefix, None, None) + self.iter_all_filtered::(prefix, None, None) } - fn iter_all_by_start( + pub(crate) fn iter_all_by_start( &self, - start: Option, + start: Option<&M::Key>, direction: Option, ) -> impl Iterator> + '_ where M: Mappable + TableWithBlueprint, M::Blueprint: Blueprint, - S: AsRef<[u8]>, { - self.iter_all_filtered::(None, start, direction) + self.iter_all_filtered::(None, start, direction) } - fn iter_all_filtered( + pub(crate) fn iter_all_filtered( &self, prefix: Option

, - start: Option, + start: Option<&M::Key>, direction: Option, ) -> impl Iterator> + '_ where M: Mappable + TableWithBlueprint, M::Blueprint: Blueprint, P: AsRef<[u8]>, - S: AsRef<[u8]>, { + let encoder = start.map(|start| { + >::KeyCodec::encode(start) + }); + + let start = encoder.as_ref().map(|encoder| encoder.as_bytes()); + self.data .as_ref() .iter_all( M::column(), prefix.as_ref().map(|p| p.as_ref()), - start.as_ref().map(|s| s.as_ref()), + start.as_ref().map(|cow| cow.as_ref()), direction.unwrap_or_default(), ) .map(|val| { @@ -379,7 +387,7 @@ impl ChainConfigDb for Database { } fn get_block_height(&self) -> StorageResult { - Self::latest_height(self) + self.latest_height() } } diff --git a/crates/fuel-core/src/database/block.rs b/crates/fuel-core/src/database/block.rs index f270e581f6f..a2bc03b6a99 100644 --- a/crates/fuel-core/src/database/block.rs +++ b/crates/fuel-core/src/database/block.rs @@ -1,7 +1,6 @@ use crate::database::{ Column, Database, - Error as DatabaseError, }; use fuel_core_storage::{ blueprint::plain::Plain, @@ -49,21 +48,21 @@ use std::borrow::{ Cow, }; -/// The table of fuel block's secondary key - `BlockHeight`. -/// It links the `BlockHeight` to corresponding `BlockId`. +/// The table of fuel block's secondary key - `BlockId`. +/// It links the `BlockId` to corresponding `BlockHeight`. pub struct FuelBlockSecondaryKeyBlockHeights; impl Mappable for FuelBlockSecondaryKeyBlockHeights { - /// Secondary key - `BlockHeight`. - type Key = BlockHeight; - type OwnedKey = Self::Key; /// Primary key - `BlockId`. - type Value = BlockId; + type Key = BlockId; + type OwnedKey = Self::Key; + /// Secondary key - `BlockHeight`. + type Value = BlockHeight; type OwnedValue = Self::Value; } impl TableWithBlueprint for FuelBlockSecondaryKeyBlockHeights { - type Blueprint = Plain, Raw>; + type Blueprint = Plain>; fn column() -> Column { Column::FuelBlockSecondaryKeyBlockHeights @@ -80,11 +79,17 @@ fuel_core_storage::basic_storage_tests!( impl StorageInspect for Database { type Error = StorageError; - fn get(&self, key: &BlockId) -> Result>, Self::Error> { + fn get( + &self, + key: &::Key, + ) -> Result::OwnedValue>>, Self::Error> { self.data.storage::().get(key) } - fn contains_key(&self, key: &BlockId) -> Result { + fn contains_key( + &self, + key: &::Key, + ) -> Result { self.data.storage::().contains_key(key) } } @@ -92,17 +97,18 @@ impl StorageInspect for Database { impl StorageMutate for Database { fn insert( &mut self, - key: &BlockId, - value: &CompressedBlock, - ) -> Result, Self::Error> { + key: &::Key, + value: &::Value, + ) -> Result::OwnedValue>, Self::Error> { let prev = self .data .storage_as_mut::() .insert(key, value)?; let height = value.header().height(); + let block_id = value.id(); self.storage::() - .insert(height, key)?; + .insert(&block_id, key)?; // Get latest metadata entry let prev_metadata = self @@ -116,8 +122,7 @@ impl StorageMutate for Database { let mut tree: MerkleTree = MerkleTree::load(storage, prev_metadata.version) .map_err(|err| StorageError::Other(anyhow::anyhow!(err)))?; - let data = key.as_slice(); - tree.push(data)?; + tree.push(block_id.as_slice())?; // Generate new metadata for the updated tree let version = tree.leaves_count(); @@ -129,7 +134,10 @@ impl StorageMutate for Database { Ok(prev) } - fn remove(&mut self, key: &BlockId) -> Result, Self::Error> { + fn remove( + &mut self, + key: &::Key, + ) -> Result::OwnedValue>, Self::Error> { let prev: Option = self.data.storage_as_mut::().remove(key)?; @@ -137,7 +145,7 @@ impl StorageMutate for Database { let height = block.header().height(); let _ = self .storage::() - .remove(height); + .remove(&block.id()); // We can't clean up `MerkleTree`. // But if we plan to insert a new block, it will override old values in the // `FuelBlockMerkleData` table. @@ -150,68 +158,56 @@ impl StorageMutate for Database { impl Database { pub fn latest_height(&self) -> StorageResult { - self.ids_of_latest_block()? - .map(|(height, _)| height) - .ok_or(not_found!("BlockHeight")) + let pair = self + .iter_all::(Some(IterDirection::Reverse)) + .next() + .transpose()?; + + let (block_height, _) = pair.ok_or(not_found!("BlockHeight"))?; + + Ok(block_height) + } + + pub fn latest_compressed_block(&self) -> StorageResult> { + let pair = self + .iter_all::(Some(IterDirection::Reverse)) + .next() + .transpose()?; + + Ok(pair.map(|(_, compressed_block)| compressed_block)) } /// Get the current block at the head of the chain. - pub fn get_current_block(&self) -> StorageResult>> { - let block_ids = self.ids_of_latest_block()?; - match block_ids { - Some((_, id)) => Ok(StorageAsRef::storage::(self).get(&id)?), - None => Ok(None), - } + pub fn get_current_block(&self) -> StorageResult> { + self.latest_compressed_block() } pub fn block_time(&self, height: &BlockHeight) -> StorageResult { - let id = self.get_block_id(height)?.unwrap_or_default(); let block = self .storage::() - .get(&id)? + .get(height)? .ok_or(not_found!(FuelBlocks))?; Ok(block.header().time().to_owned()) } pub fn get_block_id(&self, height: &BlockHeight) -> StorageResult> { - self.storage::() + self.storage::() .get(height) - .map(|v| v.map(|v| v.into_owned())) + .map(|v| v.map(|v| v.id())) } - pub fn all_block_ids( - &self, - start: Option, - direction: IterDirection, - ) -> impl Iterator> + '_ { - let start = start.map(|b| b.to_bytes()); - self.iter_all_by_start::( - start, - Some(direction), - ) - } - - pub fn ids_of_genesis_block(&self) -> StorageResult<(BlockHeight, BlockId)> { - self.iter_all::(Some(IterDirection::Forward)) - .next() - .ok_or(DatabaseError::ChainUninitialized)? - } - - pub fn ids_of_latest_block(&self) -> StorageResult> { - let ids = self - .iter_all::(Some(IterDirection::Reverse)) - .next() - .transpose()?; - - Ok(ids) + pub fn get_block_height(&self, id: &BlockId) -> StorageResult> { + self.storage::() + .get(id) + .map(|v| v.map(|v| v.into_owned())) } /// Retrieve the full block and all associated transactions pub(crate) fn get_full_block( &self, - block_id: &BlockId, + height: &BlockHeight, ) -> StorageResult> { - let db_block = self.storage::().get(block_id)?; + let db_block = self.storage::().get(height)?; if let Some(block) = db_block { // fetch all the transactions // TODO: optimize with multi-key get @@ -334,7 +330,7 @@ mod tests { for block in &blocks { StorageMutate::::insert( &mut database, - &block.id(), + block.header().height(), &block.compress(&ChainId::default()), ) .unwrap(); @@ -398,7 +394,7 @@ mod tests { for block in &blocks { StorageMutate::::insert( database, - &block.id(), + block.header().height(), &block.compress(&ChainId::default()), ) .unwrap(); diff --git a/crates/fuel-core/src/database/coin.rs b/crates/fuel-core/src/database/coin.rs index d1979c86ff0..ad7dfc15602 100644 --- a/crates/fuel-core/src/database/coin.rs +++ b/crates/fuel-core/src/database/coin.rs @@ -126,9 +126,9 @@ impl Database { start_coin: Option, direction: Option, ) -> impl Iterator> + '_ { - self.iter_all_filtered::( - Some(*owner), - start_coin.map(|b| owner_coin_id_key(owner, &b)), + let start_coin = start_coin.map(|b| owner_coin_id_key(owner, &b)); + self.iter_all_filtered::( + Some(*owner), start_coin.as_ref(), direction, ) // Safety: key is always 64 bytes diff --git a/crates/fuel-core/src/database/contracts.rs b/crates/fuel-core/src/database/contracts.rs index ead374f4653..2dd4418ea51 100644 --- a/crates/fuel-core/src/database/contracts.rs +++ b/crates/fuel-core/src/database/contracts.rs @@ -93,9 +93,11 @@ impl Database { start_asset: Option, direction: Option, ) -> impl Iterator> + '_ { - self.iter_all_filtered::( + let start_asset = + start_asset.map(|asset| ContractsAssetKey::new(&contract, &asset)); + self.iter_all_filtered::( Some(contract), - start_asset.map(|asset_id| ContractsAssetKey::new(&contract, &asset_id)), + start_asset.as_ref(), direction, ) .map(|res| res.map(|(key, balance)| (*key.asset_id(), balance))) diff --git a/crates/fuel-core/src/database/message.rs b/crates/fuel-core/src/database/message.rs index 96ed1984479..21bdcac862e 100644 --- a/crates/fuel-core/src/database/message.rs +++ b/crates/fuel-core/src/database/message.rs @@ -32,10 +32,7 @@ use fuel_core_types::{ Nonce, }, }; -use std::{ - borrow::Cow, - ops::Deref, -}; +use std::borrow::Cow; fuel_core_types::fuel_vm::double_key!(OwnedMessageKey, Address, address, Nonce, nonce); @@ -120,9 +117,11 @@ impl Database { start_message_id: Option, direction: Option, ) -> impl Iterator> + '_ { - self.iter_all_filtered::( + let start_message_id = + start_message_id.map(|msg_id| OwnedMessageKey::new(owner, &msg_id)); + self.iter_all_filtered::( Some(*owner), - start_message_id.map(|msg_id| OwnedMessageKey::new(owner, &msg_id)), + start_message_id.as_ref(), direction, ) .map(|res| res.map(|(key, _)| *key.nonce())) @@ -133,8 +132,7 @@ impl Database { start: Option, direction: Option, ) -> impl Iterator> + '_ { - let start = start.map(|v| v.deref().to_vec()); - self.iter_all_by_start::(start, direction) + self.iter_all_by_start::(start.as_ref(), direction) .map(|res| res.map(|(_, message)| message)) } diff --git a/crates/fuel-core/src/database/sealed_block.rs b/crates/fuel-core/src/database/sealed_block.rs index a1cd34fa668..c7fec5f5d3e 100644 --- a/crates/fuel-core/src/database/sealed_block.rs +++ b/crates/fuel-core/src/database/sealed_block.rs @@ -1,5 +1,6 @@ use crate::database::Database; use fuel_core_storage::{ + iter::IterDirection, not_found, tables::{ FuelBlocks, @@ -15,7 +16,6 @@ use fuel_core_types::{ Genesis, Sealed, }, - primitives::BlockId, SealedBlock, SealedBlockHeader, }, @@ -25,14 +25,15 @@ use fuel_core_types::{ use std::ops::Range; impl Database { - pub fn get_sealed_block_by_id( + /// Returns `SealedBlock` by `height`. + /// Reusable across different trait implementations + pub fn get_sealed_block_by_height( &self, - block_id: &BlockId, + height: &BlockHeight, ) -> StorageResult> { // combine the block and consensus metadata into a sealed fuel block type - - let block = self.get_full_block(block_id)?; - let consensus = self.storage::().get(block_id)?; + let block = self.get_full_block(height)?; + let consensus = self.storage::().get(height)?; if let (Some(block), Some(consensus)) = (block, consensus) { let sealed_block = SealedBlock { @@ -46,51 +47,26 @@ impl Database { } } - /// Returns `SealedBlock` by `height`. - /// Reusable across different trait implementations - pub fn get_sealed_block_by_height( - &self, - height: &BlockHeight, - ) -> StorageResult> { - let block_id = match self.get_block_id(height)? { - Some(i) => i, - None => return Ok(None), - }; - self.get_sealed_block_by_id(&block_id) - } - pub fn get_genesis(&self) -> StorageResult { - let (_, genesis_block_id) = self.ids_of_genesis_block()?; - let consensus = self - .storage::() - .get(&genesis_block_id)? - .map(|c| c.into_owned()); + let pair = self + .iter_all::(Some(IterDirection::Forward)) + .next() + .transpose()?; - if let Some(Consensus::Genesis(genesis)) = consensus { + if let Some((_, Consensus::Genesis(genesis))) = pair { Ok(genesis) } else { Err(not_found!(SealedBlockConsensus)) } } - pub fn get_sealed_block_header_by_height( - &self, - height: &BlockHeight, - ) -> StorageResult> { - let block_id = match self.get_block_id(height)? { - Some(i) => i, - None => return Ok(None), - }; - self.get_sealed_block_header(&block_id) - } - pub fn get_sealed_block_headers( &self, block_height_range: Range, ) -> StorageResult> { let headers = block_height_range .map(BlockHeight::from) - .map(|height| self.get_sealed_block_header_by_height(&height)) + .map(|height| self.get_sealed_block_header(&height)) .collect::>>()? .into_iter() .flatten() @@ -100,10 +76,10 @@ impl Database { pub fn get_sealed_block_header( &self, - block_id: &BlockId, + height: &BlockHeight, ) -> StorageResult> { - let header = self.storage::().get(block_id)?; - let consensus = self.storage::().get(block_id)?; + let header = self.storage::().get(height)?; + let consensus = self.storage::().get(height)?; if let (Some(header), Some(consensus)) = (header, consensus) { let sealed_block = SealedBlockHeader { diff --git a/crates/fuel-core/src/database/transactions.rs b/crates/fuel-core/src/database/transactions.rs index c7ec700f62f..2f977e48488 100644 --- a/crates/fuel-core/src/database/transactions.rs +++ b/crates/fuel-core/src/database/transactions.rs @@ -104,8 +104,7 @@ impl Database { start: Option<&Bytes32>, direction: Option, ) -> impl Iterator> + '_ { - let start = start.map(|b| b.as_ref().to_vec()); - self.iter_all_by_start::(start, direction) + self.iter_all_by_start::(start, direction) .map(|res| res.map(|(_, tx)| tx)) } @@ -119,14 +118,17 @@ impl Database { start: Option, direction: Option, ) -> impl Iterator> + '_ { - let start = start - .map(|cursor| owned_tx_index_key(&owner, cursor.block_height, cursor.tx_idx)); - self.iter_all_filtered::(Some(owner), start, direction) - .map(|res| { - res.map(|(key, tx_id)| { - (TxPointer::new(key.block_height, key.tx_idx), tx_id) - }) - }) + let start = start.map(|cursor| { + OwnedTransactionIndexKey::new(&owner, cursor.block_height, cursor.tx_idx) + }); + self.iter_all_filtered::( + Some(owner), + start.as_ref(), + direction, + ) + .map(|res| { + res.map(|(key, tx_id)| (TxPointer::new(key.block_height, key.tx_idx), tx_id)) + }) } pub fn record_tx_id_owner( diff --git a/crates/fuel-core/src/graphql_api/database.rs b/crates/fuel-core/src/graphql_api/database.rs index feb9a638c18..eb0a3c00f93 100644 --- a/crates/fuel-core/src/graphql_api/database.rs +++ b/crates/fuel-core/src/graphql_api/database.rs @@ -24,9 +24,12 @@ use fuel_core_txpool::types::{ TxId, }; use fuel_core_types::{ - blockchain::primitives::{ - BlockId, - DaBlockHeight, + blockchain::{ + block::CompressedBlock, + primitives::{ + BlockId, + DaBlockHeight, + }, }, entities::message::{ MerkleProof, @@ -97,20 +100,20 @@ pub struct ReadView { } impl DatabaseBlocks for ReadView { - fn block_id(&self, height: &BlockHeight) -> StorageResult { - self.on_chain.block_id(height) + fn block_height(&self, block_id: &BlockId) -> StorageResult { + self.on_chain.block_height(block_id) } - fn blocks_ids( + fn blocks( &self, - start: Option, + height: Option, direction: IterDirection, - ) -> BoxedIter<'_, StorageResult<(BlockHeight, BlockId)>> { - self.on_chain.blocks_ids(start, direction) + ) -> BoxedIter<'_, StorageResult> { + self.on_chain.blocks(height, direction) } - fn ids_of_latest_block(&self) -> StorageResult<(BlockHeight, BlockId)> { - self.on_chain.ids_of_latest_block() + fn latest_height(&self) -> StorageResult { + self.on_chain.latest_height() } } diff --git a/crates/fuel-core/src/graphql_api/ports.rs b/crates/fuel-core/src/graphql_api/ports.rs index 44ff62b79b3..3e63781a3af 100644 --- a/crates/fuel-core/src/graphql_api/ports.rs +++ b/crates/fuel-core/src/graphql_api/ports.rs @@ -22,9 +22,12 @@ use fuel_core_storage::{ }; use fuel_core_txpool::service::TxStatusMessage; use fuel_core_types::{ - blockchain::primitives::{ - BlockId, - DaBlockHeight, + blockchain::{ + block::CompressedBlock, + primitives::{ + BlockId, + DaBlockHeight, + }, }, entities::message::{ MerkleProof, @@ -102,15 +105,15 @@ pub trait DatabaseBlocks: StorageInspect + StorageInspect { - fn block_id(&self, height: &BlockHeight) -> StorageResult; + fn block_height(&self, block_id: &BlockId) -> StorageResult; - fn blocks_ids( + fn blocks( &self, - start: Option, + height: Option, direction: IterDirection, - ) -> BoxedIter<'_, StorageResult<(BlockHeight, BlockId)>>; + ) -> BoxedIter<'_, StorageResult>; - fn ids_of_latest_block(&self) -> StorageResult<(BlockHeight, BlockId)>; + fn latest_height(&self) -> StorageResult; } /// Trait that specifies all the getters required for messages. diff --git a/crates/fuel-core/src/query/block.rs b/crates/fuel-core/src/query/block.rs index 8aeed56f76d..2d7edbd0b3f 100644 --- a/crates/fuel-core/src/query/block.rs +++ b/crates/fuel-core/src/query/block.rs @@ -2,7 +2,6 @@ use crate::fuel_core_graphql_api::ports::OnChainDatabase; use fuel_core_storage::{ iter::{ BoxedIter, - IntoBoxedIter, IterDirection, }, not_found, @@ -23,11 +22,13 @@ use fuel_core_types::{ }; pub trait SimpleBlockData: Send + Sync { - fn block(&self, id: &BlockId) -> StorageResult; + fn block(&self, id: &BlockHeight) -> StorageResult; + + fn block_by_id(&self, id: &BlockId) -> StorageResult; } impl SimpleBlockData for D { - fn block(&self, id: &BlockId) -> StorageResult { + fn block(&self, id: &BlockHeight) -> StorageResult { let block = self .storage::() .get(id)? @@ -36,60 +37,45 @@ impl SimpleBlockData for D { Ok(block) } + + fn block_by_id(&self, id: &BlockId) -> StorageResult { + let height = self.block_height(id)?; + self.block(&height) + } } pub trait BlockQueryData: Send + Sync + SimpleBlockData { - fn block_id(&self, height: &BlockHeight) -> StorageResult; - - fn latest_block_id(&self) -> StorageResult; - fn latest_block_height(&self) -> StorageResult; fn latest_block(&self) -> StorageResult; fn compressed_blocks( &self, - start: Option, + height: Option, direction: IterDirection, ) -> BoxedIter>; - fn consensus(&self, id: &BlockId) -> StorageResult; + fn consensus(&self, id: &BlockHeight) -> StorageResult; } impl BlockQueryData for D { - fn block_id(&self, height: &BlockHeight) -> StorageResult { - self.block_id(height) - } - - fn latest_block_id(&self) -> StorageResult { - self.ids_of_latest_block().map(|(_, id)| id) - } - fn latest_block_height(&self) -> StorageResult { - self.ids_of_latest_block().map(|(height, _)| height) + self.latest_height() } fn latest_block(&self) -> StorageResult { - self.latest_block_id().and_then(|id| self.block(&id)) + self.block(&self.latest_block_height()?) } fn compressed_blocks( &self, - start: Option, + height: Option, direction: IterDirection, ) -> BoxedIter> { - self.blocks_ids(start.map(Into::into), direction) - .map(|result| { - result.and_then(|(_, id)| { - let block = self.block(&id)?; - - Ok(block) - }) - }) - .into_boxed() + self.blocks(height, direction) } - fn consensus(&self, id: &BlockId) -> StorageResult { + fn consensus(&self, id: &BlockHeight) -> StorageResult { self.storage::() .get(id) .map(|c| c.map(|c| c.into_owned()))? diff --git a/crates/fuel-core/src/query/message.rs b/crates/fuel-core/src/query/message.rs index 334c24dc0d7..93b96e47380 100644 --- a/crates/fuel-core/src/query/message.rs +++ b/crates/fuel-core/src/query/message.rs @@ -27,10 +27,7 @@ use fuel_core_storage::{ StorageAsRef, }; use fuel_core_types::{ - blockchain::{ - block::CompressedBlock, - primitives::BlockId, - }, + blockchain::block::CompressedBlock, entities::message::{ MerkleProof, Message, @@ -45,6 +42,7 @@ use fuel_core_types::{ }, fuel_types::{ Address, + BlockHeight, Bytes32, MessageId, Nonce, @@ -147,7 +145,7 @@ pub fn message_proof( database: &T, transaction_id: Bytes32, desired_nonce: Nonce, - commit_block_id: BlockId, + commit_block_height: BlockHeight, ) -> StorageResult> { // Check if the receipts for this transaction actually contain this message id or exit. let receipt = database @@ -185,7 +183,7 @@ pub fn message_proof( // Get the message fuel block header. let (message_block_header, message_block_txs) = match database - .block(&message_block_id) + .block_by_id(&message_block_id) .into_api_result::()? { Some(t) => t.into_inner(), @@ -202,7 +200,7 @@ pub fn message_proof( // Get the commit fuel block header. let commit_block_header = match database - .block(&commit_block_id) + .block(&commit_block_height) .into_api_result::()? { Some(t) => t.into_inner().0, diff --git a/crates/fuel-core/src/query/message/test.rs b/crates/fuel-core/src/query/message/test.rs index e8ca628066f..aa8415cfa35 100644 --- a/crates/fuel-core/src/query/message/test.rs +++ b/crates/fuel-core/src/query/message/test.rs @@ -1,10 +1,13 @@ use std::ops::Deref; use fuel_core_types::{ - blockchain::header::{ - ApplicationHeader, - ConsensusHeader, - PartialBlockHeader, + blockchain::{ + header::{ + ApplicationHeader, + ConsensusHeader, + PartialBlockHeader, + }, + primitives::BlockId, }, entities::message::MerkleProof, fuel_tx::{ @@ -59,7 +62,8 @@ fn receipt(i: Option) -> Receipt { mockall::mock! { pub ProofDataStorage {} impl SimpleBlockData for ProofDataStorage { - fn block(&self, block_id: &BlockId) -> StorageResult; + fn block(&self, height: &BlockHeight) -> StorageResult; + fn block_by_id(&self, id: &BlockId) -> StorageResult; } impl DatabaseMessageProof for ProofDataStorage { @@ -182,16 +186,25 @@ async fn can_build_message_proof() { }) }); - data.expect_block().times(2).returning({ + data.expect_block().times(1).returning({ let commit_block = commit_block.clone(); + move |block_height| { + let block = if commit_block.header().height() == block_height { + commit_block.clone() + } else { + panic!("Shouldn't request any other block") + }; + Ok(block) + } + }); + + data.expect_block_by_id().times(1).returning({ let message_block = message_block.clone(); move |block_id| { - let block = if &commit_block.id() == block_id { - commit_block.clone() - } else if &message_block.id() == block_id { + let block = if &message_block.id() == block_id { message_block.clone() } else { - panic!("Should request any other block") + panic!("Shouldn't request any other block") }; Ok(block) } @@ -203,7 +216,7 @@ async fn can_build_message_proof() { data.deref(), transaction_id, nonce.to_owned(), - commit_block.id(), + *commit_block.header().height(), ) .unwrap() .unwrap(); diff --git a/crates/fuel-core/src/schema/block.rs b/crates/fuel-core/src/schema/block.rs index a092600c071..41c3f75b92f 100644 --- a/crates/fuel-core/src/schema/block.rs +++ b/crates/fuel-core/src/schema/block.rs @@ -6,6 +6,7 @@ use crate::{ fuel_core_graphql_api::{ api_service::ConsensusModule, database::ReadView, + ports::DatabaseBlocks, Config as GraphQLConfig, IntoApiResult, }, @@ -95,8 +96,8 @@ impl Block { async fn consensus(&self, ctx: &Context<'_>) -> async_graphql::Result { let query: &ReadView = ctx.data_unchecked(); - let id = self.0.header().id(); - let consensus = query.consensus(&id)?; + let height = self.0.header().height(); + let consensus = query.consensus(height)?; Ok(consensus.into()) } @@ -191,23 +192,25 @@ impl BlockQuery { #[graphql(desc = "Height of the block")] height: Option, ) -> async_graphql::Result> { let query: &ReadView = ctx.data_unchecked(); - let id = match (id, height) { + let height = match (id, height) { (Some(_), Some(_)) => { return Err(async_graphql::Error::new( "Can't provide both an id and a height", )) } - (Some(id), None) => Ok(id.0.into()), + (Some(id), None) => query.block_height(&id.0.into()), (None, Some(height)) => { let height: u32 = height.into(); - query.block_id(&height.into()) + Ok(height.into()) } (None, None) => { return Err(async_graphql::Error::new("Missing either id or height")) } }; - id.and_then(|id| query.block(&id)).into_api_result() + height + .and_then(|height| query.block(&height)) + .into_api_result() } async fn blocks( @@ -261,14 +264,14 @@ impl HeaderQuery { fn blocks_query( query: &ReadView, - start: Option, + height: Option, direction: IterDirection, ) -> BoxedIter> where T: async_graphql::OutputType, T: From, { - let blocks = query.compressed_blocks(start, direction).map(|result| { + let blocks = query.compressed_blocks(height, direction).map(|result| { result.map(|block| ((*block.header().height()).into(), block.into())) }); diff --git a/crates/fuel-core/src/schema/dap.rs b/crates/fuel-core/src/schema/dap.rs index 8283336e640..832d92a1339 100644 --- a/crates/fuel-core/src/schema/dap.rs +++ b/crates/fuel-core/src/schema/dap.rs @@ -159,8 +159,7 @@ impl ConcreteStorage { fn vm_database(storage: &DatabaseTransaction) -> anyhow::Result> { let block = storage .get_current_block()? - .ok_or(not_found!("Block for VMDatabase"))? - .into_owned(); + .ok_or(not_found!("Block for VMDatabase"))?; let vm_database = VmStorage::new( storage.as_ref().clone(), diff --git a/crates/fuel-core/src/schema/message.rs b/crates/fuel-core/src/schema/message.rs index dfc17606864..e77ec7af8c1 100644 --- a/crates/fuel-core/src/schema/message.rs +++ b/crates/fuel-core/src/schema/message.rs @@ -116,11 +116,12 @@ impl MessageQuery { commit_block_height: Option, ) -> async_graphql::Result> { let query: &ReadView = ctx.data_unchecked(); - let block_id = match (commit_block_id, commit_block_height) { - (Some(commit_block_id), None) => commit_block_id.0.into(), + let height = match (commit_block_id, commit_block_height) { + (Some(commit_block_id), None) => { + query.block_height(&commit_block_id.0.into())? + }, (None, Some(commit_block_height)) => { - let block_height = commit_block_height.0.into(); - query.block_id(&block_height)? + commit_block_height.0.into() } _ => Err(anyhow::anyhow!( "Either `commit_block_id` or `commit_block_height` must be provided exclusively" @@ -131,7 +132,7 @@ impl MessageQuery { query, transaction_id.into(), nonce.into(), - block_id, + height, )? .map(MessageProof)) } diff --git a/crates/fuel-core/src/schema/tx/types.rs b/crates/fuel-core/src/schema/tx/types.rs index fcd0e110ff2..efd58aeeaec 100644 --- a/crates/fuel-core/src/schema/tx/types.rs +++ b/crates/fuel-core/src/schema/tx/types.rs @@ -7,6 +7,7 @@ use crate::{ fuel_core_graphql_api::{ api_service::TxPool, database::ReadView, + ports::DatabaseBlocks, Config, IntoApiResult, }, @@ -159,7 +160,8 @@ impl SuccessStatus { async fn block(&self, ctx: &Context<'_>) -> async_graphql::Result { let query: &ReadView = ctx.data_unchecked(); - let block = query.block(&self.block_id)?; + let height = query.block_height(&self.block_id)?; + let block = query.block(&height)?; Ok(block.into()) } @@ -200,7 +202,8 @@ impl FailureStatus { async fn block(&self, ctx: &Context<'_>) -> async_graphql::Result { let query: &ReadView = ctx.data_unchecked(); - let block = query.block(&self.block_id)?; + let height = query.block_height(&self.block_id)?; + let block = query.block(&height)?; Ok(block.into()) } diff --git a/crates/fuel-core/src/service/adapters/block_importer.rs b/crates/fuel-core/src/service/adapters/block_importer.rs index 7fdfb2c3035..f1ecd9bd7e9 100644 --- a/crates/fuel-core/src/service/adapters/block_importer.rs +++ b/crates/fuel-core/src/service/adapters/block_importer.rs @@ -18,6 +18,7 @@ use fuel_core_importer::{ }; use fuel_core_poa::ports::RelayerPort; use fuel_core_storage::{ + iter::IterDirection, tables::{ FuelBlocks, SealedBlockConsensus, @@ -117,7 +118,11 @@ impl RelayerPort for MaybeRelayerAdapter { impl ImporterDatabase for Database { fn latest_block_height(&self) -> StorageResult> { - Ok(self.ids_of_latest_block()?.map(|(height, _)| height)) + Ok(self + .iter_all::(Some(IterDirection::Reverse)) + .next() + .transpose()? + .map(|(height, _)| height)) } fn increase_tx_count(&self, new_txs_count: u64) -> StorageResult { @@ -131,14 +136,14 @@ impl ExecutorDatabase for Database { chain_id: &ChainId, block: &SealedBlock, ) -> StorageResult { - let block_id = block.entity.id(); + let height = block.entity.header().height(); let mut found = self .storage::() - .insert(&block_id, &block.entity.compress(chain_id))? + .insert(height, &block.entity.compress(chain_id))? .is_some(); found |= self .storage::() - .insert(&block_id, &block.consensus)? + .insert(height, &block.consensus)? .is_some(); // TODO: Use `batch_insert` from https://github.com/FuelLabs/fuel-core/pull/1576 diff --git a/crates/fuel-core/src/service/adapters/graphql_api/on_chain.rs b/crates/fuel-core/src/service/adapters/graphql_api/on_chain.rs index dd9c9937ffa..d09f045cfb0 100644 --- a/crates/fuel-core/src/service/adapters/graphql_api/on_chain.rs +++ b/crates/fuel-core/src/service/adapters/graphql_api/on_chain.rs @@ -11,6 +11,7 @@ use crate::{ }, }, }; +use fuel_core_importer::ports::ImporterDatabase; use fuel_core_storage::{ iter::{ BoxedIter, @@ -18,15 +19,19 @@ use fuel_core_storage::{ IterDirection, }, not_found, + tables::FuelBlocks, transactional::AtomicView, Error as StorageError, Result as StorageResult, }; use fuel_core_txpool::types::ContractId; use fuel_core_types::{ - blockchain::primitives::{ - BlockId, - DaBlockHeight, + blockchain::{ + block::CompressedBlock, + primitives::{ + BlockId, + DaBlockHeight, + }, }, entities::message::Message, fuel_tx::AssetId, @@ -39,25 +44,25 @@ use fuel_core_types::{ use std::sync::Arc; impl DatabaseBlocks for Database { - fn block_id(&self, height: &BlockHeight) -> StorageResult { - self.get_block_id(height) - .and_then(|height| height.ok_or(not_found!("BlockId"))) + fn block_height(&self, id: &BlockId) -> StorageResult { + self.get_block_height(id) + .and_then(|height| height.ok_or(not_found!("BlockHeight"))) } - fn blocks_ids( + fn blocks( &self, - start: Option, + height: Option, direction: IterDirection, - ) -> BoxedIter<'_, StorageResult<(BlockHeight, BlockId)>> { - self.all_block_ids(start, direction) - .map(|result| result.map_err(StorageError::from)) + ) -> BoxedIter<'_, StorageResult> { + self.iter_all_by_start::(height.as_ref(), Some(direction)) + .map(|result| result.map(|(_, block)| block)) .into_boxed() } - fn ids_of_latest_block(&self) -> StorageResult<(BlockHeight, BlockId)> { - self.ids_of_latest_block() + fn latest_height(&self) -> StorageResult { + self.latest_block_height() .transpose() - .ok_or(not_found!("BlockId"))? + .ok_or(not_found!("BlockHeight"))? } } diff --git a/crates/fuel-core/src/service/adapters/p2p.rs b/crates/fuel-core/src/service/adapters/p2p.rs index aa3e0766d70..35dbac0f918 100644 --- a/crates/fuel-core/src/service/adapters/p2p.rs +++ b/crates/fuel-core/src/service/adapters/p2p.rs @@ -28,7 +28,7 @@ impl P2pDb for Database { &self, height: &BlockHeight, ) -> StorageResult> { - self.get_sealed_block_header_by_height(height) + self.get_sealed_block_header(height) } fn get_sealed_headers( diff --git a/crates/fuel-core/src/service/adapters/producer.rs b/crates/fuel-core/src/service/adapters/producer.rs index f966c48e337..5e5845287ef 100644 --- a/crates/fuel-core/src/service/adapters/producer.rs +++ b/crates/fuel-core/src/service/adapters/producer.rs @@ -135,9 +135,8 @@ impl fuel_core_producer::ports::Relayer for MaybeRelayerAdapter { impl fuel_core_producer::ports::BlockProducerDatabase for Database { fn get_block(&self, height: &BlockHeight) -> StorageResult> { - let id = self.get_block_id(height)?.ok_or(not_found!("BlockId"))?; self.storage::() - .get(&id)? + .get(height)? .ok_or(not_found!(FuelBlocks)) } diff --git a/crates/fuel-core/src/service/genesis.rs b/crates/fuel-core/src/service/genesis.rs index 9942df0a810..022a587e2da 100644 --- a/crates/fuel-core/src/service/genesis.rs +++ b/crates/fuel-core/src/service/genesis.rs @@ -19,6 +19,7 @@ use fuel_core_storage::{ Messages, }, transactional::Transactional, + IsNotFound, MerkleRoot, StorageAsMut, }; @@ -66,8 +67,10 @@ pub fn maybe_initialize_state( database: &Database, ) -> anyhow::Result<()> { // check if chain is initialized - if database.ids_of_latest_block()?.is_none() { - import_genesis_block(config, database)?; + if let Err(err) = database.get_genesis() { + if err.is_not_found() { + import_genesis_block(config, database)?; + } } Ok(()) diff --git a/crates/storage/src/structured_storage/blocks.rs b/crates/storage/src/structured_storage/blocks.rs index f31cbef5800..22f033c688e 100644 --- a/crates/storage/src/structured_storage/blocks.rs +++ b/crates/storage/src/structured_storage/blocks.rs @@ -4,7 +4,7 @@ use crate::{ blueprint::plain::Plain, codec::{ postcard::Postcard, - raw::Raw, + primitive::Primitive, }, column::Column, structured_storage::TableWithBlueprint, @@ -12,7 +12,7 @@ use crate::{ }; impl TableWithBlueprint for FuelBlocks { - type Blueprint = Plain; + type Blueprint = Plain, Postcard>; fn column() -> Column { Column::FuelBlocks diff --git a/crates/storage/src/structured_storage/sealed_block.rs b/crates/storage/src/structured_storage/sealed_block.rs index c0fb6d8db21..4d4b9c56d1d 100644 --- a/crates/storage/src/structured_storage/sealed_block.rs +++ b/crates/storage/src/structured_storage/sealed_block.rs @@ -4,7 +4,7 @@ use crate::{ blueprint::plain::Plain, codec::{ postcard::Postcard, - raw::Raw, + primitive::Primitive, }, column::Column, structured_storage::TableWithBlueprint, @@ -12,7 +12,7 @@ use crate::{ }; impl TableWithBlueprint for SealedBlockConsensus { - type Blueprint = Plain; + type Blueprint = Plain, Postcard>; fn column() -> Column { Column::FuelBlockConsensus @@ -22,6 +22,6 @@ impl TableWithBlueprint for SealedBlockConsensus { #[cfg(test)] crate::basic_storage_tests!( SealedBlockConsensus, - ::Key::from([1u8; 32]), + ::Key::default(), ::Value::default() ); diff --git a/crates/storage/src/tables.rs b/crates/storage/src/tables.rs index 1ec13b0f034..9d06c06b424 100644 --- a/crates/storage/src/tables.rs +++ b/crates/storage/src/tables.rs @@ -6,7 +6,6 @@ use fuel_core_types::{ blockchain::{ block::CompressedBlock, consensus::Consensus, - primitives::BlockId, }, entities::{ coins::coin::CompressedCoin, @@ -20,6 +19,7 @@ use fuel_core_types::{ UtxoId, }, fuel_types::{ + BlockHeight, Bytes32, ContractId, Nonce, @@ -39,9 +39,7 @@ pub struct FuelBlocks; impl Mappable for FuelBlocks { /// Unique identifier of the fuel block. type Key = Self::OwnedKey; - // TODO: Seems it would be faster to use `BlockHeight` as primary key. - // https://github.com/FuelLabs/fuel-core/issues/1580. - type OwnedKey = BlockId; + type OwnedKey = BlockHeight; type Value = Self::OwnedValue; type OwnedValue = CompressedBlock; } @@ -76,7 +74,7 @@ pub struct SealedBlockConsensus; impl Mappable for SealedBlockConsensus { type Key = Self::OwnedKey; - type OwnedKey = BlockId; + type OwnedKey = BlockHeight; type Value = Self::OwnedValue; type OwnedValue = Consensus; } diff --git a/tests/tests/blocks.rs b/tests/tests/blocks.rs index 05f3ba38a12..4473dffcaa1 100644 --- a/tests/tests/blocks.rs +++ b/tests/tests/blocks.rs @@ -45,7 +45,7 @@ use std::{ async fn block() { // setup test data in the node let block = CompressedBlock::default(); - let id = block.id(); + let height = block.header().height(); let mut db = Database::default(); // setup server & client let srv = FuelService::from_database(db.clone(), Config::local_node()) @@ -53,13 +53,13 @@ async fn block() { .unwrap(); let client = FuelClient::from(srv.bound_address); - db.storage::().insert(&id, &block).unwrap(); + db.storage::().insert(height, &block).unwrap(); db.storage::() - .insert(&id, &Consensus::PoA(Default::default())) + .insert(height, &Consensus::PoA(Default::default())) .unwrap(); // run test - let block = client.block(&id.into()).await.unwrap(); + let block = client.block_by_height(**height).await.unwrap(); assert!(block.is_some()); } diff --git a/tests/tests/poa.rs b/tests/tests/poa.rs index 10fb590a955..b48b2799aed 100644 --- a/tests/tests/poa.rs +++ b/tests/tests/poa.rs @@ -1,5 +1,6 @@ use fuel_core::{ database::Database, + fuel_core_graphql_api::ports::DatabaseBlocks, service::{ Config, FuelService, @@ -52,9 +53,10 @@ async fn can_get_sealed_block_from_poa_produced_block() { let block_id = BlockId::from_str(&block_id).unwrap(); + let block_height = db.block_height(&block_id).unwrap(); // check sealed block header is correct let sealed_block_header = db - .get_sealed_block_header(&block_id) + .get_sealed_block_header(&block_height) .unwrap() .expect("expected sealed header to be available"); @@ -68,9 +70,10 @@ async fn can_get_sealed_block_from_poa_produced_block() { .verify(&poa_public, &block_id.into_message()) .expect("failed to verify signature"); + let block_height = db.block_height(&block_id).unwrap(); // check sealed block is correct let sealed_block = db - .get_sealed_block_by_id(&block_id) + .get_sealed_block_by_height(&block_height) .unwrap() .expect("expected sealed header to be available");