Skip to main content

p2panda_sync/protocols/
log_sync.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Two-party sync protocol over append-only logs.
4use std::fmt::{Debug, Display};
5use std::marker::PhantomData;
6
7use futures_util::{Sink, SinkExt, Stream, StreamExt};
8use p2panda_core::logs::{LogHeights, LogRanges, Logs, compare_logs};
9use p2panda_core::{
10    AnyOperation, Body, Extensions, Hash, Header, LogId, Operation, RawOperation, SeqNum,
11    VerifyingKey,
12};
13use p2panda_store::logs::LogStore;
14use serde::{Deserialize, Serialize};
15use thiserror::Error;
16use tokio::select;
17use tokio::sync::broadcast;
18use tracing::{Instrument, debug, trace};
19
20use crate::api::{LogEntry, log_heights, log_ranges};
21use crate::dedup::{DEFAULT_BUFFER_CAPACITY, DeduplicationBuffer};
22use crate::traits::Protocol;
23
24/// Sync session life-cycle states.
25#[derive(Debug, Default)]
26enum State<L> {
27    /// Initialise session metrics and announce sync start on event stream.
28    #[default]
29    Start,
30
31    /// Calculate local log heights and send Have message to remote.
32    SendHave,
33
34    /// Receive have message from remote and calculate operation diff.
35    ReceiveHave { local: LogHeights<VerifyingKey, L> },
36
37    /// Send PreSync message to remote or Done if we have nothing to send.
38    SendPreSync {
39        remote_needs: LogRanges<VerifyingKey, L>,
40    },
41
42    /// Receive PreSync message from remote or Done if they have nothing to send.
43    ReceivePreSyncOrDone {
44        remote_needs: LogRanges<VerifyingKey, L>,
45        outbound_operations: u32,
46        outbound_bytes: u32,
47    },
48
49    /// Enter sync loop where we exchange operations with the remote, moves onto next state when
50    /// both peers have send Done messages.
51    Sync {
52        remote_needs: LogRanges<VerifyingKey, L>,
53        metrics: LogSyncMetrics,
54    },
55
56    /// Announce on the event stream that the sync session successfully completed.
57    End { metrics: LogSyncMetrics },
58}
59
60/// Efficient sync protocol for append-only log data types.
61#[derive(Debug)]
62pub struct LogSync<L, E, S, Evt> {
63    state: State<L>,
64    logs: Logs<VerifyingKey, L>,
65    store: S,
66    event_tx: broadcast::Sender<Evt>,
67    buffer_capacity: usize,
68    _marker: PhantomData<E>,
69}
70
71impl<L, E, S, Evt> LogSync<L, E, S, Evt> {
72    pub fn new(store: S, logs: Logs<VerifyingKey, L>, event_tx: broadcast::Sender<Evt>) -> Self {
73        Self::new_with_capacity(store, logs, event_tx, DEFAULT_BUFFER_CAPACITY)
74    }
75
76    pub fn new_with_capacity(
77        store: S,
78        logs: Logs<VerifyingKey, L>,
79        event_tx: broadcast::Sender<Evt>,
80        buffer_capacity: usize,
81    ) -> Self {
82        Self {
83            state: Default::default(),
84            store,
85            event_tx,
86            logs,
87            buffer_capacity,
88            _marker: PhantomData,
89        }
90    }
91}
92
93impl<L, E, S, Evt> Protocol for LogSync<L, E, S, Evt>
94where
95    L: LogId + Debug + Send + 'static,
96    E: Extensions + Send + 'static,
97    S: LogStore<AnyOperation, VerifyingKey, L, SeqNum, Hash> + Clone + Send + 'static,
98    Evt: Debug + From<LogSyncEvent<E>> + Send + 'static,
99{
100    type Error = LogSyncError;
101    type Output = (DeduplicationBuffer<Hash>, LogSyncMetrics);
102    type Message = LogSyncMessage<L>;
103
104    async fn run(
105        mut self,
106        sink: &mut (impl Sink<Self::Message, Error = impl Debug> + Unpin),
107        stream: &mut (impl Stream<Item = Result<Self::Message, impl Debug>> + Unpin),
108    ) -> Result<Self::Output, Self::Error> {
109        let mut sync_done_received = false;
110        let mut sync_done_sent = false;
111        let mut dedup = DeduplicationBuffer::new(self.buffer_capacity);
112        let state_machine_span = tracing::error_span!("log-sync");
113
114        let metrics = loop {
115            match self.state {
116                State::Start => {
117                    self.state = State::SendHave;
118                    trace!(parent: &state_machine_span, state = ?self.state, "Updated state");
119                }
120                State::SendHave => {
121                    let span = tracing::error_span!(parent: &state_machine_span, "send-have");
122                    let local = log_heights(&self.store, &self.logs)
123                        .instrument(span.clone())
124                        .await
125                        .map_err(|err| LogSyncError::LogStore(format!("{err}")))?;
126
127                    sink.send(LogSyncMessage::<L>::Have(local.clone()))
128                        .instrument(span.clone())
129                        .await
130                        .inspect_err(|error| debug!(parent: &span, ?error, "Failed to send LogSyncMessage::Have"))
131                        .map_err(|err| LogSyncError::MessageSink(format!("{err:?}")))?;
132
133                    self.state = State::ReceiveHave { local };
134                    trace!(parent: &state_machine_span, state = ?self.state, "Updated state");
135                }
136                State::ReceiveHave { local } => {
137                    let span = tracing::error_span!(parent: &state_machine_span, "receive-have");
138                    let Some(message) = stream.next().instrument(span.clone()).await else {
139                        debug!(parent: &span, "Stream unexpectedly closed");
140                        return Err(LogSyncError::UnexpectedStreamClosure);
141                    };
142                    let message = message
143                        .inspect_err(
144                            |error| debug!(parent: &span, ?error, "Message stream errored"),
145                        )
146                        .map_err(|err| LogSyncError::MessageStream(format!("{err:?}")))?;
147
148                    let LogSyncMessage::Have(remote) = message else {
149                        debug!(parent: &span, ?message, "Unexpected message type");
150                        return Err(LogSyncError::UnexpectedMessage(message.to_string()));
151                    };
152
153                    let remote_needs = compare_logs(&local, &remote);
154
155                    self.state = State::SendPreSync { remote_needs };
156                    trace!(parent: &state_machine_span, state = ?self.state, "Updated state");
157                }
158                State::SendPreSync { remote_needs } => {
159                    let span = tracing::error_span!(
160                        parent: &state_machine_span,
161                        "send-pre-sync",
162                        total_operations = tracing::field::Empty,
163                        total_bytes = tracing::field::Empty
164                    );
165                    let mut outbound_operations = 0;
166                    let mut outbound_bytes = 0;
167                    for (verifying_key, log_range) in remote_needs.iter() {
168                        for (log_id, (after, until)) in log_range.iter() {
169                            if let Some((inner_outbound_operations, inner_outbound_bytes)) = self
170                                .store
171                                .get_log_size(verifying_key, log_id, *after, *until)
172                                .instrument(span.clone())
173                                .await
174                                .inspect_err(
175                                    |error| debug!(parent: &span, ?error, "Log sync error"),
176                                )
177                                .map_err(|err| LogSyncError::OperationStore(format!("{err}")))?
178                            {
179                                outbound_operations += inner_outbound_operations;
180                                outbound_bytes += inner_outbound_bytes;
181                            };
182                        }
183                    }
184
185                    span.record("total_operations", outbound_operations);
186                    span.record("total_bytes", outbound_bytes);
187
188                    let message = if outbound_bytes > 0 {
189                        LogSyncMessage::PreSync {
190                            total_operations: outbound_operations,
191                            total_bytes: outbound_bytes,
192                        }
193                    } else {
194                        sync_done_sent = true;
195                        LogSyncMessage::Done
196                    };
197
198                    sink.send(message)
199                        .instrument(span.clone())
200                        .await
201                        .inspect_err(|error| debug!(parent: &span, ?error, "Failed to send LogSyncMessage::PreSync"))
202                        .map_err(|err| LogSyncError::MessageSink(format!("{err:?}")))?;
203
204                    self.state = State::ReceivePreSyncOrDone {
205                        remote_needs,
206                        outbound_operations,
207                        outbound_bytes,
208                    };
209                    trace!(parent: &state_machine_span, state = ?self.state, "Updated state");
210                }
211                State::ReceivePreSyncOrDone {
212                    remote_needs,
213                    outbound_operations,
214                    outbound_bytes,
215                } => {
216                    let span = tracing::error_span!(
217                        parent: &state_machine_span,
218                        "receive-pre-sync-or-done",
219                        outbound_operations,
220                        outbound_bytes,
221                        local_ops = tracing::field::Empty,
222                        remote_ops = tracing::field::Empty,
223                        local_bytes = tracing::field::Empty,
224                        remote_bytes = tracing::field::Empty,
225
226                    );
227                    let Some(message) = stream.next().instrument(span.clone()).await else {
228                        debug!(parent: &span, "Stream closed unexpectedly");
229                        return Err(LogSyncError::UnexpectedStreamClosure);
230                    };
231                    let entered = span.enter();
232                    let message = message
233                        .inspect_err(|error| debug!(?error, "Log sync error"))
234                        .map_err(|err| LogSyncError::MessageStream(format!("{err:?}")))?;
235
236                    let (inbound_operations, inbound_bytes) = match message {
237                        LogSyncMessage::PreSync {
238                            total_operations,
239                            total_bytes,
240                        } => (total_operations, total_bytes),
241                        LogSyncMessage::Done => {
242                            sync_done_received = true;
243                            (0, 0)
244                        }
245                        message => {
246                            debug!(?message, "Unexpected message type");
247                            return Err(LogSyncError::UnexpectedMessage(message.to_string()));
248                        }
249                    };
250
251                    span.record("local_ops", outbound_operations);
252                    span.record("remote_ops", inbound_operations);
253                    span.record("local_bytes", outbound_bytes);
254                    span.record("remote_bytes", inbound_bytes);
255                    debug!("sync metrics exchanged");
256
257                    let metrics = LogSyncMetrics {
258                        outbound_operations,
259                        outbound_bytes,
260                        inbound_operations,
261                        inbound_bytes,
262                        sent_operations: 0,
263                        sent_bytes: 0,
264                        received_operations: 0,
265                        received_bytes: 0,
266                    };
267
268                    self.event_tx
269                        .send(
270                            LogSyncEvent::MetricsExchanged {
271                                metrics: metrics.clone(),
272                            }
273                            .into(),
274                        )
275                        .inspect_err(|error| {
276                            debug!(?error, "Failed to send LogSyncEvent::MetricsExchanged")
277                        })
278                        .map_err(|_| LogSyncError::BroadcastSend)?;
279
280                    self.state = State::Sync {
281                        remote_needs,
282                        metrics,
283                    };
284                    drop(entered);
285                    trace!(parent: &state_machine_span, state = ?self.state, "Updated state");
286                }
287                State::Sync {
288                    remote_needs,
289                    mut metrics,
290                } => {
291                    let span = tracing::error_span!(parent: &state_machine_span, "sync");
292                    let mut operations = log_ranges(&self.store, remote_needs);
293
294                    // We perform a loop awaiting futures on both the receiving and sending
295                    // stream. This means that processing of both streams is done concurrently.
296                    loop {
297                        select! {
298                            Some(message) = stream.next().instrument(span.clone()), if !sync_done_received => {
299                                let message =
300                                    message
301                                    .inspect_err(|error| debug!(parent: &span, ?error, "Log sync error"))
302                                    .map_err(|err| LogSyncError::MessageStream(format!("{err:?}")))?;
303
304                                match message {
305                                    LogSyncMessage::Operation((header, body)) => {
306                                        metrics.received_bytes += {
307                                            header.len()
308                                                + body.as_ref().map(|bytes| bytes.len()).unwrap_or_default()
309                                        } as u32;
310                                        metrics.received_operations += 1;
311
312                                        let header = Header::<E>::decode(&header)?;
313                                        let body = body.map(|ref bytes| Body::from_bytes(bytes));
314
315                                        // Insert message hash into deduplication buffer.
316                                        if !dedup.insert(header.hash()) {
317                                            trace!(
318                                                parent: &span,
319                                                operation_id = %header.hash().fmt_short(),
320                                                "ignore duplicate operation sent from remote"
321                                            );
322
323                                            continue;
324                                        }
325
326                                        trace!(
327                                            parent: &span,
328                                            id = ?header.hash().fmt_short(),
329                                            received_ops = metrics.received_operations,
330                                            received_bytes = metrics.received_bytes,
331                                            "received operation"
332                                        );
333
334                                        // Forward data received from the remote to the app layer.
335                                        self.event_tx
336                                            .send(
337                                                LogSyncEvent::OperationReceived {
338                                                    operation: Box::new(Operation {
339                                                        hash: header.hash(),header,body,
340                                                    }),
341                                                    metrics: metrics.clone()
342                                                }
343                                                .into(),
344                                            )
345                                            .inspect_err(|error| debug!(parent: &span, ?error, "Sending Broadcast OperationReceived failed"))
346                                            .map_err(|_| LogSyncError::BroadcastSend)?;
347                                    }
348                                    LogSyncMessage::Done => {
349                                        trace!(parent: &span, "received done message");
350                                        sync_done_received = true;
351                                    }
352                                    message => {
353                                        return Err(LogSyncError::UnexpectedMessage(message.to_string()));
354                                    }
355                                }
356                            },
357                            result = operations.next().instrument(span.clone()), if !sync_done_sent => {
358                                let Some(result) = result else {
359                                    trace!(
360                                        parent: &span,
361                                        "send sync done message",
362                                    );
363                                    sink.send(LogSyncMessage::Done)
364                                        .instrument(span.clone())
365                                        .await
366                                        .inspect_err(|error| debug!(parent: &span, ?error, "Failed to send LogSyncMessage::Done"))
367                                        .map_err(|err| LogSyncError::MessageSink(format!("{err:?}")))?;
368                                    sync_done_sent = true;
369                                    continue;
370                                };
371
372                                let LogEntry { entry: operation, bytes: header_bytes, .. } = result.map_err(|err| LogSyncError::LogStore(format!("{err}")))?;
373
374                                let header = operation.header;
375                                let body = operation.body;
376                                let hash = operation.hash;
377                                let author = header.verifying_key;
378
379                                metrics.sent_bytes += { header_bytes.len() +
380                                    body.as_ref().map(|body|
381                                body.to_bytes().len()).unwrap_or_default() } as u32;
382                                metrics.sent_operations += 1;
383
384                                trace!(
385                                    parent: &span,
386                                    verifying_key = %author.fmt_short(),
387                                    seq_num = header.seq_num,
388                                    id = %hash.fmt_short(),
389                                    sent_ops = metrics.sent_operations,
390                                    sent_bytes = metrics.sent_bytes,
391                                    "send operation",
392                                );
393
394                                sink.send(LogSyncMessage::Operation((header_bytes, body.as_ref().map(|body|
395                                    body.to_bytes()))))
396                                    .instrument(span.clone())
397                                    .await
398                                    .inspect_err(|error| debug!(parent: &span, ?error, "Failed to send operation"))
399                                    .map_err(|err| LogSyncError::MessageSink(format!("{err:?}")))?;
400
401                                dedup.insert(hash);
402                            },
403                            else => {
404                                trace!(parent: &span, "sync session gracefully closed");
405                                break;
406                        }
407                        }
408                    }
409                    self.state = State::End { metrics };
410                    trace!(parent: &state_machine_span, state = ?self.state, "Updated state");
411                }
412                State::End { metrics } => {
413                    trace!(parent: &state_machine_span, "End-state reached");
414                    break metrics;
415                }
416            }
417        };
418
419        Ok((dedup, metrics))
420    }
421}
422
423/// Protocol messages.
424#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
425#[serde(bound(deserialize = "L: LogId"))]
426pub enum LogSyncMessage<L>
427where
428    L: LogId,
429{
430    Have(LogHeights<VerifyingKey, L>),
431    PreSync {
432        total_operations: u32,
433        total_bytes: u32,
434    },
435    Operation(RawOperation),
436    Done,
437}
438
439impl<L> Display for LogSyncMessage<L>
440where
441    L: LogId,
442{
443    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
444        let value = match self {
445            LogSyncMessage::Have(_) => "have",
446            LogSyncMessage::PreSync { .. } => "pre_sync",
447            LogSyncMessage::Operation(_) => "operation",
448            LogSyncMessage::Done => "done",
449        };
450
451        write!(f, "{value}")
452    }
453}
454
455/// Events emitted from log sync sessions.
456#[derive(Clone, Debug, PartialEq)]
457pub enum LogSyncEvent<E> {
458    /// We have calculated and sent the estimated bytes and operations to be sent during this sync
459    /// session and received the remotes' estimates.
460    ///
461    /// This is the first event to occur and will only be sent once.
462    MetricsExchanged { metrics: LogSyncMetrics },
463
464    /// An operation has been received from the remote.
465    OperationReceived {
466        operation: Box<Operation<E>>,
467        metrics: LogSyncMetrics,
468    },
469}
470
471/// Sync metrics emitted in event messages.
472#[derive(Clone, Debug, PartialEq, Default)]
473pub struct LogSyncMetrics {
474    pub outbound_operations: u32,
475    pub outbound_bytes: u32,
476    pub inbound_operations: u32,
477    pub inbound_bytes: u32,
478    pub sent_operations: u32,
479    pub sent_bytes: u32,
480    pub received_operations: u32,
481    pub received_bytes: u32,
482}
483
484/// Protocol error types.
485#[derive(Debug, Error)]
486pub enum LogSyncError {
487    #[error("failed decoding incoming header bytes: {0}")]
488    DecodeHeader(#[from] p2panda_core::operation::HeaderError),
489
490    #[error("log store error: {0}")]
491    LogStore(String),
492
493    #[error("operation store error: {0}")]
494    OperationStore(String),
495
496    #[error("no active receivers when broadcasting")]
497    BroadcastSend,
498
499    #[error("log sync error sending on message sink: {0}")]
500    MessageSink(String),
501
502    #[error("log sync error receiving from message stream: {0}")]
503    MessageStream(String),
504
505    #[error("remote unexpectedly closed stream during initial sync")]
506    UnexpectedStreamClosure,
507
508    #[error("log sync received unexpected protocol message: {0}")]
509    UnexpectedMessage(String),
510}
511
512/// Returns a displayable string representing the underlying value in a short format, easy to read
513/// during debugging and logging.
514pub trait ShortFormat {
515    fn fmt_short(&self) -> String;
516}
517
518impl ShortFormat for VerifyingKey {
519    fn fmt_short(&self) -> String {
520        self.to_hex()[0..10].to_string()
521    }
522}
523
524impl ShortFormat for Hash {
525    fn fmt_short(&self) -> String {
526        self.to_hex()[0..5].to_string()
527    }
528}
529
530#[cfg(test)]
531mod tests {
532    use std::collections::BTreeMap;
533
534    use futures_channel::mpsc;
535    use futures_util::StreamExt;
536    use p2panda_core::test_utils::setup_logging;
537    use p2panda_core::{Body, Hash};
538    use p2panda_store::operations::OperationStore;
539    use p2panda_store::{SqliteStore, tx_unwrap};
540
541    use crate::protocols::log_sync::{LogSyncError, LogSyncEvent, Logs, Operation};
542    use crate::test_utils::{Peer, TestLogId, TestLogSyncMessage, run_protocol, run_protocol_uni};
543    use crate::traits::Protocol;
544
545    #[tokio::test]
546    #[test_log::test]
547    async fn log_sync_no_operations() {
548        let mut peer: Peer = Peer::new(0).await;
549
550        let (session, mut event_rx) = peer.log_sync_protocol(&Logs::default());
551        let ((_, metrics), mut remote_message_rx) = run_protocol_uni(
552            session,
553            &[
554                TestLogSyncMessage::Have(BTreeMap::default()),
555                TestLogSyncMessage::Done,
556            ],
557        )
558        .await
559        .unwrap();
560
561        let recv = event_rx.recv().await.unwrap();
562        let LogSyncEvent::MetricsExchanged {
563            metrics: event_metrics,
564        } = recv
565        else {
566            panic!("Not a LogSyncEvent::MetricsExchanged: {recv:?}");
567        };
568
569        assert_eq!(metrics.outbound_operations, 0);
570        assert_eq!(metrics.outbound_bytes, 0);
571        assert_eq!(metrics.inbound_operations, 0);
572        assert_eq!(metrics.inbound_bytes, 0);
573
574        assert_eq!(event_metrics, metrics);
575
576        assert_eq!(
577            remote_message_rx.recv().await.unwrap(),
578            TestLogSyncMessage::Have(BTreeMap::default())
579        );
580
581        assert_eq!(
582            remote_message_rx.recv().await.unwrap(),
583            TestLogSyncMessage::Done
584        );
585    }
586
587    #[tokio::test]
588    #[test_log::test]
589    async fn log_sync_some_operations() {
590        let mut peer = Peer::new(0).await;
591        let log_id = 0;
592
593        let body = Body::from_bytes(b"Hello, Sloth!");
594        let (header_0, header_bytes_0) = peer.create_operation(&body, log_id).await;
595        let (header_1, header_bytes_1) = peer.create_operation(&body, log_id).await;
596        let (header_2, header_bytes_2) = peer.create_operation(&body, log_id).await;
597
598        let mut logs = Logs::default();
599        logs.insert(peer.id(), vec![log_id]);
600
601        let (session, mut event_rx) = peer.log_sync_protocol(&logs);
602        let ((_, final_metrics), remote_message_rx) = run_protocol_uni(
603            session,
604            &[
605                TestLogSyncMessage::Have(BTreeMap::default()),
606                TestLogSyncMessage::Done,
607            ],
608        )
609        .await
610        .unwrap();
611
612        let expected_bytes = header_0.payload_size
613            + header_bytes_0.len() as u32
614            + header_1.payload_size
615            + header_bytes_1.len() as u32
616            + header_2.payload_size
617            + header_bytes_2.len() as u32;
618
619        let recv = event_rx.recv().await.unwrap();
620        let LogSyncEvent::MetricsExchanged { metrics } = recv else {
621            panic!("Not a LogSyncEvent::MetricsExchanged: {recv:?}");
622        };
623
624        assert_eq!(metrics.outbound_operations, 3);
625        assert_eq!(metrics.outbound_bytes, expected_bytes);
626        assert_eq!(metrics.inbound_operations, 0);
627        assert_eq!(metrics.inbound_bytes, 0);
628
629        assert_eq!(final_metrics.sent_operations, 3);
630        assert_eq!(final_metrics.sent_bytes, expected_bytes);
631        assert_eq!(final_metrics.received_operations, 0);
632        assert_eq!(final_metrics.received_bytes, 0);
633
634        let messages = remote_message_rx.collect::<Vec<_>>().await;
635
636        assert_eq!(messages.len(), 6);
637
638        assert_eq!(
639            messages[0],
640            TestLogSyncMessage::Have(BTreeMap::from([(peer.id(), BTreeMap::from([(0, 2)]))]))
641        );
642
643        assert_eq!(
644            messages[1],
645            TestLogSyncMessage::PreSync {
646                total_operations: 3,
647                total_bytes: expected_bytes
648            }
649        );
650
651        let TestLogSyncMessage::Operation((header, Some(body_inner))) = &messages[2] else {
652            panic!("Not a TestLogSyncMessage::Operation: {:?}", &messages[2]);
653        };
654        let (header, body_inner) = (header.clone(), body_inner.clone());
655        assert_eq!(header, header_bytes_0);
656        assert_eq!(Body::from_bytes(&body_inner), body);
657
658        let TestLogSyncMessage::Operation((header, Some(body_inner))) = &messages[3] else {
659            panic!("Not a TestLogSyncMessage::Operation: {:?}", &messages[3]);
660        };
661        let (header, body_inner) = (header.clone(), body_inner.clone());
662        assert_eq!(header, header_bytes_1);
663        assert_eq!(Body::from_bytes(&body_inner), body);
664
665        let TestLogSyncMessage::Operation((header, Some(body_inner))) = &messages[4] else {
666            panic!("Not a TestLogSyncMessage::Operation: {:?}", &messages[4]);
667        };
668        let (header, body_inner) = (header.clone(), body_inner.clone());
669        assert_eq!(header, header_bytes_2);
670        assert_eq!(Body::from_bytes(&body_inner), body);
671
672        assert_eq!(messages[5], TestLogSyncMessage::Done);
673    }
674
675    #[tokio::test]
676    #[test_log::test]
677    async fn log_sync_bidirectional_exchange() {
678        setup_logging();
679
680        const LOG_ID: TestLogId = 0;
681
682        let mut peer_a = Peer::new(0).await;
683        let mut peer_b = Peer::new(1).await;
684
685        let body_a = Body::from_bytes("From Alice".as_bytes());
686        let body_b = Body::from_bytes("From Bob".as_bytes());
687
688        let (header_a0, _) = peer_a.create_operation(&body_a, LOG_ID).await;
689        let (header_a1, _) = peer_a.create_operation(&body_a, LOG_ID).await;
690
691        let (header_b0, _) = peer_b.create_operation(&body_b, LOG_ID).await;
692        let (header_b1, _) = peer_b.create_operation(&body_b, LOG_ID).await;
693
694        let mut logs = Logs::default();
695        logs.insert(peer_a.id(), vec![LOG_ID]);
696        logs.insert(peer_b.id(), vec![LOG_ID]);
697
698        let (a_session, mut peer_a_event_rx) = peer_a.log_sync_protocol(&logs);
699        let (b_session, mut peer_b_event_rx) = peer_b.log_sync_protocol(&logs);
700
701        let ((_, local_metrics), (_, remote_metrics)) =
702            run_protocol(a_session, b_session).await.unwrap();
703
704        std::assert_matches!(
705            peer_a_event_rx.recv().await.unwrap(),
706            LogSyncEvent::MetricsExchanged { .. }
707        );
708
709        let recv = peer_a_event_rx.recv().await.unwrap();
710        let LogSyncEvent::OperationReceived { operation, .. } = recv else {
711            panic!("Not a LogSyncEvent::OperationReceived: {recv:?}");
712        };
713        let Operation {
714            header,
715            body: body_inner,
716            ..
717        } = *operation;
718        assert_eq!(header, header_b0);
719        assert_eq!(body_inner.unwrap(), body_b);
720
721        let recv = peer_a_event_rx.recv().await.unwrap();
722        let LogSyncEvent::OperationReceived { operation, .. } = recv else {
723            panic!("Not a LogSyncEvent::OperationReceived: {recv:?}");
724        };
725        let Operation {
726            header,
727            body: body_inner,
728            ..
729        } = *operation;
730        assert_eq!(header, header_b1);
731        assert_eq!(body_inner.unwrap(), body_b);
732
733        std::assert_matches!(
734            peer_b_event_rx.recv().await.unwrap(),
735            LogSyncEvent::MetricsExchanged { .. }
736        );
737
738        let recv = peer_b_event_rx.recv().await.unwrap();
739        let LogSyncEvent::OperationReceived { operation, .. } = recv else {
740            panic!("Not a LogSyncEvent::OperationReceived: {recv:?}");
741        };
742        let Operation {
743            header,
744            body: body_inner,
745            ..
746        } = *operation;
747        assert_eq!(header, header_a0);
748        assert_eq!(body_inner.unwrap(), body_a);
749
750        let recv = peer_b_event_rx.recv().await.unwrap();
751        let LogSyncEvent::OperationReceived { operation, .. } = recv else {
752            panic!("Not a LogSyncEvent::OperationReceived: {recv:?}");
753        };
754        let Operation {
755            header,
756            body: body_inner,
757            ..
758        } = *operation;
759        assert_eq!(header, header_a1);
760        assert_eq!(body_inner.unwrap(), body_a);
761
762        assert_eq!(
763            local_metrics.inbound_operations,
764            local_metrics.received_operations
765        );
766        assert_eq!(local_metrics.inbound_bytes, local_metrics.received_bytes);
767        assert_eq!(
768            local_metrics.outbound_operations,
769            local_metrics.sent_operations
770        );
771        assert_eq!(local_metrics.outbound_bytes, local_metrics.sent_bytes);
772        assert_eq!(local_metrics.outbound_bytes, remote_metrics.inbound_bytes);
773        assert_eq!(
774            local_metrics.outbound_operations,
775            remote_metrics.inbound_operations
776        );
777    }
778
779    #[tokio::test]
780    #[test_log::test]
781    async fn log_sync_unexpected_operation_before_presend() {
782        let mut peer = Peer::new(0).await;
783        const LOG_ID: TestLogId = 1;
784
785        let body = Body::from_bytes(b"unexpected op before presend");
786        let (_, header_bytes) = peer.create_operation(&body, LOG_ID).await;
787
788        let mut logs = Logs::default();
789        logs.insert(peer.id(), vec![LOG_ID]);
790
791        let (session, _event_rx) = peer.log_sync_protocol(&logs);
792
793        let messages = vec![
794            TestLogSyncMessage::Have(BTreeMap::from([(peer.id(), BTreeMap::from([(LOG_ID, 0)]))])),
795            TestLogSyncMessage::Operation((header_bytes.clone(), Some(body.to_bytes()))),
796            TestLogSyncMessage::PreSync {
797                total_operations: 1,
798                total_bytes: 100,
799            },
800            TestLogSyncMessage::Done,
801        ];
802
803        let result = run_protocol_uni(session, &messages).await;
804
805        std::assert_matches!(result, Err(LogSyncError::UnexpectedMessage(_)));
806    }
807
808    #[tokio::test]
809    #[test_log::test]
810    async fn log_sync_unexpected_presend_twice() {
811        let mut peer = Peer::new(0).await;
812        const LOG_ID: TestLogId = 1;
813
814        let body = Body::from_bytes(b"two presends");
815        peer.create_operation(&body, LOG_ID).await;
816
817        let mut logs = Logs::default();
818        logs.insert(peer.id(), vec![LOG_ID]);
819
820        let (session, _event_rx) = peer.log_sync_protocol(&logs);
821
822        let messages = vec![
823            TestLogSyncMessage::Have(BTreeMap::from([(peer.id(), BTreeMap::from([(LOG_ID, 0)]))])),
824            TestLogSyncMessage::PreSync {
825                total_operations: 1,
826                total_bytes: 32,
827            },
828            TestLogSyncMessage::PreSync {
829                total_operations: 1,
830                total_bytes: 32,
831            },
832            TestLogSyncMessage::Done,
833        ];
834
835        let result = run_protocol_uni(session, &messages).await;
836
837        std::assert_matches!(result, Err(LogSyncError::UnexpectedMessage(_)));
838    }
839
840    #[tokio::test]
841    #[test_log::test]
842    async fn log_sync_unexpected_done_before_anything() {
843        let mut peer = Peer::new(0).await;
844        let logs = Logs::default();
845
846        let (session, _event_rx) = peer.log_sync_protocol(&logs);
847
848        let messages = vec![TestLogSyncMessage::Done];
849        let result = run_protocol_uni(session, &messages).await;
850
851        std::assert_matches!(result, Err(LogSyncError::UnexpectedMessage(_)));
852    }
853
854    #[tokio::test]
855    #[test_log::test]
856    async fn log_sync_unexpected_have_after_presend() {
857        let mut peer = Peer::new(0).await;
858        const LOG_ID: TestLogId = 1;
859
860        let body = Body::from_bytes(b"bad have order");
861        peer.create_operation(&body, LOG_ID).await;
862
863        let mut logs = Logs::default();
864        logs.insert(peer.id(), vec![LOG_ID]);
865
866        let (session, _event_rx) = peer.log_sync_protocol(&logs);
867
868        let messages = vec![
869            TestLogSyncMessage::Have(BTreeMap::from([(peer.id(), BTreeMap::from([(LOG_ID, 0)]))])),
870            TestLogSyncMessage::PreSync {
871                total_operations: 1,
872                total_bytes: 64,
873            },
874            TestLogSyncMessage::Have(BTreeMap::from([(
875                peer.id(),
876                BTreeMap::from([(LOG_ID, 99)]),
877            )])),
878            TestLogSyncMessage::Done,
879        ];
880
881        let result = run_protocol_uni(session, &messages).await;
882
883        std::assert_matches!(result, Err(LogSyncError::UnexpectedMessage(_)));
884    }
885
886    #[tokio::test]
887    #[test_log::test]
888    async fn log_sync_with_concurrently_pruned_log() {
889        setup_logging();
890
891        let mut peer_a = Peer::new(0).await;
892        let mut peer_b = Peer::new(1).await;
893        let mut peer_c = Peer::new(2).await;
894
895        let body = Body::from_bytes(&[0; 1000]);
896
897        for _ in 0..100 {
898            let _ = peer_a.create_operation(&body, 0).await;
899        }
900
901        for _ in 0..100 {
902            let _ = peer_a.create_operation(&body, 1).await;
903        }
904
905        let mut to_be_pruned_log = vec![];
906
907        for _ in 0..50 {
908            let (header, _) = peer_c.create_operation(&body, 0).await;
909            let id = header.hash();
910
911            let operation = Operation {
912                hash: header.hash(),
913                header,
914                body: Some(body.clone()),
915            };
916
917            tx_unwrap!(
918                &peer_a.store,
919                peer_a
920                    .store
921                    .insert_operation_tx(&id, &operation, &0)
922                    .await
923                    .unwrap()
924            );
925
926            to_be_pruned_log.push(id);
927        }
928
929        let mut logs = Logs::default();
930        logs.insert(peer_a.id(), vec![0, 1]);
931        logs.insert(peer_c.id(), vec![0]);
932
933        let (a_session, _peer_a_event_rx) = peer_a.log_sync_protocol(&logs);
934        let (b_session, mut peer_b_event_rx) = peer_b.log_sync_protocol(&logs);
935
936        let _peer_b_event_tx_clone = b_session.event_tx.clone();
937
938        // Spawn a task to run the sync session.
939        let session_b_handle = {
940            let (mut local_message_tx, local_message_rx) = mpsc::channel(512);
941            let (mut remote_message_tx, remote_message_rx) = mpsc::channel(512);
942            let mut local_message_rx = local_message_rx.map(Ok::<_, ()>);
943            let mut remote_message_rx = remote_message_rx.map(Ok::<_, ()>);
944            tokio::spawn(async move {
945                a_session
946                    .run(&mut local_message_tx, &mut remote_message_rx)
947                    .await
948                    .unwrap();
949            });
950            tokio::spawn(async move {
951                b_session
952                    .run(&mut remote_message_tx, &mut local_message_rx)
953                    .await
954                    .unwrap()
955            })
956        };
957
958        loop {
959            let event = peer_b_event_rx.recv().await.unwrap();
960
961            if let LogSyncEvent::OperationReceived { .. } = event {
962                tx_unwrap!(&peer_a.store, {
963                    <SqliteStore as OperationStore<Operation<()>, Hash>>::delete_operation_tx(
964                        &peer_a.store,
965                        &to_be_pruned_log[0],
966                    )
967                    .await
968                    .unwrap();
969                });
970
971                break;
972            }
973        }
974
975        let (_, metrics) = session_b_handle.await.unwrap();
976        assert_eq!(metrics.inbound_operations, 250);
977        assert_eq!(metrics.received_operations, 249);
978    }
979}