mithril_signer/services/signature_publisher/
delayer.rs

1use std::{sync::Arc, time::Duration};
2
3use mithril_common::{
4    entities::{ProtocolMessage, SignedEntityType, SingleSignature},
5    logging::LoggerExtensions,
6    StdResult,
7};
8use slog::{error, Logger};
9
10use super::SignaturePublisher;
11
12/// A decorator of [SignaturePublisher] that publishes right away on a first publisher
13/// and with a delay on the second publisher.
14pub struct SignaturePublisherDelayer {
15    first_publisher: Arc<dyn SignaturePublisher>,
16    second_publisher: Arc<dyn SignaturePublisher>,
17    delay_between_publish: Duration,
18    logger: Logger,
19}
20
21impl SignaturePublisherDelayer {
22    /// Creates a new [SignaturePublisherDelayer]
23    pub fn new(
24        first_publisher: Arc<dyn SignaturePublisher>,
25        second_publisher: Arc<dyn SignaturePublisher>,
26        delay_between_publish: Duration,
27        logger: Logger,
28    ) -> Self {
29        Self {
30            first_publisher,
31            second_publisher,
32            delay_between_publish,
33            logger: logger.new_with_component_name::<Self>(),
34        }
35    }
36}
37
38#[async_trait::async_trait]
39impl SignaturePublisher for SignaturePublisherDelayer {
40    async fn publish(
41        &self,
42        signed_entity_type: &SignedEntityType,
43        signature: &SingleSignature,
44        protocol_message: &ProtocolMessage,
45    ) -> StdResult<()> {
46        if let Err(e) = self
47            .first_publisher
48            .publish(signed_entity_type, signature, protocol_message)
49            .await
50        {
51            error!(
52                self.logger,
53                "Delayer failed to publish first signature";
54                "error" => ?e
55            );
56        }
57
58        tokio::time::sleep(self.delay_between_publish).await;
59
60        if let Err(e) = self
61            .second_publisher
62            .publish(signed_entity_type, signature, protocol_message)
63            .await
64        {
65            error!(
66                self.logger,
67                "Delayer failed to publish second signature";
68                "error" => ?e
69            );
70            return Err(e);
71        }
72
73        Ok(())
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use mithril_common::{entities::Epoch, test_utils::fake_data};
80
81    use crate::{services::MockSignaturePublisher, test_tools::TestLogger};
82
83    use super::*;
84
85    #[tokio::test]
86    async fn should_call_both_publishers_when_first_succeeds() {
87        let mut first_publisher = MockSignaturePublisher::new();
88        first_publisher
89            .expect_publish()
90            .once()
91            .returning(|_, _, _| Ok(()));
92
93        let mut second_publisher = MockSignaturePublisher::new();
94        second_publisher
95            .expect_publish()
96            .once()
97            .returning(|_, _, _| Ok(()));
98
99        let delayer = SignaturePublisherDelayer::new(
100            Arc::new(first_publisher),
101            Arc::new(second_publisher),
102            Duration::from_millis(0),
103            TestLogger::stdout(),
104        );
105
106        delayer
107            .publish(
108                &SignedEntityType::MithrilStakeDistribution(Epoch(1)),
109                &fake_data::single_signature(vec![1]),
110                &ProtocolMessage::default(),
111            )
112            .await
113            .unwrap();
114    }
115
116    #[tokio::test]
117    async fn should_call_second_publisher_even_if_first_fails_and_log_error() {
118        let (logger, log_inspector) = TestLogger::memory();
119        let mut first_publisher = MockSignaturePublisher::new();
120        first_publisher
121            .expect_publish()
122            .once()
123            .returning(|_, _, _| Err(anyhow::anyhow!("first publisher failure")));
124
125        let mut second_publisher = MockSignaturePublisher::new();
126        second_publisher
127            .expect_publish()
128            .once()
129            .returning(|_, _, _| Ok(()));
130
131        let delayer = SignaturePublisherDelayer::new(
132            Arc::new(first_publisher),
133            Arc::new(second_publisher),
134            Duration::from_millis(0),
135            logger,
136        );
137
138        delayer
139            .publish(
140                &SignedEntityType::MithrilStakeDistribution(Epoch(1)),
141                &fake_data::single_signature(vec![1]),
142                &ProtocolMessage::default(),
143            )
144            .await
145            .unwrap();
146
147        assert!(log_inspector.contains_log("Delayer failed to publish first signature"));
148        assert!(log_inspector.contains_log("first publisher failure"));
149    }
150
151    #[tokio::test]
152    async fn should_return_and_log_error_if_second_publisher_fails() {
153        let (logger, log_inspector) = TestLogger::memory();
154        let mut first_publisher = MockSignaturePublisher::new();
155        first_publisher
156            .expect_publish()
157            .once()
158            .returning(|_, _, _| Ok(()));
159
160        let mut second_publisher = MockSignaturePublisher::new();
161        second_publisher
162            .expect_publish()
163            .once()
164            .returning(|_, _, _| Err(anyhow::anyhow!("second publisher failure")));
165
166        let delayer = SignaturePublisherDelayer::new(
167            Arc::new(first_publisher),
168            Arc::new(second_publisher),
169            Duration::from_millis(0),
170            logger,
171        );
172
173        delayer
174            .publish(
175                &SignedEntityType::MithrilStakeDistribution(Epoch(1)),
176                &fake_data::single_signature(vec![1]),
177                &ProtocolMessage::default(),
178            )
179            .await
180            .expect_err("Expected error when delayed publisher failed");
181
182        assert!(log_inspector.contains_log("Delayer failed to publish second signature"));
183        assert!(log_inspector.contains_log("second publisher failure"));
184    }
185
186    #[tokio::test]
187    async fn should_wait_before_calling_second_publisher() {
188        let mut first_publisher = MockSignaturePublisher::new();
189        first_publisher
190            .expect_publish()
191            .once()
192            .returning(|_, _, _| Ok(()));
193
194        let mut second_publisher = MockSignaturePublisher::new();
195        second_publisher
196            .expect_publish()
197            .once()
198            .returning(|_, _, _| Ok(()));
199
200        let delay = Duration::from_millis(50);
201        let delayer = SignaturePublisherDelayer::new(
202            Arc::new(first_publisher),
203            Arc::new(second_publisher),
204            delay,
205            TestLogger::stdout(),
206        );
207
208        let start_time = std::time::Instant::now();
209        delayer
210            .publish(
211                &SignedEntityType::MithrilStakeDistribution(Epoch(1)),
212                &fake_data::single_signature(vec![1]),
213                &ProtocolMessage::default(),
214            )
215            .await
216            .unwrap();
217
218        let elapsed_time = start_time.elapsed();
219        assert!(
220            elapsed_time >= delay,
221            "Expected at least {delay:?} time elapsed, but got {elapsed_time:?}"
222        );
223        assert!(
224            elapsed_time < delay * 2,
225            "Expected less than {:?} time elapsed, but got {:?}",
226            delay * 2,
227            elapsed_time
228        );
229    }
230}