fuel_core/service/
config.rs

1use clap::ValueEnum;
2use std::{
3    num::{
4        NonZeroU32,
5        NonZeroU64,
6    },
7    path::PathBuf,
8    time::Duration,
9};
10use strum_macros::{
11    Display,
12    EnumString,
13    EnumVariantNames,
14};
15
16use fuel_core_chain_config::SnapshotReader;
17#[cfg(feature = "test-helpers")]
18use fuel_core_chain_config::{
19    ChainConfig,
20    StateConfig,
21};
22pub use fuel_core_consensus_module::RelayerConsensusConfig;
23pub use fuel_core_importer;
24#[cfg(feature = "p2p")]
25use fuel_core_p2p::config::{
26    Config as P2PConfig,
27    NotInitialized,
28};
29pub use fuel_core_poa::Trigger;
30#[cfg(feature = "relayer")]
31use fuel_core_relayer::Config as RelayerConfig;
32use fuel_core_tx_status_manager::config::Config as TxStatusManagerConfig;
33use fuel_core_txpool::config::Config as TxPoolConfig;
34use fuel_core_types::{
35    blockchain::header::StateTransitionBytecodeVersion,
36    signer::SignMode,
37};
38
39use crate::{
40    combined_database::CombinedDatabaseConfig,
41    graphql_api::ServiceConfig as GraphQLConfig,
42};
43
44use fuel_core_types::fuel_types::{
45    AssetId,
46    ChainId,
47};
48#[cfg(feature = "parallel-executor")]
49use std::num::NonZeroUsize;
50
51#[derive(Clone, Debug)]
52pub struct Config {
53    pub graphql_config: GraphQLConfig,
54    pub combined_db_config: CombinedDatabaseConfig,
55    pub snapshot_reader: SnapshotReader,
56    pub continue_on_error: bool,
57    /// When `true`:
58    /// - Enables manual block production.
59    /// - Enables debugger endpoint.
60    /// - Allows setting `utxo_validation` to `false`.
61    pub debug: bool,
62    /// When `true`:
63    /// - Enables dry run in the past.
64    /// - Enables storage read replay for historical blocks.
65    /// - Enables querying historical contract state and balances.
66    pub historical_execution: bool,
67    pub expensive_subscriptions: bool,
68    pub utxo_validation: bool,
69    pub allow_syscall: bool,
70    pub native_executor_version: Option<StateTransitionBytecodeVersion>,
71    #[cfg(feature = "parallel-executor")]
72    pub executor_number_of_cores: NonZeroUsize,
73    pub block_production: Trigger,
74    pub predefined_blocks_path: Option<PathBuf>,
75    pub txpool: TxPoolConfig,
76    pub tx_status_manager: TxStatusManagerConfig,
77    pub block_producer: fuel_core_producer::Config,
78    pub gas_price_config: GasPriceConfig,
79    pub da_compression: DaCompressionMode,
80    pub block_importer: fuel_core_importer::Config,
81    #[cfg(feature = "relayer")]
82    pub relayer: Option<RelayerConfig>,
83    #[cfg(feature = "p2p")]
84    pub p2p: Option<P2PConfig<NotInitialized>>,
85    #[cfg(feature = "p2p")]
86    pub sync: fuel_core_sync::Config,
87    #[cfg(feature = "p2p")]
88    pub pre_confirmation_signature_service:
89        fuel_core_poa::pre_confirmation_signature_service::config::Config,
90    #[cfg(feature = "shared-sequencer")]
91    pub shared_sequencer: fuel_core_shared_sequencer::Config,
92    pub consensus_signer: SignMode,
93    pub name: String,
94    pub relayer_consensus_config: fuel_core_consensus_module::RelayerConsensusConfig,
95    /// The number of reserved peers to connect to before starting to sync.
96    pub min_connected_reserved_peers: usize,
97    /// Time to wait after receiving the latest block before considered to be Synced.
98    pub time_until_synced: Duration,
99    /// The timeout after which block production is considered failed.
100    pub production_timeout: Duration,
101    /// The size of the memory pool in number of `MemoryInstance`s.
102    pub memory_pool_size: usize,
103}
104
105impl Config {
106    #[cfg(feature = "test-helpers")]
107    pub fn local_node() -> Self {
108        Self::local_node_with_state_config(StateConfig::local_testnet())
109    }
110
111    #[cfg(feature = "test-helpers")]
112    pub fn local_node_with_state_config(state_config: StateConfig) -> Self {
113        Self::local_node_with_configs(ChainConfig::local_testnet(), state_config)
114    }
115
116    #[cfg(feature = "test-helpers")]
117    pub fn local_node_with_configs(
118        chain_config: ChainConfig,
119        state_config: StateConfig,
120    ) -> Self {
121        Self::local_node_with_reader(SnapshotReader::new_in_memory(
122            chain_config,
123            state_config,
124        ))
125    }
126
127    #[cfg(feature = "test-helpers")]
128    pub fn local_node_with_reader(snapshot_reader: SnapshotReader) -> Self {
129        let block_importer = fuel_core_importer::Config::new(false);
130        let latest_block = snapshot_reader.last_block_config();
131        // In tests, we always want to use the native executor as a default configuration.
132        let native_executor_version = latest_block
133            .map(|last_block| last_block.state_transition_version.saturating_add(1))
134            .unwrap_or(
135                fuel_core_types::blockchain::header::LATEST_STATE_TRANSITION_VERSION,
136            );
137
138        let utxo_validation = false;
139
140        let combined_db_config = CombinedDatabaseConfig {
141            #[cfg(feature = "rocksdb")]
142            database_config: crate::state::rocks_db::DatabaseConfig::config_for_tests(),
143            database_path: Default::default(),
144            #[cfg(feature = "rocksdb")]
145            database_type: DbType::RocksDb,
146            #[cfg(not(feature = "rocksdb"))]
147            database_type: DbType::InMemory,
148            #[cfg(feature = "rocksdb")]
149            state_rewind_policy:
150                crate::state::historical_rocksdb::StateRewindPolicy::RewindFullRange,
151        };
152
153        #[cfg(feature = "p2p")]
154        let network_name = snapshot_reader.chain_config().chain_name.clone();
155        let gas_price_config = GasPriceConfig::local_node();
156
157        const MAX_TXS_TTL: Duration = Duration::from_secs(60 * 100000000);
158
159        Self {
160            graphql_config: GraphQLConfig {
161                addr: std::net::SocketAddr::new(
162                    std::net::Ipv4Addr::new(127, 0, 0, 1).into(),
163                    0,
164                ),
165                number_of_threads: 0,
166                database_batch_size: 100,
167                block_subscriptions_queue: 1000,
168                max_queries_depth: 16,
169                max_queries_complexity: 80000,
170                max_queries_recursive_depth: 16,
171                max_queries_resolver_recursive_depth: 1,
172                max_queries_directives: 10,
173                max_concurrent_queries: 1024,
174                request_body_bytes_limit: 16 * 1024 * 1024,
175                query_log_threshold_time: Duration::from_secs(2),
176                api_request_timeout: Duration::from_secs(60),
177                assemble_tx_dry_run_limit: 3,
178                assemble_tx_estimate_predicates_limit: 5,
179                costs: Default::default(),
180                required_fuel_block_height_tolerance: 10,
181                required_fuel_block_height_timeout: Duration::from_secs(30),
182            },
183            combined_db_config,
184            continue_on_error: false,
185            debug: true,
186            historical_execution: true,
187            allow_syscall: true,
188            expensive_subscriptions: true,
189            utxo_validation,
190            native_executor_version: Some(native_executor_version),
191            #[cfg(feature = "parallel-executor")]
192            executor_number_of_cores: NonZeroUsize::new(1).expect("1 is not zero"),
193            snapshot_reader,
194            block_production: Trigger::Instant,
195            predefined_blocks_path: None,
196            txpool: TxPoolConfig {
197                utxo_validation,
198                max_txs_ttl: MAX_TXS_TTL,
199                ..Default::default()
200            },
201            tx_status_manager: TxStatusManagerConfig {
202                subscription_ttl: MAX_TXS_TTL,
203                ..Default::default()
204            },
205            block_producer: fuel_core_producer::Config {
206                ..Default::default()
207            },
208            da_compression: DaCompressionMode::Disabled,
209            gas_price_config,
210            block_importer,
211            #[cfg(feature = "relayer")]
212            relayer: None,
213            #[cfg(feature = "p2p")]
214            p2p: Some(P2PConfig::<NotInitialized>::default(network_name.as_str())),
215            #[cfg(feature = "p2p")]
216            sync: fuel_core_sync::Config::default(),
217            #[cfg(feature = "p2p")]
218            pre_confirmation_signature_service:
219                fuel_core_poa::pre_confirmation_signature_service::config::Config::default(
220                ),
221            #[cfg(feature = "shared-sequencer")]
222            shared_sequencer: fuel_core_shared_sequencer::Config::local_node(),
223            consensus_signer: SignMode::Key(fuel_core_types::secrecy::Secret::new(
224                fuel_core_chain_config::default_consensus_dev_key().into(),
225            )),
226            name: String::default(),
227            relayer_consensus_config: Default::default(),
228            min_connected_reserved_peers: 0,
229            time_until_synced: Duration::ZERO,
230            production_timeout: Duration::from_secs(20),
231            memory_pool_size: 4,
232        }
233    }
234
235    // TODO: Rework our configs system to avoid nesting of the same configs.
236    pub fn make_config_consistent(mut self) -> Config {
237        if !self.debug && !self.utxo_validation {
238            tracing::warn!(
239                "The `utxo_validation` should be `true` with disabled `debug`"
240            );
241            self.utxo_validation = true;
242        }
243
244        if self.txpool.utxo_validation != self.utxo_validation {
245            tracing::warn!("The `utxo_validation` of `TxPool` was inconsistent");
246            self.txpool.utxo_validation = self.utxo_validation;
247        }
248
249        self
250    }
251
252    pub fn base_asset_id(&self) -> AssetId {
253        *self
254            .snapshot_reader
255            .chain_config()
256            .consensus_parameters
257            .base_asset_id()
258    }
259
260    pub fn chain_id(&self) -> ChainId {
261        self.snapshot_reader
262            .chain_config()
263            .consensus_parameters
264            .chain_id()
265    }
266}
267
268impl From<&Config> for fuel_core_poa::Config {
269    fn from(config: &Config) -> Self {
270        fuel_core_poa::Config {
271            trigger: config.block_production,
272            signer: config.consensus_signer.clone(),
273            metrics: false,
274            min_connected_reserved_peers: config.min_connected_reserved_peers,
275            time_until_synced: config.time_until_synced,
276            production_timeout: config.production_timeout,
277            chain_id: config
278                .snapshot_reader
279                .chain_config()
280                .consensus_parameters
281                .chain_id(),
282        }
283    }
284}
285
286#[cfg(feature = "p2p")]
287impl From<&Config> for fuel_core_poa::pre_confirmation_signature_service::config::Config {
288    fn from(value: &Config) -> Self {
289        fuel_core_poa::pre_confirmation_signature_service::config::Config {
290            echo_delegation_interval: value
291                .pre_confirmation_signature_service
292                .echo_delegation_interval,
293            key_expiration_interval: value
294                .pre_confirmation_signature_service
295                .key_expiration_interval,
296            key_rotation_interval: value
297                .pre_confirmation_signature_service
298                .key_rotation_interval,
299        }
300    }
301}
302
303#[derive(
304    Clone, Copy, Debug, Display, Eq, PartialEq, EnumString, EnumVariantNames, ValueEnum,
305)]
306#[strum(serialize_all = "kebab_case")]
307pub enum DbType {
308    InMemory,
309    RocksDb,
310}
311
312#[derive(Clone, Debug)]
313pub struct GasPriceConfig {
314    pub starting_exec_gas_price: u64,
315    pub exec_gas_price_change_percent: u16,
316    pub min_exec_gas_price: u64,
317    pub exec_gas_price_threshold_percent: u8,
318    pub da_committer_url: Option<url::Url>,
319    pub da_poll_interval: Option<Duration>,
320    pub da_gas_price_factor: NonZeroU64,
321    pub starting_recorded_height: Option<u32>,
322    pub min_da_gas_price: u64,
323    pub max_da_gas_price: u64,
324    pub max_da_gas_price_change_percent: u16,
325    pub da_gas_price_p_component: i64,
326    pub da_gas_price_d_component: i64,
327    pub gas_price_metrics: bool,
328    pub activity_normal_range_size: u16,
329    pub activity_capped_range_size: u16,
330    pub activity_decrease_range_size: u16,
331    pub block_activity_threshold: u8,
332}
333
334impl GasPriceConfig {
335    #[cfg(feature = "test-helpers")]
336    pub fn local_node() -> GasPriceConfig {
337        let starting_gas_price = 0;
338        let gas_price_change_percent = 0;
339        let min_gas_price = 0;
340        let gas_price_threshold_percent = 50;
341        let gas_price_metrics = false;
342
343        GasPriceConfig {
344            starting_exec_gas_price: starting_gas_price,
345            exec_gas_price_change_percent: gas_price_change_percent,
346            min_exec_gas_price: min_gas_price,
347            exec_gas_price_threshold_percent: gas_price_threshold_percent,
348            da_gas_price_factor: NonZeroU64::new(100).expect("100 is not zero"),
349            starting_recorded_height: None,
350            min_da_gas_price: 0,
351            max_da_gas_price: 1,
352            max_da_gas_price_change_percent: 0,
353            da_gas_price_p_component: 0,
354            da_gas_price_d_component: 0,
355            gas_price_metrics,
356            activity_normal_range_size: 0,
357            activity_capped_range_size: 0,
358            activity_decrease_range_size: 0,
359            da_committer_url: None,
360            block_activity_threshold: 0,
361            da_poll_interval: Some(Duration::from_secs(1)),
362        }
363    }
364}
365
366#[derive(Debug, Clone)]
367pub struct DaCompressionConfig {
368    pub retention_duration: Duration,
369    pub metrics: bool,
370    pub starting_height: Option<NonZeroU32>,
371}
372
373#[derive(Debug, Clone)]
374pub enum DaCompressionMode {
375    Disabled,
376    Enabled(DaCompressionConfig),
377}