mithril_aggregator/tools/
single_signature_authenticator.rs

1use slog::{debug, Logger};
2use std::sync::Arc;
3
4use mithril_common::entities::{SingleSignatureAuthenticationStatus, SingleSignatures};
5use mithril_common::logging::LoggerExtensions;
6use mithril_common::StdResult;
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 SingleSignatures,
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
85            .expect_verify_single_signature()
86            .returning(|_, _| Ok(()));
87        multi_signer
88            .expect_verify_single_signature_for_next_stake_distribution()
89            .returning(|_, _| Ok(()));
90
91        Self {
92            multi_signer: Arc::new(multi_signer),
93            logger: crate::test_tools::TestLogger::stdout(),
94        }
95    }
96
97    pub(crate) fn new_that_reject_everything() -> Self {
98        let mut multi_signer = crate::multi_signer::MockMultiSigner::new();
99        multi_signer
100            .expect_verify_single_signature()
101            .returning(|_, _| Err(anyhow::anyhow!("error")));
102        multi_signer
103            .expect_verify_single_signature_for_next_stake_distribution()
104            .returning(|_, _| Err(anyhow::anyhow!("error")));
105
106        Self {
107            multi_signer: Arc::new(multi_signer),
108            logger: crate::test_tools::TestLogger::stdout(),
109        }
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use anyhow::anyhow;
116
117    use crate::multi_signer::MockMultiSigner;
118    use crate::test_tools::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 = SingleSignatures {
135            authentication_status: SingleSignatureAuthenticationStatus::Unauthenticated,
136            ..SingleSignatures::fake("party_id", &signed_message)
137        };
138
139        let authenticator = SingleSignatureAuthenticator::new(
140            mock_multi_signer(|mock_config| {
141                mock_config
142                    .expect_verify_single_signature()
143                    .returning(|_, _| Ok(()));
144            }),
145            TestLogger::stdout(),
146        );
147
148        authenticator
149            .authenticate(&mut single_signature, &signed_message)
150            .await
151            .unwrap();
152
153        assert_eq!(
154            single_signature.authentication_status,
155            SingleSignatureAuthenticationStatus::Authenticated
156        );
157    }
158
159    #[tokio::test]
160    async fn single_signature_against_valid_signed_message_for_next_stake_distribution_is_authenticated(
161    ) {
162        let signed_message = "signed_message".to_string();
163        let mut single_signature = SingleSignatures {
164            authentication_status: SingleSignatureAuthenticationStatus::Unauthenticated,
165            ..SingleSignatures::fake("party_id", &signed_message)
166        };
167
168        let authenticator = SingleSignatureAuthenticator::new(
169            mock_multi_signer(|mock_config| {
170                mock_config
171                    .expect_verify_single_signature()
172                    .returning(|_, _| Err(anyhow!("error")));
173                mock_config
174                    .expect_verify_single_signature_for_next_stake_distribution()
175                    .returning(|_, _| Ok(()));
176            }),
177            TestLogger::stdout(),
178        );
179
180        authenticator
181            .authenticate(&mut single_signature, &signed_message)
182            .await
183            .unwrap();
184
185        assert_eq!(
186            single_signature.authentication_status,
187            SingleSignatureAuthenticationStatus::Authenticated
188        );
189    }
190
191    #[tokio::test]
192    async fn single_signature_against_invalid_signed_message_for_current_and_next_stake_distribution_is_not_authenticated(
193    ) {
194        let signed_message = "signed_message".to_string();
195        let mut single_signature = SingleSignatures {
196            authentication_status: SingleSignatureAuthenticationStatus::Unauthenticated,
197            ..SingleSignatures::fake("party_id", &signed_message)
198        };
199
200        let authenticator = SingleSignatureAuthenticator::new(
201            mock_multi_signer(|mock_config| {
202                mock_config
203                    .expect_verify_single_signature()
204                    .returning(|_, _| Err(anyhow!("verify_single_signature error")));
205                mock_config
206                    .expect_verify_single_signature_for_next_stake_distribution()
207                    .returning(|_, _| {
208                        Err(anyhow!(
209                            "verify_single_signature_for_next_stake_distribution error"
210                        ))
211                    });
212            }),
213            TestLogger::stdout(),
214        );
215
216        authenticator
217            .authenticate(&mut single_signature, &signed_message)
218            .await
219            .unwrap();
220
221        assert_eq!(
222            single_signature.authentication_status,
223            SingleSignatureAuthenticationStatus::Unauthenticated
224        );
225    }
226
227    #[tokio::test]
228    async fn single_signature_previously_authenticated_but_fail_new_authentication_is_now_unauthenticated(
229    ) {
230        let signed_message = "signed_message".to_string();
231        let mut single_signature = SingleSignatures {
232            authentication_status: SingleSignatureAuthenticationStatus::Authenticated,
233            ..SingleSignatures::fake("party_id", &signed_message)
234        };
235
236        let authenticator = SingleSignatureAuthenticator::new(
237            mock_multi_signer(|mock_config| {
238                mock_config
239                    .expect_verify_single_signature()
240                    .returning(|_, _| Err(anyhow!("verify_single_signature error")));
241                mock_config
242                    .expect_verify_single_signature_for_next_stake_distribution()
243                    .returning(|_, _| {
244                        Err(anyhow!(
245                            "verify_single_signature_for_next_stake_distribution error"
246                        ))
247                    });
248            }),
249            TestLogger::stdout(),
250        );
251
252        authenticator
253            .authenticate(&mut single_signature, &signed_message)
254            .await
255            .unwrap();
256
257        assert_eq!(
258            single_signature.authentication_status,
259            SingleSignatureAuthenticationStatus::Unauthenticated
260        );
261    }
262}