mithril_client/
message.rs

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