140 lines
4.9 KiB
Nim
140 lines
4.9 KiB
Nim
# beacon_chain
|
|
# Copyright (c) 2022-2023 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)
|