nimbus-eth2/beacon_chain/validator_client/doppelganger_service.nim
zah df5ef95111
Doppelganger detection bug fix (#3997)
When the client was started without any validators, the doppelganger
detection structures were never initialized properly. Later, when
validators were added through the Keymanager API, they interacted
with the uninitialized doppelganger detection structures and their
duties were inappropriately skipped.
2022-08-19 13:34:08 +03:00

118 lines
4.3 KiB
Nim

# beacon_chain
# Copyright (c) 2022 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.
import chronicles
import common, api
const
ServiceName = "doppelganger_service"
logScope: service = ServiceName
const
DOPPELGANGER_EPOCHS_COUNT = 2
proc getCheckingList*(vc: ValidatorClientRef): seq[ValidatorIndex] =
var res: seq[ValidatorIndex]
for index, value in vc.doppelgangerDetection.validators.pairs():
if value.status == DoppelgangerStatus.Checking:
res.add(index)
res
proc waitForNextEpoch(service: DoppelgangerServiceRef) {.async.} =
let vc = service.client
let sleepTime = vc.beaconClock.durationToNextEpoch() + TIME_DELAY_FROM_SLOT
debug "Sleeping until next epoch", sleep_time = sleepTime
await sleepAsync(sleepTime)
proc processActivities(service: DoppelgangerServiceRef, epoch: Epoch,
activities: GetValidatorsActivityResponse) =
let vc = service.client
if len(activities.data) == 0:
debug "Unable to monitor validator's activity for epoch", epoch = epoch
for index, value in vc.doppelgangerDetection.validators.mpairs():
if value.status == DoppelgangerStatus.Checking:
value.epochsCount = 0'u64
value.lastAttempt = DoppelgangerAttempt.Failure
else:
for activity in activities.data:
let vindex = activity.index
vc.doppelgangerDetection.validators.withValue(vindex, value):
if activity.active:
if value.status == DoppelgangerStatus.Checking:
value.epochsCount = 0'u64
value.lastAttempt = DoppelgangerAttempt.SuccessTrue
warn "Validator's activity has been seen",
validator_index = vindex, epoch = epoch
vc.gracefulExit.fire()
return
else:
if value.status == DoppelgangerStatus.Checking:
value.lastAttempt = DoppelgangerAttempt.SuccessFalse
if value.epochsCount == DOPPELGANGER_EPOCHS_COUNT:
value.status = DoppelgangerStatus.Passed
info "Validator successfully passed doppelganger detection",
validator_index = vindex
else:
inc(value.epochsCount)
debug "Validator's activity was not seen",
validator_index = vindex, epoch = epoch,
epochs_count = value.epochsCount
proc mainLoop(service: DoppelgangerServiceRef) {.async.} =
let vc = service.client
service.state = ServiceState.Running
if service.enabled:
debug "Service started"
else:
debug "Service disabled because of configuration settings"
return
while true:
let breakLoop =
try:
await service.waitForNextEpoch()
let
currentEpoch = vc.currentSlot().epoch()
previousEpoch =
if currentEpoch == Epoch(0):
currentEpoch
else:
currentEpoch - 1'u64
validators = vc.getCheckingList()
if len(validators) > 0:
let activities = await vc.getValidatorsActivity(previousEpoch,
validators)
service.processActivities(previousEpoch, activities)
else:
debug "No validators found that require doppelganger protection"
discard
false
except CancelledError:
debug "Service interrupted"
true
except CatchableError as exc:
warn "Service crashed with unexpected error", err_name = exc.name,
err_msg = exc.msg
true
if breakLoop:
break
proc init*(t: type DoppelgangerServiceRef,
vc: ValidatorClientRef): Future[DoppelgangerServiceRef] {.async.} =
logScope: service = ServiceName
let res = DoppelgangerServiceRef(name: ServiceName,
client: vc, state: ServiceState.Initialized,
enabled: vc.config.doppelgangerDetection)
debug "Initializing service"
return res
proc start*(service: DoppelgangerServiceRef) =
service.lifeFut = mainLoop(service)