2021-01-11 21:27:01 +00:00
# beacon_chain
# 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).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
{. push raises : [ Defect ] . }
2020-08-20 16:30:47 +00:00
import
2021-03-12 09:46:26 +00:00
std / tables ,
2020-08-20 16:30:47 +00:00
stew / results ,
2021-01-11 21:27:01 +00:00
chronicles , chronos , metrics ,
2021-03-05 13:12:00 +00:00
.. / spec / [ crypto , datatypes , digest ] ,
.. / consensus_object_pools / [ block_clearance , blockchain_dag , exit_pool , attestation_pool ] ,
2021-03-11 10:10:57 +00:00
. / gossip_validation , . / gossip_to_consensus ,
2021-04-02 14:36:43 +00:00
. / batch_validation ,
2021-03-05 13:12:00 +00:00
.. / validators / validator_pool ,
.. / beacon_node_types ,
2021-04-06 11:59:11 +00:00
.. / beacon_clock , .. / ssz / sszdump
2020-08-20 16:30:47 +00:00
# Metrics for tracking attestation and beacon block loss
declareCounter beacon_attestations_received ,
" Number of beacon chain attestations received by this peer "
declareCounter beacon_aggregates_received ,
" Number of beacon chain aggregate attestations received by this peer "
declareCounter beacon_blocks_received ,
" Number of beacon chain blocks received by this peer "
2020-09-14 14:26:31 +00:00
declareCounter beacon_attester_slashings_received ,
" Number of beacon chain attester slashings received by this peer "
declareCounter beacon_proposer_slashings_received ,
" Number of beacon chain proposer slashings received by this peer "
declareCounter beacon_voluntary_exits_received ,
" Number of beacon chain voluntary exits received by this peer "
2020-08-20 16:30:47 +00:00
const delayBuckets = [ 2 .0 , 4 .0 , 6 .0 , 8 .0 , 10 .0 , 12 .0 , 14 .0 , Inf ]
declareHistogram beacon_attestation_delay ,
" Time(s) between slot start and attestation reception " , buckets = delayBuckets
declareHistogram beacon_aggregate_delay ,
" Time(s) between slot start and aggregate reception " , buckets = delayBuckets
declareHistogram beacon_block_delay ,
" Time(s) between slot start and beacon block reception " , buckets = delayBuckets
type
Eth2Processor * = object
2021-04-06 11:59:11 +00:00
doppelGangerDetectionEnabled * : bool
2020-08-20 16:30:47 +00:00
getWallTime * : GetWallTimeFn
2021-03-11 10:10:57 +00:00
# Local sources of truth for validation
# ----------------------------------------------------------------
2020-08-20 16:30:47 +00:00
chainDag * : ChainDAGRef
attestationPool * : ref AttestationPool
2020-10-27 17:21:35 +00:00
validatorPool : ref ValidatorPool
2020-08-20 16:30:47 +00:00
2021-02-01 11:18:16 +00:00
doppelgangerDetection * : DoppelgangerProtection
2020-10-27 17:21:35 +00:00
2021-03-11 10:10:57 +00:00
# Gossip validated -> enqueue for further verification
# ----------------------------------------------------------------
verifQueues : ref VerifQueueManager
2020-08-20 16:30:47 +00:00
2021-03-11 10:10:57 +00:00
# Validated with no further verification required
# ----------------------------------------------------------------
exitPool : ref ExitPool
2020-08-20 16:30:47 +00:00
2021-04-02 14:36:43 +00:00
# Almost validated, pending cryptographic signature check
# ----------------------------------------------------------------
2021-04-03 01:50:47 +00:00
batchCrypto * : ref BatchCrypto
2021-04-02 14:36:43 +00:00
2021-03-11 10:10:57 +00:00
# Missing information
# ----------------------------------------------------------------
quarantine * : QuarantineRef
2020-08-20 16:30:47 +00:00
2021-03-11 10:10:57 +00:00
# Initialization
# ------------------------------------------------------------------------------
2020-08-20 16:30:47 +00:00
2021-03-11 10:10:57 +00:00
proc new * ( T : type Eth2Processor ,
2021-04-06 11:59:11 +00:00
doppelGangerDetectionEnabled : bool ,
2021-03-11 10:10:57 +00:00
verifQueues : ref VerifQueueManager ,
chainDag : ChainDAGRef ,
attestationPool : ref AttestationPool ,
exitPool : ref ExitPool ,
validatorPool : ref ValidatorPool ,
quarantine : QuarantineRef ,
2021-04-02 14:36:43 +00:00
rng : ref BrHmacDrbgContext ,
2021-03-11 10:10:57 +00:00
getWallTime : GetWallTimeFn ) : ref Eth2Processor =
( ref Eth2Processor ) (
2021-04-06 11:59:11 +00:00
doppelGangerDetectionEnabled : doppelGangerDetectionEnabled ,
2021-03-11 10:10:57 +00:00
getWallTime : getWallTime ,
verifQueues : verifQueues ,
chainDag : chainDag ,
attestationPool : attestationPool ,
exitPool : exitPool ,
validatorPool : validatorPool ,
2021-04-02 14:36:43 +00:00
quarantine : quarantine ,
2021-04-26 20:39:44 +00:00
batchCrypto : BatchCrypto . new (
rng = rng ,
# Only run eager attestation signature verification if we're not
# processing blocks in order to give priority to block processing
eager = proc ( ) : bool = not verifQueues [ ] . hasBlocks ( ) )
2021-03-11 10:10:57 +00:00
)
2020-08-20 16:30:47 +00:00
2021-03-11 10:10:57 +00:00
# Gossip Management
# -----------------------------------------------------------------------------------
2021-01-11 21:27:01 +00:00
2020-08-20 16:30:47 +00:00
proc blockValidator * (
self : var Eth2Processor ,
2020-09-18 11:53:09 +00:00
signedBlock : SignedBeaconBlock ) : ValidationResult =
2020-08-20 16:30:47 +00:00
logScope :
signedBlock = shortLog ( signedBlock . message )
blockRoot = shortLog ( signedBlock . root )
let
wallTime = self . getWallTime ( )
( afterGenesis , wallSlot ) = wallTime . toSlot ( )
if not afterGenesis :
2020-10-20 12:31:20 +00:00
return ValidationResult . Ignore # not an issue with block, so don't penalize
2020-08-20 16:30:47 +00:00
logScope : wallSlot
let delay = wallTime - signedBlock . message . slot . toBeaconTime
2021-03-17 10:17:15 +00:00
if signedBlock . root in self . chainDag :
2020-08-20 16:30:47 +00:00
# The gossip algorithm itself already does one round of hashing to find
# already-seen data, but it is fairly aggressive about forgetting about
# what it has seen already
debug " Dropping already-seen gossip block " , delay
2020-10-20 12:31:20 +00:00
return ValidationResult . Ignore # "[IGNORE] The block is the first block ..."
2020-08-20 16:30:47 +00:00
# Start of block processing - in reality, we have already gone through SSZ
# decoding at this stage, which may be significant
debug " Block received " , delay
let blck = self . chainDag . isValidBeaconBlock (
2020-10-28 13:04:21 +00:00
self . quarantine , signedBlock , wallTime , { } )
2020-08-20 16:30:47 +00:00
2021-03-11 10:10:57 +00:00
self . verifQueues [ ] . dumpBlock ( signedBlock , blck )
2020-08-20 16:30:47 +00:00
if not blck . isOk :
2020-09-18 11:53:09 +00:00
return blck . error [ 0 ]
2020-08-20 16:30:47 +00:00
2020-08-27 07:34:12 +00:00
beacon_blocks_received . inc ( )
2020-11-11 13:39:36 +00:00
beacon_block_delay . observe ( delay . toFloatSeconds ( ) )
2020-08-27 07:34:12 +00:00
2020-08-20 16:30:47 +00:00
# Block passed validation - enqueue it for processing. The block processing
# queue is effectively unbounded as we use a freestanding task to enqueue
# the block - this is done so that when blocks arrive concurrently with
# sync, we don't lose the gossip blocks, but also don't block the gossip
# propagation of seemingly good blocks
2020-08-27 07:34:12 +00:00
trace " Block validated "
2021-03-26 06:52:01 +00:00
self . verifQueues [ ] . addBlock ( SyncBlock ( blk : signedBlock ) )
2020-08-20 16:30:47 +00:00
2020-10-20 12:31:20 +00:00
ValidationResult . Accept
2020-08-20 16:30:47 +00:00
2021-01-29 12:38:52 +00:00
proc checkForPotentialDoppelganger (
2020-10-27 17:21:35 +00:00
self : var Eth2Processor , attestationData : AttestationData ,
2021-02-08 07:27:30 +00:00
attesterIndices : openArray [ ValidatorIndex ] , wallSlot : Slot ) =
2020-10-27 17:21:35 +00:00
let epoch = wallSlot . epoch
2021-03-01 10:09:05 +00:00
# Only check for current epoch, not potential attestations bouncing around
# from up to several minutes prior.
if attestationData . slot . epoch < epoch :
return
2021-02-01 11:18:16 +00:00
if epoch < self . doppelgangerDetection . broadcastStartEpoch :
2020-10-27 17:21:35 +00:00
let tgtBlck = self . chainDag . getRef ( attestationData . target . root )
doAssert not tgtBlck . isNil # because attestation is valid above
let epochRef = self . chainDag . getEpochRef (
tgtBlck , attestationData . target . epoch )
for validatorIndex in attesterIndices :
let validatorPubkey = epochRef . validator_keys [ validatorIndex ]
2021-04-26 19:41:35 +00:00
if self . doppelgangerDetectionEnabled and
self . validatorPool [ ] . getValidator ( validatorPubkey ) ! =
default ( AttachedValidator ) :
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. " ,
2020-10-27 17:21:35 +00:00
validatorIndex ,
2021-03-01 10:09:05 +00:00
validatorPubkey ,
attestationSlot = attestationData . slot
2021-04-26 19:41:35 +00:00
quit QuitFailure
2020-10-27 17:21:35 +00:00
2021-04-02 14:36:43 +00:00
{. pop . } # async can raise anything
2020-08-20 16:30:47 +00:00
proc attestationValidator * (
2021-04-02 14:36:43 +00:00
self : ref Eth2Processor ,
2020-08-20 16:30:47 +00:00
attestation : Attestation ,
2021-05-10 07:13:36 +00:00
subnet_id : SubnetId ,
checkSignature : bool = true ) : Future [ ValidationResult ] {. async . } =
2020-08-20 16:30:47 +00:00
logScope :
attestation = shortLog ( attestation )
2021-05-10 07:13:36 +00:00
subnet_id
2020-08-20 16:30:47 +00:00
let
wallTime = self . getWallTime ( )
( afterGenesis , wallSlot ) = wallTime . toSlot ( )
if not afterGenesis :
notice " Attestation before genesis "
2020-10-20 12:31:20 +00:00
return ValidationResult . Ignore
2020-08-20 16:30:47 +00:00
logScope : wallSlot
2020-09-02 16:16:25 +00:00
# Potential under/overflows are fine; would just create odd metrics and logs
2020-08-20 16:30:47 +00:00
let delay = wallTime - attestation . data . slot . toBeaconTime
2020-10-09 08:58:54 +00:00
debug " Attestation received " , delay
2021-04-02 14:36:43 +00:00
# Now proceed to validation
let v = await self . attestationPool . validateAttestation (
2021-05-10 07:13:36 +00:00
self . batchCrypto , attestation , wallTime , subnet_id , checkSignature )
2020-08-27 07:34:12 +00:00
if v . isErr ( ) :
debug " Dropping attestation " , err = v . error ( )
2020-09-18 11:53:09 +00:00
return v . error [ 0 ]
2020-08-20 16:30:47 +00:00
beacon_attestations_received . inc ( )
2020-11-11 13:39:36 +00:00
beacon_attestation_delay . observe ( delay . toFloatSeconds ( ) )
2020-08-20 16:30:47 +00:00
2021-04-26 20:39:44 +00:00
let ( attestation_index , sig ) = v . get ( )
2021-04-09 12:59:24 +00:00
self [ ] . checkForPotentialDoppelganger (
2021-04-26 20:39:44 +00:00
attestation . data , [ attestation_index ] , wallSlot )
2020-10-27 17:21:35 +00:00
2020-08-27 07:34:12 +00:00
trace " Attestation validated "
2021-04-26 20:39:44 +00:00
self . attestationPool [ ] . addAttestation (
attestation , [ attestation_index ] , sig , wallSlot )
2020-08-20 16:30:47 +00:00
2021-04-02 14:36:43 +00:00
return ValidationResult . Accept
2020-08-20 16:30:47 +00:00
proc aggregateValidator * (
2021-04-02 14:36:43 +00:00
self : ref Eth2Processor ,
signedAggregateAndProof : SignedAggregateAndProof ) : Future [ ValidationResult ] {. async . } =
2020-08-20 16:30:47 +00:00
logScope :
aggregate = shortLog ( signedAggregateAndProof . message . aggregate )
signature = shortLog ( signedAggregateAndProof . signature )
let
wallTime = self . getWallTime ( )
( afterGenesis , wallSlot ) = wallTime . toSlot ( )
if not afterGenesis :
notice " Aggregate before genesis "
2020-10-20 12:31:20 +00:00
return ValidationResult . Ignore
2020-08-20 16:30:47 +00:00
logScope : wallSlot
2020-09-02 16:16:25 +00:00
# Potential under/overflows are fine; would just create odd logs
2020-08-20 16:30:47 +00:00
let delay =
wallTime - signedAggregateAndProof . message . aggregate . data . slot . toBeaconTime
debug " Aggregate received " , delay
2021-04-02 14:36:43 +00:00
let v = await self . attestationPool . validateAggregate (
self . batchCrypto ,
2020-08-27 07:34:12 +00:00
signedAggregateAndProof , wallTime )
if v . isErr :
2020-12-14 20:58:32 +00:00
debug " Dropping aggregate " ,
err = v . error ,
aggregator_index = signedAggregateAndProof . message . aggregator_index ,
selection_proof = signedAggregateAndProof . message . selection_proof ,
wallSlot
2020-09-18 11:53:09 +00:00
return v . error [ 0 ]
2020-08-20 16:30:47 +00:00
beacon_aggregates_received . inc ( )
2020-11-11 13:39:36 +00:00
beacon_aggregate_delay . observe ( delay . toFloatSeconds ( ) )
2020-08-20 16:30:47 +00:00
2021-04-26 20:39:44 +00:00
let ( attesting_indices , sig ) = v . get ( )
2021-04-02 14:36:43 +00:00
self [ ] . checkForPotentialDoppelganger (
2021-04-26 20:39:44 +00:00
signedAggregateAndProof . message . aggregate . data , attesting_indices ,
2021-04-09 12:59:24 +00:00
wallSlot )
2020-10-27 17:21:35 +00:00
2020-12-14 20:58:32 +00:00
trace " Aggregate validated " ,
aggregator_index = signedAggregateAndProof . message . aggregator_index ,
selection_proof = signedAggregateAndProof . message . selection_proof ,
wallSlot
2021-04-26 20:39:44 +00:00
self . attestationPool [ ] . addAttestation (
signedAggregateAndProof . message . aggregate , attesting_indices , sig , wallSlot )
2020-08-20 16:30:47 +00:00
2021-04-02 14:36:43 +00:00
return ValidationResult . Accept
2020-09-14 14:26:31 +00:00
proc attesterSlashingValidator * (
2020-09-18 11:53:09 +00:00
self : var Eth2Processor , attesterSlashing : AttesterSlashing ) :
ValidationResult =
2020-09-14 14:26:31 +00:00
logScope :
attesterSlashing = shortLog ( attesterSlashing )
let v = self . exitPool [ ] . validateAttesterSlashing ( attesterSlashing )
if v . isErr :
debug " Dropping attester slashing " , err = v . error
2020-09-18 11:53:09 +00:00
return v . error [ 0 ]
2020-09-14 14:26:31 +00:00
beacon_attester_slashings_received . inc ( )
2020-10-20 12:31:20 +00:00
ValidationResult . Accept
2020-09-18 11:53:09 +00:00
2020-09-14 14:26:31 +00:00
proc proposerSlashingValidator * (
2020-09-18 11:53:09 +00:00
self : var Eth2Processor , proposerSlashing : ProposerSlashing ) :
ValidationResult =
2020-09-14 14:26:31 +00:00
logScope :
proposerSlashing = shortLog ( proposerSlashing )
let v = self . exitPool [ ] . validateProposerSlashing ( proposerSlashing )
if v . isErr :
debug " Dropping proposer slashing " , err = v . error
2020-09-18 11:53:09 +00:00
return v . error [ 0 ]
2020-09-14 14:26:31 +00:00
beacon_proposer_slashings_received . inc ( )
2020-10-20 12:31:20 +00:00
ValidationResult . Accept
2020-09-18 11:53:09 +00:00
2020-09-14 14:26:31 +00:00
proc voluntaryExitValidator * (
2020-09-24 17:05:49 +00:00
self : var Eth2Processor , signedVoluntaryExit : SignedVoluntaryExit ) :
ValidationResult =
2020-09-14 14:26:31 +00:00
logScope :
2020-09-24 17:05:49 +00:00
signedVoluntaryExit = shortLog ( signedVoluntaryExit )
2020-09-14 14:26:31 +00:00
2020-09-24 17:05:49 +00:00
let v = self . exitPool [ ] . validateVoluntaryExit ( signedVoluntaryExit )
2020-09-14 14:26:31 +00:00
if v . isErr :
debug " Dropping voluntary exit " , err = v . error
2020-09-18 11:53:09 +00:00
return v . error [ 0 ]
2020-09-14 14:26:31 +00:00
beacon_voluntary_exits_received . inc ( )
2020-10-20 12:31:20 +00:00
ValidationResult . Accept