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::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}