mithril_common/entities/
cardano_transactions_set_proof.rs

1use crate::StdResult;
2use crate::crypto_helper::{MKMapProof, ProtocolMkProof};
3use crate::entities::TransactionHash;
4
5use super::BlockRange;
6
7/// A cryptographic proof of a set of Cardano transactions is included in the global Cardano transactions set
8#[derive(Clone, Debug, PartialEq)]
9pub struct CardanoTransactionsSetProof {
10    /// Hashes of the certified transactions
11    pub(crate) transactions_hashes: Vec<TransactionHash>,
12
13    /// Proof of the transactions
14    pub(crate) transactions_proof: ProtocolMkProof,
15}
16
17impl CardanoTransactionsSetProof {
18    /// CardanoTransactionsSetProof factory
19    pub fn new<T: Into<MKMapProof<BlockRange>>>(
20        transactions_hashes: Vec<TransactionHash>,
21        transactions_proof: T,
22    ) -> Self {
23        Self {
24            transactions_hashes,
25            transactions_proof: ProtocolMkProof::new(transactions_proof.into()),
26        }
27    }
28
29    /// Return the hex encoded merkle root of this proof
30    pub fn merkle_root(&self) -> String {
31        self.transactions_proof.compute_root().to_hex()
32    }
33
34    /// Get the hashes of the transactions certified by this proof
35    pub fn transactions_hashes(&self) -> &[TransactionHash] {
36        &self.transactions_hashes
37    }
38
39    /// Verify that transactions set proof is valid
40    pub fn verify(&self) -> StdResult<()> {
41        self.transactions_proof.verify()?;
42        for hash in &self.transactions_hashes {
43            self.transactions_proof.contains(&hash.to_owned().into())?;
44        }
45
46        Ok(())
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use crate::crypto_helper::MKTreeStoreInMemory;
53    use crate::entities::BlockNumber;
54    use crate::test::entities_extensions::CardanoTransactionsSetProofTestExtension;
55
56    use super::*;
57
58    #[test]
59    fn should_verify_where_all_hashes_are_contained_in_the_proof() {
60        let leaves = vec![
61            (BlockNumber(0), "tx-1".to_string()),
62            (BlockNumber(1), "tx-2".to_string()),
63            (BlockNumber(1), "tx-3".to_string()),
64            (BlockNumber(10), "tx-4".to_string()),
65            (BlockNumber(20), "tx-5".to_string()),
66            (BlockNumber(22), "tx-6".to_string()),
67        ];
68        let proof =
69            CardanoTransactionsSetProof::from_leaves::<MKTreeStoreInMemory>(&leaves).unwrap();
70
71        proof.verify().expect("The proof should be valid");
72    }
73
74    #[test]
75    fn shouldnt_verify_where_at_least_one_hash_is_not_contained_in_the_proof() {
76        let leaves = vec![
77            (BlockNumber(0), "tx-1".to_string()),
78            (BlockNumber(1), "tx-2".to_string()),
79            (BlockNumber(1), "tx-3".to_string()),
80            (BlockNumber(10), "tx-4".to_string()),
81            (BlockNumber(20), "tx-5".to_string()),
82            (BlockNumber(22), "tx-6".to_string()),
83        ];
84        let proof =
85            CardanoTransactionsSetProof::from_leaves::<MKTreeStoreInMemory>(&leaves).unwrap();
86        let mut transactions_hashes_tampered = proof.transactions_hashes().to_vec();
87        transactions_hashes_tampered.push("tx-123".to_string());
88        let proof = CardanoTransactionsSetProof {
89            transactions_hashes: transactions_hashes_tampered,
90            ..proof
91        };
92
93        proof.verify().expect_err("The proof should be invalid");
94    }
95}