From c4329f80681a9458ca95ff0ac7618add9ccc1a83 Mon Sep 17 00:00:00 2001 From: acerone85 Date: Fri, 11 Oct 2024 11:16:02 +0100 Subject: [PATCH 1/5] feat: CLI arguments for configuring GraphQL query costs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: MÃ¥rten Blankfors --- CHANGELOG.md | 4 +- bin/fuel-core/src/cli/run.rs | 24 +++ bin/fuel-core/src/cli/run/graphql.rs | 160 ++++++++++++++++++ crates/fuel-core/src/graphql_api.rs | 22 ++- .../fuel-core/src/graphql_api/api_service.rs | 6 + crates/fuel-core/src/schema/balance.rs | 6 +- crates/fuel-core/src/schema/blob.rs | 6 +- crates/fuel-core/src/schema/block.rs | 16 +- crates/fuel-core/src/schema/chain.rs | 12 +- crates/fuel-core/src/schema/coins.rs | 14 +- crates/fuel-core/src/schema/contract.rs | 16 +- crates/fuel-core/src/schema/da_compressed.rs | 4 +- crates/fuel-core/src/schema/gas_price.rs | 6 +- crates/fuel-core/src/schema/message.rs | 14 +- crates/fuel-core/src/schema/node_info.rs | 6 +- crates/fuel-core/src/schema/relayed_tx.rs | 4 +- crates/fuel-core/src/schema/tx.rs | 24 +-- crates/fuel-core/src/schema/tx/types.rs | 16 +- crates/fuel-core/src/schema/upgrades.rs | 10 +- crates/fuel-core/src/service/config.rs | 1 + tests/tests/dos.rs | 19 ++- 21 files changed, 306 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35b21a83e99..47d07538020 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Changed - - [2334](https://github.com/FuelLabs/fuel-core/pull/2334): Prepare the GraphQL service for the switching to `async` methods. +### Added +- [2335](https://github.com/FuelLabs/fuel-core/pull/2335): Added CLI arguments for configuring GraphQL query costs. + ## [Version 0.39.0] ### Added diff --git a/bin/fuel-core/src/cli/run.rs b/bin/fuel-core/src/cli/run.rs index ab3523dec1a..644ae029479 100644 --- a/bin/fuel-core/src/cli/run.rs +++ b/bin/fuel-core/src/cli/run.rs @@ -22,6 +22,7 @@ use fuel_core::{ }, fuel_core_graphql_api::{ worker_service::DaCompressionConfig, + Costs, ServiceConfig as GraphQLConfig, }, producer::Config as ProducerConfig, @@ -498,6 +499,29 @@ impl Command { request_body_bytes_limit: graphql.graphql_request_body_bytes_limit, api_request_timeout: graphql.api_request_timeout.into(), query_log_threshold_time: graphql.query_log_threshold_time.into(), + costs: Costs { + balance_query: graphql.costs.balance_query, + coins_to_spend: graphql.costs.coins_to_spend, + get_peers: graphql.costs.get_peers, + estimate_predicates: graphql.costs.estimate_predicates, + dry_run: graphql.costs.dry_run, + submit: graphql.costs.submit, + submit_and_await: graphql.costs.submit_and_await, + status_change: graphql.costs.status_change, + storage_read: graphql.costs.storage_read, + tx_get: graphql.costs.tx_get, + tx_status_read: graphql.costs.tx_status_read, + tx_raw_payload: graphql.costs.tx_raw_payload, + block_header: graphql.costs.block_header, + block_transactions: graphql.costs.block_transactions, + block_transactions_ids: graphql.costs.block_transactions_ids, + storage_iterator: graphql.costs.storage_iterator, + bytecode_read: graphql.costs.bytecode_read, + state_transition_bytecode_read: graphql + .costs + .state_transition_bytecode_read, + da_compressed_block_read: graphql.costs.da_compressed_block_read, + }, }, combined_db_config, snapshot_reader, diff --git a/bin/fuel-core/src/cli/run/graphql.rs b/bin/fuel-core/src/cli/run/graphql.rs index 95cfb2852f5..e9c1e74d50c 100644 --- a/bin/fuel-core/src/cli/run/graphql.rs +++ b/bin/fuel-core/src/cli/run/graphql.rs @@ -2,6 +2,8 @@ use std::net; +use fuel_core::fuel_core_graphql_api::DEFAULT_QUERY_COSTS; + #[derive(Debug, Clone, clap::Args)] pub struct GraphQLArgs { /// The IP address to bind the GraphQL service to. @@ -55,4 +57,162 @@ pub struct GraphQLArgs { /// Timeout before drop the request. #[clap(long = "api-request-timeout", default_value = "30s", env)] pub api_request_timeout: humantime::Duration, + + #[clap(flatten)] + pub costs: QueryCosts, +} + +/// Costs for individual graphql queries. +#[derive(Debug, Clone, clap::Args)] +pub struct QueryCosts { + /// Query costs for getting balances. + #[clap( + long = "query-cost-balance-query", + default_value = DEFAULT_QUERY_COSTS.balance_query.to_string(), + env + )] + pub balance_query: usize, + + /// Query costs for getting coins to spend. + #[clap( + long = "query-cost-coins-to-spend", + default_value = DEFAULT_QUERY_COSTS.coins_to_spend.to_string(), + env)] + pub coins_to_spend: usize, + + /// Query costs for getting peers. + #[clap( + long = "query-cost-get-peers", + default_value = DEFAULT_QUERY_COSTS.get_peers.to_string(), + env + )] + pub get_peers: usize, + + /// Query costs for estimating predicates. + #[clap( + long = "query-cost-estimate-predicates", + default_value = DEFAULT_QUERY_COSTS.estimate_predicates.to_string(), + env + )] + pub estimate_predicates: usize, + + /// Query costs for dry running a set of transactions. + #[clap( + long = "query-cost-dry-run", + default_value = DEFAULT_QUERY_COSTS.dry_run.to_string(), + env + )] + pub dry_run: usize, + + /// Query costs for submitting a transaction. + #[clap( + long = "query-cost-submit", + default_value = DEFAULT_QUERY_COSTS.submit.to_string(), + env + )] + pub submit: usize, + + /// Query costs for submitting and awaiting a transaction. + #[clap( + long = "query-cost-submit-and-await", + default_value = DEFAULT_QUERY_COSTS.submit_and_await.to_string(), + env + )] + pub submit_and_await: usize, + + /// Query costs for the status change query. + #[clap( + long = "query-cost-status-change", + default_value = DEFAULT_QUERY_COSTS.status_change.to_string(), + env + )] + pub status_change: usize, + + /// Query costs for reading from storage. + #[clap( + long = "query-cost-storage-read", + default_value = DEFAULT_QUERY_COSTS.storage_read.to_string(), + env + )] + pub storage_read: usize, + + /// Query costs for getting a transaction. + #[clap( + long = "query-cost-tx-get", + default_value = DEFAULT_QUERY_COSTS.tx_get.to_string(), + env + )] + pub tx_get: usize, + + /// Query costs for reading tx status. + #[clap( + long = "query-cost-tx-status-read", + default_value = DEFAULT_QUERY_COSTS.tx_status_read.to_string(), + env + )] + pub tx_status_read: usize, + + /// Query costs for getting the raw tx payload. + #[clap( + long = "query-cost-tx-raw-payload", + default_value = DEFAULT_QUERY_COSTS.tx_raw_payload.to_string(), + env + )] + pub tx_raw_payload: usize, + + /// Query costs for block header. + #[clap( + long = "query-cost-block-header", + default_value = DEFAULT_QUERY_COSTS.block_header.to_string(), + env + )] + pub block_header: usize, + + /// Query costs for block transactions. + #[clap( + long = "query-cost-block-transactions", + default_value = DEFAULT_QUERY_COSTS.block_transactions.to_string(), + env + )] + pub block_transactions: usize, + + /// Query costs for block transactions ids. + #[clap( + long = "query-cost-block-transactions-ids", + default_value = DEFAULT_QUERY_COSTS.block_transactions_ids.to_string(), + env + )] + pub block_transactions_ids: usize, + + /// Query costs for iterating over storage entries. + #[clap( + long = "query-cost-storage-iterator", + default_value = DEFAULT_QUERY_COSTS.storage_iterator.to_string(), + env + )] + pub storage_iterator: usize, + + /// Query costs for reading bytecode. + #[clap( + long = "query-cost-bytecode-read", + default_value = DEFAULT_QUERY_COSTS.bytecode_read.to_string(), + env + )] + pub bytecode_read: usize, + + /// Query costs for reading state transition bytecode. + #[clap( + long = "query-cost-state-transition-bytecode-read", + default_value = DEFAULT_QUERY_COSTS.state_transition_bytecode_read.to_string(), + env + )] + pub state_transition_bytecode_read: usize, + + /// Query costs for reading a DA compressed block. + #[clap( + long = "query-cost-da-compressed-block-read", + default_value = DEFAULT_QUERY_COSTS.da_compressed_block_read.to_string(), + env + )] + pub da_compressed_block_read: usize, } diff --git a/crates/fuel-core/src/graphql_api.rs b/crates/fuel-core/src/graphql_api.rs index 6d0d280fd3f..bf97ec541f8 100644 --- a/crates/fuel-core/src/graphql_api.rs +++ b/crates/fuel-core/src/graphql_api.rs @@ -4,6 +4,7 @@ use fuel_core_storage::{ }; use std::{ net::SocketAddr, + sync::OnceLock, time::Duration, }; @@ -30,8 +31,11 @@ pub struct ServiceConfig { /// Time to wait after submitting a query before debug info will be logged about query. pub query_log_threshold_time: Duration, pub api_request_timeout: Duration, + /// Configurable cost parameters to limit graphql queries complexity + pub costs: Costs, } +#[derive(Clone, Debug)] pub struct Costs { pub balance_query: usize, pub coins_to_spend: usize, @@ -54,16 +58,18 @@ pub struct Costs { pub da_compressed_block_read: usize, } -pub const QUERY_COSTS: Costs = Costs { - // balance_query: 4000, +impl Default for Costs { + fn default() -> Self { + DEFAULT_QUERY_COSTS + } +} + +pub const DEFAULT_QUERY_COSTS: Costs = Costs { balance_query: 40001, coins_to_spend: 40001, - // get_peers: 2000, get_peers: 40001, - // estimate_predicates: 3000, estimate_predicates: 40001, dry_run: 12000, - // submit: 5000, submit: 40001, submit_and_await: 40001, status_change: 40001, @@ -80,6 +86,12 @@ pub const QUERY_COSTS: Costs = Costs { da_compressed_block_read: 4000, }; +pub fn query_costs() -> &'static Costs { + QUERY_COSTS.get().unwrap_or(&DEFAULT_QUERY_COSTS) +} + +pub static QUERY_COSTS: OnceLock = OnceLock::new(); + #[derive(Clone, Debug)] pub struct Config { pub config: ServiceConfig, diff --git a/crates/fuel-core/src/graphql_api/api_service.rs b/crates/fuel-core/src/graphql_api/api_service.rs index ec79918e09c..296005ca16d 100644 --- a/crates/fuel-core/src/graphql_api/api_service.rs +++ b/crates/fuel-core/src/graphql_api/api_service.rs @@ -15,6 +15,7 @@ use crate::{ view_extension::ViewExtension, Config, }, + graphql_api::QUERY_COSTS, schema::{ CoreSchema, CoreSchemaBuilder, @@ -220,6 +221,7 @@ where OnChain::LatestView: OnChainDatabase, OffChain::LatestView: OffChainDatabase, { + initialize_query_costs(config.clone()); let network_addr = config.config.addr; let combined_read_database = ReadDatabase::new(genesis_block_height, on_database, off_database); @@ -296,6 +298,10 @@ where )) } +fn initialize_query_costs(config: Config) { + QUERY_COSTS.get_or_init(|| config.config.costs); +} + async fn graphql_playground() -> impl IntoResponse { Html(playground_source(GraphQLPlaygroundConfig::new( "/v1/graphql", diff --git a/crates/fuel-core/src/schema/balance.rs b/crates/fuel-core/src/schema/balance.rs index 7c43e3a9509..98b40c3963c 100644 --- a/crates/fuel-core/src/schema/balance.rs +++ b/crates/fuel-core/src/schema/balance.rs @@ -1,7 +1,7 @@ use crate::{ fuel_core_graphql_api::{ api_service::ConsensusProvider, - QUERY_COSTS, + query_costs, }, schema::{ scalars::{ @@ -52,7 +52,7 @@ pub struct BalanceQuery; #[Object] impl BalanceQuery { - #[graphql(complexity = "QUERY_COSTS.balance_query")] + #[graphql(complexity = "query_costs().balance_query")] async fn balance( &self, ctx: &Context<'_>, @@ -70,7 +70,7 @@ impl BalanceQuery { // TODO: This API should be migrated to the indexer for better support and // discontinued within fuel-core. - #[graphql(complexity = "QUERY_COSTS.balance_query")] + #[graphql(complexity = "query_costs().balance_query")] async fn balances( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/blob.rs b/crates/fuel-core/src/schema/blob.rs index 93974346f1f..76cc32530cc 100644 --- a/crates/fuel-core/src/schema/blob.rs +++ b/crates/fuel-core/src/schema/blob.rs @@ -1,5 +1,5 @@ use crate::{ - fuel_core_graphql_api::QUERY_COSTS, + fuel_core_graphql_api::query_costs, graphql_api::IntoApiResult, schema::{ scalars::{ @@ -27,7 +27,7 @@ impl Blob { self.0.into() } - #[graphql(complexity = "QUERY_COSTS.bytecode_read")] + #[graphql(complexity = "query_costs().bytecode_read")] async fn bytecode(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; query @@ -48,7 +48,7 @@ pub struct BlobQuery; #[Object] impl BlobQuery { - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn blob( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/block.rs b/crates/fuel-core/src/schema/block.rs index cebd04989b6..5878a1defb1 100644 --- a/crates/fuel-core/src/schema/block.rs +++ b/crates/fuel-core/src/schema/block.rs @@ -7,9 +7,9 @@ use crate::{ fuel_core_graphql_api::{ api_service::ConsensusModule, database::ReadView, + query_costs, Config as GraphQLConfig, IntoApiResult, - QUERY_COSTS, }, schema::{ scalars::{ @@ -112,14 +112,14 @@ impl Block { self.0.header().clone().into() } - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn consensus(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let height = self.0.header().height(); Ok(query.consensus(height)?.try_into()?) } - #[graphql(complexity = "QUERY_COSTS.block_transactions_ids")] + #[graphql(complexity = "query_costs().block_transactions_ids")] async fn transaction_ids(&self) -> Vec { self.0 .transactions() @@ -129,7 +129,7 @@ impl Block { } // Assume that in average we have 32 transactions per block. - #[graphql(complexity = "QUERY_COSTS.block_transactions + child_complexity")] + #[graphql(complexity = "query_costs().block_transactions + child_complexity")] async fn transactions( &self, ctx: &Context<'_>, @@ -240,7 +240,7 @@ pub struct BlockQuery; #[Object] impl BlockQuery { - #[graphql(complexity = "QUERY_COSTS.block_header + child_complexity")] + #[graphql(complexity = "query_costs().block_header + child_complexity")] async fn block( &self, ctx: &Context<'_>, @@ -270,7 +270,7 @@ impl BlockQuery { } #[graphql(complexity = "{\ - (QUERY_COSTS.block_header + child_complexity) \ + (query_costs().block_header + child_complexity) \ * (first.unwrap_or_default() as usize + last.unwrap_or_default() as usize) \ }")] async fn blocks( @@ -298,7 +298,7 @@ pub struct HeaderQuery; #[Object] impl HeaderQuery { - #[graphql(complexity = "QUERY_COSTS.block_header + child_complexity")] + #[graphql(complexity = "query_costs().block_header + child_complexity")] async fn header( &self, ctx: &Context<'_>, @@ -312,7 +312,7 @@ impl HeaderQuery { } #[graphql(complexity = "{\ - (QUERY_COSTS.block_header + child_complexity) \ + (query_costs().block_header + child_complexity) \ * (first.unwrap_or_default() as usize + last.unwrap_or_default() as usize) \ }")] async fn headers( diff --git a/crates/fuel-core/src/schema/chain.rs b/crates/fuel-core/src/schema/chain.rs index 643902cc0c5..9acdbb01940 100644 --- a/crates/fuel-core/src/schema/chain.rs +++ b/crates/fuel-core/src/schema/chain.rs @@ -1,7 +1,7 @@ use crate::{ fuel_core_graphql_api::{ api_service::ConsensusProvider, - QUERY_COSTS, + query_costs, }, graphql_api::Config, schema::{ @@ -775,13 +775,13 @@ impl HeavyOperation { #[Object] impl ChainInfo { - #[graphql(complexity = "QUERY_COSTS.storage_read")] + #[graphql(complexity = "query_costs().storage_read")] async fn name(&self, ctx: &Context<'_>) -> async_graphql::Result { let config: &Config = ctx.data_unchecked(); Ok(config.chain_name.clone()) } - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn latest_block(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; @@ -789,7 +789,7 @@ impl ChainInfo { Ok(latest_block) } - #[graphql(complexity = "QUERY_COSTS.storage_read")] + #[graphql(complexity = "query_costs().storage_read")] async fn da_height(&self, ctx: &Context<'_>) -> U64 { let Ok(query) = ctx.read_view() else { return 0.into(); @@ -798,7 +798,7 @@ impl ChainInfo { query.da_height().unwrap_or_default().0.into() } - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn consensus_parameters( &self, ctx: &Context<'_>, @@ -810,7 +810,7 @@ impl ChainInfo { Ok(ConsensusParameters(params)) } - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn gas_costs(&self, ctx: &Context<'_>) -> async_graphql::Result { let params = ctx .data_unchecked::() diff --git a/crates/fuel-core/src/schema/coins.rs b/crates/fuel-core/src/schema/coins.rs index 3610c152e6b..648595c65d6 100644 --- a/crates/fuel-core/src/schema/coins.rs +++ b/crates/fuel-core/src/schema/coins.rs @@ -4,8 +4,8 @@ use crate::{ SpendQuery, }, fuel_core_graphql_api::{ + query_costs, IntoApiResult, - QUERY_COSTS, }, graphql_api::api_service::ConsensusProvider, query::asset_query::AssetSpendTarget, @@ -92,7 +92,7 @@ impl MessageCoin { self.0.amount.into() } - #[graphql(complexity = "QUERY_COSTS.storage_read")] + #[graphql(complexity = "query_costs().storage_read")] async fn asset_id(&self, ctx: &Context<'_>) -> AssetId { let params = ctx .data_unchecked::() @@ -148,7 +148,7 @@ pub struct CoinQuery; #[async_graphql::Object] impl CoinQuery { /// Gets the coin by `utxo_id`. - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn coin( &self, ctx: &Context<'_>, @@ -160,9 +160,9 @@ impl CoinQuery { /// Gets all unspent coins of some `owner` maybe filtered with by `asset_id` per page. #[graphql(complexity = "{\ - QUERY_COSTS.storage_iterator\ - + (QUERY_COSTS.storage_read + first.unwrap_or_default() as usize) * child_complexity \ - + (QUERY_COSTS.storage_read + last.unwrap_or_default() as usize) * child_complexity\ + query_costs().storage_iterator\ + + (query_costs().storage_read + first.unwrap_or_default() as usize) * child_complexity \ + + (query_costs().storage_read + last.unwrap_or_default() as usize) * child_complexity\ }")] async fn coins( &self, @@ -205,7 +205,7 @@ impl CoinQuery { /// The list of spendable coins per asset from the query. The length of the result is /// the same as the length of `query_per_asset`. The ordering of assets and `query_per_asset` /// is the same. - #[graphql(complexity = "QUERY_COSTS.coins_to_spend")] + #[graphql(complexity = "query_costs().coins_to_spend")] async fn coins_to_spend( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/contract.rs b/crates/fuel-core/src/schema/contract.rs index a8fe6dea693..ec7d260f79f 100644 --- a/crates/fuel-core/src/schema/contract.rs +++ b/crates/fuel-core/src/schema/contract.rs @@ -1,7 +1,7 @@ use crate::{ fuel_core_graphql_api::{ + query_costs, IntoApiResult, - QUERY_COSTS, }, schema::{ scalars::{ @@ -46,7 +46,7 @@ impl Contract { self.0.into() } - #[graphql(complexity = "QUERY_COSTS.bytecode_read")] + #[graphql(complexity = "query_costs().bytecode_read")] async fn bytecode(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; query @@ -55,7 +55,7 @@ impl Contract { .map_err(Into::into) } - #[graphql(complexity = "QUERY_COSTS.storage_read")] + #[graphql(complexity = "query_costs().storage_read")] async fn salt(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; query @@ -70,7 +70,7 @@ pub struct ContractQuery; #[Object] impl ContractQuery { - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn contract( &self, ctx: &Context<'_>, @@ -118,7 +118,7 @@ pub struct ContractBalanceQuery; #[Object] impl ContractBalanceQuery { - #[graphql(complexity = "QUERY_COSTS.storage_read")] + #[graphql(complexity = "query_costs().storage_read")] async fn contract_balance( &self, ctx: &Context<'_>, @@ -144,9 +144,9 @@ impl ContractBalanceQuery { } #[graphql(complexity = "{\ - QUERY_COSTS.storage_iterator\ - + (QUERY_COSTS.storage_read + first.unwrap_or_default() as usize) * child_complexity \ - + (QUERY_COSTS.storage_read + last.unwrap_or_default() as usize) * child_complexity\ + query_costs().storage_iterator\ + + (query_costs().storage_read + first.unwrap_or_default() as usize) * child_complexity \ + + (query_costs().storage_read + last.unwrap_or_default() as usize) * child_complexity\ }")] async fn contract_balances( &self, diff --git a/crates/fuel-core/src/schema/da_compressed.rs b/crates/fuel-core/src/schema/da_compressed.rs index 9badfb1b8c1..2f2939be83f 100644 --- a/crates/fuel-core/src/schema/da_compressed.rs +++ b/crates/fuel-core/src/schema/da_compressed.rs @@ -4,8 +4,8 @@ use super::{ }; use crate::{ fuel_core_graphql_api::{ + query_costs, IntoApiResult, - QUERY_COSTS, }, schema::scalars::U32, }; @@ -36,7 +36,7 @@ pub struct DaCompressedBlockQuery; #[Object] impl DaCompressedBlockQuery { - #[graphql(complexity = "QUERY_COSTS.da_compressed_block_read")] + #[graphql(complexity = "query_costs().da_compressed_block_read")] async fn da_compressed_block( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/gas_price.rs b/crates/fuel-core/src/schema/gas_price.rs index d2a80d67f9d..7f7c7555e6d 100644 --- a/crates/fuel-core/src/schema/gas_price.rs +++ b/crates/fuel-core/src/schema/gas_price.rs @@ -5,7 +5,7 @@ use super::scalars::{ use crate::{ graphql_api::{ api_service::GasPriceProvider, - QUERY_COSTS, + query_costs, }, schema::ReadViewProvider, }; @@ -42,7 +42,7 @@ pub struct LatestGasPriceQuery {} #[Object] impl LatestGasPriceQuery { - #[graphql(complexity = "QUERY_COSTS.block_header")] + #[graphql(complexity = "query_costs().block_header")] async fn latest_gas_price( &self, ctx: &Context<'_>, @@ -80,7 +80,7 @@ pub struct EstimateGasPriceQuery {} #[Object] impl EstimateGasPriceQuery { - #[graphql(complexity = "2 * QUERY_COSTS.storage_read")] + #[graphql(complexity = "2 * query_costs().storage_read")] async fn estimate_gas_price( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/message.rs b/crates/fuel-core/src/schema/message.rs index 05416623934..ed81c058d47 100644 --- a/crates/fuel-core/src/schema/message.rs +++ b/crates/fuel-core/src/schema/message.rs @@ -11,7 +11,7 @@ use super::{ ReadViewProvider, }; use crate::{ - fuel_core_graphql_api::QUERY_COSTS, + fuel_core_graphql_api::query_costs, graphql_api::IntoApiResult, schema::scalars::{ BlockId, @@ -64,7 +64,7 @@ pub struct MessageQuery {} #[Object] impl MessageQuery { - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn message( &self, ctx: &Context<'_>, @@ -76,9 +76,9 @@ impl MessageQuery { } #[graphql(complexity = "{\ - QUERY_COSTS.storage_iterator\ - + (QUERY_COSTS.storage_read + first.unwrap_or_default() as usize) * child_complexity \ - + (QUERY_COSTS.storage_read + last.unwrap_or_default() as usize) * child_complexity\ + query_costs().storage_iterator\ + + (query_costs().storage_read + first.unwrap_or_default() as usize) * child_complexity \ + + (query_costs().storage_read + last.unwrap_or_default() as usize) * child_complexity\ }")] async fn messages( &self, @@ -122,7 +122,7 @@ impl MessageQuery { } // 256 * QUERY_COSTS.storage_read because the depth of the Merkle tree in the worst case is 256 - #[graphql(complexity = "256 * QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "256 * query_costs().storage_read + child_complexity")] async fn message_proof( &self, ctx: &Context<'_>, @@ -153,7 +153,7 @@ impl MessageQuery { .map(MessageProof)) } - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn message_status( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/node_info.rs b/crates/fuel-core/src/schema/node_info.rs index f58e28a1d3b..5805c76dc60 100644 --- a/crates/fuel-core/src/schema/node_info.rs +++ b/crates/fuel-core/src/schema/node_info.rs @@ -3,8 +3,8 @@ use super::scalars::{ U64, }; use crate::fuel_core_graphql_api::{ + query_costs, Config as GraphQLConfig, - QUERY_COSTS, }; use async_graphql::{ Context, @@ -42,7 +42,7 @@ impl NodeInfo { self.node_version.to_owned() } - #[graphql(complexity = "QUERY_COSTS.get_peers + child_complexity")] + #[graphql(complexity = "query_costs().get_peers + child_complexity")] async fn peers(&self, _ctx: &Context<'_>) -> async_graphql::Result> { #[cfg(feature = "p2p")] { @@ -66,7 +66,7 @@ pub struct NodeQuery {} #[Object] impl NodeQuery { - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn node_info(&self, ctx: &Context<'_>) -> async_graphql::Result { let config = ctx.data_unchecked::(); diff --git a/crates/fuel-core/src/schema/relayed_tx.rs b/crates/fuel-core/src/schema/relayed_tx.rs index 850c67cda4b..6ae34b81c07 100644 --- a/crates/fuel-core/src/schema/relayed_tx.rs +++ b/crates/fuel-core/src/schema/relayed_tx.rs @@ -1,5 +1,5 @@ use crate::{ - fuel_core_graphql_api::QUERY_COSTS, + fuel_core_graphql_api::query_costs, schema::{ scalars::{ RelayedTransactionId, @@ -23,7 +23,7 @@ pub struct RelayedTransactionQuery {} #[Object] impl RelayedTransactionQuery { - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn relayed_transaction_status( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/tx.rs b/crates/fuel-core/src/schema/tx.rs index 89b20101db0..4681e7c8d38 100644 --- a/crates/fuel-core/src/schema/tx.rs +++ b/crates/fuel-core/src/schema/tx.rs @@ -6,8 +6,8 @@ use crate::{ ConsensusProvider, TxPool, }, + query_costs, IntoApiResult, - QUERY_COSTS, }, graphql_api::{ database::ReadView, @@ -89,7 +89,7 @@ pub struct TxQuery; #[Object] impl TxQuery { - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn transaction( &self, ctx: &Context<'_>, @@ -111,7 +111,7 @@ impl TxQuery { // We assume that each block has 100 transactions. #[graphql(complexity = "{\ - (QUERY_COSTS.tx_get + child_complexity) \ + (query_costs().tx_get + child_complexity) \ * (first.unwrap_or_default() as usize + last.unwrap_or_default() as usize) }")] async fn transactions( @@ -176,9 +176,9 @@ impl TxQuery { } #[graphql(complexity = "{\ - QUERY_COSTS.storage_iterator\ - + (QUERY_COSTS.storage_read + first.unwrap_or_default() as usize) * child_complexity \ - + (QUERY_COSTS.storage_read + last.unwrap_or_default() as usize) * child_complexity\ + query_costs().storage_iterator\ + + (query_costs().storage_read + first.unwrap_or_default() as usize) * child_complexity \ + + (query_costs().storage_read + last.unwrap_or_default() as usize) * child_complexity\ }")] async fn transactions_by_owner( &self, @@ -219,7 +219,7 @@ impl TxQuery { } /// Estimate the predicate gas for the provided transaction - #[graphql(complexity = "QUERY_COSTS.estimate_predicates + child_complexity")] + #[graphql(complexity = "query_costs().estimate_predicates + child_complexity")] async fn estimate_predicates( &self, ctx: &Context<'_>, @@ -264,7 +264,7 @@ pub struct TxMutation; impl TxMutation { /// Execute a dry-run of multiple transactions using a fork of current state, no changes are committed. #[graphql( - complexity = "QUERY_COSTS.dry_run * txs.len() + child_complexity * txs.len()" + complexity = "query_costs().dry_run * txs.len() + child_complexity * txs.len()" )] async fn dry_run( &self, @@ -316,7 +316,7 @@ impl TxMutation { /// Submits transaction to the `TxPool`. /// /// Returns submitted transaction if the transaction is included in the `TxPool` without problems. - #[graphql(complexity = "QUERY_COSTS.submit + child_complexity")] + #[graphql(complexity = "query_costs().submit + child_complexity")] async fn submit( &self, ctx: &Context<'_>, @@ -356,7 +356,7 @@ impl TxStatusSubscription { /// then the updates arrive. In such a case the stream will close without /// a status. If this occurs the stream can simply be restarted to return /// the latest status. - #[graphql(complexity = "QUERY_COSTS.status_change + child_complexity")] + #[graphql(complexity = "query_costs().status_change + child_complexity")] async fn status_change<'a>( &self, ctx: &'a Context<'a>, @@ -376,7 +376,7 @@ impl TxStatusSubscription { } /// Submits transaction to the `TxPool` and await either confirmation or failure. - #[graphql(complexity = "QUERY_COSTS.submit_and_await + child_complexity")] + #[graphql(complexity = "query_costs().submit_and_await + child_complexity")] async fn submit_and_await<'a>( &self, ctx: &'a Context<'a>, @@ -394,7 +394,7 @@ impl TxStatusSubscription { /// Submits the transaction to the `TxPool` and returns a stream of events. /// Compared to the `submitAndAwait`, the stream also contains ` /// SubmittedStatus` as an intermediate state. - #[graphql(complexity = "QUERY_COSTS.submit_and_await + child_complexity")] + #[graphql(complexity = "query_costs().submit_and_await + child_complexity")] async fn submit_and_await_status<'a>( &self, ctx: &'a Context<'a>, diff --git a/crates/fuel-core/src/schema/tx/types.rs b/crates/fuel-core/src/schema/tx/types.rs index 097d6f06fdd..d237bb817ae 100644 --- a/crates/fuel-core/src/schema/tx/types.rs +++ b/crates/fuel-core/src/schema/tx/types.rs @@ -10,8 +10,8 @@ use crate::{ TxPool, }, database::ReadView, + query_costs, IntoApiResult, - QUERY_COSTS, }, schema::{ block::Block, @@ -177,14 +177,14 @@ impl SuccessStatus { self.block_height.into() } - #[graphql(complexity = "QUERY_COSTS.block_header + child_complexity")] + #[graphql(complexity = "query_costs().block_header + child_complexity")] async fn block(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let block = query.block(&self.block_height)?; Ok(block.into()) } - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn transaction(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let transaction = query.transaction(&self.tx_id)?; @@ -233,14 +233,14 @@ impl FailureStatus { self.block_height.into() } - #[graphql(complexity = "QUERY_COSTS.block_header + child_complexity")] + #[graphql(complexity = "query_costs().block_header + child_complexity")] async fn block(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let block = query.block(&self.block_height)?; Ok(block.into()) } - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn transaction(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let transaction = query.transaction(&self.tx_id)?; @@ -412,7 +412,7 @@ impl Transaction { TransactionId(self.1) } - #[graphql(complexity = "QUERY_COSTS.storage_read")] + #[graphql(complexity = "query_costs().storage_read")] async fn input_asset_ids(&self, ctx: &Context<'_>) -> Option> { let params = ctx .data_unchecked::() @@ -688,7 +688,7 @@ impl Transaction { } } - #[graphql(complexity = "QUERY_COSTS.tx_status_read + child_complexity")] + #[graphql(complexity = "query_costs().tx_status_read + child_complexity")] async fn status( &self, ctx: &Context<'_>, @@ -843,7 +843,7 @@ impl Transaction { } } - #[graphql(complexity = "QUERY_COSTS.tx_raw_payload")] + #[graphql(complexity = "query_costs().tx_raw_payload")] /// Return the transaction bytes using canonical encoding async fn raw_payload(&self) -> HexString { HexString(self.0.clone().to_bytes()) diff --git a/crates/fuel-core/src/schema/upgrades.rs b/crates/fuel-core/src/schema/upgrades.rs index 4df3efddf72..a4591ccd155 100644 --- a/crates/fuel-core/src/schema/upgrades.rs +++ b/crates/fuel-core/src/schema/upgrades.rs @@ -1,8 +1,8 @@ use crate::{ graphql_api::{ api_service::ConsensusProvider, + query_costs, IntoApiResult, - QUERY_COSTS, }, schema::{ chain::ConsensusParameters, @@ -29,7 +29,7 @@ pub struct UpgradeQuery; #[Object] impl UpgradeQuery { - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn consensus_parameters( &self, ctx: &Context<'_>, @@ -42,7 +42,7 @@ impl UpgradeQuery { Ok(ConsensusParameters(params)) } - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn state_transition_bytecode_by_version( &self, ctx: &Context<'_>, @@ -54,7 +54,7 @@ impl UpgradeQuery { .into_api_result() } - #[graphql(complexity = "QUERY_COSTS.storage_read + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn state_transition_bytecode_by_root( &self, root: HexString, @@ -73,7 +73,7 @@ impl StateTransitionBytecode { HexString(self.root.to_vec()) } - #[graphql(complexity = "QUERY_COSTS.state_transition_bytecode_read")] + #[graphql(complexity = "query_costs().state_transition_bytecode_read")] async fn bytecode( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/service/config.rs b/crates/fuel-core/src/service/config.rs index e5473135d6d..18286b24e2c 100644 --- a/crates/fuel-core/src/service/config.rs +++ b/crates/fuel-core/src/service/config.rs @@ -147,6 +147,7 @@ impl Config { request_body_bytes_limit: 16 * 1024 * 1024, query_log_threshold_time: Duration::from_secs(2), api_request_timeout: Duration::from_secs(60), + costs: Default::default(), }, combined_db_config, continue_on_error: false, diff --git a/tests/tests/dos.rs b/tests/tests/dos.rs index b9067738a5d..8f32f09ec0c 100644 --- a/tests/tests/dos.rs +++ b/tests/tests/dos.rs @@ -234,7 +234,7 @@ async fn complex_queries__40_full_blocks__works() { } #[tokio::test] -async fn complex_queries__41_full_block__query_to_complex() { +async fn complex_queries__41_full_block__query_too_complex() { let query = FULL_BLOCK_QUERY.to_string(); let query = query.replace("$NUMBER_OF_BLOCKS", "41"); @@ -245,6 +245,23 @@ async fn complex_queries__41_full_block__query_to_complex() { assert!(result.contains("Query is too complex.")); } +#[tokio::test] +async fn complex_queries__increased_block_cost__query_too_complex() { + let query = FULL_BLOCK_QUERY.to_string(); + let query = query.replace("$NUMBER_OF_BLOCKS", "1"); + + let mut config = Config::local_node(); + config.graphql_config.costs.block_header *= 41; + config.graphql_config.costs.block_transactions *= 41; + config.graphql_config.costs.block_header *= 41; + + let node = FuelService::new_node(config).await.unwrap(); + let url = format!("http://{}/v1/graphql", node.bound_address); + + let result = send_graph_ql_query(&url, query.as_str()).await; + assert!(result.contains("Query is too complex.")); +} + #[tokio::test] async fn complex_queries__100_block_headers__works() { let query = r#" From ceb43f615850f960adea78f6482d6d8451b268f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Blankfors?= Date: Mon, 14 Oct 2024 09:19:35 +0200 Subject: [PATCH 2/5] refactor: Address review comments * Move query costs initialization function * Feature gate default implementation on "test-helpers" feature * Only set block header query cost in test --- crates/fuel-core/src/graphql_api.rs | 5 +++++ crates/fuel-core/src/graphql_api/api_service.rs | 9 +++------ tests/tests/dos.rs | 7 +++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/fuel-core/src/graphql_api.rs b/crates/fuel-core/src/graphql_api.rs index bf97ec541f8..5bc5e633275 100644 --- a/crates/fuel-core/src/graphql_api.rs +++ b/crates/fuel-core/src/graphql_api.rs @@ -58,6 +58,7 @@ pub struct Costs { pub da_compressed_block_read: usize, } +#[cfg(feature = "test-helpers")] impl Default for Costs { fn default() -> Self { DEFAULT_QUERY_COSTS @@ -92,6 +93,10 @@ pub fn query_costs() -> &'static Costs { pub static QUERY_COSTS: OnceLock = OnceLock::new(); +fn initialize_query_costs(config: Config) { + QUERY_COSTS.get_or_init(|| config.config.costs); +} + #[derive(Clone, Debug)] pub struct Config { pub config: ServiceConfig, diff --git a/crates/fuel-core/src/graphql_api/api_service.rs b/crates/fuel-core/src/graphql_api/api_service.rs index 296005ca16d..d09e26a9e54 100644 --- a/crates/fuel-core/src/graphql_api/api_service.rs +++ b/crates/fuel-core/src/graphql_api/api_service.rs @@ -15,7 +15,7 @@ use crate::{ view_extension::ViewExtension, Config, }, - graphql_api::QUERY_COSTS, + graphql_api, schema::{ CoreSchema, CoreSchemaBuilder, @@ -221,7 +221,8 @@ where OnChain::LatestView: OnChainDatabase, OffChain::LatestView: OffChainDatabase, { - initialize_query_costs(config.clone()); + graphql_api::initialize_query_costs(config.clone()); + let network_addr = config.config.addr; let combined_read_database = ReadDatabase::new(genesis_block_height, on_database, off_database); @@ -298,10 +299,6 @@ where )) } -fn initialize_query_costs(config: Config) { - QUERY_COSTS.get_or_init(|| config.config.costs); -} - async fn graphql_playground() -> impl IntoResponse { Html(playground_source(GraphQLPlaygroundConfig::new( "/v1/graphql", diff --git a/tests/tests/dos.rs b/tests/tests/dos.rs index 8f32f09ec0c..5b001e08377 100644 --- a/tests/tests/dos.rs +++ b/tests/tests/dos.rs @@ -246,14 +246,13 @@ async fn complex_queries__41_full_block__query_too_complex() { } #[tokio::test] -async fn complex_queries__increased_block_cost__query_too_complex() { +async fn complex_queries__increased_block_header_cost__query_too_complex() { let query = FULL_BLOCK_QUERY.to_string(); let query = query.replace("$NUMBER_OF_BLOCKS", "1"); let mut config = Config::local_node(); - config.graphql_config.costs.block_header *= 41; - config.graphql_config.costs.block_transactions *= 41; - config.graphql_config.costs.block_header *= 41; + config.graphql_config.costs.block_header = + config.graphql_config.max_queries_complexity; let node = FuelService::new_node(config).await.unwrap(); let url = format!("http://{}/v1/graphql", node.bound_address); From bc09dc44173736eaa82fd0a08f66f790153445f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Blankfors?= Date: Mon, 14 Oct 2024 11:15:00 +0200 Subject: [PATCH 3/5] fix: Use RwLock instead of OnceLock to allow writing new values (e.g. for tests) --- crates/fuel-core/src/graphql_api.rs | 12 ++++---- .../fuel-core/src/graphql_api/api_service.rs | 2 +- crates/fuel-core/src/schema/balance.rs | 4 +-- crates/fuel-core/src/schema/blob.rs | 4 +-- crates/fuel-core/src/schema/block.rs | 16 ++++++----- crates/fuel-core/src/schema/chain.rs | 10 +++---- crates/fuel-core/src/schema/coins.rs | 12 ++++---- crates/fuel-core/src/schema/contract.rs | 14 +++++----- crates/fuel-core/src/schema/da_compressed.rs | 2 +- crates/fuel-core/src/schema/gas_price.rs | 4 +-- crates/fuel-core/src/schema/message.rs | 14 ++++++---- crates/fuel-core/src/schema/node_info.rs | 4 +-- crates/fuel-core/src/schema/relayed_tx.rs | 2 +- crates/fuel-core/src/schema/tx.rs | 28 +++++++++++-------- crates/fuel-core/src/schema/tx/types.rs | 16 ++++++----- crates/fuel-core/src/schema/upgrades.rs | 8 +++--- 16 files changed, 82 insertions(+), 70 deletions(-) diff --git a/crates/fuel-core/src/graphql_api.rs b/crates/fuel-core/src/graphql_api.rs index 5bc5e633275..ef19cdee029 100644 --- a/crates/fuel-core/src/graphql_api.rs +++ b/crates/fuel-core/src/graphql_api.rs @@ -4,7 +4,7 @@ use fuel_core_storage::{ }; use std::{ net::SocketAddr, - sync::OnceLock, + sync::RwLock, time::Duration, }; @@ -87,14 +87,14 @@ pub const DEFAULT_QUERY_COSTS: Costs = Costs { da_compressed_block_read: 4000, }; -pub fn query_costs() -> &'static Costs { - QUERY_COSTS.get().unwrap_or(&DEFAULT_QUERY_COSTS) +pub fn query_costs(extract_field: impl FnOnce(&Costs) -> usize) -> usize { + extract_field(&QUERY_COSTS.read().unwrap()) } -pub static QUERY_COSTS: OnceLock = OnceLock::new(); +static QUERY_COSTS: RwLock = RwLock::new(DEFAULT_QUERY_COSTS); -fn initialize_query_costs(config: Config) { - QUERY_COSTS.get_or_init(|| config.config.costs); +fn initialize_query_costs(costs: Costs) { + *QUERY_COSTS.write().unwrap() = costs; } #[derive(Clone, Debug)] diff --git a/crates/fuel-core/src/graphql_api/api_service.rs b/crates/fuel-core/src/graphql_api/api_service.rs index d09e26a9e54..00ba44a8202 100644 --- a/crates/fuel-core/src/graphql_api/api_service.rs +++ b/crates/fuel-core/src/graphql_api/api_service.rs @@ -221,7 +221,7 @@ where OnChain::LatestView: OnChainDatabase, OffChain::LatestView: OffChainDatabase, { - graphql_api::initialize_query_costs(config.clone()); + graphql_api::initialize_query_costs(config.config.costs.clone()); let network_addr = config.config.addr; let combined_read_database = diff --git a/crates/fuel-core/src/schema/balance.rs b/crates/fuel-core/src/schema/balance.rs index 98b40c3963c..38f2804c7e4 100644 --- a/crates/fuel-core/src/schema/balance.rs +++ b/crates/fuel-core/src/schema/balance.rs @@ -52,7 +52,7 @@ pub struct BalanceQuery; #[Object] impl BalanceQuery { - #[graphql(complexity = "query_costs().balance_query")] + #[graphql(complexity = "query_costs(|costs| costs.balance_query)")] async fn balance( &self, ctx: &Context<'_>, @@ -70,7 +70,7 @@ impl BalanceQuery { // TODO: This API should be migrated to the indexer for better support and // discontinued within fuel-core. - #[graphql(complexity = "query_costs().balance_query")] + #[graphql(complexity = "query_costs(|costs| costs.balance_query)")] async fn balances( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/blob.rs b/crates/fuel-core/src/schema/blob.rs index 76cc32530cc..59b6972d04c 100644 --- a/crates/fuel-core/src/schema/blob.rs +++ b/crates/fuel-core/src/schema/blob.rs @@ -27,7 +27,7 @@ impl Blob { self.0.into() } - #[graphql(complexity = "query_costs().bytecode_read")] + #[graphql(complexity = "query_costs(|costs| costs.bytecode_read)")] async fn bytecode(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; query @@ -48,7 +48,7 @@ pub struct BlobQuery; #[Object] impl BlobQuery { - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn blob( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/block.rs b/crates/fuel-core/src/schema/block.rs index 5878a1defb1..a406ab73422 100644 --- a/crates/fuel-core/src/schema/block.rs +++ b/crates/fuel-core/src/schema/block.rs @@ -112,14 +112,14 @@ impl Block { self.0.header().clone().into() } - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn consensus(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let height = self.0.header().height(); Ok(query.consensus(height)?.try_into()?) } - #[graphql(complexity = "query_costs().block_transactions_ids")] + #[graphql(complexity = "query_costs(|costs| costs.block_transactions_ids)")] async fn transaction_ids(&self) -> Vec { self.0 .transactions() @@ -129,7 +129,9 @@ impl Block { } // Assume that in average we have 32 transactions per block. - #[graphql(complexity = "query_costs().block_transactions + child_complexity")] + #[graphql( + complexity = "query_costs(|costs| costs.block_transactions) + child_complexity" + )] async fn transactions( &self, ctx: &Context<'_>, @@ -240,7 +242,7 @@ pub struct BlockQuery; #[Object] impl BlockQuery { - #[graphql(complexity = "query_costs().block_header + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.block_header) + child_complexity")] async fn block( &self, ctx: &Context<'_>, @@ -270,7 +272,7 @@ impl BlockQuery { } #[graphql(complexity = "{\ - (query_costs().block_header + child_complexity) \ + (query_costs(|costs| costs.block_header) + child_complexity) \ * (first.unwrap_or_default() as usize + last.unwrap_or_default() as usize) \ }")] async fn blocks( @@ -298,7 +300,7 @@ pub struct HeaderQuery; #[Object] impl HeaderQuery { - #[graphql(complexity = "query_costs().block_header + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.block_header) + child_complexity")] async fn header( &self, ctx: &Context<'_>, @@ -312,7 +314,7 @@ impl HeaderQuery { } #[graphql(complexity = "{\ - (query_costs().block_header + child_complexity) \ + (query_costs(|costs| costs.block_header) + child_complexity) \ * (first.unwrap_or_default() as usize + last.unwrap_or_default() as usize) \ }")] async fn headers( diff --git a/crates/fuel-core/src/schema/chain.rs b/crates/fuel-core/src/schema/chain.rs index 9acdbb01940..d8462b0deb6 100644 --- a/crates/fuel-core/src/schema/chain.rs +++ b/crates/fuel-core/src/schema/chain.rs @@ -775,13 +775,13 @@ impl HeavyOperation { #[Object] impl ChainInfo { - #[graphql(complexity = "query_costs().storage_read")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read)")] async fn name(&self, ctx: &Context<'_>) -> async_graphql::Result { let config: &Config = ctx.data_unchecked(); Ok(config.chain_name.clone()) } - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn latest_block(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; @@ -789,7 +789,7 @@ impl ChainInfo { Ok(latest_block) } - #[graphql(complexity = "query_costs().storage_read")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read)")] async fn da_height(&self, ctx: &Context<'_>) -> U64 { let Ok(query) = ctx.read_view() else { return 0.into(); @@ -798,7 +798,7 @@ impl ChainInfo { query.da_height().unwrap_or_default().0.into() } - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn consensus_parameters( &self, ctx: &Context<'_>, @@ -810,7 +810,7 @@ impl ChainInfo { Ok(ConsensusParameters(params)) } - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn gas_costs(&self, ctx: &Context<'_>) -> async_graphql::Result { let params = ctx .data_unchecked::() diff --git a/crates/fuel-core/src/schema/coins.rs b/crates/fuel-core/src/schema/coins.rs index 648595c65d6..f1a549a07d7 100644 --- a/crates/fuel-core/src/schema/coins.rs +++ b/crates/fuel-core/src/schema/coins.rs @@ -92,7 +92,7 @@ impl MessageCoin { self.0.amount.into() } - #[graphql(complexity = "query_costs().storage_read")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read)")] async fn asset_id(&self, ctx: &Context<'_>) -> AssetId { let params = ctx .data_unchecked::() @@ -148,7 +148,7 @@ pub struct CoinQuery; #[async_graphql::Object] impl CoinQuery { /// Gets the coin by `utxo_id`. - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn coin( &self, ctx: &Context<'_>, @@ -160,9 +160,9 @@ impl CoinQuery { /// Gets all unspent coins of some `owner` maybe filtered with by `asset_id` per page. #[graphql(complexity = "{\ - query_costs().storage_iterator\ - + (query_costs().storage_read + first.unwrap_or_default() as usize) * child_complexity \ - + (query_costs().storage_read + last.unwrap_or_default() as usize) * child_complexity\ + query_costs(|costs| costs.storage_iterator)\ + + (query_costs(|costs| costs.storage_read) + first.unwrap_or_default() as usize) * child_complexity \ + + (query_costs(|costs| costs.storage_read) + last.unwrap_or_default() as usize) * child_complexity\ }")] async fn coins( &self, @@ -205,7 +205,7 @@ impl CoinQuery { /// The list of spendable coins per asset from the query. The length of the result is /// the same as the length of `query_per_asset`. The ordering of assets and `query_per_asset` /// is the same. - #[graphql(complexity = "query_costs().coins_to_spend")] + #[graphql(complexity = "query_costs(|costs| costs.coins_to_spend)")] async fn coins_to_spend( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/contract.rs b/crates/fuel-core/src/schema/contract.rs index ec7d260f79f..8ce52bbab73 100644 --- a/crates/fuel-core/src/schema/contract.rs +++ b/crates/fuel-core/src/schema/contract.rs @@ -46,7 +46,7 @@ impl Contract { self.0.into() } - #[graphql(complexity = "query_costs().bytecode_read")] + #[graphql(complexity = "query_costs(|costs| costs.bytecode_read)")] async fn bytecode(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; query @@ -55,7 +55,7 @@ impl Contract { .map_err(Into::into) } - #[graphql(complexity = "query_costs().storage_read")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read)")] async fn salt(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; query @@ -70,7 +70,7 @@ pub struct ContractQuery; #[Object] impl ContractQuery { - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn contract( &self, ctx: &Context<'_>, @@ -118,7 +118,7 @@ pub struct ContractBalanceQuery; #[Object] impl ContractBalanceQuery { - #[graphql(complexity = "query_costs().storage_read")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read)")] async fn contract_balance( &self, ctx: &Context<'_>, @@ -144,9 +144,9 @@ impl ContractBalanceQuery { } #[graphql(complexity = "{\ - query_costs().storage_iterator\ - + (query_costs().storage_read + first.unwrap_or_default() as usize) * child_complexity \ - + (query_costs().storage_read + last.unwrap_or_default() as usize) * child_complexity\ + query_costs(|costs| costs.storage_iterator)\ + + (query_costs(|costs| costs.storage_read) + first.unwrap_or_default() as usize) * child_complexity\ + + (query_costs(|costs| costs.storage_read) + last.unwrap_or_default() as usize) * child_complexity\ }")] async fn contract_balances( &self, diff --git a/crates/fuel-core/src/schema/da_compressed.rs b/crates/fuel-core/src/schema/da_compressed.rs index 2f2939be83f..d0dcd87eafd 100644 --- a/crates/fuel-core/src/schema/da_compressed.rs +++ b/crates/fuel-core/src/schema/da_compressed.rs @@ -36,7 +36,7 @@ pub struct DaCompressedBlockQuery; #[Object] impl DaCompressedBlockQuery { - #[graphql(complexity = "query_costs().da_compressed_block_read")] + #[graphql(complexity = "query_costs(|costs| costs.da_compressed_block_read)")] async fn da_compressed_block( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/gas_price.rs b/crates/fuel-core/src/schema/gas_price.rs index 7f7c7555e6d..64162c6c124 100644 --- a/crates/fuel-core/src/schema/gas_price.rs +++ b/crates/fuel-core/src/schema/gas_price.rs @@ -42,7 +42,7 @@ pub struct LatestGasPriceQuery {} #[Object] impl LatestGasPriceQuery { - #[graphql(complexity = "query_costs().block_header")] + #[graphql(complexity = "query_costs(|costs| costs.block_header)")] async fn latest_gas_price( &self, ctx: &Context<'_>, @@ -80,7 +80,7 @@ pub struct EstimateGasPriceQuery {} #[Object] impl EstimateGasPriceQuery { - #[graphql(complexity = "2 * query_costs().storage_read")] + #[graphql(complexity = "2 * query_costs(|costs| costs.storage_read)")] async fn estimate_gas_price( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/message.rs b/crates/fuel-core/src/schema/message.rs index ed81c058d47..8418ed98b8f 100644 --- a/crates/fuel-core/src/schema/message.rs +++ b/crates/fuel-core/src/schema/message.rs @@ -64,7 +64,7 @@ pub struct MessageQuery {} #[Object] impl MessageQuery { - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn message( &self, ctx: &Context<'_>, @@ -76,9 +76,9 @@ impl MessageQuery { } #[graphql(complexity = "{\ - query_costs().storage_iterator\ - + (query_costs().storage_read + first.unwrap_or_default() as usize) * child_complexity \ - + (query_costs().storage_read + last.unwrap_or_default() as usize) * child_complexity\ + query_costs(|costs| costs.storage_iterator)\ + + (query_costs(|costs| costs.storage_read) + first.unwrap_or_default() as usize) * child_complexity \ + + (query_costs(|costs| costs.storage_read) + last.unwrap_or_default() as usize) * child_complexity\ }")] async fn messages( &self, @@ -122,7 +122,9 @@ impl MessageQuery { } // 256 * QUERY_COSTS.storage_read because the depth of the Merkle tree in the worst case is 256 - #[graphql(complexity = "256 * query_costs().storage_read + child_complexity")] + #[graphql( + complexity = "256 * query_costs(|costs| costs.storage_read) + child_complexity" + )] async fn message_proof( &self, ctx: &Context<'_>, @@ -153,7 +155,7 @@ impl MessageQuery { .map(MessageProof)) } - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn message_status( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/node_info.rs b/crates/fuel-core/src/schema/node_info.rs index 5805c76dc60..4ebda8fad46 100644 --- a/crates/fuel-core/src/schema/node_info.rs +++ b/crates/fuel-core/src/schema/node_info.rs @@ -42,7 +42,7 @@ impl NodeInfo { self.node_version.to_owned() } - #[graphql(complexity = "query_costs().get_peers + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.get_peers) + child_complexity")] async fn peers(&self, _ctx: &Context<'_>) -> async_graphql::Result> { #[cfg(feature = "p2p")] { @@ -66,7 +66,7 @@ pub struct NodeQuery {} #[Object] impl NodeQuery { - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn node_info(&self, ctx: &Context<'_>) -> async_graphql::Result { let config = ctx.data_unchecked::(); diff --git a/crates/fuel-core/src/schema/relayed_tx.rs b/crates/fuel-core/src/schema/relayed_tx.rs index 6ae34b81c07..22486ee148c 100644 --- a/crates/fuel-core/src/schema/relayed_tx.rs +++ b/crates/fuel-core/src/schema/relayed_tx.rs @@ -23,7 +23,7 @@ pub struct RelayedTransactionQuery {} #[Object] impl RelayedTransactionQuery { - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn relayed_transaction_status( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/tx.rs b/crates/fuel-core/src/schema/tx.rs index 4681e7c8d38..11ec223c34c 100644 --- a/crates/fuel-core/src/schema/tx.rs +++ b/crates/fuel-core/src/schema/tx.rs @@ -89,7 +89,7 @@ pub struct TxQuery; #[Object] impl TxQuery { - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn transaction( &self, ctx: &Context<'_>, @@ -111,7 +111,7 @@ impl TxQuery { // We assume that each block has 100 transactions. #[graphql(complexity = "{\ - (query_costs().tx_get + child_complexity) \ + (query_costs(|costs| costs.tx_get) + child_complexity) \ * (first.unwrap_or_default() as usize + last.unwrap_or_default() as usize) }")] async fn transactions( @@ -176,9 +176,9 @@ impl TxQuery { } #[graphql(complexity = "{\ - query_costs().storage_iterator\ - + (query_costs().storage_read + first.unwrap_or_default() as usize) * child_complexity \ - + (query_costs().storage_read + last.unwrap_or_default() as usize) * child_complexity\ + query_costs(|costs| costs.storage_iterator)\ + + (query_costs(|costs| costs.storage_read) + first.unwrap_or_default() as usize) * child_complexity \ + + (query_costs(|costs| costs.storage_read) + last.unwrap_or_default() as usize) * child_complexity\ }")] async fn transactions_by_owner( &self, @@ -219,7 +219,9 @@ impl TxQuery { } /// Estimate the predicate gas for the provided transaction - #[graphql(complexity = "query_costs().estimate_predicates + child_complexity")] + #[graphql( + complexity = "query_costs(|costs| costs.estimate_predicates) + child_complexity" + )] async fn estimate_predicates( &self, ctx: &Context<'_>, @@ -264,7 +266,7 @@ pub struct TxMutation; impl TxMutation { /// Execute a dry-run of multiple transactions using a fork of current state, no changes are committed. #[graphql( - complexity = "query_costs().dry_run * txs.len() + child_complexity * txs.len()" + complexity = "query_costs(|costs| costs.dry_run) * txs.len() + child_complexity * txs.len()" )] async fn dry_run( &self, @@ -316,7 +318,7 @@ impl TxMutation { /// Submits transaction to the `TxPool`. /// /// Returns submitted transaction if the transaction is included in the `TxPool` without problems. - #[graphql(complexity = "query_costs().submit + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.submit) + child_complexity")] async fn submit( &self, ctx: &Context<'_>, @@ -356,7 +358,7 @@ impl TxStatusSubscription { /// then the updates arrive. In such a case the stream will close without /// a status. If this occurs the stream can simply be restarted to return /// the latest status. - #[graphql(complexity = "query_costs().status_change + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.status_change) + child_complexity")] async fn status_change<'a>( &self, ctx: &'a Context<'a>, @@ -376,7 +378,9 @@ impl TxStatusSubscription { } /// Submits transaction to the `TxPool` and await either confirmation or failure. - #[graphql(complexity = "query_costs().submit_and_await + child_complexity")] + #[graphql( + complexity = "query_costs(|costs| costs.submit_and_await) + child_complexity" + )] async fn submit_and_await<'a>( &self, ctx: &'a Context<'a>, @@ -394,7 +398,9 @@ impl TxStatusSubscription { /// Submits the transaction to the `TxPool` and returns a stream of events. /// Compared to the `submitAndAwait`, the stream also contains ` /// SubmittedStatus` as an intermediate state. - #[graphql(complexity = "query_costs().submit_and_await + child_complexity")] + #[graphql( + complexity = "query_costs(|costs| costs.submit_and_await) + child_complexity" + )] async fn submit_and_await_status<'a>( &self, ctx: &'a Context<'a>, diff --git a/crates/fuel-core/src/schema/tx/types.rs b/crates/fuel-core/src/schema/tx/types.rs index d237bb817ae..9a60ac3ba0e 100644 --- a/crates/fuel-core/src/schema/tx/types.rs +++ b/crates/fuel-core/src/schema/tx/types.rs @@ -177,14 +177,14 @@ impl SuccessStatus { self.block_height.into() } - #[graphql(complexity = "query_costs().block_header + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.block_header) + child_complexity")] async fn block(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let block = query.block(&self.block_height)?; Ok(block.into()) } - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn transaction(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let transaction = query.transaction(&self.tx_id)?; @@ -233,14 +233,14 @@ impl FailureStatus { self.block_height.into() } - #[graphql(complexity = "query_costs().block_header + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.block_header) + child_complexity")] async fn block(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let block = query.block(&self.block_height)?; Ok(block.into()) } - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn transaction(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let transaction = query.transaction(&self.tx_id)?; @@ -412,7 +412,7 @@ impl Transaction { TransactionId(self.1) } - #[graphql(complexity = "query_costs().storage_read")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read)")] async fn input_asset_ids(&self, ctx: &Context<'_>) -> Option> { let params = ctx .data_unchecked::() @@ -688,7 +688,9 @@ impl Transaction { } } - #[graphql(complexity = "query_costs().tx_status_read + child_complexity")] + #[graphql( + complexity = "query_costs(|costs| costs.tx_status_read) + child_complexity" + )] async fn status( &self, ctx: &Context<'_>, @@ -843,7 +845,7 @@ impl Transaction { } } - #[graphql(complexity = "query_costs().tx_raw_payload")] + #[graphql(complexity = "query_costs(|costs| costs.tx_raw_payload)")] /// Return the transaction bytes using canonical encoding async fn raw_payload(&self) -> HexString { HexString(self.0.clone().to_bytes()) diff --git a/crates/fuel-core/src/schema/upgrades.rs b/crates/fuel-core/src/schema/upgrades.rs index a4591ccd155..045c88d4288 100644 --- a/crates/fuel-core/src/schema/upgrades.rs +++ b/crates/fuel-core/src/schema/upgrades.rs @@ -29,7 +29,7 @@ pub struct UpgradeQuery; #[Object] impl UpgradeQuery { - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn consensus_parameters( &self, ctx: &Context<'_>, @@ -42,7 +42,7 @@ impl UpgradeQuery { Ok(ConsensusParameters(params)) } - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn state_transition_bytecode_by_version( &self, ctx: &Context<'_>, @@ -54,7 +54,7 @@ impl UpgradeQuery { .into_api_result() } - #[graphql(complexity = "query_costs().storage_read + child_complexity")] + #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] async fn state_transition_bytecode_by_root( &self, root: HexString, @@ -73,7 +73,7 @@ impl StateTransitionBytecode { HexString(self.root.to_vec()) } - #[graphql(complexity = "query_costs().state_transition_bytecode_read")] + #[graphql(complexity = "query_costs(|costs| costs.state_transition_bytecode_read)")] async fn bytecode( &self, ctx: &Context<'_>, From 93dd8319a317e0c85faf97c152eeb105401504f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Blankfors?= Date: Mon, 14 Oct 2024 13:36:16 +0200 Subject: [PATCH 4/5] Revert "fix: Use RwLock instead of OnceLock to allow writing new values (e.g. for tests)" This reverts commit 35c7214b4ef60874d8ef8aec1db7c69baf29878f. --- crates/fuel-core/src/graphql_api.rs | 12 ++++---- .../fuel-core/src/graphql_api/api_service.rs | 2 +- crates/fuel-core/src/schema/balance.rs | 4 +-- crates/fuel-core/src/schema/blob.rs | 4 +-- crates/fuel-core/src/schema/block.rs | 16 +++++------ crates/fuel-core/src/schema/chain.rs | 10 +++---- crates/fuel-core/src/schema/coins.rs | 12 ++++---- crates/fuel-core/src/schema/contract.rs | 14 +++++----- crates/fuel-core/src/schema/da_compressed.rs | 2 +- crates/fuel-core/src/schema/gas_price.rs | 4 +-- crates/fuel-core/src/schema/message.rs | 14 ++++------ crates/fuel-core/src/schema/node_info.rs | 4 +-- crates/fuel-core/src/schema/relayed_tx.rs | 2 +- crates/fuel-core/src/schema/tx.rs | 28 ++++++++----------- crates/fuel-core/src/schema/tx/types.rs | 16 +++++------ crates/fuel-core/src/schema/upgrades.rs | 8 +++--- 16 files changed, 70 insertions(+), 82 deletions(-) diff --git a/crates/fuel-core/src/graphql_api.rs b/crates/fuel-core/src/graphql_api.rs index ef19cdee029..5bc5e633275 100644 --- a/crates/fuel-core/src/graphql_api.rs +++ b/crates/fuel-core/src/graphql_api.rs @@ -4,7 +4,7 @@ use fuel_core_storage::{ }; use std::{ net::SocketAddr, - sync::RwLock, + sync::OnceLock, time::Duration, }; @@ -87,14 +87,14 @@ pub const DEFAULT_QUERY_COSTS: Costs = Costs { da_compressed_block_read: 4000, }; -pub fn query_costs(extract_field: impl FnOnce(&Costs) -> usize) -> usize { - extract_field(&QUERY_COSTS.read().unwrap()) +pub fn query_costs() -> &'static Costs { + QUERY_COSTS.get().unwrap_or(&DEFAULT_QUERY_COSTS) } -static QUERY_COSTS: RwLock = RwLock::new(DEFAULT_QUERY_COSTS); +pub static QUERY_COSTS: OnceLock = OnceLock::new(); -fn initialize_query_costs(costs: Costs) { - *QUERY_COSTS.write().unwrap() = costs; +fn initialize_query_costs(config: Config) { + QUERY_COSTS.get_or_init(|| config.config.costs); } #[derive(Clone, Debug)] diff --git a/crates/fuel-core/src/graphql_api/api_service.rs b/crates/fuel-core/src/graphql_api/api_service.rs index 00ba44a8202..d09e26a9e54 100644 --- a/crates/fuel-core/src/graphql_api/api_service.rs +++ b/crates/fuel-core/src/graphql_api/api_service.rs @@ -221,7 +221,7 @@ where OnChain::LatestView: OnChainDatabase, OffChain::LatestView: OffChainDatabase, { - graphql_api::initialize_query_costs(config.config.costs.clone()); + graphql_api::initialize_query_costs(config.clone()); let network_addr = config.config.addr; let combined_read_database = diff --git a/crates/fuel-core/src/schema/balance.rs b/crates/fuel-core/src/schema/balance.rs index 38f2804c7e4..98b40c3963c 100644 --- a/crates/fuel-core/src/schema/balance.rs +++ b/crates/fuel-core/src/schema/balance.rs @@ -52,7 +52,7 @@ pub struct BalanceQuery; #[Object] impl BalanceQuery { - #[graphql(complexity = "query_costs(|costs| costs.balance_query)")] + #[graphql(complexity = "query_costs().balance_query")] async fn balance( &self, ctx: &Context<'_>, @@ -70,7 +70,7 @@ impl BalanceQuery { // TODO: This API should be migrated to the indexer for better support and // discontinued within fuel-core. - #[graphql(complexity = "query_costs(|costs| costs.balance_query)")] + #[graphql(complexity = "query_costs().balance_query")] async fn balances( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/blob.rs b/crates/fuel-core/src/schema/blob.rs index 59b6972d04c..76cc32530cc 100644 --- a/crates/fuel-core/src/schema/blob.rs +++ b/crates/fuel-core/src/schema/blob.rs @@ -27,7 +27,7 @@ impl Blob { self.0.into() } - #[graphql(complexity = "query_costs(|costs| costs.bytecode_read)")] + #[graphql(complexity = "query_costs().bytecode_read")] async fn bytecode(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; query @@ -48,7 +48,7 @@ pub struct BlobQuery; #[Object] impl BlobQuery { - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn blob( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/block.rs b/crates/fuel-core/src/schema/block.rs index a406ab73422..5878a1defb1 100644 --- a/crates/fuel-core/src/schema/block.rs +++ b/crates/fuel-core/src/schema/block.rs @@ -112,14 +112,14 @@ impl Block { self.0.header().clone().into() } - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn consensus(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let height = self.0.header().height(); Ok(query.consensus(height)?.try_into()?) } - #[graphql(complexity = "query_costs(|costs| costs.block_transactions_ids)")] + #[graphql(complexity = "query_costs().block_transactions_ids")] async fn transaction_ids(&self) -> Vec { self.0 .transactions() @@ -129,9 +129,7 @@ impl Block { } // Assume that in average we have 32 transactions per block. - #[graphql( - complexity = "query_costs(|costs| costs.block_transactions) + child_complexity" - )] + #[graphql(complexity = "query_costs().block_transactions + child_complexity")] async fn transactions( &self, ctx: &Context<'_>, @@ -242,7 +240,7 @@ pub struct BlockQuery; #[Object] impl BlockQuery { - #[graphql(complexity = "query_costs(|costs| costs.block_header) + child_complexity")] + #[graphql(complexity = "query_costs().block_header + child_complexity")] async fn block( &self, ctx: &Context<'_>, @@ -272,7 +270,7 @@ impl BlockQuery { } #[graphql(complexity = "{\ - (query_costs(|costs| costs.block_header) + child_complexity) \ + (query_costs().block_header + child_complexity) \ * (first.unwrap_or_default() as usize + last.unwrap_or_default() as usize) \ }")] async fn blocks( @@ -300,7 +298,7 @@ pub struct HeaderQuery; #[Object] impl HeaderQuery { - #[graphql(complexity = "query_costs(|costs| costs.block_header) + child_complexity")] + #[graphql(complexity = "query_costs().block_header + child_complexity")] async fn header( &self, ctx: &Context<'_>, @@ -314,7 +312,7 @@ impl HeaderQuery { } #[graphql(complexity = "{\ - (query_costs(|costs| costs.block_header) + child_complexity) \ + (query_costs().block_header + child_complexity) \ * (first.unwrap_or_default() as usize + last.unwrap_or_default() as usize) \ }")] async fn headers( diff --git a/crates/fuel-core/src/schema/chain.rs b/crates/fuel-core/src/schema/chain.rs index d8462b0deb6..9acdbb01940 100644 --- a/crates/fuel-core/src/schema/chain.rs +++ b/crates/fuel-core/src/schema/chain.rs @@ -775,13 +775,13 @@ impl HeavyOperation { #[Object] impl ChainInfo { - #[graphql(complexity = "query_costs(|costs| costs.storage_read)")] + #[graphql(complexity = "query_costs().storage_read")] async fn name(&self, ctx: &Context<'_>) -> async_graphql::Result { let config: &Config = ctx.data_unchecked(); Ok(config.chain_name.clone()) } - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn latest_block(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; @@ -789,7 +789,7 @@ impl ChainInfo { Ok(latest_block) } - #[graphql(complexity = "query_costs(|costs| costs.storage_read)")] + #[graphql(complexity = "query_costs().storage_read")] async fn da_height(&self, ctx: &Context<'_>) -> U64 { let Ok(query) = ctx.read_view() else { return 0.into(); @@ -798,7 +798,7 @@ impl ChainInfo { query.da_height().unwrap_or_default().0.into() } - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn consensus_parameters( &self, ctx: &Context<'_>, @@ -810,7 +810,7 @@ impl ChainInfo { Ok(ConsensusParameters(params)) } - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn gas_costs(&self, ctx: &Context<'_>) -> async_graphql::Result { let params = ctx .data_unchecked::() diff --git a/crates/fuel-core/src/schema/coins.rs b/crates/fuel-core/src/schema/coins.rs index f1a549a07d7..648595c65d6 100644 --- a/crates/fuel-core/src/schema/coins.rs +++ b/crates/fuel-core/src/schema/coins.rs @@ -92,7 +92,7 @@ impl MessageCoin { self.0.amount.into() } - #[graphql(complexity = "query_costs(|costs| costs.storage_read)")] + #[graphql(complexity = "query_costs().storage_read")] async fn asset_id(&self, ctx: &Context<'_>) -> AssetId { let params = ctx .data_unchecked::() @@ -148,7 +148,7 @@ pub struct CoinQuery; #[async_graphql::Object] impl CoinQuery { /// Gets the coin by `utxo_id`. - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn coin( &self, ctx: &Context<'_>, @@ -160,9 +160,9 @@ impl CoinQuery { /// Gets all unspent coins of some `owner` maybe filtered with by `asset_id` per page. #[graphql(complexity = "{\ - query_costs(|costs| costs.storage_iterator)\ - + (query_costs(|costs| costs.storage_read) + first.unwrap_or_default() as usize) * child_complexity \ - + (query_costs(|costs| costs.storage_read) + last.unwrap_or_default() as usize) * child_complexity\ + query_costs().storage_iterator\ + + (query_costs().storage_read + first.unwrap_or_default() as usize) * child_complexity \ + + (query_costs().storage_read + last.unwrap_or_default() as usize) * child_complexity\ }")] async fn coins( &self, @@ -205,7 +205,7 @@ impl CoinQuery { /// The list of spendable coins per asset from the query. The length of the result is /// the same as the length of `query_per_asset`. The ordering of assets and `query_per_asset` /// is the same. - #[graphql(complexity = "query_costs(|costs| costs.coins_to_spend)")] + #[graphql(complexity = "query_costs().coins_to_spend")] async fn coins_to_spend( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/contract.rs b/crates/fuel-core/src/schema/contract.rs index 8ce52bbab73..ec7d260f79f 100644 --- a/crates/fuel-core/src/schema/contract.rs +++ b/crates/fuel-core/src/schema/contract.rs @@ -46,7 +46,7 @@ impl Contract { self.0.into() } - #[graphql(complexity = "query_costs(|costs| costs.bytecode_read)")] + #[graphql(complexity = "query_costs().bytecode_read")] async fn bytecode(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; query @@ -55,7 +55,7 @@ impl Contract { .map_err(Into::into) } - #[graphql(complexity = "query_costs(|costs| costs.storage_read)")] + #[graphql(complexity = "query_costs().storage_read")] async fn salt(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; query @@ -70,7 +70,7 @@ pub struct ContractQuery; #[Object] impl ContractQuery { - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn contract( &self, ctx: &Context<'_>, @@ -118,7 +118,7 @@ pub struct ContractBalanceQuery; #[Object] impl ContractBalanceQuery { - #[graphql(complexity = "query_costs(|costs| costs.storage_read)")] + #[graphql(complexity = "query_costs().storage_read")] async fn contract_balance( &self, ctx: &Context<'_>, @@ -144,9 +144,9 @@ impl ContractBalanceQuery { } #[graphql(complexity = "{\ - query_costs(|costs| costs.storage_iterator)\ - + (query_costs(|costs| costs.storage_read) + first.unwrap_or_default() as usize) * child_complexity\ - + (query_costs(|costs| costs.storage_read) + last.unwrap_or_default() as usize) * child_complexity\ + query_costs().storage_iterator\ + + (query_costs().storage_read + first.unwrap_or_default() as usize) * child_complexity \ + + (query_costs().storage_read + last.unwrap_or_default() as usize) * child_complexity\ }")] async fn contract_balances( &self, diff --git a/crates/fuel-core/src/schema/da_compressed.rs b/crates/fuel-core/src/schema/da_compressed.rs index d0dcd87eafd..2f2939be83f 100644 --- a/crates/fuel-core/src/schema/da_compressed.rs +++ b/crates/fuel-core/src/schema/da_compressed.rs @@ -36,7 +36,7 @@ pub struct DaCompressedBlockQuery; #[Object] impl DaCompressedBlockQuery { - #[graphql(complexity = "query_costs(|costs| costs.da_compressed_block_read)")] + #[graphql(complexity = "query_costs().da_compressed_block_read")] async fn da_compressed_block( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/gas_price.rs b/crates/fuel-core/src/schema/gas_price.rs index 64162c6c124..7f7c7555e6d 100644 --- a/crates/fuel-core/src/schema/gas_price.rs +++ b/crates/fuel-core/src/schema/gas_price.rs @@ -42,7 +42,7 @@ pub struct LatestGasPriceQuery {} #[Object] impl LatestGasPriceQuery { - #[graphql(complexity = "query_costs(|costs| costs.block_header)")] + #[graphql(complexity = "query_costs().block_header")] async fn latest_gas_price( &self, ctx: &Context<'_>, @@ -80,7 +80,7 @@ pub struct EstimateGasPriceQuery {} #[Object] impl EstimateGasPriceQuery { - #[graphql(complexity = "2 * query_costs(|costs| costs.storage_read)")] + #[graphql(complexity = "2 * query_costs().storage_read")] async fn estimate_gas_price( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/message.rs b/crates/fuel-core/src/schema/message.rs index 8418ed98b8f..ed81c058d47 100644 --- a/crates/fuel-core/src/schema/message.rs +++ b/crates/fuel-core/src/schema/message.rs @@ -64,7 +64,7 @@ pub struct MessageQuery {} #[Object] impl MessageQuery { - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn message( &self, ctx: &Context<'_>, @@ -76,9 +76,9 @@ impl MessageQuery { } #[graphql(complexity = "{\ - query_costs(|costs| costs.storage_iterator)\ - + (query_costs(|costs| costs.storage_read) + first.unwrap_or_default() as usize) * child_complexity \ - + (query_costs(|costs| costs.storage_read) + last.unwrap_or_default() as usize) * child_complexity\ + query_costs().storage_iterator\ + + (query_costs().storage_read + first.unwrap_or_default() as usize) * child_complexity \ + + (query_costs().storage_read + last.unwrap_or_default() as usize) * child_complexity\ }")] async fn messages( &self, @@ -122,9 +122,7 @@ impl MessageQuery { } // 256 * QUERY_COSTS.storage_read because the depth of the Merkle tree in the worst case is 256 - #[graphql( - complexity = "256 * query_costs(|costs| costs.storage_read) + child_complexity" - )] + #[graphql(complexity = "256 * query_costs().storage_read + child_complexity")] async fn message_proof( &self, ctx: &Context<'_>, @@ -155,7 +153,7 @@ impl MessageQuery { .map(MessageProof)) } - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn message_status( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/node_info.rs b/crates/fuel-core/src/schema/node_info.rs index 4ebda8fad46..5805c76dc60 100644 --- a/crates/fuel-core/src/schema/node_info.rs +++ b/crates/fuel-core/src/schema/node_info.rs @@ -42,7 +42,7 @@ impl NodeInfo { self.node_version.to_owned() } - #[graphql(complexity = "query_costs(|costs| costs.get_peers) + child_complexity")] + #[graphql(complexity = "query_costs().get_peers + child_complexity")] async fn peers(&self, _ctx: &Context<'_>) -> async_graphql::Result> { #[cfg(feature = "p2p")] { @@ -66,7 +66,7 @@ pub struct NodeQuery {} #[Object] impl NodeQuery { - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn node_info(&self, ctx: &Context<'_>) -> async_graphql::Result { let config = ctx.data_unchecked::(); diff --git a/crates/fuel-core/src/schema/relayed_tx.rs b/crates/fuel-core/src/schema/relayed_tx.rs index 22486ee148c..6ae34b81c07 100644 --- a/crates/fuel-core/src/schema/relayed_tx.rs +++ b/crates/fuel-core/src/schema/relayed_tx.rs @@ -23,7 +23,7 @@ pub struct RelayedTransactionQuery {} #[Object] impl RelayedTransactionQuery { - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn relayed_transaction_status( &self, ctx: &Context<'_>, diff --git a/crates/fuel-core/src/schema/tx.rs b/crates/fuel-core/src/schema/tx.rs index 11ec223c34c..4681e7c8d38 100644 --- a/crates/fuel-core/src/schema/tx.rs +++ b/crates/fuel-core/src/schema/tx.rs @@ -89,7 +89,7 @@ pub struct TxQuery; #[Object] impl TxQuery { - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn transaction( &self, ctx: &Context<'_>, @@ -111,7 +111,7 @@ impl TxQuery { // We assume that each block has 100 transactions. #[graphql(complexity = "{\ - (query_costs(|costs| costs.tx_get) + child_complexity) \ + (query_costs().tx_get + child_complexity) \ * (first.unwrap_or_default() as usize + last.unwrap_or_default() as usize) }")] async fn transactions( @@ -176,9 +176,9 @@ impl TxQuery { } #[graphql(complexity = "{\ - query_costs(|costs| costs.storage_iterator)\ - + (query_costs(|costs| costs.storage_read) + first.unwrap_or_default() as usize) * child_complexity \ - + (query_costs(|costs| costs.storage_read) + last.unwrap_or_default() as usize) * child_complexity\ + query_costs().storage_iterator\ + + (query_costs().storage_read + first.unwrap_or_default() as usize) * child_complexity \ + + (query_costs().storage_read + last.unwrap_or_default() as usize) * child_complexity\ }")] async fn transactions_by_owner( &self, @@ -219,9 +219,7 @@ impl TxQuery { } /// Estimate the predicate gas for the provided transaction - #[graphql( - complexity = "query_costs(|costs| costs.estimate_predicates) + child_complexity" - )] + #[graphql(complexity = "query_costs().estimate_predicates + child_complexity")] async fn estimate_predicates( &self, ctx: &Context<'_>, @@ -266,7 +264,7 @@ pub struct TxMutation; impl TxMutation { /// Execute a dry-run of multiple transactions using a fork of current state, no changes are committed. #[graphql( - complexity = "query_costs(|costs| costs.dry_run) * txs.len() + child_complexity * txs.len()" + complexity = "query_costs().dry_run * txs.len() + child_complexity * txs.len()" )] async fn dry_run( &self, @@ -318,7 +316,7 @@ impl TxMutation { /// Submits transaction to the `TxPool`. /// /// Returns submitted transaction if the transaction is included in the `TxPool` without problems. - #[graphql(complexity = "query_costs(|costs| costs.submit) + child_complexity")] + #[graphql(complexity = "query_costs().submit + child_complexity")] async fn submit( &self, ctx: &Context<'_>, @@ -358,7 +356,7 @@ impl TxStatusSubscription { /// then the updates arrive. In such a case the stream will close without /// a status. If this occurs the stream can simply be restarted to return /// the latest status. - #[graphql(complexity = "query_costs(|costs| costs.status_change) + child_complexity")] + #[graphql(complexity = "query_costs().status_change + child_complexity")] async fn status_change<'a>( &self, ctx: &'a Context<'a>, @@ -378,9 +376,7 @@ impl TxStatusSubscription { } /// Submits transaction to the `TxPool` and await either confirmation or failure. - #[graphql( - complexity = "query_costs(|costs| costs.submit_and_await) + child_complexity" - )] + #[graphql(complexity = "query_costs().submit_and_await + child_complexity")] async fn submit_and_await<'a>( &self, ctx: &'a Context<'a>, @@ -398,9 +394,7 @@ impl TxStatusSubscription { /// Submits the transaction to the `TxPool` and returns a stream of events. /// Compared to the `submitAndAwait`, the stream also contains ` /// SubmittedStatus` as an intermediate state. - #[graphql( - complexity = "query_costs(|costs| costs.submit_and_await) + child_complexity" - )] + #[graphql(complexity = "query_costs().submit_and_await + child_complexity")] async fn submit_and_await_status<'a>( &self, ctx: &'a Context<'a>, diff --git a/crates/fuel-core/src/schema/tx/types.rs b/crates/fuel-core/src/schema/tx/types.rs index 9a60ac3ba0e..d237bb817ae 100644 --- a/crates/fuel-core/src/schema/tx/types.rs +++ b/crates/fuel-core/src/schema/tx/types.rs @@ -177,14 +177,14 @@ impl SuccessStatus { self.block_height.into() } - #[graphql(complexity = "query_costs(|costs| costs.block_header) + child_complexity")] + #[graphql(complexity = "query_costs().block_header + child_complexity")] async fn block(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let block = query.block(&self.block_height)?; Ok(block.into()) } - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn transaction(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let transaction = query.transaction(&self.tx_id)?; @@ -233,14 +233,14 @@ impl FailureStatus { self.block_height.into() } - #[graphql(complexity = "query_costs(|costs| costs.block_header) + child_complexity")] + #[graphql(complexity = "query_costs().block_header + child_complexity")] async fn block(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let block = query.block(&self.block_height)?; Ok(block.into()) } - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn transaction(&self, ctx: &Context<'_>) -> async_graphql::Result { let query = ctx.read_view()?; let transaction = query.transaction(&self.tx_id)?; @@ -412,7 +412,7 @@ impl Transaction { TransactionId(self.1) } - #[graphql(complexity = "query_costs(|costs| costs.storage_read)")] + #[graphql(complexity = "query_costs().storage_read")] async fn input_asset_ids(&self, ctx: &Context<'_>) -> Option> { let params = ctx .data_unchecked::() @@ -688,9 +688,7 @@ impl Transaction { } } - #[graphql( - complexity = "query_costs(|costs| costs.tx_status_read) + child_complexity" - )] + #[graphql(complexity = "query_costs().tx_status_read + child_complexity")] async fn status( &self, ctx: &Context<'_>, @@ -845,7 +843,7 @@ impl Transaction { } } - #[graphql(complexity = "query_costs(|costs| costs.tx_raw_payload)")] + #[graphql(complexity = "query_costs().tx_raw_payload")] /// Return the transaction bytes using canonical encoding async fn raw_payload(&self) -> HexString { HexString(self.0.clone().to_bytes()) diff --git a/crates/fuel-core/src/schema/upgrades.rs b/crates/fuel-core/src/schema/upgrades.rs index 045c88d4288..a4591ccd155 100644 --- a/crates/fuel-core/src/schema/upgrades.rs +++ b/crates/fuel-core/src/schema/upgrades.rs @@ -29,7 +29,7 @@ pub struct UpgradeQuery; #[Object] impl UpgradeQuery { - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn consensus_parameters( &self, ctx: &Context<'_>, @@ -42,7 +42,7 @@ impl UpgradeQuery { Ok(ConsensusParameters(params)) } - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn state_transition_bytecode_by_version( &self, ctx: &Context<'_>, @@ -54,7 +54,7 @@ impl UpgradeQuery { .into_api_result() } - #[graphql(complexity = "query_costs(|costs| costs.storage_read) + child_complexity")] + #[graphql(complexity = "query_costs().storage_read + child_complexity")] async fn state_transition_bytecode_by_root( &self, root: HexString, @@ -73,7 +73,7 @@ impl StateTransitionBytecode { HexString(self.root.to_vec()) } - #[graphql(complexity = "query_costs(|costs| costs.state_transition_bytecode_read)")] + #[graphql(complexity = "query_costs().state_transition_bytecode_read")] async fn bytecode( &self, ctx: &Context<'_>, From 645db49d23c20485bd2f794464b1005f6cd93641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Blankfors?= Date: Mon, 14 Oct 2024 13:48:32 +0200 Subject: [PATCH 5/5] feat: Return error in tests if attempting multiple initializations --- crates/fuel-core/src/graphql_api.rs | 36 ++++++++++++------- .../fuel-core/src/graphql_api/api_service.rs | 2 +- tests/tests/dos.rs | 15 ++++---- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/crates/fuel-core/src/graphql_api.rs b/crates/fuel-core/src/graphql_api.rs index 5bc5e633275..c510418ff96 100644 --- a/crates/fuel-core/src/graphql_api.rs +++ b/crates/fuel-core/src/graphql_api.rs @@ -18,6 +18,17 @@ pub(crate) mod validation_extension; pub(crate) mod view_extension; pub mod worker_service; +#[derive(Clone, Debug)] +pub struct Config { + pub config: ServiceConfig, + pub utxo_validation: bool, + pub debug: bool, + pub vm_backtrace: bool, + pub max_tx: usize, + pub max_txpool_dependency_chain_length: usize, + pub chain_name: String, +} + #[derive(Clone, Debug)] pub struct ServiceConfig { pub addr: SocketAddr, @@ -35,7 +46,7 @@ pub struct ServiceConfig { pub costs: Costs, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Costs { pub balance_query: usize, pub coins_to_spend: usize, @@ -93,19 +104,18 @@ pub fn query_costs() -> &'static Costs { pub static QUERY_COSTS: OnceLock = OnceLock::new(); -fn initialize_query_costs(config: Config) { - QUERY_COSTS.get_or_init(|| config.config.costs); -} +fn initialize_query_costs(costs: Costs) -> anyhow::Result<()> { + #[cfg(feature = "test-helpers")] + if costs != DEFAULT_QUERY_COSTS { + // We don't support setting these values in test contexts, because + // it can lead to unexpected behavior if multiple tests try to + // initialize different values. + anyhow::bail!("cannot initialize queries with non-default costs in tests") + } -#[derive(Clone, Debug)] -pub struct Config { - pub config: ServiceConfig, - pub utxo_validation: bool, - pub debug: bool, - pub vm_backtrace: bool, - pub max_tx: usize, - pub max_txpool_dependency_chain_length: usize, - pub chain_name: String, + QUERY_COSTS.get_or_init(|| costs); + + Ok(()) } pub trait IntoApiResult { diff --git a/crates/fuel-core/src/graphql_api/api_service.rs b/crates/fuel-core/src/graphql_api/api_service.rs index d09e26a9e54..ec65597eb0d 100644 --- a/crates/fuel-core/src/graphql_api/api_service.rs +++ b/crates/fuel-core/src/graphql_api/api_service.rs @@ -221,7 +221,7 @@ where OnChain::LatestView: OnChainDatabase, OffChain::LatestView: OffChainDatabase, { - graphql_api::initialize_query_costs(config.clone()); + graphql_api::initialize_query_costs(config.config.costs.clone())?; let network_addr = config.config.addr; let combined_read_database = diff --git a/tests/tests/dos.rs b/tests/tests/dos.rs index 5b001e08377..ca4295024fc 100644 --- a/tests/tests/dos.rs +++ b/tests/tests/dos.rs @@ -246,19 +246,18 @@ async fn complex_queries__41_full_block__query_too_complex() { } #[tokio::test] -async fn complex_queries__increased_block_header_cost__query_too_complex() { - let query = FULL_BLOCK_QUERY.to_string(); - let query = query.replace("$NUMBER_OF_BLOCKS", "1"); - +async fn complex_queries__increased_block_header_cost__failed_to_initialize_service() { let mut config = Config::local_node(); config.graphql_config.costs.block_header = config.graphql_config.max_queries_complexity; - let node = FuelService::new_node(config).await.unwrap(); - let url = format!("http://{}/v1/graphql", node.bound_address); + let Err(error) = FuelService::new_node(config).await else { + panic!("expected error"); + }; - let result = send_graph_ql_query(&url, query.as_str()).await; - assert!(result.contains("Query is too complex.")); + assert!(error + .to_string() + .contains("cannot initialize queries with non-default costs in tests")) } #[tokio::test]