mithril_common/signable_builder/
signable_builder_service.rs1use 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#[cfg_attr(test, mockall::automock)]
18#[async_trait]
19pub trait SignableBuilderService: Send + Sync {
20 async fn compute_protocol_message(
22 &self,
23 signed_entity_type: SignedEntityType,
24 ) -> StdResult<ProtocolMessage>;
25}
26
27pub 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
40pub 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 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 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}