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, CardanoDbBeacon, Epoch, ProtocolMessage, ProtocolMessagePartKey,
10 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_stake_distribution_builder: Arc<dyn SignableBuilder<Epoch>>,
34 cardano_database_signable_builder: Arc<dyn SignableBuilder<CardanoDbBeacon>>,
35 logger: Logger,
36}
37
38pub 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 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 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}