diff options
| author | tv <tv@krebsco.de> | 2026-11-08 22:43:19 +0000 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-08 23:52:59 +0100 |
| commit | 24527adf52ca2d8d3b927feb1b811e10fc55fc5c (patch) | |
| tree | 599616858c8ec8bca78163810e65585f5e0daba4 /src/document | |
| parent | 1f50adb6ab723425ce031b45ed0db80d404805f0 (diff) | |
signatures: sign what is sent
A node signed its changes no rule signs a while after them (--sign-every
1s): the change left at once, unsigned, and its signature followed on
its own a second later, to every neighbour, over every link. A few
writes made a cascade of SIGNATURES. Under a policy requiring
signatures it signed every change as it was made instead.
A node now signs its latest change, if no signature vouches for it, as
a sync message takes it (--sign-every sent, the default): the
signature leaves in the same message, and goes where the change goes.
One signature per message, however many changes it carries, each
vouching for those before it: a slow writer's every change is signed,
a fast writer's batches are, at no threshold to pick. A policy
requiring signatures signs what is sent too, even with `none`. `all`
still signs every change as it is made; changes to protocol data,
listed paths and ?signed=true writes are signed as they are made, as
before. The interval, and the task signing on it, go.
Diffstat (limited to 'src/document')
| -rw-r--r-- | src/document/signatures.rs | 64 |
1 files changed, 24 insertions, 40 deletions
diff --git a/src/document/signatures.rs b/src/document/signatures.rs index 102cad2..f2809fe 100644 --- a/src/document/signatures.rs +++ b/src/document/signatures.rs @@ -12,8 +12,8 @@ //! touches protocol data (`_continuum`) or a path the swarm lists as signed //! (`_continuum/access/signed`, `listed`), or when its write asks for it (`Sign`); a //! change left unsigned is vouched for by the node's next signed one, which, as -//! `--sign-every` says, is its latest change, signed a while after it if nothing signed -//! it before (`SignEvery`), or every change at once. +//! `--sign-every` says (`SignEvery`), is its latest change, signed as it leaves in a +//! sync message (`sent`), or every change at once (`all`). //! //! Signatures travel with sync messages (`SyncEnvelope::signatures`): each actor's //! latest one a node verified, those a peer wasn't sent yet (`newer_than`). A received @@ -86,49 +86,40 @@ pub(crate) fn listed(doc: &Document) -> Vec<Vec<String>> { } /// Which of this node's changes it signs besides those `Sign` says (`--sign-every`). -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub(crate) enum SignEvery { /// Every change, as it is made (`all`, `true`). Change, + /// Its latest change, if unsigned, as a sync message takes it (`sent`): one + /// signature per message, however many changes it carries, each vouching for those + /// before it. + #[default] + Sent, /// No others (`none`, `false`): one left unsigned waits for the node's next signed - /// change. + /// change; unless the policy requires signatures, then as `sent`. Never, - /// Its latest change, if unsigned, this often: every change is vouched for within it. - Interval(std::time::Duration), -} - -impl Default for SignEvery { - fn default() -> Self { - SignEvery::Interval(std::time::Duration::from_secs(1)) - } } impl std::fmt::Display for SignEvery { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - SignEvery::Change => f.write_str("all"), - SignEvery::Never => f.write_str("none"), - SignEvery::Interval(every) => f.write_str(&crate::units::format_duration(*every)), - } + f.write_str(match self { + SignEvery::Change => "all", + SignEvery::Sent => "sent", + SignEvery::Never => "none", + }) } } impl std::str::FromStr for SignEvery { type Err = String; - /// `all` or `true`, `none` or `false`, or a duration (`units::parse_duration`; 0 is - /// `none`). + /// `all` or `true`, `sent`, `none` or `false`. fn from_str(text: &str) -> Result<Self, String> { match text.trim() { "all" | "true" => Ok(SignEvery::Change), + "sent" => Ok(SignEvery::Sent), "none" | "false" => Ok(SignEvery::Never), - other => match crate::units::parse_duration(other) { - Ok(every) if every.is_zero() => Ok(SignEvery::Never), - Ok(every) => Ok(SignEvery::Interval(every)), - Err(error) => Err(format!( - "{other:?} is neither all, none nor a duration ({error})" - )), - }, + other => Err(format!("{other:?} is neither all, sent nor none")), } } } @@ -149,11 +140,8 @@ impl<'de> serde::Deserialize<'de> for SignEvery { SignEvery::Never }), serde_json::Value::String(text) => text.parse().map_err(D::Error::custom), - serde_json::Value::Number(number) => { - number.to_string().parse().map_err(D::Error::custom) - } other => Err(D::Error::custom(format!( - "{other} is neither all, none nor a duration" + "{other} is neither all, sent nor none" ))), } } @@ -524,7 +512,8 @@ impl Signatures { self.unsigned = None; } - /// Whether this node's latest change waits for a signature (`sign_latest`). + /// Whether this node's latest change waits for a signature (`sign_latest`, as a sync + /// message takes it). pub(crate) fn pending(&self) -> bool { self.unsigned.is_some() } @@ -625,23 +614,18 @@ mod tests { assert!(check(&named, &hash, &key.sign(&message(&hash))).is_none()); } - /// `--sign-every`: `all`, `none`, a duration; 0 is `none`. + /// `--sign-every`: `all`, `sent`, `none`. #[test] fn sign_every_reads() { - use std::time::Duration; let read = |text: &str| text.parse::<SignEvery>(); assert_eq!(read("all"), Ok(SignEvery::Change)); assert_eq!(read("true"), Ok(SignEvery::Change)); + assert_eq!(read("sent"), Ok(SignEvery::Sent)); assert_eq!(read("none"), Ok(SignEvery::Never)); - assert_eq!(read("0"), Ok(SignEvery::Never)); - assert_eq!( - read("500ms"), - Ok(SignEvery::Interval(Duration::from_millis(500))) - ); - assert!(read("sometimes").is_err()); + assert!(read("1s").is_err()); let toml: SignEvery = serde_json::from_value(json!(false)).unwrap(); assert_eq!(toml, SignEvery::Never); - assert_eq!(SignEvery::default().to_string(), "1s"); + assert_eq!(SignEvery::default().to_string(), "sent"); } /// The latest change left unsigned is signed later, once; a signature since covers it. |
