minerva 0.2.0

Causal ordering for distributed systems
//! The snapshot's inert mirror of the machine's private state.
//!
//! A direct clone of the [`Epochs`] internals rather than a parallel
//! record vocabulary: the snapshot module is a child of the epoch module,
//! so the machine's private types are in scope, and holding them directly
//! means the round-trip laws are equalities over the machine's own state,
//! never a translation that could drift. The codec (`codec.rs`) is the
//! only writer that can produce one of these from untrusted bytes, and it
//! re-validates every machine invariant before construction, so
//! [`to_epochs`](SnapshotState::to_epochs) is total.

extern crate alloc;

use alloc::collections::{BTreeSet, VecDeque};
use alloc::vec::Vec;
use core::num::{NonZeroU64, NonZeroUsize};

use super::super::arrival::ArrivalRound;
use super::super::{Epochs, SealedEpoch, Window};

/// The complete lifecycle state a checkpoint carries: exactly the
/// machine's own fields, with the horizon widened to its wire form.
#[derive(Clone, Debug, PartialEq, Eq)]
pub(super) struct SnapshotState {
    /// The next window's lineage generation, one-based on the type: the
    /// codec refuses generation zero before this record exists, so the
    /// machine's own field rides unchanged (ruling R-91).
    pub(super) generation: NonZeroU64,
    /// The current generation's roster, admissions included.
    pub(super) roster: BTreeSet<u32>,
    /// The retained-recognizer horizon, as persisted (the live machine's
    /// `NonZeroUsize`, widened; rehydration validates it against the
    /// caller's configuration rather than trusting storage).
    pub(super) horizon: u64,
    /// The open window, whole: candidates, the protocol ledger, both
    /// report rounds, the winner latch, and the local adoption bit.
    pub(super) window: Option<Window>,
    /// The retained sealed recognizers, oldest first.
    pub(super) lineage: Vec<SealedEpoch>,
    /// The open arrival round, whole (PRD 0028 R3): the boundary, the
    /// family, and every endorsement slot. Any recorded-membership
    /// content --- this round, a tagged candidate, an admission-bearing
    /// seal --- moves the frame to its version-2 sibling spelling, so a
    /// version-1 reader refuses rather than restoring a machine that
    /// silently shed it.
    pub(super) arrival: Option<ArrivalRound>,
}

impl SnapshotState {
    /// Clones the machine's complete lifecycle state.
    pub(super) fn from_epochs(epochs: &Epochs) -> Self {
        // The codec's roster-implied round encoding and inline adoption
        // rounds lean on two machine invariants; fail loudly at the
        // capture seam if a refactor ever relaxes them, rather than as
        // a silently non-canonical frame.
        if let Some(window) = &epochs.window {
            debug_assert!(
                window.confirmations.reports.keys().eq(epochs.roster.iter()),
                "a round ranges over exactly the roster"
            );
            debug_assert!(
                window.adoptions.keys().eq(window.candidates.keys()),
                "adoption rounds pair with the candidates"
            );
            debug_assert!(
                window
                    .adoptions
                    .values()
                    .all(|round| round.reports.keys().eq(epochs.roster.iter())),
                "every adoption round ranges over exactly the roster"
            );
        }
        if let Some(round) = &epochs.arrival {
            debug_assert!(
                epochs
                    .lineage
                    .back()
                    .is_some_and(|sealed| sealed.declaration() == round.admission().base()),
                "an open arrival round's base is the newest sealed record"
            );
        }
        Self {
            generation: epochs.generation,
            roster: epochs.roster.clone(),
            horizon: u64::try_from(epochs.horizon.get()).unwrap_or(u64::MAX),
            window: epochs.window.clone(),
            lineage: epochs.lineage.iter().cloned().collect(),
            arrival: epochs.arrival.clone(),
        }
    }

    /// Rebuilds a live machine from validated state. Total: every
    /// cross-field invariant was proven either by
    /// [`from_epochs`](Self::from_epochs) (the machine upheld it) or by
    /// the codec's decode-time validation.
    pub(super) fn to_epochs(&self, horizon: NonZeroUsize) -> Epochs {
        Epochs {
            roster: self.roster.clone(),
            horizon,
            generation: self.generation,
            window: self.window.clone(),
            lineage: VecDeque::from(self.lineage.clone()),
            arrival: self.arrival.clone(),
        }
    }
}