mithril_common/signable_builder/
cardano_transactions.rs

1use 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/// Cardano transactions importer
17#[cfg_attr(test, automock)]
18#[async_trait]
19pub trait TransactionsImporter: Send + Sync {
20    /// Import all transactions up to the given beacon into the system
21    async fn import(&self, up_to_beacon: BlockNumber) -> StdResult<()>;
22}
23
24/// Block Range Merkle roots retriever
25#[cfg_attr(test, automock)]
26#[async_trait]
27pub trait BlockRangeRootRetriever<S: MKTreeStorer>: Send + Sync {
28    /// Returns a Merkle map of the block ranges roots up to a given beacon
29    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    /// Returns a Merkle map of the block ranges roots up to a given beacon
35    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
50/// A [CardanoTransactionsSignableBuilder] builder
51pub 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    /// Constructor
58    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        // Arrange
119        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        // Action
138        let signable = cardano_transactions_signable_builder
139            .compute_protocol_message(block_number)
140            .await
141            .unwrap();
142
143        // Assert
144        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}