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, DEFAULT_EPOCH_TIME_LENGTH_S};
10use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize};
11use std::path::PathBuf;
12
13#[derive(Clone, Debug, Serialize, Deserialize, Default)]
15pub struct ConsensusCoreRpcConfig {
16 #[serde(rename = "core_consensus_json_rpc_host")]
18 pub host: String,
19
20 #[serde(
22 rename = "core_consensus_json_rpc_port",
23 deserialize_with = "from_str_or_number"
24 )]
25 pub port: u16,
26
27 #[serde(rename = "core_consensus_json_rpc_username")]
29 pub username: String,
30
31 #[serde(rename = "core_consensus_json_rpc_password")]
33 pub password: String,
34}
35
36impl ConsensusCoreRpcConfig {
37 pub fn url(&self) -> String {
39 format!("{}:{}", self.host, self.port)
40 }
41}
42
43#[derive(Clone, Debug, Serialize, Deserialize, Default)]
45pub struct CheckTxCoreRpcConfig {
46 #[serde(rename = "core_check_tx_json_rpc_host")]
48 pub host: String,
49
50 #[serde(
52 rename = "core_check_tx_json_rpc_port",
53 deserialize_with = "from_str_or_number"
54 )]
55 pub port: u16,
56
57 #[serde(rename = "core_check_tx_json_rpc_username")]
59 pub username: String,
60
61 #[serde(rename = "core_check_tx_json_rpc_password")]
63 pub password: String,
64}
65
66impl CheckTxCoreRpcConfig {
67 pub fn url(&self) -> String {
69 format!("{}:{}", self.host, self.port)
70 }
71}
72
73#[derive(Clone, Debug, Serialize, Deserialize)]
75#[serde(default)]
76#[derive(Default)]
77pub struct CoreConfig {
78 #[serde(flatten)]
80 pub consensus_rpc: ConsensusCoreRpcConfig,
81 #[serde(flatten)]
83 pub check_tx_rpc: CheckTxCoreRpcConfig,
84}
85
86#[derive(Clone, Debug, Serialize, Deserialize)]
88pub struct ExecutionConfig {
91 #[serde(default = "ExecutionConfig::default_use_document_triggers")]
93 pub use_document_triggers: bool,
94
95 #[serde(default = "ExecutionConfig::default_verify_sum_trees")]
97 pub verify_sum_trees: bool,
98
99 #[serde(default = "ExecutionConfig::default_verify_token_sum_trees")]
101 pub verify_token_sum_trees: bool,
102
103 #[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#[derive(Clone, Debug, Serialize)]
128pub struct PlatformConfig {
131 pub network: Network,
133 #[serde(flatten)]
135 pub drive: DriveConfig,
136
137 #[serde(flatten)]
139 pub core: CoreConfig,
140
141 #[serde(flatten)]
143 pub abci: AbciConfig,
144
145 pub prometheus_bind_address: Option<String>,
154
155 pub grpc_bind_address: String,
157
158 #[serde(flatten)]
160 pub execution: ExecutionConfig,
161
162 #[serde(flatten)]
164 pub validator_set: ValidatorSetConfig,
165
166 #[serde(flatten)]
168 pub chain_lock: ChainLockConfig,
169
170 #[serde(flatten)]
172 pub instant_lock: InstantLockConfig,
173
174 pub block_spacing_ms: u64,
177
178 pub db_path: PathBuf,
180
181 pub rejections_path: Option<PathBuf>,
186
187 #[cfg(feature = "testing-config")]
188 #[serde(skip)]
190 pub testing_configs: PlatformTestConfig,
191
192 pub tokio_console_enabled: bool,
194
195 pub tokio_console_address: String,
198
199 pub tokio_console_retention_secs: u64,
201}
202
203#[derive(Deserialize)]
205struct PlatformConfigIntermediate {
206 #[serde(
208 default = "PlatformConfig::default_network",
209 deserialize_with = "from_str_to_network_with_aliases"
210 )]
211 pub network: Network,
212 #[serde(flatten)]
214 pub drive: DriveConfig,
215 #[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 #[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 let mut config = PlatformConfigIntermediate::deserialize(deserializer)?;
255
256 config.drive.network = config.network;
258
259 Ok(PlatformConfig {
261 network: config.network,
262 drive: config.drive,
263 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
301pub trait QuorumLikeConfig: Sized {
303 fn quorum_type(&self) -> QuorumType;
305
306 fn quorum_size(&self) -> u16;
308
309 fn quorum_window(&self) -> u32;
311
312 fn quorum_active_signers(&self) -> u16;
314
315 fn quorum_rotation(&self) -> bool;
317}
318
319#[derive(Clone, Debug, Serialize, Deserialize, Encode, Decode)]
321pub struct ValidatorSetConfig {
322 #[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 #[serde(
332 rename = "validator_set_quorum_size",
333 deserialize_with = "from_str_or_number"
334 )]
335 pub quorum_size: u16,
336
337 #[serde(
341 rename = "validator_set_quorum_window",
342 deserialize_with = "from_str_or_number"
343 )]
344 pub quorum_window: u32,
345
346 #[serde(
348 rename = "validator_set_quorum_active_signers",
349 deserialize_with = "from_str_or_number"
350 )]
351 pub quorum_active_signers: u16,
352
353 #[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 Self::default_100_67()
365 }
366}
367
368impl ValidatorSetConfig {
369 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#[derive(Clone, Debug, Serialize, Deserialize, Encode, Decode)]
405pub struct ChainLockConfig {
406 #[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 #[serde(
416 rename = "chain_lock_quorum_size",
417 deserialize_with = "from_str_or_number"
418 )]
419 pub quorum_size: u16,
420
421 #[serde(
425 rename = "chain_lock_quorum_window",
426 deserialize_with = "from_str_or_number"
427 )]
428 pub quorum_window: u32,
429
430 #[serde(
432 rename = "chain_lock_quorum_active_signers",
433 deserialize_with = "from_str_or_number"
434 )]
435 pub quorum_active_signers: u16,
436
437 #[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 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 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#[derive(Clone, Debug, Serialize, Deserialize, Encode, Decode)]
495pub struct InstantLockConfig {
496 #[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 #[serde(
506 rename = "instant_lock_quorum_size",
507 deserialize_with = "from_str_or_number"
508 )]
509 pub quorum_size: u16,
510
511 #[serde(
515 rename = "instant_lock_quorum_window",
516 deserialize_with = "from_str_or_number"
517 )]
518 pub quorum_window: u32,
519
520 #[serde(
522 rename = "instant_lock_quorum_active_signers",
523 deserialize_with = "from_str_or_number"
524 )]
525 pub quorum_active_signers: u16,
526
527 #[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 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 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 DEFAULT_EPOCH_TIME_LENGTH_S
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
648pub trait FromEnv {
650 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
688impl PlatformConfig {
690 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 }
698 }
699
700 pub fn default_local() -> Self {
702 Self {
703 network: Network::Regtest,
704 validator_set: ValidatorSetConfig {
705 quorum_type: QuorumType::LlmqTestPlatform,
706 quorum_size: 3,
707 quorum_window: 24,
708 quorum_active_signers: 2,
709 quorum_rotation: false,
710 },
711 chain_lock: ChainLockConfig {
712 quorum_type: QuorumType::LlmqTest,
713 quorum_active_signers: 2,
714 quorum_size: 3,
715 quorum_window: 24,
716 quorum_rotation: false,
717 },
718 instant_lock: InstantLockConfig {
719 quorum_type: QuorumType::LlmqTest,
720 quorum_active_signers: 2,
721 quorum_size: 3,
722 quorum_window: 24,
723 quorum_rotation: false,
724 },
725 block_spacing_ms: 5000,
726 drive: Default::default(),
727 abci: Default::default(),
728 core: Default::default(),
729 execution: Default::default(),
730 db_path: PathBuf::from("/var/lib/dash-platform/data"),
731 rejections_path: Some(PathBuf::from("/var/log/dash/rejected")),
732 #[cfg(feature = "testing-config")]
733 testing_configs: PlatformTestConfig::default(),
734 tokio_console_enabled: false,
735 tokio_console_address: PlatformConfig::default_tokio_console_address(),
736 tokio_console_retention_secs: PlatformConfig::default_tokio_console_retention_secs(),
737 prometheus_bind_address: None,
738 grpc_bind_address: "127.0.0.1:26670".to_string(),
739 }
740 }
741
742 pub fn default_devnet() -> Self {
744 Self {
745 network: Network::Regtest,
746 validator_set: ValidatorSetConfig {
747 quorum_type: QuorumType::LlmqDevnetPlatform,
748 quorum_size: 12,
749 quorum_window: 24,
750 quorum_active_signers: 8,
751 quorum_rotation: false,
752 },
753 chain_lock: ChainLockConfig {
754 quorum_type: QuorumType::LlmqDevnetPlatform,
755 quorum_size: 12,
756 quorum_window: 24,
757 quorum_active_signers: 8,
758 quorum_rotation: false,
759 },
760 instant_lock: InstantLockConfig {
761 quorum_type: QuorumType::LlmqDevnetDip0024,
762 quorum_active_signers: 4,
763 quorum_size: 8,
764 quorum_window: 48,
765 quorum_rotation: true,
766 },
767 block_spacing_ms: 5000,
768 drive: Default::default(),
769 abci: Default::default(),
770 core: Default::default(),
771 execution: Default::default(),
772 db_path: PathBuf::from("/var/lib/dash-platform/data"),
773 rejections_path: Some(PathBuf::from("/var/log/dash/rejected")),
774 #[cfg(feature = "testing-config")]
775 testing_configs: PlatformTestConfig::default(),
776 tokio_console_enabled: false,
777 tokio_console_address: PlatformConfig::default_tokio_console_address(),
778 tokio_console_retention_secs: PlatformConfig::default_tokio_console_retention_secs(),
779 prometheus_bind_address: None,
780 grpc_bind_address: "127.0.0.1:26670".to_string(),
781 }
782 }
783
784 pub fn default_testnet() -> Self {
786 Self {
787 network: Network::Testnet,
788 validator_set: ValidatorSetConfig {
789 quorum_type: QuorumType::Llmq25_67,
790 quorum_size: 25,
791 quorum_window: 24,
792 quorum_active_signers: 24,
793 quorum_rotation: false,
794 },
795 chain_lock: ChainLockConfig {
796 quorum_type: QuorumType::Llmq50_60,
797 quorum_active_signers: 24,
798 quorum_size: 50,
799 quorum_window: 24,
800 quorum_rotation: false,
801 },
802 instant_lock: InstantLockConfig {
803 quorum_type: QuorumType::Llmq60_75,
804 quorum_active_signers: 32,
805 quorum_size: 60,
806 quorum_window: 24 * 12,
807 quorum_rotation: true,
808 },
809 block_spacing_ms: 5000,
810 drive: DriveConfig::default_testnet(),
811 abci: Default::default(),
812 core: Default::default(),
813 execution: Default::default(),
814 db_path: PathBuf::from("/var/lib/dash-platform/data"),
815 rejections_path: Some(PathBuf::from("/var/log/dash/rejected")),
816 #[cfg(feature = "testing-config")]
817 testing_configs: PlatformTestConfig::default(),
818 prometheus_bind_address: None,
819 grpc_bind_address: "127.0.0.1:26670".to_string(),
820 tokio_console_enabled: false,
821 tokio_console_address: PlatformConfig::default_tokio_console_address(),
822 tokio_console_retention_secs: PlatformConfig::default_tokio_console_retention_secs(),
823 }
824 }
825
826 pub fn default_mainnet() -> Self {
828 Self {
829 network: Network::Mainnet,
830 validator_set: ValidatorSetConfig {
831 quorum_type: QuorumType::Llmq100_67,
832 quorum_size: 100,
833 quorum_window: 24,
834 quorum_active_signers: 24,
835 quorum_rotation: false,
836 },
837 chain_lock: ChainLockConfig {
838 quorum_type: QuorumType::Llmq400_60,
839 quorum_active_signers: 4,
840 quorum_size: 400,
841 quorum_window: 24 * 12,
842 quorum_rotation: false,
843 },
844 instant_lock: InstantLockConfig {
845 quorum_type: QuorumType::Llmq60_75,
846 quorum_active_signers: 32,
847 quorum_size: 60,
848 quorum_window: 24 * 12,
849 quorum_rotation: true,
850 },
851 block_spacing_ms: 5000,
852 drive: Default::default(),
853 abci: Default::default(),
854 core: Default::default(),
855 execution: Default::default(),
856 db_path: PathBuf::from("/var/lib/dash-platform/data"),
857 rejections_path: Some(PathBuf::from("/var/log/dash/rejected")),
858 #[cfg(feature = "testing-config")]
859 testing_configs: PlatformTestConfig::default(),
860 prometheus_bind_address: None,
861 grpc_bind_address: "127.0.0.1:26670".to_string(),
862 tokio_console_enabled: false,
863 tokio_console_address: PlatformConfig::default_tokio_console_address(),
864 tokio_console_retention_secs: PlatformConfig::default_tokio_console_retention_secs(),
865 }
866 }
867}
868
869#[derive(Clone, Copy, Debug, PartialEq, Eq)]
874pub enum CheckpointStep {
875 CreateDirectory,
877 CreateCheckpoint,
879 WriteState,
881 OpenCheckpoint,
883}
884
885impl CheckpointStep {
886 pub fn description(self) -> &'static str {
888 match self {
889 CheckpointStep::CreateDirectory => "trying to create checkpoints directory",
890 CheckpointStep::CreateCheckpoint => "trying to create grovedb checkpoint",
891 CheckpointStep::WriteState => "trying to write checkpoint platform state",
892 CheckpointStep::OpenCheckpoint => "trying to open grovedb checkpoint",
893 }
894 }
895}
896
897#[cfg(feature = "testing-config")]
898#[derive(Clone, Debug)]
900pub struct PlatformTestConfig {
901 pub block_signing: bool,
903 pub store_platform_state: bool,
905 pub block_commit_signature_verification: bool,
907 pub disable_instant_lock_signature_verification: bool,
909 pub disable_checkpoints: bool,
911 pub checkpoint_faults:
918 std::sync::Arc<std::sync::Mutex<std::collections::VecDeque<CheckpointStep>>>,
919}
920
921#[cfg(feature = "testing-config")]
922impl PlatformTestConfig {
923 pub fn default_minimal_verifications() -> Self {
925 Self {
926 block_signing: false,
927 store_platform_state: false,
928 block_commit_signature_verification: false,
929 disable_instant_lock_signature_verification: true,
930 disable_checkpoints: true,
931 checkpoint_faults: Default::default(),
932 }
933 }
934}
935
936#[cfg(feature = "testing-config")]
937impl Default for PlatformTestConfig {
938 fn default() -> Self {
939 Self {
940 block_signing: true,
941 store_platform_state: true,
942 block_commit_signature_verification: true,
943 disable_instant_lock_signature_verification: false,
944 disable_checkpoints: true,
945 checkpoint_faults: Default::default(),
946 }
947 }
948}
949
950#[cfg(test)]
951mod tests {
952 use super::FromEnv;
953 use crate::config::{
954 ChainLockConfig, ConsensusCoreRpcConfig, CoreConfig, InstantLockConfig, PlatformConfig,
955 QuorumLikeConfig, ValidatorSetConfig,
956 };
957 use crate::logging::LogDestination;
958 use dpp::dashcore::Network;
959 use dpp::dashcore_rpc::dashcore_rpc_json::QuorumType;
960 use std::env;
961
962 #[test]
963 fn test_config_from_env() {
964 let vectors = &[
967 ("STDOUT", "pretty"),
968 ("UPPERCASE", "json"),
969 ("lowercase", "pretty"),
970 ("miXedC4s3", "full"),
971 ("123", "compact"),
972 ];
973 for vector in vectors {
974 env::set_var(format!("ABCI_LOG_{}_DESTINATION", vector.0), "bytes");
975 env::set_var(format!("ABCI_LOG_{}_FORMAT", vector.0), vector.1);
976 }
977
978 let envfile = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(".env.local");
979
980 dotenvy::from_path(envfile.as_path()).expect("cannot load .env file");
981 assert_eq!("/tmp/db", env::var("DB_PATH").unwrap());
982 assert_eq!("/tmp/rejected", env::var("REJECTIONS_PATH").unwrap());
983
984 let config = super::PlatformConfig::from_env().expect("expected config from env");
985 assert!(config.execution.verify_sum_trees);
986 assert_ne!(config.validator_set.quorum_type, QuorumType::UNKNOWN);
987 for id in vectors {
988 matches!(config.abci.log[id.0].destination, LogDestination::Bytes);
989 }
990 }
991
992 #[test]
993 #[ignore]
994 fn test_config_from_testnet_propagates_network() {
996 let envfile = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(".env.testnet");
1000
1001 dotenvy::from_path(envfile.as_path()).expect("cannot load .env file");
1002
1003 let config = super::PlatformConfig::from_env().expect("expected config from env");
1004 assert!(config.execution.verify_sum_trees);
1005 assert_eq!(config.validator_set.quorum_type, QuorumType::Llmq25_67);
1006 assert_eq!(config.network, config.drive.network);
1007 assert_eq!(config.network, Network::Testnet);
1008 }
1009
1010 #[test]
1017 fn network_aliases_deserialize_all_variants_case_insensitively() {
1018 let cases = &[
1019 ("\"dash\"", Network::Mainnet),
1020 ("\"DASH\"", Network::Mainnet),
1021 ("\"MainNet\"", Network::Mainnet),
1022 ("\"main\"", Network::Mainnet),
1023 ("\"local\"", Network::Regtest),
1024 ("\"regtest\"", Network::Regtest),
1025 ("\"testnet\"", Network::Testnet),
1026 ("\"TEST\"", Network::Testnet),
1027 ("\"devnet\"", Network::Devnet),
1028 ("\"Dev\"", Network::Devnet),
1029 ];
1030
1031 for (input, expected) in cases {
1032 let mut de = serde_json::Deserializer::from_str(input);
1033 let network = super::from_str_to_network_with_aliases(&mut de)
1034 .expect("alias should parse successfully");
1035 assert_eq!(
1036 &network, expected,
1037 "input {} should map to {:?}",
1038 input, expected
1039 );
1040 }
1041 }
1042
1043 #[test]
1046 fn network_alias_unknown_value_returns_custom_error() {
1047 let mut de = serde_json::Deserializer::from_str("\"nonsense_network\"");
1048 let err = super::from_str_to_network_with_aliases(&mut de)
1049 .expect_err("unknown network should fail");
1050 let msg = err.to_string();
1051 assert!(
1052 msg.contains("unknown network") && msg.contains("nonsense_network"),
1053 "error message should name the offending value, got: {}",
1054 msg
1055 );
1056 }
1057
1058 #[test]
1062 fn deserialize_quorum_type_accepts_string_names() {
1063 let json = r#"{
1066 "validator_set_quorum_type": "llmq_25_67",
1067 "validator_set_quorum_size": "25",
1068 "validator_set_quorum_window": "24",
1069 "validator_set_quorum_active_signers": "24",
1070 "validator_set_quorum_rotation": "false"
1071 }"#;
1072 let cfg: ValidatorSetConfig =
1073 serde_json::from_str(json).expect("quorum type string should deserialize");
1074 assert_eq!(cfg.quorum_type, QuorumType::Llmq25_67);
1075 }
1076
1077 #[test]
1080 fn deserialize_quorum_type_accepts_numeric_string() {
1081 let json = r#"{
1082 "validator_set_quorum_type": "6",
1083 "validator_set_quorum_size": "25",
1084 "validator_set_quorum_window": "24",
1085 "validator_set_quorum_active_signers": "24",
1086 "validator_set_quorum_rotation": "false"
1087 }"#;
1088 let cfg: ValidatorSetConfig =
1089 serde_json::from_str(json).expect("numeric quorum type should deserialize");
1090 assert_ne!(cfg.quorum_type, QuorumType::UNKNOWN);
1092 }
1093
1094 #[test]
1096 fn deserialize_quorum_type_rejects_unknown_names() {
1097 let json = r#"{
1098 "validator_set_quorum_type": "this_is_not_a_quorum",
1099 "validator_set_quorum_size": "25",
1100 "validator_set_quorum_window": "24",
1101 "validator_set_quorum_active_signers": "24",
1102 "validator_set_quorum_rotation": "false"
1103 }"#;
1104 let err = serde_json::from_str::<ValidatorSetConfig>(json)
1105 .expect_err("unknown quorum type name should fail");
1106 let msg = err.to_string();
1107 assert!(
1108 msg.contains("unsupported") && msg.contains("QUORUM_TYPE"),
1109 "expected unsupported QUORUM_TYPE error, got: {}",
1110 msg
1111 );
1112 }
1113
1114 #[test]
1118 fn consensus_core_rpc_url_composes_host_and_port() {
1119 let cfg = ConsensusCoreRpcConfig {
1120 host: "node.example".to_string(),
1121 port: 9998,
1122 username: "u".to_string(),
1123 password: "p".to_string(),
1124 };
1125 assert_eq!(cfg.url(), "node.example:9998");
1126 }
1127
1128 #[test]
1130 fn check_tx_core_rpc_url_composes_host_and_port() {
1131 let cfg = super::CheckTxCoreRpcConfig {
1132 host: "127.0.0.1".to_string(),
1133 port: 1,
1134 username: String::new(),
1135 password: String::new(),
1136 };
1137 assert_eq!(cfg.url(), "127.0.0.1:1");
1138 }
1139
1140 #[test]
1143 fn core_config_default_is_empty() {
1144 let cfg = CoreConfig::default();
1145 assert_eq!(cfg.consensus_rpc.host, "");
1146 assert_eq!(cfg.consensus_rpc.port, 0);
1147 assert_eq!(cfg.check_tx_rpc.host, "");
1148 assert_eq!(cfg.check_tx_rpc.port, 0);
1149 }
1150
1151 #[test]
1156 fn default_for_network_dispatches_all_known_networks() {
1157 let mainnet = PlatformConfig::default_for_network(Network::Mainnet);
1158 assert_eq!(mainnet.network, Network::Mainnet);
1159 assert_eq!(mainnet.validator_set.quorum_type, QuorumType::Llmq100_67);
1160
1161 let testnet = PlatformConfig::default_for_network(Network::Testnet);
1162 assert_eq!(testnet.network, Network::Testnet);
1163 assert_eq!(testnet.validator_set.quorum_type, QuorumType::Llmq25_67);
1164
1165 let devnet = PlatformConfig::default_for_network(Network::Devnet);
1166 assert_eq!(devnet.network, Network::Regtest);
1168
1169 let regtest = PlatformConfig::default_for_network(Network::Regtest);
1170 assert_eq!(regtest.network, Network::Regtest);
1171 }
1172
1173 #[test]
1179 fn validator_set_default_100_67_accessors() {
1180 let cfg = ValidatorSetConfig::default_100_67();
1181 assert_eq!(cfg.quorum_type(), QuorumType::Llmq100_67);
1182 assert_eq!(cfg.quorum_size(), 100);
1183 assert_eq!(cfg.quorum_window(), 24);
1184 assert_eq!(cfg.quorum_active_signers(), 24);
1185 assert!(!cfg.quorum_rotation());
1186 }
1187
1188 #[test]
1190 fn chain_lock_default_100_67_accessors() {
1191 let cfg = ChainLockConfig::default_100_67();
1192 assert_eq!(cfg.quorum_type(), QuorumType::Llmq100_67);
1193 assert_eq!(cfg.quorum_size(), 100);
1194 assert_eq!(cfg.quorum_window(), 24);
1195 assert_eq!(cfg.quorum_active_signers(), 24);
1196 assert!(!cfg.quorum_rotation());
1197 }
1198
1199 #[test]
1201 fn chain_lock_default_is_llmq_400_60() {
1202 let cfg = ChainLockConfig::default();
1203 assert_eq!(cfg.quorum_type(), QuorumType::Llmq400_60);
1204 assert_eq!(cfg.quorum_size(), 400);
1205 assert_eq!(cfg.quorum_window(), 24 * 12);
1206 assert_eq!(cfg.quorum_active_signers(), 4);
1207 assert!(!cfg.quorum_rotation());
1208 }
1209
1210 #[test]
1212 fn instant_lock_default_is_llmq_60_75_rotated() {
1213 let cfg = InstantLockConfig::default();
1214 assert_eq!(cfg.quorum_type(), QuorumType::Llmq60_75);
1215 assert_eq!(cfg.quorum_size(), 60);
1216 assert_eq!(cfg.quorum_window(), 24 * 12);
1217 assert_eq!(cfg.quorum_active_signers(), 32);
1218 assert!(cfg.quorum_rotation());
1219 }
1220
1221 #[test]
1223 fn instant_lock_default_100_67_accessors() {
1224 let cfg = InstantLockConfig::default_100_67();
1225 assert_eq!(cfg.quorum_type(), QuorumType::Llmq100_67);
1226 assert_eq!(cfg.quorum_size(), 100);
1227 assert_eq!(cfg.quorum_window(), 24);
1228 assert_eq!(cfg.quorum_active_signers(), 24);
1229 assert!(!cfg.quorum_rotation());
1230 }
1231
1232 #[test]
1234 fn platform_config_default_is_mainnet() {
1235 let cfg = PlatformConfig::default();
1236 assert_eq!(cfg.network, Network::Mainnet);
1237 assert_eq!(cfg.validator_set.quorum_type, QuorumType::Llmq100_67);
1238 assert_eq!(cfg.chain_lock.quorum_type, QuorumType::Llmq400_60);
1239 assert_eq!(cfg.instant_lock.quorum_type, QuorumType::Llmq60_75);
1240 assert_eq!(cfg.block_spacing_ms, 5000);
1241 }
1242
1243 #[test]
1247 fn execution_config_defaults() {
1248 let cfg = super::ExecutionConfig::default();
1249 assert!(cfg.verify_sum_trees);
1250 assert!(cfg.verify_token_sum_trees);
1251 assert!(cfg.use_document_triggers);
1252 assert_eq!(cfg.epoch_time_length_s, 788400);
1253 }
1254
1255 #[test]
1260 fn validator_set_config_serializes_quorum_type_as_string() {
1261 let cfg = ValidatorSetConfig::default_100_67();
1262 let json = serde_json::to_string(&cfg).expect("valid serialize");
1263 assert!(
1265 json.contains("llmq_100_67") || json.contains("Llmq100_67"),
1266 "expected textual quorum type in JSON, got: {}",
1267 json
1268 );
1269 }
1270}