1use std::collections::{BTreeSet, HashMap, HashSet};
2use std::path::PathBuf;
3use std::str::FromStr;
4
5use anyhow::Context;
6use config::{ConfigError, Map, Source, Value, ValueKind};
7use serde::Deserialize;
8
9use mithril_cardano_node_chain::chain_observer::ChainObserverType;
10use mithril_cli_helper::{register_config_value, serde_deserialization};
11use mithril_common::crypto_helper::{ManifestSigner, ProtocolGenesisSigner};
12use mithril_common::entities::{
13 BlockNumber, CardanoBlocksTransactionsSigningConfig, CardanoTransactionsSigningConfig,
14 CompressionAlgorithm, ConfigSecret, HexEncodedGenesisVerificationKey, HexEncodedKey,
15 ProtocolParameters, SignedEntityConfig, SignedEntityTypeDiscriminants,
16};
17use mithril_common::{AggregateSignatureType, CardanoNetwork, StdResult};
18use mithril_dmq::DmqNetwork;
19use mithril_doc::{Documenter, DocumenterDefault, StructDoc};
20use mithril_era::adapters::EraReaderAdapterType;
21
22use crate::entities::AggregatorEpochSettings;
23use crate::http_server::SERVER_BASE_PATH;
24use crate::services::ancillary_signer::GcpCryptoKeyVersionResourceName;
25use crate::tools::DEFAULT_GCP_CREDENTIALS_JSON_ENV_VAR;
26use crate::tools::url_sanitizer::SanitizedUrlWithTrailingSlash;
27
28#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
30pub enum ExecutionEnvironment {
31 Test,
33
34 Production,
37}
38
39impl FromStr for ExecutionEnvironment {
40 type Err = ConfigError;
41
42 fn from_str(s: &str) -> Result<Self, Self::Err> {
43 match s {
44 "production" => Ok(Self::Production),
45 "test" => Ok(Self::Test),
46 _ => Err(ConfigError::Message(format!(
47 "Unknown execution environment {s}"
48 ))),
49 }
50 }
51}
52
53pub trait ConfigurationSource {
58 fn environment(&self) -> ExecutionEnvironment;
60
61 fn cardano_cli_path(&self) -> PathBuf {
63 panic!("cardano_cli_path is not implemented.");
64 }
65
66 fn cardano_node_socket_path(&self) -> PathBuf {
68 panic!("cardano_node_socket_path is not implemented.");
69 }
70
71 fn dmq_node_socket_path(&self) -> Option<PathBuf> {
73 panic!("dmq_node_socket_path is not implemented.");
74 }
75
76 fn cardano_node_version(&self) -> String {
82 panic!("cardano_node_version is not implemented.");
83 }
84
85 fn network(&self) -> String {
87 panic!("network is not implemented.");
88 }
89
90 fn network_magic(&self) -> Option<u64> {
94 panic!("network_magic is not implemented.");
95 }
96
97 fn dmq_network_magic(&self) -> Option<u64> {
101 panic!("dmq_network_magic is not implemented.");
102 }
103
104 fn chain_observer_type(&self) -> ChainObserverType {
106 panic!("chain_observer_type is not implemented.");
107 }
108
109 fn protocol_parameters(&self) -> Option<ProtocolParameters> {
111 panic!("protocol_parameters is not implemented.");
112 }
113
114 fn snapshot_uploader_type(&self) -> SnapshotUploaderType {
116 panic!("snapshot_uploader_type is not implemented.");
117 }
118
119 fn snapshot_bucket_name(&self) -> Option<String> {
121 panic!("snapshot_bucket_name is not implemented.");
122 }
123
124 fn snapshot_use_cdn_domain(&self) -> bool {
126 panic!("snapshot_use_cdn_domain is not implemented.");
127 }
128
129 fn server_ip(&self) -> String {
131 panic!("server_ip is not implemented.");
132 }
133
134 fn server_port(&self) -> u16 {
136 panic!("server_port is not implemented.");
137 }
138
139 fn public_server_url(&self) -> Option<String> {
141 panic!("public_server_url is not implemented.");
142 }
143
144 fn run_interval(&self) -> u64 {
146 panic!("run_interval is not implemented.");
147 }
148
149 fn db_directory(&self) -> PathBuf {
151 panic!("db_directory is not implemented.");
152 }
153
154 fn snapshot_directory(&self) -> PathBuf {
156 panic!("snapshot_directory is not implemented.");
157 }
158
159 fn data_stores_directory(&self) -> PathBuf {
161 panic!("data_stores_directory is not implemented.");
162 }
163
164 fn genesis_verification_key(&self) -> HexEncodedGenesisVerificationKey {
166 panic!("genesis_verification_key is not implemented.");
167 }
168
169 fn reset_digests_cache(&self) -> bool {
171 panic!("reset_digests_cache is not implemented.");
172 }
173
174 fn disable_digests_cache(&self) -> bool {
176 panic!("disable_digests_cache is not implemented.");
177 }
178
179 fn store_retention_limit(&self) -> Option<usize> {
184 panic!("store_retention_limit is not implemented.");
185 }
186
187 fn era_reader_adapter_type(&self) -> EraReaderAdapterType {
189 panic!("era_reader_adapter_type is not implemented.");
190 }
191
192 fn era_reader_adapter_params(&self) -> Option<String> {
194 panic!("era_reader_adapter_params is not implemented.");
195 }
196
197 fn ancillary_files_signer_config(&self) -> AncillaryFilesSignerConfig {
201 panic!("ancillary_files_signer_config is not implemented.");
202 }
203
204 fn signed_entity_types(&self) -> Option<String> {
210 panic!("signed_entity_types is not implemented.");
211 }
212
213 fn snapshot_compression_algorithm(&self) -> CompressionAlgorithm {
215 panic!("snapshot_compression_algorithm is not implemented.");
216 }
217
218 fn zstandard_parameters(&self) -> Option<ZstandardCompressionParameters> {
221 panic!("zstandard_parameters is not implemented.");
222 }
223
224 fn blockfrost_parameters(&self) -> Option<BlockfrostParameters> {
226 panic!("blockfrost_parameters is not implemented.");
227 }
228
229 fn signer_importer_run_interval(&self) -> u64 {
231 panic!("signer_importer_run_interval is not implemented.");
232 }
233
234 fn allow_unparsable_block(&self) -> bool {
238 panic!("allow_unparsable_block is not implemented.");
239 }
240
241 fn cardano_blocks_transactions_prover_cache_pool_size(&self) -> usize {
243 panic!("cardano_blocks_transactions_prover_cache_pool_size is not implemented.");
244 }
245
246 fn cardano_blocks_transactions_database_connection_pool_size(&self) -> usize {
248 panic!("cardano_blocks_transactions_database_connection_pool_size is not implemented.");
249 }
250
251 fn cardano_transactions_prover_cache_pool_size(&self) -> usize {
253 panic!("cardano_transactions_prover_cache_pool_size is not implemented.");
254 }
255
256 fn cardano_transactions_database_connection_pool_size(&self) -> usize {
258 panic!("cardano_transactions_database_connection_pool_size is not implemented.");
259 }
260
261 fn cardano_transactions_signing_config(&self) -> Option<CardanoTransactionsSigningConfig> {
263 panic!("cardano_transactions_signing_config is not implemented.");
264 }
265
266 fn cardano_blocks_transactions_signing_config(
268 &self,
269 ) -> Option<CardanoBlocksTransactionsSigningConfig> {
270 panic!("cardano_blocks_transactions_signing_config is not implemented.");
271 }
272
273 fn preload_security_parameter(&self) -> BlockNumber {
275 panic!("preload_security_parameter is not implemented.");
276 }
277
278 fn cardano_transactions_prover_max_hashes_allowed_by_request(&self) -> usize {
280 panic!("cardano_transactions_prover_max_hashes_allowed_by_request is not implemented.");
281 }
282
283 fn cardano_transactions_block_streamer_max_roll_forwards_per_poll(&self) -> usize {
285 panic!(
286 "cardano_transactions_block_streamer_max_roll_forwards_per_poll is not implemented."
287 );
288 }
289
290 fn cardano_transactions_block_streamer_throttling_interval(&self) -> Option<u64> {
292 panic!("cardano_transactions_block_streamer_throttling_interval is not implemented.");
293 }
294
295 fn enable_metrics_server(&self) -> bool {
297 panic!("enable_metrics_server is not implemented.");
298 }
299
300 fn metrics_server_ip(&self) -> String {
302 panic!("metrics_server_ip is not implemented.");
303 }
304
305 fn metrics_server_port(&self) -> u16 {
307 panic!("metrics_server_port is not implemented.");
308 }
309
310 fn persist_usage_report_interval_in_seconds(&self) -> u64 {
312 panic!("persist_usage_report_interval_in_seconds is not implemented.");
313 }
314
315 fn leader_aggregator_endpoint(&self) -> Option<String> {
321 panic!("leader_aggregator_endpoint is not implemented.");
322 }
323
324 fn custom_origin_tag_white_list(&self) -> Option<String> {
327 panic!("custom_origin_tag_white_list is not implemented.");
328 }
329
330 fn get_server_url(&self) -> StdResult<SanitizedUrlWithTrailingSlash> {
332 panic!("get_server_url is not implemented.");
333 }
334
335 fn get_network(&self) -> StdResult<CardanoNetwork> {
337 CardanoNetwork::from_code(self.network(), self.network_magic())
338 .with_context(|| "Invalid network configuration")
339 }
340
341 fn get_dmq_network(&self) -> StdResult<DmqNetwork> {
343 DmqNetwork::from_code(self.network(), self.dmq_network_magic())
344 .with_context(|| "Invalid DMQ network configuration")
345 }
346
347 fn get_sqlite_dir(&self) -> PathBuf {
349 let store_dir = &self.data_stores_directory();
350
351 if !store_dir.exists() {
352 std::fs::create_dir_all(store_dir).unwrap();
353 }
354
355 self.data_stores_directory()
356 }
357
358 fn get_snapshot_dir(&self) -> StdResult<PathBuf> {
360 if !&self.snapshot_directory().exists() {
361 std::fs::create_dir_all(self.snapshot_directory())?;
362 }
363
364 Ok(self.snapshot_directory())
365 }
366
367 fn safe_epoch_retention_limit(&self) -> Option<u64> {
369 self.store_retention_limit()
370 .map(|limit| if limit > 3 { limit as u64 } else { 3 })
371 }
372
373 fn compute_allowed_signed_entity_types_discriminants(
375 &self,
376 ) -> StdResult<BTreeSet<SignedEntityTypeDiscriminants>> {
377 let allowed_discriminants = self
378 .signed_entity_types()
379 .as_ref()
380 .map(SignedEntityTypeDiscriminants::parse_list)
381 .transpose()
382 .with_context(|| "Invalid 'signed_entity_types' configuration")?
383 .unwrap_or_default();
384 let allowed_discriminants =
385 SignedEntityConfig::append_allowed_signed_entity_types_discriminants(
386 allowed_discriminants,
387 );
388
389 Ok(allowed_discriminants)
390 }
391
392 fn allow_http_serve_directory(&self) -> bool {
394 match self.snapshot_uploader_type() {
395 SnapshotUploaderType::Local => true,
396 SnapshotUploaderType::Gcp => false,
397 }
398 }
399
400 fn get_leader_aggregator_epoch_settings_configuration(
402 &self,
403 ) -> StdResult<AggregatorEpochSettings> {
404 let allowed_discriminants = self.compute_allowed_signed_entity_types_discriminants()?;
405
406 let cardano_transactions_signing_config = if allowed_discriminants
407 .contains(&SignedEntityTypeDiscriminants::CardanoTransactions)
408 {
409 let cardano_transactions_signing_config =
410 self.cardano_transactions_signing_config().with_context(
411 || "Configuration `cardano_transactions_signing_config` is mandatory for a Leader Aggregator when `CardanoTransactions` is enabled in `signed_entity_types`"
412 )?;
413 Some(cardano_transactions_signing_config)
414 } else {
415 None
416 };
417
418 let cardano_blocks_transactions_signing_config = if allowed_discriminants
419 .contains(&SignedEntityTypeDiscriminants::CardanoBlocksTransactions)
420 {
421 let cardano_blocks_transactions_signing_config =
422 self.cardano_blocks_transactions_signing_config().with_context(
423 || "Configuration `cardano_blocks_transactions_signing_config` is mandatory for a Leader Aggregator when `CardanoBlocksTransactions` is enabled in `signed_entity_types`"
424 )?;
425 Some(cardano_blocks_transactions_signing_config)
426 } else {
427 None
428 };
429
430 Ok(AggregatorEpochSettings {
431 protocol_parameters: self.protocol_parameters().with_context(
432 || "Configuration `protocol_parameters` is mandatory for a Leader Aggregator",
433 )?,
434 cardano_transactions_signing_config,
435 cardano_blocks_transactions_signing_config,
436 })
437 }
438
439 fn is_follower_aggregator(&self) -> bool {
441 self.leader_aggregator_endpoint().is_some()
442 }
443
444 fn compute_origin_tag_white_list(&self) -> HashSet<String> {
446 let mut white_list = HashSet::from([
447 "EXPLORER".to_string(),
448 "BENCHMARK".to_string(),
449 "CI".to_string(),
450 "NA".to_string(),
451 ]);
452 if let Some(custom_tags) = &self.custom_origin_tag_white_list() {
453 white_list.extend(custom_tags.split(',').map(|tag| tag.trim().to_string()));
454 }
455
456 white_list
457 }
458
459 fn aggregate_signature_type(&self) -> AggregateSignatureType {
461 panic!("get_aggregate_signature_type is not implemented.");
462 }
463
464 fn signature_processor_wait_delay_on_error_ms(&self) -> u64 {
466 panic!("signature_processor_wait_delay_on_error_ms is not implemented.");
467 }
468}
469
470#[derive(Debug, Clone, Deserialize, Documenter)]
472pub struct ServeCommandConfiguration {
473 pub environment: ExecutionEnvironment,
475
476 #[example = "`cardano-cli`"]
478 pub cardano_cli_path: PathBuf,
479
480 #[example = "`/ipc/node.socket`"]
482 pub cardano_node_socket_path: PathBuf,
483
484 #[example = "`/ipc/dmq.socket`"]
486 pub dmq_node_socket_path: Option<PathBuf>,
487
488 pub cardano_node_version: String,
494
495 #[example = "`mainnet` or `preprod` or `devnet`"]
497 pub network: String,
498
499 #[example = "`1097911063` or `42`"]
503 pub network_magic: Option<u64>,
504
505 #[example = "`1097911063` or `42`"]
509 pub dmq_network_magic: Option<u64>,
510
511 pub chain_observer_type: ChainObserverType,
513
514 #[example = "`{ k: 5, m: 100, phi_f: 0.65 }`"]
516 pub protocol_parameters: Option<ProtocolParameters>,
517
518 #[example = "`gcp` or `local`"]
520 pub snapshot_uploader_type: SnapshotUploaderType,
521
522 pub snapshot_bucket_name: Option<String>,
524
525 pub snapshot_use_cdn_domain: bool,
527
528 pub server_ip: String,
530
531 pub server_port: u16,
533
534 pub public_server_url: Option<String>,
536
537 #[example = "`60000`"]
539 pub run_interval: u64,
540
541 pub db_directory: PathBuf,
543
544 pub snapshot_directory: PathBuf,
546
547 #[example = "`./mithril-aggregator/stores`"]
549 pub data_stores_directory: PathBuf,
550
551 pub genesis_verification_key: HexEncodedGenesisVerificationKey,
553
554 pub reset_digests_cache: bool,
556
557 pub disable_digests_cache: bool,
559
560 pub store_retention_limit: Option<usize>,
565
566 pub era_reader_adapter_type: EraReaderAdapterType,
568
569 pub era_reader_adapter_params: Option<String>,
571
572 #[example = "\
578 - secret-key:<br/>`{ \"type\": \"secret-key\", \"secret_key\": \"136372c3138312c3138382c3130352c3233312c3135\" }`<br/>\
579 - Gcp kms:<br/>`{ \"type\": \"gcp-kms\", \"resource_name\": \"projects/project_name/locations/_location_name/keyRings/key_ring_name/cryptoKeys/key_name/cryptoKeyVersions/key_version\" }`\
580 "]
581 #[serde(deserialize_with = "serde_deserialization::string_or_struct")]
582 pub ancillary_files_signer_config: AncillaryFilesSignerConfig,
583
584 #[example = "`CardanoImmutableFilesFull,CardanoStakeDistribution,CardanoDatabase,CardanoTransactions`"]
589 pub signed_entity_types: Option<String>,
590
591 #[example = "`gzip` or `zstandard`"]
593 pub snapshot_compression_algorithm: CompressionAlgorithm,
594
595 #[example = "`{ level: 9, number_of_workers: 4 }`"]
598 pub zstandard_parameters: Option<ZstandardCompressionParameters>,
599
600 #[example = "\
605 `{ \"project_id\": \"preprodWuV1ICdtOWfZYfdcxpZ0tsS1N9rVZomQ\" }`<br/>\
606 or `{ \"project_id\": \"preprodWuV1ICdtOWfZYfdcxpZ0tsS1N9rVZomQ\", \"base_url\": \"https://your-custom-blockfrost-server.io/api/v0/\" }`\
607 "]
608 #[serde(
609 default,
610 deserialize_with = "serde_deserialization::string_or_struct_optional"
611 )]
612 pub blockfrost_parameters: Option<BlockfrostParameters>,
613
614 pub signer_importer_run_interval: u64,
616
617 pub allow_unparsable_block: bool,
621
622 pub cardano_blocks_transactions_prover_cache_pool_size: usize,
624
625 pub cardano_blocks_transactions_database_connection_pool_size: usize,
627
628 pub cardano_transactions_prover_cache_pool_size: usize,
630
631 pub cardano_transactions_database_connection_pool_size: usize,
633
634 #[example = "`{ security_parameter: 3000, step: 120 }`"]
636 pub cardano_transactions_signing_config: Option<CardanoTransactionsSigningConfig>,
637
638 #[example = "`{ security_parameter: 3000, step: 120 }`"]
640 pub cardano_blocks_transactions_signing_config: Option<CardanoBlocksTransactionsSigningConfig>,
641
642 #[example = "`2160`"]
645 pub preload_security_parameter: BlockNumber,
646
647 pub cardano_transactions_prover_max_hashes_allowed_by_request: usize,
649
650 pub cardano_transactions_block_streamer_max_roll_forwards_per_poll: usize,
652
653 pub cardano_transactions_block_streamer_throttling_interval: Option<u64>,
657
658 pub enable_metrics_server: bool,
660
661 pub metrics_server_ip: String,
663
664 pub metrics_server_port: u16,
666
667 pub persist_usage_report_interval_in_seconds: u64,
669
670 pub leader_aggregator_endpoint: Option<String>,
676
677 pub custom_origin_tag_white_list: Option<String>,
680
681 pub aggregate_signature_type: AggregateSignatureType,
683
684 pub signature_processor_wait_delay_on_error_ms: u64,
686}
687
688#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
690#[serde(rename_all = "lowercase")]
691pub enum SnapshotUploaderType {
692 Gcp,
694 Local,
696}
697
698#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
700pub struct ZstandardCompressionParameters {
701 pub level: i32,
703
704 pub number_of_workers: u32,
706}
707
708impl Default for ZstandardCompressionParameters {
709 fn default() -> Self {
710 Self {
711 level: 9,
712 number_of_workers: 4,
713 }
714 }
715}
716
717#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
721pub struct BlockfrostParameters {
722 pub project_id: ConfigSecret<String>,
724
725 pub base_url: Option<String>,
728}
729
730impl FromStr for BlockfrostParameters {
731 type Err = serde_json::Error;
732
733 fn from_str(s: &str) -> Result<Self, Self::Err> {
734 serde_json::from_str(s)
735 }
736}
737
738#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
742#[serde(rename_all = "kebab-case", tag = "type")]
743pub enum AncillaryFilesSignerConfig {
744 SecretKey {
746 secret_key: HexEncodedKey,
748 },
749 GcpKms {
751 resource_name: GcpCryptoKeyVersionResourceName,
753 #[serde(default = "default_gcp_kms_credentials_json_env_var")]
755 credentials_json_env_var: String,
756 },
757}
758
759fn default_gcp_kms_credentials_json_env_var() -> String {
760 DEFAULT_GCP_CREDENTIALS_JSON_ENV_VAR.to_string()
761}
762
763impl FromStr for AncillaryFilesSignerConfig {
764 type Err = serde_json::Error;
765
766 fn from_str(s: &str) -> Result<Self, Self::Err> {
767 serde_json::from_str(s)
768 }
769}
770
771impl ServeCommandConfiguration {
772 pub fn new_sample(tmp_path: PathBuf) -> Self {
774 let genesis_verification_key = ProtocolGenesisSigner::create_deterministic_signer()
775 .create_verifier()
776 .to_verification_key();
777 let ancillary_files_signer_secret_key =
778 ManifestSigner::create_deterministic_signer().secret_key();
779
780 Self {
781 environment: ExecutionEnvironment::Test,
782 cardano_cli_path: PathBuf::new(),
783 cardano_node_socket_path: PathBuf::new(),
784 dmq_node_socket_path: None,
785 cardano_node_version: "0.0.1".to_string(),
786 network: "devnet".to_string(),
787 network_magic: Some(42),
788 dmq_network_magic: Some(3141592),
789 chain_observer_type: ChainObserverType::Fake,
790 protocol_parameters: Some(ProtocolParameters {
791 k: 5,
792 m: 100,
793 phi_f: 0.95,
794 }),
795 snapshot_uploader_type: SnapshotUploaderType::Local,
796 snapshot_bucket_name: None,
797 snapshot_use_cdn_domain: false,
798 server_ip: "0.0.0.0".to_string(),
799 server_port: 8000,
800 public_server_url: None,
801 run_interval: 5000,
802 db_directory: PathBuf::new(),
803 snapshot_directory: tmp_path,
810 data_stores_directory: PathBuf::from(":memory:"),
811 genesis_verification_key: genesis_verification_key.to_json_hex().unwrap(),
812 reset_digests_cache: false,
813 disable_digests_cache: false,
814 store_retention_limit: None,
815 era_reader_adapter_type: EraReaderAdapterType::Bootstrap,
816 era_reader_adapter_params: None,
817 ancillary_files_signer_config: AncillaryFilesSignerConfig::SecretKey {
818 secret_key: ancillary_files_signer_secret_key.to_json_hex().unwrap(),
819 },
820 signed_entity_types: None,
821 snapshot_compression_algorithm: CompressionAlgorithm::Zstandard,
822 zstandard_parameters: Some(ZstandardCompressionParameters::default()),
823 blockfrost_parameters: None,
824 signer_importer_run_interval: 1,
825 allow_unparsable_block: false,
826 cardano_blocks_transactions_prover_cache_pool_size: 3,
827 cardano_blocks_transactions_database_connection_pool_size: 5,
828 cardano_transactions_prover_cache_pool_size: 3,
829 cardano_transactions_database_connection_pool_size: 5,
830 cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig {
831 security_parameter: BlockNumber(120),
832 step: BlockNumber(15),
833 }),
834 cardano_blocks_transactions_signing_config: Some(
835 CardanoBlocksTransactionsSigningConfig {
836 security_parameter: BlockNumber(120),
837 step: BlockNumber(15),
838 },
839 ),
840 preload_security_parameter: BlockNumber(30),
841 cardano_transactions_prover_max_hashes_allowed_by_request: 100,
842 cardano_transactions_block_streamer_max_roll_forwards_per_poll: 1000,
843 cardano_transactions_block_streamer_throttling_interval: None,
844 enable_metrics_server: true,
845 metrics_server_ip: "0.0.0.0".to_string(),
846 metrics_server_port: 9090,
847 persist_usage_report_interval_in_seconds: 10,
848 leader_aggregator_endpoint: None,
849 custom_origin_tag_white_list: None,
850 aggregate_signature_type: AggregateSignatureType::Concatenation,
851 signature_processor_wait_delay_on_error_ms: 5000,
852 }
853 }
854
855 pub fn get_local_server_url(&self) -> StdResult<SanitizedUrlWithTrailingSlash> {
857 SanitizedUrlWithTrailingSlash::parse(&format!(
858 "http://{}:{}/{SERVER_BASE_PATH}/",
859 self.server_ip, self.server_port
860 ))
861 }
862}
863
864impl ConfigurationSource for ServeCommandConfiguration {
865 fn environment(&self) -> ExecutionEnvironment {
866 self.environment.clone()
867 }
868
869 fn cardano_cli_path(&self) -> PathBuf {
870 self.cardano_cli_path.clone()
871 }
872
873 fn cardano_node_socket_path(&self) -> PathBuf {
874 self.cardano_node_socket_path.clone()
875 }
876
877 fn dmq_node_socket_path(&self) -> Option<PathBuf> {
878 self.dmq_node_socket_path.clone()
879 }
880
881 fn cardano_node_version(&self) -> String {
882 self.cardano_node_version.clone()
883 }
884
885 fn network(&self) -> String {
886 self.network.clone()
887 }
888
889 fn network_magic(&self) -> Option<u64> {
890 self.network_magic
891 }
892
893 fn dmq_network_magic(&self) -> Option<u64> {
894 self.dmq_network_magic
895 }
896
897 fn chain_observer_type(&self) -> ChainObserverType {
898 self.chain_observer_type.clone()
899 }
900
901 fn protocol_parameters(&self) -> Option<ProtocolParameters> {
902 self.protocol_parameters.clone()
903 }
904
905 fn snapshot_uploader_type(&self) -> SnapshotUploaderType {
906 self.snapshot_uploader_type
907 }
908
909 fn snapshot_bucket_name(&self) -> Option<String> {
910 self.snapshot_bucket_name.clone()
911 }
912
913 fn snapshot_use_cdn_domain(&self) -> bool {
914 self.snapshot_use_cdn_domain
915 }
916
917 fn server_ip(&self) -> String {
918 self.server_ip.clone()
919 }
920
921 fn server_port(&self) -> u16 {
922 self.server_port
923 }
924
925 fn public_server_url(&self) -> Option<String> {
926 self.public_server_url.clone()
927 }
928
929 fn run_interval(&self) -> u64 {
930 self.run_interval
931 }
932
933 fn db_directory(&self) -> PathBuf {
934 self.db_directory.clone()
935 }
936
937 fn snapshot_directory(&self) -> PathBuf {
938 self.snapshot_directory.clone()
939 }
940
941 fn data_stores_directory(&self) -> PathBuf {
942 self.data_stores_directory.clone()
943 }
944
945 fn genesis_verification_key(&self) -> HexEncodedGenesisVerificationKey {
946 self.genesis_verification_key.clone()
947 }
948
949 fn reset_digests_cache(&self) -> bool {
950 self.reset_digests_cache
951 }
952
953 fn disable_digests_cache(&self) -> bool {
954 self.disable_digests_cache
955 }
956
957 fn store_retention_limit(&self) -> Option<usize> {
958 self.store_retention_limit
959 }
960
961 fn era_reader_adapter_type(&self) -> EraReaderAdapterType {
962 self.era_reader_adapter_type.clone()
963 }
964
965 fn era_reader_adapter_params(&self) -> Option<String> {
966 self.era_reader_adapter_params.clone()
967 }
968
969 fn ancillary_files_signer_config(&self) -> AncillaryFilesSignerConfig {
970 self.ancillary_files_signer_config.clone()
971 }
972
973 fn signed_entity_types(&self) -> Option<String> {
974 self.signed_entity_types.clone()
975 }
976
977 fn snapshot_compression_algorithm(&self) -> CompressionAlgorithm {
978 self.snapshot_compression_algorithm
979 }
980
981 fn zstandard_parameters(&self) -> Option<ZstandardCompressionParameters> {
982 self.zstandard_parameters
983 }
984
985 fn blockfrost_parameters(&self) -> Option<BlockfrostParameters> {
986 self.blockfrost_parameters.clone()
987 }
988
989 fn signer_importer_run_interval(&self) -> u64 {
990 self.signer_importer_run_interval
991 }
992
993 fn allow_unparsable_block(&self) -> bool {
994 self.allow_unparsable_block
995 }
996
997 fn cardano_blocks_transactions_prover_cache_pool_size(&self) -> usize {
998 self.cardano_blocks_transactions_prover_cache_pool_size
999 }
1000
1001 fn cardano_blocks_transactions_database_connection_pool_size(&self) -> usize {
1002 self.cardano_blocks_transactions_database_connection_pool_size
1003 }
1004
1005 fn cardano_transactions_prover_cache_pool_size(&self) -> usize {
1006 self.cardano_transactions_prover_cache_pool_size
1007 }
1008
1009 fn cardano_transactions_database_connection_pool_size(&self) -> usize {
1010 self.cardano_transactions_database_connection_pool_size
1011 }
1012
1013 fn cardano_transactions_signing_config(&self) -> Option<CardanoTransactionsSigningConfig> {
1014 self.cardano_transactions_signing_config.clone()
1015 }
1016
1017 fn cardano_blocks_transactions_signing_config(
1018 &self,
1019 ) -> Option<CardanoBlocksTransactionsSigningConfig> {
1020 self.cardano_blocks_transactions_signing_config.clone()
1021 }
1022
1023 fn preload_security_parameter(&self) -> BlockNumber {
1024 self.preload_security_parameter
1025 }
1026
1027 fn cardano_transactions_prover_max_hashes_allowed_by_request(&self) -> usize {
1028 self.cardano_transactions_prover_max_hashes_allowed_by_request
1029 }
1030
1031 fn cardano_transactions_block_streamer_max_roll_forwards_per_poll(&self) -> usize {
1032 self.cardano_transactions_block_streamer_max_roll_forwards_per_poll
1033 }
1034
1035 fn cardano_transactions_block_streamer_throttling_interval(&self) -> Option<u64> {
1036 self.cardano_transactions_block_streamer_throttling_interval
1037 }
1038
1039 fn enable_metrics_server(&self) -> bool {
1040 self.enable_metrics_server
1041 }
1042
1043 fn metrics_server_ip(&self) -> String {
1044 self.metrics_server_ip.clone()
1045 }
1046
1047 fn metrics_server_port(&self) -> u16 {
1048 self.metrics_server_port
1049 }
1050
1051 fn persist_usage_report_interval_in_seconds(&self) -> u64 {
1052 self.persist_usage_report_interval_in_seconds
1053 }
1054
1055 fn leader_aggregator_endpoint(&self) -> Option<String> {
1056 self.leader_aggregator_endpoint.clone()
1057 }
1058
1059 fn custom_origin_tag_white_list(&self) -> Option<String> {
1060 self.custom_origin_tag_white_list.clone()
1061 }
1062
1063 fn get_server_url(&self) -> StdResult<SanitizedUrlWithTrailingSlash> {
1064 match &self.public_server_url {
1065 Some(url) => SanitizedUrlWithTrailingSlash::parse(url),
1066 None => self.get_local_server_url(),
1067 }
1068 }
1069
1070 fn aggregate_signature_type(&self) -> AggregateSignatureType {
1071 self.aggregate_signature_type
1072 }
1073
1074 fn signature_processor_wait_delay_on_error_ms(&self) -> u64 {
1075 self.signature_processor_wait_delay_on_error_ms
1076 }
1077}
1078
1079#[derive(Debug, Clone, DocumenterDefault)]
1081pub struct DefaultConfiguration {
1082 pub environment: ExecutionEnvironment,
1084
1085 pub server_ip: String,
1087
1088 pub server_port: String,
1090
1091 pub db_directory: String,
1093
1094 pub snapshot_directory: String,
1096
1097 pub snapshot_uploader_type: String,
1099
1100 pub era_reader_adapter_type: String,
1102
1103 pub chain_observer_type: String,
1105
1106 pub reset_digests_cache: String,
1108
1109 pub disable_digests_cache: String,
1111
1112 pub snapshot_compression_algorithm: String,
1114
1115 pub snapshot_use_cdn_domain: String,
1117
1118 pub signer_importer_run_interval: u64,
1120
1121 pub allow_unparsable_block: String,
1125
1126 pub cardano_blocks_transactions_prover_cache_pool_size: u32,
1128
1129 pub cardano_blocks_transactions_database_connection_pool_size: u32,
1131
1132 pub cardano_transactions_prover_cache_pool_size: u32,
1134
1135 pub cardano_transactions_database_connection_pool_size: u32,
1137
1138 pub cardano_transactions_signing_config: CardanoTransactionsSigningConfig,
1140
1141 pub cardano_blocks_transactions_signing_config: CardanoBlocksTransactionsSigningConfig,
1143
1144 pub preload_security_parameter: u64,
1146
1147 pub cardano_transactions_prover_max_hashes_allowed_by_request: u32,
1149
1150 pub cardano_transactions_block_streamer_max_roll_forwards_per_poll: u32,
1152
1153 pub cardano_transactions_block_streamer_throttling_interval: u64,
1155
1156 pub enable_metrics_server: String,
1158
1159 pub metrics_server_ip: String,
1161
1162 pub metrics_server_port: u16,
1164
1165 pub persist_usage_report_interval_in_seconds: u64,
1167
1168 pub aggregate_signature_type: String,
1170
1171 pub signature_processor_wait_delay_on_error_ms: u64,
1173}
1174
1175impl Default for DefaultConfiguration {
1176 fn default() -> Self {
1177 Self {
1178 environment: ExecutionEnvironment::Production,
1179 server_ip: "0.0.0.0".to_string(),
1180 server_port: "8080".to_string(),
1181 db_directory: "/db".to_string(),
1182 snapshot_directory: ".".to_string(),
1183 snapshot_uploader_type: "gcp".to_string(),
1184 era_reader_adapter_type: "bootstrap".to_string(),
1185 chain_observer_type: "pallas".to_string(),
1186 reset_digests_cache: "false".to_string(),
1187 disable_digests_cache: "false".to_string(),
1188 snapshot_compression_algorithm: "zstandard".to_string(),
1189 snapshot_use_cdn_domain: "false".to_string(),
1190 signer_importer_run_interval: 720,
1191 allow_unparsable_block: "false".to_string(),
1192 cardano_blocks_transactions_prover_cache_pool_size: 10,
1193 cardano_blocks_transactions_database_connection_pool_size: 10,
1194 cardano_transactions_prover_cache_pool_size: 10,
1195 cardano_transactions_database_connection_pool_size: 10,
1196 cardano_transactions_signing_config: CardanoTransactionsSigningConfig {
1197 security_parameter: BlockNumber(3000),
1198 step: BlockNumber(120),
1199 },
1200 cardano_blocks_transactions_signing_config: CardanoBlocksTransactionsSigningConfig {
1201 security_parameter: BlockNumber(3000),
1202 step: BlockNumber(120),
1203 },
1204 preload_security_parameter: 2160,
1205 cardano_transactions_prover_max_hashes_allowed_by_request: 100,
1206 cardano_transactions_block_streamer_max_roll_forwards_per_poll: 10000,
1207 cardano_transactions_block_streamer_throttling_interval: 50,
1208 enable_metrics_server: "false".to_string(),
1209 metrics_server_ip: "0.0.0.0".to_string(),
1210 metrics_server_port: 9090,
1211 persist_usage_report_interval_in_seconds: 10,
1212 aggregate_signature_type: "Concatenation".to_string(),
1213 signature_processor_wait_delay_on_error_ms: 1000,
1214 }
1215 }
1216}
1217
1218impl DefaultConfiguration {
1219 fn namespace() -> String {
1220 "default configuration".to_string()
1221 }
1222}
1223
1224impl From<ExecutionEnvironment> for ValueKind {
1225 fn from(value: ExecutionEnvironment) -> Self {
1226 match value {
1227 ExecutionEnvironment::Production => ValueKind::String("Production".to_string()),
1228 ExecutionEnvironment::Test => ValueKind::String("Test".to_string()),
1229 }
1230 }
1231}
1232
1233impl Source for DefaultConfiguration {
1234 fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
1235 Box::new(self.clone())
1236 }
1237
1238 fn collect(&self) -> Result<Map<String, Value>, ConfigError> {
1239 let mut result = Map::new();
1240
1241 let namespace = DefaultConfiguration::namespace();
1242
1243 let myself = self.clone();
1244 register_config_value!(result, &namespace, myself.environment);
1245 register_config_value!(result, &namespace, myself.server_ip);
1246 register_config_value!(result, &namespace, myself.server_port);
1247 register_config_value!(result, &namespace, myself.db_directory);
1248 register_config_value!(result, &namespace, myself.snapshot_directory);
1249 register_config_value!(result, &namespace, myself.snapshot_uploader_type);
1250 register_config_value!(result, &namespace, myself.era_reader_adapter_type);
1251 register_config_value!(result, &namespace, myself.reset_digests_cache);
1252 register_config_value!(result, &namespace, myself.disable_digests_cache);
1253 register_config_value!(result, &namespace, myself.snapshot_compression_algorithm);
1254 register_config_value!(result, &namespace, myself.snapshot_use_cdn_domain);
1255 register_config_value!(result, &namespace, myself.signer_importer_run_interval);
1256 register_config_value!(result, &namespace, myself.allow_unparsable_block);
1257 register_config_value!(
1258 result,
1259 &namespace,
1260 myself.cardano_blocks_transactions_prover_cache_pool_size
1261 );
1262 register_config_value!(
1263 result,
1264 &namespace,
1265 myself.cardano_blocks_transactions_database_connection_pool_size
1266 );
1267 register_config_value!(
1268 result,
1269 &namespace,
1270 myself.cardano_transactions_prover_cache_pool_size
1271 );
1272 register_config_value!(
1273 result,
1274 &namespace,
1275 myself.cardano_transactions_database_connection_pool_size
1276 );
1277 register_config_value!(
1278 result,
1279 &namespace,
1280 myself.cardano_transactions_prover_max_hashes_allowed_by_request
1281 );
1282 register_config_value!(
1283 result,
1284 &namespace,
1285 myself.cardano_transactions_block_streamer_max_roll_forwards_per_poll
1286 );
1287 register_config_value!(
1288 result,
1289 &namespace,
1290 myself.cardano_transactions_block_streamer_throttling_interval
1291 );
1292 register_config_value!(result, &namespace, myself.enable_metrics_server);
1293 register_config_value!(result, &namespace, myself.metrics_server_ip);
1294 register_config_value!(result, &namespace, myself.metrics_server_port);
1295 register_config_value!(
1296 result,
1297 &namespace,
1298 myself.persist_usage_report_interval_in_seconds
1299 );
1300 register_config_value!(result, &namespace, myself.preload_security_parameter);
1301 register_config_value!(
1302 result,
1303 &namespace,
1304 myself.cardano_transactions_signing_config,
1305 |v: CardanoTransactionsSigningConfig| HashMap::from([
1306 (
1307 "security_parameter".to_string(),
1308 ValueKind::from(*v.security_parameter),
1309 ),
1310 ("step".to_string(), ValueKind::from(*v.step),)
1311 ])
1312 );
1313 register_config_value!(
1314 result,
1315 &namespace,
1316 myself.cardano_blocks_transactions_signing_config,
1317 |v: CardanoBlocksTransactionsSigningConfig| HashMap::from([
1318 (
1319 "security_parameter".to_string(),
1320 ValueKind::from(*v.security_parameter),
1321 ),
1322 ("step".to_string(), ValueKind::from(*v.step),)
1323 ])
1324 );
1325 register_config_value!(result, &namespace, myself.aggregate_signature_type);
1326 register_config_value!(
1327 result,
1328 &namespace,
1329 myself.signature_processor_wait_delay_on_error_ms
1330 );
1331 Ok(result)
1332 }
1333}
1334
1335#[cfg(test)]
1336mod test {
1337 use mithril_common::temp_dir;
1338 use mithril_common::test::double::fake_data;
1339
1340 use super::*;
1341
1342 #[test]
1343 fn safe_epoch_retention_limit_wont_change_a_value_higher_than_three() {
1344 for limit in 4..=10u64 {
1345 let configuration = ServeCommandConfiguration {
1346 store_retention_limit: Some(limit as usize),
1347 ..ServeCommandConfiguration::new_sample(temp_dir!())
1348 };
1349 assert_eq!(configuration.safe_epoch_retention_limit(), Some(limit));
1350 }
1351 }
1352
1353 #[test]
1354 fn safe_epoch_retention_limit_wont_change_a_none_value() {
1355 let configuration = ServeCommandConfiguration {
1356 store_retention_limit: None,
1357 ..ServeCommandConfiguration::new_sample(temp_dir!())
1358 };
1359 assert_eq!(configuration.safe_epoch_retention_limit(), None);
1360 }
1361
1362 #[test]
1363 fn safe_epoch_retention_limit_wont_yield_a_value_lower_than_three() {
1364 for limit in 0..=3 {
1365 let configuration = ServeCommandConfiguration {
1366 store_retention_limit: Some(limit),
1367 ..ServeCommandConfiguration::new_sample(temp_dir!())
1368 };
1369 assert_eq!(configuration.safe_epoch_retention_limit(), Some(3));
1370 }
1371 }
1372
1373 #[test]
1374 fn can_build_config_with_ctx_signing_config_from_default_configuration() {
1375 #[derive(Debug, Deserialize)]
1376 struct TargetConfig {
1377 cardano_transactions_signing_config: CardanoTransactionsSigningConfig,
1378 }
1379
1380 let config_builder = config::Config::builder().add_source(DefaultConfiguration::default());
1381 let target: TargetConfig = config_builder.build().unwrap().try_deserialize().unwrap();
1382
1383 assert_eq!(
1384 target.cardano_transactions_signing_config,
1385 DefaultConfiguration::default().cardano_transactions_signing_config
1386 );
1387 }
1388
1389 #[test]
1390 fn can_build_config_with_cardano_blocks_tx_signing_config_from_default_configuration() {
1391 #[derive(Debug, Deserialize)]
1392 struct TargetConfig {
1393 cardano_blocks_transactions_signing_config: CardanoBlocksTransactionsSigningConfig,
1394 }
1395
1396 let config_builder = config::Config::builder().add_source(DefaultConfiguration::default());
1397 let target: TargetConfig = config_builder.build().unwrap().try_deserialize().unwrap();
1398
1399 assert_eq!(
1400 target.cardano_blocks_transactions_signing_config,
1401 DefaultConfiguration::default().cardano_blocks_transactions_signing_config
1402 );
1403 }
1404
1405 #[test]
1406 fn compute_allowed_signed_entity_types_discriminants_append_default_discriminants() {
1407 let config = ServeCommandConfiguration {
1408 signed_entity_types: None,
1409 ..ServeCommandConfiguration::new_sample(temp_dir!())
1410 };
1411
1412 assert_eq!(
1413 config.compute_allowed_signed_entity_types_discriminants().unwrap(),
1414 BTreeSet::from(SignedEntityConfig::DEFAULT_ALLOWED_DISCRIMINANTS)
1415 );
1416 }
1417
1418 #[test]
1419 fn allow_http_serve_directory() {
1420 let config = ServeCommandConfiguration {
1421 snapshot_uploader_type: SnapshotUploaderType::Local,
1422 ..ServeCommandConfiguration::new_sample(temp_dir!())
1423 };
1424
1425 assert!(config.allow_http_serve_directory());
1426
1427 let config = ServeCommandConfiguration {
1428 snapshot_uploader_type: SnapshotUploaderType::Gcp,
1429 ..ServeCommandConfiguration::new_sample(temp_dir!())
1430 };
1431
1432 assert!(!config.allow_http_serve_directory());
1433 }
1434
1435 #[test]
1436 fn get_server_url_return_local_url_with_server_base_path_if_public_url_is_not_set() {
1437 let config = ServeCommandConfiguration {
1438 server_ip: "1.2.3.4".to_string(),
1439 server_port: 5678,
1440 public_server_url: None,
1441 ..ServeCommandConfiguration::new_sample(temp_dir!())
1442 };
1443
1444 assert_eq!(
1445 config.get_server_url().unwrap().as_str(),
1446 &format!("http://1.2.3.4:5678/{SERVER_BASE_PATH}/")
1447 );
1448 }
1449
1450 #[test]
1451 fn get_server_url_return_sanitized_public_url_if_it_is_set() {
1452 let config = ServeCommandConfiguration {
1453 server_ip: "1.2.3.4".to_string(),
1454 server_port: 5678,
1455 public_server_url: Some("https://example.com".to_string()),
1456 ..ServeCommandConfiguration::new_sample(temp_dir!())
1457 };
1458
1459 assert_eq!(
1460 config.get_server_url().unwrap().as_str(),
1461 "https://example.com/"
1462 );
1463 }
1464
1465 #[test]
1466 fn joining_to_local_server_url_keep_base_path() {
1467 let config = ServeCommandConfiguration {
1468 server_ip: "1.2.3.4".to_string(),
1469 server_port: 6789,
1470 public_server_url: None,
1471 ..ServeCommandConfiguration::new_sample(temp_dir!())
1472 };
1473
1474 let joined_url = config.get_local_server_url().unwrap().join("some/path").unwrap();
1475 assert!(
1476 joined_url.as_str().contains(SERVER_BASE_PATH),
1477 "Joined URL `{joined_url}`, does not contain base path `{SERVER_BASE_PATH}`"
1478 );
1479 }
1480
1481 #[test]
1482 fn joining_to_public_server_url_without_trailing_slash() {
1483 let subpath_without_trailing_slash = "subpath_without_trailing_slash";
1484 let config = ServeCommandConfiguration {
1485 public_server_url: Some(format!(
1486 "https://example.com/{subpath_without_trailing_slash}"
1487 )),
1488 ..ServeCommandConfiguration::new_sample(temp_dir!())
1489 };
1490
1491 let joined_url = config.get_server_url().unwrap().join("some/path").unwrap();
1492 assert!(
1493 joined_url.as_str().contains(subpath_without_trailing_slash),
1494 "Joined URL `{joined_url}`, does not contain subpath `{subpath_without_trailing_slash}`"
1495 );
1496 }
1497
1498 #[test]
1499 fn is_follower_aggregator_returns_true_when_in_follower_mode() {
1500 let config = ServeCommandConfiguration {
1501 leader_aggregator_endpoint: Some("some_endpoint".to_string()),
1502 ..ServeCommandConfiguration::new_sample(temp_dir!())
1503 };
1504
1505 assert!(config.is_follower_aggregator());
1506 }
1507
1508 #[test]
1509 fn is_follower_aggregator_returns_false_when_in_leader_mode() {
1510 let config = ServeCommandConfiguration {
1511 leader_aggregator_endpoint: None,
1512 ..ServeCommandConfiguration::new_sample(temp_dir!())
1513 };
1514
1515 assert!(!config.is_follower_aggregator());
1516 }
1517
1518 #[test]
1519 fn deserializing_blockfrost_parameters() {
1520 let deserialized_without_base_url: BlockfrostParameters =
1521 serde_json::from_str(r#"{ "project_id": "preprodWuV1ICdtOWfZYf" }"#).unwrap();
1522 assert_eq!(
1523 deserialized_without_base_url,
1524 BlockfrostParameters {
1525 project_id: ConfigSecret::new("preprodWuV1ICdtOWfZYf".to_string()),
1526 base_url: None,
1527 }
1528 );
1529
1530 let deserialized_with_base_url: BlockfrostParameters = serde_json::from_str(
1531 r#"{ "project_id": "preprodWuV1ICdtOWfZYf", "base_url": "https://test.foo.bar" }"#,
1532 )
1533 .unwrap();
1534 assert_eq!(
1535 deserialized_with_base_url,
1536 BlockfrostParameters {
1537 project_id: ConfigSecret::new("preprodWuV1ICdtOWfZYf".to_string()),
1538 base_url: Some("https://test.foo.bar".to_string()),
1539 }
1540 );
1541 }
1542
1543 mod get_leader_aggregator_epoch_settings_configuration {
1544 use super::*;
1545
1546 #[test]
1547 fn succeed_when_cardano_transactions_is_disabled_and_cardano_transactions_signing_config_is_not_set()
1548 {
1549 let epoch_settings = ServeCommandConfiguration {
1550 signed_entity_types: None,
1551 cardano_transactions_signing_config: None,
1552 protocol_parameters: Some(ProtocolParameters::new(1, 2, 3.1)),
1553 ..ServeCommandConfiguration::new_sample(temp_dir!())
1554 }
1555 .get_leader_aggregator_epoch_settings_configuration()
1556 .unwrap();
1557
1558 assert_eq!(
1559 AggregatorEpochSettings {
1560 protocol_parameters: ProtocolParameters::new(1, 2, 3.1),
1561 cardano_transactions_signing_config: None,
1562 cardano_blocks_transactions_signing_config: None,
1563 },
1564 epoch_settings
1565 );
1566 }
1567
1568 #[test]
1569 fn succeed_when_cardano_transactions_is_enabled_and_cardano_transactions_signing_config_is_set()
1570 {
1571 let epoch_settings = ServeCommandConfiguration {
1572 signed_entity_types: Some(
1573 SignedEntityTypeDiscriminants::CardanoTransactions.to_string(),
1574 ),
1575 cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig {
1576 security_parameter: BlockNumber(10),
1577 step: BlockNumber(30),
1578 }),
1579 protocol_parameters: Some(ProtocolParameters::new(2, 3, 4.1)),
1580 ..ServeCommandConfiguration::new_sample(temp_dir!())
1581 }
1582 .get_leader_aggregator_epoch_settings_configuration()
1583 .unwrap();
1584
1585 assert_eq!(
1586 AggregatorEpochSettings {
1587 protocol_parameters: ProtocolParameters::new(2, 3, 4.1),
1588 cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig {
1589 security_parameter: BlockNumber(10),
1590 step: BlockNumber(30),
1591 }),
1592 cardano_blocks_transactions_signing_config: None,
1593 },
1594 epoch_settings
1595 );
1596 }
1597
1598 #[test]
1599 fn fails_when_cardano_transactions_is_enabled_without_associated_config() {
1600 let error = ServeCommandConfiguration {
1601 cardano_transactions_signing_config: None,
1602 signed_entity_types: Some(
1603 SignedEntityTypeDiscriminants::CardanoTransactions.to_string(),
1604 ),
1605 protocol_parameters: Some(fake_data::protocol_parameters()),
1606 ..ServeCommandConfiguration::new_sample(temp_dir!())
1607 }
1608 .get_leader_aggregator_epoch_settings_configuration()
1609 .unwrap_err();
1610
1611 assert!(
1612 error
1613 .to_string()
1614 .contains("Configuration `cardano_transactions_signing_config` is mandatory")
1615 );
1616 }
1617
1618 #[test]
1619 fn succeed_when_cardano_blocks_transactions_is_disabled_and_cardano_blocks_transactions_signing_config_is_not_set()
1620 {
1621 let epoch_settings = ServeCommandConfiguration {
1622 signed_entity_types: None,
1623 cardano_blocks_transactions_signing_config: None,
1624 protocol_parameters: Some(ProtocolParameters::new(1, 2, 3.1)),
1625 ..ServeCommandConfiguration::new_sample(temp_dir!())
1626 }
1627 .get_leader_aggregator_epoch_settings_configuration()
1628 .unwrap();
1629
1630 assert_eq!(
1631 AggregatorEpochSettings {
1632 protocol_parameters: ProtocolParameters::new(1, 2, 3.1),
1633 cardano_transactions_signing_config: None,
1634 cardano_blocks_transactions_signing_config: None,
1635 },
1636 epoch_settings
1637 );
1638 }
1639
1640 #[test]
1641 fn succeed_when_cardano_blocks_transactions_is_enabled_and_cardano_blocks_transactions_signing_config_is_set()
1642 {
1643 let epoch_settings = ServeCommandConfiguration {
1644 signed_entity_types: Some(
1645 SignedEntityTypeDiscriminants::CardanoBlocksTransactions.to_string(),
1646 ),
1647 cardano_blocks_transactions_signing_config: Some(
1648 CardanoBlocksTransactionsSigningConfig {
1649 security_parameter: BlockNumber(10),
1650 step: BlockNumber(30),
1651 },
1652 ),
1653 protocol_parameters: Some(ProtocolParameters::new(2, 3, 4.1)),
1654 ..ServeCommandConfiguration::new_sample(temp_dir!())
1655 }
1656 .get_leader_aggregator_epoch_settings_configuration()
1657 .unwrap();
1658
1659 assert_eq!(
1660 AggregatorEpochSettings {
1661 protocol_parameters: ProtocolParameters::new(2, 3, 4.1),
1662 cardano_transactions_signing_config: None,
1663 cardano_blocks_transactions_signing_config: Some(
1664 CardanoBlocksTransactionsSigningConfig {
1665 security_parameter: BlockNumber(10),
1666 step: BlockNumber(30),
1667 }
1668 ),
1669 },
1670 epoch_settings
1671 );
1672 }
1673
1674 #[test]
1675 fn fails_when_cardano_blocks_transactions_is_enabled_without_associated_config() {
1676 let error = ServeCommandConfiguration {
1677 cardano_blocks_transactions_signing_config: None,
1678 signed_entity_types: Some(
1679 SignedEntityTypeDiscriminants::CardanoBlocksTransactions.to_string(),
1680 ),
1681 protocol_parameters: Some(fake_data::protocol_parameters()),
1682 ..ServeCommandConfiguration::new_sample(temp_dir!())
1683 }
1684 .get_leader_aggregator_epoch_settings_configuration()
1685 .unwrap_err();
1686
1687 assert!(error.to_string().contains(
1688 "Configuration `cardano_blocks_transactions_signing_config` is mandatory"
1689 ));
1690 }
1691 }
1692
1693 #[test]
1694 fn serialized_ancillary_files_signer_config_use_snake_case_for_keys_and_kebab_case_for_type_value()
1695 {
1696 let serialized_json = r#"{
1697 "type": "secret-key",
1698 "secret_key": "whatever"
1699 }"#;
1700
1701 let deserialized: AncillaryFilesSignerConfig =
1702 serde_json::from_str(serialized_json).unwrap();
1703 assert_eq!(
1704 deserialized,
1705 AncillaryFilesSignerConfig::SecretKey {
1706 secret_key: "whatever".to_string()
1707 }
1708 );
1709 }
1710
1711 #[test]
1712 fn deserializing_ancillary_signing_gcp_kms_configuration() {
1713 let serialized_json = r#"{
1714 "type": "gcp-kms",
1715 "resource_name": "projects/123456789/locations/global/keyRings/my-keyring/cryptoKeys/my-key/cryptoKeyVersions/1",
1716 "credentials_json_env_var": "CUSTOM_ENV_VAR"
1717 }"#;
1718
1719 let deserialized: AncillaryFilesSignerConfig =
1720 serde_json::from_str(serialized_json).unwrap();
1721 assert_eq!(
1722 deserialized,
1723 AncillaryFilesSignerConfig::GcpKms {
1724 resource_name: GcpCryptoKeyVersionResourceName {
1725 project: "123456789".to_string(),
1726 location: "global".to_string(),
1727 key_ring: "my-keyring".to_string(),
1728 key_name: "my-key".to_string(),
1729 version: "1".to_string(),
1730 },
1731 credentials_json_env_var: "CUSTOM_ENV_VAR".to_string()
1732 }
1733 );
1734 }
1735
1736 #[test]
1737 fn deserializing_ancillary_signing_gcp_kms_configuration_without_credentials_json_env_var_fallback_to_default()
1738 {
1739 let serialized_json = r#"{
1740 "type": "gcp-kms",
1741 "resource_name": "projects/123456789/locations/global/keyRings/my-keyring/cryptoKeys/my-key/cryptoKeyVersions/1"
1742 }"#;
1743
1744 let deserialized: AncillaryFilesSignerConfig =
1745 serde_json::from_str(serialized_json).unwrap();
1746 if let AncillaryFilesSignerConfig::GcpKms {
1747 credentials_json_env_var,
1748 ..
1749 } = deserialized
1750 {
1751 assert_eq!(
1752 credentials_json_env_var,
1753 DEFAULT_GCP_CREDENTIALS_JSON_ENV_VAR
1754 );
1755 } else {
1756 panic!("Expected GcpKms variant but got {deserialized:?}");
1757 }
1758 }
1759
1760 mod origin_tag {
1761 use super::*;
1762
1763 #[test]
1764 fn default_origin_tag_white_list_is_not_empty() {
1765 let config = ServeCommandConfiguration {
1766 custom_origin_tag_white_list: None,
1767 ..ServeCommandConfiguration::new_sample(temp_dir!())
1768 };
1769 assert_ne!(config.compute_origin_tag_white_list().len(), 0,);
1770 }
1771
1772 #[test]
1773 fn custom_origin_tag_are_added_to_default_white_list() {
1774 let config = ServeCommandConfiguration {
1775 custom_origin_tag_white_list: Some("TAG_A,TAG_B , TAG_C".to_string()),
1776 ..ServeCommandConfiguration::new_sample(temp_dir!())
1777 };
1778
1779 let default_white_list = ServeCommandConfiguration {
1780 custom_origin_tag_white_list: None,
1781 ..ServeCommandConfiguration::new_sample(temp_dir!())
1782 }
1783 .compute_origin_tag_white_list();
1784
1785 let mut expected_white_list = default_white_list.clone();
1786 assert!(expected_white_list.insert("TAG_A".to_string()));
1787 assert!(expected_white_list.insert("TAG_B".to_string()));
1788 assert!(expected_white_list.insert("TAG_C".to_string()));
1789
1790 assert_eq!(expected_white_list, config.compute_origin_tag_white_list());
1791 }
1792 }
1793}