2022-06-09 08:50:36 +00:00
|
|
|
# beacon_chain
|
|
|
|
# Copyright (c) 2021-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 std/[tables, os, sets, sequtils]
|
2021-07-13 11:15:07 +00:00
|
|
|
import chronos, presto, presto/client as presto_client, chronicles, confutils,
|
|
|
|
json_serialization/std/[options, net],
|
2021-09-27 09:24:58 +00:00
|
|
|
stew/[base10, results, byteutils]
|
2021-08-03 15:17:11 +00:00
|
|
|
|
2021-07-13 11:15:07 +00:00
|
|
|
# Local modules
|
2021-08-12 13:08:20 +00:00
|
|
|
import
|
|
|
|
../spec/datatypes/[phase0, altair],
|
2021-10-19 14:09:26 +00:00
|
|
|
../spec/[eth2_merkleization, helpers, signatures,
|
|
|
|
validator],
|
|
|
|
../spec/eth2_apis/[eth2_rest_serialization, rest_beacon_client],
|
|
|
|
../validators/[keystore_management, validator_pool, slashing_protection],
|
2022-05-10 10:03:40 +00:00
|
|
|
".."/[conf, beacon_clock, version, nimbus_binary_common],
|
|
|
|
../spec/eth2_apis/eth2_rest_serialization
|
2021-10-19 14:09:26 +00:00
|
|
|
|
|
|
|
export os, sets, sequtils, sequtils, chronos, presto, chronicles, confutils,
|
2021-07-13 11:15:07 +00:00
|
|
|
nimbus_binary_common, version, conf, options, tables, results, base10,
|
2021-08-03 15:17:11 +00:00
|
|
|
byteutils, presto_client
|
2021-07-13 11:15:07 +00:00
|
|
|
|
2021-10-19 14:09:26 +00:00
|
|
|
export eth2_rest_serialization, rest_beacon_client,
|
2021-08-24 19:49:51 +00:00
|
|
|
phase0, altair, helpers, signatures, validator, eth2_merkleization,
|
2021-07-13 11:15:07 +00:00
|
|
|
beacon_clock,
|
2021-10-19 14:09:26 +00:00
|
|
|
keystore_management, slashing_protection, validator_pool
|
2021-07-13 11:15:07 +00:00
|
|
|
|
|
|
|
const
|
|
|
|
SYNC_TOLERANCE* = 4'u64
|
|
|
|
SLOT_LOOKAHEAD* = 1.seconds
|
|
|
|
HISTORICAL_DUTIES_EPOCHS* = 2'u64
|
|
|
|
TIME_DELAY_FROM_SLOT* = 79.milliseconds
|
|
|
|
SUBSCRIPTION_BUFFER_SLOTS* = 2'u64
|
|
|
|
|
|
|
|
type
|
|
|
|
ServiceState* {.pure.} = enum
|
|
|
|
Initialized, Running, Error, Closing, Closed
|
|
|
|
|
|
|
|
BlockServiceEventRef* = ref object of RootObj
|
|
|
|
slot*: Slot
|
|
|
|
proposers*: seq[ValidatorPubKey]
|
|
|
|
|
|
|
|
ClientServiceRef* = ref object of RootObj
|
|
|
|
state*: ServiceState
|
|
|
|
lifeFut*: Future[void]
|
|
|
|
client*: ValidatorClientRef
|
|
|
|
|
|
|
|
DutiesServiceRef* = ref object of ClientServiceRef
|
|
|
|
|
|
|
|
FallbackServiceRef* = ref object of ClientServiceRef
|
|
|
|
|
|
|
|
ForkServiceRef* = ref object of ClientServiceRef
|
|
|
|
|
|
|
|
AttestationServiceRef* = ref object of ClientServiceRef
|
|
|
|
|
|
|
|
BlockServiceRef* = ref object of ClientServiceRef
|
|
|
|
|
2022-05-10 10:03:40 +00:00
|
|
|
SyncCommitteeServiceRef* = ref object of ClientServiceRef
|
|
|
|
|
2021-07-13 11:15:07 +00:00
|
|
|
DutyAndProof* = object
|
|
|
|
epoch*: Epoch
|
|
|
|
dependentRoot*: Eth2Digest
|
|
|
|
data*: RestAttesterDuty
|
|
|
|
slotSig*: Option[ValidatorSig]
|
|
|
|
|
2022-05-10 10:03:40 +00:00
|
|
|
SyncCommitteeDuty* = object
|
|
|
|
pubkey*: ValidatorPubKey
|
|
|
|
validator_index*: ValidatorIndex
|
|
|
|
validator_sync_committee_index*: IndexInSyncCommittee
|
|
|
|
|
|
|
|
SyncDutyAndProof* = object
|
|
|
|
epoch*: Epoch
|
|
|
|
data*: SyncCommitteeDuty
|
|
|
|
slotSig*: Option[ValidatorSig]
|
|
|
|
|
|
|
|
SyncCommitteeSubscriptionInfo* = object
|
|
|
|
validator_index*: ValidatorIndex
|
|
|
|
validator_sync_committee_indices*: seq[IndexInSyncCommittee]
|
|
|
|
|
2021-07-13 11:15:07 +00:00
|
|
|
ProposerTask* = object
|
|
|
|
duty*: RestProposerDuty
|
|
|
|
future*: Future[void]
|
|
|
|
|
|
|
|
ProposedData* = object
|
|
|
|
epoch*: Epoch
|
|
|
|
dependentRoot*: Eth2Digest
|
|
|
|
duties*: seq[ProposerTask]
|
|
|
|
|
|
|
|
BeaconNodeServer* = object
|
|
|
|
client*: RestClientRef
|
|
|
|
endpoint*: string
|
2021-10-20 09:58:38 +00:00
|
|
|
config*: Option[RestSpecVC]
|
2021-07-13 11:15:07 +00:00
|
|
|
ident*: Option[string]
|
2021-08-03 15:17:11 +00:00
|
|
|
genesis*: Option[RestGenesis]
|
2021-07-13 11:15:07 +00:00
|
|
|
syncInfo*: Option[RestSyncInfo]
|
|
|
|
status*: RestBeaconNodeStatus
|
|
|
|
|
|
|
|
EpochDuties* = object
|
|
|
|
duties*: Table[Epoch, DutyAndProof]
|
|
|
|
|
2022-05-10 10:03:40 +00:00
|
|
|
EpochSyncDuties* = object
|
|
|
|
duties*: Table[Epoch, SyncDutyAndProof]
|
|
|
|
|
2021-07-13 11:15:07 +00:00
|
|
|
RestBeaconNodeStatus* {.pure.} = enum
|
|
|
|
Uninitalized, Offline, Incompatible, NotSynced, Online
|
|
|
|
|
|
|
|
BeaconNodeServerRef* = ref BeaconNodeServer
|
|
|
|
|
|
|
|
AttesterMap* = Table[ValidatorPubKey, EpochDuties]
|
2022-05-10 10:03:40 +00:00
|
|
|
SyncCommitteeDutiesMap* = Table[ValidatorPubKey, EpochSyncDuties]
|
2021-07-13 11:15:07 +00:00
|
|
|
ProposerMap* = Table[Epoch, ProposedData]
|
|
|
|
|
|
|
|
ValidatorClient* = object
|
|
|
|
config*: ValidatorClientConf
|
|
|
|
graffitiBytes*: GraffitiBytes
|
|
|
|
beaconNodes*: seq[BeaconNodeServerRef]
|
|
|
|
nodesAvailable*: AsyncEvent
|
|
|
|
fallbackService*: FallbackServiceRef
|
|
|
|
forkService*: ForkServiceRef
|
|
|
|
dutiesService*: DutiesServiceRef
|
|
|
|
attestationService*: AttestationServiceRef
|
|
|
|
blockService*: BlockServiceRef
|
2022-05-10 10:03:40 +00:00
|
|
|
syncCommitteeService*: SyncCommitteeServiceRef
|
2021-07-13 11:15:07 +00:00
|
|
|
runSlotLoop*: Future[void]
|
|
|
|
beaconClock*: BeaconClock
|
|
|
|
attachedValidators*: ValidatorPool
|
2022-02-16 11:31:23 +00:00
|
|
|
forks*: seq[Fork]
|
|
|
|
forksAvailable*: AsyncEvent
|
2021-07-13 11:15:07 +00:00
|
|
|
attesters*: AttesterMap
|
|
|
|
proposers*: ProposerMap
|
2022-05-10 10:03:40 +00:00
|
|
|
syncCommitteeDuties*: SyncCommitteeDutiesMap
|
2021-08-03 15:17:11 +00:00
|
|
|
beaconGenesis*: RestGenesis
|
2021-07-13 11:15:07 +00:00
|
|
|
proposerTasks*: Table[Slot, seq[ProposerTask]]
|
|
|
|
|
|
|
|
ValidatorClientRef* = ref ValidatorClient
|
|
|
|
|
|
|
|
ValidatorClientError* = object of CatchableError
|
|
|
|
ValidatorApiError* = object of ValidatorClientError
|
|
|
|
|
|
|
|
const
|
|
|
|
DefaultDutyAndProof* = DutyAndProof(epoch: Epoch(0xFFFF_FFFF_FFFF_FFFF'u64))
|
2022-05-10 10:03:40 +00:00
|
|
|
DefaultSyncDutyAndProof* = SyncDutyAndProof(epoch: Epoch(0xFFFF_FFFF_FFFF_FFFF'u64))
|
2021-07-13 11:15:07 +00:00
|
|
|
SlotDuration* = int64(SECONDS_PER_SLOT).seconds
|
2022-01-05 13:41:39 +00:00
|
|
|
OneThirdDuration* = int64(SECONDS_PER_SLOT).seconds div INTERVALS_PER_SLOT
|
2021-07-13 11:15:07 +00:00
|
|
|
|
|
|
|
proc `$`*(bn: BeaconNodeServerRef): string =
|
|
|
|
if bn.ident.isSome():
|
|
|
|
bn.client.address.hostname & ":" &
|
|
|
|
Base10.toString(bn.client.address.port) & " [" & bn.ident.get() & "]"
|
|
|
|
else:
|
|
|
|
bn.client.address.hostname & ":" &
|
|
|
|
Base10.toString(bn.client.address.port)
|
|
|
|
|
|
|
|
chronicles.formatIt BeaconNodeServerRef:
|
|
|
|
$it
|
|
|
|
|
|
|
|
chronicles.expandIt(RestAttesterDuty):
|
|
|
|
pubkey = shortLog(it.pubkey)
|
|
|
|
slot = it.slot
|
|
|
|
validator_index = it.validator_index
|
|
|
|
committee_index = it.committee_index
|
|
|
|
committee_length = it.committee_length
|
|
|
|
committees_at_slot = it.committees_at_slot
|
|
|
|
validator_committee_index = it.validator_committee_index
|
|
|
|
|
|
|
|
proc stop*(csr: ClientServiceRef) {.async.} =
|
|
|
|
if csr.state == ServiceState.Running:
|
|
|
|
csr.state = ServiceState.Closing
|
|
|
|
if not(csr.lifeFut.finished()):
|
|
|
|
await csr.lifeFut.cancelAndWait()
|
|
|
|
csr.state = ServiceState.Closed
|
|
|
|
|
|
|
|
proc isDefault*(dap: DutyAndProof): bool =
|
|
|
|
dap.epoch == Epoch(0xFFFF_FFFF_FFFF_FFFF'u64)
|
|
|
|
|
2022-05-10 10:03:40 +00:00
|
|
|
proc isDefault*(sdap: SyncDutyAndProof): bool =
|
|
|
|
sdap.epoch == Epoch(0xFFFF_FFFF_FFFF_FFFF'u64)
|
|
|
|
|
2021-07-13 11:15:07 +00:00
|
|
|
proc isDefault*(prd: ProposedData): bool =
|
|
|
|
prd.epoch == Epoch(0xFFFF_FFFF_FFFF_FFFF'u64)
|
|
|
|
|
|
|
|
proc init*(t: typedesc[DutyAndProof], epoch: Epoch, dependentRoot: Eth2Digest,
|
|
|
|
duty: RestAttesterDuty,
|
|
|
|
slotSig: Option[ValidatorSig]): DutyAndProof =
|
|
|
|
DutyAndProof(epoch: epoch, dependentRoot: dependentRoot, data: duty,
|
|
|
|
slotSig: slotSig)
|
|
|
|
|
2022-05-10 10:03:40 +00:00
|
|
|
proc init*(t: typedesc[SyncDutyAndProof], epoch: Epoch,
|
|
|
|
duty: SyncCommitteeDuty,
|
|
|
|
slotSig: Option[ValidatorSig]): SyncDutyAndProof =
|
|
|
|
SyncDutyAndProof(epoch: epoch, data: duty, slotSig: slotSig)
|
|
|
|
|
2021-07-13 11:15:07 +00:00
|
|
|
proc init*(t: typedesc[ProposedData], epoch: Epoch, dependentRoot: Eth2Digest,
|
2022-04-08 16:22:49 +00:00
|
|
|
data: openArray[ProposerTask]): ProposedData =
|
2021-07-13 11:15:07 +00:00
|
|
|
ProposedData(epoch: epoch, dependentRoot: dependentRoot, duties: @data)
|
|
|
|
|
|
|
|
proc getCurrentSlot*(vc: ValidatorClientRef): Option[Slot] =
|
|
|
|
let
|
|
|
|
wallTime = vc.beaconClock.now()
|
|
|
|
wallSlot = wallTime.toSlot()
|
|
|
|
|
|
|
|
if not(wallSlot.afterGenesis):
|
2022-01-11 10:01:54 +00:00
|
|
|
let checkGenesisTime = vc.beaconClock.fromNow(start_beacon_time(Slot(0)))
|
2021-07-13 11:15:07 +00:00
|
|
|
warn "Jump in time detected, something wrong with wallclock",
|
|
|
|
wall_time = wallTime, genesisIn = checkGenesisTime.offset
|
|
|
|
none[Slot]()
|
|
|
|
else:
|
|
|
|
some(wallSlot.slot)
|
|
|
|
|
|
|
|
proc getAttesterDutiesForSlot*(vc: ValidatorClientRef,
|
2021-07-15 08:17:32 +00:00
|
|
|
slot: Slot): seq[DutyAndProof] =
|
2022-05-10 10:03:40 +00:00
|
|
|
## Returns all `DutyAndProof` for the given `slot`.
|
2021-07-15 08:17:32 +00:00
|
|
|
var res: seq[DutyAndProof]
|
2021-07-13 11:15:07 +00:00
|
|
|
let epoch = slot.epoch()
|
2022-05-10 10:03:40 +00:00
|
|
|
for key, item in vc.attesters:
|
2021-07-13 11:15:07 +00:00
|
|
|
let duty = item.duties.getOrDefault(epoch, DefaultDutyAndProof)
|
|
|
|
if not(duty.isDefault()):
|
|
|
|
if duty.data.slot == slot:
|
2021-07-15 08:17:32 +00:00
|
|
|
res.add(duty)
|
2021-07-13 11:15:07 +00:00
|
|
|
res
|
|
|
|
|
2022-05-10 10:03:40 +00:00
|
|
|
proc getSyncCommitteeDutiesForSlot*(vc: ValidatorClientRef,
|
|
|
|
slot: Slot): seq[SyncDutyAndProof] =
|
|
|
|
## Returns all `SyncDutyAndProof` for the given `slot`.
|
|
|
|
var res: seq[SyncDutyAndProof]
|
|
|
|
let epoch = slot.epoch()
|
|
|
|
for key, item in mpairs(vc.syncCommitteeDuties):
|
|
|
|
item.duties.withValue(epoch, duty):
|
|
|
|
res.add(duty[])
|
|
|
|
res
|
|
|
|
|
|
|
|
proc removeOldSyncPeriodDuties*(vc: ValidatorClientRef,
|
|
|
|
slot: Slot) =
|
|
|
|
if slot.is_sync_committee_period:
|
|
|
|
let epoch = slot.epoch()
|
|
|
|
var prunedDuties = SyncCommitteeDutiesMap()
|
|
|
|
for key, item in vc.syncCommitteeDuties:
|
|
|
|
var curPeriodDuties = EpochSyncDuties()
|
|
|
|
for epochKey, epochDuty in item.duties:
|
|
|
|
if epochKey >= epoch:
|
|
|
|
curPeriodDuties.duties[epochKey] = epochDuty
|
|
|
|
prunedDuties[key] = curPeriodDuties
|
|
|
|
vc.syncCommitteeDuties = prunedDuties
|
|
|
|
|
2021-07-13 11:15:07 +00:00
|
|
|
proc getDurationToNextAttestation*(vc: ValidatorClientRef,
|
|
|
|
slot: Slot): string =
|
2022-01-11 10:01:54 +00:00
|
|
|
var minSlot = FAR_FUTURE_SLOT
|
2021-07-13 11:15:07 +00:00
|
|
|
let currentEpoch = slot.epoch()
|
|
|
|
for epoch in [currentEpoch, currentEpoch + 1'u64]:
|
2022-05-10 10:03:40 +00:00
|
|
|
for key, item in vc.attesters:
|
2021-07-13 11:15:07 +00:00
|
|
|
let duty = item.duties.getOrDefault(epoch, DefaultDutyAndProof)
|
|
|
|
if not(duty.isDefault()):
|
2022-01-11 10:01:54 +00:00
|
|
|
let dutySlotTime = duty.data.slot
|
|
|
|
if duty.data.slot < minSlot:
|
|
|
|
minSlot = duty.data.slot
|
|
|
|
if minSlot != FAR_FUTURE_SLOT:
|
2021-07-13 11:15:07 +00:00
|
|
|
break
|
2022-01-11 10:01:54 +00:00
|
|
|
|
|
|
|
if minSlot == FAR_FUTURE_SLOT:
|
2021-07-13 11:15:07 +00:00
|
|
|
"<unknown>"
|
|
|
|
else:
|
2022-01-11 10:01:54 +00:00
|
|
|
$(minSlot.attestation_deadline() - slot.start_beacon_time())
|
2021-07-13 11:15:07 +00:00
|
|
|
|
|
|
|
proc getDurationToNextBlock*(vc: ValidatorClientRef, slot: Slot): string =
|
2022-01-11 10:01:54 +00:00
|
|
|
var minSlot = FAR_FUTURE_SLOT
|
2021-07-13 11:15:07 +00:00
|
|
|
let currentEpoch = slot.epoch()
|
|
|
|
for epoch in [currentEpoch, currentEpoch + 1'u64]:
|
|
|
|
let data = vc.proposers.getOrDefault(epoch)
|
|
|
|
if not(data.isDefault()):
|
|
|
|
for item in data.duties:
|
|
|
|
if item.duty.pubkey in vc.attachedValidators:
|
2022-01-11 10:01:54 +00:00
|
|
|
if item.duty.slot < minSlot:
|
|
|
|
minSlot = item.duty.slot
|
|
|
|
if minSlot != FAR_FUTURE_SLOT:
|
2021-07-13 11:15:07 +00:00
|
|
|
break
|
2022-01-11 10:01:54 +00:00
|
|
|
if minSlot == FAR_FUTURE_SLOT:
|
2021-07-13 11:15:07 +00:00
|
|
|
"<unknown>"
|
|
|
|
else:
|
2022-01-11 10:01:54 +00:00
|
|
|
$(minSlot.block_deadline() - slot.start_beacon_time())
|
2021-07-13 11:15:07 +00:00
|
|
|
|
|
|
|
iterator attesterDutiesForEpoch*(vc: ValidatorClientRef,
|
|
|
|
epoch: Epoch): DutyAndProof =
|
2022-05-10 10:03:40 +00:00
|
|
|
for key, item in vc.attesters:
|
2021-07-13 11:15:07 +00:00
|
|
|
let epochDuties = item.duties.getOrDefault(epoch)
|
|
|
|
if not(isDefault(epochDuties)):
|
|
|
|
yield epochDuties
|
|
|
|
|
2022-05-10 10:03:40 +00:00
|
|
|
proc syncMembersSubscriptionInfoForEpoch*(
|
|
|
|
vc: ValidatorClientRef,
|
|
|
|
epoch: Epoch): seq[SyncCommitteeSubscriptionInfo] =
|
|
|
|
var res: seq[SyncCommitteeSubscriptionInfo]
|
|
|
|
for key, item in mpairs(vc.syncCommitteeDuties):
|
|
|
|
var cur: SyncCommitteeSubscriptionInfo
|
|
|
|
var initialized = false
|
|
|
|
|
|
|
|
item.duties.withValue(epoch, epochDuties):
|
|
|
|
if not initialized:
|
|
|
|
cur.validator_index = epochDuties.data.validator_index
|
|
|
|
initialized = true
|
|
|
|
cur.validator_sync_committee_indices.add(
|
|
|
|
epochDuties.data.validator_sync_committee_index)
|
|
|
|
|
|
|
|
if initialized:
|
|
|
|
res.add cur
|
|
|
|
|
|
|
|
res
|
|
|
|
|
2022-01-11 10:01:54 +00:00
|
|
|
proc getDelay*(vc: ValidatorClientRef, deadline: BeaconTime): TimeDiff =
|
|
|
|
vc.beaconClock.now() - deadline
|
2021-07-19 14:31:02 +00:00
|
|
|
|
|
|
|
proc getValidator*(vc: ValidatorClientRef,
|
2022-04-08 16:22:49 +00:00
|
|
|
key: ValidatorPubKey): Option[AttachedValidator] =
|
2021-07-19 14:31:02 +00:00
|
|
|
let validator = vc.attachedValidators.getValidator(key)
|
|
|
|
if isNil(validator):
|
|
|
|
warn "Validator not in pool anymore", validator = shortLog(validator)
|
|
|
|
none[AttachedValidator]()
|
|
|
|
else:
|
|
|
|
if validator.index.isNone():
|
|
|
|
warn "Validator index is missing", validator = shortLog(validator)
|
|
|
|
none[AttachedValidator]()
|
|
|
|
else:
|
|
|
|
some(validator)
|
2022-02-16 11:31:23 +00:00
|
|
|
|
|
|
|
proc forkAtEpoch*(vc: ValidatorClientRef, epoch: Epoch): Fork =
|
|
|
|
# If schedule is present, it MUST not be empty.
|
|
|
|
doAssert(len(vc.forks) > 0)
|
|
|
|
var res: Fork
|
|
|
|
for item in vc.forks:
|
|
|
|
if item.epoch <= epoch:
|
|
|
|
res = item
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
res
|
2022-05-10 10:03:40 +00:00
|
|
|
|
|
|
|
proc getSubcommitteeIndex*(syncCommitteeIndex: IndexInSyncCommittee): SyncSubcommitteeIndex =
|
|
|
|
SyncSubcommitteeIndex(uint16(syncCommitteeIndex) div SYNC_SUBCOMMITTEE_SIZE)
|