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