diff options
| author | tv <tv@krebsco.de> | 2026-11-08 22:41:06 +0000 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-08 23:52:55 +0100 |
| commit | 1f50adb6ab723425ce031b45ed0db80d404805f0 (patch) | |
| tree | 17a171f32bef8423e32c32b2d90e7ae994f43894 /src/document/signatures.rs | |
| parent | 6b707411c0eee63ceba91819b1948d512b54f1a8 (diff) | |
signatures: a node signs at start what it kept unsigned when it stopped
A node commits a change, keeps it in its document file, and may stop
before any signature vouches for it. It starts again under a new
actor and only ever signed its current actor's latest change: the old
one stays unsigned for good. Peers pulling it then wait for a
signature under a policy requiring them, and so does everything the
node writes after, building on it. Signing every change at commit hid
this under such a policy; signing what is sent, next, won't.
At start, before anything leaves, a node signs each of its own
changes at the document's heads that no signature vouches for:
nothing it made since builds on them, so they are heads.
Diffstat (limited to 'src/document/signatures.rs')
| -rw-r--r-- | src/document/signatures.rs | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/src/document/signatures.rs b/src/document/signatures.rs index 2f408d5..102cad2 100644 --- a/src/document/signatures.rs +++ b/src/document/signatures.rs @@ -443,6 +443,35 @@ impl Signatures { } } + /// This node's changes at `doc`'s heads that no signature vouches for, signed: + /// those it made and kept before it stopped, under an actor of an earlier run, and + /// never sent (it signs what it sends, as it sends it). Nothing it made since builds + /// on them, so they are heads; peers pulling them unsigned would wait for a + /// signature for good under a policy requiring them, and everything the node writes + /// after, building on them, with them. How many it signed. + pub(crate) fn sign_own_heads( + &mut self, + doc: &Document, + key: &crate::identity::NodeKey, + ) -> usize { + let node = key.id(); + let mut signed = 0; + for head in doc.get_heads() { + let Some(meta) = doc.get_change_meta_by_hash(&head) else { + continue; + }; + let own = meta + .author + .as_ref() + .is_some_and(|author| author.as_bytes() == node.as_bytes()); + if own && !self.verified(meta.actor.as_ref(), meta.seq) { + self.sign(doc, head, key); + signed += 1; + } + } + signed + } + /// Whether a valid signature of `hash` is kept: it vouches for its change and the /// changes of its actor it builds on. pub(crate) fn signed(&self, hash: &ChangeHash) -> bool { @@ -552,6 +581,26 @@ mod tests { .unwrap(); } + /// Changes of this node at the heads, of an earlier run, unsigned: signed at start; + /// another node's, and those vouched for, left. + #[test] + fn own_heads_signed_at_start() { + let key = NodeKey::from_seed([1; 32]); + let other = NodeKey::from_seed([2; 32]); + let mut doc = Document::new(); + write(&mut doc, &key.id(), &["a"], json!(1)); + // Another node's, concurrent: a head too. + let mut theirs = Document::new(); + write(&mut theirs, &other.id(), &["b"], json!(2)); + doc.merge(&mut theirs).unwrap(); + assert_eq!(doc.get_heads().len(), 2); + let mut signatures = Signatures::default(); + assert_eq!(signatures.sign_own_heads(&doc, &key), 1); + let ours = doc.get_last_local_change().unwrap(); + assert!(signatures.verified(ours.actor_id(), ours.seq())); + assert_eq!(signatures.sign_own_heads(&doc, &key), 0); + } + /// A signature verifies its actor's changes up to it, by the node its author names; /// another node's key, or another change, doesn't. #[test] |
