mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-21 14:58:31 +00:00
Implement slasher (#1567)
This is an implementation of a slasher that lives inside the BN and can be enabled via `lighthouse bn --slasher`. Features included in this PR: - [x] Detection of attester slashing conditions (double votes, surrounds existing, surrounded by existing) - [x] Integration into Lighthouse's attestation verification flow - [x] Detection of proposer slashing conditions - [x] Extraction of attestations from blocks as they are verified - [x] Compression of chunks - [x] Configurable history length - [x] Pruning of old attestations and blocks - [x] More tests Future work: * Focus on a slice of history separate from the most recent N epochs (e.g. epochs `current - K` to `current - M`) * Run out-of-process * Ingest attestations from the chain without a resync Design notes are here https://hackmd.io/@sproul/HJSEklmPL
This commit is contained in:
@@ -32,12 +32,13 @@ use futures::channel::mpsc::Sender;
|
||||
use itertools::process_results;
|
||||
use operation_pool::{OperationPool, PersistedOperationPool};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use slasher::Slasher;
|
||||
use slog::{crit, debug, error, info, trace, warn, Logger};
|
||||
use slot_clock::SlotClock;
|
||||
use state_processing::{
|
||||
common::get_indexed_attestation, per_block_processing,
|
||||
per_block_processing::errors::AttestationValidationError, per_slot_processing,
|
||||
BlockSignatureStrategy, SigVerifiedOp,
|
||||
BlockSignatureStrategy, SigVerifiedOp, VerifyOperation,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
@@ -232,6 +233,8 @@ pub struct BeaconChain<T: BeaconChainTypes> {
|
||||
pub(crate) log: Logger,
|
||||
/// Arbitrary bytes included in the blocks.
|
||||
pub(crate) graffiti: Graffiti,
|
||||
/// Optional slasher.
|
||||
pub(crate) slasher: Option<Arc<Slasher<T::EthSpec>>>,
|
||||
}
|
||||
|
||||
type BeaconBlockAndState<T> = (BeaconBlock<T>, BeaconState<T>);
|
||||
@@ -518,10 +521,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
}
|
||||
|
||||
/// Apply a function to the canonical head without cloning it.
|
||||
pub fn with_head<U>(
|
||||
pub fn with_head<U, E>(
|
||||
&self,
|
||||
f: impl FnOnce(&BeaconSnapshot<T::EthSpec>) -> Result<U, Error>,
|
||||
) -> Result<U, Error> {
|
||||
f: impl FnOnce(&BeaconSnapshot<T::EthSpec>) -> Result<U, E>,
|
||||
) -> Result<U, E>
|
||||
where
|
||||
E: From<Error>,
|
||||
{
|
||||
let head_lock = self
|
||||
.canonical_head
|
||||
.try_read_for(HEAD_LOCK_TIMEOUT)
|
||||
@@ -1080,6 +1086,63 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
Ok(signed_aggregate)
|
||||
}
|
||||
|
||||
/// Move slashings collected by the slasher into the op pool for block inclusion.
|
||||
fn ingest_slashings_to_op_pool(&self, state: &BeaconState<T::EthSpec>) {
|
||||
if let Some(slasher) = self.slasher.as_ref() {
|
||||
let attester_slashings = slasher.get_attester_slashings();
|
||||
let proposer_slashings = slasher.get_proposer_slashings();
|
||||
|
||||
if !attester_slashings.is_empty() || !proposer_slashings.is_empty() {
|
||||
debug!(
|
||||
self.log,
|
||||
"Ingesting slashings";
|
||||
"num_attester_slashings" => attester_slashings.len(),
|
||||
"num_proposer_slashings" => proposer_slashings.len(),
|
||||
);
|
||||
}
|
||||
|
||||
for slashing in attester_slashings {
|
||||
let verified_slashing = match slashing.clone().validate(state, &self.spec) {
|
||||
Ok(verified) => verified,
|
||||
Err(e) => {
|
||||
error!(
|
||||
self.log,
|
||||
"Attester slashing from slasher failed verification";
|
||||
"error" => format!("{:?}", e),
|
||||
"slashing" => format!("{:?}", slashing),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = self.import_attester_slashing(verified_slashing) {
|
||||
error!(
|
||||
self.log,
|
||||
"Attester slashing from slasher is invalid";
|
||||
"error" => format!("{:?}", e),
|
||||
"slashing" => format!("{:?}", slashing),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for slashing in proposer_slashings {
|
||||
let verified_slashing = match slashing.clone().validate(state, &self.spec) {
|
||||
Ok(verified) => verified,
|
||||
Err(e) => {
|
||||
error!(
|
||||
self.log,
|
||||
"Proposer slashing from slasher failed verification";
|
||||
"error" => format!("{:?}", e),
|
||||
"slashing" => format!("{:?}", slashing),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
self.import_proposer_slashing(verified_slashing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that the shuffling at `block_root` is equal to one of the shufflings of `state`.
|
||||
///
|
||||
/// The `target_epoch` argument determines which shuffling to check compatibility with, it
|
||||
@@ -1525,6 +1588,18 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
metrics::stop_timer(attestation_observation_timer);
|
||||
|
||||
// If a slasher is configured, provide the attestations from the block.
|
||||
if let Some(slasher) = self.slasher.as_ref() {
|
||||
for attestation in &signed_block.message.body.attestations {
|
||||
let committee =
|
||||
state.get_beacon_committee(attestation.data.slot, attestation.data.index)?;
|
||||
let indexed_attestation =
|
||||
get_indexed_attestation(&committee.committee, attestation)
|
||||
.map_err(|e| BlockError::BeaconChainError(e.into()))?;
|
||||
slasher.accept_attestation(indexed_attestation);
|
||||
}
|
||||
}
|
||||
|
||||
// If there are new validators in this block, update our pubkey cache.
|
||||
//
|
||||
// We perform this _before_ adding the block to fork choice because the pubkey cache is
|
||||
@@ -1735,6 +1810,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
state.latest_block_header.canonical_root()
|
||||
};
|
||||
|
||||
self.ingest_slashings_to_op_pool(&state);
|
||||
let (proposer_slashings, attester_slashings) =
|
||||
self.op_pool.get_slashings(&state, &self.spec);
|
||||
|
||||
@@ -1951,6 +2027,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
{
|
||||
self.persist_head_and_fork_choice()?;
|
||||
self.op_pool.prune_attestations(self.epoch()?);
|
||||
self.ingest_slashings_to_op_pool(&new_head.beacon_state);
|
||||
self.persist_op_pool()?;
|
||||
}
|
||||
|
||||
let update_head_timer = metrics::start_timer(&metrics::UPDATE_HEAD_TIMES);
|
||||
|
||||
Reference in New Issue
Block a user