1use anyhow::Context;
2use std::collections::BTreeSet;
3
4use crate::StdResult;
5use crate::crypto_helper::{
6 MKMap, MKMapNode, MKProof, MKTree, MKTreeNode, MKTreeStoreInMemory, MKTreeStorer,
7};
8use crate::entities::{
9 BlockNumber, BlockNumberOffset, BlockRange, CardanoBlock, CardanoBlockWithTransactions,
10 CardanoTransaction, IntoMKTreeNode, MkSetProof,
11};
12use crate::messages::{CardanoBlocksProofsMessage, CardanoTransactionsProofsV2Message};
13use crate::test::crypto_helper::mkmap_helpers;
14use crate::test::entities_extensions::BlockNumberTestExtension;
15
16pub trait MKTreeTestExtension {
18 fn compute_root_from_iter<T: IntoIterator<Item = U>, U: Into<MKTreeNode>>(
22 leaves: T,
23 ) -> StdResult<MKTreeNode>;
24}
25
26impl<S: MKTreeStorer> MKTreeTestExtension for MKTree<S> {
27 fn compute_root_from_iter<T: IntoIterator<Item = U>, U: Into<MKTreeNode>>(
28 leaves: T,
29 ) -> StdResult<MKTreeNode> {
30 let mk_tree = Self::new_from_iter(leaves)?;
31 mk_tree.compute_root()
32 }
33}
34
35pub trait MKProofTestExtension {
37 fn from_leaves<T: Into<MKTreeNode> + Clone>(leaves: &[T]) -> StdResult<MKProof>;
39
40 fn from_subset_of_leaves<T: Into<MKTreeNode> + Clone>(
42 leaves: &[T],
43 leaves_to_verify: &[T],
44 ) -> StdResult<MKProof>;
45}
46
47impl MKProofTestExtension for MKProof {
48 fn from_leaves<T: Into<MKTreeNode> + Clone>(leaves: &[T]) -> StdResult<MKProof> {
49 Self::from_subset_of_leaves(leaves, leaves)
50 }
51
52 fn from_subset_of_leaves<T: Into<MKTreeNode> + Clone>(
53 leaves: &[T],
54 leaves_to_verify: &[T],
55 ) -> StdResult<MKProof> {
56 fn list_to_mknode<T: Into<MKTreeNode> + Clone>(hashes: &[T]) -> Vec<MKTreeNode> {
57 hashes.iter().map(|h| h.clone().into()).collect()
58 }
59
60 let leaves = list_to_mknode(leaves);
61 let leaves_to_verify = list_to_mknode(leaves_to_verify);
62
63 let mktree = MKTree::<MKTreeStoreInMemory>::new(&leaves)
64 .with_context(|| "MKTree creation should not fail")?;
65 mktree.compute_proof(&leaves_to_verify)
66 }
67}
68
69pub trait MKMapTestExtension<K, V, S: MKTreeStorer> {
71 fn compute_root_from_iter<T: IntoIterator<Item = (K, V)>>(entries: T) -> StdResult<MKTreeNode>;
75
76 fn from_blocks_with_transactions(
78 leaves: &[CardanoBlockWithTransactions],
79 ) -> StdResult<MKMap<BlockRange, MKMapNode<BlockRange, S>, S>>;
80
81 fn compute_proof_for_transactions_hashes<H: AsRef<str>>(
85 &self,
86 transactions_hashes_to_prove: &[H],
87 all_leaves: &[CardanoBlockWithTransactions],
88 ) -> StdResult<MkSetProof<CardanoTransaction>>;
89
90 fn compute_proof_message_for_transactions_hashes<H: AsRef<str>>(
94 &self,
95 certificate_hash: &str,
96 transactions_hashes_to_prove: &[H],
97 all_leaves: &[CardanoBlockWithTransactions],
98 ) -> StdResult<CardanoTransactionsProofsV2Message> {
99 let proof =
100 self.compute_proof_for_transactions_hashes(transactions_hashes_to_prove, all_leaves)?;
101
102 let message = CardanoTransactionsProofsV2Message::new(
103 certificate_hash,
104 Some(proof.try_into()?),
105 Vec::new(),
106 BlockNumber(9999),
107 BlockNumberOffset(15),
108 );
109
110 Ok(message)
111 }
112
113 fn compute_proof_for_blocks_hashes<H: AsRef<str>>(
117 &self,
118 blocks_hashes_to_prove: &[H],
119 all_leaves: &[CardanoBlockWithTransactions],
120 ) -> StdResult<MkSetProof<CardanoBlock>>;
121
122 fn compute_proof_message_for_blocks_hashes<H: AsRef<str>>(
126 &self,
127 certificate_hash: &str,
128 blocks_hashes_to_prove: &[H],
129 all_leaves: &[CardanoBlockWithTransactions],
130 ) -> StdResult<CardanoBlocksProofsMessage> {
131 let proof = self.compute_proof_for_blocks_hashes(blocks_hashes_to_prove, all_leaves)?;
132
133 let message = CardanoBlocksProofsMessage::new(
134 certificate_hash,
135 Some(proof.try_into()?),
136 Vec::new(),
137 BlockNumber(9999),
138 BlockNumberOffset(15),
139 );
140
141 Ok(message)
142 }
143}
144
145impl<S: MKTreeStorer> MKMapTestExtension<BlockRange, MKMapNode<BlockRange, S>, S>
146 for MKMap<BlockRange, MKMapNode<BlockRange, S>, S>
147{
148 fn compute_root_from_iter<T: IntoIterator<Item = (BlockRange, MKMapNode<BlockRange, S>)>>(
149 entries: T,
150 ) -> StdResult<MKTreeNode> {
151 let mk_map = Self::new_from_iter(entries)?;
152 mk_map.compute_root()
153 }
154
155 fn from_blocks_with_transactions(
156 leaves: &[CardanoBlockWithTransactions],
157 ) -> StdResult<MKMap<BlockRange, MKMapNode<BlockRange, S>, S>> {
158 let ordered_leaves: BTreeSet<_> =
159 leaves.iter().flat_map(|l| l.clone().into_mk_tree_node()).collect();
160 let node_per_block_range = BlockNumber::group_items_by_block_range(
161 ordered_leaves.into_iter().map(|n| (n.block_number(), n)),
162 );
163
164 mkmap_helpers::fold_nodes_per_block_range_into_mkmap::<_, _, S>(node_per_block_range)
165 }
166
167 fn compute_proof_for_transactions_hashes<H: AsRef<str>>(
168 &self,
169 transactions_hashes_to_prove: &[H],
170 all_leaves: &[CardanoBlockWithTransactions],
171 ) -> StdResult<MkSetProof<CardanoTransaction>> {
172 let hashes_to_prove: Vec<_> =
173 transactions_hashes_to_prove.iter().map(AsRef::as_ref).collect();
174 let leaves_to_prove: Vec<CardanoTransaction> = all_leaves
175 .iter()
176 .flat_map(|l| l.clone().into_transactions())
177 .filter(|tx| hashes_to_prove.contains(&tx.transaction_hash.as_str()))
178 .collect();
179 let mk_tree_nodes_to_prove: Vec<MKTreeNode> = leaves_to_prove
180 .iter()
181 .cloned()
182 .map(|l| l.into_mk_tree_node())
183 .collect();
184
185 let proof = self.compute_proof(&mk_tree_nodes_to_prove)?;
186 Ok(MkSetProof::new(leaves_to_prove, proof))
187 }
188
189 fn compute_proof_for_blocks_hashes<H: AsRef<str>>(
190 &self,
191 blocks_hashes_to_prove: &[H],
192 all_leaves: &[CardanoBlockWithTransactions],
193 ) -> StdResult<MkSetProof<CardanoBlock>> {
194 let hashes_to_prove: Vec<_> = blocks_hashes_to_prove.iter().map(AsRef::as_ref).collect();
195 let leaves_to_prove: Vec<CardanoBlock> = all_leaves
196 .iter()
197 .filter(|b| hashes_to_prove.contains(&b.block_hash.as_ref()))
198 .cloned()
199 .map(Into::into)
200 .collect();
201 let mk_tree_nodes_to_prove: Vec<MKTreeNode> = leaves_to_prove
202 .iter()
203 .cloned()
204 .map(|l| l.into_mk_tree_node())
205 .collect();
206
207 let proof = self.compute_proof(&mk_tree_nodes_to_prove)?;
208 Ok(MkSetProof::new(leaves_to_prove, proof))
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use crate::entities::SlotNumber;
215
216 use super::*;
217
218 #[test]
219 fn mk_map_from_blocks_with_txs_order_the_leaves() {
220 let ordered_blocks_with_txs = [
221 CardanoBlockWithTransactions::new(
222 "block_hash-10",
223 BlockNumber(10),
224 SlotNumber(100),
225 vec!["tx_hash-1", "tx_hash-2"],
226 ),
227 CardanoBlockWithTransactions::new(
228 "block_hash-15",
229 BlockNumber(15),
230 SlotNumber(150),
231 vec!["tx_hash-4"],
232 ),
233 CardanoBlockWithTransactions::new(
234 "block_hash-16",
235 BlockNumber(16),
236 SlotNumber(160),
237 vec!["tx_hash-5", "tx_hash-6", "tx_hash-7"],
238 ),
239 ];
240 let unordered_blocks = [
241 CardanoBlockWithTransactions::new(
242 "block_hash-16",
243 BlockNumber(16),
244 SlotNumber(160),
245 vec!["tx_hash-5", "tx_hash-6", "tx_hash-7"],
246 ),
247 CardanoBlockWithTransactions::new(
248 "block_hash-10",
249 BlockNumber(10),
250 SlotNumber(100),
251 vec!["tx_hash-1", "tx_hash-2"],
252 ),
253 CardanoBlockWithTransactions::new(
254 "block_hash-15",
255 BlockNumber(15),
256 SlotNumber(150),
257 vec!["tx_hash-4"],
258 ),
259 ];
260 let unordered_txs = [
261 CardanoBlockWithTransactions::new(
262 "block_hash-10",
263 BlockNumber(10),
264 SlotNumber(100),
265 vec!["tx_hash-2", "tx_hash-1"],
266 ),
267 CardanoBlockWithTransactions::new(
268 "block_hash-15",
269 BlockNumber(15),
270 SlotNumber(150),
271 vec!["tx_hash-4"],
272 ),
273 CardanoBlockWithTransactions::new(
274 "block_hash-16",
275 BlockNumber(16),
276 SlotNumber(160),
277 vec!["tx_hash-6", "tx_hash-7", "tx_hash-5"],
278 ),
279 ];
280
281 let ordered_blocks_with_txs_mk_map_root =
282 MKMap::<_, _, MKTreeStoreInMemory>::from_blocks_with_transactions(
283 &ordered_blocks_with_txs,
284 )
285 .unwrap()
286 .compute_root()
287 .unwrap();
288 let unordered_blocks_mk_map_root =
289 MKMap::<_, _, MKTreeStoreInMemory>::from_blocks_with_transactions(&unordered_blocks)
290 .unwrap()
291 .compute_root()
292 .unwrap();
293 let unordered_txs_mk_map_root =
294 MKMap::<_, _, MKTreeStoreInMemory>::from_blocks_with_transactions(&unordered_txs)
295 .unwrap()
296 .compute_root()
297 .unwrap();
298
299 assert_eq!(
300 ordered_blocks_with_txs_mk_map_root,
301 unordered_blocks_mk_map_root
302 );
303 assert_eq!(
304 ordered_blocks_with_txs_mk_map_root,
305 unordered_txs_mk_map_root
306 );
307 }
308
309 #[test]
310 fn compute_proofs_for_blocks_and_txs_from_the_same_mkmap() {
311 let blocks_with_txs = [
312 CardanoBlockWithTransactions::new(
313 "block_hash-10",
314 BlockNumber(10),
315 SlotNumber(100),
316 vec!["tx_hash-1", "tx_hash-2"],
317 ),
318 CardanoBlockWithTransactions::new(
319 "block_hash-15",
320 BlockNumber(15),
321 SlotNumber(150),
322 vec!["tx_hash-4"],
323 ),
324 CardanoBlockWithTransactions::new(
325 "block_hash-16",
326 BlockNumber(16),
327 SlotNumber(160),
328 vec!["tx_hash-5", "tx_hash-6", "tx_hash-7"],
329 ),
330 ];
331
332 let mk_map =
333 MKMap::<_, _, MKTreeStoreInMemory>::from_blocks_with_transactions(&blocks_with_txs)
334 .unwrap();
335
336 let proof_for_blocks_subset = mk_map
337 .compute_proof_for_blocks_hashes(&["block_hash-10", "block_hash-16"], &blocks_with_txs)
338 .unwrap();
339 proof_for_blocks_subset.verify().unwrap();
340
341 let proof_for_txs_subset = mk_map
342 .compute_proof_for_transactions_hashes(&["tx_hash-1", "tx_hash-4"], &blocks_with_txs)
343 .unwrap();
344 proof_for_txs_subset.verify().unwrap();
345
346 assert_eq!(
347 proof_for_blocks_subset.merkle_root(),
348 proof_for_txs_subset.merkle_root()
349 );
350 }
351}