diff options
| author | tv <tv@krebsco.de> | 2026-10-23 22:12:20 +0200 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-23 22:20:34 +0200 |
| commit | 276f936fc8730504666fbf1c3c5b2942f9be9016 (patch) | |
| tree | 3d7d7c424eee120cb49094b72f48952606509e05 /src/transport/mod.rs | |
| parent | 2311d0cc57a2e0d5776b355983ca2e3f82f9980f (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.rs | 49 |
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)] |
