diff --git a/beacon_chain/beacon_chain_db_light_client.nim b/beacon_chain/beacon_chain_db_light_client.nim index 0cb1cac0b..1945d1571 100644 --- a/beacon_chain/beacon_chain_db_light_client.nim +++ b/beacon_chain/beacon_chain_db_light_client.nim @@ -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. # diff --git a/beacon_chain/beacon_node_light_client.nim b/beacon_chain/beacon_node_light_client.nim index b77f16a4f..d48afc0e2 100644 --- a/beacon_chain/beacon_node_light_client.nim +++ b/beacon_chain/beacon_node_light_client.nim @@ -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: diff --git a/beacon_chain/consensus_object_pools/blockchain_dag_light_client.nim b/beacon_chain/consensus_object_pools/blockchain_dag_light_client.nim index a6cd98c05..c62e8b545 100644 --- a/beacon_chain/consensus_object_pools/blockchain_dag_light_client.nim +++ b/beacon_chain/consensus_object_pools/blockchain_dag_light_client.nim @@ -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: diff --git a/beacon_chain/consensus_object_pools/light_client_pool.nim b/beacon_chain/consensus_object_pools/light_client_pool.nim index d68d9fa03..243077411 100644 --- a/beacon_chain/consensus_object_pools/light_client_pool.nim +++ b/beacon_chain/consensus_object_pools/light_client_pool.nim @@ -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. diff --git a/beacon_chain/gossip_processing/gossip_validation.nim b/beacon_chain/gossip_processing/gossip_validation.nim index 9f1cc865c..82b8c40b8 100644 --- a/beacon_chain/gossip_processing/gossip_validation.nim +++ b/beacon_chain/gossip_processing/gossip_validation.nim @@ -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") diff --git a/beacon_chain/gossip_processing/light_client_processor.nim b/beacon_chain/gossip_processing/light_client_processor.nim index a64555846..bd97a7a3d 100644 --- a/beacon_chain/gossip_processing/light_client_processor.nim +++ b/beacon_chain/gossip_processing/light_client_processor.nim @@ -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: diff --git a/beacon_chain/light_client.nim b/beacon_chain/light_client.nim index b3e40d003..9627435f1 100644 --- a/beacon_chain/light_client.nim +++ b/beacon_chain/light_client.nim @@ -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) diff --git a/beacon_chain/light_client_db.nim b/beacon_chain/light_client_db.nim index df6cd07ee..83d3c0fe3 100644 --- a/beacon_chain/light_client_db.nim +++ b/beacon_chain/light_client_db.nim @@ -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") diff --git a/beacon_chain/nimbus_light_client.nim b/beacon_chain/nimbus_light_client.nim index 54e514bc3..801f168b0 100644 --- a/beacon_chain/nimbus_light_client.nim +++ b/beacon_chain/nimbus_light_client.nim @@ -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) diff --git a/beacon_chain/spec/datatypes/altair.nim b/beacon_chain/spec/datatypes/altair.nim index 79abfb83d..509c320b7 100644 --- a/beacon_chain/spec/datatypes/altair.nim +++ b/beacon_chain/spec/datatypes/altair.nim @@ -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) diff --git a/beacon_chain/spec/forks_light_client.nim b/beacon_chain/spec/forks_light_client.nim index d429d4a0a..1b7466254 100644 --- a/beacon_chain/spec/forks_light_client.nim +++ b/beacon_chain/spec/forks_light_client.nim @@ -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()) diff --git a/beacon_chain/spec/helpers.nim b/beacon_chain/spec/helpers.nim index 53aa903ab..232b5174b 100644 --- a/beacon_chain/spec/helpers.nim +++ b/beacon_chain/spec/helpers.nim @@ -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*( diff --git a/beacon_chain/spec/light_client_sync.nim b/beacon_chain/spec/light_client_sync.nim index 9204d05d6..2bd26c006 100644 --- a/beacon_chain/spec/light_client_sync.nim +++ b/beacon_chain/spec/light_client_sync.nim @@ -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): diff --git a/beacon_chain/sszdump.nim b/beacon_chain/sszdump.nim index 7e74fdd35..88713201d 100644 --- a/beacon_chain/sszdump.nim +++ b/beacon_chain/sszdump.nim @@ -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: diff --git a/beacon_chain/sync/light_client_manager.nim b/beacon_chain/sync/light_client_manager.nim index 7286a6f47..4fb4542b0 100644 --- a/beacon_chain/sync/light_client_manager.nim +++ b/beacon_chain/sync/light_client_manager.nim @@ -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( diff --git a/beacon_chain/validators/validator_duties.nim b/beacon_chain/validators/validator_duties.nim index 129f78bc8..8c0b73b63 100644 --- a/beacon_chain/validators/validator_duties.nim +++ b/beacon_chain/validators/validator_duties.nim @@ -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 = diff --git a/docs/the_nimbus_book/src/el-light-client.md b/docs/the_nimbus_book/src/el-light-client.md index 9e6f34eed..48f7801eb 100644 --- a/docs/the_nimbus_book/src/el-light-client.md +++ b/docs/the_nimbus_book/src/el-light-client.md @@ -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 ``` diff --git a/tests/consensus_spec/altair/test_fixture_light_client_sync_protocol.nim b/tests/consensus_spec/altair/test_fixture_light_client_sync_protocol.nim index 0a77e1af3..d3d385cf1 100644 --- a/tests/consensus_spec/altair/test_fixture_light_client_sync_protocol.nim +++ b/tests/consensus_spec/altair/test_fixture_light_client_sync_protocol.nim @@ -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 diff --git a/tests/consensus_spec/test_fixture_light_client_sync.nim b/tests/consensus_spec/test_fixture_light_client_sync.nim index f3dd90c53..fa1043af1 100644 --- a/tests/consensus_spec/test_fixture_light_client_sync.nim +++ b/tests/consensus_spec/test_fixture_light_client_sync.nim @@ -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 diff --git a/tests/test_light_client.nim b/tests/test_light_client.nim index 8004df706..66334bd6d 100644 --- a/tests/test_light_client.nim +++ b/tests/test_light_client.nim @@ -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 diff --git a/tests/test_light_client_processor.nim b/tests/test_light_client_processor.nim index 741331974..d0e697bfe 100644 --- a/tests/test_light_client_processor.nim +++ b/tests/test_light_client_processor.nim @@ -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: