aboutsummaryrefslogtreecommitdiffstats
path: root/src/transport
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-12 00:44:56 +0000
committertv <tv@krebsco.de>2026-11-12 02:05:42 +0100
commit5b13f5e9d83a11b8a60f2d4711065360ba20f70c (patch)
treeef3d02e1b6da79f2997da3d5aefc91854bfe1e55 /src/transport
parent6a38c6c85ba976e325f7e07a2ba92ffa7babf54b (diff)
sync: a delta's signatures as a blob of their own
A sync message larger than the threshold for its peer (16 chunks, lower on a slow link or after a message to it failed whole) goes as a delta: the peer fetches its changes in chunks, from any node holding them. Its signatures, each writer's latest with changes in it, went in the envelope offering it: up to 65536 of them, 6 MiB, in one message, the size the delta exists to avoid. Nor did an Automerge message count its signatures (one per writer in a whole document pulled) towards the threshold. A delta's signatures are now a blob of their own: the offerer packs and keeps them (Blobs::keep, the latest 64), and the spec names them by BLAKE3 hash and size (DeltaSpec::signatures, None where no signature vouches for its changes). They are served as content (BlobId::Content), so their hash checks them. The receiver fetches them from the offerer alone, as another node holding the changes holds signatures of its own, before the changes, and offers them under the write lock just before judging the changes, so they wait for them as before. The envelope of a delta carries none. An Automerge message's signatures count towards its size: past the threshold, it goes as a delta. Wire version 49.
Diffstat (limited to 'src/transport')
-rw-r--r--src/transport/mod.rs31
-rw-r--r--src/transport/wire.rs6
2 files changed, 31 insertions, 6 deletions
diff --git a/src/transport/mod.rs b/src/transport/mod.rs
index fcdf56e..2778ed7 100644
--- a/src/transport/mod.rs
+++ b/src/transport/mod.rs
@@ -734,9 +734,10 @@ pub(crate) struct SyncEnvelope {
/// to come (`swarm::hop`).
pub(crate) sent: crate::clock::UnixMillis,
/// Signatures of changes (`document::signatures`): of the changes the message
- /// carries, each writer's latest among them past a few hundred; of a delta, each
- /// writer's latest with changes in it (`Signatures::for_changes`, `for_delta`),
- /// `signatures::MAX_PER_MESSAGE` at most, as `signatures::pack` packs them.
+ /// carries, each writer's latest among them past a few hundred
+ /// (`Signatures::for_changes`), `signatures::MAX_PER_MESSAGE` at most, as
+ /// `signatures::pack` packs them. A delta's are a blob of their own
+ /// (`DeltaSpec::signatures`).
pub(crate) signatures: serde_bytes::ByteBuf,
}
@@ -815,6 +816,21 @@ pub(crate) struct DeltaSpec {
/// Sorted.
#[serde(with = "crate::transport::wire::codec::change_hashes")]
pub(crate) to: Vec<ChangeHash>,
+ /// The signatures vouching for its changes, where any do: a blob of their own
+ /// (`BlobId::Content`), from the node that offered it. Each node holding `to` serves
+ /// the same changes, but its own signatures.
+ pub(crate) signatures: Option<DeltaSignatures>,
+}
+
+/// A delta's signatures as a blob (`DeltaSpec::signatures`): each writer's latest with
+/// changes in it (`Signatures::for_delta`), as `signatures::pack` packs them,
+/// `signatures::MAX_PER_MESSAGE` at most.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
+pub(crate) struct DeltaSignatures {
+ /// BLAKE3, as `BlobId::Content` names them.
+ #[serde(with = "crate::transport::wire::codec::bytes32")]
+ pub(crate) hash: [u8; 32],
+ pub(crate) size: u64,
}
impl DeltaSpec {
@@ -823,10 +839,15 @@ impl DeltaSpec {
from.dedup();
to.sort_unstable();
to.dedup();
- Self { from, to }
+ Self {
+ from,
+ to,
+ signatures: None,
+ }
}
- /// Names the spec as a blob: SHA-256 over both lists.
+ /// Names the spec as a blob: SHA-256 over both lists (its changes, the same from
+ /// every node holding them).
pub(crate) fn id(&self) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(b"continuum/delta/1");
diff --git a/src/transport/wire.rs b/src/transport/wire.rs
index 7fe8983..3b67a74 100644
--- a/src/transport/wire.rs
+++ b/src/transport/wire.rs
@@ -14,7 +14,7 @@ use super::{EncodedMessage, MessageKind, ParsedMessage};
/// The wire format's version, the first byte of every payload. Bump it with every change
/// to a message type or a codec (`golden_bytes` fails then).
-pub(crate) const VERSION: u8 = 48;
+pub(crate) const VERSION: u8 = 49;
/// What the second byte of a payload says.
fn tag(kind: MessageKind) -> u8 {
@@ -480,6 +480,10 @@ pub(crate) fn samples() -> Vec<ParsedMessage> {
body: SyncBody::Delta(DeltaSpec {
from: Vec::new(),
to: vec![ChangeHash([2; 32])],
+ signatures: Some(crate::transport::DeltaSignatures {
+ hash: [3; 32],
+ size: 96,
+ }),
}),
ready: true,
writer: Some(7),