Skip to main content

SlashingProtection

Struct SlashingProtection 

Source
pub struct SlashingProtection { /* private fields */ }
Expand description

Per-validator local slashing-protection state.

Implements DSL-094 (+ DSL-095/096/097 in later commits). Traces to SPEC §14.

§Fields

  • last_proposed_slot — largest slot the validator has proposed at. check_proposal_slot requires a strictly greater slot before signing a new proposal.

Future DSLs add last_source_epoch, last_target_epoch, attested_hash_by_target and similar fields as their guards come online.

§Default

Default::default()last_proposed_slot = 0. Any slot > 0 passes check_proposal_slot on a fresh instance.

Implementations§

Source§

impl SlashingProtection

Source

pub fn new() -> Self

Construct with last_proposed_slot = 0.

Source

pub fn last_proposed_slot(&self) -> u64

Last slot at which the validator proposed. Used for introspection + persistence round-trips.

Source

pub fn check_proposal_slot(&self, slot: u64) -> bool

true iff the caller MAY sign a new proposal at slot.

Implements DSL-094.

§Predicate

slot > self.last_proposed_slot — strict greater-than so the same slot cannot be signed twice (that would be the canonical proposer-equivocation offense).

Fresh validators have last_proposed_slot = 0, so any slot > 0 is safe to sign.

Source

pub fn record_proposal(&mut self, slot: u64)

Record a successful proposal at slot. Subsequent check_proposal_slot(s) calls with s <= slot will return false.

Implements DSL-094. Persistence semantics land in DSL-097.

Source

pub fn last_attested_source_epoch(&self) -> u64

source.epoch of the last recorded attestation.

Source

pub fn last_attested_target_epoch(&self) -> u64

target.epoch of the last recorded attestation.

Source

pub fn last_attested_block_hash(&self) -> Option<&str>

Lowercase 0x-prefixed hex of the last recorded attestation’s block hash. None when no attestation has been recorded.

Source

pub fn check_attestation( &self, source_epoch: u64, target_epoch: u64, block_hash: &Bytes32, ) -> bool

true iff the caller MAY sign an attestation at (source_epoch, target_epoch, block_hash).

Implements DSL-095 (same-(src, tgt) different-hash self-check). DSL-096 (surround-vote self-check) extends this method in a later commit.

§DSL-095 rule

When the candidate FFG coordinates match the stored last-attested pair EXACTLY, the attestation is allowed only if the candidate block hash matches the stored hash (case-insensitive hex compare). This is the “re-sign the same vote” carve-out — a validator restarting mid-epoch may re-emit its own attestation, but may NOT switch to a different block at the same source/target pair (that would be an AttesterDoubleVote, DSL-014).

If no prior attestation is stored (last_attested_* = 0, None), the check falls through to the surround guard (DSL-096 — currently a no-op stub) and returns true.

Source

pub fn record_attestation( &mut self, source_epoch: u64, target_epoch: u64, block_hash: &Bytes32, )

Record a successful attestation. Updates last_attested_source_epoch, last_attested_target_epoch and last_attested_block_hash.

Implements the DSL-095/096 persistence primitive. DSL-097 pins the full contract (including the proposer-side record_proposal companion).

Source

pub fn rewind_proposal_to_slot(&mut self, new_tip_slot: u64)

Rewind the proposal watermark on fork-choice reorg.

Previews DSL-156 — DSL-099 composes this fn alongside [rewind_attestation_to_epoch] (DSL-098) inside [reconcile_with_chain_tip]. The DSL-156 dedicated test file lands in Phase 10.

§Semantics

Caps last_proposed_slot at new_tip_slot using strict > so the boundary (stored == tip) is a no-op and already-lower slots remain untouched. Reconcile must never RAISE a watermark — doing so would weaken slashing protection.

No hash-equivalent to clear on the proposal side: DSL-094 only tracks the slot, not a block binding.

Source

pub fn save(&self, path: &PathBuf) -> Result<()>

Persist slashing-protection state to disk as pretty-printed JSON.

Implements DSL-101. Traces to SPEC §14.4.

§On-disk format

JSON, pretty-printed for operator debuggability. All fields are primitive (u64) except last_attested_block_hash, which is stored as the canonical 0x<lowercase-hex> form produced by [record_attestation]. External tooling that rewrites the hash to uppercase is tolerated on reload via [check_attestation]’s case-insensitive compare.

§Errors

Returns any I/O error raised by the underlying std::fs write. Serialization is infallible (every field is serde-safe by construction).

§Companion load

The inverse of this call. load handles the missing-file case by returning Self::default() so a first-boot validator does not need a bootstrap branch.

Source

pub fn load(path: &PathBuf) -> Result<Self>

Load slashing-protection state from disk.

Implements DSL-101.

§Behaviour
  • Path exists: decode the file via serde_json::from_slice. Uses the same schema as save; DSL-100 handles legacy JSON lacking last_attested_block_hash via #[serde(default)] on that field.
  • Path does NOT exist: return Self::default(). This is the intentional first-boot path — a validator with no prior state calls load and gets a clean instance, avoiding an explicit bootstrap branch at every call site.
§Errors
  • std::fs::read errors (permission, I/O) propagate.
  • Deserialization errors surface as InvalidData via serde_json. NOTE: a legitimate legacy file triggers #[serde(default)], not an error.
Source

pub fn reconcile_with_chain_tip(&mut self, tip_slot: u64, tip_epoch: u64)

Reconcile local slashing-protection state with the canonical chain tip on validator startup or after a reorg.

Implements DSL-099. Traces to SPEC §14.3.

§Semantics

Composes [rewind_proposal_to_slot] (DSL-156) with [rewind_attestation_to_epoch] (DSL-098) under a single entry point. Net effect:

  • last_proposed_slot capped at tip_slot (never raised).
  • last_attested_source_epoch / last_attested_target_epoch capped at tip_epoch (never raised).
  • last_attested_block_hash cleared unconditionally — the hash binds to a specific block that the reorg invalidates.

Idempotent by construction: both legs are caps, and a second call with the same (tip_slot, tip_epoch) finds the state already satisfying both caps.

Called by:

  • validator boot sequence (rejoin canonical chain after downtime),
  • DSL-130 global-reorg orchestration.
Source

pub fn rewind_attestation_to_epoch(&mut self, new_tip_epoch: u64)

Rewind attestation state on fork-choice reorg or chain-tip refresh.

Implements DSL-098. Traces to SPEC §14.3.

§Semantics

The stored (source, target, hash) triple is the validator’s local memory of “what I already signed.” When a reorg drops the chain back below the attested epochs, that memory is a ghost watermark — the block the hash points to no longer exists on the canonical chain. Keeping it would block honest re-attestation through DSL-095/096.

Two legs:

  1. Cap last_attested_source_epoch and last_attested_target_epoch at new_tip_epoch. Use strict > so the boundary case (stored == tip) is a no-op — the cap must never RAISE a watermark, only lower it.
  2. Clear last_attested_block_hash unconditionally. The hash binds to a specific block; a reorg invalidates that binding regardless of epoch ordering.

After rewind, a re-attestation on the new canonical tip passes [check_attestation].

Companion DSL-099 (reconcile_with_chain_tip) calls this alongside the proposal-rewind DSL-156; DSL-130 triggers the whole bundle on global reorg.

Trait Implementations§

Source§

impl Clone for SlashingProtection

Source§

fn clone(&self) -> SlashingProtection

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SlashingProtection

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for SlashingProtection

Source§

fn default() -> SlashingProtection

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

impl<'de> Deserialize<'de> for SlashingProtection

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for SlashingProtection

Source§

fn eq(&self, other: &SlashingProtection) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for SlashingProtection

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl Eq for SlashingProtection

Source§

impl StructuralPartialEq for SlashingProtection

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<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

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<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Conv for T

Source§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> FmtForward for T

Source§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
Source§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
Source§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
Source§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
Source§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
Source§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
Source§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
Source§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
Source§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
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> Pipe for T
where T: ?Sized,

Source§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
Source§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
Source§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
Source§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows 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
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows 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
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
Source§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
Source§

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

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: 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: 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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Tap for T

Source§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
Source§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
Source§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
Source§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
Source§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
Source§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
Source§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
Source§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
Source§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
Source§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .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
where Self: BorrowMut<B>, B: ?Sized,

Calls .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
where Self: AsRef<R>, R: ?Sized,

Calls .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
where Self: AsMut<R>, R: ?Sized,

Calls .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
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> TryConv for T

Source§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
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
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,