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_prover_max_hashes_allowed_by_request(&self) -> usize {
282 panic!("cardano_prover_max_hashes_allowed_by_request is not implemented.");
283 }
284
285 fn cardano_transactions_block_streamer_max_roll_forwards_per_poll(&self) -> usize {
287 panic!(
288 "cardano_transactions_block_streamer_max_roll_forwards_per_poll is not implemented."
289 );
290 }
291
292 fn cardano_transactions_block_streamer_throttling_interval(&self) -> Option<u64> {
294 panic!("cardano_transactions_block_streamer_throttling_interval is not implemented.");
295 }
296
297 fn enable_metrics_server(&self) -> bool {
299 panic!("enable_metrics_server is not implemented.");
300 }
301
302 fn metrics_server_ip(&self) -> String {
304 panic!("metrics_server_ip is not implemented.");
305 }
306
307 fn metrics_server_port(&self) -> u16 {
309 panic!("metrics_server_port is not implemented.");
310 }
311
312 fn persist_usage_report_interval_in_seconds(&self) -> u64 {
314 panic!("persist_usage_report_interval_in_seconds is not implemented.");
315 }
316
317 fn leader_aggregator_endpoint(&self) -> Option<String> {
323 panic!("leader_aggregator_endpoint is not implemented.");
324 }
325
326 fn custom_origin_tag_white_list(&self) -> Option<String> {
329 panic!("custom_origin_tag_white_list is not implemented.");
330 }
331
332 fn get_server_url(&self) -> StdResult<SanitizedUrlWithTrailingSlash> {
334 panic!("get_server_url is not implemented.");
335 }
336
337 fn get_network(&self) -> StdResult<CardanoNetwork> {
339 CardanoNetwork::from_code(self.network(), self.network_magic())
340 .with_context(|| "Invalid network configuration")
341 }
342
343 fn get_dmq_network(&self) -> StdResult<DmqNetwork> {
345 DmqNetwork::from_code(self.network(), self.dmq_network_magic())
346 .with_context(|| "Invalid DMQ network configuration")
347 }
348
349 fn get_sqlite_dir(&self) -> PathBuf {
351 let store_dir = &self.data_stores_directory();
352
353 if !store_dir.exists() {
354 std::fs::create_dir_all(store_dir).unwrap();
355 }
356
357 self.data_stores_directory()
358 }
359
360 fn get_snapshot_dir(&self) -> StdResult<PathBuf> {
362 if !&self.snapshot_directory().exists() {
363 std::fs::create_dir_all(self.snapshot_directory())?;
364 }
365
366 Ok(self.snapshot_directory())
367 }
368
369 fn safe_epoch_retention_limit(&self) -> Option<u64> {
371 self.store_retention_limit()
372 .map(|limit| if limit > 3 { limit as u64 } else { 3 })
373 }
374
375 fn compute_allowed_signed_entity_types_discriminants(
377 &self,
378 ) -> StdResult<BTreeSet<SignedEntityTypeDiscriminants>> {
379 let allowed_discriminants = self
380 .signed_entity_types()
381 .as_ref()
382 .map(SignedEntityTypeDiscriminants::parse_list)
383 .transpose()
384 .with_context(|| "Invalid 'signed_entity_types' configuration")?
385 .unwrap_or_default();
386 let allowed_discriminants =
387 SignedEntityConfig::append_allowed_signed_entity_types_discriminants(
388 allowed_discriminants,
389 );
390
391 Ok(allowed_discriminants)
392 }
393
394 fn allow_http_serve_directory(&self) -> bool {
396 match self.snapshot_uploader_type() {
397 SnapshotUploaderType::Local => true,
398 SnapshotUploaderType::Gcp => false,
399 }
400 }
401
402 fn get_leader_aggregator_epoch_settings_configuration(
404 &self,
405 ) -> StdResult<AggregatorEpochSettings> {
406 let allowed_discriminants = self.compute_allowed_signed_entity_types_discriminants()?;
407
408 let cardano_transactions_signing_config = if allowed_discriminants
409 .contains(&SignedEntityTypeDiscriminants::CardanoTransactions)
410 {
411 let cardano_transactions_signing_config =
412 self.cardano_transactions_signing_config().with_context(
413 || "Configuration `cardano_transactions_signing_config` is mandatory for a Leader Aggregator when `CardanoTransactions` is enabled in `signed_entity_types`"
414 )?;
415 Some(cardano_transactions_signing_config)
416 } else {
417 None
418 };
419
420 let cardano_blocks_transactions_signing_config = if allowed_discriminants
421 .contains(&SignedEntityTypeDiscriminants::CardanoBlocksTransactions)
422 {
423 let cardano_blocks_transactions_signing_config =
424 self.cardano_blocks_transactions_signing_config().with_context(
425 || "Configuration `cardano_blocks_transactions_signing_config` is mandatory for a Leader Aggregator when `CardanoBlocksTransactions` is enabled in `signed_entity_types`"
426 )?;
427 Some(cardano_blocks_transactions_signing_config)
428 } else {
429 None
430 };
431
432 Ok(AggregatorEpochSettings {
433 protocol_parameters: self.protocol_parameters().with_context(
434 || "Configuration `protocol_parameters` is mandatory for a Leader Aggregator",
435 )?,
436 cardano_transactions_signing_config,
437 cardano_blocks_transactions_signing_config,
438 })
439 }
440
441 fn is_follower_aggregator(&self) -> bool {
443 self.leader_aggregator_endpoint().is_some()
444 }
445
446 fn compute_origin_tag_white_list(&self) -> HashSet<String> {
448 let mut white_list = HashSet::from([
449 "EXPLORER".to_string(),
450 "BENCHMARK".to_string(),
451 "CI".to_string(),
452 "NA".to_string(),
453 ]);
454 if let Some(custom_tags) = &self.custom_origin_tag_white_list() {
455 white_list.extend(custom_tags.split(',').map(|tag| tag.trim().to_string()));
456 }
457
458 white_list
459 }
460
461 fn aggregate_signature_type(&self) -> AggregateSignatureType {
463 panic!("get_aggregate_signature_type is not implemented.");
464 }
465
466 fn signature_processor_wait_delay_on_error_ms(&self) -> u64 {
468 panic!("signature_processor_wait_delay_on_error_ms is not implemented.");
469 }
470}
471
472#[derive(Debug, Clone, Deserialize, Documenter)]
474pub struct ServeCommandConfiguration {
475 pub environment: ExecutionEnvironment,
477
478 #[example = "`cardano-cli`"]
480 pub cardano_cli_path: PathBuf,
481
482 #[example = "`/ipc/node.socket`"]
484 pub cardano_node_socket_path: PathBuf,
485
486 #[example = "`/ipc/dmq.socket`"]
488 pub dmq_node_socket_path: Option<PathBuf>,
489
490 pub cardano_node_version: String,
496
497 #[example = "`mainnet` or `preprod` or `devnet`"]
499 pub network: String,
500
501 #[example = "`1097911063` or `42`"]
505 pub network_magic: Option<u64>,
506
507 #[example = "`1097911063` or `42`"]
511 pub dmq_network_magic: Option<u64>,
512
513 pub chain_observer_type: ChainObserverType,
515
516 #[example = "`{ k: 5, m: 100, phi_f: 0.65 }`"]
518 pub protocol_parameters: Option<ProtocolParameters>,
519
520 #[example = "`gcp` or `local`"]
522 pub snapshot_uploader_type: SnapshotUploaderType,
523
524 pub snapshot_bucket_name: Option<String>,
526
527 pub snapshot_use_cdn_domain: bool,
529
530 pub server_ip: String,
532
533 pub server_port: u16,
535
536 pub public_server_url: Option<String>,
538
539 #[example = "`60000`"]
541 pub run_interval: u64,
542
543 pub db_directory: PathBuf,
545
546 pub snapshot_directory: PathBuf,
548
549 #[example = "`./mithril-aggregator/stores`"]
551 pub data_stores_directory: PathBuf,
552
553 pub genesis_verification_key: HexEncodedGenesisVerificationKey,
555
556 pub reset_digests_cache: bool,
558
559 pub disable_digests_cache: bool,
561
562 pub store_retention_limit: Option<usize>,
567
568 pub era_reader_adapter_type: EraReaderAdapterType,
570
571 pub era_reader_adapter_params: Option<String>,
573
574 #[example = "\
580 - secret-key:<br/>`{ \"type\": \"secret-key\", \"secret_key\": \"136372c3138312c3138382c3130352c3233312c3135\" }`<br/>\
581 - Gcp kms:<br/>`{ \"type\": \"gcp-kms\", \"resource_name\": \"projects/project_name/locations/_location_name/keyRings/key_ring_name/cryptoKeys/key_name/cryptoKeyVersions/key_version\" }`\
582 "]
583 #[serde(deserialize_with = "serde_deserialization::string_or_struct")]
584 pub ancillary_files_signer_config: AncillaryFilesSignerConfig,
585
586 #[example = "`CardanoImmutableFilesFull,CardanoStakeDistribution,CardanoDatabase,CardanoTransactions`"]
591 pub signed_entity_types: Option<String>,
592
593 #[example = "`gzip` or `zstandard`"]
595 pub snapshot_compression_algorithm: CompressionAlgorithm,
596
597 #[example = "`{ level: 9, number_of_workers: 4 }`"]
600 pub zstandard_parameters: Option<ZstandardCompressionParameters>,
601
602 #[example = "\
607 `{ \"project_id\": \"preprodWuV1ICdtOWfZYfdcxpZ0tsS1N9rVZomQ\" }`<br/>\
608 or `{ \"project_id\": \"preprodWuV1ICdtOWfZYfdcxpZ0tsS1N9rVZomQ\", \"base_url\": \"https://your-custom-blockfrost-server.io/api/v0/\" }`\
609 "]
610 #[serde(
611 default,
612 deserialize_with = "serde_deserialization::string_or_struct_optional"
613 )]
614 pub blockfrost_parameters: Option<BlockfrostParameters>,
615
616 pub signer_importer_run_interval: u64,
618
619 pub allow_unparsable_block: bool,
623
624 pub cardano_blocks_transactions_prover_cache_pool_size: usize,
626
627 pub cardano_blocks_transactions_database_connection_pool_size: usize,
629
630 pub cardano_transactions_prover_cache_pool_size: usize,
632
633 pub cardano_transactions_database_connection_pool_size: usize,
635
636 #[example = "`{ security_parameter: 3000, step: 120 }`"]
638 pub cardano_transactions_signing_config: Option<CardanoTransactionsSigningConfig>,
639
640 #[example = "`{ security_parameter: 3000, step: 120 }`"]
642 pub cardano_blocks_transactions_signing_config: Option<CardanoBlocksTransactionsSigningConfig>,
643
644 #[example = "`2160`"]
647 pub preload_security_parameter: BlockNumber,
648
649 pub cardano_prover_max_hashes_allowed_by_request: usize,
653
654 pub cardano_transactions_block_streamer_max_roll_forwards_per_poll: usize,
656
657 pub cardano_transactions_block_streamer_throttling_interval: Option<u64>,
661
662 pub enable_metrics_server: bool,
664
665 pub metrics_server_ip: String,
667
668 pub metrics_server_port: u16,
670
671 pub persist_usage_report_interval_in_seconds: u64,
673
674 pub leader_aggregator_endpoint: Option<String>,
680
681 pub custom_origin_tag_white_list: Option<String>,
684
685 pub aggregate_signature_type: AggregateSignatureType,
687
688 pub signature_processor_wait_delay_on_error_ms: u64,
690}
691
692#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
694#[serde(rename_all = "lowercase")]
695pub enum SnapshotUploaderType {
696 Gcp,
698 Local,
700}
701
702#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq)]
704pub struct ZstandardCompressionParameters {
705 pub level: i32,
707
708 pub number_of_workers: u32,
710}
711
712impl Default for ZstandardCompressionParameters {
713 fn default() -> Self {
714 Self {
715 level: 9,
716 number_of_workers: 4,
717 }
718 }
719}
720
721#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
725pub struct BlockfrostParameters {
726 pub project_id: ConfigSecret<String>,
728
729 pub base_url: Option<String>,
732}
733
734impl FromStr for BlockfrostParameters {
735 type Err = serde_json::Error;
736
737 fn from_str(s: &str) -> Result<Self, Self::Err> {
738 serde_json::from_str(s)
739 }
740}
741
742#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
746#[serde(rename_all = "kebab-case", tag = "type")]
747pub enum AncillaryFilesSignerConfig {
748 SecretKey {
750 secret_key: HexEncodedKey,
752 },
753 GcpKms {
755 resource_name: GcpCryptoKeyVersionResourceName,
757 #[serde(default = "default_gcp_kms_credentials_json_env_var")]
759 credentials_json_env_var: String,
760 },
761}
762
763fn default_gcp_kms_credentials_json_env_var() -> String {
764 DEFAULT_GCP_CREDENTIALS_JSON_ENV_VAR.to_string()
765}
766
767impl FromStr for AncillaryFilesSignerConfig {
768 type Err = serde_json::Error;
769
770 fn from_str(s: &str) -> Result<Self, Self::Err> {
771 serde_json::from_str(s)
772 }
773}
774
775impl ServeCommandConfiguration {
776 pub fn new_sample(tmp_path: PathBuf) -> Self {
778 let genesis_verification_key = ProtocolGenesisSigner::create_deterministic_signer()
779 .create_verifier()
780 .to_verification_key();
781 let ancillary_files_signer_secret_key =
782 ManifestSigner::create_deterministic_signer().secret_key();
783
784 Self {
785 environment: ExecutionEnvironment::Test,
786 cardano_cli_path: PathBuf::new(),
787 cardano_node_socket_path: PathBuf::new(),
788 dmq_node_socket_path: None,
789 cardano_node_version: "0.0.1".to_string(),
790 network: "devnet".to_string(),
791 network_magic: Some(42),
792 dmq_network_magic: Some(3141592),
793 chain_observer_type: ChainObserverType::Fake,
794 protocol_parameters: Some(ProtocolParameters {
795 k: 5,
796 m: 100,
797 phi_f: 0.95,
798 }),
799 snapshot_uploader_type: SnapshotUploaderType::Local,
800 snapshot_bucket_name: None,
801 snapshot_use_cdn_domain: false,
802 server_ip: "0.0.0.0".to_string(),
803 server_port: 8000,
804 public_server_url: None,
805 run_interval: 5000,
806 db_directory: PathBuf::new(),
807 snapshot_directory: tmp_path,
814 data_stores_directory: PathBuf::from(":memory:"),
815 genesis_verification_key: genesis_verification_key.to_json_hex().unwrap(),
816 reset_digests_cache: false,
817 disable_digests_cache: false,
818 store_retention_limit: None,
819 era_reader_adapter_type: EraReaderAdapterType::Bootstrap,
820 era_reader_adapter_params: None,
821 ancillary_files_signer_config: AncillaryFilesSignerConfig::SecretKey {
822 secret_key: ancillary_files_signer_secret_key.to_json_hex().unwrap(),
823 },
824 signed_entity_types: None,
825 snapshot_compression_algorithm: CompressionAlgorithm::Zstandard,
826 zstandard_parameters: Some(ZstandardCompressionParameters::default()),
827 blockfrost_parameters: None,
828 signer_importer_run_interval: 1,
829 allow_unparsable_block: false,
830 cardano_blocks_transactions_prover_cache_pool_size: 3,
831 cardano_blocks_transactions_database_connection_pool_size: 5,
832 cardano_transactions_prover_cache_pool_size: 3,
833 cardano_transactions_database_connection_pool_size: 5,
834 cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig {
835 security_parameter: BlockNumber(120),
836 step: BlockNumber(15),
837 }),
838 cardano_blocks_transactions_signing_config: Some(
839 CardanoBlocksTransactionsSigningConfig {
840 security_parameter: BlockNumber(120),
841 step: BlockNumber(15),
842 },
843 ),
844 preload_security_parameter: BlockNumber(30),
845 cardano_prover_max_hashes_allowed_by_request: 100,
846 cardano_transactions_block_streamer_max_roll_forwards_per_poll: 1000,
847 cardano_transactions_block_streamer_throttling_interval: None,
848 enable_metrics_server: true,
849 metrics_server_ip: "0.0.0.0".to_string(),
850 metrics_server_port: 9090,
851 persist_usage_report_interval_in_seconds: 10,
852 leader_aggregator_endpoint: None,
853 custom_origin_tag_white_list: None,
854 aggregate_signature_type: AggregateSignatureType::Concatenation,
855 signature_processor_wait_delay_on_error_ms: 5000,
856 }
857 }
858
859 pub fn get_local_server_url(&self) -> StdResult<SanitizedUrlWithTrailingSlash> {
861 SanitizedUrlWithTrailingSlash::parse(&format!(
862 "http://{}:{}/{SERVER_BASE_PATH}/",
863 self.server_ip, self.server_port
864 ))
865 }
866}
867
868impl ConfigurationSource for ServeCommandConfiguration {
869 fn environment(&self) -> ExecutionEnvironment {
870 self.environment.clone()
871 }
872
873 fn cardano_cli_path(&self) -> PathBuf {
874 self.cardano_cli_path.clone()
875 }
876
877 fn cardano_node_socket_path(&self) -> PathBuf {
878 self.cardano_node_socket_path.clone()
879 }
880
881 fn dmq_node_socket_path(&self) -> Option<PathBuf> {
882 self.dmq_node_socket_path.clone()
883 }
884
885 fn cardano_node_version(&self) -> String {
886 self.cardano_node_version.clone()
887 }
888
889 fn network(&self) -> String {
890 self.network.clone()
891 }
892
893 fn network_magic(&self) -> Option<u64> {
894 self.network_magic
895 }
896
897 fn dmq_network_magic(&self) -> Option<u64> {
898 self.dmq_network_magic
899 }
900
901 fn chain_observer_type(&self) -> ChainObserverType {
902 self.chain_observer_type.clone()
903 }
904
905 fn protocol_parameters(&self) -> Option<ProtocolParameters> {
906 self.protocol_parameters.clone()
907 }
908
909 fn snapshot_uploader_type(&self) -> SnapshotUploaderType {
910 self.snapshot_uploader_type
911 }
912
913 fn snapshot_bucket_name(&self) -> Option<String> {
914 self.snapshot_bucket_name.clone()
915 }
916
917 fn snapshot_use_cdn_domain(&self) -> bool {
918 self.snapshot_use_cdn_domain
919 }
920
921 fn server_ip(&self) -> String {
922 self.server_ip.clone()
923 }
924
925 fn server_port(&self) -> u16 {
926 self.server_port
927 }
928
929 fn public_server_url(&self) -> Option<String> {
930 self.public_server_url.clone()
931 }
932
933 fn run_interval(&self) -> u64 {
934 self.run_interval
935 }
936
937 fn db_directory(&self) -> PathBuf {
938 self.db_directory.clone()
939 }
940
941 fn snapshot_directory(&self) -> PathBuf {
942 self.snapshot_directory.clone()
943 }
944
945 fn data_stores_directory(&self) -> PathBuf {
946 self.data_stores_directory.clone()
947 }
948
949 fn genesis_verification_key(&self) -> HexEncodedGenesisVerificationKey {
950 self.genesis_verification_key.clone()
951 }
952
953 fn reset_digests_cache(&self) -> bool {
954 self.reset_digests_cache
955 }
956
957 fn disable_digests_cache(&self) -> bool {
958 self.disable_digests_cache
959 }
960
961 fn store_retention_limit(&self) -> Option<usize> {
962 self.store_retention_limit
963 }
964
965 fn era_reader_adapter_type(&self) -> EraReaderAdapterType {
966 self.era_reader_adapter_type.clone()
967 }
968
969 fn era_reader_adapter_params(&self) -> Option<String> {
970 self.era_reader_adapter_params.clone()
971 }
972
973 fn ancillary_files_signer_config(&self) -> AncillaryFilesSignerConfig {
974 self.ancillary_files_signer_config.clone()
975 }
976
977 fn signed_entity_types(&self) -> Option<String> {
978 self.signed_entity_types.clone()
979 }
980
981 fn snapshot_compression_algorithm(&self) -> CompressionAlgorithm {
982 self.snapshot_compression_algorithm
983 }
984
985 fn zstandard_parameters(&self) -> Option<ZstandardCompressionParameters> {
986 self.zstandard_parameters
987 }
988
989 fn blockfrost_parameters(&self) -> Option<BlockfrostParameters> {
990 self.blockfrost_parameters.clone()
991 }
992
993 fn signer_importer_run_interval(&self) -> u64 {
994 self.signer_importer_run_interval
995 }
996
997 fn allow_unparsable_block(&self) -> bool {
998 self.allow_unparsable_block
999 }
1000
1001 fn cardano_blocks_transactions_prover_cache_pool_size(&self) -> usize {
1002 self.cardano_blocks_transactions_prover_cache_pool_size
1003 }
1004
1005 fn cardano_blocks_transactions_database_connection_pool_size(&self) -> usize {
1006 self.cardano_blocks_transactions_database_connection_pool_size
1007 }
1008
1009 fn cardano_transactions_prover_cache_pool_size(&self) -> usize {
1010 self.cardano_transactions_prover_cache_pool_size
1011 }
1012
1013 fn cardano_transactions_database_connection_pool_size(&self) -> usize {
1014 self.cardano_transactions_database_connection_pool_size
1015 }
1016
1017 fn cardano_transactions_signing_config(&self) -> Option<CardanoTransactionsSigningConfig> {
1018 self.cardano_transactions_signing_config.clone()
1019 }
1020
1021 fn cardano_blocks_transactions_signing_config(
1022 &self,
1023 ) -> Option<CardanoBlocksTransactionsSigningConfig> {
1024 self.cardano_blocks_transactions_signing_config.clone()
1025 }
1026
1027 fn preload_security_parameter(&self) -> BlockNumber {
1028 self.preload_security_parameter
1029 }
1030
1031 fn cardano_prover_max_hashes_allowed_by_request(&self) -> usize {
1032 self.cardano_prover_max_hashes_allowed_by_request
1033 }
1034
1035 fn cardano_transactions_block_streamer_max_roll_forwards_per_poll(&self) -> usize {
1036 self.cardano_transactions_block_streamer_max_roll_forwards_per_poll
1037 }
1038
1039 fn cardano_transactions_block_streamer_throttling_interval(&self) -> Option<u64> {
1040 self.cardano_transactions_block_streamer_throttling_interval
1041 }
1042
1043 fn enable_metrics_server(&self) -> bool {
1044 self.enable_metrics_server
1045 }
1046
1047 fn metrics_server_ip(&self) -> String {
1048 self.metrics_server_ip.clone()
1049 }
1050
1051 fn metrics_server_port(&self) -> u16 {
1052 self.metrics_server_port
1053 }
1054
1055 fn persist_usage_report_interval_in_seconds(&self) -> u64 {
1056 self.persist_usage_report_interval_in_seconds
1057 }
1058
1059 fn leader_aggregator_endpoint(&self) -> Option<String> {
1060 self.leader_aggregator_endpoint.clone()
1061 }
1062
1063 fn custom_origin_tag_white_list(&self) -> Option<String> {
1064 self.custom_origin_tag_white_list.clone()
1065 }
1066
1067 fn get_server_url(&self) -> StdResult<SanitizedUrlWithTrailingSlash> {
1068 match &self.public_server_url {
1069 Some(url) => SanitizedUrlWithTrailingSlash::parse(url),
1070 None => self.get_local_server_url(),
1071 }
1072 }
1073
1074 fn aggregate_signature_type(&self) -> AggregateSignatureType {
1075 self.aggregate_signature_type
1076 }
1077
1078 fn signature_processor_wait_delay_on_error_ms(&self) -> u64 {
1079 self.signature_processor_wait_delay_on_error_ms
1080 }
1081}
1082
1083#[derive(Debug, Clone, DocumenterDefault)]
1085pub struct DefaultConfiguration {
1086 pub environment: ExecutionEnvironment,
1088
1089 pub server_ip: String,
1091
1092 pub server_port: String,
1094
1095 pub db_directory: String,
1097
1098 pub snapshot_directory: String,
1100
1101 pub snapshot_uploader_type: String,
1103
1104 pub era_reader_adapter_type: String,
1106
1107 pub chain_observer_type: String,
1109
1110 pub reset_digests_cache: String,
1112
1113 pub disable_digests_cache: String,
1115
1116 pub snapshot_compression_algorithm: String,
1118
1119 pub snapshot_use_cdn_domain: String,
1121
1122 pub signer_importer_run_interval: u64,
1124
1125 pub allow_unparsable_block: String,
1129
1130 pub cardano_blocks_transactions_prover_cache_pool_size: u32,
1132
1133 pub cardano_blocks_transactions_database_connection_pool_size: u32,
1135
1136 pub cardano_transactions_prover_cache_pool_size: u32,
1138
1139 pub cardano_transactions_database_connection_pool_size: u32,
1141
1142 pub cardano_transactions_signing_config: CardanoTransactionsSigningConfig,
1144
1145 pub cardano_blocks_transactions_signing_config: CardanoBlocksTransactionsSigningConfig,
1147
1148 pub preload_security_parameter: u64,
1150
1151 pub cardano_prover_max_hashes_allowed_by_request: u32,
1155
1156 pub cardano_transactions_block_streamer_max_roll_forwards_per_poll: u32,
1158
1159 pub cardano_transactions_block_streamer_throttling_interval: u64,
1161
1162 pub enable_metrics_server: String,
1164
1165 pub metrics_server_ip: String,
1167
1168 pub metrics_server_port: u16,
1170
1171 pub persist_usage_report_interval_in_seconds: u64,
1173
1174 pub aggregate_signature_type: String,
1176
1177 pub signature_processor_wait_delay_on_error_ms: u64,
1179}
1180
1181impl Default for DefaultConfiguration {
1182 fn default() -> Self {
1183 Self {
1184 environment: ExecutionEnvironment::Production,
1185 server_ip: "0.0.0.0".to_string(),
1186 server_port: "8080".to_string(),
1187 db_directory: "/db".to_string(),
1188 snapshot_directory: ".".to_string(),
1189 snapshot_uploader_type: "gcp".to_string(),
1190 era_reader_adapter_type: "bootstrap".to_string(),
1191 chain_observer_type: "pallas".to_string(),
1192 reset_digests_cache: "false".to_string(),
1193 disable_digests_cache: "false".to_string(),
1194 snapshot_compression_algorithm: "zstandard".to_string(),
1195 snapshot_use_cdn_domain: "false".to_string(),
1196 signer_importer_run_interval: 720,
1197 allow_unparsable_block: "false".to_string(),
1198 cardano_blocks_transactions_prover_cache_pool_size: 10,
1199 cardano_blocks_transactions_database_connection_pool_size: 10,
1200 cardano_transactions_prover_cache_pool_size: 10,
1201 cardano_transactions_database_connection_pool_size: 10,
1202 cardano_transactions_signing_config: CardanoTransactionsSigningConfig {
1203 security_parameter: BlockNumber(3000),
1204 step: BlockNumber(120),
1205 },
1206 cardano_blocks_transactions_signing_config: CardanoBlocksTransactionsSigningConfig {
1207 security_parameter: BlockNumber(3000),
1208 step: BlockNumber(120),
1209 },
1210 preload_security_parameter: 2160,
1211 cardano_prover_max_hashes_allowed_by_request: 100,
1212 cardano_transactions_block_streamer_max_roll_forwards_per_poll: 10000,
1213 cardano_transactions_block_streamer_throttling_interval: 50,
1214 enable_metrics_server: "false".to_string(),
1215 metrics_server_ip: "0.0.0.0".to_string(),
1216 metrics_server_port: 9090,
1217 persist_usage_report_interval_in_seconds: 10,
1218 aggregate_signature_type: "Concatenation".to_string(),
1219 signature_processor_wait_delay_on_error_ms: 1000,
1220 }
1221 }
1222}
1223
1224impl DefaultConfiguration {
1225 fn namespace() -> String {
1226 "default configuration".to_string()
1227 }
1228}
1229
1230impl From<ExecutionEnvironment> for ValueKind {
1231 fn from(value: ExecutionEnvironment) -> Self {
1232 match value {
1233 ExecutionEnvironment::Production => ValueKind::String("Production".to_string()),
1234 ExecutionEnvironment::Test => ValueKind::String("Test".to_string()),
1235 }
1236 }
1237}
1238
1239impl Source for DefaultConfiguration {
1240 fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
1241 Box::new(self.clone())
1242 }
1243
1244 fn collect(&self) -> Result<Map<String, Value>, ConfigError> {
1245 let mut result = Map::new();
1246
1247 let namespace = DefaultConfiguration::namespace();
1248
1249 let myself = self.clone();
1250 register_config_value!(result, &namespace, myself.environment);
1251 register_config_value!(result, &namespace, myself.server_ip);
1252 register_config_value!(result, &namespace, myself.server_port);
1253 register_config_value!(result, &namespace, myself.db_directory);
1254 register_config_value!(result, &namespace, myself.snapshot_directory);
1255 register_config_value!(result, &namespace, myself.snapshot_uploader_type);
1256 register_config_value!(result, &namespace, myself.era_reader_adapter_type);
1257 register_config_value!(result, &namespace, myself.reset_digests_cache);
1258 register_config_value!(result, &namespace, myself.disable_digests_cache);
1259 register_config_value!(result, &namespace, myself.snapshot_compression_algorithm);
1260 register_config_value!(result, &namespace, myself.snapshot_use_cdn_domain);
1261 register_config_value!(result, &namespace, myself.signer_importer_run_interval);
1262 register_config_value!(result, &namespace, myself.allow_unparsable_block);
1263 register_config_value!(
1264 result,
1265 &namespace,
1266 myself.cardano_blocks_transactions_prover_cache_pool_size
1267 );
1268 register_config_value!(
1269 result,
1270 &namespace,
1271 myself.cardano_blocks_transactions_database_connection_pool_size
1272 );
1273 register_config_value!(
1274 result,
1275 &namespace,
1276 myself.cardano_transactions_prover_cache_pool_size
1277 );
1278 register_config_value!(
1279 result,
1280 &namespace,
1281 myself.cardano_transactions_database_connection_pool_size
1282 );
1283 register_config_value!(
1284 result,
1285 &namespace,
1286 myself.cardano_prover_max_hashes_allowed_by_request
1287 );
1288 register_config_value!(
1289 result,
1290 &namespace,
1291 myself.cardano_transactions_block_streamer_max_roll_forwards_per_poll
1292 );
1293 register_config_value!(
1294 result,
1295 &namespace,
1296 myself.cardano_transactions_block_streamer_throttling_interval
1297 );
1298 register_config_value!(result, &namespace, myself.enable_metrics_server);
1299 register_config_value!(result, &namespace, myself.metrics_server_ip);
1300 register_config_value!(result, &namespace, myself.metrics_server_port);
1301 register_config_value!(
1302 result,
1303 &namespace,
1304 myself.persist_usage_report_interval_in_seconds
1305 );
1306 register_config_value!(result, &namespace, myself.preload_security_parameter);
1307 register_config_value!(
1308 result,
1309 &namespace,
1310 myself.cardano_transactions_signing_config,
1311 |v: CardanoTransactionsSigningConfig| HashMap::from([
1312 (
1313 "security_parameter".to_string(),
1314 ValueKind::from(*v.security_parameter),
1315 ),
1316 ("step".to_string(), ValueKind::from(*v.step),)
1317 ])
1318 );
1319 register_config_value!(
1320 result,
1321 &namespace,
1322 myself.cardano_blocks_transactions_signing_config,
1323 |v: CardanoBlocksTransactionsSigningConfig| HashMap::from([
1324 (
1325 "security_parameter".to_string(),
1326 ValueKind::from(*v.security_parameter),
1327 ),
1328 ("step".to_string(), ValueKind::from(*v.step),)
1329 ])
1330 );
1331 register_config_value!(result, &namespace, myself.aggregate_signature_type);
1332 register_config_value!(
1333 result,
1334 &namespace,
1335 myself.signature_processor_wait_delay_on_error_ms
1336 );
1337 Ok(result)
1338 }
1339}
1340
1341#[cfg(test)]
1342mod test {
1343 use mithril_common::temp_dir;
1344 use mithril_common::test::double::fake_data;
1345
1346 use super::*;
1347
1348 #[test]
1349 fn safe_epoch_retention_limit_wont_change_a_value_higher_than_three() {
1350 for limit in 4..=10u64 {
1351 let configuration = ServeCommandConfiguration {
1352 store_retention_limit: Some(limit as usize),
1353 ..ServeCommandConfiguration::new_sample(temp_dir!())
1354 };
1355 assert_eq!(configuration.safe_epoch_retention_limit(), Some(limit));
1356 }
1357 }
1358
1359 #[test]
1360 fn safe_epoch_retention_limit_wont_change_a_none_value() {
1361 let configuration = ServeCommandConfiguration {
1362 store_retention_limit: None,
1363 ..ServeCommandConfiguration::new_sample(temp_dir!())
1364 };
1365 assert_eq!(configuration.safe_epoch_retention_limit(), None);
1366 }
1367
1368 #[test]
1369 fn safe_epoch_retention_limit_wont_yield_a_value_lower_than_three() {
1370 for limit in 0..=3 {
1371 let configuration = ServeCommandConfiguration {
1372 store_retention_limit: Some(limit),
1373 ..ServeCommandConfiguration::new_sample(temp_dir!())
1374 };
1375 assert_eq!(configuration.safe_epoch_retention_limit(), Some(3));
1376 }
1377 }
1378
1379 #[test]
1380 fn can_build_config_with_ctx_signing_config_from_default_configuration() {
1381 #[derive(Debug, Deserialize)]
1382 struct TargetConfig {
1383 cardano_transactions_signing_config: CardanoTransactionsSigningConfig,
1384 }
1385
1386 let config_builder = config::Config::builder().add_source(DefaultConfiguration::default());
1387 let target: TargetConfig = config_builder.build().unwrap().try_deserialize().unwrap();
1388
1389 assert_eq!(
1390 target.cardano_transactions_signing_config,
1391 DefaultConfiguration::default().cardano_transactions_signing_config
1392 );
1393 }
1394
1395 #[test]
1396 fn can_build_config_with_cardano_blocks_tx_signing_config_from_default_configuration() {
1397 #[derive(Debug, Deserialize)]
1398 struct TargetConfig {
1399 cardano_blocks_transactions_signing_config: CardanoBlocksTransactionsSigningConfig,
1400 }
1401
1402 let config_builder = config::Config::builder().add_source(DefaultConfiguration::default());
1403 let target: TargetConfig = config_builder.build().unwrap().try_deserialize().unwrap();
1404
1405 assert_eq!(
1406 target.cardano_blocks_transactions_signing_config,
1407 DefaultConfiguration::default().cardano_blocks_transactions_signing_config
1408 );
1409 }
1410
1411 #[test]
1412 fn compute_allowed_signed_entity_types_discriminants_append_default_discriminants() {
1413 let config = ServeCommandConfiguration {
1414 signed_entity_types: None,
1415 ..ServeCommandConfiguration::new_sample(temp_dir!())
1416 };
1417
1418 assert_eq!(
1419 config.compute_allowed_signed_entity_types_discriminants().unwrap(),
1420 BTreeSet::from(SignedEntityConfig::DEFAULT_ALLOWED_DISCRIMINANTS)
1421 );
1422 }
1423
1424 #[test]
1425 fn allow_http_serve_directory() {
1426 let config = ServeCommandConfiguration {
1427 snapshot_uploader_type: SnapshotUploaderType::Local,
1428 ..ServeCommandConfiguration::new_sample(temp_dir!())
1429 };
1430
1431 assert!(config.allow_http_serve_directory());
1432
1433 let config = ServeCommandConfiguration {
1434 snapshot_uploader_type: SnapshotUploaderType::Gcp,
1435 ..ServeCommandConfiguration::new_sample(temp_dir!())
1436 };
1437
1438 assert!(!config.allow_http_serve_directory());
1439 }
1440
1441 #[test]
1442 fn get_server_url_return_local_url_with_server_base_path_if_public_url_is_not_set() {
1443 let config = ServeCommandConfiguration {
1444 server_ip: "1.2.3.4".to_string(),
1445 server_port: 5678,
1446 public_server_url: None,
1447 ..ServeCommandConfiguration::new_sample(temp_dir!())
1448 };
1449
1450 assert_eq!(
1451 config.get_server_url().unwrap().as_str(),
1452 &format!("http://1.2.3.4:5678/{SERVER_BASE_PATH}/")
1453 );
1454 }
1455
1456 #[test]
1457 fn get_server_url_return_sanitized_public_url_if_it_is_set() {
1458 let config = ServeCommandConfiguration {
1459 server_ip: "1.2.3.4".to_string(),
1460 server_port: 5678,
1461 public_server_url: Some("https://example.com".to_string()),
1462 ..ServeCommandConfiguration::new_sample(temp_dir!())
1463 };
1464
1465 assert_eq!(
1466 config.get_server_url().unwrap().as_str(),
1467 "https://example.com/"
1468 );
1469 }
1470
1471 #[test]
1472 fn joining_to_local_server_url_keep_base_path() {
1473 let config = ServeCommandConfiguration {
1474 server_ip: "1.2.3.4".to_string(),
1475 server_port: 6789,
1476 public_server_url: None,
1477 ..ServeCommandConfiguration::new_sample(temp_dir!())
1478 };
1479
1480 let joined_url = config.get_local_server_url().unwrap().join("some/path").unwrap();
1481 assert!(
1482 joined_url.as_str().contains(SERVER_BASE_PATH),
1483 "Joined URL `{joined_url}`, does not contain base path `{SERVER_BASE_PATH}`"
1484 );
1485 }
1486
1487 #[test]
1488 fn joining_to_public_server_url_without_trailing_slash() {
1489 let subpath_without_trailing_slash = "subpath_without_trailing_slash";
1490 let config = ServeCommandConfiguration {
1491 public_server_url: Some(format!(
1492 "https://example.com/{subpath_without_trailing_slash}"
1493 )),
1494 ..ServeCommandConfiguration::new_sample(temp_dir!())
1495 };
1496
1497 let joined_url = config.get_server_url().unwrap().join("some/path").unwrap();
1498 assert!(
1499 joined_url.as_str().contains(subpath_without_trailing_slash),
1500 "Joined URL `{joined_url}`, does not contain subpath `{subpath_without_trailing_slash}`"
1501 );
1502 }
1503
1504 #[test]
1505 fn is_follower_aggregator_returns_true_when_in_follower_mode() {
1506 let config = ServeCommandConfiguration {
1507 leader_aggregator_endpoint: Some("some_endpoint".to_string()),
1508 ..ServeCommandConfiguration::new_sample(temp_dir!())
1509 };
1510
1511 assert!(config.is_follower_aggregator());
1512 }
1513
1514 #[test]
1515 fn is_follower_aggregator_returns_false_when_in_leader_mode() {
1516 let config = ServeCommandConfiguration {
1517 leader_aggregator_endpoint: None,
1518 ..ServeCommandConfiguration::new_sample(temp_dir!())
1519 };
1520
1521 assert!(!config.is_follower_aggregator());
1522 }
1523
1524 #[test]
1525 fn deserializing_blockfrost_parameters() {
1526 let deserialized_without_base_url: BlockfrostParameters =
1527 serde_json::from_str(r#"{ "project_id": "preprodWuV1ICdtOWfZYf" }"#).unwrap();
1528 assert_eq!(
1529 deserialized_without_base_url,
1530 BlockfrostParameters {
1531 project_id: ConfigSecret::new("preprodWuV1ICdtOWfZYf".to_string()),
1532 base_url: None,
1533 }
1534 );
1535
1536 let deserialized_with_base_url: BlockfrostParameters = serde_json::from_str(
1537 r#"{ "project_id": "preprodWuV1ICdtOWfZYf", "base_url": "https://test.foo.bar" }"#,
1538 )
1539 .unwrap();
1540 assert_eq!(
1541 deserialized_with_base_url,
1542 BlockfrostParameters {
1543 project_id: ConfigSecret::new("preprodWuV1ICdtOWfZYf".to_string()),
1544 base_url: Some("https://test.foo.bar".to_string()),
1545 }
1546 );
1547 }
1548
1549 mod get_leader_aggregator_epoch_settings_configuration {
1550 use super::*;
1551
1552 #[test]
1553 fn succeed_when_cardano_transactions_is_disabled_and_cardano_transactions_signing_config_is_not_set()
1554 {
1555 let epoch_settings = ServeCommandConfiguration {
1556 signed_entity_types: None,
1557 cardano_transactions_signing_config: None,
1558 protocol_parameters: Some(ProtocolParameters::new(1, 2, 3.1)),
1559 ..ServeCommandConfiguration::new_sample(temp_dir!())
1560 }
1561 .get_leader_aggregator_epoch_settings_configuration()
1562 .unwrap();
1563
1564 assert_eq!(
1565 AggregatorEpochSettings {
1566 protocol_parameters: ProtocolParameters::new(1, 2, 3.1),
1567 cardano_transactions_signing_config: None,
1568 cardano_blocks_transactions_signing_config: None,
1569 },
1570 epoch_settings
1571 );
1572 }
1573
1574 #[test]
1575 fn succeed_when_cardano_transactions_is_enabled_and_cardano_transactions_signing_config_is_set()
1576 {
1577 let epoch_settings = ServeCommandConfiguration {
1578 signed_entity_types: Some(
1579 SignedEntityTypeDiscriminants::CardanoTransactions.to_string(),
1580 ),
1581 cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig {
1582 security_parameter: BlockNumber(10),
1583 step: BlockNumber(30),
1584 }),
1585 protocol_parameters: Some(ProtocolParameters::new(2, 3, 4.1)),
1586 ..ServeCommandConfiguration::new_sample(temp_dir!())
1587 }
1588 .get_leader_aggregator_epoch_settings_configuration()
1589 .unwrap();
1590
1591 assert_eq!(
1592 AggregatorEpochSettings {
1593 protocol_parameters: ProtocolParameters::new(2, 3, 4.1),
1594 cardano_transactions_signing_config: Some(CardanoTransactionsSigningConfig {
1595 security_parameter: BlockNumber(10),
1596 step: BlockNumber(30),
1597 }),
1598 cardano_blocks_transactions_signing_config: None,
1599 },
1600 epoch_settings
1601 );
1602 }
1603
1604 #[test]
1605 fn fails_when_cardano_transactions_is_enabled_without_associated_config() {
1606 let error = ServeCommandConfiguration {
1607 cardano_transactions_signing_config: None,
1608 signed_entity_types: Some(
1609 SignedEntityTypeDiscriminants::CardanoTransactions.to_string(),
1610 ),
1611 protocol_parameters: Some(fake_data::protocol_parameters()),
1612 ..ServeCommandConfiguration::new_sample(temp_dir!())
1613 }
1614 .get_leader_aggregator_epoch_settings_configuration()
1615 .unwrap_err();
1616
1617 assert!(
1618 error
1619 .to_string()
1620 .contains("Configuration `cardano_transactions_signing_config` is mandatory")
1621 );
1622 }
1623
1624 #[test]
1625 fn succeed_when_cardano_blocks_transactions_is_disabled_and_cardano_blocks_transactions_signing_config_is_not_set()
1626 {
1627 let epoch_settings = ServeCommandConfiguration {
1628 signed_entity_types: None,
1629 cardano_blocks_transactions_signing_config: None,
1630 protocol_parameters: Some(ProtocolParameters::new(1, 2, 3.1)),
1631 ..ServeCommandConfiguration::new_sample(temp_dir!())
1632 }
1633 .get_leader_aggregator_epoch_settings_configuration()
1634 .unwrap();
1635
1636 assert_eq!(
1637 AggregatorEpochSettings {
1638 protocol_parameters: ProtocolParameters::new(1, 2, 3.1),
1639 cardano_transactions_signing_config: None,
1640 cardano_blocks_transactions_signing_config: None,
1641 },
1642 epoch_settings
1643 );
1644 }
1645
1646 #[test]
1647 fn succeed_when_cardano_blocks_transactions_is_enabled_and_cardano_blocks_transactions_signing_config_is_set()
1648 {
1649 let epoch_settings = ServeCommandConfiguration {
1650 signed_entity_types: Some(
1651 SignedEntityTypeDiscriminants::CardanoBlocksTransactions.to_string(),
1652 ),
1653 cardano_blocks_transactions_signing_config: Some(
1654 CardanoBlocksTransactionsSigningConfig {
1655 security_parameter: BlockNumber(10),
1656 step: BlockNumber(30),
1657 },
1658 ),
1659 protocol_parameters: Some(ProtocolParameters::new(2, 3, 4.1)),
1660 ..ServeCommandConfiguration::new_sample(temp_dir!())
1661 }
1662 .get_leader_aggregator_epoch_settings_configuration()
1663 .unwrap();
1664
1665 assert_eq!(
1666 AggregatorEpochSettings {
1667 protocol_parameters: ProtocolParameters::new(2, 3, 4.1),
1668 cardano_transactions_signing_config: None,
1669 cardano_blocks_transactions_signing_config: Some(
1670 CardanoBlocksTransactionsSigningConfig {
1671 security_parameter: BlockNumber(10),
1672 step: BlockNumber(30),
1673 }
1674 ),
1675 },
1676 epoch_settings
1677 );
1678 }
1679
1680 #[test]
1681 fn fails_when_cardano_blocks_transactions_is_enabled_without_associated_config() {
1682 let error = ServeCommandConfiguration {
1683 cardano_blocks_transactions_signing_config: None,
1684 signed_entity_types: Some(
1685 SignedEntityTypeDiscriminants::CardanoBlocksTransactions.to_string(),
1686 ),
1687 protocol_parameters: Some(fake_data::protocol_parameters()),
1688 ..ServeCommandConfiguration::new_sample(temp_dir!())
1689 }
1690 .get_leader_aggregator_epoch_settings_configuration()
1691 .unwrap_err();
1692
1693 assert!(error.to_string().contains(
1694 "Configuration `cardano_blocks_transactions_signing_config` is mandatory"
1695 ));
1696 }
1697 }
1698
1699 #[test]
1700 fn serialized_ancillary_files_signer_config_use_snake_case_for_keys_and_kebab_case_for_type_value()
1701 {
1702 let serialized_json = r#"{
1703 "type": "secret-key",
1704 "secret_key": "whatever"
1705 }"#;
1706
1707 let deserialized: AncillaryFilesSignerConfig =
1708 serde_json::from_str(serialized_json).unwrap();
1709 assert_eq!(
1710 deserialized,
1711 AncillaryFilesSignerConfig::SecretKey {
1712 secret_key: "whatever".to_string()
1713 }
1714 );
1715 }
1716
1717 #[test]
1718 fn deserializing_ancillary_signing_gcp_kms_configuration() {
1719 let serialized_json = r#"{
1720 "type": "gcp-kms",
1721 "resource_name": "projects/123456789/locations/global/keyRings/my-keyring/cryptoKeys/my-key/cryptoKeyVersions/1",
1722 "credentials_json_env_var": "CUSTOM_ENV_VAR"
1723 }"#;
1724
1725 let deserialized: AncillaryFilesSignerConfig =
1726 serde_json::from_str(serialized_json).unwrap();
1727 assert_eq!(
1728 deserialized,
1729 AncillaryFilesSignerConfig::GcpKms {
1730 resource_name: GcpCryptoKeyVersionResourceName {
1731 project: "123456789".to_string(),
1732 location: "global".to_string(),
1733 key_ring: "my-keyring".to_string(),
1734 key_name: "my-key".to_string(),
1735 version: "1".to_string(),
1736 },
1737 credentials_json_env_var: "CUSTOM_ENV_VAR".to_string()
1738 }
1739 );
1740 }
1741
1742 #[test]
1743 fn deserializing_ancillary_signing_gcp_kms_configuration_without_credentials_json_env_var_fallback_to_default()
1744 {
1745 let serialized_json = r#"{
1746 "type": "gcp-kms",
1747 "resource_name": "projects/123456789/locations/global/keyRings/my-keyring/cryptoKeys/my-key/cryptoKeyVersions/1"
1748 }"#;
1749
1750 let deserialized: AncillaryFilesSignerConfig =
1751 serde_json::from_str(serialized_json).unwrap();
1752 if let AncillaryFilesSignerConfig::GcpKms {
1753 credentials_json_env_var,
1754 ..
1755 } = deserialized
1756 {
1757 assert_eq!(
1758 credentials_json_env_var,
1759 DEFAULT_GCP_CREDENTIALS_JSON_ENV_VAR
1760 );
1761 } else {
1762 panic!("Expected GcpKms variant but got {deserialized:?}");
1763 }
1764 }
1765
1766 mod origin_tag {
1767 use super::*;
1768
1769 #[test]
1770 fn default_origin_tag_white_list_is_not_empty() {
1771 let config = ServeCommandConfiguration {
1772 custom_origin_tag_white_list: None,
1773 ..ServeCommandConfiguration::new_sample(temp_dir!())
1774 };
1775 assert_ne!(config.compute_origin_tag_white_list().len(), 0,);
1776 }
1777
1778 #[test]
1779 fn custom_origin_tag_are_added_to_default_white_list() {
1780 let config = ServeCommandConfiguration {
1781 custom_origin_tag_white_list: Some("TAG_A,TAG_B , TAG_C".to_string()),
1782 ..ServeCommandConfiguration::new_sample(temp_dir!())
1783 };
1784
1785 let default_white_list = ServeCommandConfiguration {
1786 custom_origin_tag_white_list: None,
1787 ..ServeCommandConfiguration::new_sample(temp_dir!())
1788 }
1789 .compute_origin_tag_white_list();
1790
1791 let mut expected_white_list = default_white_list.clone();
1792 assert!(expected_white_list.insert("TAG_A".to_string()));
1793 assert!(expected_white_list.insert("TAG_B".to_string()));
1794 assert!(expected_white_list.insert("TAG_C".to_string()));
1795
1796 assert_eq!(expected_white_list, config.compute_origin_tag_white_list());
1797 }
1798 }
1799}