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