Skip to main content

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