Skip to main content

ResponderCommitmentState

Struct ResponderCommitmentState 

Source
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

Source

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.

Source

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).

Source

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).

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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§

Source§

impl Default for ResponderCommitmentState

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more