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