mithril_aggregator/services/signer_registration/
leader.rs

1use 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
19/// A [MithrilSignerRegistrationLeader] supports signer registrations in a leader aggregator
20pub struct MithrilSignerRegistrationLeader {
21    /// Current signer registration round
22    current_round: RwLock<Option<SignerRegistrationRound>>,
23
24    /// Verification key store
25    verification_key_store: Arc<dyn VerificationKeyStorer>,
26
27    /// Signer recorder
28    signer_recorder: Arc<dyn SignerRecorder>,
29
30    /// Signer registration verifier
31    signer_registration_verifier: Arc<dyn SignerRegistrationVerifier>,
32}
33
34impl MithrilSignerRegistrationLeader {
35    /// MithrilSignerRegistererLeader factory
36    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, &registration_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        /// MithrilSignerRegistrationLeaderBuilder is a test builder for [MithrilSignerRegistrationFollower]
183        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}