Skip to main content

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, BlockNumberOffset, CardanoDbBeacon, Epoch, ProtocolMessage,
10        ProtocolMessagePartKey, 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_blocks_transactions_signable_builder:
34        Arc<dyn SignableBuilder<(BlockNumber, BlockNumberOffset)>>,
35    cardano_stake_distribution_builder: Arc<dyn SignableBuilder<Epoch>>,
36    cardano_database_signable_builder: Arc<dyn SignableBuilder<CardanoDbBeacon>>,
37    logger: Logger,
38}
39
40/// SignableBuilders dependencies required by the [MithrilSignableBuilderService].
41pub struct SignableBuilderServiceDependencies {
42    mithril_stake_distribution_builder: Arc<dyn SignableBuilder<Epoch>>,
43    immutable_signable_builder: Arc<dyn SignableBuilder<CardanoDbBeacon>>,
44    cardano_transactions_signable_builder: Arc<dyn SignableBuilder<BlockNumber>>,
45    cardano_blocks_transactions_signable_builder:
46        Arc<dyn SignableBuilder<(BlockNumber, BlockNumberOffset)>>,
47    cardano_stake_distribution_builder: Arc<dyn SignableBuilder<Epoch>>,
48    cardano_database_signable_builder: Arc<dyn SignableBuilder<CardanoDbBeacon>>,
49}
50
51impl SignableBuilderServiceDependencies {
52    /// Create a new instance of [SignableBuilderServiceDependencies].
53    pub fn new(
54        mithril_stake_distribution_builder: Arc<dyn SignableBuilder<Epoch>>,
55        immutable_signable_builder: Arc<dyn SignableBuilder<CardanoDbBeacon>>,
56        cardano_transactions_signable_builder: Arc<dyn SignableBuilder<BlockNumber>>,
57        cardano_blocks_transactions_signable_builder: Arc<
58            dyn SignableBuilder<(BlockNumber, BlockNumberOffset)>,
59        >,
60        cardano_stake_distribution_builder: Arc<dyn SignableBuilder<Epoch>>,
61        cardano_database_signable_builder: Arc<dyn SignableBuilder<CardanoDbBeacon>>,
62    ) -> Self {
63        Self {
64            mithril_stake_distribution_builder,
65            immutable_signable_builder,
66            cardano_transactions_signable_builder,
67            cardano_blocks_transactions_signable_builder,
68            cardano_stake_distribution_builder,
69            cardano_database_signable_builder,
70        }
71    }
72}
73
74impl MithrilSignableBuilderService {
75    /// MithrilSignableBuilderService factory
76    pub fn new(
77        seed_signable_builder: Arc<dyn SignableSeedBuilder>,
78        dependencies: SignableBuilderServiceDependencies,
79        logger: Logger,
80    ) -> Self {
81        Self {
82            seed_signable_builder,
83            mithril_stake_distribution_builder: dependencies.mithril_stake_distribution_builder,
84            immutable_signable_builder: dependencies.immutable_signable_builder,
85            cardano_transactions_signable_builder: dependencies
86                .cardano_transactions_signable_builder,
87            cardano_blocks_transactions_signable_builder: dependencies
88                .cardano_blocks_transactions_signable_builder,
89            cardano_stake_distribution_builder: dependencies.cardano_stake_distribution_builder,
90            cardano_database_signable_builder: dependencies.cardano_database_signable_builder,
91            logger: logger.new_with_component_name::<Self>(),
92        }
93    }
94
95    async fn compute_signed_entity_protocol_message(
96        &self,
97        signed_entity_type: SignedEntityType,
98    ) -> StdResult<ProtocolMessage> {
99        debug!(
100            self.logger,
101            "Compute protocol message for signed entity type: '{signed_entity_type:?}'"
102        );
103
104        let protocol_message = match &signed_entity_type {
105            SignedEntityType::MithrilStakeDistribution(e) => {
106                self.mithril_stake_distribution_builder
107                    .compute_protocol_message(*e)
108                    .await
109            }
110            SignedEntityType::CardanoImmutableFilesFull(beacon) => {
111                self.immutable_signable_builder
112                    .compute_protocol_message(beacon.clone())
113                    .await
114            }
115            SignedEntityType::CardanoStakeDistribution(e) => {
116                self.cardano_stake_distribution_builder
117                    .compute_protocol_message(*e)
118                    .await
119            }
120            SignedEntityType::CardanoTransactions(_, block_number) => {
121                self.cardano_transactions_signable_builder
122                    .compute_protocol_message(*block_number)
123                    .await
124            }
125            SignedEntityType::CardanoBlocksTransactions(_, block_number, block_number_offset) => {
126                self.cardano_blocks_transactions_signable_builder
127                    .compute_protocol_message((*block_number, *block_number_offset))
128                    .await
129            }
130            SignedEntityType::CardanoDatabase(beacon) => {
131                self.cardano_database_signable_builder
132                    .compute_protocol_message(beacon.clone())
133                    .await
134            }
135        }
136        .with_context(|| {
137            format!("Signable builder service can not compute protocol message for signed entity type: '{signed_entity_type:?}'")
138        })?;
139
140        Ok(protocol_message)
141    }
142
143    async fn compute_seeded_protocol_message(
144        &self,
145        protocol_message: ProtocolMessage,
146    ) -> StdResult<ProtocolMessage> {
147        let mut protocol_message = protocol_message;
148        let next_aggregate_verification_key = self
149            .seed_signable_builder
150            .compute_next_aggregate_verification_key_for_concatenation()
151            .await?;
152        protocol_message.set_message_part(
153            ProtocolMessagePartKey::NextAggregateVerificationKey,
154            next_aggregate_verification_key,
155        );
156
157        #[cfg(feature = "future_snark")]
158        if let Some(next_snark_aggregate_verification_key) = self
159            .seed_signable_builder
160            .compute_next_aggregate_verification_key_for_snark()
161            .await?
162        {
163            protocol_message.set_message_part(
164                ProtocolMessagePartKey::NextSnarkAggregateVerificationKey,
165                next_snark_aggregate_verification_key,
166            );
167        }
168
169        let next_protocol_parameters =
170            self.seed_signable_builder.compute_next_protocol_parameters().await?;
171        protocol_message.set_message_part(
172            ProtocolMessagePartKey::NextProtocolParameters,
173            next_protocol_parameters,
174        );
175        let current_epoch = self.seed_signable_builder.compute_current_epoch().await?;
176        protocol_message.set_message_part(ProtocolMessagePartKey::CurrentEpoch, current_epoch);
177
178        Ok(protocol_message)
179    }
180}
181
182#[async_trait]
183impl SignableBuilderService for MithrilSignableBuilderService {
184    async fn compute_protocol_message(
185        &self,
186        signed_entity_type: SignedEntityType,
187    ) -> StdResult<ProtocolMessage> {
188        let protocol_message = self
189            .compute_signed_entity_protocol_message(signed_entity_type)
190            .await?;
191        let protocol_message = self.compute_seeded_protocol_message(protocol_message).await?;
192
193        Ok(protocol_message)
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use super::*;
200
201    use crate::{
202        StdResult,
203        entities::{BlockNumber, BlockNumberOffset, Epoch, ProtocolMessage},
204        signable_builder::{Beacon as Beaconnable, MockSignableSeedBuilder, SignableBuilder},
205        test::TestLogger,
206    };
207
208    use async_trait::async_trait;
209    use mockall::mock;
210
211    mock! {
212        SignableBuilderImpl<U> { }
213
214        #[async_trait]
215        impl<U> SignableBuilder<U> for SignableBuilderImpl<U> where U: Beaconnable,
216        {
217            async fn compute_protocol_message(&self, beacon: U) -> StdResult<ProtocolMessage>;
218        }
219    }
220
221    struct MockDependencyInjector {
222        mock_signable_seed_builder: MockSignableSeedBuilder,
223        mock_mithril_stake_distribution_signable_builder: MockSignableBuilderImpl<Epoch>,
224        mock_cardano_immutable_files_full_signable_builder:
225            MockSignableBuilderImpl<CardanoDbBeacon>,
226        mock_cardano_transactions_signable_builder: MockSignableBuilderImpl<BlockNumber>,
227        mock_cardano_blocks_transactions_signable_builder:
228            MockSignableBuilderImpl<(BlockNumber, BlockNumberOffset)>,
229        mock_cardano_stake_distribution_signable_builder: MockSignableBuilderImpl<Epoch>,
230        mock_cardano_database_signable_builder: MockSignableBuilderImpl<CardanoDbBeacon>,
231    }
232
233    impl MockDependencyInjector {
234        fn new() -> MockDependencyInjector {
235            MockDependencyInjector {
236                mock_signable_seed_builder: MockSignableSeedBuilder::new(),
237                mock_mithril_stake_distribution_signable_builder: MockSignableBuilderImpl::new(),
238                mock_cardano_immutable_files_full_signable_builder: MockSignableBuilderImpl::new(),
239                mock_cardano_transactions_signable_builder: MockSignableBuilderImpl::new(),
240                mock_cardano_blocks_transactions_signable_builder: MockSignableBuilderImpl::new(),
241                mock_cardano_stake_distribution_signable_builder: MockSignableBuilderImpl::new(),
242                mock_cardano_database_signable_builder: MockSignableBuilderImpl::new(),
243            }
244        }
245
246        fn build_signable_builder_service(self) -> MithrilSignableBuilderService {
247            let dependencies = SignableBuilderServiceDependencies::new(
248                Arc::new(self.mock_mithril_stake_distribution_signable_builder),
249                Arc::new(self.mock_cardano_immutable_files_full_signable_builder),
250                Arc::new(self.mock_cardano_transactions_signable_builder),
251                Arc::new(self.mock_cardano_blocks_transactions_signable_builder),
252                Arc::new(self.mock_cardano_stake_distribution_signable_builder),
253                Arc::new(self.mock_cardano_database_signable_builder),
254            );
255
256            MithrilSignableBuilderService::new(
257                Arc::new(self.mock_signable_seed_builder),
258                dependencies,
259                TestLogger::stdout(),
260            )
261        }
262    }
263
264    fn build_mock_container() -> MockDependencyInjector {
265        let mut mock_container = MockDependencyInjector::new();
266        mock_container
267            .mock_signable_seed_builder
268            .expect_compute_next_aggregate_verification_key_for_concatenation()
269            .once()
270            .return_once(move || Ok("next-avk-123".to_string()));
271        #[cfg(feature = "future_snark")]
272        mock_container
273            .mock_signable_seed_builder
274            .expect_compute_next_aggregate_verification_key_for_snark()
275            .once()
276            .return_once(move || Ok(Some("next-snark-avk-123".to_string())));
277        mock_container
278            .mock_signable_seed_builder
279            .expect_compute_next_protocol_parameters()
280            .once()
281            .return_once(move || Ok("protocol-params-hash-123".to_string()));
282        mock_container
283            .mock_signable_seed_builder
284            .expect_compute_current_epoch()
285            .once()
286            .return_once(move || Ok("epoch-123".to_string()));
287
288        mock_container
289    }
290
291    #[tokio::test]
292    async fn build_mithril_stake_distribution_signable_when_given_mithril_stake_distribution_entity_type()
293     {
294        let mut mock_container = build_mock_container();
295        mock_container
296            .mock_mithril_stake_distribution_signable_builder
297            .expect_compute_protocol_message()
298            .once()
299            .return_once(|_| Ok(ProtocolMessage::new()));
300        let signable_builder_service = mock_container.build_signable_builder_service();
301        let signed_entity_type = SignedEntityType::MithrilStakeDistribution(Epoch(1));
302
303        signable_builder_service
304            .compute_protocol_message(signed_entity_type)
305            .await
306            .unwrap();
307    }
308
309    #[tokio::test]
310    async fn build_snapshot_signable_when_given_cardano_immutable_files_full_entity_type() {
311        let mut mock_container = build_mock_container();
312        mock_container
313            .mock_cardano_immutable_files_full_signable_builder
314            .expect_compute_protocol_message()
315            .once()
316            .return_once(|_| Ok(ProtocolMessage::new()));
317        let signable_builder_service = mock_container.build_signable_builder_service();
318        let signed_entity_type =
319            SignedEntityType::CardanoImmutableFilesFull(CardanoDbBeacon::default());
320
321        signable_builder_service
322            .compute_protocol_message(signed_entity_type)
323            .await
324            .unwrap();
325    }
326
327    #[tokio::test]
328    async fn build_transactions_signable_when_given_cardano_transactions_entity_type() {
329        let mut mock_container = build_mock_container();
330        mock_container
331            .mock_cardano_transactions_signable_builder
332            .expect_compute_protocol_message()
333            .once()
334            .return_once(|_| Ok(ProtocolMessage::new()));
335        let signable_builder_service = mock_container.build_signable_builder_service();
336        let signed_entity_type = SignedEntityType::CardanoTransactions(Epoch(5), BlockNumber(1000));
337
338        signable_builder_service
339            .compute_protocol_message(signed_entity_type)
340            .await
341            .unwrap();
342    }
343
344    #[tokio::test]
345    async fn build_blocks_transactions_signable_when_given_cardano_blocks_transactions_entity_type()
346    {
347        let mut mock_container = build_mock_container();
348        mock_container
349            .mock_cardano_blocks_transactions_signable_builder
350            .expect_compute_protocol_message()
351            .once()
352            .return_once(|_| Ok(ProtocolMessage::new()));
353        let signable_builder_service = mock_container.build_signable_builder_service();
354        let signed_entity_type = SignedEntityType::CardanoBlocksTransactions(
355            Epoch(6),
356            BlockNumber(1010),
357            BlockNumberOffset(15),
358        );
359
360        signable_builder_service
361            .compute_protocol_message(signed_entity_type)
362            .await
363            .unwrap();
364    }
365
366    #[tokio::test]
367    async fn build_cardano_stake_distribution_signable_when_given_cardano_stake_distribution_entity_type()
368     {
369        let mut mock_container = build_mock_container();
370        mock_container
371            .mock_cardano_stake_distribution_signable_builder
372            .expect_compute_protocol_message()
373            .once()
374            .return_once(|_| Ok(ProtocolMessage::new()));
375        let signable_builder_service = mock_container.build_signable_builder_service();
376        let signed_entity_type = SignedEntityType::CardanoStakeDistribution(Epoch(5));
377
378        signable_builder_service
379            .compute_protocol_message(signed_entity_type)
380            .await
381            .unwrap();
382    }
383
384    #[tokio::test]
385    async fn build_cardano_database_signable_when_given_cardano_database_entity_type() {
386        let mut mock_container = build_mock_container();
387        mock_container
388            .mock_cardano_database_signable_builder
389            .expect_compute_protocol_message()
390            .once()
391            .return_once(|_| Ok(ProtocolMessage::new()));
392        let signable_builder_service = mock_container.build_signable_builder_service();
393        let signed_entity_type = SignedEntityType::CardanoDatabase(CardanoDbBeacon::default());
394
395        signable_builder_service
396            .compute_protocol_message(signed_entity_type)
397            .await
398            .unwrap();
399    }
400}