show next attestation slot & wait time in Slot end log

This commit is contained in:
Dustin Brody 2021-02-14 16:37:32 +01:00 committed by zah
parent f398720fc4
commit c7093c4ab5
4 changed files with 89 additions and 15 deletions

View File

@ -151,4 +151,19 @@ type
validators*: Table[ValidatorPubKey, AttachedValidator] validators*: Table[ValidatorPubKey, AttachedValidator]
slashingProtection*: SlashingProtectionDB slashingProtection*: SlashingProtectionDB
AttestationSubnets* = object
enabled*: bool
stabilitySubnets*: seq[tuple[subnet: uint8, expiration: Epoch]]
nextCycleEpoch*: Epoch
# These encode states in per-subnet state machines
subscribedSubnets*: set[uint8]
subscribeSlot*: array[ATTESTATION_SUBNET_COUNT, Slot]
unsubscribeSlot*: array[ATTESTATION_SUBNET_COUNT, Slot]
# Used to track the next attestation slots, using an epoch-relative
# coordinate system. Defaults don't need initialization.
attestingSlots*: array[2, uint32]
lastCalculatedAttestationEpoch*: Epoch
func shortLog*(v: AttachedValidator): string = shortLog(v.pubKey) func shortLog*(v: AttachedValidator): string = shortLog(v.pubKey)

View File

@ -457,6 +457,11 @@ proc updateSubscriptionSchedule(node: BeaconNode, epoch: Epoch) {.async.} =
node.chainDag.headState.data.data.fork, node.chainDag.headState.data.data.fork,
node.chainDag.headState.data.data.genesis_validators_root, slot))) node.chainDag.headState.data.data.genesis_validators_root, slot)))
# The relevant bitmap are 32 bits each.
static: doAssert SLOTS_PER_EPOCH <= 32
node.attestationSubnets.lastCalculatedAttestationEpoch = epoch
node.attestationSubnets.attestingSlots[epoch mod 2] = 0
for (validatorIndices, committeeIndex, subnetIndex, slot) in for (validatorIndices, committeeIndex, subnetIndex, slot) in
get_committee_assignments( get_committee_assignments(
node.chainDag.headState.data.data, epoch, validatorIndices, cache): node.chainDag.headState.data.data, epoch, validatorIndices, cache):
@ -469,6 +474,23 @@ proc updateSubscriptionSchedule(node: BeaconNode, epoch: Epoch) {.async.} =
get_beacon_committee_len( get_beacon_committee_len(
node.chainDag.headState.data.data, slot, committeeIndex, cache) node.chainDag.headState.data.data, slot, committeeIndex, cache)
# Each get_committee_assignments() call here is on the next epoch. At any
# given time, only care about two epochs, the current and next epoch. So,
# after it is done for an epoch, [aS[epoch mod 2], aS[1 - (epoch mod 2)]]
# provides, sequentially, the current and next epochs' slot schedules. If
# get_committee_assignments() has not been called for the next epoch yet,
# typically because there hasn't been a block in the current epoch, there
# isn't valid information in aS[1 - (epoch mod 2)], and only slots within
# the current epoch can be known. Usually, this is not a major issue, but
# when there hasn't been a block substantially through an epoch, it might
# prove misleading to claim that there aren't attestations known, when it
# only might be known either way for 3 more slots. However, it's also not
# as important to attest when blocks aren't flowing as only attestions in
# blocks garner rewards.
node.attestationSubnets.attestingSlots[epoch mod 2] =
node.attestationSubnets.attestingSlots[epoch mod 2] or
(1'u32 shl (slot mod SLOTS_PER_EPOCH))
if not isAnyCommitteeValidatorAggregating( if not isAnyCommitteeValidatorAggregating(
validatorIndices, committeeLen, slot): validatorIndices, committeeLen, slot):
continue continue
@ -606,6 +628,9 @@ proc getInitialAttestationSubnets(node: BeaconNode): Table[uint8, Slot] =
var cache = StateCache() var cache = StateCache()
template mergeAttestationSubnets(epoch: Epoch) = template mergeAttestationSubnets(epoch: Epoch) =
# TODO when https://github.com/nim-lang/Nim/issues/15972 and
# https://github.com/nim-lang/Nim/issues/16217 are fixed, in
# Nimbus's Nim, use (_, _, subnetIndex, slot).
for (_, ci, subnetIndex, slot) in get_committee_assignments( for (_, ci, subnetIndex, slot) in get_committee_assignments(
node.chainDag.headState.data.data, epoch, validatorIndices, cache): node.chainDag.headState.data.data, epoch, validatorIndices, cache):
if subnetIndex in result: if subnetIndex in result:
@ -821,7 +846,36 @@ proc updateGossipStatus(node: BeaconNode, slot: Slot) =
# This exits early all but one call each epoch. # This exits early all but one call each epoch.
traceAsyncErrors node.cycleAttestationSubnets(slot) traceAsyncErrors node.cycleAttestationSubnets(slot)
proc onSlotEnd(node: BeaconNode, slot, nextSlot: Slot) {.async.} = func getNextAttestation(node: BeaconNode, slot: Slot): Slot =
# The relevant attestations are in, depending on calculated bounds:
# [aS[epoch mod 2], aS[1 - (epoch mod 2)]]
# current epoch next epoch
let orderedAttestingSlots = [
node.attestationSubnets.attestingSlots[ slot.epoch mod 2'u64],
node.attestationSubnets.attestingSlots[1 - (slot.epoch mod 2'u64)]]
static: doAssert MIN_ATTESTATION_INCLUSION_DELAY == 1
# Cleverer ways exist, but a short loop is fine. O(n) vs O(log n) isn't that
# important when n is 32 or 64, with early exit on average no more than half
# through.
for i in [0'u64, 1'u64]:
let bitmapEpoch = slot.epoch + i
if bitmapEpoch > node.attestationSubnets.lastCalculatedAttestationEpoch:
doAssert i == 0 or orderedAttestingSlots[0] == 0
return FAR_FUTURE_SLOT
for slotOffset in 0 ..< SLOTS_PER_EPOCH:
let nextAttestationSlot =
compute_start_slot_at_epoch(bitmapEpoch) + slotOffset
if ((orderedAttestingSlots[i] and (1'u32 shl slotOffset)) != 0) and
nextAttestationSlot > slot:
return nextAttestationSlot
FAR_FUTURE_SLOT
proc onSlotEnd(node: BeaconNode, slot: Slot) {.async.} =
# Things we do when slot processing has ended and we're about to wait for the # Things we do when slot processing has ended and we're about to wait for the
# next slot # next slot
@ -839,13 +893,28 @@ proc onSlotEnd(node: BeaconNode, slot, nextSlot: Slot) {.async.} =
# the database are synced with the filesystem. # the database are synced with the filesystem.
node.db.checkpoint() node.db.checkpoint()
let
horizonSlot = compute_start_slot_at_epoch(
node.attestationSubnets.lastCalculatedAttestationEpoch + 1) - 1
nextAttestationSlot = node.getNextAttestation(slot)
nextActionWait = saturate(fromNow(node.beaconClock, nextAttestationSlot))
horizonDistance = saturate(fromNow(node.beaconClock, horizonSlot))
info "Slot end", info "Slot end",
slot = shortLog(slot), slot = shortLog(slot),
nextSlot = shortLog(nextSlot), nextSlot = shortLog(slot + 1),
head = shortLog(node.chainDag.head), head = shortLog(node.chainDag.head),
headEpoch = shortLog(node.chainDag.head.slot.compute_epoch_at_slot()), headEpoch = shortLog(node.chainDag.head.slot.compute_epoch_at_slot()),
finalizedHead = shortLog(node.chainDag.finalizedHead.blck), finalizedHead = shortLog(node.chainDag.finalizedHead.blck),
finalizedEpoch = shortLog(node.chainDag.finalizedHead.blck.slot.compute_epoch_at_slot()) finalizedEpoch =
shortLog(node.chainDag.finalizedHead.blck.slot.compute_epoch_at_slot()),
nextAttestationSlot,
nextActionWait =
if nextAttestationSlot == FAR_FUTURE_SLOT:
"n/a"
else:
shortLog(nextActionWait),
lookaheadTime = shortLog(horizonDistance)
node.updateGossipStatus(slot) node.updateGossipStatus(slot)
@ -909,7 +978,7 @@ proc onSlotStart(node: BeaconNode, lastSlot, scheduledSlot: Slot) {.async.} =
slot = wallSlot.slot # afterGenesis == true! slot = wallSlot.slot # afterGenesis == true!
nextSlot = slot + 1 nextSlot = slot + 1
defer: await onSlotEnd(node, slot, nextSlot) defer: await onSlotEnd(node, slot)
beacon_slot.set slot.int64 beacon_slot.set slot.int64
beacon_current_epoch.set slot.epoch.int64 beacon_current_epoch.set slot.epoch.int64

View File

@ -572,16 +572,6 @@ type
Table[Epoch, seq[ValidatorIndex]] Table[Epoch, seq[ValidatorIndex]]
beacon_proposer_indices*: Table[Slot, Option[ValidatorIndex]] beacon_proposer_indices*: Table[Slot, Option[ValidatorIndex]]
AttestationSubnets* = object
enabled*: bool
stabilitySubnets*: seq[tuple[subnet: uint8, expiration: Epoch]]
nextCycleEpoch*: Epoch
# These encode states in per-subnet state machines
subscribedSubnets*: set[uint8]
subscribeSlot*: array[ATTESTATION_SUBNET_COUNT, Slot]
unsubscribeSlot*: array[ATTESTATION_SUBNET_COUNT, Slot]
# This matches the mutable state of the Solidity deposit contract # This matches the mutable state of the Solidity deposit contract
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/solidity_deposit_contract/deposit_contract.sol # https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/solidity_deposit_contract/deposit_contract.sol
DepositContractState* = object DepositContractState* = object

View File

@ -169,7 +169,7 @@ proc checkBitVector(sszSubType, dir: string, expectedHash: SSZHashTreeRoot) =
proc checkBitList(sszSubType, dir: string, expectedHash: SSZHashTreeRoot) = proc checkBitList(sszSubType, dir: string, expectedHash: SSZHashTreeRoot) =
var maxLen: int var maxLen: int
let wasMatched = scanf(sszSubType, "bitlist_$i", maxLen) discard scanf(sszSubType, "bitlist_$i", maxLen)
case maxLen case maxLen
of 0: checkBasic(BitList[0], dir, expectedHash) of 0: checkBasic(BitList[0], dir, expectedHash)
of 1: checkBasic(BitList[1], dir, expectedHash) of 1: checkBasic(BitList[1], dir, expectedHash)