mithril_common/entities/
cardano_transactions_set_proof.rs1use crate::crypto_helper::{MKMapProof, ProtocolMkProof};
2use crate::entities::TransactionHash;
3use crate::StdResult;
4
5use super::BlockRange;
6
7cfg_test_tools! {
8 use crate::crypto_helper::{MKMap, MKTree, MKTreeNode, MKMapNode, MKTreeStorer, MKTreeStoreInMemory};
9 use crate::entities::BlockNumber;
10 use std::collections::HashMap;
11}
12
13#[derive(Clone, Debug, PartialEq)]
15pub struct CardanoTransactionsSetProof {
16 pub(crate) transactions_hashes: Vec<TransactionHash>,
18
19 pub(crate) transactions_proof: ProtocolMkProof,
21}
22
23impl CardanoTransactionsSetProof {
24 pub fn new<T: Into<MKMapProof<BlockRange>>>(
26 transactions_hashes: Vec<TransactionHash>,
27 transactions_proof: T,
28 ) -> Self {
29 Self {
30 transactions_hashes,
31 transactions_proof: ProtocolMkProof::new(transactions_proof.into()),
32 }
33 }
34
35 pub fn merkle_root(&self) -> String {
37 self.transactions_proof.compute_root().to_hex()
38 }
39
40 pub fn transactions_hashes(&self) -> &[TransactionHash] {
42 &self.transactions_hashes
43 }
44
45 pub fn verify(&self) -> StdResult<()> {
47 self.transactions_proof.verify()?;
48 for hash in &self.transactions_hashes {
49 self.transactions_proof.contains(&hash.to_owned().into())?;
50 }
51
52 Ok(())
53 }
54
55 cfg_test_tools! {
56 pub fn dummy() -> Self {
58 let leaves = vec![
59 (BlockNumber(0), "tx-1".to_string()),
60 (BlockNumber(1), "tx-2".to_string()),
61 (BlockNumber(1), "tx-3".to_string()),
62 (BlockNumber(10), "tx-4".to_string()),
63 (BlockNumber(20), "tx-5".to_string()),
64 (BlockNumber(22), "tx-6".to_string()),
65 ];
66
67 Self::from_leaves::<MKTreeStoreInMemory>(&leaves).unwrap()
68 }
69
70 pub fn from_leaves<S: MKTreeStorer>(leaves: &[(BlockNumber, TransactionHash)]) -> StdResult<Self> {
72 let transactions_hashes: Vec<TransactionHash> =
73 leaves.iter().map(|(_, t)| t.into()).collect();
74 let mut transactions_by_block_ranges: HashMap<BlockRange, Vec<TransactionHash>> =
75 HashMap::new();
76 for (block_number, transaction_hash) in leaves {
77 let block_range = BlockRange::from_block_number(*block_number);
78 transactions_by_block_ranges
79 .entry(block_range)
80 .or_default()
81 .push(transaction_hash.to_owned());
82 }
83 let mk_map = MKMap::<_, _, MKTreeStoreInMemory>::new(
84 transactions_by_block_ranges
85 .into_iter()
86 .try_fold(
87 vec![],
88 |mut acc, (block_range, transactions)| -> StdResult<Vec<(_, MKMapNode<_,S>)>> {
89 acc.push((block_range, MKTree::<S>::new(&transactions)?.into()));
90 Ok(acc)
91 },
92 )?
93 .as_slice(),
94 )?;
95 let mk_leaves: Vec<MKTreeNode> = transactions_hashes
96 .iter()
97 .map(|h| h.to_owned().into())
98 .collect();
99 let mk_proof = mk_map.compute_proof(&mk_leaves)?;
100 Ok(Self::new(transactions_hashes, mk_proof))
101 }
102
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn should_verify_where_all_hashes_are_contained_in_the_proof() {
112 let leaves = vec![
113 (BlockNumber(0), "tx-1".to_string()),
114 (BlockNumber(1), "tx-2".to_string()),
115 (BlockNumber(1), "tx-3".to_string()),
116 (BlockNumber(10), "tx-4".to_string()),
117 (BlockNumber(20), "tx-5".to_string()),
118 (BlockNumber(22), "tx-6".to_string()),
119 ];
120 let proof =
121 CardanoTransactionsSetProof::from_leaves::<MKTreeStoreInMemory>(&leaves).unwrap();
122
123 proof.verify().expect("The proof should be valid");
124 }
125
126 #[test]
127 fn shouldnt_verify_where_at_least_one_hash_is_not_contained_in_the_proof() {
128 let leaves = vec![
129 (BlockNumber(0), "tx-1".to_string()),
130 (BlockNumber(1), "tx-2".to_string()),
131 (BlockNumber(1), "tx-3".to_string()),
132 (BlockNumber(10), "tx-4".to_string()),
133 (BlockNumber(20), "tx-5".to_string()),
134 (BlockNumber(22), "tx-6".to_string()),
135 ];
136 let proof =
137 CardanoTransactionsSetProof::from_leaves::<MKTreeStoreInMemory>(&leaves).unwrap();
138 let mut transactions_hashes_tampered = proof.transactions_hashes().to_vec();
139 transactions_hashes_tampered.push("tx-123".to_string());
140 let proof = CardanoTransactionsSetProof {
141 transactions_hashes: transactions_hashes_tampered,
142 ..proof
143 };
144
145 proof.verify().expect_err("The proof should be invalid");
146 }
147}