1use async_graphql::Context;
2use fuel_core_storage::{
3 Error as StorageError,
4 IsNotFound,
5};
6use std::{
7 net::SocketAddr,
8 sync::OnceLock,
9 time::Duration,
10};
11
12pub mod api_service;
13pub(crate) mod block_height_subscription;
14pub mod database;
15pub(crate) mod extensions;
16pub(crate) mod indexation;
17pub mod ports;
18pub mod storage;
19pub mod worker_service;
20
21#[derive(Clone, Debug)]
22pub struct Config {
23 pub config: ServiceConfig,
24 pub utxo_validation: bool,
25 pub debug: bool,
26 pub allow_syscall: bool,
27 pub historical_execution: bool,
28 pub expensive_subscriptions: bool,
29 pub max_tx: usize,
30 pub max_gas: u64,
31 pub max_size: usize,
32 pub max_txpool_dependency_chain_length: usize,
33 pub chain_name: String,
34}
35
36#[derive(Clone, Debug)]
37pub struct ServiceConfig {
38 pub addr: SocketAddr,
39 pub number_of_threads: usize,
40 pub database_batch_size: usize,
41 pub block_subscriptions_queue: usize,
42 pub max_queries_depth: usize,
43 pub max_queries_complexity: usize,
44 pub max_queries_recursive_depth: usize,
45 pub max_queries_resolver_recursive_depth: usize,
46 pub max_queries_directives: usize,
47 pub max_concurrent_queries: usize,
48 pub request_body_bytes_limit: usize,
49 pub required_fuel_block_height_tolerance: u32,
52 pub required_fuel_block_height_timeout: Duration,
55 pub query_log_threshold_time: Duration,
57 pub api_request_timeout: Duration,
58 pub assemble_tx_dry_run_limit: usize,
59 pub assemble_tx_estimate_predicates_limit: usize,
60 pub costs: Costs,
62}
63
64#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
65pub struct Costs {
66 pub balance_query: usize,
67 pub coins_to_spend: usize,
68 pub get_peers: usize,
69 pub estimate_predicates: usize,
70 pub assemble_tx: usize,
71 pub dry_run: usize,
72 pub storage_read_replay: usize,
73 pub submit: usize,
74 pub submit_and_await: usize,
75 pub status_change: usize,
76 pub storage_read: usize,
77 pub tx_get: usize,
78 pub tx_status_read: usize,
79 pub tx_raw_payload: usize,
80 pub block_header: usize,
81 pub block_transactions: usize,
82 pub block_transactions_ids: usize,
83 pub storage_iterator: usize,
84 pub bytecode_read: usize,
85 pub state_transition_bytecode_read: usize,
86 pub da_compressed_block_read: usize,
87}
88
89#[cfg(feature = "test-helpers")]
90impl Default for Costs {
91 fn default() -> Self {
92 DEFAULT_QUERY_COSTS
93 }
94}
95
96const BALANCES_QUERY_COST_WITH_INDEXATION: usize = 0;
97const BALANCES_QUERY_COST_WITHOUT_INDEXATION: usize = 40001;
98
99pub const DEFAULT_QUERY_COSTS: Costs = Costs {
100 balance_query: BALANCES_QUERY_COST_WITH_INDEXATION,
101 coins_to_spend: 40001,
102 get_peers: 40001,
103 estimate_predicates: 40001,
104 dry_run: 12000,
105 assemble_tx: 76_000,
106 storage_read_replay: 40001,
107 submit: 40001,
108 submit_and_await: 40001,
109 status_change: 40001,
110 storage_read: 40,
111 tx_get: 200,
112 tx_status_read: 200,
113 tx_raw_payload: 600,
114 block_header: 600,
115 block_transactions: 6000,
116 block_transactions_ids: 200,
117 storage_iterator: 100,
118 bytecode_read: 8000,
119 state_transition_bytecode_read: 76_000,
120 da_compressed_block_read: 4000,
121};
122
123pub fn query_costs() -> &'static Costs {
124 QUERY_COSTS.get().unwrap_or(&DEFAULT_QUERY_COSTS)
125}
126
127pub static QUERY_COSTS: OnceLock<Costs> = OnceLock::new();
128
129#[cfg(feature = "test-helpers")]
130fn default_query_costs(balances_indexation_enabled: bool) -> Costs {
131 let mut cost = DEFAULT_QUERY_COSTS;
132
133 if !balances_indexation_enabled {
134 cost.balance_query = BALANCES_QUERY_COST_WITHOUT_INDEXATION;
135 }
136
137 cost
138}
139
140fn initialize_query_costs(
141 costs: Costs,
142 _balances_indexation_enabled: bool,
143) -> anyhow::Result<()> {
144 #[cfg(feature = "test-helpers")]
145 if costs != default_query_costs(_balances_indexation_enabled) {
146 anyhow::bail!("cannot initialize queries with non-default costs in tests")
150 }
151
152 QUERY_COSTS.get_or_init(|| costs);
153
154 Ok(())
155}
156
157pub trait IntoApiResult<T> {
158 fn into_api_result<NewT, E>(self) -> Result<Option<NewT>, E>
159 where
160 NewT: From<T>,
161 E: From<StorageError>;
162}
163
164impl<T> IntoApiResult<T> for Result<T, StorageError> {
165 fn into_api_result<NewT, E>(self) -> Result<Option<NewT>, E>
166 where
167 NewT: From<T>,
168 E: From<StorageError>,
169 {
170 if self.is_not_found() {
171 Ok(None)
172 } else {
173 Ok(Some(self?.into()))
174 }
175 }
176}
177
178pub fn require_historical_execution(ctx: &Context<'_>) -> async_graphql::Result<()> {
179 let config = ctx.data_unchecked::<Config>();
180
181 if config.historical_execution {
182 Ok(())
183 } else {
184 Err(async_graphql::Error::new(
185 "`--historical-execution` is required for this operation",
186 ))
187 }
188}
189
190pub fn require_expensive_subscriptions(ctx: &Context<'_>) -> async_graphql::Result<()> {
191 let config = ctx.data_unchecked::<Config>();
192
193 if config.expensive_subscriptions {
194 Ok(())
195 } else {
196 Err(async_graphql::Error::new(
197 "`--expensive-subscriptions` is required for this operation",
198 ))
199 }
200}