pub struct SlashingManager { /* private fields */ }Expand description
Top-level slashing lifecycle manager.
Traces to SPEC §7. Owns the
processed-hash dedup map, the pending-slash book, and correlation-
window counters — all of which land in subsequent DSL commits. For
DSL-022 the manager holds only the current_epoch field, which is
consumed when calling ValidatorEntry::slash_absolute.
Implementations§
Source§impl SlashingManager
impl SlashingManager
Sourcepub fn new(current_epoch: u64) -> Self
pub fn new(current_epoch: u64) -> Self
New manager at current_epoch with the default
MAX_PENDING_SLASHES book capacity. Further fields (pending
book, processed map) start empty.
Sourcepub fn with_book_capacity(current_epoch: u64, book_capacity: usize) -> Self
pub fn with_book_capacity(current_epoch: u64, book_capacity: usize) -> Self
New manager with a caller-specified book capacity. Used by
DSL-027 tests to exercise the PendingBookFull rejection.
Sourcepub fn current_epoch(&self) -> u64
pub fn current_epoch(&self) -> u64
Current epoch accessor.
Sourcepub fn book(&self) -> &PendingSlashBook
pub fn book(&self) -> &PendingSlashBook
Immutable view of the pending-slash book.
Sourcepub fn book_mut(&mut self) -> &mut PendingSlashBook
pub fn book_mut(&mut self) -> &mut PendingSlashBook
Mutable access to the pending-slash book.
Exposed for adjudication code (DSL-064..070) that needs to
transition pending statuses to Reverted/ChallengeOpen
outside the manager’s own submit_evidence +
finalise_expired_slashes flow. Test suites also use this to
inject pre-Reverted state for DSL-033 skip-path coverage.
Sourcepub fn is_processed(&self, hash: &Bytes32) -> bool
pub fn is_processed(&self, hash: &Bytes32) -> bool
true iff the evidence hash has been admitted. Used by
DSL-026 (AlreadySlashed short-circuit) + tests.
Sourcepub fn processed_epoch(&self, hash: &Bytes32) -> Option<u64>
pub fn processed_epoch(&self, hash: &Bytes32) -> Option<u64>
Admission epoch recorded for a processed hash. None when the
hash is not in the map.
Sourcepub fn submit_evidence(
&mut self,
evidence: SlashingEvidence,
validator_set: &mut dyn ValidatorView,
effective_balances: &dyn EffectiveBalanceView,
bond_escrow: &mut dyn BondEscrow,
reward_payout: &mut dyn RewardPayout,
proposer: &dyn ProposerView,
network_id: &Bytes32,
) -> Result<SlashingResult, SlashingError>
pub fn submit_evidence( &mut self, evidence: SlashingEvidence, validator_set: &mut dyn ValidatorView, effective_balances: &dyn EffectiveBalanceView, bond_escrow: &mut dyn BondEscrow, reward_payout: &mut dyn RewardPayout, proposer: &dyn ProposerView, network_id: &Bytes32, ) -> Result<SlashingResult, SlashingError>
Optimistic-admission entry point for validator slashing evidence.
Implements the base-slash branch of DSL-022. Traces to SPEC §7.3 step 5, §4.
§Pipeline (DSL-022 + DSL-023 scope)
verify_evidence(...)→VerifiedEvidence(DSL-011..020). Failure propagates asSlashingError.bond_escrow.lock(reporter_idx, REPORTER_BOND_MOJOS, BondTag::Reporter(evidence.hash()))(DSL-023). Lock failure collapses toSlashingError::BondLockFailedwith no validator-side mutation — hence the ordering (bond BEFORE anyslash_absolute).- For each slashable index:
eff_bal = effective_balances.get(idx)bps_term = eff_bal * base_bps / BPS_DENOMINATORfloor_term = eff_bal / MIN_SLASHING_PENALTY_QUOTIENTbase_slash = max(bps_term, floor_term)- Skip iff
validator_set.get(idx).is_slashed()OR index absent from the view (defensive tolerance per SPEC §7.3). - Otherwise
validator_set.get_mut(idx).slash_absolute( base_slash, self.current_epoch). - Record a
PerValidatorSlash.
- Return
SlashingResult { per_validator, reporter_bond_escrowed: REPORTER_BOND_MOJOS, .. }— reward / pending-slash fields stay0/ empty until DSL-024/025.
§Deviations from SPEC signature
SPEC §7.3 lists additional parameters (CollateralSlasher,
RewardPayout, ProposerView) that are consumed by
DSL-025. Signature grows incrementally — each future DSL adds
the trait it needs.
Sourcepub fn finalise_expired_slashes(
&mut self,
validator_set: &mut dyn ValidatorView,
effective_balances: &dyn EffectiveBalanceView,
bond_escrow: &mut dyn BondEscrow,
total_active_balance: u64,
) -> Vec<FinalisationResult>
pub fn finalise_expired_slashes( &mut self, validator_set: &mut dyn ValidatorView, effective_balances: &dyn EffectiveBalanceView, bond_escrow: &mut dyn BondEscrow, total_active_balance: u64, ) -> Vec<FinalisationResult>
Transition every expired pending slash from
Accepted/ChallengeOpen to Finalised { finalised_at_epoch: self.current_epoch } and emit one FinalisationResult per
transition.
Implements DSL-029. Traces to SPEC §7.4 steps 1, 6–7.
§Scope (incremental)
This method currently covers the status transition + result emission only. Side effects land in subsequent DSLs:
- DSL-030 populates
per_validator_correlation_penalty. - DSL-031 populates
reporter_bond_returnedviabond_escrow.release. - DSL-032 populates
exit_lock_until_epochviavalidator_set.schedule_exit.
§Behaviour
- Iterates
book.expired_by(self.current_epoch)in ascending window-expiry order (stable across calls). - Skips pendings already in
Reverted { .. }orFinalised { .. }(DSL-033). - Idempotent: calling twice in the same epoch yields an empty second result vec.
Sourcepub fn submit_appeal(
&mut self,
appeal: &SlashAppeal,
bond_escrow: &mut dyn BondEscrow,
) -> Result<(), SlashingError>
pub fn submit_appeal( &mut self, appeal: &SlashAppeal, bond_escrow: &mut dyn BondEscrow, ) -> Result<(), SlashingError>
Submit an appeal against an existing pending slash.
Implements DSL-055. Traces to SPEC §6.1, §7.2.
§Scope (incremental)
First-cut pipeline stops at the UnknownEvidence precondition:
if appeal.evidence_hash is not present in the pending-slash
book the method returns SlashingError::UnknownEvidence(hex)
WITHOUT touching the bond escrow. Later DSLs extend the
pipeline:
- DSL-056:
WindowExpired - DSL-057:
VariantMismatch - DSL-058:
DuplicateAppeal - DSL-059:
TooManyAttempts - DSL-060/061:
SlashAlreadyReverted/SlashAlreadyFinalised - DSL-062: appellant-bond lock (FIRST bond-touching step)
- DSL-063:
PayloadTooLarge - DSL-064+: dispatch to per-ground verifiers + adjudicate
§Error ordering invariant
UnknownEvidence MUST be checked BEFORE any bond operation
so a caller with a stale / misrouted appeal does not pay
gas to lock collateral that would immediately need to be
returned. Preserved by running the book lookup as the first
statement — see the DSL-055 test suite’s
test_dsl_055_bond_not_locked guard.
Sourcepub fn set_epoch(&mut self, epoch: u64)
pub fn set_epoch(&mut self, epoch: u64)
Advance the manager’s epoch. Consumers at the consensus layer
call this at every epoch boundary AFTER running
finalise_expired_slashes — keeps the current epoch in lock
step with the chain. Test helper.
Sourcepub fn mark_processed(&mut self, hash: Bytes32, epoch: u64)
pub fn mark_processed(&mut self, hash: Bytes32, epoch: u64)
Record a processed-evidence entry for persistence load or test fixtures.
submit_evidence does this implicitly on admission;
this method is the public surface for replaying a
persisted book or constructing a unit-test fixture
without going through the full verify + bond-lock
pipeline.
Sourcepub fn mark_slashed_in_window(
&mut self,
epoch: u64,
idx: u32,
effective_balance: u64,
)
pub fn mark_slashed_in_window( &mut self, epoch: u64, idx: u32, effective_balance: u64, )
Record a (epoch, validator_index) → effective_balance
entry in the slashed-in-window cohort map. Companion to
mark_processed; used by persistence load + tests.
Sourcepub fn is_slashed_in_window(&self, epoch: u64, idx: u32) -> bool
pub fn is_slashed_in_window(&self, epoch: u64, idx: u32) -> bool
Lookup for slashed_in_window — test helper so integration
tests can verify DSL-129 rewind actually cleared an entry.
Sourcepub fn pending(&self, hash: &Bytes32) -> Option<&PendingSlash>
pub fn pending(&self, hash: &Bytes32) -> Option<&PendingSlash>
Read-side lookup for a pending slash by evidence_hash.
Implements DSL-150.
Convenience wrapper over self.book().get(hash) so callers
don’t need to chain through book(). Returns None when
the slash has been removed via book.remove or was never
admitted.
Sourcepub fn prune(&mut self, before_epoch: u64) -> usize
pub fn prune(&mut self, before_epoch: u64) -> usize
Prune processed + slashed_in_window entries older than
before_epoch. Convenience alias for
prune_processed_older_than
per DSL-150 naming.
Does NOT touch book — pending slashes are removed via
book.remove or finalise_expired_slashes which own the
status-transition lifecycle.
Typical caller: DSL-127 run_epoch_boundary with
before_epoch = current.saturating_sub(CORRELATION_WINDOW_EPOCHS).
Sourcepub fn is_slashed(&self, idx: u32, validator_set: &dyn ValidatorView) -> bool
pub fn is_slashed(&self, idx: u32, validator_set: &dyn ValidatorView) -> bool
Delegate to ValidatorView::get(idx)?.is_slashed().
Implements DSL-149.
Returns false for unknown indices (no panic) — matches
DSL-136 ValidatorView::get out-of-range semantics.
Read-only: does not mutate self or the validator set.
Sourcepub fn rewind_on_reorg(
&mut self,
new_tip_epoch: u64,
validator_set: &mut dyn ValidatorView,
collateral: Option<&mut dyn CollateralSlasher>,
bond_escrow: &mut dyn BondEscrow,
) -> Vec<Bytes32> ⓘ
pub fn rewind_on_reorg( &mut self, new_tip_epoch: u64, validator_set: &mut dyn ValidatorView, collateral: Option<&mut dyn CollateralSlasher>, bond_escrow: &mut dyn BondEscrow, ) -> Vec<Bytes32> ⓘ
Rewind every pending slash whose submitted_at_epoch is
STRICTLY greater than new_tip_epoch — the canonical
fork-choice reorg response.
Implements DSL-129. Traces to SPEC §13.
§Side effects per rewound entry
ValidatorEntry::credit_stake(base_slash_amount)on each slashable validator.ValidatorEntry::restore_status()on each.CollateralSlasher::credit(validator_index, collateral_slashed)on each (whencollateralpresent).BondEscrow::releaseof the reporter bond atBondTag::Reporter(evidence_hash)— NOTforfeit. Reorg is not the reporter’s fault; the bond returns intact.- Entry removed from
self.book,self.processed, andself.slashed_in_window.
§What it does NOT do
- NO reporter penalty. DSL-069 applies a reporter penalty on SUSTAINED-APPEAL revert; a reorg is a consensus-layer signal that the original evidence was never canonical, so the reporter is not at fault.
- NO appeal-history inspection. Any filed appeals on the rewound slash are discarded along with the slash itself.
§Returns
List of evidence_hash values that were rewound. Empty
when no pending slashes are past the new tip.
Sourcepub fn prune_processed_older_than(&mut self, cutoff_epoch: u64) -> usize
pub fn prune_processed_older_than(&mut self, cutoff_epoch: u64) -> usize
Prune processed-evidence map entries whose recorded epoch is
strictly less than cutoff_epoch. Called by the DSL-127
run_epoch_boundary step 8 (last step) to bound memory of
the AlreadySlashed dedup window.
Returns the number of entries removed. Also drops any
slashed_in_window rows whose epoch is older than the
cutoff — the cohort-sum window (DSL-030) is
CORRELATION_WINDOW_EPOCHS wide so entries older than
current_epoch - CORRELATION_WINDOW_EPOCHS can never
contribute to a future finalisation.
Trait Implementations§
Source§impl Clone for SlashingManager
impl Clone for SlashingManager
Source§fn clone(&self) -> SlashingManager
fn clone(&self) -> SlashingManager
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for SlashingManager
impl Debug for SlashingManager
Auto Trait Implementations§
impl Freeze for SlashingManager
impl RefUnwindSafe for SlashingManager
impl Send for SlashingManager
impl Sync for SlashingManager
impl Unpin for SlashingManager
impl UnsafeUnpin for SlashingManager
impl UnwindSafe for SlashingManager
Blanket Implementations§
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
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
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.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> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.