mithril_common/signable_builder/
signable_builder_service.rs

1use anyhow::Context;
2use async_trait::async_trait;
3use slog::{debug, Logger};
4use std::sync::Arc;
5
6use crate::{
7    entities::{
8        BlockNumber, CardanoDbBeacon, Epoch, ProtocolMessage, ProtocolMessagePartKey,
9        SignedEntityType,
10    },
11    logging::LoggerExtensions,
12    signable_builder::{SignableBuilder, SignableSeedBuilder},
13    StdResult,
14};
15
16/// ArtifactBuilder Service trait
17#[cfg_attr(test, mockall::automock)]
18#[async_trait]
19pub trait SignableBuilderService: Send + Sync {
20    /// Compute signable from signed entity type
21    async fn compute_protocol_message(
22        &self,
23        signed_entity_type: SignedEntityType,
24    ) -> StdResult<ProtocolMessage>;
25}
26
27/// Mithril Signable Builder Service
28pub struct MithrilSignableBuilderService {
29    seed_signable_builder: Arc<dyn SignableSeedBuilder>,
30    mithril_stake_distribution_builder: Arc<dyn SignableBuilder<Epoch>>,
31    immutable_signable_builder: Arc<dyn SignableBuilder<CardanoDbBeacon>>,
32    cardano_transactions_signable_builder: Arc<dyn SignableBuilder<BlockNumber>>,
33    cardano_stake_distribution_builder: Arc<dyn SignableBuilder<Epoch>>,
34    cardano_database_signable_builder: Arc<dyn SignableBuilder<CardanoDbBeacon>>,
35    logger: Logger,
36}
37
38/// SignableBuilders dependencies required by the [MithrilSignableBuilderService].
39pub struct SignableBuilderServiceDependencies {
40    mithril_stake_distribution_builder: Arc<dyn SignableBuilder<Epoch>>,
41    immutable_signable_builder: Arc<dyn SignableBuilder<CardanoDbBeacon>>,
42    cardano_transactions_signable_builder: Arc<dyn SignableBuilder<BlockNumber>>,
43    cardano_stake_distribution_builder: Arc<dyn SignableBuilder<Epoch>>,
44    cardano_database_signable_builder: Arc<dyn SignableBuilder<CardanoDbBeacon>>,
45}
46
47impl SignableBuilderServiceDependencies {
48    /// Create a new instance of [SignableBuilderServiceDependencies].
49    pub fn new(
50        mithril_stake_distribution_builder: Arc<dyn SignableBuilder<Epoch>>,
51        immutable_signable_builder: Arc<dyn SignableBuilder<CardanoDbBeacon>>,
52        cardano_transactions_signable_builder: Arc<dyn SignableBuilder<BlockNumber>>,
53        cardano_stake_distribution_builder: Arc<dyn SignableBuilder<Epoch>>,
54        cardano_database_signable_builder: Arc<dyn SignableBuilder<CardanoDbBeacon>>,
55    ) -> Self {
56        Self {
57            mithril_stake_distribution_builder,
58            immutable_signable_builder,
59            cardano_transactions_signable_builder,
60            cardano_stake_distribution_builder,
61            cardano_database_signable_builder,
62        }
63    }
64}
65
66impl MithrilSignableBuilderService {
67    /// MithrilSignableBuilderService factory
68    pub fn new(
69        seed_signable_builder: Arc<dyn SignableSeedBuilder>,
70        dependencies: SignableBuilderServiceDependencies,
71        logger: Logger,
72    ) -> Self {
73        Self {
74            seed_signable_builder,
75            mithril_stake_distribution_builder: dependencies.mithril_stake_distribution_builder,
76            immutable_signable_builder: dependencies.immutable_signable_builder,
77            cardano_transactions_signable_builder: dependencies
78                .cardano_transactions_signable_builder,
79            cardano_stake_distribution_builder: dependencies.cardano_stake_distribution_builder,
80            cardano_database_signable_builder: dependencies.cardano_database_signable_builder,
81            logger: logger.new_with_component_name::<Self>(),
82        }
83    }
84
85    async fn compute_signed_entity_protocol_message(
86        &self,
87        signed_entity_type: SignedEntityType,
88    ) -> StdResult<ProtocolMessage> {
89        debug!(
90            self.logger,
91            "Compute protocol message for signed entity type: '{signed_entity_type:?}'"
92        );
93
94        let protocol_message = match signed_entity_type {
95            SignedEntityType::MithrilStakeDistribution(e) => self
96                .mithril_stake_distribution_builder
97                .compute_protocol_message(e)
98                .await
99                .with_context(|| format!(
100                    "Signable builder service can not compute protocol message with epoch: '{e}'"
101                ))?,
102            SignedEntityType::CardanoImmutableFilesFull(beacon) => self
103                .immutable_signable_builder
104                .compute_protocol_message(beacon.clone())
105                .await
106                .with_context(|| format!(
107                    "Signable builder service can not compute protocol message with beacon: '{beacon}'"
108                ))?,
109            SignedEntityType::CardanoStakeDistribution(e) => self
110                .cardano_stake_distribution_builder
111                .compute_protocol_message(e)
112                .await
113                .with_context(|| "Signable builder service can not compute protocol message for Cardano stake distribution with epoch: '{e}")?,
114            SignedEntityType::CardanoTransactions(_, block_number) => self
115                .cardano_transactions_signable_builder
116                .compute_protocol_message(block_number)
117                .await
118                .with_context(|| format!(
119                    "Signable builder service can not compute protocol message with block_number: '{block_number}'"
120                ))?,
121            SignedEntityType::CardanoDatabase(beacon) =>
122                self
123                .cardano_database_signable_builder
124                .compute_protocol_message(beacon.clone())
125                .await
126                .with_context(|| format!(
127                    "Signable builder service can not compute protocol message for Cardano database with beacon: '{beacon}'"
128                ))?,
129        };
130
131        Ok(protocol_message)
132    }
133
134    async fn compute_seeded_protocol_message(
135        &self,
136        protocol_message: ProtocolMessage,
137    ) -> StdResult<ProtocolMessage> {
138        let mut protocol_message = protocol_message;
139        let next_aggregate_verification_key = self
140            .seed_signable_builder
141            .compute_next_aggregate_verification_key()
142            .await?;
143        protocol_message.set_message_part(
144            ProtocolMessagePartKey::NextAggregateVerificationKey,
145            next_aggregate_verification_key,
146        );
147
148        let next_protocol_parameters = self
149            .seed_signable_builder
150            .compute_next_protocol_parameters()
151            .await?;
152        protocol_message.set_message_part(
153            ProtocolMessagePartKey::NextProtocolParameters,
154            next_protocol_parameters,
155        );
156        let current_epoch = self.seed_signable_builder.compute_current_epoch().await?;
157        protocol_message.set_message_part(ProtocolMessagePartKey::CurrentEpoch, current_epoch);
158
159        Ok(protocol_message)
160    }
161}
162
163#[async_trait]
164impl SignableBuilderService for MithrilSignableBuilderService {
165    async fn compute_protocol_message(
166        &self,
167        signed_entity_type: SignedEntityType,
168    ) -> StdResult<ProtocolMessage> {
169        let protocol_message = self
170            .compute_signed_entity_protocol_message(signed_entity_type)
171            .await?;
172        let protocol_message = self
173            .compute_seeded_protocol_message(protocol_message)
174            .await?;
175
176        Ok(protocol_message)
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    use crate::{
185        entities::{BlockNumber, Epoch, ProtocolMessage},
186        signable_builder::{Beacon as Beaconnable, MockSignableSeedBuilder, SignableBuilder},
187        test_utils::TestLogger,
188        StdResult,
189    };
190
191    use async_trait::async_trait;
192    use mockall::mock;
193
194    mock! {
195        SignableBuilderImpl<U> { }
196
197        #[async_trait]
198        impl<U> SignableBuilder<U> for SignableBuilderImpl<U> where U: Beaconnable,
199        {
200            async fn compute_protocol_message(&self, beacon: U) -> StdResult<ProtocolMessage>;
201        }
202    }
203
204    struct MockDependencyInjector {
205        mock_signable_seed_builder: MockSignableSeedBuilder,
206        mock_mithril_stake_distribution_signable_builder: MockSignableBuilderImpl<Epoch>,
207        mock_cardano_immutable_files_full_signable_builder:
208            MockSignableBuilderImpl<CardanoDbBeacon>,
209        mock_cardano_transactions_signable_builder: MockSignableBuilderImpl<BlockNumber>,
210        mock_cardano_stake_distribution_signable_builder: MockSignableBuilderImpl<Epoch>,
211        mock_cardano_database_signable_builder: MockSignableBuilderImpl<CardanoDbBeacon>,
212    }
213
214    impl MockDependencyInjector {
215        fn new() -> MockDependencyInjector {
216            MockDependencyInjector {
217                mock_signable_seed_builder: MockSignableSeedBuilder::new(),
218                mock_mithril_stake_distribution_signable_builder: MockSignableBuilderImpl::new(),
219                mock_cardano_immutable_files_full_signable_builder: MockSignableBuilderImpl::new(),
220                mock_cardano_stake_distribution_signable_builder: MockSignableBuilderImpl::new(),
221                mock_cardano_transactions_signable_builder: MockSignableBuilderImpl::new(),
222                mock_cardano_database_signable_builder: MockSignableBuilderImpl::new(),
223            }
224        }
225
226        fn build_signable_builder_service(self) -> MithrilSignableBuilderService {
227            let dependencies = SignableBuilderServiceDependencies::new(
228                Arc::new(self.mock_mithril_stake_distribution_signable_builder),
229                Arc::new(self.mock_cardano_immutable_files_full_signable_builder),
230                Arc::new(self.mock_cardano_transactions_signable_builder),
231                Arc::new(self.mock_cardano_stake_distribution_signable_builder),
232                Arc::new(self.mock_cardano_database_signable_builder),
233            );
234
235            MithrilSignableBuilderService::new(
236                Arc::new(self.mock_signable_seed_builder),
237                dependencies,
238                TestLogger::stdout(),
239            )
240        }
241    }
242
243    fn build_mock_container() -> MockDependencyInjector {
244        let mut mock_container = MockDependencyInjector::new();
245        mock_container
246            .mock_signable_seed_builder
247            .expect_compute_next_aggregate_verification_key()
248            .once()
249            .return_once(move || Ok("next-avk-123".to_string()));
250        mock_container
251            .mock_signable_seed_builder
252            .expect_compute_next_protocol_parameters()
253            .once()
254            .return_once(move || Ok("protocol-params-hash-123".to_string()));
255        mock_container
256            .mock_signable_seed_builder
257            .expect_compute_current_epoch()
258            .once()
259            .return_once(move || Ok("epoch-123".to_string()));
260
261        mock_container
262    }
263
264    #[tokio::test]
265    async fn build_mithril_stake_distribution_signable_when_given_mithril_stake_distribution_entity_type(
266    ) {
267        let mut mock_container = build_mock_container();
268        mock_container
269            .mock_mithril_stake_distribution_signable_builder
270            .expect_compute_protocol_message()
271            .once()
272            .return_once(|_| Ok(ProtocolMessage::new()));
273        let signable_builder_service = mock_container.build_signable_builder_service();
274        let signed_entity_type = SignedEntityType::MithrilStakeDistribution(Epoch(1));
275
276        signable_builder_service
277            .compute_protocol_message(signed_entity_type)
278            .await
279            .unwrap();
280    }
281
282    #[tokio::test]
283    async fn build_snapshot_signable_when_given_cardano_immutable_files_full_entity_type() {
284        let mut mock_container = build_mock_container();
285        mock_container
286            .mock_cardano_immutable_files_full_signable_builder
287            .expect_compute_protocol_message()
288            .once()
289            .return_once(|_| Ok(ProtocolMessage::new()));
290        let signable_builder_service = mock_container.build_signable_builder_service();
291        let signed_entity_type =
292            SignedEntityType::CardanoImmutableFilesFull(CardanoDbBeacon::default());
293
294        signable_builder_service
295            .compute_protocol_message(signed_entity_type)
296            .await
297            .unwrap();
298    }
299
300    #[tokio::test]
301    async fn build_transactions_signable_when_given_cardano_transactions_entity_type() {
302        let mut mock_container = build_mock_container();
303        mock_container
304            .mock_cardano_transactions_signable_builder
305            .expect_compute_protocol_message()
306            .once()
307            .return_once(|_| Ok(ProtocolMessage::new()));
308        let signable_builder_service = mock_container.build_signable_builder_service();
309        let signed_entity_type = SignedEntityType::CardanoTransactions(Epoch(5), BlockNumber(1000));
310
311        signable_builder_service
312            .compute_protocol_message(signed_entity_type)
313            .await
314            .unwrap();
315    }
316
317    #[tokio::test]
318    async fn build_cardano_stake_distribution_signable_when_given_cardano_stake_distribution_entity_type(
319    ) {
320        let mut mock_container = build_mock_container();
321        mock_container
322            .mock_cardano_stake_distribution_signable_builder
323            .expect_compute_protocol_message()
324            .once()
325            .return_once(|_| Ok(ProtocolMessage::new()));
326        let signable_builder_service = mock_container.build_signable_builder_service();
327        let signed_entity_type = SignedEntityType::CardanoStakeDistribution(Epoch(5));
328
329        signable_builder_service
330            .compute_protocol_message(signed_entity_type)
331            .await
332            .unwrap();
333    }
334
335    #[tokio::test]
336    async fn build_cardano_database_signable_when_given_cardano_database_entity_type() {
337        let mut mock_container = build_mock_container();
338        mock_container
339            .mock_cardano_database_signable_builder
340            .expect_compute_protocol_message()
341            .once()
342            .return_once(|_| Ok(ProtocolMessage::new()));
343        let signable_builder_service = mock_container.build_signable_builder_service();
344        let signed_entity_type = SignedEntityType::CardanoDatabase(CardanoDbBeacon::default());
345
346        signable_builder_service
347            .compute_protocol_message(signed_entity_type)
348            .await
349            .unwrap();
350    }
351}