Optional slashing protection for remote keys (#4981)

* Optional slashing protection for remote keys

* Merge remote-tracking branch 'origin/unstable' into disable-slashing-protection-web3signer

* Start writing tests

* Merge remote-tracking branch 'origin/unstable' into disable-slashing-protection-web3signer

* Merge remote-tracking branch 'michael/disable-slashing-protection-web3signer' into disable-slashing-protection-web3signer

* Make half-written tests compile

* Make tests work

* Update help text

* Update book CLI text

* Merge remote-tracking branch 'origin/unstable' into disable-slashing-protection-web3signer

* More logging & CLI tests

* CLI tweaks
This commit is contained in:
Michael Sproul
2024-02-06 12:30:31 +11:00
committed by GitHub
parent 795c5778e1
commit 7bec3f9b59
7 changed files with 463 additions and 163 deletions

View File

@@ -367,6 +367,17 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
constructed by builders, regardless of payload value.")
.takes_value(false),
)
.arg(
Arg::with_name("disable-slashing-protection-web3signer")
.long("disable-slashing-protection-web3signer")
.help("Disable Lighthouse's slashing protection for all web3signer keys. This can \
reduce the I/O burden on the VC but is only safe if slashing protection \
is enabled on the remote signer and is implemented correctly. DO NOT ENABLE \
THIS FLAG UNLESS YOU ARE CERTAIN THAT SLASHING PROTECTION IS ENABLED ON \
THE REMOTE SIGNER. YOU WILL GET SLASHED IF YOU USE THIS FLAG WITHOUT \
ENABLING WEB3SIGNER'S SLASHING PROTECTION.")
.takes_value(false)
)
/*
* Experimental/development options.
*/

View File

@@ -76,6 +76,8 @@ pub struct Config {
pub enable_latency_measurement_service: bool,
/// Defines the number of validators per `validator/register_validator` request sent to the BN.
pub validator_registration_batch_size: usize,
/// Enable slashing protection even while using web3signer keys.
pub enable_web3signer_slashing_protection: bool,
/// Enables block production via the block v3 endpoint. This configuration option can be removed post deneb.
pub produce_block_v3: bool,
/// Specifies the boost factor, a percentage multiplier to apply to the builder's payload value.
@@ -124,6 +126,7 @@ impl Default for Config {
broadcast_topics: vec![ApiTopic::Subscriptions],
enable_latency_measurement_service: true,
validator_registration_batch_size: 500,
enable_web3signer_slashing_protection: true,
produce_block_v3: false,
builder_boost_factor: None,
prefer_builder_proposals: false,
@@ -407,6 +410,19 @@ impl Config {
return Err("validator-registration-batch-size cannot be 0".to_string());
}
config.enable_web3signer_slashing_protection =
if cli_args.is_present("disable-slashing-protection-web3signer") {
warn!(
log,
"Slashing protection for remote keys disabled";
"info" => "ensure slashing protection on web3signer is enabled or you WILL \
get slashed"
);
false
} else {
true
};
Ok(config)
}
}

View File

@@ -117,6 +117,20 @@ impl SigningContext {
}
impl SigningMethod {
/// Return whether this signing method requires local slashing protection.
pub fn requires_local_slashing_protection(
&self,
enable_web3signer_slashing_protection: bool,
) -> bool {
match self {
// Slashing protection is ALWAYS required for local keys. DO NOT TURN THIS OFF.
SigningMethod::LocalKeystore { .. } => true,
// Slashing protection is only required for remote signer keys when the configuration
// dictates that it is desired.
SigningMethod::Web3Signer { .. } => enable_web3signer_slashing_protection,
}
}
/// Return the signature of `signable_message`, with respect to the `signing_context`.
pub async fn get_signature<T: EthSpec, Payload: AbstractExecPayload<T>>(
&self,

View File

@@ -97,6 +97,7 @@ pub struct ValidatorStore<T, E: EthSpec> {
fee_recipient_process: Option<Address>,
gas_limit: Option<u64>,
builder_proposals: bool,
enable_web3signer_slashing_protection: bool,
produce_block_v3: bool,
prefer_builder_proposals: bool,
builder_boost_factor: Option<u64>,
@@ -131,6 +132,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
fee_recipient_process: config.fee_recipient,
gas_limit: config.gas_limit,
builder_proposals: config.builder_proposals,
enable_web3signer_slashing_protection: config.enable_web3signer_slashing_protection,
produce_block_v3: config.produce_block_v3,
prefer_builder_proposals: config.prefer_builder_proposals,
builder_boost_factor: config.builder_boost_factor,
@@ -604,19 +606,26 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
let signing_context = self.signing_context(Domain::BeaconProposer, signing_epoch);
let domain_hash = signing_context.domain_hash(&self.spec);
let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?;
// Check for slashing conditions.
let slashing_status = self.slashing_protection.check_and_insert_block_proposal(
&validator_pubkey,
&block.block_header(),
domain_hash,
);
let slashing_status = if signing_method
.requires_local_slashing_protection(self.enable_web3signer_slashing_protection)
{
self.slashing_protection.check_and_insert_block_proposal(
&validator_pubkey,
&block.block_header(),
domain_hash,
)
} else {
Ok(Safe::Valid)
};
match slashing_status {
// We can safely sign this block without slashing.
Ok(Safe::Valid) => {
metrics::inc_counter_vec(&metrics::SIGNED_BLOCKS_TOTAL, &[metrics::SUCCESS]);
let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?;
let signature = signing_method
.get_signature::<E, Payload>(
SignableMessage::BeaconBlock(&block),
@@ -672,20 +681,28 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
});
}
// Get the signing method and check doppelganger protection.
let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?;
// Checking for slashing conditions.
let signing_epoch = attestation.data.target.epoch;
let signing_context = self.signing_context(Domain::BeaconAttester, signing_epoch);
let domain_hash = signing_context.domain_hash(&self.spec);
let slashing_status = self.slashing_protection.check_and_insert_attestation(
&validator_pubkey,
&attestation.data,
domain_hash,
);
let slashing_status = if signing_method
.requires_local_slashing_protection(self.enable_web3signer_slashing_protection)
{
self.slashing_protection.check_and_insert_attestation(
&validator_pubkey,
&attestation.data,
domain_hash,
)
} else {
Ok(Safe::Valid)
};
match slashing_status {
// We can safely sign this attestation.
Ok(Safe::Valid) => {
let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?;
let signature = signing_method
.get_signature::<E, BlindedPayload<E>>(
SignableMessage::AttestationData(&attestation.data),