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::CardanoBlocksTransactions(_, _block_number) =>
122                anyhow::bail!("Cardano blocks transactions is not supported yet"),
123            SignedEntityType::CardanoDatabase(beacon) =>
124                self
125                .cardano_database_signable_builder
126                .compute_protocol_message(beacon.clone())
127                .await
128                .with_context(|| format!(
129                    "Signable builder service can not compute protocol message for Cardano database with beacon: '{beacon}'"
130                ))?,
131        };
132
133        Ok(protocol_message)
134    }
135
136    async fn compute_seeded_protocol_message(
137        &self,
138        protocol_message: ProtocolMessage,
139    ) -> StdResult<ProtocolMessage> {
140        let mut protocol_message = protocol_message;
141        let next_aggregate_verification_key = self
142            .seed_signable_builder
143            .compute_next_aggregate_verification_key_for_concatenation()
144            .await?;
145        protocol_message.set_message_part(
146            ProtocolMessagePartKey::NextAggregateVerificationKey,
147            next_aggregate_verification_key,
148        );
149
150        let next_protocol_parameters =
151            self.seed_signable_builder.compute_next_protocol_parameters().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.compute_seeded_protocol_message(protocol_message).await?;
173
174        Ok(protocol_message)
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181
182    use crate::{
183        StdResult,
184        entities::{BlockNumber, Epoch, ProtocolMessage},
185        signable_builder::{Beacon as Beaconnable, MockSignableSeedBuilder, SignableBuilder},
186        test::TestLogger,
187    };
188
189    use async_trait::async_trait;
190    use mockall::mock;
191
192    mock! {
193        SignableBuilderImpl<U> { }
194
195        #[async_trait]
196        impl<U> SignableBuilder<U> for SignableBuilderImpl<U> where U: Beaconnable,
197        {
198            async fn compute_protocol_message(&self, beacon: U) -> StdResult<ProtocolMessage>;
199        }
200    }
201
202    struct MockDependencyInjector {
203        mock_signable_seed_builder: MockSignableSeedBuilder,
204        mock_mithril_stake_distribution_signable_builder: MockSignableBuilderImpl<Epoch>,
205        mock_cardano_immutable_files_full_signable_builder:
206            MockSignableBuilderImpl<CardanoDbBeacon>,
207        mock_cardano_transactions_signable_builder: MockSignableBuilderImpl<BlockNumber>,
208        mock_cardano_stake_distribution_signable_builder: MockSignableBuilderImpl<Epoch>,
209        mock_cardano_database_signable_builder: MockSignableBuilderImpl<CardanoDbBeacon>,
210    }
211
212    impl MockDependencyInjector {
213        fn new() -> MockDependencyInjector {
214            MockDependencyInjector {
215                mock_signable_seed_builder: MockSignableSeedBuilder::new(),
216                mock_mithril_stake_distribution_signable_builder: MockSignableBuilderImpl::new(),
217                mock_cardano_immutable_files_full_signable_builder: MockSignableBuilderImpl::new(),
218                mock_cardano_stake_distribution_signable_builder: MockSignableBuilderImpl::new(),
219                mock_cardano_transactions_signable_builder: MockSignableBuilderImpl::new(),
220                mock_cardano_database_signable_builder: MockSignableBuilderImpl::new(),
221            }
222        }
223
224        fn build_signable_builder_service(self) -> MithrilSignableBuilderService {
225            let dependencies = SignableBuilderServiceDependencies::new(
226                Arc::new(self.mock_mithril_stake_distribution_signable_builder),
227                Arc::new(self.mock_cardano_immutable_files_full_signable_builder),
228                Arc::new(self.mock_cardano_transactions_signable_builder),
229                Arc::new(self.mock_cardano_stake_distribution_signable_builder),
230                Arc::new(self.mock_cardano_database_signable_builder),
231            );
232
233            MithrilSignableBuilderService::new(
234                Arc::new(self.mock_signable_seed_builder),
235                dependencies,
236                TestLogger::stdout(),
237            )
238        }
239    }
240
241    fn build_mock_container() -> MockDependencyInjector {
242        let mut mock_container = MockDependencyInjector::new();
243        mock_container
244            .mock_signable_seed_builder
245            .expect_compute_next_aggregate_verification_key_for_concatenation()
246            .once()
247            .return_once(move || Ok("next-avk-123".to_string()));
248        mock_container
249            .mock_signable_seed_builder
250            .expect_compute_next_protocol_parameters()
251            .once()
252            .return_once(move || Ok("protocol-params-hash-123".to_string()));
253        mock_container
254            .mock_signable_seed_builder
255            .expect_compute_current_epoch()
256            .once()
257            .return_once(move || Ok("epoch-123".to_string()));
258
259        mock_container
260    }
261
262    #[tokio::test]
263    async fn build_mithril_stake_distribution_signable_when_given_mithril_stake_distribution_entity_type()
264     {
265        let mut mock_container = build_mock_container();
266        mock_container
267            .mock_mithril_stake_distribution_signable_builder
268            .expect_compute_protocol_message()
269            .once()
270            .return_once(|_| Ok(ProtocolMessage::new()));
271        let signable_builder_service = mock_container.build_signable_builder_service();
272        let signed_entity_type = SignedEntityType::MithrilStakeDistribution(Epoch(1));
273
274        signable_builder_service
275            .compute_protocol_message(signed_entity_type)
276            .await
277            .unwrap();
278    }
279
280    #[tokio::test]
281    async fn build_snapshot_signable_when_given_cardano_immutable_files_full_entity_type() {
282        let mut mock_container = build_mock_container();
283        mock_container
284            .mock_cardano_immutable_files_full_signable_builder
285            .expect_compute_protocol_message()
286            .once()
287            .return_once(|_| Ok(ProtocolMessage::new()));
288        let signable_builder_service = mock_container.build_signable_builder_service();
289        let signed_entity_type =
290            SignedEntityType::CardanoImmutableFilesFull(CardanoDbBeacon::default());
291
292        signable_builder_service
293            .compute_protocol_message(signed_entity_type)
294            .await
295            .unwrap();
296    }
297
298    #[tokio::test]
299    async fn build_transactions_signable_when_given_cardano_transactions_entity_type() {
300        let mut mock_container = build_mock_container();
301        mock_container
302            .mock_cardano_transactions_signable_builder
303            .expect_compute_protocol_message()
304            .once()
305            .return_once(|_| Ok(ProtocolMessage::new()));
306        let signable_builder_service = mock_container.build_signable_builder_service();
307        let signed_entity_type = SignedEntityType::CardanoTransactions(Epoch(5), BlockNumber(1000));
308
309        signable_builder_service
310            .compute_protocol_message(signed_entity_type)
311            .await
312            .unwrap();
313    }
314
315    #[tokio::test]
316    async fn build_cardano_stake_distribution_signable_when_given_cardano_stake_distribution_entity_type()
317     {
318        let mut mock_container = build_mock_container();
319        mock_container
320            .mock_cardano_stake_distribution_signable_builder
321            .expect_compute_protocol_message()
322            .once()
323            .return_once(|_| Ok(ProtocolMessage::new()));
324        let signable_builder_service = mock_container.build_signable_builder_service();
325        let signed_entity_type = SignedEntityType::CardanoStakeDistribution(Epoch(5));
326
327        signable_builder_service
328            .compute_protocol_message(signed_entity_type)
329            .await
330            .unwrap();
331    }
332
333    #[tokio::test]
334    async fn build_cardano_database_signable_when_given_cardano_database_entity_type() {
335        let mut mock_container = build_mock_container();
336        mock_container
337            .mock_cardano_database_signable_builder
338            .expect_compute_protocol_message()
339            .once()
340            .return_once(|_| Ok(ProtocolMessage::new()));
341        let signable_builder_service = mock_container.build_signable_builder_service();
342        let signed_entity_type = SignedEntityType::CardanoDatabase(CardanoDbBeacon::default());
343
344        signable_builder_service
345            .compute_protocol_message(signed_entity_type)
346            .await
347            .unwrap();
348    }
349}