aboutsummaryrefslogtreecommitdiffstats
path: root/src/document/signatures.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/document/signatures.rs')
-rw-r--r--src/document/signatures.rs82
1 files changed, 46 insertions, 36 deletions
diff --git a/src/document/signatures.rs b/src/document/signatures.rs
index 3001690..a8b4713 100644
--- a/src/document/signatures.rs
+++ b/src/document/signatures.rs
@@ -433,43 +433,38 @@ impl Signatures {
}
}
- /// Signs this node's latest change in `doc` with `key` if no signature of it is kept:
- /// after a switch or a rejoin, the changes it replayed there (`remake::delta`), made
- /// without one, vouched for at once.
- pub(crate) fn sign_own_latest(&mut self, doc: &Document, key: &crate::identity::NodeKey) {
- if let Some(change) = doc.get_last_local_change()
- && !self.by_change.contains_key(&change.hash())
- {
- self.sign(doc, change.hash(), key);
- }
- }
-
- /// 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(
+ /// The latest change of each of this node's `actors` in `doc` that no signature
+ /// vouches for, signed: one it made and kept before it stopped, under an actor of an
+ /// earlier run, never sent (it signs what it sends, as it sends it); one left
+ /// unsigned below a signed change a drop took away (`drop_taken`, a fresh actor
+ /// after it). Peers pulling such a change unsigned would wait for a signature for
+ /// good under a policy requiring them, and for everything built on it. At start,
+ /// and after the document is swapped. How many it signed.
+ pub(crate) fn sign_own_actors(
&mut self,
- doc: &Document,
+ doc: &mut Document,
key: &crate::identity::NodeKey,
+ actors: &[super::ActorId],
) -> usize {
- let node = key.id();
+ let current = doc.get_actor().clone();
let mut signed = 0;
- for head in doc.get_heads() {
- let Some(meta) = doc.get_change_meta_by_hash(&head) else {
+ for actor in actors.iter().chain(std::iter::once(&current)) {
+ doc.set_actor(actor.clone());
+ let Some(change) = doc.get_last_local_change() 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);
+ if !self.verified(change.actor_id(), change.seq()) {
+ self.sign_change(doc, change.hash(), key);
signed += 1;
}
}
+ doc.set_actor(current.clone());
+ if doc
+ .get_last_local_change()
+ .is_some_and(|change| self.verified(change.actor_id(), change.seq()))
+ {
+ self.unsigned = None;
+ }
signed
}
@@ -519,10 +514,15 @@ impl Signatures {
hash: ChangeHash,
key: &crate::identity::NodeKey,
) {
+ self.sign_change(doc, hash, key);
+ self.unsigned = None;
+ }
+
+ /// `hash` signed with `key` and kept, vouching for its actor's changes up to it.
+ fn sign_change(&mut self, doc: &Document, hash: ChangeHash, key: &crate::identity::NodeKey) {
let signature = key.sign(&message(&hash));
self.insert(hash, signature);
self.vouch(doc, hash, &signature);
- self.unsigned = None;
}
/// Whether this node's latest change waits for a signature (`sign_latest`, as a sync
@@ -569,6 +569,7 @@ pub(crate) fn check(
mod tests {
use super::*;
use crate::clock::UnixMillis;
+ use crate::document::ActorId;
use crate::document::replica::{Writer, transact};
use crate::document::store;
use crate::identity::NodeKey;
@@ -583,24 +584,33 @@ 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.
+ /// The latest unsigned change of each of this node's actors is signed, below the
+ /// heads too (an earlier actor's, built on under a later one); another node's, and
+ /// those vouched for, left.
#[test]
- fn own_heads_signed_at_start() {
+ fn own_actors_latest_signed() {
let key = NodeKey::from_seed([1; 32]);
let other = NodeKey::from_seed([2; 32]);
let mut doc = Document::new();
+ let earlier = doc.get_actor().clone();
write(&mut doc, &key.id(), &["a"], json!(1));
- // Another node's, concurrent: a head too.
+ // Another node's, concurrent.
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);
+ // A later actor of this node builds on the earlier one's change: not a head now.
+ let mut doc = doc.with_actor(ActorId::random());
+ write(&mut doc, &key.id(), &["c"], json!(3));
let mut signatures = Signatures::default();
- assert_eq!(signatures.sign_own_heads(&doc, &key), 1);
+ assert_eq!(
+ signatures.sign_own_actors(&mut doc, &key, std::slice::from_ref(&earlier)),
+ 2
+ );
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);
+ assert!(signatures.verified(&earlier, 1));
+ assert_eq!(signatures.sign_own_actors(&mut doc, &key, &[earlier]), 0);
+ assert!(signatures.unsigned.is_none());
}
/// A signature verifies its actor's changes up to it, by the node its author names;