diff options
| author | tv <tv@krebsco.de> | 2026-11-08 09:33:41 +0000 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-08 10:35:31 +0100 |
| commit | b758c93a1887ea36b0f52ccd4a6f9824de77d819 (patch) | |
| tree | 96f7f4ca3007e393b185739a66b0474323b50872 /src/access/rules.rs | |
| parent | af3869df9ff3a0266857bd7c7534b75b36d04aa5 (diff) | |
access: an epoch's root is never dropped
A REFUSALS naming the swarm's first change made the node receiving it
judge that change again in a document that isn't empty, refuse it as
"a change without dependencies that isn't the epoch's root", and drop
it with everything built on it: its whole document.
The first epoch's root is now the first change a document holds, as it
comes to an empty one or as it is in one; a change judged again on a
peer's word is never refused for having no dependencies; and
drop_taken refuses to drop an epoch's root, or anything that would
leave nothing kept, logging an error instead.
Review finding 1 (critical).
Diffstat (limited to 'src/access/rules.rs')
| -rw-r--r-- | src/access/rules.rs | 10 |
1 files changed, 8 insertions, 2 deletions
diff --git a/src/access/rules.rs b/src/access/rules.rs index f285328..2aed4b8 100644 --- a/src/access/rules.rs +++ b/src/access/rules.rs @@ -33,7 +33,7 @@ use crate::document::at::At; use crate::document::inspect::{Action, Op, Path}; use crate::document::meta; use crate::document::replica::Epoch; -use crate::document::{ChangeHash, Document, store}; +use crate::document::{ChangeHash, Document, ReadDoc, store}; /// A change to judge, as its bytes say. #[derive(Debug, Clone)] @@ -75,8 +75,10 @@ impl std::fmt::Display for Refusal { /// Judges `change`, about to be applied to `doc` (of `epoch`). pub(crate) fn judge(doc: &Document, epoch: Epoch, change: &Change<'_>) -> Result<(), Refusal> { if change.deps.is_empty() { + // The first epoch's: the first change a document holds, as it comes to an empty + // one, or as it is in one (judged again, `swarm::refusals`). let root = if epoch.generation == 0 { - doc.get_heads().is_empty() + doc.get_heads().is_empty() || doc.get_change_by_hash(&change.hash).is_some() } else { change.hash.0 == epoch.id }; @@ -720,6 +722,10 @@ mod tests { }; assert!(root(&Document::new(), Epoch::GENESIS, [1; 32]).is_ok()); assert!(refused(root(&swarm(false), Epoch::GENESIS, [1; 32]))); + // The first epoch's, judged again in a document that holds it: its root. + let held = swarm(false); + let first = held.get_changes(&[])[0].hash(); + assert!(root(&held, Epoch::GENESIS, first.0).is_ok()); let epoch = Epoch { generation: 3, id: [5; 32], |
