Skip to main content

mithril_common/messages/proof_v2/
cardano_blocks_proof.rs

1use serde::{Deserialize, Serialize};
2
3use crate::entities::{BlockHash, BlockNumber, BlockNumberOffset, CardanoBlock};
4use crate::messages::proof_v2::ProofMessageVerifier;
5use crate::messages::{CardanoBlockMessagePart, MkSetProofMessagePart, VerifyProofsV2Error};
6
7#[cfg(target_family = "wasm")]
8use wasm_bindgen::prelude::*;
9
10/// A cryptographic proof for a set of Cardano blocks
11#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
12#[cfg_attr(
13    target_family = "wasm",
14    wasm_bindgen(getter_with_clone, js_name = "CardanoBlocksProofs")
15)]
16pub struct CardanoBlocksProofsMessage {
17    /// Hash of the certificate that validates this proof Merkle root
18    pub certificate_hash: String,
19
20    /// Blocks that have been certified
21    // Note: Skip in wasm as `wasm_bindgen` doesn't support generics
22    #[cfg_attr(target_family = "wasm", wasm_bindgen(skip))]
23    pub certified_blocks: Option<MkSetProofMessagePart<CardanoBlockMessagePart>>,
24
25    /// Hashes of the blocks that could not be certified
26    pub non_certified_blocks: Vec<String>,
27
28    /// Latest block number that has been certified by the associated Mithril certificate
29    #[cfg_attr(target_family = "wasm", wasm_bindgen(skip))]
30    pub latest_block_number: BlockNumber,
31
32    /// Security parameter that has been certified by the associated Mithril certificate
33    #[cfg_attr(target_family = "wasm", wasm_bindgen(skip))]
34    pub security_parameter: BlockNumberOffset,
35}
36
37#[cfg_attr(target_family = "wasm", wasm_bindgen(js_class = "CardanoBlocksProofs"))]
38impl CardanoBlocksProofsMessage {
39    /// Hashes of the Cardano blocks that have been certified
40    #[cfg_attr(target_family = "wasm", wasm_bindgen(getter))]
41    pub fn blocks_hashes(&self) -> Vec<BlockHash> {
42        self.certified_blocks
43            .as_ref()
44            .map(|cbs| cbs.items.iter().map(|cb| cb.block_hash.clone()).collect())
45            .unwrap_or_default()
46    }
47}
48
49#[cfg(target_family = "wasm")]
50#[wasm_bindgen(js_class = "CardanoBlocksProofs")]
51impl CardanoBlocksProofsMessage {
52    /// Cardano blocks that have been certified
53    #[wasm_bindgen(getter)]
54    pub fn certified_blocks(&self) -> Vec<CardanoBlockMessagePart> {
55        self.certified_blocks
56            .as_ref()
57            .map(|cbs| cbs.items.clone())
58            .unwrap_or_default()
59    }
60
61    /// Latest block number that has been certified by the associated Mithril certificate
62    #[wasm_bindgen(getter)]
63    pub fn latest_block_number(&self) -> u64 {
64        *self.latest_block_number
65    }
66
67    /// Security parameter that has been certified by the associated Mithril certificate
68    #[wasm_bindgen(getter)]
69    pub fn security_parameter(&self) -> u64 {
70        *self.latest_block_number
71    }
72}
73
74/// Blocks successfully verified by [`CardanoBlocksProofsMessage::verify`].
75///
76/// Can be used to reconstruct a [`ProtocolMessage`][crate::entities::ProtocolMessage]
77/// and confirm it was signed by a certificate.
78#[derive(Debug, Clone, PartialEq)]
79pub struct VerifiedCardanoBlocks {
80    certificate_hash: String,
81    merkle_root: String,
82    certified_blocks: Vec<CardanoBlockMessagePart>,
83    latest_block_number: BlockNumber,
84    security_parameter: BlockNumberOffset,
85}
86
87impl VerifiedCardanoBlocks {
88    /// Hash of the certificate that signs this struct Merkle root.
89    pub fn certificate_hash(&self) -> &str {
90        &self.certificate_hash
91    }
92
93    /// Hex encoded Merkle root of the certified blocks
94    pub fn certified_merkle_root(&self) -> &str {
95        &self.merkle_root
96    }
97
98    /// Certified blocks
99    pub fn certified_blocks(&self) -> &[CardanoBlockMessagePart] {
100        &self.certified_blocks
101    }
102
103    /// Hashes of the certified blocks
104    pub fn certified_blocks_hashes(&self) -> Vec<BlockHash> {
105        self.certified_blocks.iter().map(|b| b.block_hash.clone()).collect()
106    }
107
108    /// Latest block number that has been certified by the associated Mithril certificate
109    pub fn latest_certified_block_number(&self) -> BlockNumber {
110        self.latest_block_number
111    }
112
113    /// Security parameter that has been certified by the associated Mithril certificate
114    pub fn security_parameter(&self) -> BlockNumberOffset {
115        self.security_parameter
116    }
117}
118
119impl CardanoBlocksProofsMessage {
120    /// Create a new `ProofsV2CardanoBlocksMessage`
121    pub fn new(
122        certificate_hash: &str,
123        certified_blocks: Option<MkSetProofMessagePart<CardanoBlockMessagePart>>,
124        non_certified_blocks: Vec<String>,
125        latest_block_number: BlockNumber,
126        security_parameter: BlockNumberOffset,
127    ) -> Self {
128        Self {
129            certificate_hash: certificate_hash.to_string(),
130            certified_blocks,
131            non_certified_blocks,
132            latest_block_number,
133            security_parameter,
134        }
135    }
136
137    /// Verify that all the certified blocks proofs are valid
138    ///
139    /// The following checks will be executed:
140    ///
141    /// 1 - Check that each Merkle proof is valid
142    ///
143    /// 2 - Check that all proofs share the same Merkle root
144    ///
145    /// 3 - Assert that there's at least one certified block
146    ///
147    /// If every check is okay, the hex encoded Merkle root of the proof will be returned.
148    pub fn verify(&self) -> Result<VerifiedCardanoBlocks, VerifyProofsV2Error> {
149        const SUBJECT: &str = "Cardano blocks";
150        let certified_blocks = self
151            .certified_blocks
152            .as_ref()
153            .ok_or(VerifyProofsV2Error::NoCertifiedItem(SUBJECT))?;
154        let merkle_root =
155            ProofMessageVerifier::<_, CardanoBlock>::new(SUBJECT, |block| block.block_hash.clone())
156                .verify(certified_blocks)?;
157
158        Ok(VerifiedCardanoBlocks {
159            certificate_hash: self.certificate_hash.clone(),
160            merkle_root,
161            certified_blocks: certified_blocks.items.clone(),
162            latest_block_number: self.latest_block_number,
163            security_parameter: self.security_parameter,
164        })
165    }
166}