1use std::collections::BTreeMap;
2use std::path::Path;
3use std::str::FromStr;
4
5use eyre::eyre;
6use namada_macros::BorshDeserializer;
7#[cfg(feature = "migrations")]
8use namada_migrations::*;
9use namada_sdk::address::{
10 Address, EstablishedAddress, EstablishedAddressGen, InternalAddress,
11};
12use namada_sdk::borsh::{BorshDeserialize, BorshSerialize, BorshSerializeExt};
13use namada_sdk::chain::{ChainId, ChainIdPrefix};
14use namada_sdk::eth_bridge::EthereumBridgeParams;
15use namada_sdk::governance::pgf::parameters::PgfParameters;
16use namada_sdk::hash::Hash;
17use namada_sdk::ibc::parameters::{IbcParameters, IbcTokenRateLimits};
18use namada_sdk::key::{RefTo, common};
19use namada_sdk::parameters::EpochDuration;
20use namada_sdk::time::{DateTimeUtc, DurationNanos, Rfc3339String};
21use namada_sdk::token::Amount;
22use namada_sdk::wallet::store::AddressVpType;
23use namada_sdk::wallet::{Wallet, pre_genesis};
24use serde::{Deserialize, Serialize};
25use sha2::{Digest, Sha256};
26
27use super::utils::{read_toml, write_toml};
28use super::{templates, transactions};
29use crate::config::genesis::templates::Validated;
30use crate::config::utils::{set_ip, set_port};
31use crate::config::{Config, TendermintMode};
32use crate::tendermint::node::Id as TendermintNodeId;
33use crate::tendermint_config::net::Address as TendermintAddress;
34use crate::tendermint_node::id_from_pk;
35use crate::wallet::{Alias, CliWalletUtils};
36use crate::wasm_loader;
37
38pub const METADATA_FILE_NAME: &str = "chain.toml";
39
40pub trait DeriveEstablishedAddress {
42 const SALT: &'static str;
44
45 fn derive_established_address(&self) -> EstablishedAddress
47 where
48 Self: BorshSerialize,
49 {
50 let mut hasher = Sha256::new();
51 hasher.update(Self::SALT.as_bytes());
52 hasher.update(self.serialize_to_vec());
53 let digest = hasher.finalize();
54 let digest_ref: &[u8; 32] = digest.as_ref();
55 EstablishedAddress::from(*digest_ref)
56 }
57
58 #[inline]
60 fn derive_address(&self) -> Address
61 where
62 Self: BorshSerialize,
63 {
64 Address::Established(self.derive_established_address())
65 }
66}
67
68impl Finalized {
69 pub fn write_toml_files(&self, output_dir: &Path) -> eyre::Result<()> {
72 let vps_file = output_dir.join(templates::VPS_FILE_NAME);
73 let tokens_file = output_dir.join(templates::TOKENS_FILE_NAME);
74 let balances_file = output_dir.join(templates::BALANCES_FILE_NAME);
75 let parameters_file = output_dir.join(templates::PARAMETERS_FILE_NAME);
76 let transactions_file =
77 output_dir.join(templates::TRANSACTIONS_FILE_NAME);
78 let metadata_file = output_dir.join(METADATA_FILE_NAME);
79
80 write_toml(&self.vps, &vps_file, "Validity predicates")?;
81 write_toml(&self.tokens, &tokens_file, "Tokens")?;
82 write_toml(&self.balances, &balances_file, "Balances")?;
83 write_toml(&self.parameters, ¶meters_file, "Parameters")?;
84 write_toml(&self.transactions, &transactions_file, "Transactions")?;
85 write_toml(&self.metadata, &metadata_file, "Chain metadata")?;
86 Ok(())
87 }
88
89 pub fn read_native_token(input_dir: &Path) -> eyre::Result<Address> {
91 let tokens_file = input_dir.join(templates::TOKENS_FILE_NAME);
92 let parameters_file = input_dir.join(templates::PARAMETERS_FILE_NAME);
93
94 let mut tokens: FinalizedTokens = read_toml(&tokens_file, "Tokens")?;
95 let parameters: FinalizedParameters =
96 read_toml(¶meters_file, "Parameters")?;
97
98 let alias = ¶meters.parameters.native_token;
99
100 Ok(tokens
101 .token
102 .remove(alias)
103 .expect("The native token must exist")
104 .address)
105 }
106
107 pub fn read_toml_files(input_dir: &Path) -> eyre::Result<Self> {
112 let vps_file = input_dir.join(templates::VPS_FILE_NAME);
113 let tokens_file = input_dir.join(templates::TOKENS_FILE_NAME);
114 let balances_file = input_dir.join(templates::BALANCES_FILE_NAME);
115 let parameters_file = input_dir.join(templates::PARAMETERS_FILE_NAME);
116 let transactions_file =
117 input_dir.join(templates::TRANSACTIONS_FILE_NAME);
118 let metadata_file = input_dir.join(METADATA_FILE_NAME);
119
120 let vps = read_toml(&vps_file, "Validity predicates")?;
121 let tokens = read_toml(&tokens_file, "Tokens")?;
122 let balances = read_toml(&balances_file, "Balances")?;
123 let parameters = read_toml(¶meters_file, "Parameters")?;
124 let transactions = read_toml(&transactions_file, "Transactions")?;
125 let metadata = read_toml(&metadata_file, "Chain metadata")?;
126 let genesis = Self {
127 vps,
128 tokens,
129 balances,
130 parameters,
131 transactions,
132 metadata,
133 };
134
135 if !genesis.is_valid() {
136 return Err(eyre!("Invalid genesis files"));
137 }
138
139 Ok(genesis)
140 }
141
142 pub fn get_native_token(&self) -> &Address {
144 let alias = &self.parameters.parameters.native_token;
145 &self
146 .tokens
147 .token
148 .get(alias)
149 .expect("The native token must exist")
150 .address
151 }
152
153 pub fn derive_wallet(
155 &self,
156 base_dir: &Path,
157 pre_genesis_wallet: Option<Wallet<CliWalletUtils>>,
158 validator: Option<(Alias, pre_genesis::ValidatorWallet)>,
159 ) -> Wallet<CliWalletUtils> {
160 let mut wallet = crate::wallet::load_or_new(base_dir);
161 for (alias, config) in &self.tokens.token {
162 wallet.insert_address(
163 alias.normalize(),
164 config.address.clone(),
165 false,
166 );
167 wallet.add_vp_type_to_address(
168 AddressVpType::Token,
169 config.address.clone(),
170 );
171 }
172 if let Some(pre_genesis_wallet) = pre_genesis_wallet {
173 wallet.extend(pre_genesis_wallet);
174 }
175 if let Some((alias, validator_wallet)) = validator {
176 let tendermint_pk = validator_wallet.tendermint_node_key.ref_to();
177 let address = self
178 .transactions
179 .find_validator(&tendermint_pk)
180 .map(|tx| Address::Established(tx.tx.data.address.raw.clone()))
181 .expect("Validator alias not found in genesis transactions.");
182 wallet.extend_from_pre_genesis_validator(
183 address.clone(),
184 alias.clone(),
185 validator_wallet,
186 );
187 }
188
189 for int_add in &[
191 InternalAddress::PoS,
192 InternalAddress::Masp,
193 InternalAddress::Ibc,
194 InternalAddress::EthBridge,
195 InternalAddress::EthBridgePool,
196 InternalAddress::Governance,
197 InternalAddress::Pgf,
198 ] {
199 wallet.insert_address(
200 int_add.to_string().to_lowercase(),
201 Address::Internal(int_add.clone()),
202 false,
203 );
204 }
205
206 wallet
207 }
208
209 pub fn derive_config(
211 &self,
212 base_dir: &Path,
213 node_mode: TendermintMode,
214 tendermint_pk: Option<&common::PublicKey>,
215 allow_duplicate_ip: bool,
216 add_persistent_peers: bool,
217 ) -> Config {
218 if node_mode != TendermintMode::Validator && tendermint_pk.is_some() {
219 println!(
220 "Warning: Validator alias used to derive config, but node \
221 mode is not validator, it is {node_mode:?}!"
222 );
223 }
224 let mut config =
225 Config::new(base_dir, self.metadata.chain_id.clone(), node_mode);
226
227 let persistent_peers =
229 self.derive_persistent_peers(add_persistent_peers);
230 let validator_net_and_tm_address =
232 if let Some(tendermint_pk) = tendermint_pk {
233 self.transactions.find_validator(tendermint_pk).map(
234 |validator_tx| {
235 (
236 validator_tx.tx.data.net_address,
237 validator_tx.derive_tendermint_address(),
238 )
239 },
240 )
241 } else {
242 None
243 };
244 let is_localhost = persistent_peers.iter().all(|peer| match peer {
247 TendermintAddress::Tcp {
248 peer_id: _,
249 host,
250 port: _,
251 } => matches!(host.as_str(), "127.0.0.1" | "localhost"),
252 TendermintAddress::Unix { path: _ } => false,
253 });
254
255 config.ledger.genesis_time = self.metadata.genesis_time.clone();
257
258 config.ledger.cometbft.p2p.persistent_peers = persistent_peers;
260 config.ledger.cometbft.consensus.timeout_commit =
261 self.metadata.consensus_timeout_commit.into();
262 config.ledger.cometbft.p2p.allow_duplicate_ip = allow_duplicate_ip;
263 config.ledger.cometbft.p2p.addr_book_strict = !is_localhost;
264
265 if let Some((net_address, tm_address)) = validator_net_and_tm_address {
266 config.ledger.cometbft.p2p.persistent_peers = config.ledger.cometbft.p2p.persistent_peers.iter()
268 .filter_map(|peer|
269 if peer != &tm_address {
271 Some(peer.to_owned())
272 } else {
273 None
274 })
275 .collect();
276
277 let first_port = net_address.port();
278 if !is_localhost {
279 set_ip(&mut config.ledger.cometbft.p2p.laddr, "0.0.0.0");
280 }
281 set_port(&mut config.ledger.cometbft.p2p.laddr, first_port);
282 if !is_localhost {
283 set_ip(&mut config.ledger.cometbft.rpc.laddr, "0.0.0.0");
284 }
285 set_port(
286 &mut config.ledger.cometbft.rpc.laddr,
287 first_port.checked_add(1).expect("Port must not overflow"),
288 );
289 set_port(
290 &mut config.ledger.cometbft.proxy_app,
291 first_port.checked_add(2).expect("Port must not overflow"),
292 );
293
294 config.ledger.cometbft.p2p.pex = false;
296 }
297
298 config
299 }
300
301 fn derive_persistent_peers(
303 &self,
304 add_persistent_peers: bool,
305 ) -> Vec<TendermintAddress> {
306 add_persistent_peers.then(|| {
307 self.transactions
308 .validator_account
309 .as_ref()
310 .map(|txs| {
311 txs.iter()
312 .map(FinalizedValidatorAccountTx::derive_tendermint_address)
313 .collect()
314 })
315 .unwrap_or_default()
316 })
317 .unwrap_or_default()
318 }
319
320 pub fn get_chain_parameters(
322 &self,
323 wasm_dir: impl AsRef<Path>,
324 ) -> namada_sdk::parameters::Parameters {
325 let templates::ChainParams {
326 min_num_of_blocks,
327 max_proposal_bytes,
328 vp_allowlist,
329 tx_allowlist,
330 implicit_vp,
331 epochs_per_year,
332 masp_epoch_multiplier,
333 masp_fee_payment_gas_limit,
334 gas_scale,
335 max_block_gas,
336 minimum_gas_price,
337 max_tx_bytes,
338 is_native_token_transferable,
339 ..
340 } = self.parameters.parameters.clone();
341
342 let implicit_vp_filename = &self
343 .vps
344 .wasm
345 .get(&implicit_vp)
346 .expect("Implicit VP must be present")
347 .filename;
348
349 let implicit_vp_code_hash =
350 wasm_loader::read_wasm(&wasm_dir, implicit_vp_filename)
351 .ok()
352 .map(Hash::sha256);
353
354 let epy_i64 = i64::try_from(epochs_per_year)
355 .expect("`epochs_per_year` must not exceed `i64::MAX`");
356 #[allow(clippy::arithmetic_side_effects)]
357 let min_duration: i64 = 60 * 60 * 24 * 365 / epy_i64;
358 let epoch_duration = EpochDuration {
359 min_num_of_blocks,
360 min_duration: namada_sdk::time::Duration::seconds(min_duration)
361 .into(),
362 };
363 let vp_allowlist = vp_allowlist.unwrap_or_default();
364 let tx_allowlist = tx_allowlist.unwrap_or_default();
365
366 namada_sdk::parameters::Parameters {
367 max_tx_bytes,
368 epoch_duration,
369 vp_allowlist,
370 tx_allowlist,
371 implicit_vp_code_hash,
372 epochs_per_year,
373 masp_epoch_multiplier,
374 max_proposal_bytes,
375 masp_fee_payment_gas_limit,
376 gas_scale,
377 max_block_gas,
378 minimum_gas_price: minimum_gas_price
379 .iter()
380 .map(|(token, amt)| {
381 (
382 self.tokens.token.get(token).cloned().unwrap().address,
383 amt.amount(),
384 )
385 })
386 .collect(),
387 is_native_token_transferable,
388 }
389 }
390
391 pub fn get_pos_params(
392 &self,
393 ) -> namada_sdk::proof_of_stake::parameters::PosParams {
394 let templates::PosParams {
395 max_validator_slots,
396 pipeline_len,
397 unbonding_len,
398 tm_votes_per_token,
399 block_proposer_reward,
400 block_vote_reward,
401 max_inflation_rate,
402 target_staked_ratio,
403 duplicate_vote_min_slash_rate,
404 light_client_attack_min_slash_rate,
405 cubic_slashing_window_length,
406 validator_stake_threshold,
407 liveness_window_check,
408 liveness_threshold,
409 rewards_gain_p,
410 rewards_gain_d,
411 } = self.parameters.pos_params.clone();
412
413 namada_sdk::proof_of_stake::parameters::PosParams {
414 owned: namada_sdk::proof_of_stake::parameters::OwnedPosParams {
415 max_validator_slots,
416 pipeline_len,
417 unbonding_len,
418 tm_votes_per_token,
419 block_proposer_reward,
420 block_vote_reward,
421 max_inflation_rate,
422 target_staked_ratio,
423 duplicate_vote_min_slash_rate,
424 light_client_attack_min_slash_rate,
425 cubic_slashing_window_length,
426 validator_stake_threshold,
427 liveness_window_check,
428 liveness_threshold,
429 rewards_gain_p,
430 rewards_gain_d,
431 },
432 max_proposal_period: self.parameters.gov_params.max_proposal_period,
433 }
434 }
435
436 pub fn get_gov_params(
437 &self,
438 ) -> namada_sdk::governance::parameters::GovernanceParameters {
439 let templates::GovernanceParams {
440 min_proposal_fund,
441 max_proposal_code_size,
442 min_proposal_voting_period,
443 max_proposal_period,
444 max_proposal_content_size,
445 min_proposal_grace_epochs,
446 max_proposal_latency,
447 } = self.parameters.gov_params.clone();
448 namada_sdk::governance::parameters::GovernanceParameters {
449 min_proposal_fund: Amount::native_whole(min_proposal_fund),
450 max_proposal_code_size,
451 max_proposal_period,
452 max_proposal_content_size,
453 min_proposal_grace_epochs,
454 min_proposal_voting_period,
455 max_proposal_latency,
456 }
457 }
458
459 pub fn get_pgf_params(&self) -> PgfParameters {
460 self.parameters.pgf_params.clone()
461 }
462
463 pub fn get_eth_bridge_params(&self) -> Option<EthereumBridgeParams> {
464 if let Some(templates::EthBridgeParams {
465 eth_start_height,
466 min_confirmations,
467 contracts,
468 erc20_whitelist,
469 }) = self.parameters.eth_bridge_params.clone()
470 {
471 Some(EthereumBridgeParams {
472 eth_start_height,
473 min_confirmations,
474 erc20_whitelist,
475 contracts,
476 })
477 } else {
478 None
479 }
480 }
481
482 pub fn get_ibc_params(&self) -> IbcParameters {
483 let templates::IbcParams {
484 default_mint_limit,
485 default_per_epoch_throughput_limit,
486 } = self.parameters.ibc_params.clone();
487 IbcParameters {
488 default_rate_limits: IbcTokenRateLimits {
489 mint_limit: default_mint_limit,
490 throughput_per_epoch_limit: default_per_epoch_throughput_limit,
491 },
492 }
493 }
494
495 pub fn get_token_address(&self, alias: &Alias) -> Option<&Address> {
496 self.tokens.token.get(alias).map(|token| &token.address)
497 }
498
499 pub fn is_valid(&self) -> bool {
501 let Self {
502 vps,
503 tokens,
504 balances,
505 parameters,
506 transactions,
507 metadata,
508 } = self.clone();
509 let Metadata {
510 chain_id,
511 genesis_time,
512 consensus_timeout_commit,
513 address_gen,
514 } = metadata.clone();
515
516 let Some(chain_id_prefix) = chain_id.prefix() else {
517 tracing::warn!(
518 "Invalid Chain ID \"{chain_id}\" - unable to find a prefix"
519 );
520 return false;
521 };
522 let metadata = Metadata {
523 chain_id: chain_id_prefix.clone(),
524 genesis_time,
525 consensus_timeout_commit,
526 address_gen,
527 };
528 let to_finalize = ToFinalize {
529 vps,
530 tokens,
531 balances,
532 parameters,
533 transactions,
534 metadata,
535 };
536 let derived_chain_id = derive_chain_id(chain_id_prefix, &to_finalize);
537 let is_valid = derived_chain_id == chain_id;
538 if !is_valid {
539 tracing::warn!(
540 "Invalid chain ID. This indicates that something in the \
541 genesis files might have been modified."
542 );
543 }
544 is_valid
545 }
546}
547
548pub fn finalize(
555 templates: templates::All<Validated>,
556 chain_id_prefix: ChainIdPrefix,
557 genesis_time: DateTimeUtc,
558 consensus_timeout_commit: crate::tendermint::Timeout,
559) -> Finalized {
560 let genesis_time: Rfc3339String = genesis_time.into();
561 let consensus_timeout_commit: DurationNanos =
562 consensus_timeout_commit.into();
563
564 let genesis_to_gen_address = GenesisToGenAddresses {
566 templates,
567 metadata: Metadata {
568 chain_id: chain_id_prefix.clone(),
569 genesis_time,
570 consensus_timeout_commit,
571 address_gen: None,
572 },
573 };
574 let genesis_bytes = genesis_to_gen_address.serialize_to_vec();
575 let addr_gen = established_address_gen(&genesis_bytes);
576
577 let templates::All {
579 vps,
580 tokens,
581 balances,
582 parameters,
583 transactions,
584 } = genesis_to_gen_address.templates;
585 let tokens = FinalizedTokens::finalize_from(tokens);
586 let transactions = FinalizedTransactions::finalize_from(transactions);
587 let parameters = FinalizedParameters::finalize_from(parameters);
588
589 let mut metadata = genesis_to_gen_address.metadata;
591 metadata.address_gen = Some(addr_gen);
592
593 let to_finalize = ToFinalize {
595 metadata,
596 vps,
597 tokens,
598 balances,
599 parameters,
600 transactions,
601 };
602 let chain_id = derive_chain_id(chain_id_prefix, &to_finalize);
603
604 let ToFinalize {
606 vps,
607 tokens,
608 balances,
609 parameters,
610 transactions,
611 metadata,
612 } = to_finalize;
613 let Metadata {
614 chain_id: _,
615 genesis_time,
616 consensus_timeout_commit,
617 address_gen,
618 } = metadata;
619 let metadata = Metadata {
620 chain_id,
621 genesis_time,
622 consensus_timeout_commit,
623 address_gen,
624 };
625 Finalized {
626 metadata,
627 vps,
628 tokens,
629 balances,
630 parameters,
631 transactions,
632 }
633}
634
635pub fn derive_chain_id(
637 chain_id_prefix: ChainIdPrefix,
638 to_finalize: &ToFinalize,
639) -> ChainId {
640 let to_finalize_bytes = to_finalize.serialize_to_vec();
641 ChainId::from_genesis(chain_id_prefix, to_finalize_bytes)
642}
643
644#[derive(
647 Clone, Debug, Deserialize, Serialize, BorshDeserialize, BorshSerialize,
648)]
649pub struct GenesisToGenAddresses {
650 pub templates: templates::All<Validated>,
652 pub metadata: Metadata<ChainIdPrefix>,
654}
655
656pub type ToFinalize = Chain<ChainIdPrefix>;
659
660pub type Finalized = Chain<ChainId>;
662
663fn established_address_gen(bytes: &[u8]) -> EstablishedAddressGen {
665 let mut hasher = Sha256::new();
666 hasher.update(bytes);
667 let hash = format!("{:.width$X}", hasher.finalize(), width = 40);
669 EstablishedAddressGen::new(hash)
670}
671
672#[derive(
674 Clone,
675 Debug,
676 Deserialize,
677 Serialize,
678 BorshDeserialize,
679 BorshSerialize,
680 PartialEq,
681 Eq,
682)]
683pub struct Chain<ID> {
684 pub vps: templates::ValidityPredicates,
685 pub tokens: FinalizedTokens,
686 pub balances: templates::DenominatedBalances,
687 pub parameters: FinalizedParameters,
688 pub transactions: FinalizedTransactions,
689 pub metadata: Metadata<ID>,
691}
692
693#[derive(
694 Clone,
695 Debug,
696 Deserialize,
697 Serialize,
698 BorshDeserialize,
699 BorshDeserializer,
700 BorshSerialize,
701 PartialEq,
702 Eq,
703)]
704pub struct FinalizedTokens {
705 pub token: BTreeMap<Alias, FinalizedTokenConfig>,
706}
707
708impl FinalizedTokens {
709 fn finalize_from(tokens: templates::Tokens) -> FinalizedTokens {
710 let templates::Tokens { token } = tokens;
711 let token = token
712 .into_iter()
713 .map(|(key, config)| {
714 let address = Address::Established(
715 (&key, &config).derive_established_address(),
716 );
717 (key, FinalizedTokenConfig { address, config })
718 })
719 .collect();
720 FinalizedTokens { token }
721 }
722}
723
724#[derive(
725 Clone,
726 Debug,
727 Deserialize,
728 Serialize,
729 BorshDeserialize,
730 BorshDeserializer,
731 BorshSerialize,
732 PartialEq,
733 Eq,
734)]
735pub struct FinalizedTokenConfig {
736 pub address: Address,
737 #[serde(flatten)]
738 pub config: templates::TokenConfig,
739}
740
741impl DeriveEstablishedAddress for (&Alias, &templates::TokenConfig) {
742 const SALT: &'static str = "token-config";
743}
744
745#[derive(
746 Clone,
747 Debug,
748 Default,
749 Deserialize,
750 Serialize,
751 BorshDeserialize,
752 BorshDeserializer,
753 BorshSerialize,
754 PartialEq,
755 Eq,
756)]
757pub struct FinalizedTransactions {
758 pub established_account: Option<Vec<FinalizedEstablishedAccountTx>>,
759 pub validator_account: Option<Vec<FinalizedValidatorAccountTx>>,
760 pub bond: Option<Vec<transactions::BondTx<Validated>>>,
761}
762
763impl FinalizedTransactions {
764 fn finalize_from(
765 transactions: transactions::Transactions<Validated>,
766 ) -> FinalizedTransactions {
767 let transactions::Transactions {
768 established_account,
769 validator_account,
770 bond,
771 } = transactions;
772 let established_account = established_account.map(|txs| {
773 txs.into_iter()
774 .map(|tx| FinalizedEstablishedAccountTx {
775 address: tx.derive_address(),
776 tx,
777 })
778 .collect()
779 });
780 let validator_account = validator_account.map(|txs| {
781 txs.into_iter()
782 .map(|tx| FinalizedValidatorAccountTx { tx })
783 .collect()
784 });
785 FinalizedTransactions {
786 established_account,
787 validator_account,
788 bond,
789 }
790 }
791
792 fn find_validator(
793 &self,
794 tendermint_pk: &common::PublicKey,
795 ) -> Option<&FinalizedValidatorAccountTx> {
796 let validator_accounts = self.validator_account.as_ref()?;
797 validator_accounts
798 .iter()
799 .find(|tx| &tx.tx.data.tendermint_node_key.pk.raw == tendermint_pk)
800 }
801}
802
803#[derive(
804 Clone,
805 Debug,
806 Deserialize,
807 Serialize,
808 BorshDeserialize,
809 BorshDeserializer,
810 BorshSerialize,
811 PartialEq,
812 Eq,
813)]
814pub struct FinalizedParameters {
815 pub parameters: templates::ChainParams<Validated>,
816 pub pos_params: templates::PosParams,
817 pub gov_params: templates::GovernanceParams,
818 pub pgf_params: PgfParameters,
819 pub eth_bridge_params: Option<templates::EthBridgeParams>,
820 pub ibc_params: templates::IbcParams,
821}
822
823impl FinalizedParameters {
824 fn finalize_from(
825 templates::Parameters {
826 parameters,
827 pos_params,
828 gov_params,
829 pgf_params,
830 eth_bridge_params,
831 ibc_params,
832 }: templates::Parameters<Validated>,
833 ) -> Self {
834 let finalized_pgf_params = PgfParameters {
835 stewards: pgf_params.stewards,
836 pgf_inflation_rate: pgf_params.pgf_inflation_rate,
837 stewards_inflation_rate: pgf_params.stewards_inflation_rate,
838 maximum_number_of_stewards: pgf_params.maximum_number_of_stewards,
839 };
840 Self {
841 parameters,
842 pos_params,
843 gov_params,
844 pgf_params: finalized_pgf_params,
845 eth_bridge_params,
846 ibc_params,
847 }
848 }
849}
850
851#[derive(
852 Clone,
853 Debug,
854 Deserialize,
855 Serialize,
856 BorshSerialize,
857 BorshDeserialize,
858 BorshDeserializer,
859 PartialEq,
860 Eq,
861)]
862pub struct FinalizedEstablishedAccountTx {
863 pub address: Address,
864 #[serde(flatten)]
865 pub tx: transactions::EstablishedAccountTx,
866}
867
868#[derive(
869 Clone,
870 Debug,
871 Deserialize,
872 Serialize,
873 BorshSerialize,
874 BorshDeserialize,
875 BorshDeserializer,
876 PartialEq,
877 Eq,
878)]
879pub struct FinalizedValidatorAccountTx {
880 #[serde(flatten)]
881 pub tx: transactions::SignedValidatorAccountTx,
882}
883
884impl FinalizedValidatorAccountTx {
885 pub fn derive_tendermint_address(&self) -> TendermintAddress {
886 let node_id: TendermintNodeId =
888 id_from_pk(&self.tx.data.tendermint_node_key.pk.raw);
889
890 TendermintAddress::from_str(&format!(
892 "{}@{}",
893 node_id, self.tx.data.net_address,
894 ))
895 .expect("Validator address must be valid")
896 }
897}
898
899#[derive(
901 Clone,
902 Debug,
903 Deserialize,
904 Serialize,
905 BorshDeserialize,
906 BorshSerialize,
907 PartialEq,
908 Eq,
909)]
910pub struct Metadata<ID> {
911 pub chain_id: ID,
914 pub genesis_time: Rfc3339String,
916 pub consensus_timeout_commit: DurationNanos,
918 pub address_gen: Option<EstablishedAddressGen>,
924}
925
926#[cfg(test)]
927mod test {
928 use std::path::PathBuf;
929
930 use super::*;
931 use crate::time::test_utils::GENESIS_TIME;
932
933 #[test]
936 fn test_finalize_is_deterministic() {
937 let templates_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
939 .parent()
940 .unwrap()
941 .parent()
942 .unwrap()
943 .join("genesis/localnet");
944 let templates = templates::load_and_validate(&templates_dir).unwrap();
945
946 let chain_id_prefix: ChainIdPrefix =
947 FromStr::from_str("test-prefix").unwrap();
948
949 let genesis_time = DateTimeUtc::from_str(GENESIS_TIME).unwrap();
950
951 let consensus_timeout_commit =
952 crate::tendermint::Timeout::from_str("1s").unwrap();
953
954 let finalized_0 = finalize(
955 templates.clone(),
956 chain_id_prefix.clone(),
957 genesis_time,
958 consensus_timeout_commit,
959 );
960
961 let finalized_1 = finalize(
962 templates,
963 chain_id_prefix,
964 genesis_time,
965 consensus_timeout_commit,
966 );
967
968 pretty_assertions::assert_eq!(finalized_0, finalized_1);
969 }
970}