update light client server for DAG failure modes (#3514)

Gracefully handles the new failure modes recently introduced to the DAG
as part of https://github.com/status-im/nimbus-eth2/pull/3513
Data that is deemed to exist but fails to load leads to an error log to
avoid suppressing logic errors accidentally. In `verifyFinalization`
mode, the assertions remain active.
This commit is contained in:
Etan Kissling 2022-03-20 11:58:59 +01:00 committed by GitHub
parent 04b851f775
commit fd1ffd62dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 265 additions and 156 deletions

View File

@ -79,7 +79,7 @@ proc addResolvedHeadBlock(
epochRefDur = epochRefTick - putBlockTick epochRefDur = epochRefTick - putBlockTick
# Update light client data # Update light client data
dag.processNewBlockForLightClient(state, trustedBlock, parent) dag.processNewBlockForLightClient(state, trustedBlock, parent.bid)
# Notify others of the new block before processing the quarantine, such that # Notify others of the new block before processing the quarantine, such that
# notifications for parents happens before those of the children # notifications for parents happens before those of the children

View File

@ -58,10 +58,67 @@ func computeEarliestLightClientSlot*(dag: ChainDAGRef): Slot =
let earliestSlot = currentSlot - MIN_SLOTS_FOR_BLOCK_REQUESTS let earliestSlot = currentSlot - MIN_SLOTS_FOR_BLOCK_REQUESTS
max(earliestSlot.sync_committee_period.start_slot, minSupportedSlot) max(earliestSlot.sync_committee_period.start_slot, minSupportedSlot)
proc updateExistingState(
dag: ChainDAGRef, state: var ForkedHashedBeaconState, bsi: BlockSlotId,
save: bool, cache: var StateCache): bool =
## Wrapper around `updateState` for states expected to exist.
let ok = dag.updateState(state, bsi, save, cache)
if not ok:
error "State failed to load unexpectedly", bsi, tail = dag.tail.slot
doAssert verifyFinalization notin dag.updateFlags
ok
template withUpdatedExistingState(
dag: ChainDAGRef, state: var ForkedHashedBeaconState,
bsiParam: BlockSlotId, okBody: untyped, failureBody: untyped): untyped =
## Wrapper around `withUpdatedState` for states expected to exist.
block:
let bsi = bsiParam
dag.withUpdatedState(state, bsiParam) do:
okBody
do:
error "State failed to load unexpectedly", bsi, tail = dag.tail.slot
doAssert verifyFinalization notin dag.updateFlags
failureBody
proc getExistingBlockIdAtSlot(dag: ChainDAGRef, slot: Slot): Opt[BlockSlotId] =
## Wrapper around `getBlockIdAtSlot` for blocks expected to exist.
let bsi = dag.getBlockIdAtSlot(slot)
if bsi.isErr:
error "Block failed to load unexpectedly", slot, tail = dag.tail.slot
doAssert verifyFinalization notin dag.updateFlags
bsi
proc existingParent*(dag: ChainDAGRef, bid: BlockId): Opt[BlockId] =
## Wrapper around `parent` for parents known to exist.
let parent = dag.parent(bid)
if parent.isErr:
error "Parent failed to load unexpectedly", bid, tail = dag.tail.slot
doAssert verifyFinalization notin dag.updateFlags
parent
proc getExistingForkedBlock(dag: ChainDAGRef, root: Eth2Digest):
Opt[ForkedTrustedSignedBeaconBlock] =
## Wrapper around `getForkedBlock` for blocks expected to exist.
let bdata = dag.getForkedBlock(root)
if bdata.isErr:
error "Block failed to load unexpectedly", root, tail = dag.tail.slot
doAssert verifyFinalization notin dag.updateFlags
bdata
proc getExistingForkedBlock*(
dag: ChainDAGRef, bid: BlockId): Opt[ForkedTrustedSignedBeaconBlock] =
## Wrapper around `getForkedBlock` for blocks expected to exist.
let bdata = dag.getForkedBlock(bid)
if bdata.isErr:
error "Block failed to load unexpectedly", bid, tail = dag.tail.slot
doAssert verifyFinalization notin dag.updateFlags
bdata
proc currentSyncCommitteeForPeriod( proc currentSyncCommitteeForPeriod(
dag: ChainDAGRef, dag: ChainDAGRef,
tmpState: var ForkedHashedBeaconState, tmpState: var ForkedHashedBeaconState,
period: SyncCommitteePeriod): SyncCommittee = period: SyncCommitteePeriod): Opt[SyncCommittee] =
## Fetch a `SyncCommittee` for a given sync committee period. ## Fetch a `SyncCommittee` for a given sync committee period.
## For non-finalized periods, follow the chain as selected by fork choice. ## For non-finalized periods, follow the chain as selected by fork choice.
let earliestSlot = dag.computeEarliestLightClientSlot let earliestSlot = dag.computeEarliestLightClientSlot
@ -69,15 +126,13 @@ proc currentSyncCommitteeForPeriod(
let let
periodStartSlot = period.start_slot periodStartSlot = period.start_slot
syncCommitteeSlot = max(periodStartSlot, earliestSlot) syncCommitteeSlot = max(periodStartSlot, earliestSlot)
# TODO introduce error handling in the case that we don't have historical bsi = ? dag.getExistingBlockIdAtSlot(syncCommitteeSlot)
# data for the period dag.withUpdatedExistingState(tmpState, bsi) do:
bsi = dag.getBlockIdAtSlot(syncCommitteeSlot).expect("TODO")
dag.withUpdatedState(tmpState, bsi) do:
withState(state): withState(state):
when stateFork >= BeaconStateFork.Altair: when stateFork >= BeaconStateFork.Altair:
state.data.current_sync_committee ok state.data.current_sync_committee
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
do: raiseAssert "Unreachable" do: err()
template syncCommitteeRoot( template syncCommitteeRoot(
state: HashedBeaconStateWithSyncCommittee): Eth2Digest = state: HashedBeaconStateWithSyncCommittee): Eth2Digest =
@ -90,7 +145,7 @@ template syncCommitteeRoot(
proc syncCommitteeRootForPeriod( proc syncCommitteeRootForPeriod(
dag: ChainDAGRef, dag: ChainDAGRef,
tmpState: var ForkedHashedBeaconState, tmpState: var ForkedHashedBeaconState,
period: SyncCommitteePeriod): Eth2Digest = period: SyncCommitteePeriod): Opt[Eth2Digest] =
## Compute a root to uniquely identify `current_sync_committee` and ## Compute a root to uniquely identify `current_sync_committee` and
## `next_sync_committee` for a given sync committee period. ## `next_sync_committee` for a given sync committee period.
## For non-finalized periods, follow the chain as selected by fork choice. ## For non-finalized periods, follow the chain as selected by fork choice.
@ -99,13 +154,13 @@ proc syncCommitteeRootForPeriod(
let let
periodStartSlot = period.start_slot periodStartSlot = period.start_slot
syncCommitteeSlot = max(periodStartSlot, earliestSlot) syncCommitteeSlot = max(periodStartSlot, earliestSlot)
bsi = dag.getBlockIdAtSlot(syncCommitteeSlot).expect("TODO") bsi = ? dag.getExistingBlockIdAtSlot(syncCommitteeSlot)
dag.withUpdatedState(tmpState, bsi) do: dag.withUpdatedExistingState(tmpState, bsi) do:
withState(state): withState(state):
when stateFork >= BeaconStateFork.Altair: when stateFork >= BeaconStateFork.Altair:
state.syncCommitteeRoot ok state.syncCommitteeRoot
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
do: raiseAssert "Unreachable" do: err()
proc getLightClientData( proc getLightClientData(
dag: ChainDAGRef, dag: ChainDAGRef,
@ -115,16 +170,10 @@ proc getLightClientData(
try: dag.lightClientCache.data[bid] try: dag.lightClientCache.data[bid]
except KeyError: raiseAssert "Unreachable" except KeyError: raiseAssert "Unreachable"
template getLightClientData(
dag: ChainDAGRef,
blck: BlockRef): CachedLightClientData =
getLightClientData(dag, blck.bid)
proc cacheLightClientData*( proc cacheLightClientData*(
dag: ChainDAGRef, dag: ChainDAGRef,
state: HashedBeaconStateWithSyncCommittee, state: HashedBeaconStateWithSyncCommittee,
blck: TrustedSignedBeaconBlockWithSyncAggregate, blck: TrustedSignedBeaconBlockWithSyncAggregate) =
isNew = true) =
## Cache data for a given block and its post-state to speed up creating future ## Cache data for a given block and its post-state to speed up creating future
## `LightClientUpdate` and `LightClientBootstrap` instances that refer to this ## `LightClientUpdate` and `LightClientBootstrap` instances that refer to this
## block and state. ## block and state.
@ -143,16 +192,19 @@ proc cacheLightClientData*(
state.data.build_proof( state.data.build_proof(
altair.FINALIZED_ROOT_INDEX, finality_branch) altair.FINALIZED_ROOT_INDEX, finality_branch)
template finalized_checkpoint(): auto = state.data.finalized_checkpoint
let let
earliestSlot = dag.computeEarliestLightClientSlot
bid = bid =
BlockId(root: blck.root, slot: blck.message.slot) BlockId(root: blck.root, slot: blck.message.slot)
finalized_bid = earliest_slot =
if finalized_checkpoint.epoch.start_slot >= earliestSlot: dag.computeEarliestLightClientSlot
dag.getBlockIdAtSlot(finalized_checkpoint.epoch.start_slot).expect("TODO").bid finalized_slot =
state.data.finalized_checkpoint.epoch.start_slot
finalized_bsi =
if finalized_slot >= earliest_slot:
dag.getExistingBlockIdAtSlot(finalized_slot).valueOr:
default(BlockSlotId) # Ignored in `createLightClientUpdates`
else: else:
default(BlockId) default(BlockSlotId) # Ignored in `createLightClientUpdates`
if dag.lightClientCache.data.hasKeyOrPut( if dag.lightClientCache.data.hasKeyOrPut(
bid, bid,
CachedLightClientData( CachedLightClientData(
@ -161,7 +213,7 @@ proc cacheLightClientData*(
next_sync_committee_branch: next_sync_committee_branch:
next_sync_committee_branch, next_sync_committee_branch,
finalized_bid: finalized_bid:
finalized_bid, finalized_bsi.bid,
finality_branch: finality_branch:
finality_branch)): finality_branch)):
doAssert false, "Redundant `cacheLightClientData` call" doAssert false, "Redundant `cacheLightClientData` call"
@ -178,23 +230,20 @@ template lazy_header(name: untyped): untyped {.dirty.} =
## `createLightClientUpdates` helper to lazily load a known block header. ## `createLightClientUpdates` helper to lazily load a known block header.
var `name ptr`: ptr[BeaconBlockHeader] var `name ptr`: ptr[BeaconBlockHeader]
template `assign name`(target: var BeaconBlockHeader, template `assign name`(target: var BeaconBlockHeader,
blockParam: BlockId | BlockRef): untyped = bid: BlockId): untyped =
if `name ptr` != nil: if `name ptr` != nil:
target = `name ptr`[] target = `name ptr`[]
else: else:
let bid = let bdata = dag.getExistingForkedBlock(bid).valueOr:
when blockParam is BlockId: return
blockParam target = toBeaconBlockHeader(bdata)
else:
blockParam.bid
target = toBeaconBlockHeader(dag.getForkedBlock(bid).get)
`name ptr` = addr target `name ptr` = addr target
template lazy_data(name: untyped): untyped {.dirty.} = template lazy_data(name: untyped): untyped {.dirty.} =
## `createLightClientUpdates` helper to lazily load cached light client state. ## `createLightClientUpdates` helper to lazily load cached light client state.
var `name` {.noinit.}: CachedLightClientData var `name` {.noinit.}: CachedLightClientData
`name`.finalized_bid.slot = FAR_FUTURE_SLOT `name`.finalized_bid.slot = FAR_FUTURE_SLOT
template `load name`(bid: BlockId | BlockRef): untyped = template `load name`(bid: BlockId): untyped =
if `name`.finalized_bid.slot == FAR_FUTURE_SLOT: if `name`.finalized_bid.slot == FAR_FUTURE_SLOT:
`name` = dag.getLightClientData(bid) `name` = dag.getLightClientData(bid)
@ -202,7 +251,7 @@ proc createLightClientUpdates(
dag: ChainDAGRef, dag: ChainDAGRef,
state: HashedBeaconStateWithSyncCommittee, state: HashedBeaconStateWithSyncCommittee,
blck: TrustedSignedBeaconBlockWithSyncAggregate, blck: TrustedSignedBeaconBlockWithSyncAggregate,
parent: BlockId) = parent_bid: BlockId) =
## Create `LightClientUpdate` and `OptimisticLightClientUpdate` instances for ## Create `LightClientUpdate` and `OptimisticLightClientUpdate` instances for
## a given block and its post-state, and keep track of best / latest ones. ## a given block and its post-state, and keep track of best / latest ones.
## Data about the parent block's post-state and its `finalized_checkpoint`'s ## Data about the parent block's post-state and its `finalized_checkpoint`'s
@ -219,7 +268,7 @@ proc createLightClientUpdates(
# Verify attested block (parent) is recent enough and that state is available # Verify attested block (parent) is recent enough and that state is available
let let
earliest_slot = dag.computeEarliestLightClientSlot earliest_slot = dag.computeEarliestLightClientSlot
attested_slot = parent.slot attested_slot = parent_bid.slot
if attested_slot < earliest_slot: if attested_slot < earliest_slot:
return return
@ -236,7 +285,7 @@ proc createLightClientUpdates(
template optimistic_update(): auto = dag.lightClientCache.optimisticUpdate template optimistic_update(): auto = dag.lightClientCache.optimisticUpdate
if attested_slot > optimistic_update.attested_header.slot: if attested_slot > optimistic_update.attested_header.slot:
optimistic_update.attested_header optimistic_update.attested_header
.assign_attested_header(parent) .assign_attested_header(parent_bid)
optimistic_update.sync_aggregate = optimistic_update.sync_aggregate =
isomorphicCast[SyncAggregate](sync_aggregate) isomorphicCast[SyncAggregate](sync_aggregate)
optimistic_update.fork_version = optimistic_update.fork_version =
@ -253,16 +302,17 @@ proc createLightClientUpdates(
template latest_update(): auto = dag.lightClientCache.latestUpdate template latest_update(): auto = dag.lightClientCache.latestUpdate
if attested_slot > latest_update.attested_header.slot: if attested_slot > latest_update.attested_header.slot:
latest_update.attested_header latest_update.attested_header
.assign_attested_header(parent) .assign_attested_header(parent_bid)
latest_update.sync_aggregate = latest_update.sync_aggregate =
isomorphicCast[SyncAggregate](sync_aggregate) isomorphicCast[SyncAggregate](sync_aggregate)
latest_update.fork_version = latest_update.fork_version =
state.data.fork.current_version state.data.fork.current_version
load_attested_data(parent) load_attested_data(parent_bid)
let finalized_slot = attested_data.finalized_bid.slot let finalized_slot = attested_data.finalized_bid.slot
if finalized_slot + UPDATE_TIMEOUT < attested_slot or if finalized_slot + UPDATE_TIMEOUT < attested_slot or
finalized_slot < earliest_slot: finalized_slot < earliest_slot or
attested_data.finalized_bid.root.isZero:
latest_update.finalized_header = BeaconBlockHeader() latest_update.finalized_header = BeaconBlockHeader()
latest_update.finality_branch.fill(Eth2Digest()) latest_update.finality_branch.fill(Eth2Digest())
if signature_period == attested_period + 1: if signature_period == attested_period + 1:
@ -321,7 +371,7 @@ proc createLightClientUpdates(
if (attested_slot > dag.finalizedHead.slot or if (attested_slot > dag.finalizedHead.slot or
attested_slot > best_attested_slot) and attested_slot > best_attested_slot) and
signature_period == finalized_period_at_signature: signature_period == finalized_period_at_signature:
load_attested_data(parent) load_attested_data(parent_bid)
let finalized_slot = attested_data.finalized_bid.slot let finalized_slot = attested_data.finalized_bid.slot
if signature_period == finalized_slot.sync_committee_period and if signature_period == finalized_slot.sync_committee_period and
finalized_slot >= earliest_slot: finalized_slot >= earliest_slot:
@ -329,7 +379,7 @@ proc createLightClientUpdates(
elif attested_slot > dag.finalizedHead.slot or elif attested_slot > dag.finalizedHead.slot or
attested_slot < best_attested_slot: attested_slot < best_attested_slot:
if signature_period == finalized_period_at_signature: if signature_period == finalized_period_at_signature:
load_attested_data(parent) load_attested_data(parent_bid)
let finalized_slot = attested_data.finalized_bid.slot let finalized_slot = attested_data.finalized_bid.slot
if signature_period != finalized_slot.sync_committee_period or if signature_period != finalized_slot.sync_committee_period or
finalized_slot < earliest_slot: finalized_slot < earliest_slot:
@ -351,13 +401,13 @@ proc createLightClientUpdates(
if verdict == new_update_is_better: if verdict == new_update_is_better:
best_update.attested_header best_update.attested_header
.assign_attested_header(parent) .assign_attested_header(parent_bid)
best_update.sync_aggregate = best_update.sync_aggregate =
isomorphicCast[SyncAggregate](sync_aggregate) isomorphicCast[SyncAggregate](sync_aggregate)
best_update.fork_version = best_update.fork_version =
state.data.fork.current_version state.data.fork.current_version
load_attested_data(parent) load_attested_data(parent_bid)
let finalized_slot = attested_data.finalized_bid.slot let finalized_slot = attested_data.finalized_bid.slot
if signature_period != finalized_slot.sync_committee_period or if signature_period != finalized_slot.sync_committee_period or
finalized_slot < earliest_slot: finalized_slot < earliest_slot:
@ -392,7 +442,7 @@ proc processNewBlockForLightClient*(
dag: ChainDAGRef, dag: ChainDAGRef,
state: ForkedHashedBeaconState, state: ForkedHashedBeaconState,
signedBlock: ForkyTrustedSignedBeaconBlock, signedBlock: ForkyTrustedSignedBeaconBlock,
parent: BlockRef) = parentBid: BlockId) =
## Update light client data with information from a new block. ## Update light client data with information from a new block.
if dag.importLightClientData == ImportLightClientData.None: if dag.importLightClientData == ImportLightClientData.None:
return return
@ -401,12 +451,12 @@ proc processNewBlockForLightClient*(
when signedBlock is bellatrix.TrustedSignedBeaconBlock: when signedBlock is bellatrix.TrustedSignedBeaconBlock:
dag.cacheLightClientData(state.bellatrixData, signedBlock) dag.cacheLightClientData(state.bellatrixData, signedBlock)
dag.createLightClientUpdates(state.bellatrixData, signedBlock, parent.bid) dag.createLightClientUpdates(state.bellatrixData, signedBlock, parentBid)
elif signedBlock is altair.TrustedSignedBeaconBlock: elif signedBlock is altair.TrustedSignedBeaconBlock:
dag.cacheLightClientData(state.altairData, signedBlock) dag.cacheLightClientData(state.altairData, signedBlock)
dag.createLightClientUpdates(state.altairData, signedBlock, parent.bid) dag.createLightClientUpdates(state.altairData, signedBlock, parentBid)
elif signedBlock is phase0.TrustedSignedBeaconBlock: elif signedBlock is phase0.TrustedSignedBeaconBlock:
discard raiseAssert "Unreachable" # These cannot occur after `earliestSlot`
else: else:
{.error: "Unreachable".} {.error: "Unreachable".}
@ -424,7 +474,11 @@ proc processHeadChangeForLightClient*(dag: ChainDAGRef) =
if headPeriod > finalizedPeriod + 1: if headPeriod > finalizedPeriod + 1:
var tmpState = assignClone(dag.headState) var tmpState = assignClone(dag.headState)
for period in finalizedPeriod + 1 ..< headPeriod: for period in finalizedPeriod + 1 ..< headPeriod:
let key = (period, dag.syncCommitteeRootForPeriod(tmpState[], period)) let
syncCommitteeRoot =
dag.syncCommitteeRootForPeriod(tmpState[], period).valueOr:
continue
key = (period, syncCommitteeRoot)
dag.lightClientCache.bestUpdates[period] = dag.lightClientCache.bestUpdates[period] =
dag.lightClientCache.pendingBestUpdates.getOrDefault(key) dag.lightClientCache.pendingBestUpdates.getOrDefault(key)
withState(dag.headState): withState(dag.headState):
@ -441,11 +495,10 @@ proc processFinalizationForLightClient*(dag: ChainDAGRef) =
if dag.importLightClientData == ImportLightClientData.None: if dag.importLightClientData == ImportLightClientData.None:
return return
let let
altairStartSlot = dag.cfg.ALTAIR_FORK_EPOCH.start_slot earliestSlot = dag.computeEarliestLightClientSlot
finalizedSlot = dag.finalizedHead.slot finalizedSlot = dag.finalizedHead.slot
if finalizedSlot < altairStartSlot: if finalizedSlot < earliestSlot:
return return
let earliestSlot = dag.computeEarliestLightClientSlot
# Keep track of latest four finalized checkpoints # Keep track of latest four finalized checkpoints
let let
@ -463,10 +516,13 @@ proc processFinalizationForLightClient*(dag: ChainDAGRef) =
# Cache `LightClientBootstrap` for newly finalized epoch boundary blocks. # Cache `LightClientBootstrap` for newly finalized epoch boundary blocks.
# Epoch boundary blocks are the block for the initial slot of an epoch, # Epoch boundary blocks are the block for the initial slot of an epoch,
# or the most recent block if no block was proposed at that slot # or the most recent block if no block was proposed at that slot
let lowSlot = max(lastCheckpoint.epoch.start_slot, earliestSlot) let lowSlot = max(lastCheckpoint[].epoch.start_slot, earliestSlot)
var boundarySlot = dag.finalizedHead.slot var boundarySlot = dag.finalizedHead.slot
while boundarySlot >= lowSlot: while boundarySlot >= lowSlot:
let bid = dag.getBlockIdAtSlot(boundarySlot).expect("historical data").bid let
bsi = dag.getExistingBlockIdAtSlot(boundarySlot).valueOr:
break
bid = bsi.bid
if bid.slot >= lowSlot: if bid.slot >= lowSlot:
dag.lightClientCache.bootstrap[bid.slot] = dag.lightClientCache.bootstrap[bid.slot] =
CachedLightClientBootstrap( CachedLightClientBootstrap(
@ -533,29 +589,32 @@ proc initBestLightClientUpdateForPeriod(
return return
let startTick = Moment.now() let startTick = Moment.now()
debug "Computing best update for period", period debug "Computing best update for period", period
defer: proc logBestUpdate(endTick = Moment.now()) =
let endTick = Moment.now() # Using a helper function reduces code size as the `defer` beneath is
# replicated on every `return`, and the log statement allocates another
# copy of the arguments on the stack for each instantiation (~1 MB stack!)
debug "Best update for period computed", debug "Best update for period computed",
period, update = dag.lightClientCache.bestUpdates.getOrDefault(period), period, update = dag.lightClientCache.bestUpdates.getOrDefault(period),
computeDur = endTick - startTick computeDur = endTick - startTick
defer: logBestUpdate()
proc maxParticipantsBlock( proc maxParticipantsBlock(
dag: ChainDAGRef, highBlck: BlockId, lowSlot: Slot): Opt[BlockId] = dag: ChainDAGRef, highBid: BlockId, lowSlot: Slot): Option[BlockId] =
## Determine the earliest block with most sync committee signatures among ## Determine the earliest block with most sync committee signatures among
## ancestors of `highBlck` with at least `lowSlot` as parent block slot. ## ancestors of `highBid` with at least `lowSlot` as parent block slot.
## Return `nil` if no block with `MIN_SYNC_COMMITTEE_PARTICIPANTS` is found. ## Return `none` if no block with `MIN_SYNC_COMMITTEE_PARTICIPANTS` exists.
var var
maxParticipants = 0 maxParticipants = 0
maxBlockRef: Opt[BlockId] maxBid: Option[BlockId]
blockRef = highBlck bid = highBid
while true: while true:
let parent = dag.parent(blockRef).valueOr: let parentBid = dag.parent(bid).valueOr:
break break
if parent.slot < lowSlot: if parentBid.slot < lowSlot:
break break
let let
bdata = dag.getForkedBlock(blockRef).get bdata = dag.getExistingForkedBlock(bid).valueOr:
break
numParticipants = numParticipants =
withBlck(bdata): withBlck(bdata):
when stateFork >= BeaconStateFork.Altair: when stateFork >= BeaconStateFork.Altair:
@ -563,19 +622,21 @@ proc initBestLightClientUpdateForPeriod(
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
if numParticipants >= maxParticipants: if numParticipants >= maxParticipants:
maxParticipants = numParticipants maxParticipants = numParticipants
maxBlockRef = ok blockRef maxBid = some bid
blockRef = parent bid = parentBid
if maxParticipants < MIN_SYNC_COMMITTEE_PARTICIPANTS: if maxParticipants < MIN_SYNC_COMMITTEE_PARTICIPANTS:
maxBlockRef.reset() maxBid.reset()
maxBlockRef maxBid
# Determine the block in the period with highest sync committee participation # Determine the block in the period with highest sync committee participation
let let
lowSlot = max(periodStartSlot, earliestSlot) lowSlot = max(periodStartSlot, earliestSlot)
highSlot = min(periodEndSlot, dag.finalizedHead.blck.slot) highSlot = min(periodEndSlot, dag.finalizedHead.blck.slot)
highBlck = dag.getBlockIdAtSlot(highSlot).expect("TODO").bid highBsi = dag.getExistingBlockIdAtSlot(highSlot).valueOr:
bestNonFinalizedRef = dag.maxParticipantsBlock(highBlck, lowSlot) return
if bestNonFinalizedRef.isNone: highBid = highBsi.bid
bestNonFinalizedBid = dag.maxParticipantsBlock(highBid, lowSlot)
if bestNonFinalizedBid.isNone:
dag.lightClientCache.bestUpdates[period] = default(altair.LightClientUpdate) dag.lightClientCache.bestUpdates[period] = default(altair.LightClientUpdate)
return return
@ -585,33 +646,44 @@ proc initBestLightClientUpdateForPeriod(
# period, despite it having a lower sync committee participation # period, despite it having a lower sync committee participation
var var
tmpState = assignClone(dag.headState) tmpState = assignClone(dag.headState)
bestFinalizedRef = bestNonFinalizedRef bestFinalizedBid = bestNonFinalizedBid
finalizedBlck: Opt[BlockId] finalizedBid {.noinit.}: BlockId
while bestFinalizedRef.isSome: bestFinalizedHasCheckpointInPeriod = false
while bestFinalizedBid.isSome:
defer:
if not bestFinalizedHasCheckpointInPeriod:
let newLowSlot = bestFinalizedBid.get.slot + 1
bestFinalizedBid = dag.maxParticipantsBlock(highBid, newLowSlot)
let let
attestedBid = dag.existingParent(bestFinalizedBid.get).valueOr:
continue
finalizedEpoch = block: finalizedEpoch = block:
dag.withUpdatedState(tmpState[], dag.parent(bestFinalizedRef.get()).expect("TODO").atSlot) do: dag.withUpdatedExistingState(tmpState[], attestedBid.atSlot) do:
withState(state): withState(state):
when stateFork >= BeaconStateFork.Altair: when stateFork >= BeaconStateFork.Altair:
state.data.finalized_checkpoint.epoch state.data.finalized_checkpoint.epoch
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
do: raiseAssert "Unreachable" do: continue
finalizedEpochStartSlot = finalizedEpoch.start_slot finalizedSlot = finalizedEpoch.start_slot
if finalizedEpochStartSlot >= lowSlot: if finalizedSlot >= lowSlot:
finalizedBlck = Opt[BlockId].ok(dag.getBlockIdAtSlot(finalizedEpochStartSlot).expect( let finalizedBsi = dag.getExistingBlockIdAtSlot(finalizedSlot).valueOr:
"TODO").bid) continue
if finalizedBlck.get.slot >= lowSlot: finalizedBid = finalizedBsi.bid
if finalizedBid.slot >= lowSlot:
bestFinalizedHasCheckpointInPeriod = true
break break
bestFinalizedRef = dag.maxParticipantsBlock(highBlck, bestFinalizedRef.get().slot + 1)
# If a finalized block has been found within the sync commitee period, # If a finalized block has been found within the sync commitee period,
# create a `LightClientUpdate` for that one. Otherwise, create a non-finalized # create a `LightClientUpdate` for that one. Otherwise, create a non-finalized
# `LightClientUpdate` # `LightClientUpdate`
var update {.noinit.}: LightClientUpdate var update {.noinit.}: LightClientUpdate
if bestFinalizedRef.isSome: if bestFinalizedBid.isSome:
# Fill data from attested block # Fill data from attested block
dag.withUpdatedState(tmpState[], dag.parent(bestFinalizedRef.get()).expect("TODO").atSlot) do: let attestedBid = dag.existingParent(bestFinalizedBid.get).valueOr:
let bdata = dag.getForkedBlock(bid).get return
dag.withUpdatedExistingState(tmpState[], attestedBid.atSlot) do:
let bdata = dag.getExistingForkedBlock(bid).valueOr:
return
withStateAndBlck(state, bdata): withStateAndBlck(state, bdata):
when stateFork >= BeaconStateFork.Altair: when stateFork >= BeaconStateFork.Altair:
update.attested_header = update.attested_header =
@ -619,21 +691,23 @@ proc initBestLightClientUpdateForPeriod(
state.data.build_proof( state.data.build_proof(
altair.FINALIZED_ROOT_INDEX, update.finality_branch) altair.FINALIZED_ROOT_INDEX, update.finality_branch)
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
do: raiseAssert "Unreachable" do: return
# Fill data from signature block # Fill data from signature block
let bdata = dag.getForkedBlock(bestFinalizedRef.get()).get let bdata = dag.getExistingForkedBlock(bestFinalizedBid.get).valueOr:
return
withBlck(bdata): withBlck(bdata):
when stateFork >= BeaconStateFork.Altair: when stateFork >= BeaconStateFork.Altair:
update.sync_aggregate = update.sync_aggregate =
isomorphicCast[SyncAggregate](blck.message.body.sync_aggregate) isomorphicCast[SyncAggregate](blck.message.body.sync_aggregate)
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
update.fork_version = update.fork_version =
dag.cfg.forkAtEpoch(bestFinalizedRef.get().slot.epoch).current_version dag.cfg.forkAtEpoch(bestFinalizedBid.get.slot.epoch).current_version
# Fill data from finalized block # Fill data from finalized block
dag.withUpdatedState(tmpState[], finalizedBlck.get().atSlot) do: dag.withUpdatedExistingState(tmpState[], finalizedBid.atSlot) do:
let bdata = dag.getForkedBlock(bid).get let bdata = dag.getExistingForkedBlock(bid).valueOr:
return
withStateAndBlck(state, bdata): withStateAndBlck(state, bdata):
when stateFork >= BeaconStateFork.Altair: when stateFork >= BeaconStateFork.Altair:
update.next_sync_committee = update.next_sync_committee =
@ -643,11 +717,14 @@ proc initBestLightClientUpdateForPeriod(
update.finalized_header = update.finalized_header =
blck.toBeaconBlockHeader blck.toBeaconBlockHeader
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
do: raiseAssert "Unreachable" do: return
else: else:
# Fill data from attested block # Fill data from attested block
dag.withUpdatedState(tmpState[], dag.parent(bestNonFinalizedRef.get()).expect("TODO").atSlot) do: let attestedBid = dag.existingParent(bestNonFinalizedBid.get).valueOr:
let bdata = dag.getForkedBlock(bid).get return
dag.withUpdatedExistingState(tmpState[], attestedBid.atSlot) do:
let bdata = dag.getExistingForkedBlock(bid).valueOr:
return
withStateAndBlck(state, bdata): withStateAndBlck(state, bdata):
when stateFork >= BeaconStateFork.Altair: when stateFork >= BeaconStateFork.Altair:
update.attested_header = update.attested_header =
@ -662,14 +739,15 @@ proc initBestLightClientUpdateForPeriod(
do: raiseAssert "Unreachable" do: raiseAssert "Unreachable"
# Fill data from signature block # Fill data from signature block
let bdata = dag.getForkedBlock(bestNonFinalizedRef.get()).get let bdata = dag.getExistingForkedBlock(bestNonFinalizedBid.get).valueOr:
return
withBlck(bdata): withBlck(bdata):
when stateFork >= BeaconStateFork.Altair: when stateFork >= BeaconStateFork.Altair:
update.sync_aggregate = update.sync_aggregate =
isomorphicCast[SyncAggregate](blck.message.body.sync_aggregate) isomorphicCast[SyncAggregate](blck.message.body.sync_aggregate)
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
update.fork_version = update.fork_version =
dag.cfg.forkAtEpoch(bestNonFinalizedRef.get.slot.epoch).current_version dag.cfg.forkAtEpoch(bestNonFinalizedBid.get.slot.epoch).current_version
dag.lightClientCache.bestUpdates[period] = update dag.lightClientCache.bestUpdates[period] = update
proc initLightClientBootstrapForPeriod( proc initLightClientBootstrapForPeriod(
@ -703,15 +781,19 @@ proc initLightClientBootstrapForPeriod(
tmpCache: StateCache tmpCache: StateCache
nextBoundarySlot = lowBoundarySlot nextBoundarySlot = lowBoundarySlot
while nextBoundarySlot <= highBoundarySlot: while nextBoundarySlot <= highBoundarySlot:
defer: nextBoundarySlot += SLOTS_PER_EPOCH
let let
blck = dag.getBlockIdAtSlot(nextBoundarySlot).expect("TODO").bid bsi = dag.getExistingBlockIdAtSlot(nextBoundarySlot).valueOr:
boundarySlot = blck.slot.nextEpochBoundarySlot continue
bid = bsi.bid
boundarySlot = bid.slot.nextEpochBoundarySlot
if boundarySlot == nextBoundarySlot and if boundarySlot == nextBoundarySlot and
blck.slot >= lowSlot and blck.slot <= highSlot and bid.slot >= lowSlot and bid.slot <= highSlot and
not dag.lightClientCache.bootstrap.hasKey(blck.slot): not dag.lightClientCache.bootstrap.hasKey(bid.slot):
var cachedBootstrap {.noinit.}: CachedLightClientBootstrap var cachedBootstrap {.noinit.}: CachedLightClientBootstrap
doAssert dag.updateState( if not dag.updateExistingState(
tmpState[], blck.atSlot, save = false, tmpCache) tmpState[], bid.atSlot, save = false, tmpCache):
continue
withStateVars(tmpState[]): withStateVars(tmpState[]):
withState(state): withState(state):
when stateFork >= BeaconStateFork.Altair: when stateFork >= BeaconStateFork.Altair:
@ -719,18 +801,16 @@ proc initLightClientBootstrapForPeriod(
altair.CURRENT_SYNC_COMMITTEE_INDEX, altair.CURRENT_SYNC_COMMITTEE_INDEX,
cachedBootstrap.current_sync_committee_branch) cachedBootstrap.current_sync_committee_branch)
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
dag.lightClientCache.bootstrap[blck.slot] = cachedBootstrap dag.lightClientCache.bootstrap[bid.slot] = cachedBootstrap
nextBoundarySlot += SLOTS_PER_EPOCH
proc initLightClientCache*(dag: ChainDAGRef) = proc initLightClientCache*(dag: ChainDAGRef) =
## Initialize cached light client data ## Initialize cached light client data
if dag.importLightClientData == ImportLightClientData.None: if dag.importLightClientData == ImportLightClientData.None:
return return
if dag.importLightClientData == ImportLightClientData.OnlyNew:
dag.lightClientCache.importTailSlot = dag.head.slot + 1
return
dag.lightClientCache.importTailSlot = dag.tail.slot dag.lightClientCache.importTailSlot = dag.tail.slot
let earliestSlot = dag.computeEarliestLightClientSlot if dag.importLightClientData == ImportLightClientData.OnlyNew:
dag.lightClientCache.importTailSlot = dag.head.slot
var earliestSlot = dag.computeEarliestLightClientSlot
if dag.head.slot < earliestSlot: if dag.head.slot < earliestSlot:
return return
@ -742,17 +822,32 @@ proc initLightClientCache*(dag: ChainDAGRef) =
let lightClientStartTick = Moment.now() let lightClientStartTick = Moment.now()
debug "Initializing cached light client data" debug "Initializing cached light client data"
template handleUnexpectedError(buggedBid: BlockId) =
# Light client data is expected to be available from `earliestSlot` onward.
# If there is an error, adjust `importTailSlot` to avoid failed lookups of
# cached light client data. For new blocks / states, the caches can always
# be updated incrementally, because those blocks / states are passed in
# directly. It is only historical blocks (or sync committees) that depend
# on a potentially corrupted database.
doAssert buggedBid.slot > dag.lightClientCache.importTailSlot
dag.lightClientCache.importTailSlot = buggedBid.slot + 1
earliestSlot = dag.computeEarliestLightClientSlot
# Build lists of block to process. # Build lists of block to process.
# As it is slow to load states in descending order, # As it is slow to load states in descending order,
# first build a todo list, then process them in ascending order # first build a todo list, then process them in ascending order
let lowSlot = max(finalizedSlot, dag.computeEarliestLightClientSlot) let lowSlot = max(finalizedSlot, earliestSlot)
var var
blocksBetween = newSeqOfCap[BlockId](dag.head.slot - lowSlot + 1) blocksBetween = newSeqOfCap[BlockId](dag.head.slot - lowSlot + 1)
blockRef = dag.head.bid bid = dag.head.bid
while blockRef.slot > lowSlot: while bid.slot > lowSlot:
blocksBetween.add blockRef blocksBetween.add bid
blockRef = dag.parent(blockRef).expect("TODO") bid = dag.existingParent(bid).valueOr:
blocksBetween.add blockRef handleUnexpectedError(bid)
break
if bid.slot >= earliestSlot:
# Add the finalized head itself (`== lowSlot`, or the last block before it)
blocksBetween.add bid
# Process blocks (reuses `dag.headState`, but restores it to the current head) # Process blocks (reuses `dag.headState`, but restores it to the current head)
var var
@ -761,15 +856,19 @@ proc initLightClientCache*(dag: ChainDAGRef) =
oldCheckpoint: Checkpoint oldCheckpoint: Checkpoint
cpIndex = 0 cpIndex = 0
for i in countdown(blocksBetween.high, blocksBetween.low): for i in countdown(blocksBetween.high, blocksBetween.low):
blockRef = blocksBetween[i] bid = blocksBetween[i]
doAssert dag.updateState( if not dag.updateExistingState(
dag.headState, blockRef.atSlot(), save = false, cache) dag.headState, bid.atSlot, save = false, cache):
handleUnexpectedError(bid)
continue
withStateVars(dag.headState): withStateVars(dag.headState):
let bdata = dag.getForkedBlock(blockRef).get let bdata = dag.getExistingForkedBlock(bid).valueOr:
handleUnexpectedError(bid)
continue
withStateAndBlck(state, bdata): withStateAndBlck(state, bdata):
when stateFork >= BeaconStateFork.Altair: when stateFork >= BeaconStateFork.Altair:
# Cache data for `LightClientUpdate` of descendant blocks # Cache data for `LightClientUpdate` of descendant blocks
dag.cacheLightClientData(state, blck, isNew = false) dag.cacheLightClientData(state, blck)
# Cache data for the block's `finalized_checkpoint`. # Cache data for the block's `finalized_checkpoint`.
# The `finalized_checkpoint` may refer to: # The `finalized_checkpoint` may refer to:
@ -792,24 +891,35 @@ proc initLightClientCache*(dag: ChainDAGRef) =
# Note that light client finality proofs refer to checkpoint blocks # Note that light client finality proofs refer to checkpoint blocks
# at their original slot, without advancing to an epoch boundary. # at their original slot, without advancing to an epoch boundary.
# This is because light clients are unable to advance slots. # This is because light clients are unable to advance slots.
if checkpoint.root != dag.finalizedHead.blck.root and if checkpoint.root != dag.finalizedHead.blck.root:
checkpoint.epoch.start_slot >= earliestSlot: let cpSlot = checkpoint.epoch.start_slot
let cpRef = if cpSlot >= earliestSlot:
dag.getBlockIdAtSlot(checkpoint.epoch.start_slot).expect("TODO").bid let
if cpRef.slot >= earliestSlot: cpBsi = dag.getExistingBlockIdAtSlot(cpSlot).valueOr:
assert cpRef.root == checkpoint.root handleUnexpectedError(bid)
doAssert dag.updateState( continue
tmpState[], cpRef.atSlot, save = false, tmpCache) cpBid = cpBsi.bid
if cpBid.slot >= earliestSlot:
assert cpBid.root == checkpoint.root
if not dag.updateExistingState(
tmpState[], cpBid.atSlot, save = false, tmpCache):
handleUnexpectedError(bid)
continue
withStateVars(tmpState[]): withStateVars(tmpState[]):
let bdata = dag.getForkedBlock(cpRef).get let bdata = dag.getExistingForkedBlock(cpBid).valueOr:
handleUnexpectedError(bid)
continue
withStateAndBlck(state, bdata): withStateAndBlck(state, bdata):
when stateFork >= BeaconStateFork.Altair: when stateFork >= BeaconStateFork.Altair:
dag.cacheLightClientData(state, blck, isNew = false) dag.cacheLightClientData(state, blck)
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
# Create `LightClientUpdate` for non-finalized blocks. # Create `LightClientUpdate` for non-finalized blocks.
if blockRef.slot > finalizedSlot: if bid.slot > finalizedSlot:
dag.createLightClientUpdates(state, blck, dag.parent(blockRef).expect("TODO")) let parentBid = dag.existingParent(bid).valueOr:
handleUnexpectedError(bid)
continue
dag.createLightClientUpdates(state, blck, parentBid)
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
let lightClientEndTick = Moment.now() let lightClientEndTick = Moment.now()
@ -819,7 +929,6 @@ proc initLightClientCache*(dag: ChainDAGRef) =
# Import historic data # Import historic data
if dag.importLightClientData == ImportLightClientData.Full: if dag.importLightClientData == ImportLightClientData.Full:
let let
earliestSlot = dag.computeEarliestLightClientSlot
earliestPeriod = earliestSlot.sync_committee_period earliestPeriod = earliestSlot.sync_committee_period
for period in earliestPeriod ..< finalizedPeriod: for period in earliestPeriod ..< finalizedPeriod:
dag.initBestLightClientUpdateForPeriod(period) dag.initBestLightClientUpdateForPeriod(period)
@ -861,53 +970,53 @@ proc getOptimisticLightClientUpdate*(
proc getLightClientBootstrap*( proc getLightClientBootstrap*(
dag: ChainDAGRef, dag: ChainDAGRef,
blockRoot: Eth2Digest): Option[altair.LightClientBootstrap] = blockRoot: Eth2Digest): Opt[altair.LightClientBootstrap] =
if not dag.serveLightClientData: if not dag.serveLightClientData:
return none(altair.LightClientBootstrap) return err()
let bdata = dag.getForkedBlock(blockRoot).valueOr: let bdata = dag.getForkedBlock(blockRoot).valueOr:
debug "Bootstrap unavailable: Block not found", blockRoot debug "Bootstrap unavailable: Block not found", blockRoot
return none(altair.LightClientBootstrap) return err()
withBlck(bdata): withBlck(bdata):
let let slot = blck.message.slot
slot = blck.message.slot
period = slot.sync_committee_period
when stateFork >= BeaconStateFork.Altair: when stateFork >= BeaconStateFork.Altair:
let earliestSlot = dag.computeEarliestLightClientSlot let earliestSlot = dag.computeEarliestLightClientSlot
if slot < earliestSlot: if slot < earliestSlot:
debug "Bootstrap unavailable: Block too old", slot debug "Bootstrap unavailable: Block too old", slot
return none(altair.LightClientBootstrap) return err()
if slot > dag.finalizedHead.blck.slot: if slot > dag.finalizedHead.blck.slot:
debug "Bootstrap unavailable: Not finalized", blockRoot debug "Bootstrap unavailable: Not finalized", blockRoot
return none(altair.LightClientBootstrap) return err()
var cachedBootstrap = var cachedBootstrap =
dag.lightClientCache.bootstrap.getOrDefault(slot) dag.lightClientCache.bootstrap.getOrDefault(slot)
if cachedBootstrap.current_sync_committee_branch.isZeroMemory: if cachedBootstrap.current_sync_committee_branch.isZeroMemory:
if dag.importLightClientData == ImportLightClientData.OnDemand: if dag.importLightClientData == ImportLightClientData.OnDemand:
let bsi = ? dag.getExistingBlockIdAtSlot(slot)
var tmpState = assignClone(dag.headState) var tmpState = assignClone(dag.headState)
dag.withUpdatedState(tmpState[], dag.getBlockIdAtSlot(slot).expect("TODO")) do: dag.withUpdatedExistingState(tmpState[], bsi) do:
withState(state): withState(state):
when stateFork >= BeaconStateFork.Altair: when stateFork >= BeaconStateFork.Altair:
state.data.build_proof( state.data.build_proof(
altair.CURRENT_SYNC_COMMITTEE_INDEX, altair.CURRENT_SYNC_COMMITTEE_INDEX,
cachedBootstrap.current_sync_committee_branch) cachedBootstrap.current_sync_committee_branch)
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
do: raiseAssert "Unreachable" do: return err()
dag.lightClientCache.bootstrap[slot] = cachedBootstrap dag.lightClientCache.bootstrap[slot] = cachedBootstrap
else: else:
debug "Bootstrap unavailable: Data not cached", slot debug "Bootstrap unavailable: Data not cached", slot
return none(altair.LightClientBootstrap) return err()
let period = slot.sync_committee_period
var tmpState = assignClone(dag.headState) var tmpState = assignClone(dag.headState)
var bootstrap {.noinit.}: altair.LightClientBootstrap var bootstrap {.noinit.}: altair.LightClientBootstrap
bootstrap.header = bootstrap.header =
blck.toBeaconBlockHeader blck.toBeaconBlockHeader
bootstrap.current_sync_committee = bootstrap.current_sync_committee =
dag.currentSyncCommitteeForPeriod(tmpState[], period) ? dag.currentSyncCommitteeForPeriod(tmpState[], period)
bootstrap.current_sync_committee_branch = bootstrap.current_sync_committee_branch =
cachedBootstrap.current_sync_committee_branch cachedBootstrap.current_sync_committee_branch
return some(bootstrap) return ok bootstrap
else: else:
debug "Bootstrap unavailable: Block before Altair", slot debug "Bootstrap unavailable: Block before Altair", slot
return none(altair.LightClientBootstrap) return err()

View File

@ -125,7 +125,7 @@ suite "Light client" & preset():
# Initialize light client store # Initialize light client store
let bootstrap = dag.getLightClientBootstrap(trusted_block_root) let bootstrap = dag.getLightClientBootstrap(trusted_block_root)
check bootstrap.isSome check bootstrap.isOk
var storeRes = initialize_light_client_store( var storeRes = initialize_light_client_store(
trusted_block_root, bootstrap.get) trusted_block_root, bootstrap.get)
check storeRes.isOk check storeRes.isOk

View File

@ -95,7 +95,7 @@ suite "Light client processor" & preset():
test "Standard sync" & preset(): test "Standard sync" & preset():
let bootstrap = dag.getLightClientBootstrap(trustedBlockRoot) let bootstrap = dag.getLightClientBootstrap(trustedBlockRoot)
check bootstrap.isSome check bootstrap.isOk
setTimeToSlot(bootstrap.get.header.slot) setTimeToSlot(bootstrap.get.header.slot)
res = processor[].storeObject( res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), bootstrap.get) MsgSource.gossip, getBeaconTime(), bootstrap.get)
@ -117,7 +117,7 @@ suite "Light client processor" & preset():
test "Forced update" & preset(): test "Forced update" & preset():
let bootstrap = dag.getLightClientBootstrap(trustedBlockRoot) let bootstrap = dag.getLightClientBootstrap(trustedBlockRoot)
check bootstrap.isSome check bootstrap.isOk
setTimeToSlot(bootstrap.get.header.slot) setTimeToSlot(bootstrap.get.header.slot)
res = processor[].storeObject( res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), bootstrap.get) MsgSource.gossip, getBeaconTime(), bootstrap.get)
@ -196,7 +196,7 @@ suite "Light client processor" & preset():
test "Invalid bootstrap" & preset(): test "Invalid bootstrap" & preset():
var bootstrap = dag.getLightClientBootstrap(trustedBlockRoot) var bootstrap = dag.getLightClientBootstrap(trustedBlockRoot)
check bootstrap.isSome check bootstrap.isOk
bootstrap.get.header.slot.inc() bootstrap.get.header.slot.inc()
setTimeToSlot(bootstrap.get.header.slot) setTimeToSlot(bootstrap.get.header.slot)
res = processor[].storeObject( res = processor[].storeObject(
@ -208,7 +208,7 @@ suite "Light client processor" & preset():
test "Duplicate bootstrap" & preset(): test "Duplicate bootstrap" & preset():
let bootstrap = dag.getLightClientBootstrap(trustedBlockRoot) let bootstrap = dag.getLightClientBootstrap(trustedBlockRoot)
check bootstrap.isSome check bootstrap.isOk
setTimeToSlot(bootstrap.get.header.slot) setTimeToSlot(bootstrap.get.header.slot)
res = processor[].storeObject( res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), bootstrap.get) MsgSource.gossip, getBeaconTime(), bootstrap.get)