mithril_common/signable_builder/
cardano_blocks_transactions.rs

1use 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/// Cardano blocks and transactions importer
14#[cfg_attr(test, mockall::automock)]
15#[async_trait]
16pub trait BlocksTransactionsImporter: Send + Sync {
17    /// Import all transactions up to the given beacon into the system
18    async fn import(&self, up_to_beacon: BlockNumber) -> StdResult<()>;
19}
20
21/// Block Range Merkle roots retriever
22#[cfg_attr(test, mockall::automock)]
23#[async_trait]
24pub trait BlockRangeRootRetriever<S: MKTreeStorer>: Send + Sync {
25    /// Returns a Merkle map of the block ranges roots up to a given beacon
26    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    /// Returns a Merkle map of the block ranges roots up to a given beacon
32    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
47/// A [CardanoBlocksTransactionsSignableBuilder] builder
48pub 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    /// Constructor
55    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        // Arrange
116        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        // Action
135        let signable = signable_builder.compute_protocol_message(block_number).await.unwrap();
136
137        // Assert
138        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}