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::test_utils::setup_logging;
521 use p2panda_core::{Body, Hash};
522 use p2panda_store::operations::OperationStore;
523 use p2panda_store::{SqliteStore, tx_unwrap};
524
525 use crate::protocols::log_sync::{LogSyncError, LogSyncEvent, Logs, Operation};
526 use crate::test_utils::{Peer, TestLogSyncMessage, run_protocol, run_protocol_uni};
527 use crate::traits::Protocol;
528
529 #[tokio::test]
530 async fn log_sync_no_operations() {
531 let mut peer: Peer = Peer::new(0).await;
532
533 let (session, mut event_rx) = peer.log_sync_protocol(&Logs::default());
534 let ((_, metrics), mut remote_message_rx) = run_protocol_uni(
535 session,
536 &[
537 TestLogSyncMessage::Have(BTreeMap::default()),
538 TestLogSyncMessage::Done,
539 ],
540 )
541 .await
542 .unwrap();
543
544 let event_metrics = assert_matches!(
545 event_rx.recv().await.unwrap(),
546 LogSyncEvent::MetricsExchanged { metrics } => metrics
547 );
548
549 assert_eq!(metrics.outbound_operations, 0);
550 assert_eq!(metrics.outbound_bytes, 0);
551 assert_eq!(metrics.inbound_operations, 0);
552 assert_eq!(metrics.inbound_bytes, 0);
553
554 assert_eq!(event_metrics, metrics);
555
556 assert_eq!(
557 remote_message_rx.recv().await.unwrap(),
558 TestLogSyncMessage::Have(BTreeMap::default())
559 );
560
561 assert_eq!(
562 remote_message_rx.recv().await.unwrap(),
563 TestLogSyncMessage::Done
564 );
565 }
566
567 #[tokio::test]
568 async fn log_sync_some_operations() {
569 let mut peer = Peer::new(0).await;
570 let log_id = 0;
571
572 let body = Body::new("Hello, Sloth!".as_bytes());
573 let (header_0, header_bytes_0) = peer.create_operation(&body, log_id).await;
574 let (header_1, header_bytes_1) = peer.create_operation(&body, log_id).await;
575 let (header_2, header_bytes_2) = peer.create_operation(&body, log_id).await;
576
577 let mut logs = Logs::default();
578 logs.insert(peer.id(), vec![log_id]);
579
580 let (session, mut event_rx) = peer.log_sync_protocol(&logs);
581 let ((_, final_metrics), remote_message_rx) = run_protocol_uni(
582 session,
583 &[
584 TestLogSyncMessage::Have(BTreeMap::default()),
585 TestLogSyncMessage::Done,
586 ],
587 )
588 .await
589 .unwrap();
590
591 let expected_bytes = header_0.payload_size
592 + header_bytes_0.len() as u64
593 + header_1.payload_size
594 + header_bytes_1.len() as u64
595 + header_2.payload_size
596 + header_bytes_2.len() as u64;
597
598 let metrics = assert_matches!(
599 event_rx.recv().await.unwrap(),
600 LogSyncEvent::MetricsExchanged { metrics } => metrics
601 );
602
603 assert_eq!(metrics.outbound_operations, 3);
604 assert_eq!(metrics.outbound_bytes, expected_bytes);
605 assert_eq!(metrics.inbound_operations, 0);
606 assert_eq!(metrics.inbound_bytes, 0);
607
608 assert_eq!(final_metrics.sent_operations, 3);
609 assert_eq!(final_metrics.sent_bytes, expected_bytes);
610 assert_eq!(final_metrics.received_operations, 0);
611 assert_eq!(final_metrics.received_bytes, 0);
612
613 let messages = remote_message_rx.collect::<Vec<_>>().await;
614
615 assert_eq!(messages.len(), 6);
616
617 assert_eq!(
618 messages[0],
619 TestLogSyncMessage::Have(BTreeMap::from([(peer.id(), BTreeMap::from([(0, 2)]))]))
620 );
621
622 assert_eq!(
623 messages[1],
624 TestLogSyncMessage::PreSync {
625 total_operations: 3,
626 total_bytes: expected_bytes
627 }
628 );
629
630 let (header, body_inner) = assert_matches!(
631 &messages[2],
632 TestLogSyncMessage::Operation(header, Some(body)) => (header.clone(), body.clone())
633 );
634 assert_eq!(header, header_bytes_0);
635 assert_eq!(Body::new(&body_inner), body);
636
637 let (header, body_inner) = assert_matches!(
638 &messages[3],
639 TestLogSyncMessage::Operation(header, Some(body)) => (header.clone(), body.clone())
640 );
641 assert_eq!(header, header_bytes_1);
642 assert_eq!(Body::new(&body_inner), body);
643
644 let (header, body_inner) = assert_matches!(
645 &messages[4],
646 TestLogSyncMessage::Operation(header, Some(body)) => (header.clone(), body.clone())
647 );
648 assert_eq!(header, header_bytes_2);
649 assert_eq!(Body::new(&body_inner), body);
650
651 assert_eq!(messages[5], TestLogSyncMessage::Done);
652 }
653
654 #[tokio::test]
655 async fn log_sync_bidirectional_exchange() {
656 setup_logging();
657
658 const LOG_ID: u64 = 0;
659
660 let mut peer_a = Peer::new(0).await;
661 let mut peer_b = Peer::new(1).await;
662
663 let body_a = Body::new("From Alice".as_bytes());
664 let body_b = Body::new("From Bob".as_bytes());
665
666 let (header_a0, _) = peer_a.create_operation(&body_a, LOG_ID).await;
667 let (header_a1, _) = peer_a.create_operation(&body_a, LOG_ID).await;
668
669 let (header_b0, _) = peer_b.create_operation(&body_b, LOG_ID).await;
670 let (header_b1, _) = peer_b.create_operation(&body_b, LOG_ID).await;
671
672 let mut logs = Logs::default();
673 logs.insert(peer_a.id(), vec![LOG_ID]);
674 logs.insert(peer_b.id(), vec![LOG_ID]);
675
676 let (a_session, mut peer_a_event_rx) = peer_a.log_sync_protocol(&logs);
677 let (b_session, mut peer_b_event_rx) = peer_b.log_sync_protocol(&logs);
678
679 let ((_, local_metrics), (_, remote_metrics)) =
680 run_protocol(a_session, b_session).await.unwrap();
681
682 assert_matches!(
683 peer_a_event_rx.recv().await.unwrap(),
684 LogSyncEvent::MetricsExchanged { .. }
685 );
686
687 let (header, body_inner) = assert_matches!(
688 peer_a_event_rx.recv().await.unwrap(),
689 LogSyncEvent::OperationReceived { operation, .. } => {
690 let Operation { header, body, .. } = *operation;
691 (header, body)
692 }
693 );
694 assert_eq!(header, header_b0);
695 assert_eq!(body_inner.unwrap(), body_b);
696
697 let (header, body_inner) = assert_matches!(
698 peer_a_event_rx.recv().await.unwrap(),
699 LogSyncEvent::OperationReceived { operation, .. } => {
700 let Operation { header, body, .. } = *operation;
701 (header, body)
702 }
703 );
704 assert_eq!(header, header_b1);
705 assert_eq!(body_inner.unwrap(), body_b);
706
707 assert_matches!(
708 peer_b_event_rx.recv().await.unwrap(),
709 LogSyncEvent::MetricsExchanged { .. }
710 );
711
712 let (header, body_inner) = assert_matches!(
713 peer_b_event_rx.recv().await.unwrap(),
714 LogSyncEvent::OperationReceived { operation, .. } => {
715 let Operation { header, body, .. } = *operation;
716 (header, body)
717 }
718 );
719 assert_eq!(header, header_a0);
720 assert_eq!(body_inner.unwrap(), body_a);
721
722 let (header, body_inner) = assert_matches!(
723 peer_b_event_rx.recv().await.unwrap(),
724 LogSyncEvent::OperationReceived { operation, .. } => {
725 let Operation { header, body, .. } = *operation;
726 (header, body)
727 }
728 );
729 assert_eq!(header, header_a1);
730 assert_eq!(body_inner.unwrap(), body_a);
731
732 assert_eq!(
733 local_metrics.inbound_operations,
734 local_metrics.received_operations
735 );
736 assert_eq!(local_metrics.inbound_bytes, local_metrics.received_bytes);
737 assert_eq!(
738 local_metrics.outbound_operations,
739 local_metrics.sent_operations
740 );
741 assert_eq!(local_metrics.outbound_bytes, local_metrics.sent_bytes);
742 assert_eq!(local_metrics.outbound_bytes, remote_metrics.inbound_bytes);
743 assert_eq!(
744 local_metrics.outbound_operations,
745 remote_metrics.inbound_operations
746 );
747 }
748
749 #[tokio::test]
750 async fn log_sync_unexpected_operation_before_presend() {
751 let mut peer = Peer::new(0).await;
752 const LOG_ID: u64 = 1;
753
754 let body = Body::new(b"unexpected op before presend");
755 let (_, header_bytes) = peer.create_operation(&body, LOG_ID).await;
756
757 let mut logs = Logs::default();
758 logs.insert(peer.id(), vec![LOG_ID]);
759
760 let (session, _event_rx) = peer.log_sync_protocol(&logs);
761
762 let messages = vec![
763 TestLogSyncMessage::Have(BTreeMap::from([(peer.id(), BTreeMap::from([(LOG_ID, 0)]))])),
764 TestLogSyncMessage::Operation(header_bytes.clone(), Some(body.to_bytes())),
765 TestLogSyncMessage::PreSync {
766 total_operations: 1,
767 total_bytes: 100,
768 },
769 TestLogSyncMessage::Done,
770 ];
771
772 let result = run_protocol_uni(session, &messages).await;
773
774 assert!(matches!(result, Err(LogSyncError::UnexpectedMessage(_))));
775 }
776
777 #[tokio::test]
778 async fn log_sync_unexpected_presend_twice() {
779 let mut peer = Peer::new(0).await;
780 const LOG_ID: u64 = 1;
781
782 let body = Body::new(b"two presends");
783 peer.create_operation(&body, LOG_ID).await;
784
785 let mut logs = Logs::default();
786 logs.insert(peer.id(), vec![LOG_ID]);
787
788 let (session, _event_rx) = peer.log_sync_protocol(&logs);
789
790 let messages = vec![
791 TestLogSyncMessage::Have(BTreeMap::from([(peer.id(), BTreeMap::from([(LOG_ID, 0)]))])),
792 TestLogSyncMessage::PreSync {
793 total_operations: 1,
794 total_bytes: 32,
795 },
796 TestLogSyncMessage::PreSync {
797 total_operations: 1,
798 total_bytes: 32,
799 },
800 TestLogSyncMessage::Done,
801 ];
802
803 let result = run_protocol_uni(session, &messages).await;
804
805 assert!(matches!(result, Err(LogSyncError::UnexpectedMessage(_))));
806 }
807
808 #[tokio::test]
809 async fn log_sync_unexpected_done_before_anything() {
810 let mut peer = Peer::new(0).await;
811 let logs = Logs::default();
812
813 let (session, _event_rx) = peer.log_sync_protocol(&logs);
814
815 let messages = vec![TestLogSyncMessage::Done];
816 let result = run_protocol_uni(session, &messages).await;
817
818 assert!(matches!(result, Err(LogSyncError::UnexpectedMessage(_))));
819 }
820
821 #[tokio::test]
822 async fn log_sync_unexpected_have_after_presend() {
823 let mut peer = Peer::new(0).await;
824 const LOG_ID: u64 = 1;
825
826 let body = Body::new(b"bad have order");
827 peer.create_operation(&body, LOG_ID).await;
828
829 let mut logs = Logs::default();
830 logs.insert(peer.id(), vec![LOG_ID]);
831
832 let (session, _event_rx) = peer.log_sync_protocol(&logs);
833
834 let messages = vec![
835 TestLogSyncMessage::Have(BTreeMap::from([(peer.id(), BTreeMap::from([(LOG_ID, 0)]))])),
836 TestLogSyncMessage::PreSync {
837 total_operations: 1,
838 total_bytes: 64,
839 },
840 TestLogSyncMessage::Have(BTreeMap::from([(
841 peer.id(),
842 BTreeMap::from([(LOG_ID, 99)]),
843 )])),
844 TestLogSyncMessage::Done,
845 ];
846
847 let result = run_protocol_uni(session, &messages).await;
848
849 assert!(matches!(result, Err(LogSyncError::UnexpectedMessage(_))));
850 }
851
852 #[tokio::test]
853 async fn log_sync_with_concurrently_pruned_log() {
854 setup_logging();
855
856 let mut peer_a = Peer::new(0).await;
857 let mut peer_b = Peer::new(1).await;
858 let mut peer_c = Peer::new(2).await;
859
860 let body = Body::new(&[0; 1000]);
861
862 for _ in 0..100 {
863 let _ = peer_a.create_operation(&body, 0).await;
864 }
865
866 for _ in 0..100 {
867 let _ = peer_a.create_operation(&body, 1).await;
868 }
869
870 let mut to_be_pruned_log = vec![];
871
872 for _ in 0..50 {
873 let (header, _) = peer_c.create_operation(&body, 0).await;
874 let id = header.hash();
875
876 let operation = Operation {
877 hash: header.hash(),
878 header,
879 body: Some(body.clone()),
880 };
881
882 tx_unwrap!(
883 &peer_a.store,
884 peer_a
885 .store
886 .insert_operation(&id, &operation, &0)
887 .await
888 .unwrap()
889 );
890
891 to_be_pruned_log.push(id);
892 }
893
894 let mut logs = Logs::default();
895 logs.insert(peer_a.id(), vec![0, 1]);
896 logs.insert(peer_c.id(), vec![0]);
897
898 let (a_session, _peer_a_event_rx) = peer_a.log_sync_protocol(&logs);
899 let (b_session, mut peer_b_event_rx) = peer_b.log_sync_protocol(&logs);
900
901 let _peer_b_event_tx_clone = b_session.event_tx.clone();
902
903 let session_b_handle = {
905 let (mut local_message_tx, local_message_rx) = mpsc::channel(512);
906 let (mut remote_message_tx, remote_message_rx) = mpsc::channel(512);
907 let mut local_message_rx = local_message_rx.map(Ok::<_, ()>);
908 let mut remote_message_rx = remote_message_rx.map(Ok::<_, ()>);
909 tokio::spawn(async move {
910 a_session
911 .run(&mut local_message_tx, &mut remote_message_rx)
912 .await
913 .unwrap();
914 });
915 tokio::spawn(async move {
916 b_session
917 .run(&mut remote_message_tx, &mut local_message_rx)
918 .await
919 .unwrap()
920 })
921 };
922
923 loop {
924 let event = peer_b_event_rx.recv().await.unwrap();
925
926 if let LogSyncEvent::OperationReceived { .. } = event {
927 tx_unwrap!(&peer_a.store, {
928 <SqliteStore as OperationStore<Operation<()>, Hash, ()>>::delete_operation(
929 &peer_a.store,
930 &to_be_pruned_log[0],
931 )
932 .await
933 .unwrap();
934 });
935
936 break;
937 }
938 }
939
940 let (_, metrics) = session_b_handle.await.unwrap();
941 assert_eq!(metrics.inbound_operations, 250);
942 assert_eq!(metrics.received_operations, 249);
943 }
944}