pub struct ResponderCommitmentState { /* private fields */ }Expand description
Responder retention state (ADR-0002).
Keeps the current (latest-rotated) commitment plus every commitment whose
hash is among the last RETAINED_GOSSIPED_COMMITMENTS gossiped hashes.
A built-but-never-gossiped commitment is dropped on the next rotation unless
it gets gossiped. Rotation and gossip are the only paths that mutate this.
Implementations§
Source§impl ResponderCommitmentState
impl ResponderCommitmentState
Sourcepub fn new() -> Self
pub fn new() -> Self
Empty state: no commitments yet. Audits before the first rotation
see None lookups and the auditor falls back to the legacy plain
digest path.
Sourcepub fn rotate(&self, new_current: BuiltCommitment)
pub fn rotate(&self, new_current: BuiltCommitment)
Rotate: the freshly-rebuilt commitment becomes current. Slots that are
neither the new current nor among the last gossiped hashes are dropped
(a built-but-never-gossiped commitment does not linger).
Sourcepub fn retire_current(&self)
pub fn retire_current(&self)
Retire the current commitment WITHOUT clearing retention: stop
advertising it (so current() returns None, the gossip-emit sites stop
re-emitting and re-stamping it, and it can age out by its gossip TTL),
while keeping it answerable via lookup_by_hash for any in-flight pin a
peer already formed — until that pin’s gossip stamp expires.
Called when the node has no key it is still responsible for: it must no longer claim to hold that data going forward, but must not strand a peer mid-audit on a root it gossiped moments ago. A never-gossiped current is simply dropped (nothing to stay answerable for).
Sourcepub fn mark_gossiped(&self, hash: [u8; 32])
pub fn mark_gossiped(&self, hash: [u8; 32])
Record that hash was emitted on the wire (gossiped). Keeps the last
RETAINED_GOSSIPED_COMMITMENTS gossiped hashes so the matching
commitments stay answerable (ADR-0002). Call at every gossip-emit site.
Re-gossiping a hash already present refreshes its answerability
deadline to now and moves it to the front: every time the node actually
puts a root on the wire — including re-emitting the current root in the
steady-state no-op-rotation case — its retention legitimately extends.
Conversely a root that stops being gossiped expires
GOSSIP_ANSWERABILITY_TTL after its last emission, which is what lets
an out-of-range key age out even when the no-op guard freezes the
committed key set.
Sourcepub fn current_for_gossip(&self) -> Option<Arc<BuiltCommitment>>
pub fn current_for_gossip(&self) -> Option<Arc<BuiltCommitment>>
Atomically snapshot the current commitment to advertise AND mark it
gossiped, under a single lock. Returns the commitment to put on the wire,
or None if there is no live current (never rotated, or retired).
This is the ONLY correct way to gossip the current commitment: doing
current() then a separate mark_gossiped() is a TOCTOU — a concurrent
retire_current/rotate between the two could drop the slot, so the node
would emit a root the responder no longer retains (a peer pinning it would
get “unknown commitment hash” → false failure). Taking the snapshot and
the stamp in one critical section guarantees anything emitted is
simultaneously retained for its answerability TTL.
Sourcepub fn age_out(&self)
pub fn age_out(&self)
Expire retention purely by the wall clock, without building, signing, or
rotating anything. Call once per rotation tick so a gossiped commitment’s
answerability deadline advances even when the rotation no-op guard
returns early (unchanged committed set) or when the node has no
responsible keys to commit to. This is the time-driven half of the
retention contract — without it, a frozen recently_gossiped entry would
keep a stale key is_held forever.
Sourcepub fn lookup_by_hash(&self, hash: &[u8; 32]) -> Option<Arc<BuiltCommitment>>
pub fn lookup_by_hash(&self, hash: &[u8; 32]) -> Option<Arc<BuiltCommitment>>
Look up a commitment by its hash. Returns Some(arc) if hash
matches any retained slot. The returned Arc keeps the
BuiltCommitment alive for as long as the caller holds it,
even if a concurrent rotate ages it out of the retention buffer.
Sourcepub fn is_held(&self, key: &XorName) -> bool
pub fn is_held(&self, key: &XorName) -> bool
Whether key is committed under any retained slot (the current
commitment plus the last-2-gossiped ones) — i.e. whether a peer could
still pin a recently gossiped root and demand this key’s bytes in a
round-2 byte challenge.
This is the SAME predicate the round-2 responder uses to decide a key is
“committed” (handle_subtree_byte_challenge calls built.proof_for(key)
on the pinned slot, which is committed iff contains_key), folded over
every retained slot. The pruner consults it before deleting an
out-of-range key, so “the pruner will not delete it” and “the responder
still owes an answer for it” are provably the same boolean and cannot
drift. slots holds at most RETAINED_GOSSIPED_COMMITMENTS + 1
commitments, and contains_key is an allocation-free binary search, so
this is a short, allocation-free read.
Sourcepub fn current(&self) -> Option<Arc<BuiltCommitment>>
pub fn current(&self) -> Option<Arc<BuiltCommitment>>
Snapshot the current commitment to ADVERTISE, if any. Used by the gossip
piggyback path: emit state.current() on the next outbound
NeighborSyncRequest/Response. Returns None once the current
commitment has been retired (the node has no responsible keys), so the
node stops re-gossiping a stale root even though lookup_by_hash may
still answer it during its remaining TTL.
Sourcepub fn retained_slot_count(&self) -> usize
pub fn retained_slot_count(&self) -> usize
Number of commitment slots currently retained (the current commitment
plus any still-answerable recently-gossiped ones). Used only for the
v12 commitment_rotated event’s retained_slots field; carries no
behavioural meaning.
Sourcepub fn clear_all(&self)
pub fn clear_all(&self)
Drop every retained slot. Called when the local store has
transitioned to empty: keeping the previously-advertised
commitment alive would invite audit failures (we can no longer
answer for any of the keys we committed to), and would leave
remote auditors pinning a hash this node will never satisfy
again. After clearing, the gossip piggyback path will emit
commitment: None until a fresh rotation occurs.
This is the one sanctioned escape from the “callers MUST NOT clear retention by any other mechanism” invariant — empty storage means there is nothing to retain.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for ResponderCommitmentState
impl !RefUnwindSafe for ResponderCommitmentState
impl Send for ResponderCommitmentState
impl Sync for ResponderCommitmentState
impl Unpin for ResponderCommitmentState
impl UnsafeUnpin for ResponderCommitmentState
impl UnwindSafe for ResponderCommitmentState
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more