Skip to main content

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    crypto_helper::ProtocolKey, 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#[cfg(feature = "unstable")]
27use crate::{VerifiedCardanoBlocks, VerifiedCardanoTransactionsV2};
28
29/// A [MessageBuilder] can be used to compute the message of Mithril artifacts.
30pub struct MessageBuilder {
31    #[cfg(feature = "fs")]
32    immutable_digester: Option<Arc<dyn ImmutableDigester>>,
33    logger: Logger,
34}
35
36impl MessageBuilder {
37    /// Constructs a new `MessageBuilder`.
38    pub fn new() -> MessageBuilder {
39        let logger = Logger::root(slog::Discard, o!());
40        Self {
41            #[cfg(feature = "fs")]
42            immutable_digester: None,
43            logger,
44        }
45    }
46
47    /// Set the [Logger] to use.
48    pub fn with_logger(mut self, logger: Logger) -> Self {
49        self.logger = logger.new_with_component_name::<Self>();
50        self
51    }
52
53    cfg_fs! {
54        fn get_immutable_digester(&self, network: &str) -> Arc<dyn ImmutableDigester> {
55            match self.immutable_digester.as_ref() {
56                None => Arc::new(CardanoImmutableDigester::new(network.to_owned(),None, self.logger.clone())),
57                Some(digester) => digester.clone(),
58            }
59        }
60
61        /// Set the [ImmutableDigester] to be used for the message computation for snapshot.
62        ///
63        /// If not set a default implementation will be used.
64        pub fn with_immutable_digester(
65            mut self,
66            immutable_digester: Arc<dyn ImmutableDigester>,
67        ) -> Self {
68            self.immutable_digester = Some(immutable_digester);
69            self
70        }
71
72        /// Compute message for a snapshot (based on the directory where it was unpacked).
73        ///
74        /// Warning: this operation can be quite long depending on the snapshot size.
75        pub async fn compute_snapshot_message(
76            &self,
77            snapshot_certificate: &MithrilCertificate,
78            unpacked_snapshot_directory: &Path,
79        ) -> MithrilResult<ProtocolMessage> {
80            let digester = self.get_immutable_digester(&snapshot_certificate.metadata.network);
81            let beacon =
82                match &snapshot_certificate.signed_entity_type {
83                SignedEntityType::CardanoImmutableFilesFull(beacon) => {Ok(beacon)},
84                other => {
85                    Err(anyhow::anyhow!(
86                    "Can't compute message: Given certificate `{}` does not certify a snapshot, certificate signed entity: {:?}",
87                    snapshot_certificate.hash,
88                    other
89                        )
90                    )},
91            }?;
92
93            let mut message = snapshot_certificate.protocol_message.clone();
94
95            let digest = digester
96                .compute_digest(unpacked_snapshot_directory, &beacon.clone())
97                .await
98                .with_context(|| {
99                    format!(
100                        "Snapshot digest computation failed: unpacked_dir: '{}'",
101                        unpacked_snapshot_directory.display()
102                    )
103                })?;
104            message.set_message_part(ProtocolMessagePartKey::SnapshotDigest, digest);
105
106            Ok(message)
107        }
108
109        /// Compute message for a Cardano database.
110        pub async fn compute_cardano_database_message(
111        &self,
112            certificate: &MithrilCertificate,
113            merkle_proof: &MKProof,
114        ) -> MithrilResult<ProtocolMessage> {
115            let mut message = certificate.protocol_message.clone();
116            message.set_message_part(
117                ProtocolMessagePartKey::CardanoDatabaseMerkleRoot,
118                merkle_proof.root().to_hex(),
119            );
120            Ok(message)
121        }
122    }
123
124    /// Compute message for a Mithril stake distribution.
125    pub fn compute_mithril_stake_distribution_message(
126        &self,
127        certificate: &MithrilCertificate,
128        mithril_stake_distribution: &MithrilStakeDistribution,
129    ) -> MithrilResult<ProtocolMessage> {
130        let signers =
131            MithrilSigner::try_into_signers(mithril_stake_distribution.signers_with_stake.clone())
132                .with_context(|| "Could not compute message: conversion failure")?;
133
134        let signer_builder =
135            SignerBuilder::new(&signers, &mithril_stake_distribution.protocol_parameters)
136                .with_context(
137                    || "Could not compute message: aggregate verification key computation failed",
138                )?;
139
140        let aggregate_verification_key = signer_builder.compute_aggregate_verification_key();
141
142        let avk = ProtocolKey::new(
143            aggregate_verification_key
144                .to_concatenation_aggregate_verification_key()
145                .to_owned(),
146        )
147        .to_json_hex()
148        .with_context(|| "Could not compute message: aggregate verification key encoding failed")?;
149
150        let mut message = certificate.protocol_message.clone();
151        message.set_message_part(ProtocolMessagePartKey::NextAggregateVerificationKey, avk);
152
153        #[cfg(feature = "future_snark")]
154        if certificate
155            .protocol_message
156            .get_message_part(&ProtocolMessagePartKey::NextSnarkAggregateVerificationKey)
157            .is_some()
158        {
159            let snark_avk = aggregate_verification_key
160                .to_snark_aggregate_verification_key()
161                .ok_or_else(|| {
162                    anyhow::anyhow!(
163                        "Could not compute message: SNARK aggregate verification key is unavailable"
164                    )
165                })?;
166            let snark_avk_encoded = ProtocolKey::new(snark_avk.to_owned())
167                .to_bytes_hex()
168                .with_context(|| {
169                    "Could not compute message: SNARK aggregate verification key encoding failed"
170                })?;
171            message.set_message_part(
172                ProtocolMessagePartKey::NextSnarkAggregateVerificationKey,
173                snark_avk_encoded,
174            );
175        }
176
177        Ok(message)
178    }
179
180    /// Compute message for a Cardano Transactions Proofs.
181    pub fn compute_cardano_transactions_proofs_message(
182        &self,
183        transactions_proofs_certificate: &MithrilCertificate,
184        verified_transactions: &VerifiedCardanoTransactions,
185    ) -> ProtocolMessage {
186        let mut message = transactions_proofs_certificate.protocol_message.clone();
187        verified_transactions.fill_protocol_message(&mut message);
188        message
189    }
190
191    cfg_unstable! {
192        /// Compute message for a Cardano Blocks Proofs.
193        pub fn compute_cardano_blocks_proofs_message(
194            &self,
195            blocks_proofs_certificate: &MithrilCertificate,
196            verified_blocks: &VerifiedCardanoBlocks,
197        ) -> ProtocolMessage {
198            let mut message = blocks_proofs_certificate.protocol_message.clone();
199            message.set_message_part(
200                ProtocolMessagePartKey::CardanoBlocksTransactionsMerkleRoot,
201                verified_blocks.certified_merkle_root().to_string(),
202            );
203            message.set_message_part(
204                ProtocolMessagePartKey::LatestBlockNumber,
205                verified_blocks.latest_certified_block_number().to_string(),
206            );
207            message.set_message_part(
208                ProtocolMessagePartKey::CardanoBlocksTransactionsBlockNumberOffset,
209                verified_blocks.security_parameter().to_string(),
210            );
211            message
212        }
213
214        /// Compute message for a Cardano Transaction V2 Proofs.
215        pub fn compute_cardano_transactions_proofs_v2_message(
216            &self,
217            transactions_proofs_certificate: &MithrilCertificate,
218            verified_transactions: &VerifiedCardanoTransactionsV2,
219        ) -> ProtocolMessage {
220            let mut message = transactions_proofs_certificate.protocol_message.clone();
221            message.set_message_part(
222                ProtocolMessagePartKey::CardanoBlocksTransactionsMerkleRoot,
223                verified_transactions.certified_merkle_root().to_string(),
224            );
225            message.set_message_part(
226                ProtocolMessagePartKey::LatestBlockNumber,
227                verified_transactions.latest_certified_block_number().to_string(),
228            );
229            message.set_message_part(
230                ProtocolMessagePartKey::CardanoBlocksTransactionsBlockNumberOffset,
231                verified_transactions.security_parameter().to_string(),
232            );
233            message
234        }
235    }
236
237    /// Compute message for a Cardano stake distribution.
238    pub fn compute_cardano_stake_distribution_message(
239        &self,
240        certificate: &MithrilCertificate,
241        cardano_stake_distribution: &CardanoStakeDistribution,
242    ) -> MithrilResult<ProtocolMessage> {
243        let mk_tree =
244            CardanoStakeDistributionSignableBuilder::compute_merkle_tree_from_stake_distribution(
245                cardano_stake_distribution.stake_distribution.clone(),
246            )?;
247
248        let mut message = certificate.protocol_message.clone();
249        message.set_message_part(
250            ProtocolMessagePartKey::CardanoStakeDistributionEpoch,
251            cardano_stake_distribution.epoch.to_string(),
252        );
253        message.set_message_part(
254            ProtocolMessagePartKey::CardanoStakeDistributionMerkleRoot,
255            mk_tree.compute_root()?.to_hex(),
256        );
257
258        Ok(message)
259    }
260}
261
262impl Default for MessageBuilder {
263    fn default() -> Self {
264        Self::new()
265    }
266}