drive_abci/
config.rs

1use crate::logging::LogConfigs;
2use crate::utils::from_str_or_number;
3use crate::{abci::config::AbciConfig, error::Error};
4use bincode::{Decode, Encode};
5use dpp::dashcore::Network;
6use dpp::dashcore_rpc::json::QuorumType;
7use dpp::util::deserializer::ProtocolVersion;
8use dpp::version::INITIAL_PROTOCOL_VERSION;
9use drive::config::DriveConfig;
10use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize};
11use std::path::PathBuf;
12
13/// Configuration for Dash Core RPC client used in consensus logic
14#[derive(Clone, Debug, Serialize, Deserialize, Default)]
15pub struct ConsensusCoreRpcConfig {
16    /// Core RPC client hostname or IP address
17    #[serde(rename = "core_consensus_json_rpc_host")]
18    pub host: String,
19
20    /// Core RPC client port number
21    #[serde(
22        rename = "core_consensus_json_rpc_port",
23        deserialize_with = "from_str_or_number"
24    )]
25    pub port: u16,
26
27    /// Core RPC client username
28    #[serde(rename = "core_consensus_json_rpc_username")]
29    pub username: String,
30
31    /// Core RPC client password
32    #[serde(rename = "core_consensus_json_rpc_password")]
33    pub password: String,
34}
35
36impl ConsensusCoreRpcConfig {
37    /// Return core address in the `host:port` format.
38    pub fn url(&self) -> String {
39        format!("{}:{}", self.host, self.port)
40    }
41}
42
43/// Configuration for Dash Core RPC client used in check tx
44#[derive(Clone, Debug, Serialize, Deserialize, Default)]
45pub struct CheckTxCoreRpcConfig {
46    /// Core RPC client hostname or IP address
47    #[serde(rename = "core_check_tx_json_rpc_host")]
48    pub host: String,
49
50    /// Core RPC client port number
51    #[serde(
52        rename = "core_check_tx_json_rpc_port",
53        deserialize_with = "from_str_or_number"
54    )]
55    pub port: u16,
56
57    /// Core RPC client username
58    #[serde(rename = "core_check_tx_json_rpc_username")]
59    pub username: String,
60
61    /// Core RPC client password
62    #[serde(rename = "core_check_tx_json_rpc_password")]
63    pub password: String,
64}
65
66impl CheckTxCoreRpcConfig {
67    /// Return core address in the `host:port` format.
68    pub fn url(&self) -> String {
69        format!("{}:{}", self.host, self.port)
70    }
71}
72
73/// Configuration for Dash Core related things
74#[derive(Clone, Debug, Serialize, Deserialize)]
75#[serde(default)]
76#[derive(Default)]
77pub struct CoreConfig {
78    /// Core RPC config for consensus
79    #[serde(flatten)]
80    pub consensus_rpc: ConsensusCoreRpcConfig,
81    /// Core RPC config for check tx
82    #[serde(flatten)]
83    pub check_tx_rpc: CheckTxCoreRpcConfig,
84}
85
86/// Configuration of the execution part of Dash Platform.
87#[derive(Clone, Debug, Serialize, Deserialize)]
88// NOTE: in renames, we use lower_snake_case, because uppercase does not work; see
89// https://github.com/softprops/envy/issues/61 and https://github.com/softprops/envy/pull/69
90pub struct ExecutionConfig {
91    /// Should we use document triggers? Useful to set as `false` for tests
92    #[serde(default = "ExecutionConfig::default_use_document_triggers")]
93    pub use_document_triggers: bool,
94
95    /// Should we verify sum trees? Useful to set as `false` for tests
96    #[serde(default = "ExecutionConfig::default_verify_sum_trees")]
97    pub verify_sum_trees: bool,
98
99    /// Should we verify sum trees? Useful to set as `false` for tests
100    #[serde(default = "ExecutionConfig::default_verify_token_sum_trees")]
101    pub verify_token_sum_trees: bool,
102
103    /// How long in seconds should an epoch last
104    /// It might last a lot longer if the chain is halted
105    #[serde(
106        default = "ExecutionConfig::default_epoch_time_length_s",
107        deserialize_with = "from_str_or_number"
108    )]
109    pub epoch_time_length_s: u64,
110}
111
112/// Configuration of Dash Platform.
113///
114/// All fields in this struct can be configured using environment variables.
115/// These variables can also be defined in `.env` file in the current directory
116/// or its parents. You can also provide path to the .env file as a command-line argument.
117///
118/// Environment variables should be renamed to `SCREAMING_SNAKE_CASE`.
119/// For example, to define [`verify_sum_trees`], you should set VERIFY_SUM_TREES
120/// environment variable:
121///
122/// ``
123/// export VERIFY_SUM_TREES=true
124/// ``
125///
126/// [`verify_sum_trees`]: PlatformConfig::verify_sum_trees
127#[derive(Clone, Debug, Serialize)]
128// NOTE: in renames, we use lower_snake_case, because uppercase does not work; see
129// https://github.com/softprops/envy/issues/61 and https://github.com/softprops/envy/pull/69
130pub struct PlatformConfig {
131    /// The network type
132    pub network: Network,
133    /// Drive configuration
134    #[serde(flatten)]
135    pub drive: DriveConfig,
136
137    /// Dash Core config
138    #[serde(flatten)]
139    pub core: CoreConfig,
140
141    /// ABCI Application Server config
142    #[serde(flatten)]
143    pub abci: AbciConfig,
144
145    /// Address to listen for Prometheus connection.
146    ///
147    /// Optional.
148    ///
149    /// /// Address should be an URL with scheme `http://`, for example:
150    /// - `http://127.0.0.1:29090`
151    ///
152    /// Port number defaults to [crate::metrics::DEFAULT_PROMETHEUS_PORT].
153    pub prometheus_bind_address: Option<String>,
154
155    /// Address to listen for gRPC connection.
156    pub grpc_bind_address: String,
157
158    /// Execution config
159    #[serde(flatten)]
160    pub execution: ExecutionConfig,
161
162    /// The default quorum type
163    #[serde(flatten)]
164    pub validator_set: ValidatorSetConfig,
165
166    /// Chain lock configuration
167    #[serde(flatten)]
168    pub chain_lock: ChainLockConfig,
169
170    /// Instant lock configuration
171    #[serde(flatten)]
172    pub instant_lock: InstantLockConfig,
173
174    // todo: this should probably be coming from Tenderdash config. It's a test only param
175    /// Approximately how often are blocks produced
176    pub block_spacing_ms: u64,
177
178    /// Path to data storage
179    pub db_path: PathBuf,
180
181    /// Path to store rejected / invalid items (like transactions).
182    /// Used mainly for debugging.
183    ///
184    /// If not set, rejected and invalid items will not be stored.
185    pub rejections_path: Option<PathBuf>,
186
187    #[cfg(feature = "testing-config")]
188    /// This should be None, except in the case of Testing platform
189    #[serde(skip)]
190    pub testing_configs: PlatformTestConfig,
191
192    /// Enable tokio console (console feature must be enabled)
193    pub tokio_console_enabled: bool,
194
195    // TODO: Use from_str_to_socket_address
196    /// Tokio console address to connect to
197    pub tokio_console_address: String,
198
199    /// Number of seconds to store task information if there is no clients connected
200    pub tokio_console_retention_secs: u64,
201}
202
203// Define an intermediate struct that mirrors PlatformConfig
204#[derive(Deserialize)]
205struct PlatformConfigIntermediate {
206    /// The network type
207    #[serde(
208        default = "PlatformConfig::default_network",
209        deserialize_with = "from_str_to_network_with_aliases"
210    )]
211    pub network: Network,
212    /// Drive configuration
213    #[serde(flatten)]
214    pub drive: DriveConfig,
215    // Include all other fields
216    #[serde(flatten)]
217    pub core: CoreConfig,
218    #[serde(flatten)]
219    pub abci: AbciConfig,
220    pub prometheus_bind_address: Option<String>,
221    pub grpc_bind_address: String,
222    #[serde(flatten)]
223    pub execution: ExecutionConfig,
224    #[serde(flatten)]
225    pub validator_set: ValidatorSetConfig,
226    #[serde(flatten)]
227    pub chain_lock: ChainLockConfig,
228    #[serde(flatten)]
229    pub instant_lock: InstantLockConfig,
230    pub block_spacing_ms: u64,
231    #[serde(default = "PlatformConfig::default_initial_protocol_version")]
232    // TODO: Is not using
233    #[allow(dead_code)]
234    pub initial_protocol_version: ProtocolVersion,
235    pub db_path: PathBuf,
236    #[serde(default)]
237    pub rejections_path: Option<PathBuf>,
238    #[cfg(feature = "testing-config")]
239    #[serde(skip)]
240    pub testing_configs: PlatformTestConfig,
241    pub tokio_console_enabled: bool,
242    #[serde(default = "PlatformConfig::default_tokio_console_address")]
243    pub tokio_console_address: String,
244    #[serde(default = "PlatformConfig::default_tokio_console_retention_secs")]
245    pub tokio_console_retention_secs: u64,
246}
247
248impl<'de> Deserialize<'de> for PlatformConfig {
249    fn deserialize<D>(deserializer: D) -> Result<PlatformConfig, D::Error>
250    where
251        D: Deserializer<'de>,
252    {
253        // First, deserialize into an intermediate struct
254        let mut config = PlatformConfigIntermediate::deserialize(deserializer)?;
255
256        // Set drive.network = network
257        config.drive.network = config.network;
258
259        // Convert the intermediate struct into your actual PlatformConfig
260        Ok(PlatformConfig {
261            network: config.network,
262            drive: config.drive,
263            // Copy other fields
264            core: config.core,
265            abci: config.abci,
266            prometheus_bind_address: config.prometheus_bind_address,
267            grpc_bind_address: config.grpc_bind_address,
268            execution: config.execution,
269            validator_set: config.validator_set,
270            chain_lock: config.chain_lock,
271            instant_lock: config.instant_lock,
272            block_spacing_ms: config.block_spacing_ms,
273            db_path: config.db_path,
274            rejections_path: config.rejections_path,
275            #[cfg(feature = "testing-config")]
276            testing_configs: config.testing_configs,
277            tokio_console_enabled: config.tokio_console_enabled,
278            tokio_console_address: config.tokio_console_address,
279            tokio_console_retention_secs: config.tokio_console_retention_secs,
280        })
281    }
282}
283
284fn from_str_to_network_with_aliases<'de, D>(deserializer: D) -> Result<Network, D::Error>
285where
286    D: serde::Deserializer<'de>,
287{
288    let network_name = String::deserialize(deserializer)?;
289
290    match network_name.to_lowercase().as_str() {
291        "dash" | "mainnet" | "main" => Ok(Network::Mainnet),
292        "local" | "regtest" => Ok(Network::Regtest),
293        "testnet" | "test" => Ok(Network::Testnet),
294        "devnet" | "dev" => Ok(Network::Devnet),
295        _ => Err(serde::de::Error::custom(format!(
296            "can't parse network name: unknown network '{network_name}'"
297        ))),
298    }
299}
300
301/// A config suitable for a quorum configuration
302pub trait QuorumLikeConfig: Sized {
303    /// Quorum type
304    fn quorum_type(&self) -> QuorumType;
305
306    /// Quorum size
307    fn quorum_size(&self) -> u16;
308
309    /// Quorum DKG interval
310    fn quorum_window(&self) -> u32;
311
312    /// Quorum active signers count
313    fn quorum_active_signers(&self) -> u16;
314
315    /// Quorum rotation (dip24) or classic
316    fn quorum_rotation(&self) -> bool;
317}
318
319/// Chain Lock quorum configuration
320#[derive(Clone, Debug, Serialize, Deserialize, Encode, Decode)]
321pub struct ValidatorSetConfig {
322    /// The quorum type used for verifying chain locks
323    #[serde(
324        rename = "validator_set_quorum_type",
325        serialize_with = "serialize_quorum_type",
326        deserialize_with = "deserialize_quorum_type"
327    )]
328    pub quorum_type: QuorumType,
329
330    /// The quorum size
331    #[serde(
332        rename = "validator_set_quorum_size",
333        deserialize_with = "from_str_or_number"
334    )]
335    pub quorum_size: u16,
336
337    /// The quorum window (DKG interval)
338    /// On Mainnet Chain Locks are signed using 400_60: One quorum in every 288 blocks and activeQuorumCount is 4.
339    /// On Testnet Chain Locks are signed using 50_60: One quorum in every 24 blocks and activeQuorumCount is 24.
340    #[serde(
341        rename = "validator_set_quorum_window",
342        deserialize_with = "from_str_or_number"
343    )]
344    pub quorum_window: u32,
345
346    /// The number of active signers
347    #[serde(
348        rename = "validator_set_quorum_active_signers",
349        deserialize_with = "from_str_or_number"
350    )]
351    pub quorum_active_signers: u16,
352
353    /// Whether the quorum is rotated DIP24 or classic
354    #[serde(
355        rename = "validator_set_quorum_rotation",
356        deserialize_with = "from_str_or_number"
357    )]
358    pub quorum_rotation: bool,
359}
360
361impl Default for ValidatorSetConfig {
362    fn default() -> Self {
363        // Mainnet
364        Self::default_100_67()
365    }
366}
367
368impl ValidatorSetConfig {
369    /// Creates a default config for LLMQ 100 67
370    pub fn default_100_67() -> Self {
371        Self {
372            quorum_type: QuorumType::Llmq100_67,
373            quorum_size: 100,
374            quorum_window: 24,
375            quorum_active_signers: 24,
376            quorum_rotation: false,
377        }
378    }
379}
380
381impl QuorumLikeConfig for ValidatorSetConfig {
382    fn quorum_type(&self) -> QuorumType {
383        self.quorum_type
384    }
385
386    fn quorum_size(&self) -> u16 {
387        self.quorum_size
388    }
389
390    fn quorum_window(&self) -> u32 {
391        self.quorum_window
392    }
393
394    fn quorum_active_signers(&self) -> u16 {
395        self.quorum_active_signers
396    }
397
398    fn quorum_rotation(&self) -> bool {
399        self.quorum_rotation
400    }
401}
402
403/// Chain Lock quorum configuration
404#[derive(Clone, Debug, Serialize, Deserialize, Encode, Decode)]
405pub struct ChainLockConfig {
406    /// The quorum type used for verifying chain locks
407    #[serde(
408        rename = "chain_lock_quorum_type",
409        serialize_with = "serialize_quorum_type",
410        deserialize_with = "deserialize_quorum_type"
411    )]
412    pub quorum_type: QuorumType,
413
414    /// The quorum size
415    #[serde(
416        rename = "chain_lock_quorum_size",
417        deserialize_with = "from_str_or_number"
418    )]
419    pub quorum_size: u16,
420
421    /// The quorum window (DKG interval)
422    /// On Mainnet Chain Locks are signed using 400_60: One quorum in every 288 blocks and activeQuorumCount is 4.
423    /// On Testnet Chain Locks are signed using 50_60: One quorum in every 24 blocks and activeQuorumCount is 24.
424    #[serde(
425        rename = "chain_lock_quorum_window",
426        deserialize_with = "from_str_or_number"
427    )]
428    pub quorum_window: u32,
429
430    /// The number of active signers
431    #[serde(
432        rename = "chain_lock_quorum_active_signers",
433        deserialize_with = "from_str_or_number"
434    )]
435    pub quorum_active_signers: u16,
436
437    /// Whether the quorum is rotated DIP24 or classic
438    #[serde(
439        rename = "chain_lock_quorum_rotation",
440        deserialize_with = "from_str_or_number"
441    )]
442    pub quorum_rotation: bool,
443}
444
445impl Default for ChainLockConfig {
446    fn default() -> Self {
447        // Mainnet
448        Self {
449            quorum_type: QuorumType::Llmq400_60,
450            quorum_size: 400,
451            quorum_window: 24 * 12,
452            quorum_active_signers: 4,
453            quorum_rotation: false,
454        }
455    }
456}
457
458impl QuorumLikeConfig for ChainLockConfig {
459    fn quorum_type(&self) -> QuorumType {
460        self.quorum_type
461    }
462
463    fn quorum_size(&self) -> u16 {
464        self.quorum_size
465    }
466
467    fn quorum_window(&self) -> u32 {
468        self.quorum_window
469    }
470
471    fn quorum_active_signers(&self) -> u16 {
472        self.quorum_active_signers
473    }
474
475    fn quorum_rotation(&self) -> bool {
476        self.quorum_rotation
477    }
478}
479
480impl ChainLockConfig {
481    /// Creates a default config for LLMQ 100 67
482    pub fn default_100_67() -> Self {
483        Self {
484            quorum_type: QuorumType::Llmq100_67,
485            quorum_size: 100,
486            quorum_window: 24,
487            quorum_active_signers: 24,
488            quorum_rotation: false,
489        }
490    }
491}
492
493/// Chain Lock quorum configuration
494#[derive(Clone, Debug, Serialize, Deserialize, Encode, Decode)]
495pub struct InstantLockConfig {
496    /// The quorum type used for verifying chain locks
497    #[serde(
498        rename = "instant_lock_quorum_type",
499        serialize_with = "serialize_quorum_type",
500        deserialize_with = "deserialize_quorum_type"
501    )]
502    pub quorum_type: QuorumType,
503
504    /// The quorum size
505    #[serde(
506        rename = "instant_lock_quorum_size",
507        deserialize_with = "from_str_or_number"
508    )]
509    pub quorum_size: u16,
510
511    /// The quorum window (DKG interval)
512    /// On Mainnet Chain Locks are signed using 400_60: One quorum in every 288 blocks and activeQuorumCount is 4.
513    /// On Testnet Chain Locks are signed using 50_60: One quorum in every 24 blocks and activeQuorumCount is 24.
514    #[serde(
515        rename = "instant_lock_quorum_window",
516        deserialize_with = "from_str_or_number"
517    )]
518    pub quorum_window: u32,
519
520    /// The number of active signers
521    #[serde(
522        rename = "instant_lock_quorum_active_signers",
523        deserialize_with = "from_str_or_number"
524    )]
525    pub quorum_active_signers: u16,
526
527    /// Whether the quorum is rotated DIP24 or classic
528    #[serde(
529        rename = "instant_lock_quorum_rotation",
530        deserialize_with = "from_str_or_number"
531    )]
532    pub quorum_rotation: bool,
533}
534
535impl Default for InstantLockConfig {
536    fn default() -> Self {
537        // Mainnet
538        Self {
539            quorum_type: QuorumType::Llmq60_75,
540            quorum_active_signers: 32,
541            quorum_size: 60,
542            quorum_window: 24 * 12,
543            quorum_rotation: true,
544        }
545    }
546}
547
548impl InstantLockConfig {
549    /// Creates a default config for LLMQ 100 67
550    pub fn default_100_67() -> Self {
551        Self {
552            quorum_type: QuorumType::Llmq100_67,
553            quorum_size: 100,
554            quorum_window: 24,
555            quorum_active_signers: 24,
556            quorum_rotation: false,
557        }
558    }
559}
560
561impl QuorumLikeConfig for InstantLockConfig {
562    fn quorum_type(&self) -> QuorumType {
563        self.quorum_type
564    }
565
566    fn quorum_size(&self) -> u16 {
567        self.quorum_size
568    }
569
570    fn quorum_window(&self) -> u32 {
571        self.quorum_window
572    }
573
574    fn quorum_active_signers(&self) -> u16 {
575        self.quorum_active_signers
576    }
577
578    fn quorum_rotation(&self) -> bool {
579        self.quorum_rotation
580    }
581}
582
583fn serialize_quorum_type<S>(quorum_type: &QuorumType, serializer: S) -> Result<S::Ok, S::Error>
584where
585    S: serde::Serializer,
586{
587    serializer.serialize_str(quorum_type.to_string().as_str())
588}
589
590fn deserialize_quorum_type<'de, D>(deserializer: D) -> Result<QuorumType, D::Error>
591where
592    D: serde::Deserializer<'de>,
593{
594    let quorum_type_name = String::deserialize(deserializer)?;
595
596    let quorum_type = if let Ok(t) = quorum_type_name.trim().parse::<u32>() {
597        QuorumType::from(t)
598    } else {
599        QuorumType::from(quorum_type_name.as_str())
600    };
601
602    if quorum_type == QuorumType::UNKNOWN {
603        return Err(serde::de::Error::custom(format!(
604            "unsupported QUORUM_TYPE: {}",
605            quorum_type_name
606        )));
607    };
608
609    Ok(quorum_type)
610}
611
612impl ExecutionConfig {
613    fn default_verify_sum_trees() -> bool {
614        true
615    }
616
617    fn default_verify_token_sum_trees() -> bool {
618        true
619    }
620
621    fn default_use_document_triggers() -> bool {
622        true
623    }
624
625    fn default_epoch_time_length_s() -> u64 {
626        788400
627    }
628}
629
630impl PlatformConfig {
631    fn default_initial_protocol_version() -> ProtocolVersion {
632        INITIAL_PROTOCOL_VERSION
633    }
634
635    fn default_network() -> Network {
636        Network::Mainnet
637    }
638
639    fn default_tokio_console_address() -> String {
640        String::from("127.0.0.1:6669")
641    }
642
643    fn default_tokio_console_retention_secs() -> u64 {
644        60 * 3
645    }
646}
647
648/// create new object using values from environment variables
649pub trait FromEnv {
650    /// create new object using values from environment variables
651    fn from_env() -> Result<Self, Error>
652    where
653        Self: Sized + DeserializeOwned,
654    {
655        envy::from_env::<Self>().map_err(Error::from)
656    }
657}
658
659impl FromEnv for PlatformConfig {
660    fn from_env() -> Result<Self, Error>
661    where
662        Self: Sized + DeserializeOwned,
663    {
664        let mut me = envy::from_env::<Self>().map_err(Error::from)?;
665        me.abci.log = LogConfigs::from_env()?;
666
667        Ok(me)
668    }
669}
670
671impl Default for ExecutionConfig {
672    fn default() -> Self {
673        Self {
674            use_document_triggers: ExecutionConfig::default_use_document_triggers(),
675            verify_sum_trees: ExecutionConfig::default_verify_sum_trees(),
676            verify_token_sum_trees: ExecutionConfig::default_verify_token_sum_trees(),
677            epoch_time_length_s: ExecutionConfig::default_epoch_time_length_s(),
678        }
679    }
680}
681
682impl Default for PlatformConfig {
683    fn default() -> Self {
684        Self::default_mainnet()
685    }
686}
687
688/// The platform config
689impl PlatformConfig {
690    /// The default depending on the network
691    pub fn default_for_network(network: Network) -> Self {
692        match network {
693            Network::Mainnet => Self::default_mainnet(),
694            Network::Testnet => Self::default_testnet(),
695            Network::Devnet => Self::default_devnet(),
696            Network::Regtest => Self::default_local(),
697            _ => Self::default_testnet(),
698        }
699    }
700
701    /// The default local config
702    pub fn default_local() -> Self {
703        Self {
704            network: Network::Regtest,
705            validator_set: ValidatorSetConfig {
706                quorum_type: QuorumType::LlmqTestPlatform,
707                quorum_size: 3,
708                quorum_window: 24,
709                quorum_active_signers: 2,
710                quorum_rotation: false,
711            },
712            chain_lock: ChainLockConfig {
713                quorum_type: QuorumType::LlmqTest,
714                quorum_active_signers: 2,
715                quorum_size: 3,
716                quorum_window: 24,
717                quorum_rotation: false,
718            },
719            instant_lock: InstantLockConfig {
720                quorum_type: QuorumType::LlmqTest,
721                quorum_active_signers: 2,
722                quorum_size: 3,
723                quorum_window: 24,
724                quorum_rotation: false,
725            },
726            block_spacing_ms: 5000,
727            drive: Default::default(),
728            abci: Default::default(),
729            core: Default::default(),
730            execution: Default::default(),
731            db_path: PathBuf::from("/var/lib/dash-platform/data"),
732            rejections_path: Some(PathBuf::from("/var/log/dash/rejected")),
733            #[cfg(feature = "testing-config")]
734            testing_configs: PlatformTestConfig::default(),
735            tokio_console_enabled: false,
736            tokio_console_address: PlatformConfig::default_tokio_console_address(),
737            tokio_console_retention_secs: PlatformConfig::default_tokio_console_retention_secs(),
738            prometheus_bind_address: None,
739            grpc_bind_address: "127.0.0.1:26670".to_string(),
740        }
741    }
742
743    /// The default devnet config
744    pub fn default_devnet() -> Self {
745        Self {
746            network: Network::Regtest,
747            validator_set: ValidatorSetConfig {
748                quorum_type: QuorumType::LlmqDevnetPlatform,
749                quorum_size: 12,
750                quorum_window: 24,
751                quorum_active_signers: 8,
752                quorum_rotation: false,
753            },
754            chain_lock: ChainLockConfig {
755                quorum_type: QuorumType::LlmqDevnetPlatform,
756                quorum_size: 12,
757                quorum_window: 24,
758                quorum_active_signers: 8,
759                quorum_rotation: false,
760            },
761            instant_lock: InstantLockConfig {
762                quorum_type: QuorumType::LlmqDevnetDip0024,
763                quorum_active_signers: 4,
764                quorum_size: 8,
765                quorum_window: 48,
766                quorum_rotation: true,
767            },
768            block_spacing_ms: 5000,
769            drive: Default::default(),
770            abci: Default::default(),
771            core: Default::default(),
772            execution: Default::default(),
773            db_path: PathBuf::from("/var/lib/dash-platform/data"),
774            rejections_path: Some(PathBuf::from("/var/log/dash/rejected")),
775            #[cfg(feature = "testing-config")]
776            testing_configs: PlatformTestConfig::default(),
777            tokio_console_enabled: false,
778            tokio_console_address: PlatformConfig::default_tokio_console_address(),
779            tokio_console_retention_secs: PlatformConfig::default_tokio_console_retention_secs(),
780            prometheus_bind_address: None,
781            grpc_bind_address: "127.0.0.1:26670".to_string(),
782        }
783    }
784
785    /// The default testnet config
786    pub fn default_testnet() -> Self {
787        Self {
788            network: Network::Testnet,
789            validator_set: ValidatorSetConfig {
790                quorum_type: QuorumType::Llmq25_67,
791                quorum_size: 25,
792                quorum_window: 24,
793                quorum_active_signers: 24,
794                quorum_rotation: false,
795            },
796            chain_lock: ChainLockConfig {
797                quorum_type: QuorumType::Llmq50_60,
798                quorum_active_signers: 24,
799                quorum_size: 50,
800                quorum_window: 24,
801                quorum_rotation: false,
802            },
803            instant_lock: InstantLockConfig {
804                quorum_type: QuorumType::Llmq60_75,
805                quorum_active_signers: 32,
806                quorum_size: 60,
807                quorum_window: 24 * 12,
808                quorum_rotation: true,
809            },
810            block_spacing_ms: 5000,
811            drive: DriveConfig::default_testnet(),
812            abci: Default::default(),
813            core: Default::default(),
814            execution: Default::default(),
815            db_path: PathBuf::from("/var/lib/dash-platform/data"),
816            rejections_path: Some(PathBuf::from("/var/log/dash/rejected")),
817            #[cfg(feature = "testing-config")]
818            testing_configs: PlatformTestConfig::default(),
819            prometheus_bind_address: None,
820            grpc_bind_address: "127.0.0.1:26670".to_string(),
821            tokio_console_enabled: false,
822            tokio_console_address: PlatformConfig::default_tokio_console_address(),
823            tokio_console_retention_secs: PlatformConfig::default_tokio_console_retention_secs(),
824        }
825    }
826
827    /// The default mainnet config
828    pub fn default_mainnet() -> Self {
829        Self {
830            network: Network::Mainnet,
831            validator_set: ValidatorSetConfig {
832                quorum_type: QuorumType::Llmq100_67,
833                quorum_size: 100,
834                quorum_window: 24,
835                quorum_active_signers: 24,
836                quorum_rotation: false,
837            },
838            chain_lock: ChainLockConfig {
839                quorum_type: QuorumType::Llmq400_60,
840                quorum_active_signers: 4,
841                quorum_size: 400,
842                quorum_window: 24 * 12,
843                quorum_rotation: false,
844            },
845            instant_lock: InstantLockConfig {
846                quorum_type: QuorumType::Llmq60_75,
847                quorum_active_signers: 32,
848                quorum_size: 60,
849                quorum_window: 24 * 12,
850                quorum_rotation: true,
851            },
852            block_spacing_ms: 5000,
853            drive: Default::default(),
854            abci: Default::default(),
855            core: Default::default(),
856            execution: Default::default(),
857            db_path: PathBuf::from("/var/lib/dash-platform/data"),
858            rejections_path: Some(PathBuf::from("/var/log/dash/rejected")),
859            #[cfg(feature = "testing-config")]
860            testing_configs: PlatformTestConfig::default(),
861            prometheus_bind_address: None,
862            grpc_bind_address: "127.0.0.1:26670".to_string(),
863            tokio_console_enabled: false,
864            tokio_console_address: PlatformConfig::default_tokio_console_address(),
865            tokio_console_retention_secs: PlatformConfig::default_tokio_console_retention_secs(),
866        }
867    }
868}
869
870#[cfg(feature = "testing-config")]
871/// Configs that should only happen during testing
872#[derive(Clone, Debug)]
873pub struct PlatformTestConfig {
874    /// Block signing
875    pub block_signing: bool,
876    /// Storing of platform state
877    pub store_platform_state: bool,
878    /// Block signature verification
879    pub block_commit_signature_verification: bool,
880    /// Disable instant lock signature verification
881    pub disable_instant_lock_signature_verification: bool,
882    /// Disable temporarily disabled contested documents validation
883    pub disable_contested_documents_is_allowed_validation: bool,
884    /// Disable checkpoint creation during tests
885    pub disable_checkpoints: bool,
886}
887
888#[cfg(feature = "testing-config")]
889impl PlatformTestConfig {
890    /// Much faster config for tests
891    pub fn default_minimal_verifications() -> Self {
892        Self {
893            block_signing: false,
894            store_platform_state: false,
895            block_commit_signature_verification: false,
896            disable_instant_lock_signature_verification: true,
897            disable_contested_documents_is_allowed_validation: true,
898            disable_checkpoints: true,
899        }
900    }
901}
902
903#[cfg(feature = "testing-config")]
904impl Default for PlatformTestConfig {
905    fn default() -> Self {
906        Self {
907            block_signing: true,
908            store_platform_state: true,
909            block_commit_signature_verification: true,
910            disable_instant_lock_signature_verification: false,
911            disable_contested_documents_is_allowed_validation: true,
912            disable_checkpoints: true,
913        }
914    }
915}
916
917#[cfg(test)]
918mod tests {
919    use super::FromEnv;
920    use crate::logging::LogDestination;
921    use dpp::dashcore::Network;
922    use dpp::dashcore_rpc::dashcore_rpc_json::QuorumType;
923    use std::env;
924
925    #[test]
926    fn test_config_from_env() {
927        // ABCI log configs are parsed manually, so they deserve separate handling
928        // Note that STDOUT is also defined in .env.example, but env var should overwrite it.
929        let vectors = &[
930            ("STDOUT", "pretty"),
931            ("UPPERCASE", "json"),
932            ("lowercase", "pretty"),
933            ("miXedC4s3", "full"),
934            ("123", "compact"),
935        ];
936        for vector in vectors {
937            env::set_var(format!("ABCI_LOG_{}_DESTINATION", vector.0), "bytes");
938            env::set_var(format!("ABCI_LOG_{}_FORMAT", vector.0), vector.1);
939        }
940
941        let envfile = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(".env.local");
942
943        dotenvy::from_path(envfile.as_path()).expect("cannot load .env file");
944        assert_eq!("/tmp/db", env::var("DB_PATH").unwrap());
945        assert_eq!("/tmp/rejected", env::var("REJECTIONS_PATH").unwrap());
946
947        let config = super::PlatformConfig::from_env().expect("expected config from env");
948        assert!(config.execution.verify_sum_trees);
949        assert_ne!(config.validator_set.quorum_type, QuorumType::UNKNOWN);
950        for id in vectors {
951            matches!(config.abci.log[id.0].destination, LogDestination::Bytes);
952        }
953    }
954
955    #[test]
956    #[ignore]
957    //todo: re-enable
958    fn test_config_from_testnet_propagates_network() {
959        // ABCI log configs are parsed manually, so they deserve separate handling
960        // Note that STDOUT is also defined in .env.example, but env var should overwrite it.
961
962        let envfile = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(".env.testnet");
963
964        dotenvy::from_path(envfile.as_path()).expect("cannot load .env file");
965
966        let config = super::PlatformConfig::from_env().expect("expected config from env");
967        assert!(config.execution.verify_sum_trees);
968        assert_eq!(config.validator_set.quorum_type, QuorumType::Llmq25_67);
969        assert_eq!(config.network, config.drive.network);
970        assert_eq!(config.network, Network::Testnet);
971    }
972}