nimbus-eth2/beacon_chain/sync/light_client_sync_helpers.nim

140 lines
4.9 KiB
Nim

# beacon_chain
# Copyright (c) 2022-2024 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: [].}
import
std/typetraits,
chronos,
stew/base10,
eth/p2p/discoveryv5/random2,
../spec/[forks_light_client, network],
../beacon_clock
func checkLightClientUpdates*(
updates: openArray[ForkedLightClientUpdate],
startPeriod: SyncCommitteePeriod,
count: uint64): Result[void, string] =
if updates.lenu64 > count:
return err("Too many values in response" &
" (" & Base10.toString(updates.lenu64) &
" > " & Base10.toString(count.uint) & ")")
let lastPeriod = startPeriod + count - 1
var expectedPeriod = startPeriod
for update in updates:
withForkyUpdate(update):
when lcDataFork > LightClientDataFork.None:
let
attPeriod =
forkyUpdate.attested_header.beacon.slot.sync_committee_period
sigPeriod = forkyUpdate.signature_slot.sync_committee_period
if attPeriod != sigPeriod:
return err("Conflicting sync committee periods" &
" (signature: " & Base10.toString(distinctBase(sigPeriod)) &
" != " & Base10.toString(distinctBase(attPeriod)) & ")")
if attPeriod < expectedPeriod:
return err("Unexpected sync committee period" &
" (" & Base10.toString(distinctBase(attPeriod)) &
" < " & Base10.toString(distinctBase(expectedPeriod)) & ")")
if attPeriod > expectedPeriod:
if attPeriod > lastPeriod:
return err("Sync committee period too high" &
" (" & Base10.toString(distinctBase(attPeriod)) &
" > " & Base10.toString(distinctBase(lastPeriod)) & ")")
expectedPeriod = attPeriod
inc expectedPeriod
else:
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
FinalityUpdate
OptimisticUpdate
LcSyncTask* = object
case kind*: LcSyncKind
of LcSyncKind.UpdatesByRange:
startPeriod*: SyncCommitteePeriod
count*: uint64
of LcSyncKind.FinalityUpdate, LcSyncKind.OptimisticUpdate:
discard
func nextLightClientSyncTask*(
current: SyncCommitteePeriod,
finalized: SyncCommitteePeriod,
optimistic: SyncCommitteePeriod,
isNextSyncCommitteeKnown: bool): LcSyncTask =
if finalized == optimistic and not isNextSyncCommitteeKnown:
if finalized >= current:
LcSyncTask(
kind: LcSyncKind.UpdatesByRange,
startPeriod: finalized,
count: 1)
else:
LcSyncTask(
kind: LcSyncKind.UpdatesByRange,
startPeriod: finalized,
count: min(current - finalized, MAX_REQUEST_LIGHT_CLIENT_UPDATES))
elif finalized + 1 < current:
LcSyncTask(
kind: LcSyncKind.UpdatesByRange,
startPeriod: finalized + 1,
count: min(current - (finalized + 1), MAX_REQUEST_LIGHT_CLIENT_UPDATES))
elif finalized != optimistic:
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)