mithril_common/signable_builder/
cardano_blocks_transactions.rs1use std::sync::Arc;
2
3use anyhow::Context;
4use async_trait::async_trait;
5
6use crate::{
7 StdResult,
8 crypto_helper::{MKMap, MKMapNode, MKTreeNode, MKTreeStorer},
9 entities::{BlockNumber, BlockRange, ProtocolMessage, ProtocolMessagePartKey},
10 signable_builder::SignableBuilder,
11};
12
13#[cfg_attr(test, mockall::automock)]
15#[async_trait]
16pub trait BlocksTransactionsImporter: Send + Sync {
17 async fn import(&self, up_to_beacon: BlockNumber) -> StdResult<()>;
19}
20
21#[cfg_attr(test, mockall::automock)]
23#[async_trait]
24pub trait BlockRangeRootRetriever<S: MKTreeStorer>: Send + Sync {
25 async fn retrieve_block_range_roots<'a>(
27 &'a self,
28 up_to_beacon: BlockNumber,
29 ) -> StdResult<Box<dyn Iterator<Item = (BlockRange, MKTreeNode)> + 'a>>;
30
31 async fn compute_merkle_map_from_block_range_roots(
33 &self,
34 up_to_beacon: BlockNumber,
35 ) -> StdResult<MKMap<BlockRange, MKMapNode<BlockRange, S>, S>> {
36 let block_range_roots_iterator = self
37 .retrieve_block_range_roots(up_to_beacon)
38 .await?
39 .map(|(block_range, root)| (block_range, root.into()));
40 let mk_hash_map = MKMap::new_from_iter(block_range_roots_iterator)
41 .with_context(|| "BlockRangeRootRetriever failed to compute the merkelized structure that proves ownership of the transaction")?;
42
43 Ok(mk_hash_map)
44 }
45}
46
47pub struct CardanoBlocksTransactionsSignableBuilder<S: MKTreeStorer> {
49 blocks_transactions_importer: Arc<dyn BlocksTransactionsImporter>,
50 block_range_root_retriever: Arc<dyn BlockRangeRootRetriever<S>>,
51}
52
53impl<S: MKTreeStorer> CardanoBlocksTransactionsSignableBuilder<S> {
54 pub fn new(
56 blocks_transactions_importer: Arc<dyn BlocksTransactionsImporter>,
57 block_range_root_retriever: Arc<dyn BlockRangeRootRetriever<S>>,
58 ) -> Self {
59 Self {
60 blocks_transactions_importer,
61 block_range_root_retriever,
62 }
63 }
64}
65
66#[async_trait]
67impl<S: MKTreeStorer> SignableBuilder<BlockNumber> for CardanoBlocksTransactionsSignableBuilder<S> {
68 async fn compute_protocol_message(&self, beacon: BlockNumber) -> StdResult<ProtocolMessage> {
69 self.blocks_transactions_importer.import(beacon).await?;
70
71 let mk_root = self
72 .block_range_root_retriever
73 .compute_merkle_map_from_block_range_roots(beacon)
74 .await?
75 .compute_root()?;
76
77 let mut protocol_message = ProtocolMessage::new();
78 protocol_message.set_message_part(
79 ProtocolMessagePartKey::CardanoBlocksTransactionsMerkleRoot,
80 mk_root.to_hex(),
81 );
82 protocol_message.set_message_part(
83 ProtocolMessagePartKey::LatestBlockNumber,
84 beacon.to_string(),
85 );
86
87 Ok(protocol_message)
88 }
89}
90
91#[cfg(test)]
92mod tests {
93
94 use crate::{
95 crypto_helper::MKTreeStoreInMemory, entities::CardanoTransaction,
96 test::builder::CardanoTransactionsBuilder,
97 };
98
99 use super::*;
100
101 fn compute_mk_map_from_transactions(
102 transactions: Vec<CardanoTransaction>,
103 ) -> MKMap<BlockRange, MKMapNode<BlockRange, MKTreeStoreInMemory>, MKTreeStoreInMemory> {
104 MKMap::new_from_iter(transactions.iter().map(|tx| {
105 (
106 BlockRange::from_block_number(tx.block_number),
107 MKMapNode::TreeNode(tx.transaction_hash.clone().into()),
108 )
109 }))
110 .unwrap()
111 }
112
113 #[tokio::test]
114 async fn test_compute_signable() {
115 let block_number = BlockNumber(1453);
117 let transactions = CardanoTransactionsBuilder::new().build_transactions(3);
118 let mk_map = compute_mk_map_from_transactions(transactions.clone());
119 let mut blocks_transactions_importer = MockBlocksTransactionsImporter::new();
120 blocks_transactions_importer
121 .expect_import()
122 .return_once(move |_| Ok(()));
123 let retrieved_transactions = transactions.clone();
124 let mut block_range_root_retriever = MockBlockRangeRootRetriever::new();
125 block_range_root_retriever
126 .expect_compute_merkle_map_from_block_range_roots()
127 .return_once(move |_| Ok(compute_mk_map_from_transactions(retrieved_transactions)));
128
129 let signable_builder = CardanoBlocksTransactionsSignableBuilder::new(
130 Arc::new(blocks_transactions_importer),
131 Arc::new(block_range_root_retriever),
132 );
133
134 let signable = signable_builder.compute_protocol_message(block_number).await.unwrap();
136
137 let mut signable_expected = ProtocolMessage::new();
139 signable_expected.set_message_part(
140 ProtocolMessagePartKey::CardanoBlocksTransactionsMerkleRoot,
141 mk_map.compute_root().unwrap().to_hex(),
142 );
143 signable_expected.set_message_part(
144 ProtocolMessagePartKey::LatestBlockNumber,
145 format!("{block_number}"),
146 );
147 assert_eq!(signable_expected, signable);
148 }
149
150 #[tokio::test]
151 async fn test_compute_signable_with_no_block_range_root_return_error() {
152 let block_number = BlockNumber(50);
153 let mut blocks_transactions_importer = MockBlocksTransactionsImporter::new();
154 blocks_transactions_importer.expect_import().return_once(|_| Ok(()));
155 let mut block_range_root_retriever = MockBlockRangeRootRetriever::new();
156 block_range_root_retriever
157 .expect_compute_merkle_map_from_block_range_roots()
158 .return_once(move |_| Ok(compute_mk_map_from_transactions(vec![])));
159 let signable_builder = CardanoBlocksTransactionsSignableBuilder::new(
160 Arc::new(blocks_transactions_importer),
161 Arc::new(block_range_root_retriever),
162 );
163
164 let result = signable_builder.compute_protocol_message(block_number).await;
165
166 assert!(result.is_err());
167 }
168}