mithril_common/signable_builder/
signable_builder_service.rs1use anyhow::Context;
2use async_trait::async_trait;
3use slog::{debug, Logger};
4use std::sync::Arc;
5
6use crate::{
7 entities::{
8 BlockNumber, CardanoDbBeacon, Epoch, ProtocolMessage, ProtocolMessagePartKey,
9 SignedEntityType,
10 },
11 logging::LoggerExtensions,
12 signable_builder::{SignableBuilder, SignableSeedBuilder},
13 StdResult,
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 = self
149 .seed_signable_builder
150 .compute_next_protocol_parameters()
151 .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
173 .compute_seeded_protocol_message(protocol_message)
174 .await?;
175
176 Ok(protocol_message)
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 use crate::{
185 entities::{BlockNumber, Epoch, ProtocolMessage},
186 signable_builder::{Beacon as Beaconnable, MockSignableSeedBuilder, SignableBuilder},
187 test_utils::TestLogger,
188 StdResult,
189 };
190
191 use async_trait::async_trait;
192 use mockall::mock;
193
194 mock! {
195 SignableBuilderImpl<U> { }
196
197 #[async_trait]
198 impl<U> SignableBuilder<U> for SignableBuilderImpl<U> where U: Beaconnable,
199 {
200 async fn compute_protocol_message(&self, beacon: U) -> StdResult<ProtocolMessage>;
201 }
202 }
203
204 struct MockDependencyInjector {
205 mock_signable_seed_builder: MockSignableSeedBuilder,
206 mock_mithril_stake_distribution_signable_builder: MockSignableBuilderImpl<Epoch>,
207 mock_cardano_immutable_files_full_signable_builder:
208 MockSignableBuilderImpl<CardanoDbBeacon>,
209 mock_cardano_transactions_signable_builder: MockSignableBuilderImpl<BlockNumber>,
210 mock_cardano_stake_distribution_signable_builder: MockSignableBuilderImpl<Epoch>,
211 mock_cardano_database_signable_builder: MockSignableBuilderImpl<CardanoDbBeacon>,
212 }
213
214 impl MockDependencyInjector {
215 fn new() -> MockDependencyInjector {
216 MockDependencyInjector {
217 mock_signable_seed_builder: MockSignableSeedBuilder::new(),
218 mock_mithril_stake_distribution_signable_builder: MockSignableBuilderImpl::new(),
219 mock_cardano_immutable_files_full_signable_builder: MockSignableBuilderImpl::new(),
220 mock_cardano_stake_distribution_signable_builder: MockSignableBuilderImpl::new(),
221 mock_cardano_transactions_signable_builder: MockSignableBuilderImpl::new(),
222 mock_cardano_database_signable_builder: MockSignableBuilderImpl::new(),
223 }
224 }
225
226 fn build_signable_builder_service(self) -> MithrilSignableBuilderService {
227 let dependencies = SignableBuilderServiceDependencies::new(
228 Arc::new(self.mock_mithril_stake_distribution_signable_builder),
229 Arc::new(self.mock_cardano_immutable_files_full_signable_builder),
230 Arc::new(self.mock_cardano_transactions_signable_builder),
231 Arc::new(self.mock_cardano_stake_distribution_signable_builder),
232 Arc::new(self.mock_cardano_database_signable_builder),
233 );
234
235 MithrilSignableBuilderService::new(
236 Arc::new(self.mock_signable_seed_builder),
237 dependencies,
238 TestLogger::stdout(),
239 )
240 }
241 }
242
243 fn build_mock_container() -> MockDependencyInjector {
244 let mut mock_container = MockDependencyInjector::new();
245 mock_container
246 .mock_signable_seed_builder
247 .expect_compute_next_aggregate_verification_key()
248 .once()
249 .return_once(move || Ok("next-avk-123".to_string()));
250 mock_container
251 .mock_signable_seed_builder
252 .expect_compute_next_protocol_parameters()
253 .once()
254 .return_once(move || Ok("protocol-params-hash-123".to_string()));
255 mock_container
256 .mock_signable_seed_builder
257 .expect_compute_current_epoch()
258 .once()
259 .return_once(move || Ok("epoch-123".to_string()));
260
261 mock_container
262 }
263
264 #[tokio::test]
265 async fn build_mithril_stake_distribution_signable_when_given_mithril_stake_distribution_entity_type(
266 ) {
267 let mut mock_container = build_mock_container();
268 mock_container
269 .mock_mithril_stake_distribution_signable_builder
270 .expect_compute_protocol_message()
271 .once()
272 .return_once(|_| Ok(ProtocolMessage::new()));
273 let signable_builder_service = mock_container.build_signable_builder_service();
274 let signed_entity_type = SignedEntityType::MithrilStakeDistribution(Epoch(1));
275
276 signable_builder_service
277 .compute_protocol_message(signed_entity_type)
278 .await
279 .unwrap();
280 }
281
282 #[tokio::test]
283 async fn build_snapshot_signable_when_given_cardano_immutable_files_full_entity_type() {
284 let mut mock_container = build_mock_container();
285 mock_container
286 .mock_cardano_immutable_files_full_signable_builder
287 .expect_compute_protocol_message()
288 .once()
289 .return_once(|_| Ok(ProtocolMessage::new()));
290 let signable_builder_service = mock_container.build_signable_builder_service();
291 let signed_entity_type =
292 SignedEntityType::CardanoImmutableFilesFull(CardanoDbBeacon::default());
293
294 signable_builder_service
295 .compute_protocol_message(signed_entity_type)
296 .await
297 .unwrap();
298 }
299
300 #[tokio::test]
301 async fn build_transactions_signable_when_given_cardano_transactions_entity_type() {
302 let mut mock_container = build_mock_container();
303 mock_container
304 .mock_cardano_transactions_signable_builder
305 .expect_compute_protocol_message()
306 .once()
307 .return_once(|_| Ok(ProtocolMessage::new()));
308 let signable_builder_service = mock_container.build_signable_builder_service();
309 let signed_entity_type = SignedEntityType::CardanoTransactions(Epoch(5), BlockNumber(1000));
310
311 signable_builder_service
312 .compute_protocol_message(signed_entity_type)
313 .await
314 .unwrap();
315 }
316
317 #[tokio::test]
318 async fn build_cardano_stake_distribution_signable_when_given_cardano_stake_distribution_entity_type(
319 ) {
320 let mut mock_container = build_mock_container();
321 mock_container
322 .mock_cardano_stake_distribution_signable_builder
323 .expect_compute_protocol_message()
324 .once()
325 .return_once(|_| Ok(ProtocolMessage::new()));
326 let signable_builder_service = mock_container.build_signable_builder_service();
327 let signed_entity_type = SignedEntityType::CardanoStakeDistribution(Epoch(5));
328
329 signable_builder_service
330 .compute_protocol_message(signed_entity_type)
331 .await
332 .unwrap();
333 }
334
335 #[tokio::test]
336 async fn build_cardano_database_signable_when_given_cardano_database_entity_type() {
337 let mut mock_container = build_mock_container();
338 mock_container
339 .mock_cardano_database_signable_builder
340 .expect_compute_protocol_message()
341 .once()
342 .return_once(|_| Ok(ProtocolMessage::new()));
343 let signable_builder_service = mock_container.build_signable_builder_service();
344 let signed_entity_type = SignedEntityType::CardanoDatabase(CardanoDbBeacon::default());
345
346 signable_builder_service
347 .compute_protocol_message(signed_entity_type)
348 .await
349 .unwrap();
350 }
351}