mithril_aggregator/tools/
single_signature_authenticator.rs

1use slog::{Logger, debug};
2use std::sync::Arc;
3
4use mithril_common::StdResult;
5use mithril_common::entities::{SingleSignature, SingleSignatureAuthenticationStatus};
6use mithril_common::logging::LoggerExtensions;
7
8use crate::MultiSigner;
9
10/// Authenticates single signatures against a signed message.
11pub struct SingleSignatureAuthenticator {
12    multi_signer: Arc<dyn MultiSigner>,
13    logger: Logger,
14}
15
16impl SingleSignatureAuthenticator {
17    /// Creates a new `SingleSignatureAuthenticator`.
18    pub fn new(multi_signer: Arc<dyn MultiSigner>, logger: Logger) -> Self {
19        Self {
20            multi_signer,
21            logger: logger.new_with_component_name::<Self>(),
22        }
23    }
24
25    /// Authenticates a single signature against a signed message.
26    pub async fn authenticate(
27        &self,
28        single_signature: &mut SingleSignature,
29        signed_message: &str,
30    ) -> StdResult<()> {
31        let is_authenticated = match self
32            .multi_signer
33            .verify_single_signature(signed_message, single_signature)
34            .await
35        {
36            Ok(()) => {
37                debug!(
38                    self.logger, "Single signature party authenticated for current stake distribution";
39                    "party_id" => &single_signature.party_id,
40                );
41                true
42            }
43            Err(_error) => {
44                // Signers may detect epoch changes before the aggregator and send
45                // new signatures using the next epoch stake distribution
46                if self
47                    .multi_signer
48                    .verify_single_signature_for_next_stake_distribution(
49                        signed_message,
50                        single_signature,
51                    )
52                    .await
53                    .is_ok()
54                {
55                    debug!(
56                        self.logger, "Single signature party authenticated for next stake distribution";
57                        "party_id" => &single_signature.party_id,
58                    );
59                    true
60                } else {
61                    debug!(
62                        self.logger, "Single signature party not authenticated";
63                        "party_id" => &single_signature.party_id,
64                    );
65                    false
66                }
67            }
68        };
69
70        single_signature.authentication_status = if is_authenticated {
71            SingleSignatureAuthenticationStatus::Authenticated
72        } else {
73            SingleSignatureAuthenticationStatus::Unauthenticated
74        };
75
76        Ok(())
77    }
78}
79
80#[cfg(test)]
81impl SingleSignatureAuthenticator {
82    pub(crate) fn new_that_authenticate_everything() -> Self {
83        let mut multi_signer = crate::multi_signer::MockMultiSigner::new();
84        multi_signer.expect_verify_single_signature().returning(|_, _| Ok(()));
85        multi_signer
86            .expect_verify_single_signature_for_next_stake_distribution()
87            .returning(|_, _| Ok(()));
88
89        Self {
90            multi_signer: Arc::new(multi_signer),
91            logger: crate::test::TestLogger::stdout(),
92        }
93    }
94
95    pub(crate) fn new_that_reject_everything() -> Self {
96        let mut multi_signer = crate::multi_signer::MockMultiSigner::new();
97        multi_signer
98            .expect_verify_single_signature()
99            .returning(|_, _| Err(anyhow::anyhow!("error")));
100        multi_signer
101            .expect_verify_single_signature_for_next_stake_distribution()
102            .returning(|_, _| Err(anyhow::anyhow!("error")));
103
104        Self {
105            multi_signer: Arc::new(multi_signer),
106            logger: crate::test::TestLogger::stdout(),
107        }
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use anyhow::anyhow;
114
115    use mithril_common::test::entities_extensions::SingleSignatureTestExtension;
116
117    use crate::multi_signer::MockMultiSigner;
118    use crate::test::TestLogger;
119
120    use super::*;
121
122    fn mock_multi_signer(
123        multi_signer_mock_config: impl FnOnce(&mut MockMultiSigner),
124    ) -> Arc<MockMultiSigner> {
125        let mut multi_signer = MockMultiSigner::new();
126        multi_signer_mock_config(&mut multi_signer);
127        Arc::new(multi_signer)
128    }
129
130    #[tokio::test]
131    async fn single_signature_against_valid_signed_message_for_current_stake_distribution_is_authenticated()
132     {
133        let signed_message = "signed_message".to_string();
134        let mut single_signature = SingleSignature {
135            authentication_status: SingleSignatureAuthenticationStatus::Unauthenticated,
136            ..SingleSignature::fake("party_id", &signed_message)
137        };
138
139        let authenticator = SingleSignatureAuthenticator::new(
140            mock_multi_signer(|mock_config| {
141                mock_config.expect_verify_single_signature().returning(|_, _| Ok(()));
142            }),
143            TestLogger::stdout(),
144        );
145
146        authenticator
147            .authenticate(&mut single_signature, &signed_message)
148            .await
149            .unwrap();
150
151        assert_eq!(
152            single_signature.authentication_status,
153            SingleSignatureAuthenticationStatus::Authenticated
154        );
155    }
156
157    #[tokio::test]
158    async fn single_signature_against_valid_signed_message_for_next_stake_distribution_is_authenticated()
159     {
160        let signed_message = "signed_message".to_string();
161        let mut single_signature = SingleSignature {
162            authentication_status: SingleSignatureAuthenticationStatus::Unauthenticated,
163            ..SingleSignature::fake("party_id", &signed_message)
164        };
165
166        let authenticator = SingleSignatureAuthenticator::new(
167            mock_multi_signer(|mock_config| {
168                mock_config
169                    .expect_verify_single_signature()
170                    .returning(|_, _| Err(anyhow!("error")));
171                mock_config
172                    .expect_verify_single_signature_for_next_stake_distribution()
173                    .returning(|_, _| Ok(()));
174            }),
175            TestLogger::stdout(),
176        );
177
178        authenticator
179            .authenticate(&mut single_signature, &signed_message)
180            .await
181            .unwrap();
182
183        assert_eq!(
184            single_signature.authentication_status,
185            SingleSignatureAuthenticationStatus::Authenticated
186        );
187    }
188
189    #[tokio::test]
190    async fn single_signature_against_invalid_signed_message_for_current_and_next_stake_distribution_is_not_authenticated()
191     {
192        let signed_message = "signed_message".to_string();
193        let mut single_signature = SingleSignature {
194            authentication_status: SingleSignatureAuthenticationStatus::Unauthenticated,
195            ..SingleSignature::fake("party_id", &signed_message)
196        };
197
198        let authenticator = SingleSignatureAuthenticator::new(
199            mock_multi_signer(|mock_config| {
200                mock_config
201                    .expect_verify_single_signature()
202                    .returning(|_, _| Err(anyhow!("verify_single_signature error")));
203                mock_config
204                    .expect_verify_single_signature_for_next_stake_distribution()
205                    .returning(|_, _| {
206                        Err(anyhow!(
207                            "verify_single_signature_for_next_stake_distribution error"
208                        ))
209                    });
210            }),
211            TestLogger::stdout(),
212        );
213
214        authenticator
215            .authenticate(&mut single_signature, &signed_message)
216            .await
217            .unwrap();
218
219        assert_eq!(
220            single_signature.authentication_status,
221            SingleSignatureAuthenticationStatus::Unauthenticated
222        );
223    }
224
225    #[tokio::test]
226    async fn single_signature_previously_authenticated_but_fail_new_authentication_is_now_unauthenticated()
227     {
228        let signed_message = "signed_message".to_string();
229        let mut single_signature = SingleSignature {
230            authentication_status: SingleSignatureAuthenticationStatus::Authenticated,
231            ..SingleSignature::fake("party_id", &signed_message)
232        };
233
234        let authenticator = SingleSignatureAuthenticator::new(
235            mock_multi_signer(|mock_config| {
236                mock_config
237                    .expect_verify_single_signature()
238                    .returning(|_, _| Err(anyhow!("verify_single_signature error")));
239                mock_config
240                    .expect_verify_single_signature_for_next_stake_distribution()
241                    .returning(|_, _| {
242                        Err(anyhow!(
243                            "verify_single_signature_for_next_stake_distribution error"
244                        ))
245                    });
246            }),
247            TestLogger::stdout(),
248        );
249
250        authenticator
251            .authenticate(&mut single_signature, &signed_message)
252            .await
253            .unwrap();
254
255        assert_eq!(
256            single_signature.authentication_status,
257            SingleSignatureAuthenticationStatus::Unauthenticated
258        );
259    }
260}