Add slasher broadcast (#2079)

## Issue Addressed

Closes #2048

## Proposed Changes

* Broadcast slashings when the `--slasher-broadcast` flag is provided.
* In the process of implementing this I refactored the slasher service into its own crate so that it could access the network code without creating a circular dependency. I moved the responsibility for putting slashings into the op pool into the service as well, as it makes sense for it to handle the whole slashing lifecycle.
This commit is contained in:
Michael Sproul
2020-12-16 03:44:01 +00:00
parent 63eeb14a81
commit 0c529b8d52
18 changed files with 414 additions and 193 deletions

View File

@@ -39,7 +39,7 @@ use slot_clock::SlotClock;
use state_processing::{
common::get_indexed_attestation, per_block_processing,
per_block_processing::errors::AttestationValidationError, per_slot_processing,
BlockSignatureStrategy, SigVerifiedOp, VerifyOperation,
BlockSignatureStrategy, SigVerifiedOp,
};
use std::borrow::Cow;
use std::cmp::Ordering;
@@ -1125,63 +1125,6 @@ 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
@@ -1876,7 +1819,6 @@ 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);
@@ -2093,7 +2035,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
if is_epoch_transition || is_reorg {
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()?;
}

View File

@@ -43,3 +43,4 @@ directory = {path = "../../common/directory"}
http_api = { path = "../http_api" }
http_metrics = { path = "../http_metrics" }
slasher = { path = "../../slasher" }
slasher_service = { path = "../../slasher/service" }

View File

@@ -13,7 +13,8 @@ use eth1::{Config as Eth1Config, Service as Eth1Service};
use eth2_libp2p::NetworkGlobals;
use genesis::{interop_genesis_state, Eth1GenesisService};
use network::{NetworkConfig, NetworkMessage, NetworkService};
use slasher::{Slasher, SlasherServer};
use slasher::Slasher;
use slasher_service::SlasherService;
use slog::{debug, info, warn};
use ssz::Decode;
use std::net::TcpListener;
@@ -348,22 +349,21 @@ where
/// Immediately start the slasher service.
///
/// Error if no slasher is configured.
pub fn start_slasher_server(&self) -> Result<(), String> {
pub fn start_slasher_service(&self) -> Result<(), String> {
let beacon_chain = self
.beacon_chain
.clone()
.ok_or("slasher service requires a beacon chain")?;
let network_send = self
.network_send
.clone()
.ok_or("slasher service requires a network sender")?;
let context = self
.runtime_context
.as_ref()
.ok_or("slasher requires a runtime_context")?
.service_context("slasher_server_ctxt".into());
let slasher = self
.slasher
.clone()
.ok_or("slasher server requires a slasher")?;
let slot_clock = self
.slot_clock
.clone()
.ok_or("slasher server requires a slot clock")?;
SlasherServer::run(slasher, slot_clock, &context.executor);
Ok(())
.service_context("slasher_service_ctxt".into());
SlasherService::new(beacon_chain, network_send).run(&context.executor)
}
/// Immediately starts the service that periodically logs information each slot.
@@ -470,7 +470,7 @@ where
};
if self.slasher.is_some() {
self.start_slasher_server()?;
self.start_slasher_service()?;
}
Ok(Client {

View File

@@ -434,6 +434,13 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.requires("slasher")
.takes_value(true)
)
.arg(
Arg::with_name("slasher-broadcast")
.long("slasher-broadcast")
.help("Broadcast slashings found by the slasher to the rest of the network \
[disabled by default].")
.requires("slasher")
)
.arg(
Arg::with_name("wss-checkpoint")
.long("wss-checkpoint")

View File

@@ -375,6 +375,8 @@ pub fn get_config<E: EthSpec>(
slasher_config.validator_chunk_size = validator_chunk_size;
}
slasher_config.broadcast = cli_args.is_present("slasher-broadcast");
client_config.slasher = Some(slasher_config);
}