1use crate::config::config_fields::{
2 CARDANO_ACTIVE_SLOTS_COEFF, CARDANO_EPOCH_DURATION_MILLIS, CARDANO_FIRST_EPOCH_NUMBER,
3 CARDANO_FIRST_EPOCH_TIMESTAMP_MILLIS, CARDANO_FIRST_SLOT_NUMBER, CARDANO_SECURITY_PARAMETER,
4};
5use crate::io::IOContext;
6use clap::{Parser, arg};
7use config_fields::CARDANO_SLOT_DURATION_MILLIS;
8use serde::{Deserialize, Serialize, de::DeserializeOwned};
9use sidechain_domain::{MainchainKeyHash, UtxoId};
10use sp_core::offchain::{Duration, Timestamp};
11use std::fmt::{Display, Formatter, Write};
12use std::str::FromStr;
13use std::{marker::PhantomData, process::exit};
14
15pub struct ConfigFieldDefinition<'a, T> {
16 pub name: &'a str,
17 pub config_file: &'a str,
18 pub path: &'a [&'a str],
19 pub default: Option<&'a str>,
20 pub _marker: PhantomData<T>,
21}
22
23impl<'a> ConfigFieldDefinition<'a, String> {
24 pub fn load_or_prompt_and_save<C: IOContext>(&self, context: &C) -> String {
25 if let Some(value) = self.load_from_file_and_print(context) {
26 value
27 } else {
28 let value = context.prompt(self.name, self.default);
29 self.save_to_file(&value, context);
30 value
31 }
32 }
33
34 pub fn prompt_with_default_from_file_and_save<C: IOContext>(&self, context: &C) -> String {
35 let value =
36 context.prompt(self.name, self.load_from_file(context).as_deref().or(self.default));
37 self.save_to_file(&value, context);
38 value
39 }
40}
41
42impl<'a, T> ConfigFieldDefinition<'a, T> {
43 pub fn prompt_with_default_from_file_parse_and_save<C: IOContext>(
44 &self,
45 context: &C,
46 ) -> Result<T, <T as FromStr>::Err>
47 where
48 T: DeserializeOwned + std::fmt::Display + FromStr + serde::Serialize,
49 {
50 let loaded_value = self.load_from_file(context).map(|v| v.to_string());
51 let default_value = loaded_value.as_deref().or(self.default);
52 let value = context.prompt(self.name, default_value);
53 let parsed_value: T = value.parse()?;
54 self.save_to_file(&parsed_value, context);
55 Ok(parsed_value)
56 }
57
58 pub fn select_options_with_default_from_file_and_save<C: IOContext>(
59 &self,
60 prompt: &str,
61 context: &C,
62 ) -> Result<T, <T as FromStr>::Err>
63 where
64 T: DeserializeOwned + std::fmt::Display + FromStr + serde::Serialize + SelectOptions,
65 {
66 let loaded_value = self.load_from_file(context).map(|v| v.to_string());
67 let default_value_opt = loaded_value.as_deref().or(self.default);
68 let options = T::select_options_with_default(default_value_opt);
69 let value = context.prompt_multi_option(prompt, options);
70 let parsed_value: T = value.parse()?;
71 self.save_to_file(&parsed_value, context);
72 Ok(parsed_value)
73 }
74
75 pub fn load_or_prompt_parse_and_save<C: IOContext>(
76 &self,
77 context: &C,
78 ) -> Result<T, <T as FromStr>::Err>
79 where
80 T: DeserializeOwned + std::fmt::Display + FromStr + serde::Serialize,
81 {
82 if let Some(value) = self.load_from_file_and_print(context) {
83 Ok(value)
84 } else {
85 let value_str = context.prompt(self.name, self.default);
86 let parsed_value: T = value_str.parse()?;
87 self.save_to_file(&parsed_value, context);
88 Ok(parsed_value)
89 }
90 }
91
92 pub fn load_from_file<C: IOContext>(&self, context: &C) -> Option<T>
94 where
95 T: DeserializeOwned,
96 {
97 self.load_file(context).and_then(|json| self.extract_from_json_object(&json))
98 }
99
100 pub fn load_from_file_and_print(&self, context: &impl IOContext) -> Option<T>
101 where
102 T: DeserializeOwned + std::fmt::Display,
103 {
104 let value = self.load_from_file(context)?;
105 context.eprint(&self.loaded_from_config_msg(&value));
106 Some(value)
107 }
108
109 pub fn save_to_file<C: IOContext>(&self, value: &T, context: &C)
111 where
112 T: Serialize,
113 {
114 let mut json =
115 self.load_file(context).unwrap_or(serde_json::Value::Object(Default::default()));
116 let mut head = &mut json;
117 for &field in self.path {
118 head[field] = head
119 .get(field)
120 .cloned()
121 .filter(serde_json::Value::is_object)
122 .unwrap_or(serde_json::Value::Object(Default::default()));
123 head = &mut head[field];
124 }
125 *head = serde_json::to_value(value).unwrap();
126 context.write_file(self.config_file, &serde_json::to_string_pretty(&json).unwrap());
127 }
128
129 pub fn save_if_empty<C: IOContext>(&self, value: T, context: &C) -> T
130 where
131 T: DeserializeOwned + serde::Serialize,
132 {
133 if let Some(value) = self.load_from_file(context) {
134 value
135 } else {
136 self.save_to_file(&value, context);
137 value
138 }
139 }
140
141 pub fn extract_from_json_object(&self, json: &serde_json::Value) -> Option<T>
143 where
144 T: DeserializeOwned,
145 {
146 let mut json: Option<&serde_json::Value> = Some(json);
147 for &field in self.path {
148 if let Some(json_inner) = json {
149 json = json_inner.get(field)
150 } else {
151 return None;
152 }
153 }
154 json.and_then(|json| serde_json::from_value(json.clone()).ok())
155 }
156
157 pub fn load_file<C: IOContext>(&self, context: &C) -> Option<serde_json::Value> {
159 if !context.file_exists(self.config_file) {
160 return None;
161 }
162
163 if let Some(file_content_string) = context.read_file(self.config_file) {
164 if let Ok(value) = serde_json::from_str(&file_content_string) {
165 return Some(value);
166 }
167 }
168
169 self.report_corrupted_file_and_quit()
170 }
171
172 pub fn report_corrupted_file_and_quit(&self) -> ! {
174 eprintln!(
175 "Config file {} is broken. Delete it or fix manually and restart this wizard",
176 self.config_file
177 );
178 exit(-1)
179 }
180
181 pub fn loaded_from_config_msg(&self, value: &T) -> String
182 where
183 T: std::fmt::Display,
184 {
185 format!("🛠️ Loaded {} from config ({}): {value}", self.name, self.config_file)
186 }
187
188 pub fn json_pointer(&self) -> String {
189 format!("/{}", self.path.join("/"))
190 }
191}
192
193#[derive(Clone, Debug)]
194pub struct ServiceConfig {
195 pub protocol: NetworkProtocol,
196 pub hostname: String,
197 pub port: u16,
198 pub timeout_seconds: u64,
199}
200
201impl ServiceConfig {
202 pub(crate) fn url(&self) -> String {
203 format!("{}://{}:{}", self.protocol, self.hostname, self.port)
204 }
205}
206
207pub trait SelectOptions {
208 fn select_options() -> Vec<String>;
209 fn select_options_with_default(default_value_opt: Option<&str>) -> Vec<String> {
210 let mut options = Self::select_options();
211
212 if let Some(default_value) = default_value_opt {
213 options.sort_by_key(|option| if *option != default_value { 1 } else { 0 });
214 }
215 options
216 }
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
220pub enum NetworkProtocol {
221 #[serde(rename = "http")]
222 Http,
223 #[serde(rename = "https")]
224 Https,
225}
226
227impl NetworkProtocol {
228 pub fn is_secure(&self) -> bool {
229 matches!(self, NetworkProtocol::Https)
230 }
231}
232
233impl std::fmt::Display for NetworkProtocol {
234 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
235 let str = match self {
236 NetworkProtocol::Http => "http".to_string(),
237 NetworkProtocol::Https => "https".to_string(),
238 };
239 write!(f, "{}", str)
240 }
241}
242
243impl FromStr for NetworkProtocol {
244 type Err = String;
245
246 fn from_str(s: &str) -> Result<Self, Self::Err> {
247 match s.to_lowercase().as_str() {
248 "http" => Ok(NetworkProtocol::Http),
249 "https" => Ok(NetworkProtocol::Https),
250 other => Err(format!("Invalid security protocol {other}, please provide http or http")),
251 }
252 }
253}
254
255impl SelectOptions for NetworkProtocol {
256 fn select_options() -> Vec<String> {
257 vec![NetworkProtocol::Http.to_string(), NetworkProtocol::Https.to_string()]
258 }
259}
260
261#[derive(Deserialize)]
262pub struct MainChainAddresses {
263 pub committee_candidates_address: String,
264 pub d_parameter_policy_id: String,
265 pub permissioned_candidates_policy_id: String,
266 pub native_token: NativeTokenConfig,
267}
268#[derive(Deserialize, PartialEq, Clone, Debug)]
269pub struct CardanoParameters {
270 pub security_parameter: u64,
271 pub active_slots_coeff: f64,
272 pub first_epoch_number: u32,
273 pub first_slot_number: u64,
274 pub epoch_duration_millis: u64,
275 pub first_epoch_timestamp_millis: u64,
276 #[serde(default = "default_slot_duration_millis")]
277 pub slot_duration_millis: u64,
278}
279
280fn default_slot_duration_millis() -> u64 {
281 1000
282}
283
284impl CardanoParameters {
285 pub fn save(&self, context: &impl IOContext) {
286 CARDANO_SECURITY_PARAMETER.save_to_file(&self.security_parameter, context);
287 CARDANO_ACTIVE_SLOTS_COEFF.save_to_file(&self.active_slots_coeff, context);
288 CARDANO_FIRST_EPOCH_NUMBER.save_to_file(&self.first_epoch_number, context);
289 CARDANO_FIRST_SLOT_NUMBER.save_to_file(&self.first_slot_number, context);
290 CARDANO_EPOCH_DURATION_MILLIS.save_to_file(&self.epoch_duration_millis, context);
291 CARDANO_FIRST_EPOCH_TIMESTAMP_MILLIS
292 .save_to_file(&self.first_epoch_timestamp_millis, context);
293 CARDANO_SLOT_DURATION_MILLIS.save_to_file(&self.slot_duration_millis, context);
294 }
295}
296
297impl From<CardanoParameters> for sidechain_domain::mainchain_epoch::MainchainEpochConfig {
298 fn from(value: CardanoParameters) -> Self {
299 Self {
300 first_epoch_timestamp_millis: Timestamp::from_unix_millis(
301 value.first_epoch_timestamp_millis,
302 ),
303 epoch_duration_millis: Duration::from_millis(value.epoch_duration_millis),
304 first_epoch_number: value.first_epoch_number,
305 first_slot_number: value.first_slot_number,
306 slot_duration_millis: Duration::from_millis(value.slot_duration_millis),
307 }
308 }
309}
310
311#[derive(Deserialize, Serialize, Parser, Clone, Debug)]
312pub struct SidechainParams {
313 #[arg(long)]
314 pub genesis_utxo: UtxoId,
315}
316
317impl Display for SidechainParams {
318 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
319 let json_string = serde_json::to_string_pretty(self).unwrap();
320 write!(f, "{}", &json_string)
321 }
322}
323
324#[derive(Deserialize, Serialize, Clone, Debug)]
325pub struct AssetConfig {
326 policy_id: String,
327 asset_name: String,
328}
329
330#[derive(Deserialize, Serialize, Clone, Debug)]
331pub struct NativeTokenConfig {
332 pub asset: AssetConfig,
333 pub illiquid_supply_address: String,
334}
335
336#[derive(Deserialize, Serialize, Clone, Debug)]
337pub struct GovernanceAuthoritiesKeyHashes(pub(crate) Vec<MainchainKeyHash>);
338
339impl FromStr for GovernanceAuthoritiesKeyHashes {
340 type Err = String;
341
342 fn from_str(s: &str) -> Result<Self, Self::Err> {
343 let mut key_hashes = vec![];
344 for x in s.split_whitespace() {
345 let trimmed = x.trim();
346 let key_hash = MainchainKeyHash::decode_hex(trimmed).map_err(|e| {
347 format!("'{}' is not a valid Cardano Key Hash, reason: {}", trimmed, e)
348 })?;
349 key_hashes.push(key_hash)
350 }
351 Ok(GovernanceAuthoritiesKeyHashes(key_hashes))
352 }
353}
354
355impl Display for GovernanceAuthoritiesKeyHashes {
356 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
357 let mut it = self.0.iter();
358 if let Some(key_hash) = it.next() {
360 f.write_str(&key_hash.to_hex_string())?;
361 for key_hash in it {
362 f.write_char(' ')?;
363 f.write_str(&key_hash.to_hex_string())?;
364 }
365 }
366 Ok(())
367 }
368}
369
370#[derive(Deserialize)]
371pub struct ChainConfig {
372 pub cardano: CardanoParameters,
373 pub chain_parameters: SidechainParams,
374 pub cardano_addresses: MainChainAddresses,
375}
376
377pub const KEYS_FILE_PATH: &str = "partner-chains-public-keys.json";
378pub const CHAIN_CONFIG_FILE_PATH: &str = "pc-chain-config.json";
379pub const RESOURCES_CONFIG_FILE_PATH: &str = "pc-resources-config.json";
380pub const CHAIN_SPEC_PATH: &str = "chain-spec.json";
381
382pub fn load_chain_config(context: &impl IOContext) -> anyhow::Result<ChainConfig> {
383 if let Some(chain_config_file) = context.read_file(CHAIN_CONFIG_FILE_PATH) {
384 serde_json::from_str::<ChainConfig>(&chain_config_file)
385 .map_err(|err| anyhow::anyhow!(format!("⚠️ Chain config file {CHAIN_CONFIG_FILE_PATH} is invalid: {err}. Run prepare-configuration wizard or fix errors manually.")))
386 } else {
387 Err(anyhow::anyhow!(format!(
388 "⚠️ Chain config file {CHAIN_CONFIG_FILE_PATH} does not exists. Run prepare-configuration wizard first."
389 )))
390 }
391}
392
393pub mod config_fields {
394 use super::*;
395 use sidechain_domain::UtxoId;
396
397 pub const NATIVE_TOKEN_POLICY: ConfigFieldDefinition<'static, String> = ConfigFieldDefinition {
398 config_file: CHAIN_CONFIG_FILE_PATH,
399 path: &["cardano_addresses", "native_token", "asset", "policy_id"],
400 name: "native token policy ID",
401 default: None,
402 _marker: PhantomData,
403 };
404
405 pub const NATIVE_TOKEN_ASSET_NAME: ConfigFieldDefinition<'static, String> =
406 ConfigFieldDefinition {
407 config_file: CHAIN_CONFIG_FILE_PATH,
408 path: &["cardano_addresses", "native_token", "asset", "asset_name"],
409 name: "native token asset name in hex",
410 default: None,
411 _marker: PhantomData,
412 };
413
414 pub const ILLIQUID_SUPPLY_ADDRESS: ConfigFieldDefinition<'static, String> =
415 ConfigFieldDefinition {
416 config_file: CHAIN_CONFIG_FILE_PATH,
417 path: &["cardano_addresses", "native_token", "illiquid_supply_address"],
418 name: "native token illiquid token supply address",
419 default: None,
420 _marker: PhantomData,
421 };
422
423 pub const SUBSTRATE_NODE_DATA_BASE_PATH: ConfigFieldDefinition<'static, String> =
424 ConfigFieldDefinition {
425 config_file: RESOURCES_CONFIG_FILE_PATH,
426 path: &["substrate_node_base_path"],
427 name: "node base path",
428 default: Some("./data"),
429 _marker: PhantomData,
430 };
431
432 pub const CARDANO_PAYMENT_VERIFICATION_KEY_FILE: ConfigFieldDefinition<'static, String> =
433 ConfigFieldDefinition {
434 config_file: RESOURCES_CONFIG_FILE_PATH,
435 path: &["cardano_payment_verification_key_file"],
436 name: "path to the payment verification file",
437 default: Some("payment.vkey"),
438 _marker: PhantomData,
439 };
440
441 pub const CARDANO_PAYMENT_SIGNING_KEY_FILE: ConfigFieldDefinition<'static, String> =
442 ConfigFieldDefinition {
443 config_file: RESOURCES_CONFIG_FILE_PATH,
444 path: &["cardano_payment_signing_key_file"],
445 name: "path to the payment signing key file",
446 default: Some("payment.skey"),
447 _marker: PhantomData,
448 };
449
450 pub const CARDANO_COLD_VERIFICATION_KEY_FILE: ConfigFieldDefinition<'static, String> =
451 ConfigFieldDefinition {
452 config_file: RESOURCES_CONFIG_FILE_PATH,
453 path: &["cardano_cold_verification_key_file"],
454 name: "path to the cold verification key file",
455 default: Some("cold.vkey"),
456 _marker: PhantomData,
457 };
458
459 pub const GENESIS_UTXO: ConfigFieldDefinition<'static, UtxoId> = ConfigFieldDefinition {
460 config_file: CHAIN_CONFIG_FILE_PATH,
461 path: &["chain_parameters", "genesis_utxo"],
462 name: "genesis utxo",
463 default: None,
464 _marker: PhantomData,
465 };
466
467 pub const BOOTNODES: ConfigFieldDefinition<'static, Vec<String>> = ConfigFieldDefinition {
468 config_file: CHAIN_CONFIG_FILE_PATH,
469 path: &["bootnodes"],
470 name: "bootnodes",
471 default: None,
472 _marker: PhantomData,
473 };
474
475 pub(crate) const INITIAL_PERMISSIONED_CANDIDATES: ConfigFieldDefinition<
476 'static,
477 Vec<crate::permissioned_candidates::PermissionedCandidateKeys>,
478 > = ConfigFieldDefinition {
479 config_file: CHAIN_CONFIG_FILE_PATH,
480 path: &["initial_permissioned_candidates"],
481 name: "initial permissioned candidates",
482 default: None,
483 _marker: PhantomData,
484 };
485
486 pub const POSTGRES_CONNECTION_STRING: ConfigFieldDefinition<'static, String> =
487 ConfigFieldDefinition {
488 config_file: RESOURCES_CONFIG_FILE_PATH,
489 path: &["db_sync_postgres_connection_string"],
490 name: "DB-Sync Postgres connection string",
491 default: Some("postgresql://postgres-user:postgres-password@localhost:5432/cexplorer"),
492 _marker: PhantomData,
493 };
494
495 pub const OGMIOS_PROTOCOL: ConfigFieldDefinition<'static, NetworkProtocol> =
496 ConfigFieldDefinition {
497 config_file: RESOURCES_CONFIG_FILE_PATH,
498 path: &["ogmios", "protocol"],
499 name: "Ogmios protocol (http/https)",
500 default: Some("http"),
501 _marker: PhantomData,
502 };
503
504 pub const OGMIOS_HOSTNAME: ConfigFieldDefinition<'static, String> = ConfigFieldDefinition {
505 config_file: RESOURCES_CONFIG_FILE_PATH,
506 path: &["ogmios", "hostname"],
507 name: "Ogmios hostname",
508 default: Some("localhost"),
509 _marker: PhantomData,
510 };
511
512 pub const OGMIOS_PORT: ConfigFieldDefinition<'static, u16> = ConfigFieldDefinition {
513 config_file: RESOURCES_CONFIG_FILE_PATH,
514 path: &["ogmios", "port"],
515 name: "Ogmios port",
516 default: Some("1337"),
517 _marker: PhantomData,
518 };
519
520 pub const OGMIOS_REQUEST_TIMEOUT: ConfigFieldDefinition<'static, u64> = ConfigFieldDefinition {
521 config_file: RESOURCES_CONFIG_FILE_PATH,
522 path: &["ogmios", "request_timeout"],
523 name: "Ogmios request timeout [seconds]",
524 default: Some("180"),
525 _marker: PhantomData,
526 };
527
528 pub const COMMITTEE_CANDIDATES_ADDRESS: ConfigFieldDefinition<'static, String> =
529 ConfigFieldDefinition {
530 config_file: CHAIN_CONFIG_FILE_PATH,
531 path: &["cardano_addresses", "committee_candidates_address"],
532 name: "Committee candidates address",
533 default: None,
534 _marker: PhantomData,
535 };
536
537 pub const D_PARAMETER_POLICY_ID: ConfigFieldDefinition<'static, String> =
538 ConfigFieldDefinition {
539 config_file: CHAIN_CONFIG_FILE_PATH,
540 path: &["cardano_addresses", "d_parameter_policy_id"],
541 name: "D parameter policy id",
542 default: None,
543 _marker: PhantomData,
544 };
545
546 pub const PERMISSIONED_CANDIDATES_POLICY_ID: ConfigFieldDefinition<'static, String> =
547 ConfigFieldDefinition {
548 config_file: CHAIN_CONFIG_FILE_PATH,
549 path: &["cardano_addresses", "permissioned_candidates_policy_id"],
550 name: "permissioned candidates policy id",
551 default: None,
552 _marker: PhantomData,
553 };
554
555 pub const GOVERNED_MAP_VALIDATOR_ADDRESS: ConfigFieldDefinition<'static, String> =
556 ConfigFieldDefinition {
557 config_file: CHAIN_CONFIG_FILE_PATH,
558 path: &["cardano_addresses", "governed_map", "validator_address"],
559 name: "Governed Map Validator Address",
560 default: None,
561 _marker: PhantomData,
562 };
563
564 pub const GOVERNED_MAP_POLICY_ID: ConfigFieldDefinition<'static, String> =
565 ConfigFieldDefinition {
566 config_file: CHAIN_CONFIG_FILE_PATH,
567 path: &["cardano_addresses", "governed_map", "policy_id"],
568 name: "Governed Map Policy Id",
569 default: None,
570 _marker: PhantomData,
571 };
572
573 pub const CARDANO_SECURITY_PARAMETER: ConfigFieldDefinition<'static, u64> =
574 ConfigFieldDefinition {
575 config_file: CHAIN_CONFIG_FILE_PATH,
576 path: &["cardano", "security_parameter"],
577 name: "cardano security parameter",
578 default: None,
579 _marker: PhantomData,
580 };
581
582 pub const CARDANO_ACTIVE_SLOTS_COEFF: ConfigFieldDefinition<'static, f64> =
583 ConfigFieldDefinition {
584 config_file: CHAIN_CONFIG_FILE_PATH,
585 path: &["cardano", "active_slots_coeff"],
586 name: "cardano active slot coefficient",
587 default: None,
588 _marker: PhantomData,
589 };
590
591 pub const CARDANO_FIRST_EPOCH_NUMBER: ConfigFieldDefinition<'static, u32> =
592 ConfigFieldDefinition {
593 config_file: CHAIN_CONFIG_FILE_PATH,
594 path: &["cardano", "first_epoch_number"],
595 name: "cardano first epoch number in shelley era",
596 default: None,
597 _marker: PhantomData,
598 };
599
600 pub const CARDANO_FIRST_SLOT_NUMBER: ConfigFieldDefinition<'static, u64> =
601 ConfigFieldDefinition {
602 config_file: CHAIN_CONFIG_FILE_PATH,
603 path: &["cardano", "first_slot_number"],
604 name: "cardano first slot number in shelley era",
605 default: None,
606 _marker: PhantomData,
607 };
608
609 pub const CARDANO_EPOCH_DURATION_MILLIS: ConfigFieldDefinition<'static, u64> =
610 ConfigFieldDefinition {
611 config_file: CHAIN_CONFIG_FILE_PATH,
612 path: &["cardano", "epoch_duration_millis"],
613 name: "cardano epoch duration in millis",
614 default: None,
615 _marker: PhantomData,
616 };
617
618 pub const CARDANO_FIRST_EPOCH_TIMESTAMP_MILLIS: ConfigFieldDefinition<'static, u64> =
619 ConfigFieldDefinition {
620 config_file: CHAIN_CONFIG_FILE_PATH,
621 path: &["cardano", "first_epoch_timestamp_millis"],
622 name: "cardano first shelley epoch timestamp in millis",
623 default: None,
624 _marker: PhantomData,
625 };
626
627 pub const CARDANO_SLOT_DURATION_MILLIS: ConfigFieldDefinition<'static, u64> =
628 ConfigFieldDefinition {
629 config_file: CHAIN_CONFIG_FILE_PATH,
630 path: &["cardano", "slot_duration_millis"],
631 name: "cardano slot duration in millis",
632 default: Some("1000"),
633 _marker: PhantomData,
634 };
635
636 pub const NODE_P2P_PORT: ConfigFieldDefinition<'static, u16> = ConfigFieldDefinition {
637 config_file: RESOURCES_CONFIG_FILE_PATH,
638 path: &["node_p2p_port"],
639 name: "substrate-node p2p protocol TCP port",
640 default: Some("30333"),
641 _marker: PhantomData,
642 };
643
644 pub const INITIAL_GOVERNANCE_AUTHORITIES: ConfigFieldDefinition<
645 'static,
646 GovernanceAuthoritiesKeyHashes,
647 > = ConfigFieldDefinition {
648 config_file: CHAIN_CONFIG_FILE_PATH,
649 path: &["initial_governance", "authorities"],
650 name: "Space separated keys hashes of the initial Multisig Governance Authorities",
651 default: Some("[]"),
652 _marker: PhantomData,
653 };
654
655 pub const INITIAL_GOVERNANCE_THRESHOLD: ConfigFieldDefinition<'static, u8> =
656 ConfigFieldDefinition {
657 config_file: CHAIN_CONFIG_FILE_PATH,
658 path: &["initial_governance", "threshold"],
659 name: "Initial Multisig Governance Threshold",
660 default: Some("1"),
661 _marker: PhantomData,
662 };
663}