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