mithril_signer/services/cardano_transactions/importer/
importer_with_pruner.rs

1use std::sync::Arc;
2
3use async_trait::async_trait;
4use slog::{Logger, debug};
5
6use mithril_common::StdResult;
7use mithril_common::entities::BlockNumber;
8use mithril_common::logging::LoggerExtensions;
9use mithril_common::signable_builder::TransactionsImporter;
10
11/// Cardano transactions pruner
12#[cfg_attr(test, mockall::automock)]
13#[async_trait]
14pub trait TransactionPruner: Send + Sync {
15    /// Prune the transactions older than the given number of blocks.
16    async fn prune(&self, number_of_blocks_to_keep: BlockNumber) -> StdResult<()>;
17}
18
19/// A decorator of [TransactionsImporter] that prunes the transactions older than a given number of
20/// blocks after running the import.
21///
22/// If the number of blocks to keep is not provided, no pruning is performed.
23pub struct TransactionsImporterWithPruner {
24    number_of_blocks_to_keep: Option<BlockNumber>,
25    transaction_pruner: Arc<dyn TransactionPruner>,
26    wrapped_importer: Arc<dyn TransactionsImporter>,
27    logger: Logger,
28}
29
30impl TransactionsImporterWithPruner {
31    /// Create a new instance of [TransactionsImporterWithPruner].
32    pub fn new(
33        number_of_blocks_to_keep: Option<BlockNumber>,
34        transaction_pruner: Arc<dyn TransactionPruner>,
35        wrapped_importer: Arc<dyn TransactionsImporter>,
36        logger: Logger,
37    ) -> Self {
38        Self {
39            number_of_blocks_to_keep,
40            transaction_pruner,
41            wrapped_importer,
42            logger: logger.new_with_component_name::<Self>(),
43        }
44    }
45}
46
47#[async_trait]
48impl TransactionsImporter for TransactionsImporterWithPruner {
49    async fn import(&self, up_to_beacon: BlockNumber) -> StdResult<()> {
50        self.wrapped_importer.import(up_to_beacon).await?;
51
52        if let Some(number_of_blocks_to_keep) = self.number_of_blocks_to_keep {
53            debug!(
54                self.logger,
55                "Transaction Import finished - Pruning transactions included in block range roots";
56                "number_of_blocks_to_keep" => *number_of_blocks_to_keep,
57            );
58            self.transaction_pruner.prune(number_of_blocks_to_keep).await?;
59        }
60
61        Ok(())
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use mockall::mock;
68    use mockall::predicate::eq;
69
70    use crate::test_tools::TestLogger;
71
72    use super::*;
73
74    mock! {
75        pub TransactionImporterImpl {}
76
77        #[async_trait]
78        impl TransactionsImporter for TransactionImporterImpl {
79            async fn import(&self, up_to_beacon: BlockNumber) -> StdResult<()>;
80        }
81    }
82
83    impl TransactionsImporterWithPruner {
84        pub(crate) fn new_with_mock<P, I>(
85            number_of_blocks_to_keep: Option<BlockNumber>,
86            transaction_pruner_mock_config: P,
87            importer_mock_config: I,
88        ) -> Self
89        where
90            P: FnOnce(&mut MockTransactionPruner),
91            I: FnOnce(&mut MockTransactionImporterImpl),
92        {
93            let mut transaction_pruner = MockTransactionPruner::new();
94            transaction_pruner_mock_config(&mut transaction_pruner);
95            let mut transaction_importer = MockTransactionImporterImpl::new();
96            importer_mock_config(&mut transaction_importer);
97
98            Self::new(
99                number_of_blocks_to_keep,
100                Arc::new(transaction_pruner),
101                Arc::new(transaction_importer),
102                TestLogger::stdout(),
103            )
104        }
105    }
106
107    #[tokio::test]
108    async fn test_does_not_prune_if_none_is_configured() {
109        let importer = TransactionsImporterWithPruner::new_with_mock(
110            None,
111            |mock| {
112                mock.expect_prune().never();
113            },
114            |mock| {
115                mock.expect_import().once().returning(|_| Ok(()));
116            },
117        );
118
119        importer
120            .import(BlockNumber(100))
121            .await
122            .expect("Import should not fail");
123    }
124
125    #[tokio::test]
126    async fn test_does_prune_if_a_block_number_is_configured() {
127        let expected_block_number = BlockNumber(5);
128        let importer = TransactionsImporterWithPruner::new_with_mock(
129            Some(expected_block_number),
130            |mock| {
131                mock.expect_prune()
132                    .with(eq(expected_block_number))
133                    .once()
134                    .returning(|_| Ok(()));
135            },
136            |mock| {
137                mock.expect_import().once().returning(|_| Ok(()));
138            },
139        );
140
141        importer
142            .import(BlockNumber(100))
143            .await
144            .expect("Import should not fail");
145    }
146}