1use 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#[derive(Debug, Default)]
26enum State<L> {
27 #[default]
29 Start,
30
31 SendHave,
33
34 ReceiveHave { local: LogHeights<VerifyingKey, L> },
36
37 SendPreSync {
39 remote_needs: LogRanges<VerifyingKey, L>,
40 },
41
42 ReceivePreSyncOrDone {
44 remote_needs: LogRanges<VerifyingKey, L>,
45 outbound_operations: u32,
46 outbound_bytes: u32,
47 },
48
49 Sync {
52 remote_needs: LogRanges<VerifyingKey, L>,
53 metrics: LogSyncMetrics,
54 },
55
56 End { metrics: LogSyncMetrics },
58}
59
60#[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 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 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 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#[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#[derive(Clone, Debug, PartialEq)]
457pub enum LogSyncEvent<E> {
458 MetricsExchanged { metrics: LogSyncMetrics },
463
464 OperationReceived {
466 operation: Box<Operation<E>>,
467 metrics: LogSyncMetrics,
468 },
469}
470
471#[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#[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
512pub 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 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}