import finalized head LC bootstrap on launch (#5775)

If the initial state replays cover the finalized head, import matching
`LightClientBootstrap` into database.

This also addresses this error when light client requests bootstrap from
the genesis slot on networks that launch with Altair enabled.

```
{"lvl":"DBG","ts":"2023-10-04 11:17:49.665+00:00","msg":"LC bootstrap unavailable: Sync committee branch not cached","topics":"chaindag_lc","slot":0}
```
This commit is contained in:
Etan Kissling 2024-01-18 23:51:26 +01:00 committed by GitHub
parent 479c133375
commit be73ce2e9a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 58 additions and 38 deletions

View File

@ -579,7 +579,7 @@ proc assignLightClientData(
forkyObject.signature_slot = signature_slot
ok()
proc createLightClientUpdates(
proc createLightClientUpdate(
dag: ChainDAGRef,
state: ForkyHashedBeaconState,
blck: ForkyTrustedSignedBeaconBlock,
@ -665,6 +665,38 @@ proc createLightClientUpdates(
current_period_best_update = best,
latest_signature_slot = latest_signature_slot)
proc createLightClientBootstrap(
dag: ChainDAGRef, bid: BlockId): Opt[void] =
let
bdata = ? dag.getExistingForkedBlock(bid)
period = bid.slot.sync_committee_period
if not dag.lcDataStore.db.hasSyncCommittee(period):
let didPutSyncCommittee = withState(dag.headState):
when consensusFork >= ConsensusFork.Altair:
if period == forkyState.data.slot.sync_committee_period:
dag.lcDataStore.db.putSyncCommittee(
period, forkyState.data.current_sync_committee)
true
else:
false
else:
false
if not didPutSyncCommittee:
let
tmpState = assignClone(dag.headState)
syncCommittee = ? dag.existingCurrentSyncCommitteeForPeriod(
tmpState[], period)
dag.lcDataStore.db.putSyncCommittee(period, syncCommittee)
withBlck(bdata):
when consensusFork >= ConsensusFork.Altair:
const lcDataFork = lcDataForkAtConsensusFork(consensusFork)
dag.lcDataStore.db.putHeader(
forkyBlck.toLightClientHeader(lcDataFork))
else: raiseAssert "Unreachable"
dag.lcDataStore.db.putCurrentSyncCommitteeBranch(
bid.slot, dag.getLightClientData(bid).current_sync_committee_branch)
ok()
proc initLightClientDataCache*(dag: ChainDAGRef) =
## Initialize cached light client data
if not dag.shouldImportLcData:
@ -712,7 +744,7 @@ proc initLightClientDataCache*(dag: ChainDAGRef) =
else:
false
# Build list of block to process.
# Build list of blocks to process.
# As it is slow to load states in descending order,
# build a reverse todo list to then process them in ascending order
let tailSlot = dag.lcDataStore.cache.tailSlot
@ -767,10 +799,16 @@ proc initLightClientDataCache*(dag: ChainDAGRef) =
current_period_best_update = best,
latest_signature_slot = GENESIS_SLOT)
else:
dag.createLightClientUpdates(
dag.createLightClientUpdate(
forkyState, forkyBlck, parentBid = blocks[i + 1])
else: raiseAssert "Unreachable"
# Import initial `LightClientBootstrap`
if dag.finalizedHead.slot >= dag.lcDataStore.cache.tailSlot:
if dag.createLightClientBootstrap(dag.finalizedHead.blck.bid).isErr:
dag.handleUnexpectedLightClientError(dag.finalizedHead.blck.bid.slot)
res.err()
let lightClientEndTick = Moment.now()
debug "Initialized cached LC data",
initDur = lightClientEndTick - lightClientStartTick, res
@ -807,7 +845,7 @@ proc processNewBlockForLightClient*(
const consensusFork = typeof(signedBlock).kind
when consensusFork >= ConsensusFork.Altair:
template forkyState: untyped = state.forky(consensusFork)
dag.createLightClientUpdates(forkyState, signedBlock, parentBid)
dag.createLightClientUpdate(forkyState, signedBlock, parentBid)
else:
raiseAssert "Unreachable" # `tailSlot` cannot be before Altair
@ -902,38 +940,9 @@ proc processFinalizationForLightClient*(
break
bid = bsi.bid
if bid.slot >= lowSlot:
let
bdata = dag.getExistingForkedBlock(bid).valueOr:
dag.handleUnexpectedLightClientError(bid.slot)
break
period = bid.slot.sync_committee_period
if not dag.lcDataStore.db.hasSyncCommittee(period):
let didPutSyncCommittee = withState(dag.headState):
when consensusFork >= ConsensusFork.Altair:
if period == forkyState.data.slot.sync_committee_period:
dag.lcDataStore.db.putSyncCommittee(
period, forkyState.data.current_sync_committee)
true
else:
false
else:
false
if not didPutSyncCommittee:
let
tmpState = assignClone(dag.headState)
syncCommittee = dag.existingCurrentSyncCommitteeForPeriod(
tmpState[], period).valueOr:
dag.handleUnexpectedLightClientError(bid.slot)
break
dag.lcDataStore.db.putSyncCommittee(period, syncCommittee)
withBlck(bdata):
when consensusFork >= ConsensusFork.Altair:
const lcDataFork = lcDataForkAtConsensusFork(consensusFork)
dag.lcDataStore.db.putHeader(
forkyBlck.toLightClientHeader(lcDataFork))
else: raiseAssert "Unreachable"
dag.lcDataStore.db.putCurrentSyncCommitteeBranch(
bid.slot, dag.getLightClientData(bid).current_sync_committee_branch)
if dag.createLightClientBootstrap(bid).isErr:
dag.handleUnexpectedLightClientError(bid.slot)
break
boundarySlot = bid.slot.nextEpochBoundarySlot
if boundarySlot < SLOTS_PER_EPOCH:
break

View File

@ -459,8 +459,19 @@ proc addObject*(
(afterGenesis, _) = wallTime.toSlot()
if not afterGenesis:
error "Processing LC object before genesis, clock turned back?"
quit 1
let mayProcessBeforeGenesis =
when obj is ForkedLightClientBootstrap:
withForkyBootstrap(obj):
when lcDataFork > LightClientDataFork.None:
forkyBootstrap.header.beacon.slot == GENESIS_SLOT and
self.cfg.ALTAIR_FORK_EPOCH == GENESIS_EPOCH
else:
false
else:
false
if not mayProcessBeforeGenesis:
error "Processing LC object before genesis, clock turned back?"
quit 1
let res = self.storeObject(src, wallTime, obj)