mithril_common/signable_builder/
signable_builder_service.rs

1use anyhow::Context;
2use async_trait::async_trait;
3use slog::{Logger, debug};
4use std::sync::Arc;
5
6use crate::{
7    StdResult,
8    entities::{
9        BlockNumber, CardanoDbBeacon, Epoch, ProtocolMessage, ProtocolMessagePartKey,
10        SignedEntityType,
11    },
12    logging::LoggerExtensions,
13    signable_builder::{SignableBuilder, SignableSeedBuilder},
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 =
149            self.seed_signable_builder.compute_next_protocol_parameters().await?;
150        protocol_message.set_message_part(
151            ProtocolMessagePartKey::NextProtocolParameters,
152            next_protocol_parameters,
153        );
154        let current_epoch = self.seed_signable_builder.compute_current_epoch().await?;
155        protocol_message.set_message_part(ProtocolMessagePartKey::CurrentEpoch, current_epoch);
156
157        Ok(protocol_message)
158    }
159}
160
161#[async_trait]
162impl SignableBuilderService for MithrilSignableBuilderService {
163    async fn compute_protocol_message(
164        &self,
165        signed_entity_type: SignedEntityType,
166    ) -> StdResult<ProtocolMessage> {
167        let protocol_message = self
168            .compute_signed_entity_protocol_message(signed_entity_type)
169            .await?;
170        let protocol_message = self.compute_seeded_protocol_message(protocol_message).await?;
171
172        Ok(protocol_message)
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    use crate::{
181        StdResult,
182        entities::{BlockNumber, Epoch, ProtocolMessage},
183        signable_builder::{Beacon as Beaconnable, MockSignableSeedBuilder, SignableBuilder},
184        test::TestLogger,
185    };
186
187    use async_trait::async_trait;
188    use mockall::mock;
189
190    mock! {
191        SignableBuilderImpl<U> { }
192
193        #[async_trait]
194        impl<U> SignableBuilder<U> for SignableBuilderImpl<U> where U: Beaconnable,
195        {
196            async fn compute_protocol_message(&self, beacon: U) -> StdResult<ProtocolMessage>;
197        }
198    }
199
200    struct MockDependencyInjector {
201        mock_signable_seed_builder: MockSignableSeedBuilder,
202        mock_mithril_stake_distribution_signable_builder: MockSignableBuilderImpl<Epoch>,
203        mock_cardano_immutable_files_full_signable_builder:
204            MockSignableBuilderImpl<CardanoDbBeacon>,
205        mock_cardano_transactions_signable_builder: MockSignableBuilderImpl<BlockNumber>,
206        mock_cardano_stake_distribution_signable_builder: MockSignableBuilderImpl<Epoch>,
207        mock_cardano_database_signable_builder: MockSignableBuilderImpl<CardanoDbBeacon>,
208    }
209
210    impl MockDependencyInjector {
211        fn new() -> MockDependencyInjector {
212            MockDependencyInjector {
213                mock_signable_seed_builder: MockSignableSeedBuilder::new(),
214                mock_mithril_stake_distribution_signable_builder: MockSignableBuilderImpl::new(),
215                mock_cardano_immutable_files_full_signable_builder: MockSignableBuilderImpl::new(),
216                mock_cardano_stake_distribution_signable_builder: MockSignableBuilderImpl::new(),
217                mock_cardano_transactions_signable_builder: MockSignableBuilderImpl::new(),
218                mock_cardano_database_signable_builder: MockSignableBuilderImpl::new(),
219            }
220        }
221
222        fn build_signable_builder_service(self) -> MithrilSignableBuilderService {
223            let dependencies = SignableBuilderServiceDependencies::new(
224                Arc::new(self.mock_mithril_stake_distribution_signable_builder),
225                Arc::new(self.mock_cardano_immutable_files_full_signable_builder),
226                Arc::new(self.mock_cardano_transactions_signable_builder),
227                Arc::new(self.mock_cardano_stake_distribution_signable_builder),
228                Arc::new(self.mock_cardano_database_signable_builder),
229            );
230
231            MithrilSignableBuilderService::new(
232                Arc::new(self.mock_signable_seed_builder),
233                dependencies,
234                TestLogger::stdout(),
235            )
236        }
237    }
238
239    fn build_mock_container() -> MockDependencyInjector {
240        let mut mock_container = MockDependencyInjector::new();
241        mock_container
242            .mock_signable_seed_builder
243            .expect_compute_next_aggregate_verification_key()
244            .once()
245            .return_once(move || Ok("next-avk-123".to_string()));
246        mock_container
247            .mock_signable_seed_builder
248            .expect_compute_next_protocol_parameters()
249            .once()
250            .return_once(move || Ok("protocol-params-hash-123".to_string()));
251        mock_container
252            .mock_signable_seed_builder
253            .expect_compute_current_epoch()
254            .once()
255            .return_once(move || Ok("epoch-123".to_string()));
256
257        mock_container
258    }
259
260    #[tokio::test]
261    async fn build_mithril_stake_distribution_signable_when_given_mithril_stake_distribution_entity_type()
262     {
263        let mut mock_container = build_mock_container();
264        mock_container
265            .mock_mithril_stake_distribution_signable_builder
266            .expect_compute_protocol_message()
267            .once()
268            .return_once(|_| Ok(ProtocolMessage::new()));
269        let signable_builder_service = mock_container.build_signable_builder_service();
270        let signed_entity_type = SignedEntityType::MithrilStakeDistribution(Epoch(1));
271
272        signable_builder_service
273            .compute_protocol_message(signed_entity_type)
274            .await
275            .unwrap();
276    }
277
278    #[tokio::test]
279    async fn build_snapshot_signable_when_given_cardano_immutable_files_full_entity_type() {
280        let mut mock_container = build_mock_container();
281        mock_container
282            .mock_cardano_immutable_files_full_signable_builder
283            .expect_compute_protocol_message()
284            .once()
285            .return_once(|_| Ok(ProtocolMessage::new()));
286        let signable_builder_service = mock_container.build_signable_builder_service();
287        let signed_entity_type =
288            SignedEntityType::CardanoImmutableFilesFull(CardanoDbBeacon::default());
289
290        signable_builder_service
291            .compute_protocol_message(signed_entity_type)
292            .await
293            .unwrap();
294    }
295
296    #[tokio::test]
297    async fn build_transactions_signable_when_given_cardano_transactions_entity_type() {
298        let mut mock_container = build_mock_container();
299        mock_container
300            .mock_cardano_transactions_signable_builder
301            .expect_compute_protocol_message()
302            .once()
303            .return_once(|_| Ok(ProtocolMessage::new()));
304        let signable_builder_service = mock_container.build_signable_builder_service();
305        let signed_entity_type = SignedEntityType::CardanoTransactions(Epoch(5), BlockNumber(1000));
306
307        signable_builder_service
308            .compute_protocol_message(signed_entity_type)
309            .await
310            .unwrap();
311    }
312
313    #[tokio::test]
314    async fn build_cardano_stake_distribution_signable_when_given_cardano_stake_distribution_entity_type()
315     {
316        let mut mock_container = build_mock_container();
317        mock_container
318            .mock_cardano_stake_distribution_signable_builder
319            .expect_compute_protocol_message()
320            .once()
321            .return_once(|_| Ok(ProtocolMessage::new()));
322        let signable_builder_service = mock_container.build_signable_builder_service();
323        let signed_entity_type = SignedEntityType::CardanoStakeDistribution(Epoch(5));
324
325        signable_builder_service
326            .compute_protocol_message(signed_entity_type)
327            .await
328            .unwrap();
329    }
330
331    #[tokio::test]
332    async fn build_cardano_database_signable_when_given_cardano_database_entity_type() {
333        let mut mock_container = build_mock_container();
334        mock_container
335            .mock_cardano_database_signable_builder
336            .expect_compute_protocol_message()
337            .once()
338            .return_once(|_| Ok(ProtocolMessage::new()));
339        let signable_builder_service = mock_container.build_signable_builder_service();
340        let signed_entity_type = SignedEntityType::CardanoDatabase(CardanoDbBeacon::default());
341
342        signable_builder_service
343            .compute_protocol_message(signed_entity_type)
344            .await
345            .unwrap();
346    }
347}