diff --git a/CHANGELOG.md b/CHANGELOG.md index 4962954f354..9810be4bc5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - [2551](https://github.com/FuelLabs/fuel-core/pull/2551): Enhanced the DA compressed block header to include block id. +- [2595](https://github.com/FuelLabs/fuel-core/pull/2595): Added `indexation` field to the `nodeInfo` GraphQL endpoint to allow checking if a specific indexation is enabled. ### Changed - [2603](https://github.com/FuelLabs/fuel-core/pull/2603): Sets the latest recorded height on initialization, not just when DA costs are received diff --git a/crates/client/assets/schema.sdl b/crates/client/assets/schema.sdl index 6829d9d3953..016f7907af7 100644 --- a/crates/client/assets/schema.sdl +++ b/crates/client/assets/schema.sdl @@ -580,6 +580,21 @@ type HeavyOperation { scalar HexString +type IndexationFlags { + """ + Is balances indexation enabled + """ + balances: Boolean! + """ + Is coins to spend indexation enabled + """ + coinsToSpend: Boolean! + """ + Is asset metadata indexation enabled + """ + assetMetadata: Boolean! +} + union Input = InputCoin | InputContract | InputMessage type InputCoin { @@ -764,6 +779,7 @@ type NodeInfo { maxSize: U64! maxDepth: U64! nodeVersion: String! + indexation: IndexationFlags! txPoolStats: TxPoolStats! peers: [PeerInfo!]! } diff --git a/crates/client/src/client/schema/node_info.rs b/crates/client/src/client/schema/node_info.rs index 1a3e7b5d859..3422162519c 100644 --- a/crates/client/src/client/schema/node_info.rs +++ b/crates/client/src/client/schema/node_info.rs @@ -27,6 +27,7 @@ pub struct NodeInfo { pub max_size: U64, pub max_depth: U64, pub node_version: String, + pub indexation: IndexationFlags, pub tx_pool_stats: TxPoolStats, } @@ -88,6 +89,14 @@ pub struct TxPoolStats { pub total_size: U64, } +#[derive(cynic::QueryFragment, Clone, Debug, PartialEq, Eq)] +#[cynic(schema_path = "./assets/schema.sdl")] +pub struct IndexationFlags { + pub balances: bool, + pub coins_to_spend: bool, + pub asset_metadata: bool, +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__node_info__tests__node_info_query_gql_output.snap b/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__node_info__tests__node_info_query_gql_output.snap index d4f70ba6e26..aac8e698201 100644 --- a/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__node_info__tests__node_info_query_gql_output.snap +++ b/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__node_info__tests__node_info_query_gql_output.snap @@ -12,6 +12,11 @@ query QueryNodeInfo { maxSize maxDepth nodeVersion + indexation { + balances + coinsToSpend + assetMetadata + } txPoolStats { txCount totalGas diff --git a/crates/client/src/client/types/node_info.rs b/crates/client/src/client/types/node_info.rs index dce6a6a1174..4905437bb07 100644 --- a/crates/client/src/client/types/node_info.rs +++ b/crates/client/src/client/types/node_info.rs @@ -1,6 +1,9 @@ use crate::client::schema::{ self, - node_info::TxPoolStats, + node_info::{ + IndexationFlags, + TxPoolStats, + }, }; #[derive(Clone, Debug, PartialEq, Eq)] @@ -12,6 +15,7 @@ pub struct NodeInfo { pub max_size: u64, pub max_depth: u64, pub node_version: String, + pub indexation: IndexationFlags, pub tx_pool_stats: TxPoolStats, } @@ -27,6 +31,7 @@ impl From<schema::node_info::NodeInfo> for NodeInfo { max_size: value.max_size.into(), max_depth: value.max_depth.into(), node_version: value.node_version, + indexation: value.indexation, tx_pool_stats: value.tx_pool_stats, } } diff --git a/crates/fuel-core/src/graphql_api/database.rs b/crates/fuel-core/src/graphql_api/database.rs index d7234b5b880..20ee054644d 100644 --- a/crates/fuel-core/src/graphql_api/database.rs +++ b/crates/fuel-core/src/graphql_api/database.rs @@ -1,8 +1,11 @@ -use crate::fuel_core_graphql_api::{ - database::arc_wrapper::ArcWrapper, - ports::{ - OffChainDatabase, - OnChainDatabase, +use crate::{ + database::database_description::IndexationKind, + fuel_core_graphql_api::{ + database::arc_wrapper::ArcWrapper, + ports::{ + OffChainDatabase, + OnChainDatabase, + }, }, }; use fuel_core_services::yield_stream::StreamYieldExt; @@ -67,6 +70,7 @@ use std::{ borrow::Cow, sync::Arc, }; +use strum::IntoEnumIterator; use super::ports::worker; @@ -88,12 +92,31 @@ pub struct ReadDatabase { on_chain: Box<dyn AtomicView<LatestView = OnChainView>>, /// The off-chain database view provider. off_chain: Box<dyn AtomicView<LatestView = OffChainView>>, - /// The flag that indicates whether the Balances indexation is enabled. - balances_indexation_enabled: bool, - /// The flag that indicates whether the CoinsToSpend indexation is enabled. - coins_to_spend_indexation_enabled: bool, - /// The flag that indicates whether the AssetMetadata indexation is enabled. - asset_metadata_indexation_enabled: bool, + /// The flag indicating which indexation is enabled. + indexation_flags: IndexationFlags, +} + +#[derive(Clone)] +pub struct IndexationFlags(u8); + +impl IndexationFlags { + pub fn new() -> Self { + Self(0) + } + + pub fn contains(&self, kind: &IndexationKind) -> bool { + self.0 & (1 << *kind as u8) != 0 + } + + pub fn insert(&mut self, kind: IndexationKind) { + self.0 |= 1 << kind as u8; + } +} + +impl Default for IndexationFlags { + fn default() -> Self { + Self::new() + } } impl ReadDatabase { @@ -110,20 +133,32 @@ impl ReadDatabase { OnChain::LatestView: OnChainDatabase, OffChain::LatestView: OffChainDatabase, { - let balances_indexation_enabled = off_chain.balances_indexation_enabled()?; - let coins_to_spend_indexation_enabled = - off_chain.coins_to_spend_indexation_enabled()?; - let asset_metadata_indexation_enabled = - off_chain.asset_metadata_indexation_enabled()?; - + let mut indexation_flags = IndexationFlags::new(); + for kind in IndexationKind::iter() { + match kind { + IndexationKind::Balances => { + if off_chain.balances_indexation_enabled()? { + indexation_flags.insert(kind); + } + } + IndexationKind::CoinsToSpend => { + if off_chain.coins_to_spend_indexation_enabled()? { + indexation_flags.insert(kind); + } + } + IndexationKind::AssetMetadata => { + if off_chain.asset_metadata_indexation_enabled()? { + indexation_flags.insert(kind); + } + } + } + } Ok(Self { batch_size, genesis_height, on_chain: Box::new(ArcWrapper::new(on_chain)), off_chain: Box::new(ArcWrapper::new(off_chain)), - balances_indexation_enabled, - coins_to_spend_indexation_enabled, - asset_metadata_indexation_enabled, + indexation_flags, }) } @@ -137,9 +172,7 @@ impl ReadDatabase { genesis_height: self.genesis_height, on_chain: self.on_chain.latest_view()?, off_chain: self.off_chain.latest_view()?, - balances_indexation_enabled: self.balances_indexation_enabled, - coins_to_spend_indexation_enabled: self.coins_to_spend_indexation_enabled, - asset_metadata_indexation_enabled: self.asset_metadata_indexation_enabled, + indexation_flags: self.indexation_flags.clone(), }) } @@ -155,9 +188,7 @@ pub struct ReadView { pub(crate) genesis_height: BlockHeight, pub(crate) on_chain: OnChainView, pub(crate) off_chain: OffChainView, - pub(crate) balances_indexation_enabled: bool, - pub(crate) coins_to_spend_indexation_enabled: bool, - pub(crate) asset_metadata_indexation_enabled: bool, + pub(crate) indexation_flags: IndexationFlags, } impl ReadView { @@ -408,3 +439,26 @@ impl ReadView { self.off_chain.message_is_spent(nonce) } } + +#[test] +fn test_indexation_flags() { + let mut indexation = IndexationFlags::new(); + assert!(!indexation.contains(&IndexationKind::Balances)); + assert!(!indexation.contains(&IndexationKind::CoinsToSpend)); + assert!(!indexation.contains(&IndexationKind::AssetMetadata)); + + indexation.insert(IndexationKind::Balances); + assert!(indexation.contains(&IndexationKind::Balances)); + assert!(!indexation.contains(&IndexationKind::CoinsToSpend)); + assert!(!indexation.contains(&IndexationKind::AssetMetadata)); + + indexation.insert(IndexationKind::CoinsToSpend); + assert!(indexation.contains(&IndexationKind::Balances)); + assert!(indexation.contains(&IndexationKind::CoinsToSpend)); + assert!(!indexation.contains(&IndexationKind::AssetMetadata)); + + indexation.insert(IndexationKind::AssetMetadata); + assert!(indexation.contains(&IndexationKind::Balances)); + assert!(indexation.contains(&IndexationKind::CoinsToSpend)); + assert!(indexation.contains(&IndexationKind::AssetMetadata)); +} diff --git a/crates/fuel-core/src/query/assets.rs b/crates/fuel-core/src/query/assets.rs index b6270763d7b..975cdb1409d 100644 --- a/crates/fuel-core/src/query/assets.rs +++ b/crates/fuel-core/src/query/assets.rs @@ -1,4 +1,5 @@ use crate::{ + database::database_description::IndexationKind, fuel_core_graphql_api::database::ReadView, graphql_api::storage::assets::AssetDetails, }; @@ -10,7 +11,10 @@ use fuel_core_types::fuel_tx::AssetId; impl ReadView { pub fn get_asset_details(&self, id: &AssetId) -> StorageResult<AssetDetails> { - if self.asset_metadata_indexation_enabled { + if self + .indexation_flags + .contains(&IndexationKind::AssetMetadata) + { Ok(self .off_chain .asset_info(id)? diff --git a/crates/fuel-core/src/query/balance.rs b/crates/fuel-core/src/query/balance.rs index b98f6bc9635..8902da6dcf8 100644 --- a/crates/fuel-core/src/query/balance.rs +++ b/crates/fuel-core/src/query/balance.rs @@ -4,6 +4,7 @@ use std::{ }; use crate::{ + database::database_description::IndexationKind, fuel_core_graphql_api::database::ReadView, graphql_api::storage::balances::TotalBalanceAmount, }; @@ -41,7 +42,7 @@ impl ReadView { asset_id: AssetId, base_asset_id: AssetId, ) -> StorageResult<AddressBalance> { - let amount = if self.balances_indexation_enabled { + let amount = if self.indexation_flags.contains(&IndexationKind::Balances) { self.off_chain.balance(&owner, &asset_id, &base_asset_id)? } else { AssetQuery::new( @@ -73,7 +74,7 @@ impl ReadView { direction: IterDirection, base_asset_id: &'a AssetId, ) -> impl Stream<Item = StorageResult<AddressBalance>> + 'a { - if self.balances_indexation_enabled { + if self.indexation_flags.contains(&IndexationKind::Balances) { futures::future::Either::Left(self.balances_with_cache( owner, start, diff --git a/crates/fuel-core/src/schema/balance.rs b/crates/fuel-core/src/schema/balance.rs index 694b60d7c97..53ae03071b7 100644 --- a/crates/fuel-core/src/schema/balance.rs +++ b/crates/fuel-core/src/schema/balance.rs @@ -1,4 +1,5 @@ use crate::{ + database::database_description::IndexationKind, fuel_core_graphql_api::{ api_service::ConsensusProvider, query_costs, @@ -111,7 +112,9 @@ impl BalanceQuery { ) -> async_graphql::Result<Connection<AssetId, Balance, EmptyFields, EmptyFields>> { let query = ctx.read_view()?; - if !query.balances_indexation_enabled && (before.is_some() || after.is_some()) { + if !query.indexation_flags.contains(&IndexationKind::Balances) + && (before.is_some() || after.is_some()) + { return Err(anyhow!( "Can not use pagination when balances indexation is not available" ) diff --git a/crates/fuel-core/src/schema/coins.rs b/crates/fuel-core/src/schema/coins.rs index b7a1cc765d0..9f0a3bea1a0 100644 --- a/crates/fuel-core/src/schema/coins.rs +++ b/crates/fuel-core/src/schema/coins.rs @@ -8,6 +8,7 @@ use crate::{ ExcludedCoinIds, SpendQuery, }, + database::database_description::IndexationKind, fuel_core_graphql_api::{ query_costs, storage::coins::CoinsToSpendIndexKey, @@ -289,7 +290,9 @@ impl CoinQuery { query_per_asset.truncate(max_input as usize); let read_view = ctx.read_view()?; - let indexation_available = read_view.coins_to_spend_indexation_enabled; + let indexation_available = read_view + .indexation_flags + .contains(&IndexationKind::CoinsToSpend); if indexation_available { coins_to_spend_with_cache( owner, diff --git a/crates/fuel-core/src/schema/node_info.rs b/crates/fuel-core/src/schema/node_info.rs index d02070b361b..4015dcaf412 100644 --- a/crates/fuel-core/src/schema/node_info.rs +++ b/crates/fuel-core/src/schema/node_info.rs @@ -3,11 +3,18 @@ use super::scalars::{ U64, }; use crate::{ + database::database_description::IndexationKind, fuel_core_graphql_api::{ query_costs, Config as GraphQLConfig, }, - graphql_api::api_service::TxPool, + graphql_api::{ + api_service::TxPool, + database::{ + IndexationFlags, + ReadDatabase, + }, + }, }; use async_graphql::{ Context, @@ -23,6 +30,7 @@ pub struct NodeInfo { max_size: U64, max_depth: U64, node_version: String, + indexation: IndexationFlags, } #[Object] @@ -55,6 +63,10 @@ impl NodeInfo { self.node_version.to_owned() } + async fn indexation(&self) -> &IndexationFlags { + &self.indexation + } + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn tx_pool_stats( &self, @@ -94,6 +106,8 @@ impl NodeQuery { const VERSION: &str = env!("CARGO_PKG_VERSION"); + let db = ctx.data_unchecked::<ReadDatabase>(); + let read_view = db.view()?; Ok(NodeInfo { utxo_validation: config.utxo_validation, vm_backtrace: config.vm_backtrace, @@ -102,6 +116,7 @@ impl NodeQuery { max_size: (config.max_size as u64).into(), max_depth: (config.max_txpool_dependency_chain_length as u64).into(), node_version: VERSION.to_owned(), + indexation: read_view.indexation_flags, }) } } @@ -168,3 +183,21 @@ impl TxPoolStats { self.0.total_gas.into() } } + +#[Object] +impl IndexationFlags { + /// Is balances indexation enabled + async fn balances(&self) -> bool { + self.contains(&IndexationKind::Balances) + } + + /// Is coins to spend indexation enabled + async fn coins_to_spend(&self) -> bool { + self.contains(&IndexationKind::CoinsToSpend) + } + + /// Is asset metadata indexation enabled + async fn asset_metadata(&self) -> bool { + self.contains(&IndexationKind::AssetMetadata) + } +}