diff --git a/beacon_chain/gossip_processing/eth2_processor.nim b/beacon_chain/gossip_processing/eth2_processor.nim index ebaebedb0..2e7424b2a 100644 --- a/beacon_chain/gossip_processing/eth2_processor.nim +++ b/beacon_chain/gossip_processing/eth2_processor.nim @@ -45,6 +45,18 @@ declareHistogram beacon_block_delay, "Time(s) between slot start and beacon block reception", buckets = delayBuckets type + DoppelgangerProtection = object + broadcastStartEpoch*: Epoch ##\ + ## Set anew, each time gossip is re-enabled after syncing completes, so + ## might reset multiple times per instance. This allows some safe level + ## of gossip interleaving between nodes so long as they don't gossip at + ## the same time. + + nodeLaunchSlot: Slot ##\ + ## Set once, at node launch. This functions as a basic protection against + ## false positives from attestations persisting within the gossip network + ## across quick restarts. + Eth2Processor* = object doppelGangerDetectionEnabled*: bool getWallTime*: GetWallTimeFn @@ -88,6 +100,8 @@ proc new*(T: type Eth2Processor, getWallTime: GetWallTimeFn): ref Eth2Processor = (ref Eth2Processor)( doppelGangerDetectionEnabled: doppelGangerDetectionEnabled, + doppelgangerDetection: DoppelgangerProtection( + nodeLaunchSlot: getWallTime().slotOrZero), getWallTime: getWallTime, blockProcessor: blockProcessor, dag: dag, @@ -158,15 +172,15 @@ proc blockValidator*( proc checkForPotentialDoppelganger( self: var Eth2Processor, attestation: Attestation, - attesterIndices: openArray[ValidatorIndex], wallSlot: Slot) = - let epoch = wallSlot.epoch - - # Only check for current epoch, not potential attestations bouncing around - # from up to several minutes prior. - if attestation.data.slot.epoch < epoch: + attesterIndices: openArray[ValidatorIndex]) = + # Only check for attestations after node launch. There might be one slot of + # overlap in quick intra-slot restarts so trade off a few true negatives in + # the service of avoiding more likely false positives. + if attestation.data.slot <= self.doppelgangerDetection.nodeLaunchSlot + 1: return - if epoch < self.doppelgangerDetection.broadcastStartEpoch: + if attestation.data.slot.epoch < + self.doppelgangerDetection.broadcastStartEpoch: let tgtBlck = self.dag.getRef(attestation.data.target.root) doAssert not tgtBlck.isNil # because attestation is valid above @@ -219,8 +233,7 @@ proc attestationValidator*( let (attestation_index, sig) = v.get() - self[].checkForPotentialDoppelganger( - attestation, [attestation_index], wallSlot) + self[].checkForPotentialDoppelganger(attestation, [attestation_index]) trace "Attestation validated" self.attestationPool[].addAttestation( @@ -267,8 +280,7 @@ proc aggregateValidator*( let (attesting_indices, sig) = v.get() self[].checkForPotentialDoppelganger( - signedAggregateAndProof.message.aggregate, attesting_indices, - wallSlot) + signedAggregateAndProof.message.aggregate, attesting_indices) trace "Aggregate validated", aggregator_index = signedAggregateAndProof.message.aggregator_index, diff --git a/beacon_chain/spec/datatypes/base.nim b/beacon_chain/spec/datatypes/base.nim index 382acbec5..3f2824e08 100644 --- a/beacon_chain/spec/datatypes/base.nim +++ b/beacon_chain/spec/datatypes/base.nim @@ -470,9 +470,6 @@ type current_justified_checkpoint*: Checkpoint finalized_checkpoint*: Checkpoint - DoppelgangerProtection* = object - broadcastStartEpoch*: Epoch - type # Caches for computing justificiation, rewards and penalties - based on # implementation in Lighthouse: