mithril_aggregator/services/signer_registration/
leader.rs1use std::sync::Arc;
2
3use anyhow::Context;
4use async_trait::async_trait;
5use tokio::sync::RwLock;
6
7use mithril_common::{
8 StdResult,
9 entities::{Epoch, Signer, SignerWithStake, StakeDistribution},
10};
11
12use crate::{SignerRegistrationVerifier, VerificationKeyStorer};
13
14use super::{
15 SignerRecorder, SignerRegisterer, SignerRegistrationError, SignerRegistrationRound,
16 SignerRegistrationRoundOpener, SignerSynchronizer,
17};
18
19pub struct MithrilSignerRegistrationLeader {
21 current_round: RwLock<Option<SignerRegistrationRound>>,
23
24 verification_key_store: Arc<dyn VerificationKeyStorer>,
26
27 signer_recorder: Arc<dyn SignerRecorder>,
29
30 signer_registration_verifier: Arc<dyn SignerRegistrationVerifier>,
32}
33
34impl MithrilSignerRegistrationLeader {
35 pub fn new(
37 verification_key_store: Arc<dyn VerificationKeyStorer>,
38 signer_recorder: Arc<dyn SignerRecorder>,
39 signer_registration_verifier: Arc<dyn SignerRegistrationVerifier>,
40 ) -> Self {
41 Self {
42 current_round: RwLock::new(None),
43 verification_key_store,
44 signer_recorder,
45 signer_registration_verifier,
46 }
47 }
48
49 #[cfg(test)]
50 pub(crate) async fn get_current_round(&self) -> Option<SignerRegistrationRound> {
51 self.current_round.read().await.as_ref().cloned()
52 }
53}
54
55#[async_trait]
56impl SignerRegistrationRoundOpener for MithrilSignerRegistrationLeader {
57 async fn open_registration_round(
58 &self,
59 registration_epoch: Epoch,
60 stake_distribution: StakeDistribution,
61 ) -> StdResult<()> {
62 let mut current_round = self.current_round.write().await;
63 *current_round = Some(SignerRegistrationRound {
64 epoch: registration_epoch,
65 stake_distribution,
66 });
67
68 Ok(())
69 }
70
71 async fn close_registration_round(&self) -> StdResult<()> {
72 let mut current_round = self.current_round.write().await;
73 *current_round = None;
74
75 Ok(())
76 }
77}
78
79#[async_trait]
80impl SignerRegisterer for MithrilSignerRegistrationLeader {
81 async fn register_signer(
82 &self,
83 epoch: Epoch,
84 signer: &Signer,
85 ) -> Result<SignerWithStake, SignerRegistrationError> {
86 let registration_round = self.current_round.read().await;
87 let registration_round = registration_round
88 .as_ref()
89 .ok_or(SignerRegistrationError::RegistrationRoundNotYetOpened)?;
90 if registration_round.epoch != epoch {
91 return Err(SignerRegistrationError::RegistrationRoundUnexpectedEpoch {
92 current_round_epoch: registration_round.epoch,
93 received_epoch: epoch,
94 });
95 }
96
97 let signer_save = self
98 .signer_registration_verifier
99 .verify(signer, ®istration_round.stake_distribution)
100 .await
101 .map_err(|err| {
102 SignerRegistrationError::InvalidSignerRegistration(
103 signer.party_id.clone(),
104 epoch,
105 err,
106 )
107 })?;
108
109 self.signer_recorder
110 .record_signer_registration(signer_save.party_id.clone())
111 .await
112 .map_err(|err| {
113 SignerRegistrationError::FailedSignerRecorder(
114 signer_save.party_id.clone(),
115 registration_round.epoch,
116 err,
117 )
118 })?;
119
120 match self
121 .verification_key_store
122 .save_verification_key(registration_round.epoch, signer_save.clone())
123 .await
124 .with_context(|| {
125 format!(
126 "VerificationKeyStorer can not save verification keys for party_id: '{}' for epoch: '{}'",
127 signer_save.party_id,
128 registration_round.epoch
129 )
130 })
131 .map_err(SignerRegistrationError::Store)?
132 {
133 Some(_) => Err(SignerRegistrationError::ExistingSigner(Box::new(
134 signer_save,
135 ))),
136 None => Ok(signer_save),
137 }
138 }
139
140 async fn get_current_round(&self) -> Option<SignerRegistrationRound> {
141 self.current_round.read().await.as_ref().cloned()
142 }
143}
144
145#[async_trait]
146impl SignerSynchronizer for MithrilSignerRegistrationLeader {
147 async fn can_synchronize_signers(
148 &self,
149 _epoch: Epoch,
150 ) -> Result<bool, SignerRegistrationError> {
151 Ok(false)
152 }
153
154 async fn synchronize_all_signers(&self) -> Result<(), SignerRegistrationError> {
155 Err(SignerRegistrationError::SignerSynchronizationUnavailableOnLeaderAggregator)
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use std::{collections::HashMap, sync::Arc};
162
163 use mithril_common::{
164 entities::{Epoch, Signer, SignerWithStake},
165 test::builder::MithrilFixtureBuilder,
166 };
167
168 use crate::{
169 MithrilSignerRegistrationLeader, SignerRegisterer, SignerRegistrationRoundOpener,
170 VerificationKeyStorer,
171 database::{repository::SignerRegistrationStore, test_helper::main_db_connection},
172 services::{MockSignerRecorder, MockSignerRegistrationVerifier, SignerSynchronizer},
173 };
174
175 use test_utils::*;
176
177 use super::*;
178
179 mod test_utils {
180 use super::*;
181
182 pub struct MithrilSignerRegistrationLeaderBuilder {
184 signer_recorder: Arc<dyn SignerRecorder>,
185 signer_registration_verifier: Arc<dyn SignerRegistrationVerifier>,
186 verification_key_store: Arc<dyn VerificationKeyStorer>,
187 }
188
189 impl Default for MithrilSignerRegistrationLeaderBuilder {
190 fn default() -> Self {
191 Self {
192 signer_recorder: Arc::new(MockSignerRecorder::new()),
193 signer_registration_verifier: Arc::new(MockSignerRegistrationVerifier::new()),
194 verification_key_store: Arc::new(SignerRegistrationStore::new(
195 Arc::new(main_db_connection().unwrap()),
196 None,
197 )),
198 }
199 }
200 }
201
202 impl MithrilSignerRegistrationLeaderBuilder {
203 pub fn with_signer_recorder(self, signer_recorder: Arc<dyn SignerRecorder>) -> Self {
204 Self {
205 signer_recorder,
206 ..self
207 }
208 }
209
210 pub fn with_signer_registration_verifier(
211 self,
212 signer_registration_verifier: Arc<dyn SignerRegistrationVerifier>,
213 ) -> Self {
214 Self {
215 signer_registration_verifier,
216 ..self
217 }
218 }
219
220 pub fn build(self) -> MithrilSignerRegistrationLeader {
221 MithrilSignerRegistrationLeader {
222 current_round: RwLock::new(None),
223 verification_key_store: self.verification_key_store,
224 signer_recorder: self.signer_recorder,
225 signer_registration_verifier: self.signer_registration_verifier,
226 }
227 }
228 }
229 }
230
231 #[tokio::test]
232 async fn can_register_signer_if_registration_round_is_opened_with_operational_certificate() {
233 let registration_epoch = Epoch(1);
234 let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
235 let signer_to_register: Signer = fixture.signers()[0].to_owned();
236 let stake_distribution = fixture.stake_distribution();
237 let signer_registration_leader = MithrilSignerRegistrationLeaderBuilder::default()
238 .with_signer_recorder({
239 let mut signer_recorder = MockSignerRecorder::new();
240 signer_recorder
241 .expect_record_signer_registration()
242 .returning(|_| Ok(()))
243 .once();
244
245 Arc::new(signer_recorder)
246 })
247 .with_signer_registration_verifier({
248 let mut signer_registration_verifier = MockSignerRegistrationVerifier::new();
249 signer_registration_verifier
250 .expect_verify()
251 .returning(|signer, _| Ok(SignerWithStake::from_signer(signer.to_owned(), 123)))
252 .once();
253
254 Arc::new(signer_registration_verifier)
255 })
256 .build();
257
258 signer_registration_leader
259 .open_registration_round(registration_epoch, stake_distribution)
260 .await
261 .expect("signer registration round opening should not fail");
262
263 signer_registration_leader
264 .register_signer(registration_epoch, &signer_to_register)
265 .await
266 .expect("signer registration should not fail");
267
268 let registered_signers = &signer_registration_leader
269 .verification_key_store
270 .get_verification_keys(registration_epoch)
271 .await
272 .expect("registered signers retrieval should not fail");
273
274 assert_eq!(
275 &Some(HashMap::from([(
276 signer_to_register.party_id.clone(),
277 signer_to_register
278 )])),
279 registered_signers
280 );
281 }
282
283 #[tokio::test]
284 async fn can_register_signer_if_registration_round_is_opened_without_operational_certificate() {
285 let registration_epoch = Epoch(1);
286 let fixture = MithrilFixtureBuilder::default()
287 .with_signers(5)
288 .disable_signers_certification()
289 .build();
290 let signer_to_register: Signer = fixture.signers()[0].to_owned();
291 let stake_distribution = fixture.stake_distribution();
292 let signer_registration_leader = MithrilSignerRegistrationLeaderBuilder::default()
293 .with_signer_recorder({
294 let mut signer_recorder = MockSignerRecorder::new();
295 signer_recorder
296 .expect_record_signer_registration()
297 .returning(|_| Ok(()))
298 .once();
299
300 Arc::new(signer_recorder)
301 })
302 .with_signer_registration_verifier({
303 let mut signer_registration_verifier = MockSignerRegistrationVerifier::new();
304 signer_registration_verifier
305 .expect_verify()
306 .returning(|signer, _| Ok(SignerWithStake::from_signer(signer.to_owned(), 123)))
307 .once();
308
309 Arc::new(signer_registration_verifier)
310 })
311 .build();
312
313 signer_registration_leader
314 .open_registration_round(registration_epoch, stake_distribution)
315 .await
316 .expect("signer registration round opening should not fail");
317
318 signer_registration_leader
319 .register_signer(registration_epoch, &signer_to_register)
320 .await
321 .expect("signer registration should not fail");
322
323 let registered_signers = &signer_registration_leader
324 .verification_key_store
325 .get_verification_keys(registration_epoch)
326 .await
327 .expect("registered signers retrieval should not fail");
328
329 assert_eq!(
330 &Some(HashMap::from([(
331 signer_to_register.party_id.clone(),
332 signer_to_register
333 )])),
334 registered_signers
335 );
336 }
337
338 #[tokio::test]
339 async fn cant_register_signer_if_registration_round_is_not_opened() {
340 let registration_epoch = Epoch(1);
341 let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
342 let signer_to_register: Signer = fixture.signers()[0].to_owned();
343 let signer_registration_leader = MithrilSignerRegistrationLeaderBuilder::default().build();
344
345 signer_registration_leader
346 .register_signer(registration_epoch, &signer_to_register)
347 .await
348 .expect_err("signer registration should fail if no round opened");
349 }
350
351 #[tokio::test]
352 async fn synchronize_all_signers_always_fails() {
353 let signer_registration_leader = MithrilSignerRegistrationLeaderBuilder::default().build();
354
355 signer_registration_leader
356 .synchronize_all_signers()
357 .await
358 .expect_err("synchronize_signers");
359 }
360}