rename options and internal structures to doppelgangerFoo and remove probing

This commit is contained in:
Dustin Brody 2021-01-29 13:38:52 +01:00 committed by zah
parent a3678d6bf0
commit 281853eee8
8 changed files with 30 additions and 57 deletions

View File

@ -51,7 +51,7 @@ type
enabled = "Always enabled"
disabled = "Always disabled"
GossipSlashingProtectionMode* {.pure.} = enum
DoppelgangerProtectionMode* {.pure.} = enum
dontcheck
warn
stop
@ -260,11 +260,11 @@ type
desc: "Write SSZ dumps of blocks, attestations and states to data dir"
name: "dump" }: bool
gossipSlashingProtection* {.
defaultValue: GossipSlashingProtectionMode.warn
doppelgangerProtection* {.
defaultValue: DoppelgangerProtectionMode.warn
desc: "[=warn*|stop] What to do when another validator is detected to be running the same validator keys (default `warn`, will become `stop` in the future)"
name: "gossip-slashing-protection"
}: GossipSlashingProtectionMode
name: "doppelganger-protection"
}: DoppelgangerProtectionMode
of createTestnet:
testnetDepositsFile* {.

View File

@ -78,7 +78,7 @@ type
attestationsQueue*: AsyncQueue[AttestationEntry]
aggregatesQueue*: AsyncQueue[AggregateEntry]
gossipSlashingProtection*: DupProtection
doppelgangerProtection*: DoppelgangerProtection
proc updateHead*(self: var Eth2Processor, wallSlot: Slot) =
## Trigger fork choice and returns the new head block.
@ -304,7 +304,7 @@ proc blockValidator*(
{.push raises: [Defect].}
proc checkForPotentialSelfSlashing(
proc checkForPotentialDoppelganger(
self: var Eth2Processor, attestationData: AttestationData,
attesterIndices: IntSet, wallSlot: Slot) =
# Attestations remain valid for 32 slots, so avoid confusing with one's own
@ -316,13 +316,9 @@ proc checkForPotentialSelfSlashing(
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/p2p-interface.md#configuration
ATTESTATION_PROPAGATION_SLOT_RANGE = 32
GUARD_EPOCHS = ATTESTATION_PROPAGATION_SLOT_RANGE div SLOTS_PER_EPOCH
# If gossipSlashingProtection not dontcheck or stop, it's the default "warn".
# If doppelgangerProtection not dontcheck or stop, it's the default "warn".
let epoch = wallSlot.epoch
if epoch < self.gossipSlashingProtection.broadcastStartEpoch and
epoch >= self.gossipSlashingProtection.probeEpoch and
epoch <= self.gossipSlashingProtection.probeEpoch + GUARD_EPOCHS:
if epoch < self.doppelgangerProtection.broadcastStartEpoch:
let tgtBlck = self.chainDag.getRef(attestationData.target.root)
doAssert not tgtBlck.isNil # because attestation is valid above
@ -336,7 +332,7 @@ proc checkForPotentialSelfSlashing(
validatorIndex,
validatorPubkey
beacon_duplicate_validator_protection_activated.inc()
if self.config.gossipSlashingProtection == GossipSlashingProtectionMode.stop:
if self.config.doppelgangerProtection == DoppelgangerProtectionMode.stop:
warn "We believe you are currently running another instance of the same validator. We've disconnected you from the network as this presents a significant slashing risk. Possible next steps are (a) making sure you've disconnected your validator from your old machine before restarting the client; and (b) running the client again with the gossip-slashing-protection option disabled, only if you are absolutely sure this is the only instance of your validator running, and reporting the issue at https://github.com/status-im/nimbus-eth2/issues."
quit QuitFailure
@ -371,7 +367,7 @@ proc attestationValidator*(
beacon_attestations_received.inc()
beacon_attestation_delay.observe(delay.toFloatSeconds())
self.checkForPotentialSelfSlashing(attestation.data, v.value, wallSlot)
self.checkForPotentialDoppelganger(attestation.data, v.value, wallSlot)
while self.attestationsQueue.full():
try:
@ -425,7 +421,7 @@ proc aggregateValidator*(
beacon_aggregates_received.inc()
beacon_aggregate_delay.observe(delay.toFloatSeconds())
self.checkForPotentialSelfSlashing(
self.checkForPotentialDoppelganger(
signedAggregateAndProof.message.aggregate.data, v.value, wallSlot)
while self.aggregatesQueue.full():

View File

@ -695,44 +695,20 @@ proc removeMessageHandlers(node: BeaconNode) =
proc setupSelfSlashingProtection(node: BeaconNode, slot: Slot) =
# When another client's already running, this is very likely to detect
# potential duplicate validators, which can trigger slashing. Assuming
# the most pessimal case of two validators started simultaneously, the
# probability of triggering a slashable condition is up to 1/n, with n
# being the number of epochs one waits before proposing or attesting.
# potential duplicate validators, which can trigger slashing.
#
# Every missed attestation costs approximately 3*get_base_reward(), which
# can be up to around 10,000 Wei. Thus, skipping attestations isn't cheap
# and one should gauge the likelihood of this simultaneous launch to tune
# the epoch delay to one's perceived risk.
#
# This approach catches both startup and network outage conditions.
const duplicateValidatorEpochs = 2
node.processor.gossipSlashingProtection.broadcastStartEpoch =
node.processor.doppelgangerProtection.broadcastStartEpoch =
slot.epoch + duplicateValidatorEpochs
# randomize() already called; also, never probe on first epoch in guard
# period, so that existing, running validators can be picked up. Whilst
# this reduces entropy for overlapping-start cases, and increases their
# collision likelihood, that can be compensated for by increasing guard
# epoch periods by 1. As a corollary, 1 guard epoch won't detect when a
# duplicate pair overlaps exactly, only the running/starting case. Even
# 2 epochs is dangerous because it'll guarantee colliding probes in the
# overlapping case.
let rng = node.network.rng
# So dPE == 2 -> epoch + 1, always; dPE == 3 -> epoch + (1 or 2), etc.
node.processor.gossipSlashingProtection.probeEpoch =
slot.epoch + 1 + rng[].rand(duplicateValidatorEpochs.int - 2).uint64
doAssert node.processor.gossipSlashingProtection.probeEpoch <
node.processor.gossipSlashingProtection.broadcastStartEpoch
debug "Setting up self-slashing protection",
debug "Setting up doppelganger protection",
epoch = slot.epoch,
probeEpoch = node.processor.gossipSlashingProtection.probeEpoch,
broadcastStartEpoch =
node.processor.gossipSlashingProtection.broadcastStartEpoch
node.processor.doppelgangerProtection.broadcastStartEpoch
proc updateGossipStatus(node: BeaconNode, slot: Slot) =
# Syncing tends to be ~1 block/s, and allow for an epoch of time for libp2p

View File

@ -653,9 +653,8 @@ type
current_justified_checkpoint*: Checkpoint
finalized_checkpoint*: Checkpoint
DupProtection* = object
DoppelgangerProtection* = object
broadcastStartEpoch*: Epoch
probeEpoch*: Epoch
func getDepositMessage*(depositData: DepositData): DepositMessage =
result.pubkey = depositData.pubkey

View File

@ -1,4 +1,4 @@
# Copyright (c) 2018-2020 Status Research & Development GmbH
# Copyright (c) 2018-2021 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).

View File

@ -1,5 +1,5 @@
# beacon_chain
# Copyright (c) 2018-2020 Status Research & Development GmbH
# Copyright (c) 2018-2021 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@ -617,14 +617,16 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} =
# The dontcheck option's a deliberately undocumented escape hatch for the
# local testnets and similar development and testing use cases.
if curSlot.epoch <
node.processor[].gossipSlashingProtection.broadcastStartEpoch and
curSlot.epoch != node.processor[].gossipSlashingProtection.probeEpoch and
node.config.gossipSlashingProtection == GossipSlashingProtectionMode.stop:
#
# If broadcastStartEpoch is 0, it hasn't had time to initialize yet, which
# means that it'd be okay not to continue, but it won't gossip regardless.
if curSlot.epoch <
node.processor[].doppelgangerProtection.broadcastStartEpoch and
node.config.doppelgangerProtection !=
DoppelgangerProtectionMode.dontcheck:
notice "Waiting to gossip out to detect potential duplicate validators",
broadcastStartEpoch =
node.processor[].gossipSlashingProtection.broadcastStartEpoch,
probeEpoch = node.processor[].gossipSlashingProtection.probeEpoch
node.processor[].doppelgangerProtection.broadcastStartEpoch
return
# Start by checking if there's work we should have done in the past that we

View File

@ -1,6 +1,6 @@
#!/bin/bash
# Copyright (c) 2020 Status Research & Development GmbH. Licensed under
# Copyright (c) 2020-2021 Status Research & Development GmbH. Licensed under
# either of:
# - Apache License, version 2.0
# - MIT license
@ -385,7 +385,7 @@ for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do
--metrics \
--metrics-address="127.0.0.1" \
--metrics-port="$(( BASE_METRICS_PORT + NUM_NODE ))" \
--gossip-slashing-protection=dontcheck \
--doppelganger-protection=dontcheck \
${EXTRA_ARGS} \
> "${DATA_DIR}/log${NUM_NODE}.txt" 2>&1 &

View File

@ -105,6 +105,6 @@ $BEACON_NODE_BIN \
--metrics \
--metrics-address="127.0.0.1" \
--metrics-port="$(( $BASE_METRICS_PORT + $NODE_ID ))" \
--gossip-slashing-protection=dontcheck \
--doppelganger-protection=dontcheck \
${ADDITIONAL_BEACON_NODE_ARGS} \
"$@"