refactor(core): WallClockStore trait replaces the store_mut backdoor

Task 10b reached LogStore::set_wall_clock through a general Node::store_mut.
Kyle: replace it with a narrower accessor. A WallClockStore trait (set_wall_clock
+ peer_time_estimate, implemented only by LogStore) now backs Node::set_wall_clock
and Node::peer_time_estimate, so no &mut Store escapes the facade.
This commit is contained in:
2026-10-22 08:44:09 -07:00
parent a43a4966e1
commit ef539b494f
7 changed files with 58 additions and 17 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ pub use node::{Node, ObserveError};
pub use observation::{
Dest, NodeId, Observation, ObservationId, Payload, SigAlgo, Signature, Tick, Topic,
};
pub use store::{Store, StoreError, TrimPolicy};
pub use store::{Store, StoreError, TrimPolicy, WallClockStore};
pub use transport::{Event, FrameBuf, SendError, Transport};
pub use wire::{frame_sender, Frame, WireError};
+17 -8
View File
@@ -12,7 +12,7 @@
use crate::{
consts, exchange::Exchange, BeliefPolicy, Dest, NodeEvent, NodeId, Observation, ObservationId,
Payload, Store, StoreError, Tick, Topic, Transport, TrimPolicy,
Payload, Store, StoreError, Tick, Topic, Transport, TrimPolicy, WallClockStore,
};
/// Errors returned by [`Node::observe`].
@@ -219,13 +219,22 @@ impl<S: Store, T: Transport> Node<S, T> {
&self.store
}
/// Mutable access to the backing store.
///
/// Used by the host for the trusted-time state machine — peer-observation
/// time estimation (`LogStore::peer_time_estimate`) and, once trusted,
/// anchoring the wall clock (`LogStore::set_wall_clock`).
pub fn store_mut(&mut self) -> &mut S {
&mut self.store
/// Lift the quarantine on held observations once the host has trusted wall
/// time, by folding the wall clock into a [`WallClockStore`] backend.
pub fn set_wall_clock(&mut self, wall_now: u64, now: Tick) -> <S as WallClockStore>::FoldReport
where
S: WallClockStore,
{
self.store.set_wall_clock(wall_now, now)
}
/// Best estimate of trusted wall time from a peer's observation, if the
/// backend supports it (see [`WallClockStore`]).
pub fn peer_time_estimate(&self, now: Tick) -> Option<u64>
where
S: WallClockStore,
{
self.store.peer_time_estimate(now)
}
/// Best-effort durability flush, advancing each stored observation's age
+22
View File
@@ -127,3 +127,25 @@ pub trait Store {
pub(crate) fn current_age(obs: &Observation, stored_at: Tick, now: Tick) -> u64 {
obs.age_ms.saturating_add(now.saturating_sub(stored_at))
}
/// A [`Store`] that a host can feed trusted wall-clock time into.
///
/// tines-core itself never deals with wall time — this trait is a host-facing
/// hook so a host can lift the quarantine on observations held across a
/// power-off, and estimate trusted time from a peer's observation, without a
/// general `&mut Store` backdoor (decision F in the spec). Only backends that
/// support a wall-clock fold implement it; `MemStore` and other pure-tick
/// stores do not.
pub trait WallClockStore: Store {
/// Summary of a wall-clock fold, for the host's logs.
type FoldReport;
/// Fold trusted wall time into the store, lifting the quarantine on held
/// observations. The host calls this exactly once, on the first trusted
/// time.
fn set_wall_clock(&mut self, wall_now: u64, now: Tick) -> Self::FoldReport;
/// Best estimate of trusted wall time from a peer's observation stored
/// since the last open, if any. `None` means stay quarantined.
fn peer_time_estimate(&self, now: Tick) -> Option<u64>;
}