mithril_client/
message.rs

1use anyhow::Context;
2use slog::{Logger, o};
3#[cfg(feature = "fs")]
4use std::{path::Path, sync::Arc};
5
6#[cfg(feature = "fs")]
7use mithril_cardano_node_internal_database::digesters::{
8    CardanoImmutableDigester, ImmutableDigester,
9};
10#[cfg(feature = "fs")]
11use mithril_common::entities::SignedEntityType;
12use mithril_common::{
13    logging::LoggerExtensions, protocol::SignerBuilder,
14    signable_builder::CardanoStakeDistributionSignableBuilder,
15};
16
17#[cfg(feature = "fs")]
18use mithril_common::crypto_helper::MKProof;
19
20use crate::{
21    CardanoStakeDistribution, MithrilCertificate, MithrilResult, MithrilSigner,
22    MithrilStakeDistribution, VerifiedCardanoTransactions,
23    common::{ProtocolMessage, ProtocolMessagePartKey},
24};
25
26/// A [MessageBuilder] can be used to compute the message of Mithril artifacts.
27pub struct MessageBuilder {
28    #[cfg(feature = "fs")]
29    immutable_digester: Option<Arc<dyn ImmutableDigester>>,
30    logger: Logger,
31}
32
33impl MessageBuilder {
34    /// Constructs a new `MessageBuilder`.
35    pub fn new() -> MessageBuilder {
36        let logger = Logger::root(slog::Discard, o!());
37        Self {
38            #[cfg(feature = "fs")]
39            immutable_digester: None,
40            logger,
41        }
42    }
43
44    /// Set the [Logger] to use.
45    pub fn with_logger(mut self, logger: Logger) -> Self {
46        self.logger = logger.new_with_component_name::<Self>();
47        self
48    }
49
50    cfg_fs! {
51        fn get_immutable_digester(&self, network: &str) -> Arc<dyn ImmutableDigester> {
52            match self.immutable_digester.as_ref() {
53                None => Arc::new(CardanoImmutableDigester::new(network.to_owned(),None, self.logger.clone())),
54                Some(digester) => digester.clone(),
55            }
56        }
57
58        /// Set the [ImmutableDigester] to be used for the message computation for snapshot.
59        ///
60        /// If not set a default implementation will be used.
61        pub fn with_immutable_digester(
62            mut self,
63            immutable_digester: Arc<dyn ImmutableDigester>,
64        ) -> Self {
65            self.immutable_digester = Some(immutable_digester);
66            self
67        }
68
69        /// Compute message for a snapshot (based on the directory where it was unpacked).
70        ///
71        /// Warning: this operation can be quite long depending on the snapshot size.
72        pub async fn compute_snapshot_message(
73            &self,
74            snapshot_certificate: &MithrilCertificate,
75            unpacked_snapshot_directory: &Path,
76        ) -> MithrilResult<ProtocolMessage> {
77            let digester = self.get_immutable_digester(&snapshot_certificate.metadata.network);
78            let beacon =
79                match &snapshot_certificate.signed_entity_type {
80                SignedEntityType::CardanoImmutableFilesFull(beacon) => {Ok(beacon)},
81                other => {
82                    Err(anyhow::anyhow!(
83                    "Can't compute message: Given certificate `{}` does not certify a snapshot, certificate signed entity: {:?}",
84                    snapshot_certificate.hash,
85                    other
86                        )
87                    )},
88            }?;
89
90            let mut message = snapshot_certificate.protocol_message.clone();
91
92            let digest = digester
93                .compute_digest(unpacked_snapshot_directory, &beacon.clone())
94                .await
95                .with_context(|| {
96                    format!(
97                        "Snapshot digest computation failed: unpacked_dir: '{}'",
98                        unpacked_snapshot_directory.display()
99                    )
100                })?;
101            message.set_message_part(ProtocolMessagePartKey::SnapshotDigest, digest);
102
103            Ok(message)
104        }
105
106        /// Compute message for a Cardano database.
107        pub async fn compute_cardano_database_message(
108        &self,
109            certificate: &MithrilCertificate,
110            merkle_proof: &MKProof,
111        ) -> MithrilResult<ProtocolMessage> {
112            let mut message = certificate.protocol_message.clone();
113            message.set_message_part(
114                ProtocolMessagePartKey::CardanoDatabaseMerkleRoot,
115                merkle_proof.root().to_hex(),
116            );
117            Ok(message)
118        }
119    }
120
121    /// Compute message for a Mithril stake distribution.
122    pub fn compute_mithril_stake_distribution_message(
123        &self,
124        certificate: &MithrilCertificate,
125        mithril_stake_distribution: &MithrilStakeDistribution,
126    ) -> MithrilResult<ProtocolMessage> {
127        let signers =
128            MithrilSigner::try_into_signers(mithril_stake_distribution.signers_with_stake.clone())
129                .with_context(|| "Could not compute message: conversion failure")?;
130
131        let signer_builder =
132            SignerBuilder::new(&signers, &mithril_stake_distribution.protocol_parameters)
133                .with_context(
134                    || "Could not compute message: aggregate verification key computation failed",
135                )?;
136
137        let avk = signer_builder
138            .compute_aggregate_verification_key()
139            .to_json_hex()
140            .with_context(
141                || "Could not compute message: aggregate verification key encoding failed",
142            )?;
143
144        let mut message = certificate.protocol_message.clone();
145        message.set_message_part(ProtocolMessagePartKey::NextAggregateVerificationKey, avk);
146
147        Ok(message)
148    }
149
150    /// Compute message for a Cardano Transactions Proofs.
151    pub fn compute_cardano_transactions_proofs_message(
152        &self,
153        transactions_proofs_certificate: &MithrilCertificate,
154        verified_transactions: &VerifiedCardanoTransactions,
155    ) -> ProtocolMessage {
156        let mut message = transactions_proofs_certificate.protocol_message.clone();
157        verified_transactions.fill_protocol_message(&mut message);
158        message
159    }
160
161    /// Compute message for a Cardano stake distribution.
162    pub fn compute_cardano_stake_distribution_message(
163        &self,
164        certificate: &MithrilCertificate,
165        cardano_stake_distribution: &CardanoStakeDistribution,
166    ) -> MithrilResult<ProtocolMessage> {
167        let mk_tree =
168            CardanoStakeDistributionSignableBuilder::compute_merkle_tree_from_stake_distribution(
169                cardano_stake_distribution.stake_distribution.clone(),
170            )?;
171
172        let mut message = certificate.protocol_message.clone();
173        message.set_message_part(
174            ProtocolMessagePartKey::CardanoStakeDistributionEpoch,
175            cardano_stake_distribution.epoch.to_string(),
176        );
177        message.set_message_part(
178            ProtocolMessagePartKey::CardanoStakeDistributionMerkleRoot,
179            mk_tree.compute_root()?.to_hex(),
180        );
181
182        Ok(message)
183    }
184}
185
186impl Default for MessageBuilder {
187    fn default() -> Self {
188        Self::new()
189    }
190}