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_tools::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_tools::TestLogger::stdout(),
107        }
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use anyhow::anyhow;
114
115    use crate::multi_signer::MockMultiSigner;
116    use crate::test_tools::TestLogger;
117
118    use super::*;
119
120    fn mock_multi_signer(
121        multi_signer_mock_config: impl FnOnce(&mut MockMultiSigner),
122    ) -> Arc<MockMultiSigner> {
123        let mut multi_signer = MockMultiSigner::new();
124        multi_signer_mock_config(&mut multi_signer);
125        Arc::new(multi_signer)
126    }
127
128    #[tokio::test]
129    async fn single_signature_against_valid_signed_message_for_current_stake_distribution_is_authenticated()
130     {
131        let signed_message = "signed_message".to_string();
132        let mut single_signature = SingleSignature {
133            authentication_status: SingleSignatureAuthenticationStatus::Unauthenticated,
134            ..SingleSignature::fake("party_id", &signed_message)
135        };
136
137        let authenticator = SingleSignatureAuthenticator::new(
138            mock_multi_signer(|mock_config| {
139                mock_config.expect_verify_single_signature().returning(|_, _| Ok(()));
140            }),
141            TestLogger::stdout(),
142        );
143
144        authenticator
145            .authenticate(&mut single_signature, &signed_message)
146            .await
147            .unwrap();
148
149        assert_eq!(
150            single_signature.authentication_status,
151            SingleSignatureAuthenticationStatus::Authenticated
152        );
153    }
154
155    #[tokio::test]
156    async fn single_signature_against_valid_signed_message_for_next_stake_distribution_is_authenticated()
157     {
158        let signed_message = "signed_message".to_string();
159        let mut single_signature = SingleSignature {
160            authentication_status: SingleSignatureAuthenticationStatus::Unauthenticated,
161            ..SingleSignature::fake("party_id", &signed_message)
162        };
163
164        let authenticator = SingleSignatureAuthenticator::new(
165            mock_multi_signer(|mock_config| {
166                mock_config
167                    .expect_verify_single_signature()
168                    .returning(|_, _| Err(anyhow!("error")));
169                mock_config
170                    .expect_verify_single_signature_for_next_stake_distribution()
171                    .returning(|_, _| Ok(()));
172            }),
173            TestLogger::stdout(),
174        );
175
176        authenticator
177            .authenticate(&mut single_signature, &signed_message)
178            .await
179            .unwrap();
180
181        assert_eq!(
182            single_signature.authentication_status,
183            SingleSignatureAuthenticationStatus::Authenticated
184        );
185    }
186
187    #[tokio::test]
188    async fn single_signature_against_invalid_signed_message_for_current_and_next_stake_distribution_is_not_authenticated()
189     {
190        let signed_message = "signed_message".to_string();
191        let mut single_signature = SingleSignature {
192            authentication_status: SingleSignatureAuthenticationStatus::Unauthenticated,
193            ..SingleSignature::fake("party_id", &signed_message)
194        };
195
196        let authenticator = SingleSignatureAuthenticator::new(
197            mock_multi_signer(|mock_config| {
198                mock_config
199                    .expect_verify_single_signature()
200                    .returning(|_, _| Err(anyhow!("verify_single_signature error")));
201                mock_config
202                    .expect_verify_single_signature_for_next_stake_distribution()
203                    .returning(|_, _| {
204                        Err(anyhow!(
205                            "verify_single_signature_for_next_stake_distribution error"
206                        ))
207                    });
208            }),
209            TestLogger::stdout(),
210        );
211
212        authenticator
213            .authenticate(&mut single_signature, &signed_message)
214            .await
215            .unwrap();
216
217        assert_eq!(
218            single_signature.authentication_status,
219            SingleSignatureAuthenticationStatus::Unauthenticated
220        );
221    }
222
223    #[tokio::test]
224    async fn single_signature_previously_authenticated_but_fail_new_authentication_is_now_unauthenticated()
225     {
226        let signed_message = "signed_message".to_string();
227        let mut single_signature = SingleSignature {
228            authentication_status: SingleSignatureAuthenticationStatus::Authenticated,
229            ..SingleSignature::fake("party_id", &signed_message)
230        };
231
232        let authenticator = SingleSignatureAuthenticator::new(
233            mock_multi_signer(|mock_config| {
234                mock_config
235                    .expect_verify_single_signature()
236                    .returning(|_, _| Err(anyhow!("verify_single_signature error")));
237                mock_config
238                    .expect_verify_single_signature_for_next_stake_distribution()
239                    .returning(|_, _| {
240                        Err(anyhow!(
241                            "verify_single_signature_for_next_stake_distribution error"
242                        ))
243                    });
244            }),
245            TestLogger::stdout(),
246        );
247
248        authenticator
249            .authenticate(&mut single_signature, &signed_message)
250            .await
251            .unwrap();
252
253        assert_eq!(
254            single_signature.authentication_status,
255            SingleSignatureAuthenticationStatus::Unauthenticated
256        );
257    }
258}