extract LC sync task scheduling helper function (#5117)

The helper function to compute delay until next light client sync task
can be useful from more general purpose contexts. Move to helpers, and
change it to return `Duration` instead of `BeaconTime` for flexibility.
This commit is contained in:
Etan Kissling 2023-06-23 08:19:51 +02:00 committed by GitHub
parent 1bc9f3a67a
commit 8a853fc4e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 59 deletions

View File

@ -9,7 +9,6 @@
import chronos, chronicles
import
eth/p2p/discoveryv5/random2,
../spec/network,
../networking/eth2_network,
../beacon_clock,
@ -107,13 +106,9 @@ proc isGossipSupported*(
if not self.isLightClientStoreInitialized():
return false
let
finalizedPeriod = self.getFinalizedPeriod()
isNextSyncCommitteeKnown = self.isNextSyncCommitteeKnown()
if isNextSyncCommitteeKnown:
period <= finalizedPeriod + 1
else:
period <= finalizedPeriod
period.isGossipSupported(
finalizedPeriod = self.getFinalizedPeriod(),
isNextSyncCommitteeKnown = self.isNextSyncCommitteeKnown())
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-alpha.3/specs/altair/light-client/p2p-interface.md#getlightclientbootstrap
proc doRequest(
@ -340,45 +335,13 @@ template query[E](
): Future[bool] =
self.query(e, Nothing())
type SchedulingMode = enum
Now,
Soon,
CurrentPeriod,
NextPeriod
func fetchTime(
self: LightClientManager,
wallTime: BeaconTime,
schedulingMode: SchedulingMode
): BeaconTime =
let
remainingTime =
case schedulingMode:
of Now:
return wallTime
of Soon:
chronos.seconds(0)
of CurrentPeriod:
let
wallPeriod = wallTime.slotOrZero().sync_committee_period
deadlineSlot = (wallPeriod + 1).start_slot - 1
deadline = deadlineSlot.start_beacon_time()
chronos.nanoseconds((deadline - wallTime).nanoseconds)
of NextPeriod:
chronos.seconds(
(SLOTS_PER_SYNC_COMMITTEE_PERIOD * SECONDS_PER_SLOT).int64)
minDelay = max(remainingTime div 8, chronos.seconds(10))
jitterSeconds = (minDelay * 2).seconds
jitterDelay = chronos.seconds(self.rng[].rand(jitterSeconds).int64)
return wallTime + minDelay + jitterDelay
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-alpha.3/specs/altair/light-client/light-client.md#light-client-sync-process
proc loop(self: LightClientManager) {.async.} =
var nextFetchTime = self.getBeaconTime()
var nextSyncTaskTime = self.getBeaconTime()
while true:
# Periodically wake and check for changes
let wallTime = self.getBeaconTime()
if wallTime < nextFetchTime or
if wallTime < nextSyncTaskTime or
self.network.peerPool.lenAvailable < 1:
await sleepAsync(chronos.seconds(2))
continue
@ -391,8 +354,11 @@ proc loop(self: LightClientManager) {.async.} =
continue
let didProgress = await self.query(Bootstrap, trustedBlockRoot.get)
if not didProgress:
nextFetchTime = self.fetchTime(wallTime, Soon)
nextSyncTaskTime =
if didProgress:
wallTime
else:
wallTime + self.rng.computeDelayWithJitter(chronos.seconds(0))
continue
# Fetch updates
@ -400,9 +366,9 @@ proc loop(self: LightClientManager) {.async.} =
current = wallTime.slotOrZero().sync_committee_period
syncTask = nextLightClientSyncTask(
current = current,
finalized = self.getFinalizedPeriod(),
optimistic = self.getOptimisticPeriod(),
current = current,
isNextSyncCommitteeKnown = self.isNextSyncCommitteeKnown())
didProgress =
@ -415,18 +381,12 @@ proc loop(self: LightClientManager) {.async.} =
of LcSyncKind.OptimisticUpdate:
await self.query(OptimisticUpdate)
schedulingMode =
if not self.isGossipSupported(current):
if didProgress:
Now
else:
Soon
elif self.getFinalizedPeriod() != self.getOptimisticPeriod():
CurrentPeriod
else:
NextPeriod
nextFetchTime = self.fetchTime(wallTime, schedulingMode)
nextSyncTaskTime = wallTime + self.rng.nextLcSyncTaskDelay(
wallTime,
finalized = self.getFinalizedPeriod(),
optimistic = self.getOptimisticPeriod(),
isNextSyncCommitteeKnown = self.isNextSyncCommitteeKnown(),
didLatestSyncTaskProgress = didProgress)
proc start*(self: var LightClientManager) =
## Start light client manager's loop.

View File

@ -11,7 +11,9 @@ import
std/typetraits,
chronos,
stew/base10,
../spec/[forks_light_client, network]
eth/p2p/discoveryv5/random2,
../spec/[forks_light_client, network],
../beacon_clock
func checkLightClientUpdates*(
updates: openArray[ForkedLightClientUpdate],
@ -49,6 +51,15 @@ func checkLightClientUpdates*(
return err("Invalid context bytes")
ok()
func isGossipSupported*(
period: SyncCommitteePeriod,
finalizedPeriod: SyncCommitteePeriod,
isNextSyncCommitteeKnown: bool): bool =
if isNextSyncCommitteeKnown:
period <= finalizedPeriod + 1
else:
period <= finalizedPeriod
type
LcSyncKind* {.pure.} = enum
UpdatesByRange
@ -64,9 +75,9 @@ type
discard
func nextLightClientSyncTask*(
current: SyncCommitteePeriod,
finalized: SyncCommitteePeriod,
optimistic: SyncCommitteePeriod,
current: SyncCommitteePeriod,
isNextSyncCommitteeKnown: bool): LcSyncTask =
if finalized == optimistic and not isNextSyncCommitteeKnown:
if finalized >= current:
@ -88,3 +99,41 @@ func nextLightClientSyncTask*(
LcSyncTask(kind: LcSyncKind.FinalityUpdate)
else:
LcSyncTask(kind: LcSyncKind.OptimisticUpdate)
func computeDelayWithJitter*(
rng: ref HmacDrbgContext, duration: Duration): Duration =
let
minDelay = max(duration div 8, chronos.seconds(10))
jitterSeconds = (minDelay * 2).seconds
jitterDelay = chronos.seconds(rng[].rand(jitterSeconds).int64)
minDelay + jitterDelay
func nextLcSyncTaskDelay*(
rng: ref HmacDrbgContext,
wallTime: BeaconTime,
finalized: SyncCommitteePeriod,
optimistic: SyncCommitteePeriod,
isNextSyncCommitteeKnown: bool,
didLatestSyncTaskProgress: bool
): Duration =
let
current = wallTime.slotOrZero().sync_committee_period
remainingDuration =
if not current.isGossipSupported(finalized, isNextSyncCommitteeKnown):
if didLatestSyncTaskProgress:
# Now
return chronos.seconds(0)
# Soon
chronos.seconds(0)
elif finalized != optimistic:
# Current sync committee period
let
wallPeriod = wallTime.slotOrZero().sync_committee_period
deadlineSlot = (wallPeriod + 1).start_slot - 1
deadline = deadlineSlot.start_beacon_time()
chronos.nanoseconds((deadline - wallTime).nanoseconds)
else:
# Next sync committee period
chronos.seconds(
(SLOTS_PER_SYNC_COMMITTEE_PERIOD * SECONDS_PER_SLOT).int64)
rng.computeDelayWithJitter(remainingDuration)