add LightClientHeader wrapper (#4481)

The light client data structures were changed to accommodate additional
fields in future forks (e.g., to also hold execution data).

There is a minor change to the JSON serialization, where the `header`
properties are now nested inside a `LightClientHeader`.
The SSZ serialization remains compatible.

See https://github.com/ethereum/consensus-specs/pull/3190
and https://github.com/ethereum/beacon-APIs/pull/287
This commit is contained in:
Etan Kissling 2023-01-13 16:46:35 +01:00 committed by GitHub
parent 77530841e3
commit 2324136552
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 214 additions and 154 deletions

View File

@ -34,8 +34,8 @@ logScope: topics = "lcdata"
# libp2p request is handled. However, the space savings are quite small.
# Furthermore, `LightClientUpdate` is consulted on each new block to attempt
# improving it. Continuously decompressing and recompressing seems inefficient.
# Finally, the libp2p context bytes depend on `attested_header.slot` to derive
# the underlying fork digest; the `kind` column is not sufficient to derive
# Finally, the libp2p context bytes depend on `attested_header.beacon.slot` for
# deriving the fork digest; the `kind` column is not sufficient to derive
# the fork digest, because the same storage format may be used across forks.
# SSZ storage selected due to the small size and reduced logic complexity.
#

View File

@ -1,5 +1,5 @@
# beacon_chain
# Copyright (c) 2022 Status Research & Development GmbH
# 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).
@ -12,6 +12,7 @@ else:
import
chronicles,
./spec/datatypes/altair,
./beacon_node
logScope: topics = "beacnde"
@ -23,7 +24,7 @@ func shouldSyncOptimistically*(node: BeaconNode, wallSlot: Slot): bool =
return false
shouldSyncOptimistically(
optimisticSlot = optimisticHeader.slot,
optimisticSlot = optimisticHeader.beacon.slot,
dagSlot = getStateField(node.dag.headState, slot),
wallSlot = wallSlot)
@ -85,8 +86,9 @@ proc initLightClient*(
if config.syncLightClient:
proc onOptimisticHeader(
lightClient: LightClient, optimisticHeader: BeaconBlockHeader) =
optimisticProcessor.setOptimisticHeader(optimisticHeader)
lightClient: LightClient,
optimisticHeader: altair.LightClientHeader) =
optimisticProcessor.setOptimisticHeader(optimisticHeader.beacon)
lightClient.onOptimisticHeader = onOptimisticHeader
lightClient.trustedBlockRoot = config.trustedBlockRoot
@ -145,13 +147,14 @@ proc updateLightClientFromDag*(node: BeaconNode) =
let lcHeader = node.lightClient.finalizedHeader
if lcHeader.isSome:
if dagPeriod <= lcHeader.get.slot.sync_committee_period:
if dagPeriod <= lcHeader.get.beacon.slot.sync_committee_period:
return
let
bdata = node.dag.getForkedBlock(dagHead.blck.bid).valueOr:
return
header = bdata.toBeaconBlockHeader
header = withBlck(bdata):
blck.toLightClientHeader(LightClientStore.kind)
current_sync_committee = block:
let tmpState = assignClone(node.dag.headState)
node.dag.currentSyncCommitteeForPeriod(tmpState[], dagPeriod).valueOr:

View File

@ -359,7 +359,7 @@ proc initLightClientUpdateForPeriod(
const lcDataFork = LightClientDataFork.Altair
update = ForkedLightClientUpdate(kind: lcDataFork)
template forkyUpdate: untyped = update.forky(lcDataFork)
forkyUpdate.attested_header = blck.toBeaconBlockHeader()
forkyUpdate.attested_header = blck.toLightClientHeader(lcDataFork)
forkyUpdate.next_sync_committee = forkyState.data.next_sync_committee
forkyUpdate.next_sync_committee_branch =
forkyState.data.build_proof(altair.NEXT_SYNC_COMMITTEE_INDEX).get
@ -377,7 +377,7 @@ proc initLightClientUpdateForPeriod(
withBlck(bdata):
withForkyUpdate(update):
when lcDataFork >= LightClientDataFork.Altair:
forkyUpdate.finalized_header = blck.toBeaconBlockHeader()
forkyUpdate.finalized_header = blck.toLightClientHeader(lcDataFork)
let bdata = dag.getExistingForkedBlock(signatureBid).valueOr:
dag.handleUnexpectedLightClientError(signatureBid.slot)
return err()
@ -465,7 +465,8 @@ template lazy_header(name: untyped): untyped {.dirty.} =
dag.handleUnexpectedLightClientError(bid.slot)
`name _ ok` = false
else:
obj.name = bdata.get.toBeaconBlockHeader()
withBlck(bdata.get):
obj.name = blck.toLightClientHeader(data_fork)
`name _ ptr` = addr obj.name
`name _ ok`
template `assign _ name _ with_migration`(
@ -480,7 +481,8 @@ template lazy_header(name: untyped): untyped {.dirty.} =
`name _ ok` = false
else:
obj.migrateToDataFork(data_fork)
obj.forky(data_fork).name = bdata.get.toBeaconBlockHeader()
withBlck(bdata.get):
obj.forky(data_fork).name = blck.toLightClientHeader(data_fork)
`name _ ptr` = addr obj.forky(data_fork).name
`name _ ok`
@ -545,8 +547,8 @@ proc createLightClientUpdates(
signature_slot = blck.message.slot
is_later = withForkyFinalityUpdate(latest):
when lcDataFork >= LightClientDataFork.Altair:
if attested_slot != forkyFinalityUpdate.attested_header.slot:
attested_slot > forkyFinalityUpdate.attested_header.slot
if attested_slot != forkyFinalityUpdate.attested_header.beacon.slot:
attested_slot > forkyFinalityUpdate.attested_header.beacon.slot
else:
signature_slot > forkyFinalityUpdate.signature_slot
else:
@ -555,7 +557,7 @@ proc createLightClientUpdates(
template forkyLatest: untyped = latest.forky(data_fork)
load_attested_data(attested_bid)
let finalized_slot = attested_data.finalized_slot
if finalized_slot == forkyLatest.finalized_header.slot:
if finalized_slot == forkyLatest.finalized_header.beacon.slot:
forkyLatest.finality_branch = attested_data.finality_branch
elif finalized_slot == GENESIS_SLOT:
forkyLatest.finalized_header.reset()
@ -605,7 +607,7 @@ proc createLightClientUpdates(
forkyBest.next_sync_committee = next_sync_committee
forkyBest.next_sync_committee_branch =
attested_data.next_sync_committee_branch
if finalized_slot == forkyBest.finalized_header.slot:
if finalized_slot == forkyBest.finalized_header.beacon.slot:
forkyBest.finality_branch = attested_data.finality_branch
elif finalized_slot == GENESIS_SLOT:
forkyBest.finalized_header.reset()
@ -922,7 +924,7 @@ proc getLightClientBootstrap*(
forkyBootstrap.current_sync_committee =
dag.existingCurrentSyncCommitteeForPeriod(tmpState[], period).valueOr:
return default(ForkedLightClientBootstrap)
forkyBootstrap.header = blck.toBeaconBlockHeader()
forkyBootstrap.header = blck.toLightClientHeader(lcDataFork)
forkyBootstrap.current_sync_committee_branch = branch
return bootstrap
else:

View File

@ -1,5 +1,5 @@
# beacon_chain
# Copyright (c) 2022 Status Research & Development GmbH
# 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).
@ -20,11 +20,11 @@ type
LightClientPool* = object
latestForwardedFinalitySlot*: Slot
## Latest finality update that was forwarded on libp2p gossip.
## Tracks `finality_update.finalized_header.slot`.
## Tracks `finality_update.finalized_header.beacon.slot`.
latestForwardedOptimisticSlot*: Slot
## Latest optimistic update that was forwarded on libp2p gossip.
## Tracks `optimistic_update.attested_header.slot`.
## Tracks `optimistic_update.attested_header.beacon.slot`.
latestBroadcastedSlot*: Slot
## Latest slot for which updates were broadcasted on libp2p gossip.

View File

@ -1098,11 +1098,11 @@ proc validateLightClientFinalityUpdate*(
wallTime: BeaconTime): Result[void, ValidationError] =
let finalized_slot = withForkyFinalityUpdate(finality_update):
when lcDataFork >= LightClientDataFork.Altair:
forkyFinalityUpdate.finalized_header.slot
forkyFinalityUpdate.finalized_header.beacon.slot
else:
GENESIS_SLOT
if finalized_slot <= pool.latestForwardedFinalitySlot:
# [IGNORE] The `finalized_header.slot` is greater than that of all
# [IGNORE] The `finalized_header.beacon.slot` is greater than that of all
# previously forwarded `finality_update`s
return errIgnore("LightClientFinalityUpdate: slot already forwarded")
@ -1134,11 +1134,11 @@ proc validateLightClientOptimisticUpdate*(
wallTime: BeaconTime): Result[void, ValidationError] =
let attested_slot = withForkyOptimisticUpdate(optimistic_update):
when lcDataFork >= LightClientDataFork.Altair:
forkyOptimisticUpdate.attested_header.slot
forkyOptimisticUpdate.attested_header.beacon.slot
else:
GENESIS_SLOT
if attested_slot <= pool.latestForwardedOptimisticSlot:
# [IGNORE] The `attested_header.slot` is greater than that of all
# [IGNORE] The `attested_header.beacon.slot` is greater than that of all
# previously forwarded `optimistic_update`s
return errIgnore("LightClientOptimisticUpdate: slot already forwarded")

View File

@ -13,6 +13,7 @@ else:
import
stew/objects,
chronos, metrics,
../spec/datatypes/altair,
../spec/light_client_sync,
../consensus_object_pools/block_pools_types,
".."/[beacon_clock, sszdump],
@ -204,12 +205,12 @@ proc tryForceUpdate(
discard
of DidUpdateWithoutSupermajority:
warn "Light client force-updated without supermajority",
finalizedSlot = store[].get.finalized_header.slot,
optimisticSlot = store[].get.optimistic_header.slot
finalizedSlot = store[].get.finalized_header.beacon.slot,
optimisticSlot = store[].get.optimistic_header.beacon.slot
of DidUpdateWithoutFinality:
warn "Light client force-updated without finality proof",
finalizedSlot = store[].get.finalized_header.slot,
optimisticSlot = store[].get.optimistic_header.slot
finalizedSlot = store[].get.finalized_header.beacon.slot,
optimisticSlot = store[].get.optimistic_header.beacon.slot
proc processObject(
self: var LightClientProcessor,
@ -299,12 +300,12 @@ template withReportedProgress(
if store[].isSome:
store[].get.finalized_header
else:
BeaconBlockHeader()
altair.LightClientHeader()
previousOptimistic =
if store[].isSome:
store[].get.optimistic_header
else:
BeaconBlockHeader()
altair.LightClientHeader()
body
@ -383,16 +384,16 @@ proc storeObject*(
let objSlot = withForkyObject(obj):
when lcDataFork >= LightClientDataFork.Altair:
when forkyObject is ForkyLightClientBootstrap:
forkyObject.header.slot
forkyObject.header.beacon.slot
elif forkyObject is SomeForkyLightClientUpdateWithFinality:
forkyObject.finalized_header.slot
forkyObject.finalized_header.beacon.slot
else:
forkyObject.attested_header.slot
forkyObject.attested_header.beacon.slot
else:
GENESIS_SLOT
debug "LC object processed",
finalizedSlot = store[].get.finalized_header.slot,
optimisticSlot = store[].get.optimistic_header.slot,
finalizedSlot = store[].get.finalized_header.beacon.slot,
optimisticSlot = store[].get.optimistic_header.beacon.slot,
kind = typeof(obj).name,
objectSlot = objSlot,
storeObjectDur
@ -400,7 +401,7 @@ proc storeObject*(
proc resetToFinalizedHeader*(
self: var LightClientProcessor,
header: BeaconBlockHeader,
header: altair.LightClientHeader,
current_sync_committee: SyncCommittee) =
let store = self.store
@ -411,8 +412,8 @@ proc resetToFinalizedHeader*(
optimistic_header: header)
debug "LC reset to finalized header",
finalizedSlot = store[].get.finalized_header.slot,
optimisticSlot = store[].get.optimistic_header.slot
finalizedSlot = store[].get.finalized_header.beacon.slot,
optimisticSlot = store[].get.optimistic_header.beacon.slot
# Enqueue
# ------------------------------------------------------------------------------
@ -499,9 +500,9 @@ func toValidationError(
of VerifierError.MissingParent,
VerifierError.UnviableFork,
VerifierError.Duplicate:
# [IGNORE] The `finalized_header.slot` is greater than that of
# [IGNORE] The `finalized_header.beacon.slot` is greater than that of
# all previously forwarded `finality_update`s
# [IGNORE] The `attested_header.slot` is greater than that of all
# [IGNORE] The `attested_header.beacon.slot` is greater than that of all
# previously forwarded `optimistic_update`s
errIgnore($r.error)
@ -531,12 +532,12 @@ proc processLightClientOptimisticUpdate*(
let
latestFinalitySlot = withForkyOptimisticUpdate(self.latestFinalityUpdate):
when lcDataFork >= LightClientDataFork.Altair:
forkyOptimisticUpdate.attested_header.slot
forkyOptimisticUpdate.attested_header.beacon.slot
else:
GENESIS_SLOT
attestedSlot = withForkyOptimisticUpdate(optimistic_update):
when lcDataFork >= LightClientDataFork.Altair:
forkyOptimisticUpdate.attested_header.slot
forkyOptimisticUpdate.attested_header.beacon.slot
else:
GENESIS_SLOT
if attestedSlot >= latestFinalitySlot:

View File

@ -26,7 +26,7 @@ logScope: topics = "lightcl"
type
LightClientHeaderCallback* =
proc(lightClient: LightClient, header: BeaconBlockHeader) {.
proc(lightClient: LightClient, header: altair.LightClientHeader) {.
gcsafe, raises: [Defect].}
LightClientValueObserver[V] =
@ -56,13 +56,15 @@ type
optimisticUpdateObserver*: LightClientOptimisticUpdateObserver
trustedBlockRoot*: Option[Eth2Digest]
func finalizedHeader*(lightClient: LightClient): Opt[BeaconBlockHeader] =
func finalizedHeader*(
lightClient: LightClient): Opt[altair.LightClientHeader] =
if lightClient.store[].isSome:
ok lightClient.store[].get.finalized_header
else:
err()
func optimisticHeader*(lightClient: LightClient): Opt[BeaconBlockHeader] =
func optimisticHeader*(
lightClient: LightClient): Opt[altair.LightClientHeader] =
if lightClient.store[].isSome:
ok lightClient.store[].get.optimistic_header
else:
@ -157,13 +159,15 @@ proc createLightClient(
func getFinalizedPeriod(): SyncCommitteePeriod =
if lightClient.store[].isSome:
lightClient.store[].get.finalized_header.slot.sync_committee_period
lightClient.store[].get.finalized_header
.beacon.slot.sync_committee_period
else:
GENESIS_SLOT.sync_committee_period
func getOptimisticPeriod(): SyncCommitteePeriod =
if lightClient.store[].isSome:
lightClient.store[].get.optimistic_header.slot.sync_committee_period
lightClient.store[].get.optimistic_header
.beacon.slot.sync_committee_period
else:
GENESIS_SLOT.sync_committee_period
@ -214,7 +218,7 @@ proc start*(lightClient: LightClient) =
proc resetToFinalizedHeader*(
lightClient: LightClient,
header: BeaconBlockHeader,
header: altair.LightClientHeader,
current_sync_committee: SyncCommittee) =
lightClient.processor[].resetToFinalizedHeader(header, current_sync_committee)

View File

@ -1,5 +1,5 @@
# beacon_chain
# Copyright (c) 2022 Status Research & Development GmbH
# 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).
@ -44,7 +44,7 @@ type
## SQLite backend
headers: LightClientHeadersStore
## LightClientHeaderKind -> BeaconBlockHeader
## LightClientHeaderKind -> altair.LightClientHeader
## Stores the latest light client headers.
syncCommittees: SyncCommitteeStore
@ -57,7 +57,7 @@ func initLightClientHeadersStore(
? backend.exec("""
CREATE TABLE IF NOT EXISTS `""" & name & """` (
`kind` INTEGER PRIMARY KEY, -- `LightClientHeaderKind`
`header` BLOB -- `BeaconBlockHeader` (SSZ)
`header` BLOB -- `altair.LightClientHeader` (SSZ)
);
""")
@ -81,26 +81,27 @@ func close(store: LightClientHeadersStore) =
store.getStmt.dispose()
store.putStmt.dispose()
proc getLatestFinalizedHeader*(db: LightClientDB): Opt[BeaconBlockHeader] =
proc getLatestFinalizedHeader*(
db: LightClientDB): Opt[altair.LightClientHeader] =
var header: seq[byte]
for res in db.headers.getStmt.exec(
LightClientHeaderKind.Finalized.int64, header):
res.expect("SQL query OK")
try:
return ok SSZ.decode(header, BeaconBlockHeader)
return ok SSZ.decode(header, altair.LightClientHeader)
except SszError as exc:
error "LC store corrupted", store = "headers",
kind = "Finalized", exc = exc.msg
return err()
func putLatestFinalizedHeader*(
db: LightClientDB, header: BeaconBlockHeader) =
db: LightClientDB, header: altair.LightClientHeader) =
block:
let res = db.headers.putStmt.exec(
(LightClientHeaderKind.Finalized.int64, SSZ.encode(header)))
res.expect("SQL query OK")
block:
let period = header.slot.sync_committee_period
let period = header.beacon.slot.sync_committee_period
doAssert period.isSupportedBySQLite
let res = db.syncCommittees.keepFromStmt.exec(period.int64)
res.expect("SQL query OK")

View File

@ -1,5 +1,5 @@
# beacon_chain
# Copyright (c) 2022 Status Research & Development GmbH
# 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).
@ -158,21 +158,21 @@ programMain:
waitFor network.start()
proc onFinalizedHeader(
lightClient: LightClient, finalizedHeader: BeaconBlockHeader) =
lightClient: LightClient, finalizedHeader: altair.LightClientHeader) =
info "New LC finalized header",
finalized_header = shortLog(finalizedHeader)
let
period = finalizedHeader.slot.sync_committee_period
period = finalizedHeader.beacon.slot.sync_committee_period
syncCommittee = lightClient.finalizedSyncCommittee.expect("Bootstrap OK")
db.putSyncCommittee(period, syncCommittee)
db.putLatestFinalizedHeader(finalizedHeader)
proc onOptimisticHeader(
lightClient: LightClient, optimisticHeader: BeaconBlockHeader) =
lightClient: LightClient, optimisticHeader: altair.LightClientHeader) =
info "New LC optimistic header",
optimistic_header = shortLog(optimisticHeader)
optimisticProcessor.setOptimisticHeader(optimisticHeader)
optimisticProcessor.setOptimisticHeader(optimisticHeader.beacon)
lightClient.onFinalizedHeader = onFinalizedHeader
lightClient.onOptimisticHeader = onOptimisticHeader
@ -181,7 +181,7 @@ programMain:
let latestHeader = db.getLatestFinalizedHeader()
if latestHeader.isOk:
let
period = latestHeader.get.slot.sync_committee_period
period = latestHeader.get.beacon.slot.sync_committee_period
syncCommittee = db.getSyncCommittee(period)
if syncCommittee.isErr:
error "LC store lacks sync committee", finalized_header = latestHeader.get
@ -192,8 +192,8 @@ programMain:
# - EL clients may not sync when only driven with `forkChoiceUpdated`,
# e.g., Geth: "Forkchoice requested unknown head"
# - `newPayload` requires the full `ExecutionPayload` (most of block content)
# - `ExecutionPayload` block root is not available in `BeaconBlockHeader`,
# so won't be exchanged via light client gossip
# - `ExecutionPayload` block root is not available in
# `altair.LightClientHeader`, so won't be exchanged via light client gossip
#
# Future `ethereum/consensus-specs` versions may remove need for full blocks.
# Therefore, this current mechanism is to be seen as temporary; it is not
@ -206,7 +206,7 @@ programMain:
# Check whether light client has synced sufficiently close to wall slot
const maxAge = 2 * SLOTS_PER_EPOCH
if optimisticHeader.slot < max(wallSlot, maxAge.Slot) - maxAge:
if optimisticHeader.beacon.slot < max(wallSlot, maxAge.Slot) - maxAge:
return false
true
@ -268,12 +268,12 @@ programMain:
finalizedBid =
if finalizedHeader.isSome:
finalizedHeader.get.toBlockId()
finalizedHeader.get.beacon.toBlockId()
else:
BlockId(root: genesisBlockRoot, slot: GENESIS_SLOT)
optimisticBid =
if optimisticHeader.isSome:
optimisticHeader.get.toBlockId()
optimisticHeader.get.beacon.toBlockId()
else:
BlockId(root: genesisBlockRoot, slot: GENESIS_SLOT)

View File

@ -160,26 +160,31 @@ type
NextSyncCommitteeBranch* =
array[log2trunc(NEXT_SYNC_COMMITTEE_INDEX), Eth2Digest]
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/sync-protocol.md#lightclientheader
LightClientHeader* = object
beacon*: BeaconBlockHeader
## Beacon block header
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/sync-protocol.md#lightclientbootstrap
LightClientBootstrap* = object
header*: BeaconBlockHeader
header*: LightClientHeader
## Header matching the requested beacon block root
current_sync_committee*: SyncCommittee
## Current sync committee corresponding to `header.state_root`
## Current sync committee corresponding to `header.beacon.state_root`
current_sync_committee_branch*: CurrentSyncCommitteeBranch
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/sync-protocol.md#lightclientupdate
LightClientUpdate* = object
attested_header*: BeaconBlockHeader
attested_header*: LightClientHeader
## Header attested to by the sync committee
next_sync_committee*: SyncCommittee
## Next sync committee corresponding to `attested_header.state_root`
## Next sync committee corresponding to `attested_header.beacon.state_root`
next_sync_committee_branch*: NextSyncCommitteeBranch
# Finalized header corresponding to `attested_header.state_root`
finalized_header*: BeaconBlockHeader
# Finalized header corresponding to `attested_header.beacon.state_root`
finalized_header*: LightClientHeader
finality_branch*: FinalityBranch
sync_aggregate*: SyncAggregate
@ -190,10 +195,10 @@ type
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/sync-protocol.md#lightclientfinalityupdate
LightClientFinalityUpdate* = object
# Header attested to by the sync committee
attested_header*: BeaconBlockHeader
attested_header*: LightClientHeader
# Finalized header corresponding to `attested_header.state_root`
finalized_header*: BeaconBlockHeader
# Finalized header corresponding to `attested_header.beacon.state_root`
finalized_header*: LightClientHeader
finality_branch*: FinalityBranch
# Sync committee aggregate signature
@ -204,7 +209,7 @@ type
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/sync-protocol.md#lightclientoptimisticupdate
LightClientOptimisticUpdate* = object
# Header attested to by the sync committee
attested_header*: BeaconBlockHeader
attested_header*: LightClientHeader
# Sync committee aggregate signature
sync_aggregate*: SyncAggregate
@ -229,7 +234,7 @@ type
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/sync-protocol.md#lightclientstore
LightClientStore* = object
finalized_header*: BeaconBlockHeader
finalized_header*: LightClientHeader
## Header that is finalized
current_sync_committee*: SyncCommittee
@ -239,7 +244,7 @@ type
best_valid_update*: Opt[LightClientUpdate]
## Best available header to switch finalized head to if we see nothing else
optimistic_header*: BeaconBlockHeader
optimistic_header*: LightClientHeader
## Most recent available reasonably-safe header
previous_max_active_participants*: uint64
@ -696,6 +701,14 @@ chronicles.formatIt SyncCommitteeContribution: shortLog(it)
chronicles.formatIt ContributionAndProof: shortLog(it)
chronicles.formatIt SignedContributionAndProof: shortLog(it)
func is_valid_light_client_header*(v: LightClientHeader): bool =
true
func shortLog*(v: LightClientHeader): auto =
(
beacon: shortLog(v.beacon)
)
func shortLog*(v: LightClientBootstrap): auto =
(
header: shortLog(v.header)

View File

@ -11,7 +11,7 @@ else:
{.push raises: [].}
import
./datatypes/[phase0, altair]
./datatypes/[phase0, altair, bellatrix, capella, eip4844]
type
LightClientDataFork* {.pure.} = enum # Append only, used in DB data!
@ -19,7 +19,7 @@ type
Altair = 1
ForkyLightClientHeader* =
BeaconBlockHeader
altair.LightClientHeader
ForkyLightClientBootstrap* =
altair.LightClientBootstrap
@ -98,7 +98,7 @@ template kind*(x: typedesc[altair.LightClientStore]): LightClientDataFork =
template header*(kind: static LightClientDataFork): auto =
when kind >= LightClientDataFork.Altair:
typedesc[BeaconBlockHeader]
typedesc[altair.LightClientHeader]
else:
static: raiseAssert "Unreachable"
@ -307,3 +307,14 @@ func migratingToDataFork*[T: SomeForkedLightClientObject](
var upgradedObject = x
upgradedObject.migrateToDataFork(newKind)
upgradedObject
func toLightClientHeader*(
blck: # `SomeSignedBeaconBlock` doesn't work here (Nim 1.6)
phase0.SignedBeaconBlock | phase0.TrustedSignedBeaconBlock |
altair.SignedBeaconBlock | altair.TrustedSignedBeaconBlock |
bellatrix.SignedBeaconBlock | bellatrix.TrustedSignedBeaconBlock |
capella.SignedBeaconBlock | capella.TrustedSignedBeaconBlock |
eip4844.SignedBeaconBlock | eip4844.TrustedSignedBeaconBlock,
kind: static LightClientDataFork): auto =
when kind >= LightClientDataFork.Altair:
kind.header(beacon: blck.message.toBeaconBlockHeader())

View File

@ -240,10 +240,10 @@ type LightClientUpdateMetadata* = object
func toMeta*(update: SomeLightClientUpdate): LightClientUpdateMetadata =
var meta {.noinit.}: LightClientUpdateMetadata
meta.attested_slot =
update.attested_header.slot
update.attested_header.beacon.slot
meta.finalized_slot =
when update is SomeLightClientUpdateWithFinality:
update.finalized_header.slot
update.finalized_header.beacon.slot
else:
GENESIS_SLOT
meta.signature_slot =
@ -324,13 +324,13 @@ template is_better_update*[
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-alpha.1/specs/altair/light-client/p2p-interface.md#getlightclientbootstrap
func contextEpoch*(bootstrap: altair.LightClientBootstrap): Epoch =
bootstrap.header.slot.epoch
bootstrap.header.beacon.slot.epoch
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/p2p-interface.md#lightclientupdatesbyrange
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/p2p-interface.md#getlightclientfinalityupdate
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/altair/light-client/p2p-interface.md#getlightclientoptimisticupdate
func contextEpoch*(update: SomeLightClientUpdate): Epoch =
update.attested_header.slot.epoch
update.attested_header.beacon.slot.epoch
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.0/specs/bellatrix/beacon-chain.md#is_merge_transition_complete
func is_merge_transition_complete*(

View File

@ -23,6 +23,8 @@ func initialize_light_client_store*(
trusted_block_root: Eth2Digest,
bootstrap: altair.LightClientBootstrap
): Result[LightClientStore, VerifierError] =
if not is_valid_light_client_header(bootstrap.header):
return err(VerifierError.Invalid)
if hash_tree_root(bootstrap.header) != trusted_block_root:
return err(VerifierError.Invalid)
@ -31,7 +33,7 @@ func initialize_light_client_store*(
bootstrap.current_sync_committee_branch,
log2trunc(altair.CURRENT_SYNC_COMMITTEE_INDEX),
get_subtree_index(altair.CURRENT_SYNC_COMMITTEE_INDEX),
bootstrap.header.state_root):
bootstrap.header.beacon.state_root):
return err(VerifierError.Invalid)
return ok(LightClientStore(
@ -54,15 +56,17 @@ proc validate_light_client_update*(
return err(VerifierError.Invalid)
# Verify update does not skip a sync committee period
if not is_valid_light_client_header(update.attested_header):
return err(VerifierError.Invalid)
when update is SomeLightClientUpdateWithFinality:
if update.attested_header.slot < update.finalized_header.slot:
if update.attested_header.beacon.slot < update.finalized_header.beacon.slot:
return err(VerifierError.Invalid)
if update.signature_slot <= update.attested_header.slot:
if update.signature_slot <= update.attested_header.beacon.slot:
return err(VerifierError.Invalid)
if current_slot < update.signature_slot:
return err(VerifierError.UnviableFork)
let
store_period = store.finalized_header.slot.sync_committee_period
store_period = store.finalized_header.beacon.slot.sync_committee_period
signature_period = update.signature_slot.sync_committee_period
is_next_sync_committee_known = store.is_next_sync_committee_known
if is_next_sync_committee_known:
@ -73,10 +77,10 @@ proc validate_light_client_update*(
return err(VerifierError.MissingParent)
# Verify update is relevant
let attested_period = update.attested_header.slot.sync_committee_period
let attested_period = update.attested_header.beacon.slot.sync_committee_period
when update is SomeLightClientUpdateWithSyncCommittee:
let is_sync_committee_update = update.is_sync_committee_update
if update.attested_header.slot <= store.finalized_header.slot:
if update.attested_header.beacon.slot <= store.finalized_header.beacon.slot:
when update is SomeLightClientUpdateWithSyncCommittee:
if is_next_sync_committee_known:
return err(VerifierError.Duplicate)
@ -89,13 +93,15 @@ proc validate_light_client_update*(
# finalized header saved in the state of the `attested_header`
when update is SomeLightClientUpdateWithFinality:
if not update.is_finality_update:
if not update.finalized_header.isZeroMemory:
if update.finalized_header != altair.LightClientHeader():
return err(VerifierError.Invalid)
else:
var finalized_root {.noinit.}: Eth2Digest
if update.finalized_header.slot != GENESIS_SLOT:
finalized_root = hash_tree_root(update.finalized_header)
elif update.finalized_header.isZeroMemory:
if update.finalized_header.beacon.slot != GENESIS_SLOT:
if not is_valid_light_client_header(update.finalized_header):
return err(VerifierError.Invalid)
finalized_root = hash_tree_root(update.finalized_header.beacon)
elif update.finalized_header == altair.LightClientHeader():
finalized_root.reset()
else:
return err(VerifierError.Invalid)
@ -104,14 +110,14 @@ proc validate_light_client_update*(
update.finality_branch,
log2trunc(altair.FINALIZED_ROOT_INDEX),
get_subtree_index(altair.FINALIZED_ROOT_INDEX),
update.attested_header.state_root):
update.attested_header.beacon.state_root):
return err(VerifierError.Invalid)
# Verify that the `next_sync_committee`, if present, actually is the
# next sync committee saved in the state of the `attested_header`
when update is SomeLightClientUpdateWithSyncCommittee:
if not is_sync_committee_update:
if not update.next_sync_committee.isZeroMemory:
if update.next_sync_committee != altair.SyncCommittee():
return err(VerifierError.Invalid)
else:
if attested_period == store_period and is_next_sync_committee_known:
@ -122,7 +128,7 @@ proc validate_light_client_update*(
update.next_sync_committee_branch,
log2trunc(altair.NEXT_SYNC_COMMITTEE_INDEX),
get_subtree_index(altair.NEXT_SYNC_COMMITTEE_INDEX),
update.attested_header.state_root):
update.attested_header.beacon.state_root):
return err(VerifierError.Invalid)
# Verify sync committee aggregate signature
@ -140,7 +146,7 @@ proc validate_light_client_update*(
fork_version = cfg.forkVersionAtEpoch(update.signature_slot.epoch)
domain = compute_domain(
DOMAIN_SYNC_COMMITTEE, fork_version, genesis_validators_root)
signing_root = compute_signing_root(update.attested_header, domain)
signing_root = compute_signing_root(update.attested_header.beacon, domain)
if not blsFastAggregateVerify(
participant_pubkeys, signing_root.data,
sync_aggregate.sync_committee_signature):
@ -154,8 +160,8 @@ func apply_light_client_update(
update: SomeLightClientUpdate): bool =
var didProgress = false
let
store_period = store.finalized_header.slot.sync_committee_period
finalized_period = update.finalized_header.slot.sync_committee_period
store_period = store.finalized_header.beacon.slot.sync_committee_period
finalized_period = update.finalized_header.beacon.slot.sync_committee_period
if not store.is_next_sync_committee_known:
assert finalized_period == store_period
when update is SomeLightClientUpdateWithSyncCommittee:
@ -172,9 +178,9 @@ func apply_light_client_update(
store.current_max_active_participants
store.current_max_active_participants = 0
didProgress = true
if update.finalized_header.slot > store.finalized_header.slot:
if update.finalized_header.beacon.slot > store.finalized_header.beacon.slot:
store.finalized_header = update.finalized_header
if store.finalized_header.slot > store.optimistic_header.slot:
if store.finalized_header.beacon.slot > store.optimistic_header.beacon.slot:
store.optimistic_header = store.finalized_header
didProgress = true
didProgress
@ -191,10 +197,15 @@ func process_light_client_store_force_update*(
current_slot: Slot): ForceUpdateResult {.discardable.} =
var res = NoUpdate
if store.best_valid_update.isSome and
current_slot > store.finalized_header.slot + UPDATE_TIMEOUT:
current_slot > store.finalized_header.beacon.slot + UPDATE_TIMEOUT:
# Forced best update when the update timeout has elapsed
# Because the apply logic waits for `finalized_header.beacon.slot`
# to indicate sync committee finality, the `attested_header` may be
# treated as `finalized_header` in extended periods of non-finality
# to guarantee progression into later sync committee periods according
# to `is_better_update`.
template best(): auto = store.best_valid_update.get
if best.finalized_header.slot <= store.finalized_header.slot:
if best.finalized_header.beacon.slot <= store.finalized_header.beacon.slot:
best.finalized_header = best.attested_header
if apply_light_client_update(store, best):
template sync_aggregate(): auto = best.sync_aggregate
@ -235,7 +246,7 @@ proc process_light_client_update*(
# Update the optimistic header
if num_active_participants > get_safety_threshold(store) and
update.attested_header.slot > store.optimistic_header.slot:
update.attested_header.beacon.slot > store.optimistic_header.beacon.slot:
store.optimistic_header = update.attested_header
didProgress = true
@ -243,13 +254,13 @@ proc process_light_client_update*(
when update is SomeLightClientUpdateWithFinality:
if num_active_participants * 3 >= static(sync_committee_bits.len * 2):
var improvesFinality =
update.finalized_header.slot > store.finalized_header.slot
update.finalized_header.beacon.slot > store.finalized_header.beacon.slot
when update is SomeLightClientUpdateWithSyncCommittee:
if not improvesFinality and not store.is_next_sync_committee_known:
improvesFinality =
update.is_sync_committee_update and update.is_finality_update and
update.finalized_header.slot.sync_committee_period ==
update.attested_header.slot.sync_committee_period
update.finalized_header.beacon.slot.sync_committee_period ==
update.attested_header.beacon.slot.sync_committee_period
if improvesFinality:
# Normal update through 2/3 threshold
if apply_light_client_update(store, update):

View File

@ -54,8 +54,8 @@ proc dump*(dir: string, v: ForkyLightClientBootstrap) =
logErrors:
let
prefix = "bootstrap"
slot = v.header.slot
blck = shortLog(v.header.hash_tree_root())
slot = v.header.beacon.slot
blck = shortLog(v.header.beacon.hash_tree_root())
root = shortLog(v.hash_tree_root())
SSZ.saveFile(
dir / &"{prefix}-{slot}-{blck}-{root}.ssz", v)
@ -70,8 +70,8 @@ proc dump*(dir: string, v: SomeLightClientUpdate) =
"finality-update"
elif v is ForkyLightClientOptimisticUpdate:
"optimistic-update"
attestedSlot = v.attested_header.slot
attestedBlck = shortLog(v.attested_header.hash_tree_root())
attestedSlot = v.attested_header.beacon.slot
attestedBlck = shortLog(v.attested_header.beacon.hash_tree_root())
syncCommitteeSuffix =
when v is SomeForkyLightClientUpdateWithSyncCommittee:
if v.is_sync_committee_update:

View File

@ -148,7 +148,8 @@ proc doRequest(
withForkyUpdate(update):
when lcDataFork >= LightClientDataFork.Altair:
let
attPeriod = forkyUpdate.attested_header.slot.sync_committee_period
attPeriod =
forkyUpdate.attested_header.beacon.slot.sync_committee_period
sigPeriod = forkyUpdate.signature_slot.sync_committee_period
if attPeriod != sigPeriod:
raise newException(

View File

@ -191,7 +191,7 @@ proc handleLightClientUpdates*(node: BeaconNode, slot: Slot) {.async.} =
if num_active_participants < MIN_SYNC_COMMITTEE_PARTICIPANTS:
return
let finalized_slot = forkyFinalityUpdate.finalized_header.slot
let finalized_slot = forkyFinalityUpdate.finalized_header.beacon.slot
if finalized_slot > node.lightClientPool[].latestForwardedFinalitySlot:
template msg(): auto = forkyFinalityUpdate
let sendResult =
@ -207,7 +207,7 @@ proc handleLightClientUpdates*(node: BeaconNode, slot: Slot) {.async.} =
warn "LC finality update failed to send",
error = sendResult.error()
let attested_slot = forkyFinalityUpdate.attested_header.slot
let attested_slot = forkyFinalityUpdate.attested_header.beacon.slot
if attested_slot > node.lightClientPool[].latestForwardedOptimisticSlot:
let msg = forkyFinalityUpdate.toOptimistic
let sendResult =

View File

@ -153,21 +153,21 @@ INF 2022-11-21 18:01:24.734+01:00 Exchanged engine configuration top
INF 2022-11-21 18:02:48.001+01:00 Slot start slot=1109714 epoch=34678 sync=bootstrapping(c092a1d110a1c8d630ac2c3fa2565813d43087f42c986855a2cd985b995a328c) peers=6 head=fb9b64fe:0 finalized=fb9b64fe:0 delay=1ms161us
WRN 2022-11-21 18:02:53.603+01:00 Peer count low, no new peers discovered topics="networking" discovered_nodes=1 new_peers=@[] current_peers=6 wanted_peers=160
INF 2022-11-21 18:03:00.001+01:00 Slot start slot=1109715 epoch=34678 sync=bootstrapping(c092a1d110a1c8d630ac2c3fa2565813d43087f42c986855a2cd985b995a328c) peers=5 head=fb9b64fe:0 finalized=fb9b64fe:0 delay=1ms154us
INF 2022-11-21 18:03:09.989+01:00 New LC optimistic header optimistic_header="(slot: 1109216, proposer_index: 1813, parent_root: \"0871af30\", state_root: \"5c0afc98\")"
INF 2022-11-21 18:03:09.989+01:00 New LC finalized header finalized_header="(slot: 1109216, proposer_index: 1813, parent_root: \"0871af30\", state_root: \"5c0afc98\")"
INF 2022-11-21 18:03:09.989+01:00 New LC optimistic header optimistic_header="(beacon: (slot: 1109216, proposer_index: 1813, parent_root: \"0871af30\", state_root: \"5c0afc98\"))"
INF 2022-11-21 18:03:09.989+01:00 New LC finalized header finalized_header="(beacon: (slot: 1109216, proposer_index: 1813, parent_root: \"0871af30\", state_root: \"5c0afc98\"))"
INF 2022-11-21 18:03:12.001+01:00 Slot start slot=1109716 epoch=34678 sync=syncing peers=6 head=c092a1d1:1109216 finalized=c092a1d1:1109216 delay=1ms159us
INF 2022-11-21 18:03:16.047+01:00 New LC optimistic header optimistic_header="(slot: 1109715, proposer_index: 262, parent_root: \"676f4fe4\", state_root: \"2d13aa42\")"
INF 2022-11-21 18:03:16.047+01:00 New LC optimistic header optimistic_header="(beacon: (slot: 1109715, proposer_index: 262, parent_root: \"676f4fe4\", state_root: \"2d13aa42\"))"
INF 2022-11-21 18:03:24.001+01:00 Slot start slot=1109717 epoch=34678 sync=synced peers=7 head=58cae92a:1109715 finalized=c092a1d1:1109216 delay=1ms120us
INF 2022-11-21 18:03:27.984+01:00 New LC optimistic header optimistic_header="(slot: 1109716, proposer_index: 1281, parent_root: \"58cae92a\", state_root: \"de464f71\")"
INF 2022-11-21 18:03:27.984+01:00 New LC optimistic header optimistic_header="(beacon: (slot: 1109716, proposer_index: 1281, parent_root: \"58cae92a\", state_root: \"de464f71\"))"
WRN 2022-11-21 18:03:31.419+01:00 Peer count low, no new peers discovered topics="networking" discovered_nodes=0 new_peers=@[] current_peers=7 wanted_peers=160
INF 2022-11-21 18:03:36.001+01:00 Slot start slot=1109718 epoch=34678 sync=synced peers=7 head=c5464508:1109716 finalized=c092a1d1:1109216 delay=1ms98us
INF 2022-11-21 18:03:40.012+01:00 New LC optimistic header optimistic_header="(slot: 1109717, proposer_index: 835, parent_root: \"c5464508\", state_root: \"13f823f8\")"
INF 2022-11-21 18:03:40.012+01:00 New LC optimistic header optimistic_header="(beacon: (slot: 1109717, proposer_index: 835, parent_root: \"c5464508\", state_root: \"13f823f8\"))"
NOT 2022-11-21 18:03:40.012+01:00 New LC optimistic block opt=99ab28aa:1109717 wallSlot=1109718
WRN 2022-11-21 18:03:40.422+01:00 Peer count low, no new peers discovered topics="networking" discovered_nodes=1 new_peers=@[] current_peers=7 wanted_peers=160
INF 2022-11-21 18:03:48.001+01:00 Slot start slot=1109719 epoch=34678 sync=synced peers=7 head=99ab28aa:1109717 finalized=c092a1d1:1109216 delay=1ms53us
WRN 2022-11-21 18:03:50.205+01:00 Peer count low, no new peers discovered topics="networking" discovered_nodes=0 new_peers=@[] current_peers=7 wanted_peers=160
INF 2022-11-21 18:04:00.001+01:00 Slot start slot=1109720 epoch=34678 sync=synced peers=7 head=99ab28aa:1109717 finalized=c092a1d1:1109216 delay=1ms145us
INF 2022-11-21 18:04:03.982+01:00 New LC optimistic header optimistic_header="(slot: 1109718, proposer_index: 1202, parent_root: \"99ab28aa\", state_root: \"7f7f88d2\")"
INF 2022-11-21 18:04:03.982+01:00 New LC optimistic header optimistic_header="(beacon: (slot: 1109718, proposer_index: 1202, parent_root: \"99ab28aa\", state_root: \"7f7f88d2\"))"
NOT 2022-11-21 18:04:03.982+01:00 New LC optimistic block opt=ab007266:1109718 wallSlot=1109720
```

View File

@ -1,5 +1,5 @@
# beacon_chain
# Copyright (c) 2021-2022 Status Research & Development GmbH
# Copyright (c) 2021-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).
@ -140,11 +140,11 @@ let full_sync_committee_bits = block:
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-alpha.2/tests/core/pyspec/eth2spec/test/helpers/light_client.py#L20-L29
func initialize_light_client_store(state: auto): LightClientStore =
LightClientStore(
finalized_header: BeaconBlockHeader(),
finalized_header: altair.LightClientHeader(),
current_sync_committee: state.current_sync_committee,
next_sync_committee: state.next_sync_committee,
best_valid_update: Opt.none(altair.LightClientUpdate),
optimistic_header: BeaconBlockHeader(),
optimistic_header: altair.LightClientHeader(),
previous_max_active_participants: 0,
current_max_active_participants: 0,
)
@ -162,13 +162,14 @@ suite "EF - Altair - Unittests - Light client - Sync protocol" & preset():
let forked = assignClone(genesisState[])
template state(): auto = forked[].altairData.data
var store = initialize_light_client_store(state)
const lcDataFork = typeof(store).kind
# Block at slot 1 doesn't increase sync committee period,
# so it won't update snapshot
var cache: StateCache
let
attested_block = block_for_next_slot(cfg, forked[], cache).altairData
attested_header = attested_block.toBeaconBlockHeader
attested_header = attested_block.toLightClientHeader(lcDataFork)
# Sync committee signing the attested_header
(sync_aggregate, signature_slot) = get_sync_aggregate(cfg, forked[])
@ -179,7 +180,7 @@ suite "EF - Altair - Unittests - Light client - Sync protocol" & preset():
check state.finalized_checkpoint.epoch == 0
# Finality is unchanged
let
finality_header = BeaconBlockHeader()
finality_header = altair.LightClientHeader()
finality_branch = default(altair.FinalityBranch)
update = altair.LightClientUpdate(
@ -208,6 +209,7 @@ suite "EF - Altair - Unittests - Light client - Sync protocol" & preset():
var forked = assignClone(genesisState[])
template state(): auto = forked[].altairData.data
var store = initialize_light_client_store(state)
const lcDataFork = typeof(store).kind
# Forward to slot before next sync committee period so that next block is
# final one in period
@ -218,13 +220,13 @@ suite "EF - Altair - Unittests - Light client - Sync protocol" & preset():
cfg, forked[], Slot(UPDATE_TIMEOUT - 2), cache, info, flags = {}
).expect("no failure")
let
store_period = sync_committee_period(store.optimistic_header.slot)
store_period = sync_committee_period(store.optimistic_header.beacon.slot)
update_period = sync_committee_period(state.slot)
check: store_period == update_period
let
attested_block = block_for_next_slot(cfg, forked[], cache).altairData
attested_header = attested_block.toBeaconBlockHeader
attested_header = attested_block.toLightClientHeader(lcDataFork)
# Sync committee signing the attested_header
(sync_aggregate, signature_slot) = get_sync_aggregate(cfg, forked[])
@ -232,7 +234,7 @@ suite "EF - Altair - Unittests - Light client - Sync protocol" & preset():
next_sync_committee_branch = default(altair.NextSyncCommitteeBranch)
# Finality is unchanged
finality_header = BeaconBlockHeader()
finality_header = altair.LightClientHeader()
finality_branch = default(altair.FinalityBranch)
update = altair.LightClientUpdate(
@ -261,6 +263,7 @@ suite "EF - Altair - Unittests - Light client - Sync protocol" & preset():
let forked = assignClone(genesisState[])
template state(): auto = forked[].altairData.data
var store = initialize_light_client_store(state)
const lcDataFork = typeof(store).kind
# Forward to next sync committee period
var
@ -270,13 +273,13 @@ suite "EF - Altair - Unittests - Light client - Sync protocol" & preset():
cfg, forked[], Slot(UPDATE_TIMEOUT), cache, info, flags = {}
).expect("no failure")
let
store_period = sync_committee_period(store.optimistic_header.slot)
store_period = sync_committee_period(store.optimistic_header.beacon.slot)
update_period = sync_committee_period(state.slot)
check: store_period + 1 == update_period
let
attested_block = block_for_next_slot(cfg, forked[], cache).altairData
attested_header = attested_block.toBeaconBlockHeader
attested_header = attested_block.toLightClientHeader(lcDataFork)
# Sync committee signing the attested_header
(sync_aggregate, signature_slot) = get_sync_aggregate(cfg, forked[])
@ -288,7 +291,7 @@ suite "EF - Altair - Unittests - Light client - Sync protocol" & preset():
state.build_proof(altair.NEXT_SYNC_COMMITTEE_INDEX).get
# Finality is unchanged
finality_header = BeaconBlockHeader()
finality_header = altair.LightClientHeader()
finality_branch = default(altair.FinalityBranch)
update = altair.LightClientUpdate(
@ -317,6 +320,7 @@ suite "EF - Altair - Unittests - Light client - Sync protocol" & preset():
let forked = assignClone(genesisState[])
template state(): auto = forked[].altairData.data
var store = initialize_light_client_store(state)
const lcDataFork = typeof(store).kind
# Change finality
var
@ -332,13 +336,13 @@ suite "EF - Altair - Unittests - Light client - Sync protocol" & preset():
check: state.finalized_checkpoint.epoch == 3
# Ensure that it's same period
let
store_period = sync_committee_period(store.optimistic_header.slot)
store_period = sync_committee_period(store.optimistic_header.beacon.slot)
update_period = sync_committee_period(state.slot)
check: store_period == update_period
let
attested_block = blocks[^1].altairData
attested_header = attested_block.toBeaconBlockHeader
attested_header = attested_block.toLightClientHeader(lcDataFork)
# Sync committee signing the attested_header
(sync_aggregate, signature_slot) = get_sync_aggregate(cfg, forked[])
@ -347,10 +351,12 @@ suite "EF - Altair - Unittests - Light client - Sync protocol" & preset():
next_sync_committee = SyncCommittee()
next_sync_committee_branch = default(altair.NextSyncCommitteeBranch)
finalized_block = blocks[SLOTS_PER_EPOCH - 1].altairData
finalized_header = finalized_block.toBeaconBlockHeader
finalized_header = finalized_block.toLightClientHeader(lcDataFork)
check:
finalized_header.slot == start_slot(state.finalized_checkpoint.epoch)
finalized_header.hash_tree_root() == state.finalized_checkpoint.root
finalized_header.beacon.slot ==
start_slot(state.finalized_checkpoint.epoch)
finalized_header.beacon.hash_tree_root() ==
state.finalized_checkpoint.root
let
finality_branch = state.build_proof(altair.FINALIZED_ROOT_INDEX).get

View File

@ -1,5 +1,5 @@
# beacon_chain
# Copyright (c) 2022 Status Research & Development GmbH
# 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).
@ -110,11 +110,14 @@ proc runTest(path: string) =
store, step.update, step.current_slot,
cfg, genesis_validators_root)
check res.isOk
let
finalized_root = hash_tree_root(store.finalized_header.beacon)
optimistic_root = hash_tree_root(store.optimistic_header.beacon)
check:
store.finalized_header.slot == step.checks.finalized_slot
hash_tree_root(store.finalized_header) == step.checks.finalized_root
store.optimistic_header.slot == step.checks.optimistic_slot
hash_tree_root(store.optimistic_header) == step.checks.optimistic_root
store.finalized_header.beacon.slot == step.checks.finalized_slot
finalized_root == step.checks.finalized_root
store.optimistic_header.beacon.slot == step.checks.optimistic_slot
optimistic_root == step.checks.optimistic_root
suite "EF - Light client - Sync" & preset():
const presetPath = SszTestsDir/const_preset

View File

@ -158,22 +158,25 @@ suite "Light client" & preset():
# Sync to latest sync committee period
var numIterations = 0
while store.finalized_header.slot.sync_committee_period + 1 < headPeriod:
template storePeriod: SyncCommitteePeriod =
store.finalized_header.beacon.slot.sync_committee_period
while storePeriod + 1 < headPeriod:
let
period =
if store.is_next_sync_committee_known:
store.finalized_header.slot.sync_committee_period + 1
storePeriod + 1
else:
store.finalized_header.slot.sync_committee_period
storePeriod
update = dag.getLightClientUpdateForPeriod(period)
check update.kind == storeDataFork
template forkyUpdate: untyped = update.forky(storeDataFork)
let res = process_light_client_update(
store, forkyUpdate, currentSlot, cfg, genesis_validators_root)
check:
forkyUpdate.finalized_header.slot.sync_committee_period == period
forkyUpdate.finalized_header.beacon.slot.sync_committee_period == period
res.isOk
if forkyUpdate.finalized_header.slot > forkyBootstrap.header.slot:
if forkyUpdate.finalized_header.beacon.slot >
forkyBootstrap.header.beacon.slot:
store.finalized_header == forkyUpdate.finalized_header
else:
store.finalized_header == forkyBootstrap.header
@ -187,7 +190,7 @@ suite "Light client" & preset():
let res = process_light_client_update(
store, forkyFinalityUpdate, currentSlot, cfg, genesis_validators_root)
check:
forkyFinalityUpdate.attested_header.slot == dag.head.parent.slot
forkyFinalityUpdate.attested_header.beacon.slot == dag.head.parent.slot
res.isOk
store.finalized_header == forkyFinalityUpdate.finalized_header
store.optimistic_header == forkyFinalityUpdate.attested_header

View File

@ -113,7 +113,7 @@ suite "Light client processor" & preset():
bootstrap.kind <= storeDataFork
let upgradedBootstrap = bootstrap.migratingToDataFork(storeDataFork)
template forkyBootstrap: untyped = upgradedBootstrap.forky(storeDataFork)
setTimeToSlot(forkyBootstrap.header.slot)
setTimeToSlot(forkyBootstrap.header.beacon.slot)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), bootstrap)
check:
@ -135,7 +135,8 @@ suite "Light client processor" & preset():
check:
res.isOk
store[].isSome
if forkyUpdate.finalized_header.slot > forkyBootstrap.header.slot:
if forkyUpdate.finalized_header.beacon.slot >
forkyBootstrap.header.beacon.slot:
store[].get.finalized_header == forkyUpdate.finalized_header
else:
store[].get.finalized_header == forkyBootstrap.header
@ -274,10 +275,10 @@ suite "Light client processor" & preset():
bootstrap.kind <= storeDataFork
withForkyBootstrap(bootstrap):
when lcDataFork >= LightClientDataFork.Altair:
forkyBootstrap.header.slot.inc()
forkyBootstrap.header.beacon.slot.inc()
let upgradedBootstrap = bootstrap.migratingToDataFork(storeDataFork)
template forkyBootstrap: untyped = upgradedBootstrap.forky(storeDataFork)
setTimeToSlot(forkyBootstrap.header.slot)
setTimeToSlot(forkyBootstrap.header.beacon.slot)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), bootstrap)
check:
@ -292,7 +293,7 @@ suite "Light client processor" & preset():
bootstrap.kind <= storeDataFork
let upgradedBootstrap = bootstrap.migratingToDataFork(storeDataFork)
template forkyBootstrap: untyped = upgradedBootstrap.forky(storeDataFork)
setTimeToSlot(forkyBootstrap.header.slot)
setTimeToSlot(forkyBootstrap.header.beacon.slot)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), bootstrap)
check: