mithril_aggregator/services/signer_registration/
leader.rs1use std::sync::Arc;
2
3use anyhow::{anyhow, Context};
4use async_trait::async_trait;
5use tokio::sync::RwLock;
6
7use mithril_common::{
8 entities::{Epoch, Signer, SignerWithStake, StakeDistribution},
9 StdResult,
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(|e| SignerRegistrationError::FailedSignerRegistration(anyhow!(e)))?;
102
103 self.signer_recorder
104 .record_signer_registration(signer_save.party_id.clone())
105 .await
106 .map_err(|e| SignerRegistrationError::FailedSignerRecorder(e.to_string()))?;
107
108 match self
109 .verification_key_store
110 .save_verification_key(registration_round.epoch, signer_save.clone())
111 .await
112 .with_context(|| {
113 format!(
114 "VerificationKeyStorer can not save verification keys for party_id: '{}' for epoch: '{}'",
115 signer_save.party_id,
116 registration_round.epoch
117 )
118 })
119 .map_err(|e| SignerRegistrationError::Store(anyhow!(e)))?
120 {
121 Some(_) => Err(SignerRegistrationError::ExistingSigner(Box::new(
122 signer_save,
123 ))),
124 None => Ok(signer_save),
125 }
126 }
127
128 async fn get_current_round(&self) -> Option<SignerRegistrationRound> {
129 self.current_round.read().await.as_ref().cloned()
130 }
131}
132
133#[async_trait]
134impl SignerSynchronizer for MithrilSignerRegistrationLeader {
135 async fn can_synchronize_signers(
136 &self,
137 _epoch: Epoch,
138 ) -> Result<bool, SignerRegistrationError> {
139 Ok(false)
140 }
141
142 async fn synchronize_all_signers(&self) -> Result<(), SignerRegistrationError> {
143 Err(SignerRegistrationError::SignerSynchronizationUnavailableOnLeaderAggregator)
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use std::{collections::HashMap, sync::Arc};
150
151 use mithril_common::{
152 entities::{Epoch, Signer, SignerWithStake},
153 test_utils::MithrilFixtureBuilder,
154 };
155
156 use crate::{
157 database::{repository::SignerRegistrationStore, test_helper::main_db_connection},
158 services::{MockSignerRecorder, MockSignerRegistrationVerifier, SignerSynchronizer},
159 MithrilSignerRegistrationLeader, SignerRegisterer, SignerRegistrationRoundOpener,
160 VerificationKeyStorer,
161 };
162
163 use test_utils::*;
164
165 use super::*;
166
167 mod test_utils {
168 use super::*;
169
170 pub struct MithrilSignerRegistrationLeaderBuilder {
172 signer_recorder: Arc<dyn SignerRecorder>,
173 signer_registration_verifier: Arc<dyn SignerRegistrationVerifier>,
174 verification_key_store: Arc<dyn VerificationKeyStorer>,
175 }
176
177 impl Default for MithrilSignerRegistrationLeaderBuilder {
178 fn default() -> Self {
179 Self {
180 signer_recorder: Arc::new(MockSignerRecorder::new()),
181 signer_registration_verifier: Arc::new(MockSignerRegistrationVerifier::new()),
182 verification_key_store: Arc::new(SignerRegistrationStore::new(
183 Arc::new(main_db_connection().unwrap()),
184 None,
185 )),
186 }
187 }
188 }
189
190 impl MithrilSignerRegistrationLeaderBuilder {
191 pub fn with_signer_recorder(self, signer_recorder: Arc<dyn SignerRecorder>) -> Self {
192 Self {
193 signer_recorder,
194 ..self
195 }
196 }
197
198 pub fn with_signer_registration_verifier(
199 self,
200 signer_registration_verifier: Arc<dyn SignerRegistrationVerifier>,
201 ) -> Self {
202 Self {
203 signer_registration_verifier,
204 ..self
205 }
206 }
207
208 pub fn build(self) -> MithrilSignerRegistrationLeader {
209 MithrilSignerRegistrationLeader {
210 current_round: RwLock::new(None),
211 verification_key_store: self.verification_key_store,
212 signer_recorder: self.signer_recorder,
213 signer_registration_verifier: self.signer_registration_verifier,
214 }
215 }
216 }
217 }
218
219 #[tokio::test]
220 async fn can_register_signer_if_registration_round_is_opened_with_operational_certificate() {
221 let registration_epoch = Epoch(1);
222 let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
223 let signer_to_register: Signer = fixture.signers()[0].to_owned();
224 let stake_distribution = fixture.stake_distribution();
225 let signer_registration_leader = MithrilSignerRegistrationLeaderBuilder::default()
226 .with_signer_recorder({
227 let mut signer_recorder = MockSignerRecorder::new();
228 signer_recorder
229 .expect_record_signer_registration()
230 .returning(|_| Ok(()))
231 .once();
232
233 Arc::new(signer_recorder)
234 })
235 .with_signer_registration_verifier({
236 let mut signer_registration_verifier = MockSignerRegistrationVerifier::new();
237 signer_registration_verifier
238 .expect_verify()
239 .returning(|signer, _| Ok(SignerWithStake::from_signer(signer.to_owned(), 123)))
240 .once();
241
242 Arc::new(signer_registration_verifier)
243 })
244 .build();
245
246 signer_registration_leader
247 .open_registration_round(registration_epoch, stake_distribution)
248 .await
249 .expect("signer registration round opening should not fail");
250
251 signer_registration_leader
252 .register_signer(registration_epoch, &signer_to_register)
253 .await
254 .expect("signer registration should not fail");
255
256 let registered_signers = &signer_registration_leader
257 .verification_key_store
258 .get_verification_keys(registration_epoch)
259 .await
260 .expect("registered signers retrieval should not fail");
261
262 assert_eq!(
263 &Some(HashMap::from([(
264 signer_to_register.party_id.clone(),
265 signer_to_register
266 )])),
267 registered_signers
268 );
269 }
270
271 #[tokio::test]
272 async fn can_register_signer_if_registration_round_is_opened_without_operational_certificate() {
273 let registration_epoch = Epoch(1);
274 let fixture = MithrilFixtureBuilder::default()
275 .with_signers(5)
276 .disable_signers_certification()
277 .build();
278 let signer_to_register: Signer = fixture.signers()[0].to_owned();
279 let stake_distribution = fixture.stake_distribution();
280 let signer_registration_leader = MithrilSignerRegistrationLeaderBuilder::default()
281 .with_signer_recorder({
282 let mut signer_recorder = MockSignerRecorder::new();
283 signer_recorder
284 .expect_record_signer_registration()
285 .returning(|_| Ok(()))
286 .once();
287
288 Arc::new(signer_recorder)
289 })
290 .with_signer_registration_verifier({
291 let mut signer_registration_verifier = MockSignerRegistrationVerifier::new();
292 signer_registration_verifier
293 .expect_verify()
294 .returning(|signer, _| Ok(SignerWithStake::from_signer(signer.to_owned(), 123)))
295 .once();
296
297 Arc::new(signer_registration_verifier)
298 })
299 .build();
300
301 signer_registration_leader
302 .open_registration_round(registration_epoch, stake_distribution)
303 .await
304 .expect("signer registration round opening should not fail");
305
306 signer_registration_leader
307 .register_signer(registration_epoch, &signer_to_register)
308 .await
309 .expect("signer registration should not fail");
310
311 let registered_signers = &signer_registration_leader
312 .verification_key_store
313 .get_verification_keys(registration_epoch)
314 .await
315 .expect("registered signers retrieval should not fail");
316
317 assert_eq!(
318 &Some(HashMap::from([(
319 signer_to_register.party_id.clone(),
320 signer_to_register
321 )])),
322 registered_signers
323 );
324 }
325
326 #[tokio::test]
327 async fn cant_register_signer_if_registration_round_is_not_opened() {
328 let registration_epoch = Epoch(1);
329 let fixture = MithrilFixtureBuilder::default().with_signers(5).build();
330 let signer_to_register: Signer = fixture.signers()[0].to_owned();
331 let signer_registration_leader = MithrilSignerRegistrationLeaderBuilder::default().build();
332
333 signer_registration_leader
334 .register_signer(registration_epoch, &signer_to_register)
335 .await
336 .expect_err("signer registration should fail if no round opened");
337 }
338
339 #[tokio::test]
340 async fn synchronize_all_signers_always_fails() {
341 let signer_registration_leader = MithrilSignerRegistrationLeaderBuilder::default().build();
342
343 signer_registration_leader
344 .synchronize_all_signers()
345 .await
346 .expect_err("synchronize_signers");
347 }
348}