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:
Michael Sproul
2020-11-23 03:43:22 +00:00
parent 59b2247ab8
commit 5828ff1204
44 changed files with 3662 additions and 87 deletions

View File

@@ -11,7 +11,9 @@ use tree_hash_derive::TreeHash;
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[derive(
Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
pub struct BeaconBlockHeader {
pub slot: Slot,
#[serde(with = "serde_utils::quoted_u64")]

View File

@@ -10,7 +10,9 @@ use tree_hash_derive::TreeHash;
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[derive(
Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
pub struct ProposerSlashing {
pub signed_header_1: SignedBeaconBlockHeader,
pub signed_header_2: SignedBeaconBlockHeader,

View File

@@ -1,6 +1,6 @@
use crate::{
test_utils::TestRandom, BeaconBlock, ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey,
SignedRoot, SigningData, Slot,
SignedBeaconBlockHeader, SignedRoot, SigningData, Slot,
};
use bls::Signature;
use serde_derive::{Deserialize, Serialize};
@@ -82,6 +82,14 @@ impl<E: EthSpec> SignedBeaconBlock<E> {
self.signature.verify(pubkey, message)
}
/// Produce a signed beacon block header corresponding to this block.
pub fn signed_block_header(&self) -> SignedBeaconBlockHeader {
SignedBeaconBlockHeader {
message: self.message.block_header(),
signature: self.signature.clone(),
}
}
/// Convenience accessor for the block's slot.
pub fn slot(&self) -> Slot {
self.message.slot

View File

@@ -1,21 +1,60 @@
use crate::{test_utils::TestRandom, BeaconBlockHeader};
use bls::Signature;
use crate::{
test_utils::TestRandom, BeaconBlockHeader, ChainSpec, Domain, EthSpec, Fork, Hash256,
PublicKey, Signature, SignedRoot,
};
use derivative::Derivative;
use serde_derive::{Deserialize, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
use std::hash::{Hash, Hasher};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
/// An exit voluntarily submitted a validator who wishes to withdraw.
/// A signed header of a `BeaconBlock`.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[derive(Derivative, Debug, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
#[derivative(PartialEq, Eq)]
pub struct SignedBeaconBlockHeader {
pub message: BeaconBlockHeader,
pub signature: Signature,
}
/// Implementation of non-crypto-secure `Hash`, for use with `HashMap` and `HashSet`.
///
/// Guarantees `header1 == header2 -> hash(header1) == hash(header2)`.
///
/// Used in the slasher.
impl Hash for SignedBeaconBlockHeader {
fn hash<H: Hasher>(&self, state: &mut H) {
self.message.hash(state);
self.signature.as_ssz_bytes().hash(state);
}
}
impl SignedBeaconBlockHeader {
/// Verify that this block header was signed by `pubkey`.
pub fn verify_signature<E: EthSpec>(
&self,
pubkey: &PublicKey,
fork: &Fork,
genesis_validators_root: Hash256,
spec: &ChainSpec,
) -> bool {
let domain = spec.get_domain(
self.message.slot.epoch(E::slots_per_epoch()),
Domain::BeaconProposer,
fork,
genesis_validators_root,
);
let message = self.message.signing_root(domain);
self.signature.verify(pubkey, message)
}
}
#[cfg(test)]
mod tests {
use super::*;