Skip to main content

safemesh_crdt/
lib.rs

1// SafeMesh — delta-state CRDT convergence, built on crdt-lean.
2// Copyright (C) 2026 Ben Cassie
3// SPDX-License-Identifier: Apache-2.0
4
5//! Delta-state G-Counter and PN-Counter for constrained mesh links.
6//!
7//! This crate is the PRODUCT half of the SafeMesh verification-guided loop:
8//! a thin `no_std + alloc` implementation of exactly the model proven in
9//! `lean/SafeMesh/` (carrier = join-semilattice, merge = join, delta =
10//! single-coordinate bump), held to those proofs by a differential
11//! conformance test (`tests/conformance.rs`) that replays a Lean-emitted
12//! corpus through this code and requires byte-identical outputs.
13//!
14//! What is PROVEN (in Lean, kernel-checked) vs what is TESTED (here): the
15//! Lean theorems are universal; this crate is checked against them over a
16//! finite corpus. The bridge (Lean compiled eval → JSON → this crate) is the
17//! named trusted component. See the repo README for the full TCB statement.
18
19#![no_std]
20#![deny(unsafe_code)]
21
22extern crate alloc;
23#[cfg(all(feature = "local-writer", target_os = "linux"))]
24extern crate std;
25
26#[cfg(all(feature = "local-writer", target_os = "linux"))]
27pub mod local;
28pub mod ownership;
29use alloc::collections::{BTreeMap, BTreeSet};
30use alloc::string::String;
31use alloc::vec;
32use alloc::vec::Vec;
33
34/// State-based merge contract: `merge` is expected to be a semilattice join.
35///
36/// For in-house types, this contract is backed by the Lean proof suite and
37/// differential conformance corpus. For user-defined types, it is a tested
38/// contract enforced by the laws harness, not a proof.
39pub trait Mergeable {
40    fn merge(&mut self, other: &Self);
41}
42
43/// Delta application surface for CRDT product types.
44pub trait Crdt: Mergeable {
45    type Delta;
46
47    /// Fixed replica domain, or `None` for CRDTs without a fixed arity.
48    fn replica_count(&self) -> Option<usize> {
49        None
50    }
51
52    /// Validate decoded records before admission or replay into this carrier.
53    fn validate_record(&self, _id: RecordId, _delta: &Self::Delta) -> Result<(), WireError> {
54        Ok(())
55    }
56
57    fn apply_delta(&mut self, delta: Self::Delta);
58}
59
60/// Error raised by the checked full-state merge (`try_merge`).
61///
62/// The Lean model fixes the replica set (`Fin n`), so a length mismatch is
63/// unrepresentable there. At the Rust boundary an untrusted peer can hand us a
64/// state vector of a different width; merging it by `zip` would silently drop
65/// the trailing coordinates (WS1 silent state loss). The checked path surfaces
66/// the mismatch as an error instead of corrupting state.
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68pub enum MergeError {
69    /// The two counters cover a different number of replicas.
70    ReplicaCountMismatch { own: usize, other: usize },
71}
72
73impl core::fmt::Display for MergeError {
74    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
75        match self {
76            MergeError::ReplicaCountMismatch { own, other } => write!(
77                f,
78                "replica-count mismatch: own={own}, other={other} (fixed replica set violated)"
79            ),
80        }
81    }
82}
83
84impl core::error::Error for MergeError {}
85
86/// Delta for a grow-only counter: one replica coordinate and its asserted tally.
87#[derive(Clone, Debug, PartialEq, Eq)]
88pub struct GCounterDelta {
89    pub replica: usize,
90    pub tally: u64,
91}
92
93/// Rejection of a counter delta with an invalid replica coordinate.
94#[derive(Clone, Copy, Debug, PartialEq, Eq)]
95pub enum CoordinateError {
96    /// The index is outside `0..replica_count`; the counter is unchanged.
97    ReplicaOutOfRange {
98        replica: usize,
99        replica_count: usize,
100    },
101}
102
103/// A grow-only counter: one tally per replica, join = pointwise max.
104///
105/// Model: `Crdt.GCounter ι = ι → ℕ` with the Pi join-semilattice. Applying a
106/// delta is joining it in, so delivery order and redelivery are irrelevant —
107/// `SafeMesh.delta_dissemination_sec` — and a replica that received bumps `B`
108/// holds, per coordinate, the max over that coordinate's bumps —
109/// `SafeMesh.deltaGCounter_correct`.
110#[derive(Clone, Debug, PartialEq, Eq)]
111pub struct GCounter {
112    counts: Vec<u64>,
113}
114
115impl GCounter {
116    /// Fresh counter for `n` replicas — the lattice bottom `⊥` (all zeros).
117    pub fn new(n: usize) -> Self {
118        GCounter { counts: vec![0; n] }
119    }
120
121    /// Number of replica coordinates.
122    pub fn len(&self) -> usize {
123        self.counts.len()
124    }
125
126    pub fn is_empty(&self) -> bool {
127        self.counts.is_empty()
128    }
129
130    /// Apply the delta `SafeMesh.deltaBump replica tally` — a single
131    /// coordinate on the wire. Joining the delta is `max` at that coordinate
132    /// (`Pi.single` joined into the state); applying the same bump again is a
133    /// no-op (idempotence, `SafeMesh.delta_dissemination_sec`).
134    ///
135    /// Out-of-range `replica` is ignored (a malformed delta must not corrupt
136    /// state; the Lean model has no such case — `Fin n` makes it unrepresentable).
137    /// Use [`Self::try_apply_bump`] to receive an error for an invalid index.
138    pub fn apply_bump(&mut self, replica: usize, tally: u64) {
139        if let Some(c) = self.counts.get_mut(replica) {
140            if tally > *c {
141                *c = tally;
142            }
143        }
144    }
145
146    /// Apply a single-coordinate delta, returning an error for an invalid index.
147    ///
148    /// Checks the coordinate before mutation. On error, the entire counter is
149    /// unchanged; on success, behaves exactly like [`Self::apply_bump`], including
150    /// accepting an equal or lower tally as a no-op.
151    pub fn try_apply_bump(&mut self, replica: usize, tally: u64) -> Result<(), CoordinateError> {
152        if replica >= self.counts.len() {
153            return Err(CoordinateError::ReplicaOutOfRange {
154                replica,
155                replica_count: self.counts.len(),
156            });
157        }
158        self.apply_bump(replica, tally);
159        Ok(())
160    }
161
162    /// Checked full-state merge: pointwise max, erroring on a replica-count
163    /// mismatch instead of silently truncating to the shorter vector (WS1).
164    /// This is the boundary-safe entry point for state received from an
165    /// untrusted peer.
166    pub fn try_merge(&mut self, other: &GCounter) -> Result<(), MergeError> {
167        if self.counts.len() != other.counts.len() {
168            return Err(MergeError::ReplicaCountMismatch {
169                own: self.counts.len(),
170                other: other.counts.len(),
171            });
172        }
173        for (c, o) in self.counts.iter_mut().zip(other.counts.iter()) {
174            if *o > *c {
175                *c = *o;
176            }
177        }
178        Ok(())
179    }
180
181    /// Full-state merge: pointwise max — `Crdt.gcounter_merge_apply`. A
182    /// delta replica and a full-state replica fed the same bumps agree:
183    /// `SafeMesh.deltaGCounter_matches_full`.
184    ///
185    /// Infallible variant for the proven equal-length invariant (fixed replica
186    /// set). Panics on a replica-count mismatch rather than dropping state; use
187    /// [`GCounter::try_merge`] for untrusted input.
188    pub fn merge(&mut self, other: &GCounter) {
189        self.try_merge(other).expect(
190            "GCounter::merge requires equal replica counts; use try_merge for untrusted input",
191        );
192    }
193
194    /// Per-coordinate state (the `ι → ℕ` vector).
195    pub fn state(&self) -> &[u64] {
196        &self.counts
197    }
198
199    /// The counter's read: sum of per-replica tallies — `Crdt.gcounterValue`.
200    pub fn value(&self) -> u64 {
201        self.counts.iter().sum()
202    }
203}
204
205impl Mergeable for GCounter {
206    fn merge(&mut self, other: &Self) {
207        GCounter::merge(self, other);
208    }
209}
210
211impl Crdt for GCounter {
212    type Delta = GCounterDelta;
213
214    fn validate_record(&self, id: RecordId, delta: &Self::Delta) -> Result<(), WireError> {
215        ownership::check_counter_record(self.len(), id, delta)
216            .map_err(|_| WireError::OwnershipViolation)
217    }
218
219    fn replica_count(&self) -> Option<usize> {
220        Some(self.len())
221    }
222
223    fn apply_delta(&mut self, delta: Self::Delta) {
224        self.apply_bump(delta.replica, delta.tally);
225    }
226}
227
228/// A grow-only set: merge = union.
229///
230/// This mirrors the upstream `crdt-lean` G-Set carrier (`Finset α`).
231#[derive(Clone, Debug, PartialEq, Eq)]
232pub struct GSet<T: Ord> {
233    elements: BTreeSet<T>,
234}
235
236impl<T: Ord> GSet<T> {
237    pub fn new() -> Self {
238        GSet {
239            elements: BTreeSet::new(),
240        }
241    }
242
243    pub fn insert(&mut self, element: T) {
244        self.elements.insert(element);
245    }
246
247    pub fn merge(&mut self, other: &Self)
248    where
249        T: Clone,
250    {
251        self.elements.extend(other.elements.iter().cloned());
252    }
253
254    pub fn contains(&self, element: &T) -> bool {
255        self.elements.contains(element)
256    }
257
258    pub fn elements(&self) -> &BTreeSet<T> {
259        &self.elements
260    }
261}
262
263impl<T: Ord> Default for GSet<T> {
264    fn default() -> Self {
265        Self::new()
266    }
267}
268
269impl<T: Ord + Clone> Mergeable for GSet<T> {
270    fn merge(&mut self, other: &Self) {
271        GSet::merge(self, other);
272    }
273}
274
275impl<T: Ord + Clone> Crdt for GSet<T> {
276    type Delta = T;
277
278    fn apply_delta(&mut self, delta: Self::Delta) {
279        self.insert(delta);
280    }
281}
282
283/// An increment/decrement counter: a pair of G-Counters (increments `P`,
284/// decrements `N`), join = componentwise.
285///
286/// Model: `Crdt.PNCounter ι = (ι → ℕ) × (ι → ℕ)` with the Prod lattice.
287/// Deltas bump one side, one coordinate (`SafeMesh.deltaBumpP` /
288/// `deltaBumpN`); each side accumulates exactly as a delta G-Counter
289/// (`SafeMesh.deltaPNCounter_correct_P` / `_N`), and equal state gives an
290/// equal read (`SafeMesh.deltaPNCounter_value_matches`).
291#[derive(Clone, Debug, PartialEq, Eq)]
292pub struct PnCounter {
293    p: GCounter,
294    n: GCounter,
295}
296
297#[derive(Clone, Debug, PartialEq, Eq)]
298pub enum PnCounterDelta {
299    Inc { replica: usize, tally: u64 },
300    Dec { replica: usize, tally: u64 },
301}
302
303impl PnCounter {
304    /// Fresh counter for `n` replicas — the lattice bottom `(⊥, ⊥)`.
305    pub fn new(n: usize) -> Self {
306        PnCounter {
307            p: GCounter::new(n),
308            n: GCounter::new(n),
309        }
310    }
311
312    /// Apply `SafeMesh.deltaBumpP replica tally` — increment side, one
313    /// coordinate on the wire, `⊥` on the other side.
314    /// Out-of-range indices are ignored; use [`Self::try_apply_inc`] for an error.
315    pub fn apply_inc(&mut self, replica: usize, tally: u64) {
316        self.p.apply_bump(replica, tally);
317    }
318
319    /// Apply an increment-side delta with a checked replica coordinate.
320    ///
321    /// Returns [`CoordinateError::ReplicaOutOfRange`] before any mutation if the
322    /// index is invalid. Both sides remain unchanged on error. Valid input has
323    /// the same effect as [`Self::apply_inc`].
324    pub fn try_apply_inc(&mut self, replica: usize, tally: u64) -> Result<(), CoordinateError> {
325        self.p.try_apply_bump(replica, tally)
326    }
327
328    /// Apply `SafeMesh.deltaBumpN replica tally` — decrement side.
329    /// Out-of-range indices are ignored; use [`Self::try_apply_dec`] for an error.
330    pub fn apply_dec(&mut self, replica: usize, tally: u64) {
331        self.n.apply_bump(replica, tally);
332    }
333
334    /// Apply a decrement-side delta with a checked replica coordinate.
335    ///
336    /// Returns [`CoordinateError::ReplicaOutOfRange`] before any mutation if the
337    /// index is invalid. Both sides remain unchanged on error. Valid input has
338    /// the same effect as [`Self::apply_dec`].
339    pub fn try_apply_dec(&mut self, replica: usize, tally: u64) -> Result<(), CoordinateError> {
340        self.n.try_apply_bump(replica, tally)
341    }
342
343    /// Checked full-state merge: componentwise checked G-Counter merge,
344    /// erroring on a replica-count mismatch on either side instead of silently
345    /// truncating (WS1). Both sides are length-checked before any mutation, so
346    /// the operation is all-or-nothing: on error `self` is left unchanged.
347    pub fn try_merge(&mut self, other: &PnCounter) -> Result<(), MergeError> {
348        if self.p.state().len() != other.p.state().len() {
349            return Err(MergeError::ReplicaCountMismatch {
350                own: self.p.state().len(),
351                other: other.p.state().len(),
352            });
353        }
354        if self.n.state().len() != other.n.state().len() {
355            return Err(MergeError::ReplicaCountMismatch {
356                own: self.n.state().len(),
357                other: other.n.state().len(),
358            });
359        }
360        self.p.try_merge(&other.p)?;
361        self.n.try_merge(&other.n)?;
362        Ok(())
363    }
364
365    /// Full-state merge: componentwise G-Counter merge —
366    /// `Crdt.pncounter_merge_apply`.
367    ///
368    /// Infallible variant for the proven equal-length invariant; panics on a
369    /// replica-count mismatch. Use [`PnCounter::try_merge`] for untrusted input.
370    pub fn merge(&mut self, other: &PnCounter) {
371        self.try_merge(other).expect(
372            "PnCounter::merge requires equal replica counts; use try_merge for untrusted input",
373        );
374    }
375
376    /// Increment-side state.
377    pub fn p_state(&self) -> &[u64] {
378        self.p.state()
379    }
380
381    /// Decrement-side state.
382    pub fn n_state(&self) -> &[u64] {
383        self.n.state()
384    }
385
386    /// The counter's read: (sum of increments) − (sum of decrements) —
387    /// `Crdt.pncounterValue` (ℤ in the model, `i64` here).
388    pub fn value(&self) -> i64 {
389        self.p.value() as i64 - self.n.value() as i64
390    }
391}
392
393impl Mergeable for PnCounter {
394    fn merge(&mut self, other: &Self) {
395        PnCounter::merge(self, other);
396    }
397}
398
399impl Crdt for PnCounter {
400    type Delta = PnCounterDelta;
401
402    fn replica_count(&self) -> Option<usize> {
403        Some(self.p.len())
404    }
405
406    fn apply_delta(&mut self, delta: Self::Delta) {
407        match delta {
408            PnCounterDelta::Inc { replica, tally } => self.apply_inc(replica, tally),
409            PnCounterDelta::Dec { replica, tally } => self.apply_dec(replica, tally),
410        }
411    }
412}
413
414/// Delta for an observed-remove set.
415#[derive(Clone, Debug, PartialEq, Eq)]
416pub enum OrSetDelta<T, K> {
417    Add { element: T, token: K },
418    Remove { tokens: Vec<K> },
419}
420
421/// Observed-remove set with add-wins semantics.
422///
423/// Lean model: `Crdt.ORSet.State α τ = Finset (α × τ) × Finset τ`.
424#[derive(Clone, Debug, PartialEq, Eq)]
425pub struct OrSet<T: Ord, K: Ord> {
426    adds: BTreeSet<(T, K)>,
427    tombstones: BTreeSet<K>,
428}
429
430impl<T: Ord, K: Ord> OrSet<T, K> {
431    pub fn new() -> Self {
432        OrSet {
433            adds: BTreeSet::new(),
434            tombstones: BTreeSet::new(),
435        }
436    }
437
438    pub fn add(&mut self, element: T, token: K) {
439        self.adds.insert((element, token));
440    }
441
442    pub fn apply_remove<I>(&mut self, tokens: I)
443    where
444        I: IntoIterator<Item = K>,
445    {
446        self.tombstones.extend(tokens);
447    }
448
449    pub fn merge(&mut self, other: &Self)
450    where
451        T: Clone,
452        K: Clone,
453    {
454        self.adds.extend(other.adds.iter().cloned());
455        self.tombstones.extend(other.tombstones.iter().cloned());
456    }
457
458    pub fn adds(&self) -> &BTreeSet<(T, K)> {
459        &self.adds
460    }
461
462    pub fn tombstones(&self) -> &BTreeSet<K> {
463        &self.tombstones
464    }
465}
466
467impl<T: Ord + Clone, K: Ord + Clone> OrSet<T, K> {
468    pub fn observed_tokens(&self, element: &T) -> BTreeSet<K> {
469        self.adds
470            .iter()
471            .filter_map(|(candidate, token)| {
472                if candidate == element {
473                    Some(token.clone())
474                } else {
475                    None
476                }
477            })
478            .collect()
479    }
480
481    pub fn contains(&self, element: &T) -> bool {
482        self.adds
483            .iter()
484            .any(|(candidate, token)| candidate == element && !self.tombstones.contains(token))
485    }
486
487    pub fn elements(&self) -> BTreeSet<T> {
488        self.adds
489            .iter()
490            .filter_map(|(element, token)| {
491                if self.tombstones.contains(token) {
492                    None
493                } else {
494                    Some(element.clone())
495                }
496            })
497            .collect()
498    }
499}
500
501impl<T: Ord, K: Ord> Default for OrSet<T, K> {
502    fn default() -> Self {
503        Self::new()
504    }
505}
506
507impl<T: Ord + Clone, K: Ord + Clone> Mergeable for OrSet<T, K> {
508    fn merge(&mut self, other: &Self) {
509        OrSet::merge(self, other);
510    }
511}
512
513impl<T: Ord + Clone, K: Ord + Clone> Crdt for OrSet<T, K> {
514    type Delta = OrSetDelta<T, K>;
515
516    fn apply_delta(&mut self, delta: Self::Delta) {
517        match delta {
518            OrSetDelta::Add { element, token } => self.add(element, token),
519            OrSetDelta::Remove { tokens } => self.apply_remove(tokens),
520        }
521    }
522}
523
524/// Delta for an RGA-family ordered sequence.
525#[derive(Clone, Debug, PartialEq, Eq)]
526pub enum RgaDelta<P, V> {
527    Insert { position: P, value: V },
528    Delete { position: P },
529}
530
531/// RGA-family sequence state: positioned values plus tombstoned positions.
532///
533/// Lean model: `Crdt.RGA.State ι α = Finset (ι × α) × Finset ι`. The Lean
534/// read is the sorted list of live position identifiers; values are carried
535/// by lookup through the live positioned set.
536#[derive(Clone, Debug, PartialEq, Eq)]
537pub struct Rga<P: Ord, V: Ord> {
538    placed: BTreeSet<(P, V)>,
539    tombstones: BTreeSet<P>,
540}
541
542impl<P: Ord, V: Ord> Rga<P, V> {
543    pub fn new() -> Self {
544        Rga {
545            placed: BTreeSet::new(),
546            tombstones: BTreeSet::new(),
547        }
548    }
549
550    pub fn insert(&mut self, position: P, value: V) {
551        self.placed.insert((position, value));
552    }
553
554    pub fn delete(&mut self, position: P) {
555        self.tombstones.insert(position);
556    }
557
558    pub fn merge(&mut self, other: &Self)
559    where
560        P: Clone,
561        V: Clone,
562    {
563        self.placed.extend(other.placed.iter().cloned());
564        self.tombstones.extend(other.tombstones.iter().cloned());
565    }
566
567    pub fn placed(&self) -> &BTreeSet<(P, V)> {
568        &self.placed
569    }
570
571    pub fn tombstones(&self) -> &BTreeSet<P> {
572        &self.tombstones
573    }
574}
575
576impl<P: Ord + Clone, V: Ord + Clone> Rga<P, V> {
577    pub fn live_entries(&self) -> Vec<(P, V)> {
578        self.placed
579            .iter()
580            .filter_map(|(position, value)| {
581                if self.tombstones.contains(position) {
582                    None
583                } else {
584                    Some((position.clone(), value.clone()))
585                }
586            })
587            .collect()
588    }
589
590    pub fn read_positions(&self) -> Vec<P> {
591        self.live_entries()
592            .into_iter()
593            .map(|(position, _)| position)
594            .collect::<BTreeSet<P>>()
595            .into_iter()
596            .collect()
597    }
598}
599
600impl<P: Ord, V: Ord> Default for Rga<P, V> {
601    fn default() -> Self {
602        Self::new()
603    }
604}
605
606impl<P: Ord + Clone, V: Ord + Clone> Mergeable for Rga<P, V> {
607    fn merge(&mut self, other: &Self) {
608        Rga::merge(self, other);
609    }
610}
611
612impl<P: Ord + Clone, V: Ord + Clone> Crdt for Rga<P, V> {
613    type Delta = RgaDelta<P, V>;
614
615    fn apply_delta(&mut self, delta: Self::Delta) {
616        match delta {
617            RgaDelta::Insert { position, value } => self.insert(position, value),
618            RgaDelta::Delete { position } => self.delete(position),
619        }
620    }
621}
622
623/// Delta for an observed-token enable-wins flag.
624#[derive(Clone, Debug, PartialEq, Eq)]
625pub enum EnableWinsFlagDelta<K> {
626    Enable { token: K },
627    Disable { tokens: Vec<K> },
628}
629
630/// Enable-wins boolean flag.
631///
632/// This is a flat tested-not-proven type. It mirrors an OR-Set over a unit
633/// element: enable adds a unique token, disable tombstones observed tokens, and
634/// concurrent unobserved enables remain live.
635#[derive(Clone, Debug, PartialEq, Eq)]
636pub struct EnableWinsFlag<K: Ord> {
637    enables: BTreeSet<K>,
638    tombstones: BTreeSet<K>,
639}
640
641impl<K: Ord> EnableWinsFlag<K> {
642    pub fn new() -> Self {
643        EnableWinsFlag {
644            enables: BTreeSet::new(),
645            tombstones: BTreeSet::new(),
646        }
647    }
648
649    pub fn enable(&mut self, token: K) {
650        self.enables.insert(token);
651    }
652
653    pub fn disable<I>(&mut self, tokens: I)
654    where
655        I: IntoIterator<Item = K>,
656    {
657        self.tombstones.extend(tokens);
658    }
659
660    pub fn merge(&mut self, other: &Self)
661    where
662        K: Clone,
663    {
664        self.enables.extend(other.enables.iter().cloned());
665        self.tombstones.extend(other.tombstones.iter().cloned());
666    }
667
668    pub fn enables(&self) -> &BTreeSet<K> {
669        &self.enables
670    }
671
672    pub fn tombstones(&self) -> &BTreeSet<K> {
673        &self.tombstones
674    }
675}
676
677impl<K: Ord + Clone> EnableWinsFlag<K> {
678    pub fn observed_tokens(&self) -> BTreeSet<K> {
679        self.enables.iter().cloned().collect()
680    }
681
682    pub fn value(&self) -> bool {
683        self.enables
684            .iter()
685            .any(|token| !self.tombstones.contains(token))
686    }
687}
688
689impl<K: Ord> Default for EnableWinsFlag<K> {
690    fn default() -> Self {
691        Self::new()
692    }
693}
694
695impl<K: Ord + Clone> Mergeable for EnableWinsFlag<K> {
696    fn merge(&mut self, other: &Self) {
697        EnableWinsFlag::merge(self, other);
698    }
699}
700
701impl<K: Ord + Clone> Crdt for EnableWinsFlag<K> {
702    type Delta = EnableWinsFlagDelta<K>;
703
704    fn apply_delta(&mut self, delta: Self::Delta) {
705        match delta {
706            EnableWinsFlagDelta::Enable { token } => self.enable(token),
707            EnableWinsFlagDelta::Disable { tokens } => self.disable(tokens),
708        }
709    }
710}
711
712/// Total-order dot for last-writer-wins registers.
713#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
714pub struct LwwDot {
715    pub timestamp: u64,
716    pub replica: u64,
717}
718
719/// One LWW register assignment.
720#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
721pub struct LwwEntry<V: Ord> {
722    pub dot: LwwDot,
723    pub value: V,
724}
725
726/// Delta for a last-writer-wins register.
727#[derive(Clone, Debug, PartialEq, Eq)]
728pub struct LwwRegisterDelta<V: Ord> {
729    pub timestamp: u64,
730    pub replica: u64,
731    pub value: V,
732}
733
734/// Last-writer-wins register.
735///
736/// This is a flat tested-not-proven type. It is a max register over the total
737/// order `(timestamp, replica, value)`, which gives deterministic merge and
738/// tie-breaking. It is not currently part of the Lean-proven product surface.
739#[derive(Clone, Debug, PartialEq, Eq)]
740pub struct LwwRegister<V: Ord> {
741    entry: Option<LwwEntry<V>>,
742}
743
744impl<V: Ord> LwwRegister<V> {
745    pub fn new() -> Self {
746        LwwRegister { entry: None }
747    }
748
749    pub fn set(&mut self, timestamp: u64, replica: u64, value: V) {
750        self.apply_entry(LwwEntry {
751            dot: LwwDot { timestamp, replica },
752            value,
753        });
754    }
755
756    pub fn entry(&self) -> Option<&LwwEntry<V>> {
757        self.entry.as_ref()
758    }
759
760    pub fn value(&self) -> Option<&V> {
761        self.entry.as_ref().map(|entry| &entry.value)
762    }
763
764    fn apply_entry(&mut self, entry: LwwEntry<V>) {
765        match &self.entry {
766            Some(current) if current >= &entry => {}
767            _ => self.entry = Some(entry),
768        }
769    }
770}
771
772impl<V: Ord> Default for LwwRegister<V> {
773    fn default() -> Self {
774        Self::new()
775    }
776}
777
778impl<V: Ord + Clone> LwwRegister<V> {
779    pub fn merge(&mut self, other: &Self) {
780        if let Some(entry) = other.entry.clone() {
781            self.apply_entry(entry);
782        }
783    }
784}
785
786impl<V: Ord + Clone> Mergeable for LwwRegister<V> {
787    fn merge(&mut self, other: &Self) {
788        LwwRegister::merge(self, other);
789    }
790}
791
792impl<V: Ord + Clone> Crdt for LwwRegister<V> {
793    type Delta = LwwRegisterDelta<V>;
794
795    fn apply_delta(&mut self, delta: Self::Delta) {
796        self.set(delta.timestamp, delta.replica, delta.value);
797    }
798}
799
800/// Delta for a last-writer-wins map.
801#[derive(Clone, Debug, PartialEq, Eq)]
802pub enum LwwMapDelta<K: Ord, V: Ord> {
803    Set {
804        key: K,
805        timestamp: u64,
806        replica: u64,
807        value: V,
808    },
809    Remove {
810        key: K,
811        timestamp: u64,
812        replica: u64,
813    },
814}
815
816/// Last-writer-wins map.
817///
818/// This is a flat tested-not-proven type. Each key has an optional max-dot value
819/// entry and an optional max-dot remove tombstone. A key is visible when its
820/// value dot is greater than its remove dot.
821#[derive(Clone, Debug, PartialEq, Eq)]
822pub struct LwwMap<K: Ord, V: Ord> {
823    entries: BTreeMap<K, LwwEntry<V>>,
824    removals: BTreeMap<K, LwwDot>,
825}
826
827impl<K: Ord, V: Ord> LwwMap<K, V> {
828    pub fn new() -> Self {
829        LwwMap {
830            entries: BTreeMap::new(),
831            removals: BTreeMap::new(),
832        }
833    }
834
835    pub fn set(&mut self, key: K, timestamp: u64, replica: u64, value: V) {
836        self.apply_entry(
837            key,
838            LwwEntry {
839                dot: LwwDot { timestamp, replica },
840                value,
841            },
842        );
843    }
844
845    pub fn remove(&mut self, key: K, timestamp: u64, replica: u64) {
846        self.apply_removal(key, LwwDot { timestamp, replica });
847    }
848
849    pub fn get(&self, key: &K) -> Option<&V> {
850        self.visible_entry(key).map(|entry| &entry.value)
851    }
852
853    pub fn visible_entry(&self, key: &K) -> Option<&LwwEntry<V>> {
854        let entry = self.entries.get(key)?;
855        match self.removals.get(key) {
856            Some(removal) if entry.dot <= *removal => None,
857            _ => Some(entry),
858        }
859    }
860
861    pub fn entries(&self) -> &BTreeMap<K, LwwEntry<V>> {
862        &self.entries
863    }
864
865    pub fn removals(&self) -> &BTreeMap<K, LwwDot> {
866        &self.removals
867    }
868
869    fn apply_entry(&mut self, key: K, entry: LwwEntry<V>) {
870        match self.entries.get(&key) {
871            Some(current) if current >= &entry => {}
872            _ => {
873                self.entries.insert(key, entry);
874            }
875        }
876    }
877
878    fn apply_removal(&mut self, key: K, dot: LwwDot) {
879        match self.removals.get(&key) {
880            Some(current) if *current >= dot => {}
881            _ => {
882                self.removals.insert(key, dot);
883            }
884        }
885    }
886}
887
888impl<K: Ord + Clone, V: Ord + Clone> LwwMap<K, V> {
889    pub fn merge(&mut self, other: &Self) {
890        for (key, entry) in other.entries.iter() {
891            self.apply_entry(key.clone(), entry.clone());
892        }
893        for (key, dot) in other.removals.iter() {
894            self.apply_removal(key.clone(), *dot);
895        }
896    }
897
898    pub fn value(&self) -> BTreeMap<K, V> {
899        self.entries
900            .iter()
901            .filter_map(|(key, entry)| {
902                if self.visible_entry(key).is_some() {
903                    Some((key.clone(), entry.value.clone()))
904                } else {
905                    None
906                }
907            })
908            .collect()
909    }
910}
911
912impl<K: Ord, V: Ord> Default for LwwMap<K, V> {
913    fn default() -> Self {
914        Self::new()
915    }
916}
917
918impl<K: Ord + Clone, V: Ord + Clone> Mergeable for LwwMap<K, V> {
919    fn merge(&mut self, other: &Self) {
920        LwwMap::merge(self, other);
921    }
922}
923
924impl<K: Ord + Clone, V: Ord + Clone> Crdt for LwwMap<K, V> {
925    type Delta = LwwMapDelta<K, V>;
926
927    fn apply_delta(&mut self, delta: Self::Delta) {
928        match delta {
929            LwwMapDelta::Set {
930                key,
931                timestamp,
932                replica,
933                value,
934            } => self.set(key, timestamp, replica, value),
935            LwwMapDelta::Remove {
936                key,
937                timestamp,
938                replica,
939            } => self.remove(key, timestamp, replica),
940        }
941    }
942}
943
944/// Stable identity for an event-log record.
945#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
946pub struct RecordId {
947    pub replica: u64,
948    pub sequence: u64,
949}
950
951/// An event-log record carrying a CRDT delta.
952#[derive(Clone, Debug, PartialEq, Eq)]
953pub struct Record<D> {
954    pub id: RecordId,
955    pub delta: D,
956}
957
958/// Per-replica contiguous prefixes for anti-entropy pulls.
959///
960/// `get(replica) == n` means every sequence `1..=n` for that replica is known.
961/// Later records that arrive before earlier records must not advance this
962/// prefix, otherwise `since` could hide gaps.
963#[derive(Clone, Debug, Default, PartialEq, Eq)]
964pub struct VersionVector {
965    entries: BTreeMap<u64, u64>,
966}
967
968impl VersionVector {
969    pub fn new() -> Self {
970        VersionVector {
971            entries: BTreeMap::new(),
972        }
973    }
974
975    pub fn get(&self, replica: u64) -> u64 {
976        self.entries.get(&replica).copied().unwrap_or(0)
977    }
978
979    /// Advance this prefix only when `id` is the next contiguous sequence.
980    ///
981    /// Use `EventLog` to ingest out-of-order records; the log remembers gaps
982    /// and advances this vector once the prefix is complete.
983    pub fn observe(&mut self, id: RecordId) {
984        if self.get(id.replica).checked_add(1) == Some(id.sequence) {
985            self.set(id.replica, id.sequence);
986        }
987    }
988
989    pub fn includes(&self, id: RecordId) -> bool {
990        self.get(id.replica) >= id.sequence
991    }
992
993    pub fn entries(&self) -> &BTreeMap<u64, u64> {
994        &self.entries
995    }
996
997    fn set(&mut self, replica: u64, sequence: u64) {
998        if sequence == 0 {
999            self.entries.remove(&replica);
1000        } else {
1001            self.entries.insert(replica, sequence);
1002        }
1003    }
1004}
1005
1006/// Full decoded payload equality distinguishes redelivery from an ID collision.
1007#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1008pub enum Admission {
1009    Accepted,
1010    Duplicate,
1011    Collision,
1012}
1013
1014#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1015pub enum AppendError {
1016    SequenceExhausted,
1017}
1018
1019/// Append-only, deduplicating event log for CRDT deltas.
1020#[derive(Clone, Debug, PartialEq, Eq)]
1021pub struct EventLog<D> {
1022    replica_count: Option<usize>,
1023    records: Vec<Record<D>>,
1024    seen: BTreeMap<RecordId, usize>,
1025    version: VersionVector,
1026}
1027
1028impl<D> EventLog<D> {
1029    pub fn new() -> Self {
1030        EventLog {
1031            replica_count: None,
1032            records: Vec::new(),
1033            seen: BTreeMap::new(),
1034            version: VersionVector::new(),
1035        }
1036    }
1037
1038    /// Create a log for a fixed replica domain, including an empty domain.
1039    pub fn with_replica_count(replica_count: usize) -> Self {
1040        Self {
1041            replica_count: Some(replica_count),
1042            ..Self::new()
1043        }
1044    }
1045
1046    /// Bind persistence metadata to the CRDT's actual domain before recording.
1047    pub fn for_crdt<C: Crdt<Delta = D>>(state: &C) -> Self {
1048        Self {
1049            replica_count: state.replica_count(),
1050            ..Self::new()
1051        }
1052    }
1053
1054    pub fn replica_count(&self) -> Option<usize> {
1055        self.replica_count
1056    }
1057
1058    pub fn append(&mut self, replica: u64, delta: D) -> RecordId
1059    where
1060        D: PartialEq,
1061    {
1062        self.append_with(replica, delta, |_| {})
1063            .expect("event log sequence exhausted")
1064    }
1065
1066    /// Allocate a fresh ID, then use the same gate as incoming records.
1067    pub fn append_with<F>(
1068        &mut self,
1069        replica: u64,
1070        delta: D,
1071        apply: F,
1072    ) -> Result<RecordId, AppendError>
1073    where
1074        D: PartialEq,
1075        F: FnOnce(&D),
1076    {
1077        let sequence = self
1078            .seen
1079            .range(
1080                RecordId {
1081                    replica,
1082                    sequence: 0,
1083                }..=RecordId {
1084                    replica,
1085                    sequence: u64::MAX,
1086                },
1087            )
1088            .next_back()
1089            .map(|(id, _)| id.sequence)
1090            .unwrap_or(0)
1091            .checked_add(1)
1092            .ok_or(AppendError::SequenceExhausted)?;
1093        let id = RecordId { replica, sequence };
1094        let outcome = self.admit_with(Record { id, delta }, apply);
1095        debug_assert_eq!(outcome, Admission::Accepted);
1096        Ok(id)
1097    }
1098
1099    pub fn merge_records<I>(&mut self, records: I) -> Vec<Admission>
1100    where
1101        D: PartialEq,
1102        I: IntoIterator<Item = Record<D>>,
1103    {
1104        records.into_iter().map(|r| self.insert_record(r)).collect()
1105    }
1106
1107    /// The sole record admission decision. Only Accepted invokes `apply`.
1108    /// The callback must be infallible and use the same delta interpretation as
1109    /// replay. This is an in-memory transition, not a crash-durability guarantee.
1110    #[must_use]
1111    pub fn admit_with<F>(&mut self, record: Record<D>, apply: F) -> Admission
1112    where
1113        D: PartialEq,
1114        F: FnOnce(&D),
1115    {
1116        if let Some(&index) = self.seen.get(&record.id) {
1117            return if self.records[index].delta == record.delta {
1118                Admission::Duplicate
1119            } else {
1120                Admission::Collision
1121            };
1122        }
1123        let id = record.id;
1124        self.seen.insert(id, self.records.len());
1125        self.records.push(record);
1126        self.advance_contiguous_version(id.replica);
1127        apply(&self.records.last().expect("accepted record").delta);
1128        Admission::Accepted
1129    }
1130
1131    pub fn version(&self) -> &VersionVector {
1132        &self.version
1133    }
1134
1135    pub fn records(&self) -> &[Record<D>] {
1136        &self.records
1137    }
1138
1139    pub fn insert_record(&mut self, record: Record<D>) -> Admission
1140    where
1141        D: PartialEq,
1142    {
1143        self.admit_with(record, |_| {})
1144    }
1145
1146    fn advance_contiguous_version(&mut self, replica: u64) {
1147        loop {
1148            let Some(next) = self.version.get(replica).checked_add(1) else {
1149                break;
1150            };
1151            if self.seen.contains_key(&RecordId {
1152                replica,
1153                sequence: next,
1154            }) {
1155                self.version.set(replica, next);
1156            } else {
1157                break;
1158            }
1159        }
1160    }
1161}
1162
1163impl<D: Clone> EventLog<D> {
1164    pub fn since(&self, version: &VersionVector) -> Vec<Record<D>> {
1165        self.records
1166            .iter()
1167            .filter(|record| !version.includes(record.id))
1168            .cloned()
1169            .collect()
1170    }
1171}
1172
1173impl<D> Default for EventLog<D> {
1174    fn default() -> Self {
1175        Self::new()
1176    }
1177}
1178
1179#[derive(Clone, Debug, PartialEq, Eq)]
1180pub struct TransportEnvelope<D> {
1181    pub from: u64,
1182    pub to: u64,
1183    pub records: Vec<Record<D>>,
1184}
1185
1186#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1187pub enum TransportError {
1188    NotSubscribed { peer: u64 },
1189    Disconnected { from: u64, to: u64 },
1190}
1191
1192/// Engineered transport coverage contract.
1193///
1194/// A transport adapter is responsible for peer subscription, connectivity
1195/// checks, and moving record batches. It is tested infrastructure, not a
1196/// proof that any real network delivers packets.
1197pub trait TransportAdapter<D> {
1198    fn subscribe(&mut self, peer: u64);
1199    fn is_subscribed(&self, peer: u64) -> bool;
1200    fn set_connected(&mut self, a: u64, b: u64, connected: bool);
1201    fn is_connected(&self, a: u64, b: u64) -> bool;
1202    fn send(&mut self, from: u64, to: u64, records: Vec<Record<D>>) -> Result<(), TransportError>;
1203    fn drain(&mut self, peer: u64) -> Vec<TransportEnvelope<D>>;
1204}
1205
1206/// Deterministic in-memory adapter for coverage-contract fault campaigns.
1207///
1208/// This adapter can drop, duplicate, reorder, partition, and heal links. It is
1209/// useful for CI and demos; it is not a real radio/network adapter.
1210#[derive(Clone, Debug, PartialEq, Eq)]
1211pub struct InMemoryTransport<D> {
1212    subscribed: BTreeSet<u64>,
1213    disconnected: BTreeSet<(u64, u64)>,
1214    queue: Vec<TransportEnvelope<D>>,
1215    dropped: Vec<TransportEnvelope<D>>,
1216    drop_next: bool,
1217    duplicate_next: bool,
1218}
1219
1220impl<D> InMemoryTransport<D> {
1221    pub fn new() -> Self {
1222        InMemoryTransport {
1223            subscribed: BTreeSet::new(),
1224            disconnected: BTreeSet::new(),
1225            queue: Vec::new(),
1226            dropped: Vec::new(),
1227            drop_next: false,
1228            duplicate_next: false,
1229        }
1230    }
1231
1232    pub fn drop_next_send(&mut self) {
1233        self.drop_next = true;
1234    }
1235
1236    pub fn duplicate_next_send(&mut self) {
1237        self.duplicate_next = true;
1238    }
1239
1240    pub fn reverse_pending_for(&mut self, peer: u64) {
1241        let mut peer_items = Vec::new();
1242        let mut retained = Vec::new();
1243        for envelope in self.queue.drain(..) {
1244            if envelope.to == peer {
1245                peer_items.push(envelope);
1246            } else {
1247                retained.push(envelope);
1248            }
1249        }
1250        peer_items.reverse();
1251        retained.extend(peer_items);
1252        self.queue = retained;
1253    }
1254
1255    pub fn pending_len(&self) -> usize {
1256        self.queue.len()
1257    }
1258
1259    pub fn dropped_len(&self) -> usize {
1260        self.dropped.len()
1261    }
1262
1263    fn link(a: u64, b: u64) -> (u64, u64) {
1264        if a <= b {
1265            (a, b)
1266        } else {
1267            (b, a)
1268        }
1269    }
1270}
1271
1272impl<D> Default for InMemoryTransport<D> {
1273    fn default() -> Self {
1274        Self::new()
1275    }
1276}
1277
1278impl<D: Clone> TransportAdapter<D> for InMemoryTransport<D> {
1279    fn subscribe(&mut self, peer: u64) {
1280        self.subscribed.insert(peer);
1281    }
1282
1283    fn is_subscribed(&self, peer: u64) -> bool {
1284        self.subscribed.contains(&peer)
1285    }
1286
1287    fn set_connected(&mut self, a: u64, b: u64, connected: bool) {
1288        let link = Self::link(a, b);
1289        if connected {
1290            self.disconnected.remove(&link);
1291        } else {
1292            self.disconnected.insert(link);
1293        }
1294    }
1295
1296    fn is_connected(&self, a: u64, b: u64) -> bool {
1297        a == b || !self.disconnected.contains(&Self::link(a, b))
1298    }
1299
1300    fn send(&mut self, from: u64, to: u64, records: Vec<Record<D>>) -> Result<(), TransportError> {
1301        if !self.is_subscribed(from) {
1302            return Err(TransportError::NotSubscribed { peer: from });
1303        }
1304        if !self.is_subscribed(to) {
1305            return Err(TransportError::NotSubscribed { peer: to });
1306        }
1307        if !self.is_connected(from, to) {
1308            return Err(TransportError::Disconnected { from, to });
1309        }
1310
1311        let envelope = TransportEnvelope { from, to, records };
1312        if self.drop_next {
1313            self.drop_next = false;
1314            self.dropped.push(envelope);
1315            return Ok(());
1316        }
1317
1318        self.queue.push(envelope.clone());
1319        if self.duplicate_next {
1320            self.duplicate_next = false;
1321            self.queue.push(envelope);
1322        }
1323        Ok(())
1324    }
1325
1326    fn drain(&mut self, peer: u64) -> Vec<TransportEnvelope<D>> {
1327        let mut incoming = Vec::new();
1328        let mut retained = Vec::new();
1329        for envelope in self.queue.drain(..) {
1330            if envelope.to == peer {
1331                incoming.push(envelope);
1332            } else {
1333                retained.push(envelope);
1334            }
1335        }
1336        self.queue = retained;
1337        incoming
1338    }
1339}
1340
1341pub fn anti_entropy<D, T>(
1342    transport: &mut T,
1343    from: u64,
1344    to: u64,
1345    local: &EventLog<D>,
1346    remote_version: &VersionVector,
1347) -> Result<(), TransportError>
1348where
1349    D: Clone,
1350    T: TransportAdapter<D>,
1351{
1352    let records = local.since(remote_version);
1353    if records.is_empty() {
1354        Ok(())
1355    } else {
1356        transport.send(from, to, records)
1357    }
1358}
1359
1360const TAG_RECORD: u8 = 0x01;
1361const TAG_EVENT_LOG: u8 = 0x03;
1362const TAG_GCOUNTER_DELTA: u8 = 0x10;
1363const TAG_PNCOUNTER_INC: u8 = 0x11;
1364const TAG_PNCOUNTER_DEC: u8 = 0x12;
1365const TAG_GSET_U64: u8 = 0x20;
1366const TAG_ORSET_U64: u8 = 0x30;
1367const TAG_ORSET_ADD_U64: u8 = 0x31;
1368const TAG_ORSET_REMOVE_U64: u8 = 0x32;
1369const TAG_ORSET_ADD_STRING: u8 = 0x33;
1370const TAG_ORSET_REMOVE_STRING: u8 = 0x34;
1371const TAG_RGA_U64: u8 = 0x40;
1372const TAG_LWW_REGISTER_DELTA_U64: u8 = 0x50;
1373const TAG_LWW_REGISTER_U64: u8 = 0x51;
1374const TAG_ENABLE_WINS_FLAG_ENABLE_U64: u8 = 0x60;
1375const TAG_ENABLE_WINS_FLAG_DISABLE_U64: u8 = 0x61;
1376const TAG_ENABLE_WINS_FLAG_U64: u8 = 0x62;
1377const TAG_LWW_MAP_SET_U64: u8 = 0x70;
1378const TAG_LWW_MAP_REMOVE_U64: u8 = 0x71;
1379const TAG_LWW_MAP_U64: u8 = 0x72;
1380
1381#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1382pub enum WireError {
1383    OwnershipViolation,
1384    UnexpectedEof,
1385    InvalidTag,
1386    TrailingBytes,
1387    LengthOverflow,
1388    RecordCollision,
1389    IntegrityMismatch,
1390    InvalidUtf8,
1391    /// Legacy frame, or a fixed-domain log constructed without its arity.
1392    MissingShape,
1393    DeltaTypeMismatch,
1394    ReplicaCountMismatch {
1395        expected: usize,
1396        actual: usize,
1397    },
1398    /// Fixed and unbounded replica domains are incompatible.
1399    ArityKindMismatch,
1400}
1401
1402/// Stable, versioned identity for persisted payloads, independent of Rust names.
1403/// External implementations must use a globally unique schema and change it when
1404/// the wire interpretation changes. Never reuse a built-in `safemesh/` identity.
1405pub trait WireSchema {
1406    fn wire_schema() -> Vec<u8>;
1407    const REQUIRES_ARITY: bool = false;
1408}
1409
1410macro_rules! wire_schema {
1411    ($ty:ty, $name:literal, $fixed:expr) => {
1412        impl WireSchema for $ty {
1413            fn wire_schema() -> Vec<u8> {
1414                $name.as_bytes().to_vec()
1415            }
1416            const REQUIRES_ARITY: bool = $fixed;
1417        }
1418    };
1419}
1420
1421wire_schema!(GCounterDelta, "safemesh/gcounter-delta/v1", true);
1422wire_schema!(PnCounterDelta, "safemesh/pncounter-delta/v1", true);
1423wire_schema!(GSet<u64>, "safemesh/gset-u64/v1", false);
1424wire_schema!(OrSetDelta<u64, u64>, "safemesh/orset-delta-u64-u64/v1", false);
1425wire_schema!(OrSetDelta<String, u64>, "safemesh/orset-delta-utf8-u64/v1", false);
1426wire_schema!(OrSet<u64, u64>, "safemesh/orset-u64-u64/v1", false);
1427wire_schema!(Rga<u64, u64>, "safemesh/rga-u64-u64/v1", false);
1428wire_schema!(
1429    LwwRegisterDelta<u64>,
1430    "safemesh/lww-register-delta-u64/v1",
1431    false
1432);
1433wire_schema!(LwwRegister<u64>, "safemesh/lww-register-u64/v1", false);
1434wire_schema!(
1435    EnableWinsFlagDelta<u64>,
1436    "safemesh/enable-wins-flag-delta-u64/v1",
1437    false
1438);
1439wire_schema!(
1440    EnableWinsFlag<u64>,
1441    "safemesh/enable-wins-flag-u64/v1",
1442    false
1443);
1444wire_schema!(LwwMapDelta<u64, u64>, "safemesh/lww-map-delta-u64-u64/v1", false);
1445wire_schema!(LwwMap<u64, u64>, "safemesh/lww-map-u64-u64/v1", false);
1446
1447impl<D: WireSchema> WireSchema for Record<D> {
1448    fn wire_schema() -> Vec<u8> {
1449        let mut schema = b"safemesh/record/v1/".to_vec();
1450        schema.extend(D::wire_schema());
1451        schema
1452    }
1453}
1454
1455impl<D: WireSchema> WireSchema for EventLog<D> {
1456    fn wire_schema() -> Vec<u8> {
1457        let mut schema = b"safemesh/event-log/v2/".to_vec();
1458        schema.extend(D::wire_schema());
1459        schema
1460    }
1461}
1462
1463impl<D: WireDecode + WireSchema + PartialEq> EventLog<D> {
1464    /// Decode and compare the saved shape with the destination before replay.
1465    /// Plain `from_wire_bytes` decodes a log and retains its domain; it does not
1466    /// load a CRDT. Use this method at every persisted-state loading boundary.
1467    pub fn from_wire_bytes_for<C: Crdt<Delta = D>>(
1468        bytes: &[u8],
1469        state: &C,
1470    ) -> Result<Self, WireError> {
1471        let log = Self::from_wire_bytes(bytes)?;
1472        match (state.replica_count(), log.replica_count) {
1473            (Some(expected), Some(actual)) if expected != actual => {
1474                return Err(WireError::ReplicaCountMismatch { expected, actual })
1475            }
1476            (None, Some(_)) | (Some(_), None) => return Err(WireError::ArityKindMismatch),
1477            _ => {}
1478        }
1479        for record in log.records() {
1480            state.validate_record(record.id, &record.delta)?;
1481        }
1482        Ok(log)
1483    }
1484}
1485
1486pub trait WireEncode {
1487    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError>;
1488
1489    fn to_wire_bytes(&self) -> Result<Vec<u8>, WireError> {
1490        let mut out = Vec::new();
1491        self.encode_wire(&mut out)?;
1492        Ok(out)
1493    }
1494}
1495
1496pub trait WireDecode: Sized {
1497    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError>;
1498
1499    fn from_wire_bytes(bytes: &[u8]) -> Result<Self, WireError> {
1500        let mut cursor = WireCursor::new(bytes);
1501        let value = Self::decode_wire(&mut cursor)?;
1502        if cursor.is_empty() {
1503            Ok(value)
1504        } else {
1505            Err(WireError::TrailingBytes)
1506        }
1507    }
1508}
1509
1510pub struct WireCursor<'a> {
1511    bytes: &'a [u8],
1512    offset: usize,
1513}
1514
1515impl<'a> WireCursor<'a> {
1516    pub fn new(bytes: &'a [u8]) -> Self {
1517        WireCursor { bytes, offset: 0 }
1518    }
1519
1520    pub fn is_empty(&self) -> bool {
1521        self.offset == self.bytes.len()
1522    }
1523
1524    fn read_u8(&mut self) -> Result<u8, WireError> {
1525        let byte = *self
1526            .bytes
1527            .get(self.offset)
1528            .ok_or(WireError::UnexpectedEof)?;
1529        self.offset += 1;
1530        Ok(byte)
1531    }
1532
1533    fn read_u32(&mut self) -> Result<u32, WireError> {
1534        let bytes = self.read_exact(4)?;
1535        Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
1536    }
1537
1538    fn read_u64(&mut self) -> Result<u64, WireError> {
1539        let bytes = self.read_exact(8)?;
1540        Ok(u64::from_le_bytes([
1541            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
1542        ]))
1543    }
1544
1545    fn read_len(&mut self) -> Result<usize, WireError> {
1546        usize::try_from(self.read_u32()?).map_err(|_| WireError::LengthOverflow)
1547    }
1548
1549    fn read_exact(&mut self, len: usize) -> Result<&'a [u8], WireError> {
1550        let end = self
1551            .offset
1552            .checked_add(len)
1553            .ok_or(WireError::LengthOverflow)?;
1554        let bytes = self
1555            .bytes
1556            .get(self.offset..end)
1557            .ok_or(WireError::UnexpectedEof)?;
1558        self.offset = end;
1559        Ok(bytes)
1560    }
1561}
1562
1563fn write_u8(out: &mut Vec<u8>, value: u8) {
1564    out.push(value);
1565}
1566
1567fn write_u32(out: &mut Vec<u8>, value: u32) {
1568    out.extend_from_slice(&value.to_le_bytes());
1569}
1570
1571fn write_u64(out: &mut Vec<u8>, value: u64) {
1572    out.extend_from_slice(&value.to_le_bytes());
1573}
1574
1575fn write_len(out: &mut Vec<u8>, len: usize) -> Result<(), WireError> {
1576    let len = u32::try_from(len).map_err(|_| WireError::LengthOverflow)?;
1577    write_u32(out, len);
1578    Ok(())
1579}
1580
1581fn write_bytes(out: &mut Vec<u8>, bytes: &[u8]) -> Result<(), WireError> {
1582    write_len(out, bytes.len())?;
1583    out.extend_from_slice(bytes);
1584    Ok(())
1585}
1586
1587fn read_tag(cursor: &mut WireCursor<'_>, expected: u8) -> Result<(), WireError> {
1588    match cursor.read_u8()? {
1589        tag if tag == expected => Ok(()),
1590        _ => Err(WireError::InvalidTag),
1591    }
1592}
1593
1594impl WireEncode for GCounterDelta {
1595    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
1596        write_u8(out, TAG_GCOUNTER_DELTA);
1597        write_u64(
1598            out,
1599            u64::try_from(self.replica).map_err(|_| WireError::LengthOverflow)?,
1600        );
1601        write_u64(out, self.tally);
1602        Ok(())
1603    }
1604}
1605
1606impl WireDecode for GCounterDelta {
1607    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
1608        read_tag(cursor, TAG_GCOUNTER_DELTA)?;
1609        let replica = usize::try_from(cursor.read_u64()?).map_err(|_| WireError::LengthOverflow)?;
1610        let tally = cursor.read_u64()?;
1611        Ok(GCounterDelta { replica, tally })
1612    }
1613}
1614
1615impl WireEncode for PnCounterDelta {
1616    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
1617        match self {
1618            PnCounterDelta::Inc { replica, tally } => {
1619                write_u8(out, TAG_PNCOUNTER_INC);
1620                write_u64(
1621                    out,
1622                    u64::try_from(*replica).map_err(|_| WireError::LengthOverflow)?,
1623                );
1624                write_u64(out, *tally);
1625            }
1626            PnCounterDelta::Dec { replica, tally } => {
1627                write_u8(out, TAG_PNCOUNTER_DEC);
1628                write_u64(
1629                    out,
1630                    u64::try_from(*replica).map_err(|_| WireError::LengthOverflow)?,
1631                );
1632                write_u64(out, *tally);
1633            }
1634        }
1635        Ok(())
1636    }
1637}
1638
1639impl WireDecode for PnCounterDelta {
1640    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
1641        let tag = cursor.read_u8()?;
1642        let replica = usize::try_from(cursor.read_u64()?).map_err(|_| WireError::LengthOverflow)?;
1643        let tally = cursor.read_u64()?;
1644        match tag {
1645            TAG_PNCOUNTER_INC => Ok(PnCounterDelta::Inc { replica, tally }),
1646            TAG_PNCOUNTER_DEC => Ok(PnCounterDelta::Dec { replica, tally }),
1647            _ => Err(WireError::InvalidTag),
1648        }
1649    }
1650}
1651
1652impl WireEncode for GSet<u64> {
1653    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
1654        write_u8(out, TAG_GSET_U64);
1655        write_len(out, self.elements.len())?;
1656        for element in &self.elements {
1657            write_u64(out, *element);
1658        }
1659        Ok(())
1660    }
1661}
1662
1663impl WireDecode for GSet<u64> {
1664    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
1665        read_tag(cursor, TAG_GSET_U64)?;
1666        let mut set = GSet::new();
1667        for _ in 0..cursor.read_len()? {
1668            set.insert(cursor.read_u64()?);
1669        }
1670        Ok(set)
1671    }
1672}
1673
1674impl WireEncode for OrSetDelta<u64, u64> {
1675    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
1676        match self {
1677            OrSetDelta::Add { element, token } => {
1678                write_u8(out, TAG_ORSET_ADD_U64);
1679                write_u64(out, *element);
1680                write_u64(out, *token);
1681            }
1682            OrSetDelta::Remove { tokens } => {
1683                write_u8(out, TAG_ORSET_REMOVE_U64);
1684                write_len(out, tokens.len())?;
1685                // Preserve order and duplicates for exact delta round trips.
1686                for token in tokens {
1687                    write_u64(out, *token);
1688                }
1689            }
1690        }
1691        Ok(())
1692    }
1693}
1694
1695impl WireDecode for OrSetDelta<u64, u64> {
1696    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
1697        match cursor.read_u8()? {
1698            TAG_ORSET_ADD_U64 => Ok(OrSetDelta::Add {
1699                element: cursor.read_u64()?,
1700                token: cursor.read_u64()?,
1701            }),
1702            TAG_ORSET_REMOVE_U64 => {
1703                let mut tokens = Vec::new();
1704                for _ in 0..cursor.read_len()? {
1705                    tokens.push(cursor.read_u64()?);
1706                }
1707                Ok(OrSetDelta::Remove { tokens })
1708            }
1709            _ => Err(WireError::InvalidTag),
1710        }
1711    }
1712}
1713
1714// UTF-8 delta tags are distinct from the u64 delta tags, including Remove.
1715// Add carries a byte-length-prefixed UTF-8 element followed by a u64 token;
1716// Remove carries a token count followed by u64 tokens in their original order.
1717impl WireEncode for OrSetDelta<String, u64> {
1718    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
1719        match self {
1720            OrSetDelta::Add { element, token } => {
1721                write_u8(out, TAG_ORSET_ADD_STRING);
1722                write_bytes(out, element.as_bytes())?;
1723                write_u64(out, *token);
1724            }
1725            OrSetDelta::Remove { tokens } => {
1726                write_u8(out, TAG_ORSET_REMOVE_STRING);
1727                write_len(out, tokens.len())?;
1728                for token in tokens {
1729                    write_u64(out, *token);
1730                }
1731            }
1732        }
1733        Ok(())
1734    }
1735}
1736
1737impl WireDecode for OrSetDelta<String, u64> {
1738    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
1739        match cursor.read_u8()? {
1740            TAG_ORSET_ADD_STRING => {
1741                let len = cursor.read_len()?;
1742                let element = core::str::from_utf8(cursor.read_exact(len)?)
1743                    .map_err(|_| WireError::InvalidUtf8)?;
1744                let token = cursor.read_u64()?;
1745                Ok(OrSetDelta::Add {
1746                    element: String::from(element),
1747                    token,
1748                })
1749            }
1750            TAG_ORSET_REMOVE_STRING => {
1751                let mut tokens = Vec::new();
1752                for _ in 0..cursor.read_len()? {
1753                    tokens.push(cursor.read_u64()?);
1754                }
1755                Ok(OrSetDelta::Remove { tokens })
1756            }
1757            _ => Err(WireError::InvalidTag),
1758        }
1759    }
1760}
1761
1762impl WireEncode for OrSet<u64, u64> {
1763    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
1764        write_u8(out, TAG_ORSET_U64);
1765        write_len(out, self.adds.len())?;
1766        for (element, token) in &self.adds {
1767            write_u64(out, *element);
1768            write_u64(out, *token);
1769        }
1770        write_len(out, self.tombstones.len())?;
1771        for token in &self.tombstones {
1772            write_u64(out, *token);
1773        }
1774        Ok(())
1775    }
1776}
1777
1778impl WireDecode for OrSet<u64, u64> {
1779    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
1780        read_tag(cursor, TAG_ORSET_U64)?;
1781        let mut set = OrSet::new();
1782        for _ in 0..cursor.read_len()? {
1783            let element = cursor.read_u64()?;
1784            let token = cursor.read_u64()?;
1785            set.add(element, token);
1786        }
1787        let mut tombstones = Vec::new();
1788        for _ in 0..cursor.read_len()? {
1789            tombstones.push(cursor.read_u64()?);
1790        }
1791        set.apply_remove(tombstones);
1792        Ok(set)
1793    }
1794}
1795
1796impl WireEncode for Rga<u64, u64> {
1797    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
1798        write_u8(out, TAG_RGA_U64);
1799        write_len(out, self.placed.len())?;
1800        for (position, value) in &self.placed {
1801            write_u64(out, *position);
1802            write_u64(out, *value);
1803        }
1804        write_len(out, self.tombstones.len())?;
1805        for position in &self.tombstones {
1806            write_u64(out, *position);
1807        }
1808        Ok(())
1809    }
1810}
1811
1812impl WireDecode for Rga<u64, u64> {
1813    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
1814        read_tag(cursor, TAG_RGA_U64)?;
1815        let mut rga = Rga::new();
1816        for _ in 0..cursor.read_len()? {
1817            let position = cursor.read_u64()?;
1818            let value = cursor.read_u64()?;
1819            rga.insert(position, value);
1820        }
1821        for _ in 0..cursor.read_len()? {
1822            rga.delete(cursor.read_u64()?);
1823        }
1824        Ok(rga)
1825    }
1826}
1827
1828impl<D: WireEncode> WireEncode for Record<D> {
1829    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
1830        write_u8(out, TAG_RECORD);
1831        write_u64(out, self.id.replica);
1832        write_u64(out, self.id.sequence);
1833        write_bytes(out, &self.delta.to_wire_bytes()?)?;
1834        Ok(())
1835    }
1836}
1837
1838impl<D: WireDecode> WireDecode for Record<D> {
1839    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
1840        read_tag(cursor, TAG_RECORD)?;
1841        let id = RecordId {
1842            replica: cursor.read_u64()?,
1843            sequence: cursor.read_u64()?,
1844        };
1845        let delta_len = cursor.read_len()?;
1846        let delta_bytes = cursor.read_exact(delta_len)?;
1847        let delta = D::from_wire_bytes(delta_bytes)?;
1848        Ok(Record { id, delta })
1849    }
1850}
1851
1852// CRC-32/ISO-HDLC: reflected polynomial, all-ones initialization and final XOR.
1853// Detects accidental corruption, including every single-byte change; not a MAC.
1854fn frame_crc32(bytes: &[u8]) -> u32 {
1855    let mut crc = u32::MAX;
1856    for &byte in bytes {
1857        crc ^= u32::from(byte);
1858        for _ in 0..8 {
1859            crc = (crc >> 1) ^ (0xedb8_8320 & 0u32.wrapping_sub(crc & 1));
1860        }
1861    }
1862    !crc
1863}
1864
1865// Shape-bearing frame: tag, body length, complemented length,
1866// body (shape marker, schema, arity, record count and length-prefixed records),
1867// CRC of length fields + body. Old unshaped frames return MissingShape.
1868// u32::MAX cannot be the count of a valid old body within a u32 frame length.
1869// Frame lengths, count and CRC are little-endian u32. Check the length pair before trusting it,
1870// then verify the CRC before decoding any record or invoking a payload decoder.
1871impl<D: WireEncode + WireSchema> WireEncode for EventLog<D> {
1872    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
1873        if D::REQUIRES_ARITY && self.replica_count.is_none() {
1874            return Err(WireError::MissingShape);
1875        }
1876        let mut body = Vec::new();
1877        write_u32(&mut body, u32::MAX);
1878        write_bytes(&mut body, &D::wire_schema())?;
1879        match self.replica_count {
1880            Some(count) => {
1881                write_u8(&mut body, 1);
1882                write_u64(
1883                    &mut body,
1884                    u64::try_from(count).map_err(|_| WireError::LengthOverflow)?,
1885                );
1886            }
1887            None => write_u8(&mut body, 0),
1888        }
1889        write_len(&mut body, self.records.len())?;
1890        for record in &self.records {
1891            write_bytes(&mut body, &record.to_wire_bytes()?)?;
1892        }
1893        let len = u32::try_from(body.len()).map_err(|_| WireError::LengthOverflow)?;
1894        write_u8(out, TAG_EVENT_LOG);
1895        let start = out.len();
1896        write_u32(out, len);
1897        write_u32(out, !len);
1898        out.extend_from_slice(&body);
1899        let checksum = frame_crc32(&out[start..]);
1900        write_u32(out, checksum);
1901        Ok(())
1902    }
1903}
1904
1905impl<D: WireDecode + WireSchema + PartialEq> WireDecode for EventLog<D> {
1906    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
1907        read_tag(cursor, TAG_EVENT_LOG)?;
1908        let start = cursor.offset;
1909        let len = cursor.read_u32()?;
1910        if cursor.read_u32()? != !len {
1911            return Err(WireError::IntegrityMismatch);
1912        }
1913        let body =
1914            cursor.read_exact(usize::try_from(len).map_err(|_| WireError::LengthOverflow)?)?;
1915        let checksum = frame_crc32(&cursor.bytes[start..cursor.offset]);
1916        if cursor.read_u32()? != checksum {
1917            return Err(WireError::IntegrityMismatch);
1918        }
1919        let mut body = WireCursor::new(body);
1920        if body.read_u32()? != u32::MAX {
1921            return Err(WireError::MissingShape);
1922        }
1923        let schema_len = body.read_len()?;
1924        if body.read_exact(schema_len)? != D::wire_schema() {
1925            return Err(WireError::DeltaTypeMismatch);
1926        }
1927        let replica_count = match body.read_u8()? {
1928            0 if !D::REQUIRES_ARITY => None,
1929            0 => return Err(WireError::MissingShape),
1930            1 => Some(usize::try_from(body.read_u64()?).map_err(|_| WireError::LengthOverflow)?),
1931            _ => return Err(WireError::ArityKindMismatch),
1932        };
1933        let mut log = EventLog {
1934            replica_count,
1935            ..EventLog::new()
1936        };
1937        for _ in 0..body.read_len()? {
1938            let record_len = body.read_len()?;
1939            let record_bytes = body.read_exact(record_len)?;
1940            if log.insert_record(Record::<D>::from_wire_bytes(record_bytes)?)
1941                == Admission::Collision
1942            {
1943                return Err(WireError::RecordCollision);
1944            }
1945        }
1946        if !body.is_empty() {
1947            return Err(WireError::TrailingBytes);
1948        }
1949        Ok(log)
1950    }
1951}
1952
1953impl WireEncode for LwwRegisterDelta<u64> {
1954    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
1955        write_u8(out, TAG_LWW_REGISTER_DELTA_U64);
1956        write_u64(out, self.timestamp);
1957        write_u64(out, self.replica);
1958        write_u64(out, self.value);
1959        Ok(())
1960    }
1961}
1962
1963impl WireDecode for LwwRegisterDelta<u64> {
1964    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
1965        read_tag(cursor, TAG_LWW_REGISTER_DELTA_U64)?;
1966        Ok(LwwRegisterDelta {
1967            timestamp: cursor.read_u64()?,
1968            replica: cursor.read_u64()?,
1969            value: cursor.read_u64()?,
1970        })
1971    }
1972}
1973
1974impl WireEncode for LwwRegister<u64> {
1975    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
1976        write_u8(out, TAG_LWW_REGISTER_U64);
1977        match self.entry() {
1978            Some(entry) => {
1979                write_u8(out, 1);
1980                write_u64(out, entry.dot.timestamp);
1981                write_u64(out, entry.dot.replica);
1982                write_u64(out, entry.value);
1983            }
1984            None => write_u8(out, 0),
1985        }
1986        Ok(())
1987    }
1988}
1989
1990impl WireDecode for LwwRegister<u64> {
1991    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
1992        read_tag(cursor, TAG_LWW_REGISTER_U64)?;
1993        let present = cursor.read_u8()?;
1994        match present {
1995            0 => Ok(LwwRegister::new()),
1996            1 => {
1997                let mut register = LwwRegister::new();
1998                register.set(cursor.read_u64()?, cursor.read_u64()?, cursor.read_u64()?);
1999                Ok(register)
2000            }
2001            _ => Err(WireError::InvalidTag),
2002        }
2003    }
2004}
2005
2006impl WireEncode for EnableWinsFlagDelta<u64> {
2007    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
2008        match self {
2009            EnableWinsFlagDelta::Enable { token } => {
2010                write_u8(out, TAG_ENABLE_WINS_FLAG_ENABLE_U64);
2011                write_u64(out, *token);
2012            }
2013            EnableWinsFlagDelta::Disable { tokens } => {
2014                write_u8(out, TAG_ENABLE_WINS_FLAG_DISABLE_U64);
2015                let mut sorted = tokens.clone();
2016                sorted.sort();
2017                sorted.dedup();
2018                write_len(out, sorted.len())?;
2019                for token in sorted {
2020                    write_u64(out, token);
2021                }
2022            }
2023        }
2024        Ok(())
2025    }
2026}
2027
2028impl WireDecode for EnableWinsFlagDelta<u64> {
2029    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
2030        let tag = cursor.read_u8()?;
2031        match tag {
2032            TAG_ENABLE_WINS_FLAG_ENABLE_U64 => Ok(EnableWinsFlagDelta::Enable {
2033                token: cursor.read_u64()?,
2034            }),
2035            TAG_ENABLE_WINS_FLAG_DISABLE_U64 => {
2036                let len = cursor.read_len()?;
2037                let mut tokens = Vec::new();
2038                for _ in 0..len {
2039                    tokens.push(cursor.read_u64()?);
2040                }
2041                Ok(EnableWinsFlagDelta::Disable { tokens })
2042            }
2043            _ => Err(WireError::InvalidTag),
2044        }
2045    }
2046}
2047
2048impl WireEncode for EnableWinsFlag<u64> {
2049    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
2050        write_u8(out, TAG_ENABLE_WINS_FLAG_U64);
2051        write_len(out, self.enables.len())?;
2052        for token in self.enables.iter() {
2053            write_u64(out, *token);
2054        }
2055        write_len(out, self.tombstones.len())?;
2056        for token in self.tombstones.iter() {
2057            write_u64(out, *token);
2058        }
2059        Ok(())
2060    }
2061}
2062
2063impl WireDecode for EnableWinsFlag<u64> {
2064    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
2065        read_tag(cursor, TAG_ENABLE_WINS_FLAG_U64)?;
2066        let enable_len = cursor.read_len()?;
2067        let mut flag = EnableWinsFlag::new();
2068        for _ in 0..enable_len {
2069            flag.enable(cursor.read_u64()?);
2070        }
2071        let tombstone_len = cursor.read_len()?;
2072        for _ in 0..tombstone_len {
2073            flag.disable([cursor.read_u64()?]);
2074        }
2075        Ok(flag)
2076    }
2077}
2078
2079impl WireEncode for LwwMapDelta<u64, u64> {
2080    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
2081        match self {
2082            LwwMapDelta::Set {
2083                key,
2084                timestamp,
2085                replica,
2086                value,
2087            } => {
2088                write_u8(out, TAG_LWW_MAP_SET_U64);
2089                write_u64(out, *key);
2090                write_u64(out, *timestamp);
2091                write_u64(out, *replica);
2092                write_u64(out, *value);
2093            }
2094            LwwMapDelta::Remove {
2095                key,
2096                timestamp,
2097                replica,
2098            } => {
2099                write_u8(out, TAG_LWW_MAP_REMOVE_U64);
2100                write_u64(out, *key);
2101                write_u64(out, *timestamp);
2102                write_u64(out, *replica);
2103            }
2104        }
2105        Ok(())
2106    }
2107}
2108
2109impl WireDecode for LwwMapDelta<u64, u64> {
2110    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
2111        let tag = cursor.read_u8()?;
2112        match tag {
2113            TAG_LWW_MAP_SET_U64 => Ok(LwwMapDelta::Set {
2114                key: cursor.read_u64()?,
2115                timestamp: cursor.read_u64()?,
2116                replica: cursor.read_u64()?,
2117                value: cursor.read_u64()?,
2118            }),
2119            TAG_LWW_MAP_REMOVE_U64 => Ok(LwwMapDelta::Remove {
2120                key: cursor.read_u64()?,
2121                timestamp: cursor.read_u64()?,
2122                replica: cursor.read_u64()?,
2123            }),
2124            _ => Err(WireError::InvalidTag),
2125        }
2126    }
2127}
2128
2129impl WireEncode for LwwMap<u64, u64> {
2130    fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError> {
2131        write_u8(out, TAG_LWW_MAP_U64);
2132        write_len(out, self.entries.len())?;
2133        for (key, entry) in self.entries.iter() {
2134            write_u64(out, *key);
2135            write_u64(out, entry.dot.timestamp);
2136            write_u64(out, entry.dot.replica);
2137            write_u64(out, entry.value);
2138        }
2139        write_len(out, self.removals.len())?;
2140        for (key, dot) in self.removals.iter() {
2141            write_u64(out, *key);
2142            write_u64(out, dot.timestamp);
2143            write_u64(out, dot.replica);
2144        }
2145        Ok(())
2146    }
2147}
2148
2149impl WireDecode for LwwMap<u64, u64> {
2150    fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError> {
2151        read_tag(cursor, TAG_LWW_MAP_U64)?;
2152        let entry_len = cursor.read_len()?;
2153        let mut map = LwwMap::new();
2154        for _ in 0..entry_len {
2155            map.set(
2156                cursor.read_u64()?,
2157                cursor.read_u64()?,
2158                cursor.read_u64()?,
2159                cursor.read_u64()?,
2160            );
2161        }
2162        let removal_len = cursor.read_len()?;
2163        for _ in 0..removal_len {
2164            map.remove(cursor.read_u64()?, cursor.read_u64()?, cursor.read_u64()?);
2165        }
2166        Ok(map)
2167    }
2168}
2169
2170#[cfg(feature = "laws")]
2171pub mod laws {
2172    use super::{Crdt, Mergeable};
2173    use alloc::vec::Vec;
2174
2175    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
2176    pub enum Law {
2177        Commutative,
2178        Associative,
2179        Idempotent,
2180        Identity,
2181        Convergence,
2182    }
2183
2184    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
2185    pub struct LawFailure {
2186        pub law: Law,
2187        pub a: usize,
2188        pub b: Option<usize>,
2189        pub c: Option<usize>,
2190    }
2191
2192    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
2193    pub struct LawReport {
2194        pub scenarios: usize,
2195    }
2196
2197    pub fn check_merge_laws<T>(identity: &T, samples: &[T]) -> Result<LawReport, LawFailure>
2198    where
2199        T: Mergeable + Clone + PartialEq,
2200    {
2201        let mut scenarios = 0;
2202
2203        for (a_idx, a) in samples.iter().enumerate() {
2204            let mut aa = a.clone();
2205            aa.merge(a);
2206            scenarios += 1;
2207            if aa != *a {
2208                return Err(LawFailure {
2209                    law: Law::Idempotent,
2210                    a: a_idx,
2211                    b: None,
2212                    c: None,
2213                });
2214            }
2215
2216            let mut left_identity = identity.clone();
2217            left_identity.merge(a);
2218            let mut right_identity = a.clone();
2219            right_identity.merge(identity);
2220            scenarios += 2;
2221            if left_identity != *a || right_identity != *a {
2222                return Err(LawFailure {
2223                    law: Law::Identity,
2224                    a: a_idx,
2225                    b: None,
2226                    c: None,
2227                });
2228            }
2229
2230            for (b_idx, b) in samples.iter().enumerate() {
2231                let mut ab = a.clone();
2232                ab.merge(b);
2233                let mut ba = b.clone();
2234                ba.merge(a);
2235                scenarios += 1;
2236                if ab != ba {
2237                    return Err(LawFailure {
2238                        law: Law::Commutative,
2239                        a: a_idx,
2240                        b: Some(b_idx),
2241                        c: None,
2242                    });
2243                }
2244
2245                for (c_idx, c) in samples.iter().enumerate() {
2246                    let mut left = a.clone();
2247                    left.merge(b);
2248                    left.merge(c);
2249
2250                    let mut right_inner = b.clone();
2251                    right_inner.merge(c);
2252                    let mut right = a.clone();
2253                    right.merge(&right_inner);
2254
2255                    scenarios += 1;
2256                    if left != right {
2257                        return Err(LawFailure {
2258                            law: Law::Associative,
2259                            a: a_idx,
2260                            b: Some(b_idx),
2261                            c: Some(c_idx),
2262                        });
2263                    }
2264                }
2265            }
2266        }
2267
2268        Ok(LawReport { scenarios })
2269    }
2270
2271    pub fn check_crdt_convergence<C>(
2272        seed_state: &C,
2273        deltas: &[C::Delta],
2274    ) -> Result<LawReport, LawFailure>
2275    where
2276        C: Crdt + Clone + PartialEq,
2277        C::Delta: Clone,
2278    {
2279        let expected = apply_all(seed_state, deltas.iter().cloned());
2280        let mut scenarios = 1;
2281
2282        let reverse = apply_all(seed_state, deltas.iter().cloned().rev());
2283        scenarios += 1;
2284        if reverse != expected {
2285            return Err(convergence_failure(0));
2286        }
2287
2288        let duplicated = apply_all(
2289            seed_state,
2290            deltas.iter().cloned().flat_map(|d| [d.clone(), d]),
2291        );
2292        scenarios += 1;
2293        if duplicated != expected {
2294            return Err(convergence_failure(1));
2295        }
2296
2297        for (seed_idx, seed) in [0x51a7_3eed_u64, 0xc0ff_ee13_u64, 0x5afe_0001_u64]
2298            .iter()
2299            .copied()
2300            .enumerate()
2301        {
2302            let shuffled = shuffled(deltas, seed);
2303            let shuffled_state = apply_all(seed_state, shuffled.into_iter());
2304            scenarios += 1;
2305            if shuffled_state != expected {
2306                return Err(convergence_failure(2 + seed_idx));
2307            }
2308        }
2309
2310        let mut even = seed_state.clone();
2311        let mut odd = seed_state.clone();
2312        for (idx, delta) in deltas.iter().cloned().enumerate() {
2313            if idx % 2 == 0 {
2314                even.apply_delta(delta);
2315            } else {
2316                odd.apply_delta(delta);
2317            }
2318        }
2319        even.merge(&odd);
2320        scenarios += 1;
2321        if even != expected {
2322            return Err(convergence_failure(5));
2323        }
2324
2325        let mut lossy_a = seed_state.clone();
2326        let mut lossy_b = seed_state.clone();
2327        for (idx, delta) in deltas.iter().cloned().enumerate() {
2328            if idx % 3 == 0 {
2329                lossy_b.apply_delta(delta);
2330            } else {
2331                lossy_a.apply_delta(delta);
2332            }
2333        }
2334        lossy_a.merge(&lossy_b);
2335        scenarios += 1;
2336        if lossy_a != expected {
2337            return Err(convergence_failure(6));
2338        }
2339
2340        Ok(LawReport { scenarios })
2341    }
2342
2343    fn apply_all<C, I>(seed_state: &C, deltas: I) -> C
2344    where
2345        C: Crdt + Clone,
2346        I: IntoIterator<Item = C::Delta>,
2347    {
2348        let mut state = seed_state.clone();
2349        for delta in deltas {
2350            state.apply_delta(delta);
2351        }
2352        state
2353    }
2354
2355    fn convergence_failure(a: usize) -> LawFailure {
2356        LawFailure {
2357            law: Law::Convergence,
2358            a,
2359            b: None,
2360            c: None,
2361        }
2362    }
2363
2364    fn shuffled<T: Clone>(items: &[T], seed: u64) -> Vec<T> {
2365        let mut out = items.to_vec();
2366        let mut rng = XorShift64::new(seed);
2367        let len = out.len();
2368        if len < 2 {
2369            return out;
2370        }
2371        let mut i = len - 1;
2372        while i > 0 {
2373            let j = rng.next_usize(i + 1);
2374            out.swap(i, j);
2375            i -= 1;
2376        }
2377        out
2378    }
2379
2380    struct XorShift64 {
2381        state: u64,
2382    }
2383
2384    impl XorShift64 {
2385        fn new(seed: u64) -> Self {
2386            let state = if seed == 0 {
2387                0x9e37_79b9_7f4a_7c15
2388            } else {
2389                seed
2390            };
2391            XorShift64 { state }
2392        }
2393
2394        fn next_u64(&mut self) -> u64 {
2395            let mut x = self.state;
2396            x ^= x << 13;
2397            x ^= x >> 7;
2398            x ^= x << 17;
2399            self.state = x;
2400            x
2401        }
2402
2403        fn next_usize(&mut self, upper: usize) -> usize {
2404            (self.next_u64() as usize) % upper
2405        }
2406    }
2407}
2408
2409#[cfg(test)]
2410mod frame_tests {
2411    use super::*;
2412    use alloc::vec;
2413
2414    #[test]
2415    fn legacy_integrity_frames_refuse_missing_shape() {
2416        for records in [vec![], vec![record(1, 5)]] {
2417            let mut body = Vec::new();
2418            write_len(&mut body, records.len()).unwrap();
2419            for record in records {
2420                write_bytes(&mut body, &record.to_wire_bytes().unwrap()).unwrap();
2421            }
2422            let mut bytes = vec![TAG_EVENT_LOG];
2423            let len = u32::try_from(body.len()).unwrap();
2424            write_u32(&mut bytes, len);
2425            write_u32(&mut bytes, !len);
2426            bytes.extend(body);
2427            let crc = frame_crc32(&bytes[1..]);
2428            write_u32(&mut bytes, crc);
2429            assert_eq!(
2430                EventLog::<GCounterDelta>::from_wire_bytes(&bytes),
2431                Err(WireError::MissingShape)
2432            );
2433        }
2434    }
2435
2436    #[test]
2437    fn crc32_matches_standard_check_vector() {
2438        assert_eq!(frame_crc32(b"123456789"), 0xcbf4_3926);
2439    }
2440
2441    #[test]
2442    fn binding_collision_fixtures_use_product_encoder() {
2443        extern crate std;
2444        fn fixture<D: WireEncode + WireDecode + WireSchema + PartialEq>(
2445            name: &str,
2446            deltas: [D; 2],
2447        ) {
2448            let mut log = if D::REQUIRES_ARITY {
2449                EventLog::with_replica_count(2)
2450            } else {
2451                EventLog::new()
2452            };
2453            log.records = deltas
2454                .into_iter()
2455                .map(|delta| Record {
2456                    id: RecordId {
2457                        replica: 1,
2458                        sequence: 1,
2459                    },
2460                    delta,
2461                })
2462                .collect();
2463            let bytes = log.to_wire_bytes().unwrap();
2464            assert!(matches!(
2465                EventLog::<D>::from_wire_bytes(&bytes),
2466                Err(WireError::RecordCollision)
2467            ));
2468            if let Some(directory) = std::env::var_os("SAFEMESH_FRAME_FIXTURE_DIR") {
2469                std::fs::write(std::path::Path::new(&directory).join(name), bytes).unwrap();
2470            }
2471        }
2472        fixture(
2473            "gcounter-collision.bin",
2474            [
2475                GCounterDelta {
2476                    replica: 1,
2477                    tally: 5,
2478                },
2479                GCounterDelta {
2480                    replica: 1,
2481                    tally: 9,
2482                },
2483            ],
2484        );
2485        fixture(
2486            "flag-collision.bin",
2487            [
2488                EnableWinsFlagDelta::Enable { token: 5u64 },
2489                EnableWinsFlagDelta::Enable { token: 9u64 },
2490            ],
2491        );
2492        fixture(
2493            "register-collision.bin",
2494            [
2495                LwwRegisterDelta {
2496                    timestamp: 1,
2497                    replica: 1,
2498                    value: 5u64,
2499                },
2500                LwwRegisterDelta {
2501                    timestamp: 2,
2502                    replica: 1,
2503                    value: 9u64,
2504                },
2505            ],
2506        );
2507        fixture(
2508            "map-collision.bin",
2509            [
2510                LwwMapDelta::Set {
2511                    key: 1u64,
2512                    timestamp: 1,
2513                    replica: 1,
2514                    value: 5u64,
2515                },
2516                LwwMapDelta::Remove {
2517                    key: 1u64,
2518                    timestamp: 2,
2519                    replica: 1,
2520                },
2521            ],
2522        );
2523    }
2524
2525    fn record(sequence: u64, tally: u64) -> Record<GCounterDelta> {
2526        Record {
2527            id: RecordId {
2528                replica: 1,
2529                sequence,
2530            },
2531            delta: GCounterDelta { replica: 1, tally },
2532        }
2533    }
2534
2535    // Deliberately bypass admission to exercise the decoder's collision gate.
2536    // The production encoder derives all frame bytes, lengths and checksums.
2537    fn wire_log(records: &[Record<GCounterDelta>]) -> Vec<u8> {
2538        let mut log = EventLog::with_replica_count(2);
2539        log.records = records.to_vec();
2540        log.to_wire_bytes().unwrap()
2541    }
2542    #[test]
2543    fn decoder_surfaces_conflicts_before_deduplication() {
2544        for records in [
2545            vec![record(1, 5), record(1, 9)],
2546            vec![record(1, 9), record(1, 5)],
2547        ] {
2548            assert_eq!(
2549                EventLog::<GCounterDelta>::from_wire_bytes(&wire_log(&records)),
2550                Err(WireError::RecordCollision)
2551            );
2552        }
2553        let decoded =
2554            EventLog::<GCounterDelta>::from_wire_bytes(&wire_log(&[record(1, 5), record(1, 5)]))
2555                .unwrap();
2556        assert_eq!(decoded.records(), &[record(1, 5)]);
2557    }
2558}