mithril_signer/services/cardano_transactions/importer/
importer_with_pruner.rs

1use std::sync::Arc;
2
3use async_trait::async_trait;
4use slog::{debug, Logger};
5
6use mithril_common::entities::BlockNumber;
7use mithril_common::logging::LoggerExtensions;
8use mithril_common::signable_builder::TransactionsImporter;
9use mithril_common::StdResult;
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
59                .prune(number_of_blocks_to_keep)
60                .await?;
61        }
62
63        Ok(())
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use mockall::mock;
70    use mockall::predicate::eq;
71
72    use crate::test_tools::TestLogger;
73
74    use super::*;
75
76    mock! {
77        pub TransactionImporterImpl {}
78
79        #[async_trait]
80        impl TransactionsImporter for TransactionImporterImpl {
81            async fn import(&self, up_to_beacon: BlockNumber) -> StdResult<()>;
82        }
83    }
84
85    impl TransactionsImporterWithPruner {
86        pub(crate) fn new_with_mock<P, I>(
87            number_of_blocks_to_keep: Option<BlockNumber>,
88            transaction_pruner_mock_config: P,
89            importer_mock_config: I,
90        ) -> Self
91        where
92            P: FnOnce(&mut MockTransactionPruner),
93            I: FnOnce(&mut MockTransactionImporterImpl),
94        {
95            let mut transaction_pruner = MockTransactionPruner::new();
96            transaction_pruner_mock_config(&mut transaction_pruner);
97            let mut transaction_importer = MockTransactionImporterImpl::new();
98            importer_mock_config(&mut transaction_importer);
99
100            Self::new(
101                number_of_blocks_to_keep,
102                Arc::new(transaction_pruner),
103                Arc::new(transaction_importer),
104                TestLogger::stdout(),
105            )
106        }
107    }
108
109    #[tokio::test]
110    async fn test_does_not_prune_if_none_is_configured() {
111        let importer = TransactionsImporterWithPruner::new_with_mock(
112            None,
113            |mock| {
114                mock.expect_prune().never();
115            },
116            |mock| {
117                mock.expect_import().once().returning(|_| Ok(()));
118            },
119        );
120
121        importer
122            .import(BlockNumber(100))
123            .await
124            .expect("Import should not fail");
125    }
126
127    #[tokio::test]
128    async fn test_does_prune_if_a_block_number_is_configured() {
129        let expected_block_number = BlockNumber(5);
130        let importer = TransactionsImporterWithPruner::new_with_mock(
131            Some(expected_block_number),
132            |mock| {
133                mock.expect_prune()
134                    .with(eq(expected_block_number))
135                    .once()
136                    .returning(|_| Ok(()));
137            },
138            |mock| {
139                mock.expect_import().once().returning(|_| Ok(()));
140            },
141        );
142
143        importer
144            .import(BlockNumber(100))
145            .await
146            .expect("Import should not fail");
147    }
148}