44 chronicles, chronicles/ chronos_tools, chronos, metrics,
55 ./ spec/ [crypto, datatypes, digest],
66 ./ block_pools/ [clearance, chain_dag],
7- ./ attestation_aggregation, ./ exit_pool,
7+ ./ attestation_aggregation, ./ exit_pool, ./ validator_pool,
88 ./ beacon_node_types, ./ attestation_pool,
99 ./ time, ./ conf, ./ sszdump
1010
6262 chainDag* : ChainDAGRef
6363 attestationPool* : ref AttestationPool
6464 exitPool: ref ExitPool
65+ validatorPool: ref ValidatorPool
6566 quarantine* : QuarantineRef
6667
6768 blocksQueue* : AsyncQueue [BlockEntry ]
6869 attestationsQueue* : AsyncQueue [AttestationEntry ]
6970 aggregatesQueue* : AsyncQueue [AggregateEntry ]
7071
72+ selfSlashingDetection* : SelfSlashingDetection
73+
7174proc updateHead * (self: var Eth2Processor , wallSlot: Slot ) =
7275 # # Trigger fork choice and returns the new head block.
7376 # # Can return `nil`
@@ -285,6 +288,34 @@ proc blockValidator*(
285288
286289 ValidationResult .Accept
287290
291+ proc checkForPotentialSelfSlashing (
292+ self: var Eth2Processor , attestationData: AttestationData ,
293+ attesterIndices: HashSet [ValidatorIndex ], wallSlot: Slot ) =
294+ # Attestations remain valid for 32 slots, so avoid confusing with one's own
295+ # reflections, for a ATTESTATION_PROPAGATION_SLOT_RANGE div SLOTS_PER_EPOCH
296+ # period after the attestation slot. For mainnet this can be one additional
297+ # epoch, and for minimal, four epochs.
298+ const GUARD_EPOCHS = ATTESTATION_PROPAGATION_SLOT_RANGE div SLOTS_PER_EPOCH
299+
300+ let epoch = wallSlot.epoch
301+ # Can skip this whole conditional by setting relevant config value to 0
302+ if epoch < self.selfSlashingDetection.broadcastStartEpoch and
303+ epoch >= self.selfSlashingDetection.probeEpoch and
304+ epoch <= self.selfSlashingDetection.probeEpoch + GUARD_EPOCHS :
305+ let tgtBlck = self.chainDag.getRef (attestationData.target.root)
306+ doAssert not tgtBlck.isNil # because attestation is valid above
307+
308+ let epochRef = self.chainDag.getEpochRef (
309+ tgtBlck, attestationData.target.epoch)
310+ for validatorIndex in attesterIndices:
311+ let validatorPubkey = epochRef.validator_keys[validatorIndex]
312+ if self.validatorPool[].getValidator (validatorPubkey) !=
313+ default (AttachedValidator ):
314+ notice " Found another validator using same public key; would be slashed" ,
315+ validatorIndex,
316+ validatorPubkey
317+ quit 1
318+
288319proc attestationValidator * (
289320 self: var Eth2Processor ,
290321 attestation: Attestation ,
@@ -315,6 +346,8 @@ proc attestationValidator*(
315346 beacon_attestations_received.inc ()
316347 beacon_attestation_delay.observe (delay.toFloatSeconds ())
317348
349+ self.checkForPotentialSelfSlashing (attestation.data, v.value, wallSlot)
350+
318351 while self.attestationsQueue.full ():
319352 let dropped = self.attestationsQueue.popFirst ()
320353 doAssert dropped.finished, " popFirst sanity"
@@ -358,6 +391,9 @@ proc aggregateValidator*(
358391 beacon_aggregates_received.inc ()
359392 beacon_aggregate_delay.observe (delay.toFloatSeconds ())
360393
394+ self.checkForPotentialSelfSlashing (
395+ signedAggregateAndProof.message.aggregate.data, v.value, wallSlot)
396+
361397 while self.aggregatesQueue.full ():
362398 let dropped = self.aggregatesQueue.popFirst ()
363399 doAssert dropped.finished, " popFirst sanity"
@@ -453,6 +489,7 @@ proc new*(T: type Eth2Processor,
453489 chainDag: ChainDAGRef ,
454490 attestationPool: ref AttestationPool ,
455491 exitPool: ref ExitPool ,
492+ validatorPool: ref ValidatorPool ,
456493 quarantine: QuarantineRef ,
457494 getWallTime: GetWallTimeFn ): ref Eth2Processor =
458495 (ref Eth2Processor )(
@@ -461,6 +498,7 @@ proc new*(T: type Eth2Processor,
461498 chainDag: chainDag,
462499 attestationPool: attestationPool,
463500 exitPool: exitPool,
501+ validatorPool: validatorPool,
464502 quarantine: quarantine,
465503 blocksQueue: newAsyncQueue [BlockEntry ](1 ),
466504 aggregatesQueue: newAsyncQueue [AggregateEntry ](MAX_ATTESTATIONS .int ),
0 commit comments