summaryrefslogtreecommitdiffstats
path: root/src/transport/mod.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-23 22:12:20 +0200
committertv <tv@krebsco.de>2026-10-23 22:20:34 +0200
commit276f936fc8730504666fbf1c3c5b2942f9be9016 (patch)
tree3d7d7c424eee120cb49094b72f48952606509e05 /src/transport/mod.rs
parent2311d0cc57a2e0d5776b355983ca2e3f82f9980f (diff)
enrich keepalive content
Add ready and syncs_with_you flags as well as actual heads, so the peer - ready: same meaning as in the sync message - syncs_with_you: sender counts recipient as member and in same epoch - heads: allows to tell who's behind or if both sides have changes
Diffstat (limited to 'src/transport/mod.rs')
-rw-r--r--src/transport/mod.rs49
1 files changed, 46 insertions, 3 deletions
diff --git a/src/transport/mod.rs b/src/transport/mod.rs
index eeba47f..aee3ee3 100644
--- a/src/transport/mod.rs
+++ b/src/transport/mod.rs
@@ -1,6 +1,8 @@
use anyhow::Result;
use async_trait::async_trait;
+use automerge::ChangeHash;
use automerge::sync::Message;
+use sha2::{Digest, Sha256};
use std::boxed::Box;
use std::future::Future;
use std::pin::Pin;
@@ -10,13 +12,54 @@ pub use crate::document::replica::Epoch;
pub mod firewall;
pub mod mycelium;
-pub type HeadsDigest = [u8; 32];
+/// Most heads listed in a keepalive; more are sent as a digest. 16 heads are about
+/// 520 bytes, well within one Mycelium chunk (1300 bytes).
+pub const MAX_LISTED_HEADS: usize = 16;
+/// A document's heads, compactly: the heads themselves while there are few, otherwise a
+/// digest of them. Either way, two summaries are equal exactly when the heads are (the
+/// number of heads decides the form, so equal heads always take the same one).
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum HeadsSummary {
+ /// Sorted.
+ Heads(Vec<ChangeHash>),
+ /// SHA-256 over the sorted heads.
+ Digest([u8; 32]),
+}
+
+impl HeadsSummary {
+ pub fn of(mut heads: Vec<ChangeHash>) -> Self {
+ heads.sort_unstable();
+ if heads.len() <= MAX_LISTED_HEADS {
+ return Self::Heads(heads);
+ }
+ let mut hasher = Sha256::new();
+ for head in &heads {
+ hasher.update(head.0);
+ }
+ Self::Digest(hasher.finalize().into())
+ }
+
+ /// The heads, if listed.
+ pub fn heads(&self) -> Option<&[ChangeHash]> {
+ match self {
+ Self::Heads(heads) => Some(heads),
+ Self::Digest(_) => None,
+ }
+ }
+}
+
+/// PING and PONG. Addressed to one peer: `syncs_with_you` is about the recipient.
#[derive(Debug, Clone)]
pub struct Keepalive {
- pub heads_digest: HeadsDigest,
- /// The digest is only comparable between nodes on the same epoch.
+ /// Only comparable between nodes on the same epoch.
+ pub heads: HeadsSummary,
pub epoch: Epoch,
+ /// The sender is ready (same meaning as `SyncEnvelope::ready`).
+ pub ready: bool,
+ /// The sender would sync with the recipient: counts it as an active member on its
+ /// own epoch.
+ pub syncs_with_you: bool,
}
#[derive(Debug)]