pub struct SsaCycle<S: PixSpec> {
id: SsaId<S::Pseudonym>,
builder: Mutex<SsaBuilder<S>>,
parts: Box<[Mutex<SsaPartBuilder<S>>]>,
counters: Arc<RecoveryCounters>,
surplus_budget: SurplusBudget,
}Expand description
All post-commitment state of one SSA cycle, held as a single unit.
§Why one entry per cycle rather than one per polynomial
The accumulator and every part builder are produced by the same call — see
CommitmentProgress — needed by the same code path, and dead at the same moment. Splitting
them across caches previously required holding three lifetimes in lockstep by hand, and getting
that wrong was H8: the part builders were keyed per polynomial with an idle timer, so the
clock measured “time since a share for this polynomial arrived”. Commitments land in a cycle’s
opening moments while shares arrive polynomial-major across all of it, so any polynomial late in
the emission order had its builder reclaimed before its first share — unrecoverably, since the
commitment cannot be retransmitted.
Keyed by SsaId, the idle timer measures “time since the cycle was active”, which is the
property that actually matters and is correct at any line rate.
§Locking
One mutex per part, plus one for the accumulator — never one around the whole cycle. A single cycle-wide lock would serialise every share of a Session behind one mutex. Callers take the part lock and the accumulator lock in that order, and never hold both.
Fields§
§id: SsaId<S::Pseudonym>§builder: Mutex<SsaBuilder<S>>§parts: Box<[Mutex<SsaPartBuilder<S>>]>Part builders indexed by PolynomialIndex; length is always num_polys.
counters: Arc<RecoveryCounters>Shared payment, liveness, completed-part, and fault counters. They remain reachable from
RecoveredSsaTail after the heavyweight cycle is released.
surplus_budget: SurplusBudgetPer-polynomial surplus accounting shared with the compact post-recovery tail.
Implementations§
Source§impl<S: PixSpec> SsaCycle<S>
impl<S: PixSpec> SsaCycle<S>
Sourcepub fn new(
id: SsaId<S::Pseudonym>,
builder: SsaBuilder<S>,
parts: Vec<SsaPartBuilder<S>>,
) -> Result<Self, S::Pseudonym>
pub fn new( id: SsaId<S::Pseudonym>, builder: SsaBuilder<S>, parts: Vec<SsaPartBuilder<S>>, ) -> Result<Self, S::Pseudonym>
Assembles a cycle from the accumulator and the full set of part builders.
parts arrives in arbitrary order — it is drained from a HashMap — so each builder is
placed by its own polynomial index rather than by iteration order. A missing or duplicated
index is rejected here, which is what makes part safe to index by an
untrusted value later. Each builder is also checked to belong to id, so a part cannot be
filed under a cycle it does not describe.
Records a share that advanced reconstruction — both a useful share and a share seen.
shares_seen first, so that in program order the counters never pass through a state where
useful_shares leads. SsaRecoveryProgress::shares_seen promises to be at least
useful_shares, and a consumer computing the surplus as the difference would otherwise
underflow a u64 on the intermediate state. The two are separate Relaxed atomics, so this
alone does not settle what another thread observes — progress clamps for
that — but it removes the window rather than only papering over it.
Records a share that arrived for an already-reconstructed polynomial.
Liveness only: the surplus a conforming Entry emits advances nothing, but it is still evidence that the Entry is serving this cycle, which is what the Exit’s egress gate and recovery-idle deadline are asking about.
Sourcepub fn record_completed_part(&self)
pub fn record_completed_part(&self)
Records a polynomial part that reconstructed and opened its commitment.
Records a share that failed verification, returning the cycle’s total afterwards.
Sourcepub fn progress(&self) -> SsaRecoveryProgress<S::Pseudonym>
pub fn progress(&self) -> SsaRecoveryProgress<S::Pseudonym>
Absolute recovery progress for this cycle.
The two share counters are independent Relaxed atomics, so a reader can observe them in
either order regardless of the order they were written in. useful_shares is loaded first and
shares_seen clamped up to it, which is what actually makes
SsaRecoveryProgress::shares_seen’s “always at least useful_shares” a contract rather
than a description: a consumer deriving the surplus as the difference cannot underflow. The
clamp is engaged only for the instant a concurrent record_useful_share
is mid-flight, and overstates shares_seen by at most the number of such writers.
Sourcepub fn credit_surplus(&self, poly_index: PolynomialIndex) -> Option<bool>
pub fn credit_surplus(&self, poly_index: PolynomialIndex) -> Option<bool>
Consumes one negotiated surplus slot for poly_index.
Called only after SsaPartBuilder::add_share has decoded and validated the share as a
post-reconstruction candidate. None means the untrusted polynomial index is out of range;
Some(false) means the Entry has exhausted this polynomial’s own budget.
Sourcepub(crate) fn recovered_tail(&self) -> RecoveredSsaTail
pub(crate) fn recovered_tail(&self) -> RecoveredSsaTail
Produces the compact state that survives full recovery while buffered SURBs drain.
Sourcepub fn part(
&self,
poly_index: PolynomialIndex,
) -> Option<&Mutex<SsaPartBuilder<S>>>
pub fn part( &self, poly_index: PolynomialIndex, ) -> Option<&Mutex<SsaPartBuilder<S>>>
The part builder for one polynomial, or None if the index is out of range.
The index originates from a peer-supplied share, so this is a checked lookup and must stay one.
Sourcepub fn builder(&self) -> &Mutex<SsaBuilder<S>>
pub fn builder(&self) -> &Mutex<SsaBuilder<S>>
The accumulator that sums recovered parts into the SSA scalar.
Auto Trait Implementations§
impl<S> !Freeze for SsaCycle<S>
impl<S> !RefUnwindSafe for SsaCycle<S>
impl<S> Send for SsaCycle<S>
impl<S> Sync for SsaCycle<S>
impl<S> Unpin for SsaCycle<S>
impl<S> UnsafeUnpin for SsaCycle<S>where
<S as PixSpec>::Pseudonym: UnsafeUnpin,
<<S as PixSpec>::Curve as CurveArithmetic>::ProjectivePoint: UnsafeUnpin,
<<S as PixSpec>::Curve as CurveArithmetic>::Scalar: UnsafeUnpin,
impl<S> UnwindSafe for SsaCycle<S>where
<S as PixSpec>::Pseudonym: UnwindSafe,
<<S as PixSpec>::Curve as CurveArithmetic>::ProjectivePoint: UnwindSafe,
<<S as PixSpec>::Curve as CurveArithmetic>::Scalar: UnwindSafe,
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
§impl<T> FutureExt for T
impl<T> FutureExt for T
§fn with_context(self, otel_cx: Context) -> WithContext<Self>
fn with_context(self, otel_cx: Context) -> WithContext<Self>
§fn with_current_context(self) -> WithContext<Self>
fn with_current_context(self) -> WithContext<Self>
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§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