pub enum SlashingError {
Show 28 variants
InvalidIndexedAttestation(String),
BlsVerifyFailed,
InvalidAttesterSlashing(String),
AttesterSlashingNotSlashable,
EmptySlashableIntersection,
InvalidSlashingEvidence(String),
InvalidProposerSlashing(String),
ValidatorNotRegistered(u32),
AlreadySlashed,
ProposerUnavailable,
PendingBookFull,
BondLockFailed,
ReporterIsAccused(u32),
AppealPayloadTooLarge {
actual: usize,
limit: usize,
},
AppellantBondLockFailed(String),
SlashAlreadyReverted,
SlashAlreadyFinalised,
TooManyAttempts {
count: usize,
limit: usize,
},
DuplicateAppeal,
AppealVariantMismatch,
AppealWindowExpired {
submitted_at: u64,
window: u64,
current: u64,
},
UnknownEvidence(String),
EvidencePayloadTooLarge {
actual: usize,
limit: usize,
},
BlockCapExceeded {
actual: usize,
limit: usize,
},
DuplicateEvidence,
ReorgTooDeep {
depth: u64,
limit: u64,
},
AdmissionPuzzleHashMismatch {
expected: Bytes32,
got: Bytes32,
},
OffenseTooOld {
offense_epoch: u64,
current_epoch: u64,
},
}Expand description
Every failure mode dig-slashing’s verifiers, manager, and adjudicator
can return.
Per SPEC §17.1. Variants carry the minimum context needed to diagnose the failure without leaking internal state.
Variants§
InvalidIndexedAttestation(String)
IndexedAttestation failed its cheap structural check
(DSL-005): empty indices, non-ascending/duplicate indices,
over-cap length, or wrong-width signature.
Consumed by verify_attester_slashing (DSL-014/DSL-015) before
any BLS work. Reason string describes the specific violation.
BlsVerifyFailed
Aggregate BLS verify returned false OR the signature bytes /
pubkey set could not be decoded at all.
Raised by IndexedAttestation::verify_signature (DSL-006) and
by verify_proposer_slashing / verify_invalid_block (DSL-013 /
DSL-018). Intentionally coarse: the security model does not
distinguish “bad pubkey width”, “missing validator index”, or
“cryptographic mismatch” — all three are equally invalid
evidence and callers MUST reject the envelope uniformly.
InvalidAttesterSlashing(String)
AttesterSlashing payload failed a structural / BLS
precondition in DSL-014..016: byte-identical attestations,
structural violation bubbled up from DSL-005, or BLS verify
failure on one of the two aggregates.
Reason string names the specific violation. Predicate-failure
paths use the dedicated SlashingError::AttesterSlashingNotSlashable
and SlashingError::EmptySlashableIntersection variants so
appeals (DSL-042, DSL-043) can distinguish without string
matching.
AttesterSlashingNotSlashable
Neither the double-vote (DSL-014) nor the surround-vote (DSL-015)
predicate holds for the two AttestationDatas.
Raised by DSL-017. Mirrored at the appeal layer by
AttesterAppealGround::NotSlashableByPredicate (DSL-042).
EmptySlashableIntersection
The intersection of attestation_a.attesting_indices and
attestation_b.attesting_indices is empty — no validator
participated in both, so there is nobody to slash.
Raised by DSL-016 after the slashable-predicate check succeeds
but the intersection yields zero indices. Mirrored at the appeal
layer by AttesterAppealGround::EmptyIntersection (DSL-043).
InvalidSlashingEvidence(String)
InvalidBlockProof payload failed one of the preconditions in
DSL-018..020: BLS verify failure over block_signing_message,
header.epoch != evidence.epoch, out-of-range
failure_witness, or the optional InvalidBlockOracle
rejected the re-execution.
Reason string names the specific violation. Appeals (DSL-049..054) distinguish the categories at their own layer.
InvalidProposerSlashing(String)
ProposerSlashing payload failed one of the preconditions in
DSL-013: slot mismatch, proposer mismatch, identical headers,
bad signature bytes, inactive validator, or BLS verify failure
on one of the two signatures.
Reason string names the specific violation for diagnostics (appeals in DSL-034..040 distinguish the same categories by structured variants; this coarse string is only the verifier’s rejection channel).
ValidatorNotRegistered(u32)
A validator index named in the evidence is not registered in the validator view.
Raised by DSL-013 (accused proposer) and DSL-018 (invalid-block proposer). Carries the offending index.
AlreadySlashed
Duplicate submit_evidence for an evidence.hash() already in
the manager’s processed map.
Raised by DSL-026 as the FIRST pipeline check — before verify,
capacity check, bond lock, or any state mutation. Persists
across pending statuses (Accepted, ChallengeOpen,
Reverted, Finalised) until a reorg rewind (DSL-129) or
prune clears the entry.
ProposerView::proposer_at_slot(current_slot) returned None.
Raised by DSL-025 reward routing. A None here is a
consensus-layer bug — the proposer at the current slot must
always exist at admission time. Surfaces as a hard error
rather than silently dropping the proposer reward.
PendingBookFull
PendingSlashBook at capacity; new slashes cannot be admitted
until existing ones finalise or revert.
Raised by DSL-027. MAX_PENDING_SLASHES = 4_096 caps memory +
pruning cost. Admission attempt at capacity performs no bond
lock or validator mutation.
BondLockFailed
Reporter bond lock failed — principal lacks collateral or the escrow rejected the tag.
Raised by DSL-023 in SlashingManager::submit_evidence when
BondEscrow::lock(reporter_idx, REPORTER_BOND_MOJOS, Reporter(hash))
returns Err(_). No state mutation occurs — the manager has
not yet touched ValidatorEntry::slash_absolute.
ReporterIsAccused(u32)
The evidence reporter named themselves among the slashable validators (self-accuse).
Raised by verify_evidence (DSL-012) when
evidence.reporter_validator_index ∈ evidence.slashable_validators().
Blocks a validator from self-slashing to collect the
whistleblower reward (DSL-025 reward routing). Payload is the
offending validator index so the adjudicator can log without
re-deriving it.
AppealPayloadTooLarge
Serialized SlashAppeal exceeds MAX_APPEAL_PAYLOAD_BYTES.
Raised by DSL-063. Caps memory + DoS cost for invalid-block witness storage. Runs BEFORE the DSL-062 bond lock so an oversized appeal never reaches collateral.
Fields
AppellantBondLockFailed(String)
Appellant-bond lock failed — principal lacks collateral or the escrow rejected the tag.
Raised by DSL-062 in SlashingManager::submit_appeal when
BondEscrow::lock(appellant_idx, APPELLANT_BOND_MOJOS, Appellant(appeal_hash)) returns Err(_). Runs as the
LAST step of the admission pipeline so all structural
rejections (DSL-055..061, DSL-063) short-circuit first.
The carried string is the underlying BondError rendered
via Display.
SlashAlreadyReverted
Pending slash is already in the Reverted terminal state —
no further appeals are accepted.
Raised by DSL-060. A sustained appeal (DSL-064..070)
transitions the book entry to Reverted{..}. Additional
appeals against a reverted slash would have nothing to
revert; the check short-circuits cheaply before bond lock.
SlashAlreadyFinalised
Pending slash is already in the Finalised terminal state —
no further appeals are accepted.
Raised by DSL-061. Window closed, correlation penalty applied, exit lock scheduled. Terminal; non-actionable.
TooManyAttempts
Appellant ran out of distinct attempts against this pending slash.
Raised by DSL-059. Caps adjudication cost at
MAX_APPEAL_ATTEMPTS_PER_SLASH (4). Only REJECTED attempts
accumulate — a sustained appeal transitions the slash to
Reverted and drains the book entry, so the counter can
never exceed the cap in practice.
Fields
DuplicateAppeal
Byte-equal appeal already present in
PendingSlash::appeal_history.
Raised by DSL-058. Prevents an appellant from spamming the
adjudicator with identical rejected appeals. Near-duplicates
(different witness bytes or different ground) are accepted;
only byte-equal envelopes trip this check. Runs AFTER
AppealVariantMismatch (DSL-057) and BEFORE bond lock
(DSL-062).
AppealVariantMismatch
Appeal’s payload variant does not match the evidence’s
payload variant (e.g., ProposerSlashingAppeal filed
against AttesterSlashing evidence).
Raised by DSL-057. Cheap structural check — no state inspection beyond the two enum tags. Runs AFTER DSL-055 (UnknownEvidence) + DSL-056 (WindowExpired) and BEFORE any bond operation.
AppealWindowExpired
Appeal filed after the slash’s appeal window closed.
Raised by DSL-056. The window is [submitted_at_epoch, submitted_at_epoch + SLASH_APPEAL_WINDOW_EPOCHS] — inclusive
on BOTH ends (the boundary epoch itself is still a valid
filing). Bond is NOT locked on this path; precondition order
guarantees this.
Fields
UnknownEvidence(String)
Appeal’s evidence_hash does not match any entry in the
PendingSlashBook.
Raised by DSL-055 as the FIRST precondition in
SlashingManager::submit_appeal — checked BEFORE any bond
lock so callers can retry cheaply. The carried string is the
hex encoding of the 32-byte evidence hash for diagnostic
logging (the raw bytes remain available at the call site).
EvidencePayloadTooLarge
Serialized evidence payload exceeds
MAX_SLASH_PROPOSAL_PAYLOAD_BYTES.
Raised by DSL-109 enforce_slashing_evidence_payload_cap.
Caps memory + DoS cost for invalid-block witness storage
inside a single REMARK. Mirrors the appeal-side
SlashingError::AppealPayloadTooLarge (DSL-063); the two
variants are kept distinct because callers upstream route
them through different admission pipelines.
Fields
BlockCapExceeded
Block-level evidence cap exceeded
(evidence_count > MAX_SLASH_PROPOSALS_PER_BLOCK) or
appeal cap exceeded
(appeal_count > MAX_APPEALS_PER_BLOCK).
Raised by DSL-108 enforce_block_level_slashing_caps and
DSL-119 enforce_block_level_appeal_caps. Caps bound
per-block admission cost — each evidence triggers DSL-103
puzzle-hash derivation + BLS verification downstream, so
a hard cap keeps block-validation time predictable.
Carries both the actual count and the limit so
operators can tell whether they are hitting the proposal
or the appeal ceiling without re-deriving constants.
Fields
DuplicateEvidence
Mempool policy caught a byte-identical evidence between
pending_evidence and incoming_evidence, or a duplicate
within incoming_evidence itself.
Raised by DSL-107
enforce_slashing_evidence_mempool_dedup_policy.
Fingerprint is the JSON wire bytes (serde_json::to_vec).
Separate from the manager-level dedup
SlashingError::AlreadySlashed (DSL-026) which operates
on the evidence.hash() digest and runs inside the
slashing manager; this variant runs earlier in the
mempool upstream of any manager state.
ReorgTooDeep
Reorg depth exceeds the retention window the trackers can reconstruct.
Raised by DSL-130 rewind_all_on_reorg when
current_epoch - new_tip_epoch > CORRELATION_WINDOW_EPOCHS.
The correlation window is the deepest per-validator state
we retain; anything older cannot be rewound correctly
because the slashed_in_window rows have been pruned
(DSL-127 step 8) and the participation / inactivity
trackers do not keep per-epoch snapshots.
An embedder receiving this error must fall back to a longer-range reconciliation path (full resync or checkpoint restore); the slashing crate cannot handle the rewind locally.
Fields
AdmissionPuzzleHashMismatch
REMARK admission found an evidence whose derived
slashing_evidence_remark_puzzle_hash_v1 does NOT match
the spent coin’s puzzle_hash.
Raised by DSL-104/105 enforcement. The payload on-chain
(the REMARK bytes) binds to a puzzle hash at coin creation
time; if the admitted spend references a coin whose
puzzle_hash does not equal the recomputed hash, an
attacker is attempting to launder a payload through a coin
that never committed to it. Carries both hashes for
diagnostic logging; the check runs BEFORE any state
mutation.
Fields
OffenseTooOld
Offense epoch is older than SLASH_LOOKBACK_EPOCHS relative to
the current epoch.
Raised by verify_evidence (DSL-011) as the very first check —
cheap filter BEFORE any BLS or validator-view work. The check
is evidence.epoch + SLASH_LOOKBACK_EPOCHS < current_epoch,
phrased with addition on the LHS to avoid underflow when
current_epoch < SLASH_LOOKBACK_EPOCHS (e.g., at network boot).
Carries both epochs so adjudicators can diagnose the exact
delta without re-deriving it.
Trait Implementations§
Source§impl Clone for SlashingError
impl Clone for SlashingError
Source§fn clone(&self) -> SlashingError
fn clone(&self) -> SlashingError
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for SlashingError
impl Debug for SlashingError
Source§impl<'de> Deserialize<'de> for SlashingError
impl<'de> Deserialize<'de> for SlashingError
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl Display for SlashingError
impl Display for SlashingError
Source§impl Error for SlashingError
impl Error for SlashingError
1.30.0 · Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()
Source§impl PartialEq for SlashingError
impl PartialEq for SlashingError
Source§impl Serialize for SlashingError
impl Serialize for SlashingError
impl Eq for SlashingError
impl StructuralPartialEq for SlashingError
Auto Trait Implementations§
impl Freeze for SlashingError
impl RefUnwindSafe for SlashingError
impl Send for SlashingError
impl Sync for SlashingError
impl Unpin for SlashingError
impl UnsafeUnpin for SlashingError
impl UnwindSafe for SlashingError
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<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.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.Source§impl<T> ToStringFallible for Twhere
T: Display,
impl<T> ToStringFallible for Twhere
T: Display,
Source§fn try_to_string(&self) -> Result<String, TryReserveError>
fn try_to_string(&self) -> Result<String, TryReserveError>
ToString::to_string, but without panic on OOM.