1use std::collections::BTreeMap;
5use std::fmt::{Debug, Display};
6use std::marker::PhantomData;
7
8use futures::{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, 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: u64,
47 outbound_bytes: u64,
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, u64, 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 u64;
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 u64;
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, u64, 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"))]
408#[serde(tag = "type", content = "value")]
409pub enum LogSyncMessage<L>
410where
411 L: LogId,
412{
413 Have(LogHeights<VerifyingKey, L>),
414 PreSync {
415 total_operations: u64,
416 total_bytes: u64,
417 },
418 Operation(Vec<u8>, Option<Vec<u8>>),
419 Done,
420}
421
422impl<L> Display for LogSyncMessage<L>
423where
424 L: LogId,
425{
426 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
427 let value = match self {
428 LogSyncMessage::Have(_) => "have",
429 LogSyncMessage::PreSync { .. } => "pre_sync",
430 LogSyncMessage::Operation(_, _) => "operation",
431 LogSyncMessage::Done => "done",
432 };
433
434 write!(f, "{value}")
435 }
436}
437
438#[derive(Clone, Debug, PartialEq)]
440pub enum LogSyncEvent<E> {
441 MetricsExchanged { metrics: LogSyncMetrics },
446
447 OperationReceived {
449 operation: Box<Operation<E>>,
450 metrics: LogSyncMetrics,
451 },
452}
453
454#[derive(Clone, Debug, PartialEq, Default)]
456pub struct LogSyncMetrics {
457 pub outbound_operations: u64,
458 pub outbound_bytes: u64,
459 pub inbound_operations: u64,
460 pub inbound_bytes: u64,
461 pub sent_operations: u64,
462 pub sent_bytes: u64,
463 pub received_operations: u64,
464 pub received_bytes: u64,
465}
466
467#[derive(Debug, Error)]
469pub enum LogSyncError {
470 #[error(transparent)]
471 Decode(#[from] DecodeError),
472
473 #[error("log store error: {0}")]
474 LogStore(String),
475
476 #[error("operation store error: {0}")]
477 OperationStore(String),
478
479 #[error("no active receivers when broadcasting")]
480 BroadcastSend,
481
482 #[error("log sync error sending on message sink: {0}")]
483 MessageSink(String),
484
485 #[error("log sync error receiving from message stream: {0}")]
486 MessageStream(String),
487
488 #[error("remote unexpectedly closed stream during initial sync")]
489 UnexpectedStreamClosure,
490
491 #[error("log sync received unexpected protocol message: {0}")]
492 UnexpectedMessage(String),
493}
494
495pub trait ShortFormat {
498 fn fmt_short(&self) -> String;
499}
500
501impl ShortFormat for VerifyingKey {
502 fn fmt_short(&self) -> String {
503 self.to_hex()[0..10].to_string()
504 }
505}
506
507impl ShortFormat for Hash {
508 fn fmt_short(&self) -> String {
509 self.to_hex()[0..5].to_string()
510 }
511}
512
513#[cfg(test)]
514mod tests {
515 use std::collections::BTreeMap;
516
517 use assert_matches::assert_matches;
518 use futures::StreamExt;
519 use futures::channel::mpsc;
520 use p2panda_core::{Body, Hash};
521 use p2panda_store::operations::OperationStore;
522 use p2panda_store::{SqliteStore, tx_unwrap};
523
524 use crate::protocols::log_sync::{LogSyncError, LogSyncEvent, Logs, Operation};
525 use crate::test_utils::{
526 Peer, TestLogSyncMessage, run_protocol, run_protocol_uni, setup_logging,
527 };
528 use crate::traits::Protocol;
529
530 #[tokio::test]
531 async fn log_sync_no_operations() {
532 let mut peer: Peer = Peer::new(0).await;
533
534 let (session, mut event_rx) = peer.log_sync_protocol(&Logs::default());
535 let ((_, metrics), mut remote_message_rx) = run_protocol_uni(
536 session,
537 &[
538 TestLogSyncMessage::Have(BTreeMap::default()),
539 TestLogSyncMessage::Done,
540 ],
541 )
542 .await
543 .unwrap();
544
545 let event_metrics = assert_matches!(
546 event_rx.recv().await.unwrap(),
547 LogSyncEvent::MetricsExchanged { metrics } => metrics
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 u64
594 + header_1.payload_size
595 + header_bytes_1.len() as u64
596 + header_2.payload_size
597 + header_bytes_2.len() as u64;
598
599 let metrics = assert_matches!(
600 event_rx.recv().await.unwrap(),
601 LogSyncEvent::MetricsExchanged { metrics } => metrics
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 (header, body_inner) = assert_matches!(
632 &messages[2],
633 TestLogSyncMessage::Operation(header, Some(body)) => (header.clone(), body.clone())
634 );
635 assert_eq!(header, header_bytes_0);
636 assert_eq!(Body::new(&body_inner), body);
637
638 let (header, body_inner) = assert_matches!(
639 &messages[3],
640 TestLogSyncMessage::Operation(header, Some(body)) => (header.clone(), body.clone())
641 );
642 assert_eq!(header, header_bytes_1);
643 assert_eq!(Body::new(&body_inner), body);
644
645 let (header, body_inner) = assert_matches!(
646 &messages[4],
647 TestLogSyncMessage::Operation(header, Some(body)) => (header.clone(), body.clone())
648 );
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: u64 = 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 assert_matches!(
684 peer_a_event_rx.recv().await.unwrap(),
685 LogSyncEvent::MetricsExchanged { .. }
686 );
687
688 let (header, body_inner) = assert_matches!(
689 peer_a_event_rx.recv().await.unwrap(),
690 LogSyncEvent::OperationReceived { operation, .. } => {
691 let Operation { header, body, .. } = *operation;
692 (header, body)
693 }
694 );
695 assert_eq!(header, header_b0);
696 assert_eq!(body_inner.unwrap(), body_b);
697
698 let (header, body_inner) = assert_matches!(
699 peer_a_event_rx.recv().await.unwrap(),
700 LogSyncEvent::OperationReceived { operation, .. } => {
701 let Operation { header, body, .. } = *operation;
702 (header, body)
703 }
704 );
705 assert_eq!(header, header_b1);
706 assert_eq!(body_inner.unwrap(), body_b);
707
708 assert_matches!(
709 peer_b_event_rx.recv().await.unwrap(),
710 LogSyncEvent::MetricsExchanged { .. }
711 );
712
713 let (header, body_inner) = assert_matches!(
714 peer_b_event_rx.recv().await.unwrap(),
715 LogSyncEvent::OperationReceived { operation, .. } => {
716 let Operation { header, body, .. } = *operation;
717 (header, body)
718 }
719 );
720 assert_eq!(header, header_a0);
721 assert_eq!(body_inner.unwrap(), body_a);
722
723 let (header, body_inner) = assert_matches!(
724 peer_b_event_rx.recv().await.unwrap(),
725 LogSyncEvent::OperationReceived { operation, .. } => {
726 let Operation { header, body, .. } = *operation;
727 (header, body)
728 }
729 );
730 assert_eq!(header, header_a1);
731 assert_eq!(body_inner.unwrap(), body_a);
732
733 assert_eq!(
734 local_metrics.inbound_operations,
735 local_metrics.received_operations
736 );
737 assert_eq!(local_metrics.inbound_bytes, local_metrics.received_bytes);
738 assert_eq!(
739 local_metrics.outbound_operations,
740 local_metrics.sent_operations
741 );
742 assert_eq!(local_metrics.outbound_bytes, local_metrics.sent_bytes);
743 assert_eq!(local_metrics.outbound_bytes, remote_metrics.inbound_bytes);
744 assert_eq!(
745 local_metrics.outbound_operations,
746 remote_metrics.inbound_operations
747 );
748 }
749
750 #[tokio::test]
751 async fn log_sync_unexpected_operation_before_presend() {
752 let mut peer = Peer::new(0).await;
753 const LOG_ID: u64 = 1;
754
755 let body = Body::new(b"unexpected op before presend");
756 let (_, header_bytes) = peer.create_operation(&body, LOG_ID).await;
757
758 let mut logs = Logs::default();
759 logs.insert(peer.id(), vec![LOG_ID]);
760
761 let (session, _event_rx) = peer.log_sync_protocol(&logs);
762
763 let messages = vec![
764 TestLogSyncMessage::Have(BTreeMap::from([(peer.id(), BTreeMap::from([(LOG_ID, 0)]))])),
765 TestLogSyncMessage::Operation(header_bytes.clone(), Some(body.to_bytes())),
766 TestLogSyncMessage::PreSync {
767 total_operations: 1,
768 total_bytes: 100,
769 },
770 TestLogSyncMessage::Done,
771 ];
772
773 let result = run_protocol_uni(session, &messages).await;
774
775 assert!(matches!(result, Err(LogSyncError::UnexpectedMessage(_))));
776 }
777
778 #[tokio::test]
779 async fn log_sync_unexpected_presend_twice() {
780 let mut peer = Peer::new(0).await;
781 const LOG_ID: u64 = 1;
782
783 let body = Body::new(b"two presends");
784 peer.create_operation(&body, LOG_ID).await;
785
786 let mut logs = Logs::default();
787 logs.insert(peer.id(), vec![LOG_ID]);
788
789 let (session, _event_rx) = peer.log_sync_protocol(&logs);
790
791 let messages = vec![
792 TestLogSyncMessage::Have(BTreeMap::from([(peer.id(), BTreeMap::from([(LOG_ID, 0)]))])),
793 TestLogSyncMessage::PreSync {
794 total_operations: 1,
795 total_bytes: 32,
796 },
797 TestLogSyncMessage::PreSync {
798 total_operations: 1,
799 total_bytes: 32,
800 },
801 TestLogSyncMessage::Done,
802 ];
803
804 let result = run_protocol_uni(session, &messages).await;
805
806 assert!(matches!(result, Err(LogSyncError::UnexpectedMessage(_))));
807 }
808
809 #[tokio::test]
810 async fn log_sync_unexpected_done_before_anything() {
811 let mut peer = Peer::new(0).await;
812 let logs = Logs::default();
813
814 let (session, _event_rx) = peer.log_sync_protocol(&logs);
815
816 let messages = vec![TestLogSyncMessage::Done];
817 let result = run_protocol_uni(session, &messages).await;
818
819 assert!(matches!(result, Err(LogSyncError::UnexpectedMessage(_))));
820 }
821
822 #[tokio::test]
823 async fn log_sync_unexpected_have_after_presend() {
824 let mut peer = Peer::new(0).await;
825 const LOG_ID: u64 = 1;
826
827 let body = Body::new(b"bad have order");
828 peer.create_operation(&body, LOG_ID).await;
829
830 let mut logs = Logs::default();
831 logs.insert(peer.id(), vec![LOG_ID]);
832
833 let (session, _event_rx) = peer.log_sync_protocol(&logs);
834
835 let messages = vec![
836 TestLogSyncMessage::Have(BTreeMap::from([(peer.id(), BTreeMap::from([(LOG_ID, 0)]))])),
837 TestLogSyncMessage::PreSync {
838 total_operations: 1,
839 total_bytes: 64,
840 },
841 TestLogSyncMessage::Have(BTreeMap::from([(
842 peer.id(),
843 BTreeMap::from([(LOG_ID, 99)]),
844 )])),
845 TestLogSyncMessage::Done,
846 ];
847
848 let result = run_protocol_uni(session, &messages).await;
849
850 assert!(matches!(result, Err(LogSyncError::UnexpectedMessage(_))));
851 }
852
853 #[tokio::test]
854 async fn log_sync_with_concurrently_pruned_log() {
855 setup_logging();
856
857 let mut peer_a = Peer::new(0).await;
858 let mut peer_b = Peer::new(1).await;
859 let mut peer_c = Peer::new(2).await;
860
861 let body = Body::new(&[0; 1000]);
862
863 for _ in 0..100 {
864 let _ = peer_a.create_operation(&body, 0).await;
865 }
866
867 for _ in 0..100 {
868 let _ = peer_a.create_operation(&body, 1).await;
869 }
870
871 let mut to_be_pruned_log = vec![];
872
873 for _ in 0..50 {
874 let (header, _) = peer_c.create_operation(&body, 0).await;
875 let id = header.hash();
876
877 let operation = Operation {
878 hash: header.hash(),
879 header,
880 body: Some(body.clone()),
881 };
882
883 tx_unwrap!(
884 &peer_a.store,
885 peer_a
886 .store
887 .insert_operation(&id, &operation, &0)
888 .await
889 .unwrap()
890 );
891
892 to_be_pruned_log.push(id);
893 }
894
895 let mut logs = Logs::default();
896 logs.insert(peer_a.id(), vec![0, 1]);
897 logs.insert(peer_c.id(), vec![0]);
898
899 let (a_session, _peer_a_event_rx) = peer_a.log_sync_protocol(&logs);
900 let (b_session, mut peer_b_event_rx) = peer_b.log_sync_protocol(&logs);
901
902 let _peer_b_event_tx_clone = b_session.event_tx.clone();
903
904 let session_b_handle = {
906 let (mut local_message_tx, local_message_rx) = mpsc::channel(512);
907 let (mut remote_message_tx, remote_message_rx) = mpsc::channel(512);
908 let mut local_message_rx = local_message_rx.map(Ok::<_, ()>);
909 let mut remote_message_rx = remote_message_rx.map(Ok::<_, ()>);
910 tokio::spawn(async move {
911 a_session
912 .run(&mut local_message_tx, &mut remote_message_rx)
913 .await
914 .unwrap();
915 });
916 tokio::spawn(async move {
917 b_session
918 .run(&mut remote_message_tx, &mut local_message_rx)
919 .await
920 .unwrap()
921 })
922 };
923
924 loop {
925 let event = peer_b_event_rx.recv().await.unwrap();
926
927 if let LogSyncEvent::OperationReceived { .. } = event {
928 tx_unwrap!(&peer_a.store, {
929 <SqliteStore as OperationStore<Operation<()>, Hash, ()>>::delete_operation(
930 &peer_a.store,
931 &to_be_pruned_log[0],
932 )
933 .await
934 .unwrap();
935 });
936
937 break;
938 }
939 }
940
941 let (_, metrics) = session_b_handle.await.unwrap();
942 assert_eq!(metrics.inbound_operations, 250);
943 assert_eq!(metrics.received_operations, 249);
944 }
945}