Add LightClient(Finality|Optimistic)Update

Introduces reduced `LightClientUpdate` structures to allow keeping track
of the latest `finalized_header` and `optimistic_header`. This may also
help in scheduling the next query for a full `LightClientUpdate` once
sync committee finality has been reached.
This commit is contained in:
Etan Kissling 2022-05-03 12:15:04 +02:00
parent 1d2ef9f8cb
commit 06d9fd8cd3
No known key found for this signature in database
GPG Key ID: B21DA824C5A3D03D
2 changed files with 100 additions and 1 deletions

View File

@ -14,6 +14,8 @@
- [Deriving light client data](#deriving-light-client-data)
- [`create_light_client_bootstrap`](#create_light_client_bootstrap)
- [`create_light_client_update`](#create_light_client_update)
- [`create_light_client_finality_update`](#create_light_client_finality_update)
- [`create_light_client_optimistic_update`](#create_light_client_optimistic_update)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
@ -133,3 +135,31 @@ Full nodes SHOULD provide the best derivable `LightClientUpdate` (according to `
- `LightClientUpdate` are assigned to sync committee periods based on their `attested_header.slot`
- `LightClientUpdate` are only considered if `compute_sync_committee_period(compute_epoch_at_slot(update.attested_header.slot)) == compute_sync_committee_period(compute_epoch_at_slot(update.signature_slot))`
- Only `LightClientUpdate` with `next_sync_committee` as selected by fork choice are provided, regardless of ranking by `is_better_update`. To uniquely identify a non-finalized sync committee fork, all of `period`, `current_sync_committee` and `next_sync_committee` need to be incorporated, as sync committees may reappear over time.
### `create_light_client_finality_update`
```python
def create_light_client_finality_update(update: LightClientUpdate) -> LightClientFinalityUpdate:
return LightClientFinalityUpdate(
attested_header=update.attested_header,
finalized_header=update.finalized_header,
finality_branch=update.finality_branch,
sync_aggregate=update.sync_aggregate,
signature_slot=update.signature_slot,
)
```
Full nodes SHOULD provide the `LightClientFinalityUpdate` with the highest `attested_header.slot` (if multiple, highest `signature_slot`) as selected by fork choice, and SHOULD support a push mechanism to deliver new `LightClientFinalityUpdate` whenever `finalized_header` changes.
### `create_light_client_optimistic_update`
```python
def create_light_client_optimistic_update(update: LightClientUpdate) -> LightClientOptimisticUpdate:
return LightClientOptimisticUpdate(
attested_header=update.attested_header,
sync_aggregate=update.sync_aggregate,
signature_slot=update.signature_slot,
)
```
Full nodes SHOULD provide the `LightClientOptimisticUpdate` with the highest `attested_header.slot` (if multiple, highest `signature_slot`) as selected by fork choice, and SHOULD support a push mechanism to deliver new `LightClientOptimisticUpdate` whenever `attested_header` changes.

View File

@ -15,6 +15,8 @@
- [Containers](#containers)
- [`LightClientBootstrap`](#lightclientbootstrap)
- [`LightClientUpdate`](#lightclientupdate)
- [`LightClientFinalityUpdate`](#lightclientfinalityupdate)
- [`LightClientOptimisticUpdate`](#lightclientoptimisticupdate)
- [`LightClientStore`](#lightclientstore)
- [Helper functions](#helper-functions)
- [`is_sync_committee_update`](#is_sync_committee_update)
@ -31,6 +33,8 @@
- [`validate_light_client_update`](#validate_light_client_update)
- [`apply_light_client_update`](#apply_light_client_update)
- [`process_light_client_update`](#process_light_client_update)
- [`process_light_client_finality_update`](#process_light_client_finality_update)
- [`process_light_client_optimistic_update`](#process_light_client_optimistic_update)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
@ -96,6 +100,33 @@ class LightClientUpdate(Container):
signature_slot: Slot
```
### `LightClientFinalityUpdate`
```python
class LightClientFinalityUpdate(Container):
# The beacon block header that is attested to by the sync committee
attested_header: BeaconBlockHeader
# The finalized beacon block header attested to by Merkle branch
finalized_header: BeaconBlockHeader
finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)]
# Sync committee aggregate signature
sync_aggregate: SyncAggregate
# Slot at which the aggregate signature was created (untrusted)
signature_slot: Slot
```
### `LightClientOptimisticUpdate`
```python
class LightClientOptimisticUpdate(Container):
# The beacon block header that is attested to by the sync committee
attested_header: BeaconBlockHeader
# Sync committee aggregate signature
sync_aggregate: SyncAggregate
# Slot at which the aggregate signature was created (untrusted)
signature_slot: Slot
```
### `LightClientStore`
```python
@ -250,7 +281,7 @@ def initialize_light_client_store(trusted_block_root: Root,
## Light client state updates
A light client receives `update` objects of type `LightClientUpdate`. Every `update` triggers `process_light_client_update(store, update, current_slot, genesis_validators_root)` where `current_slot` is the current slot based on a local clock. `process_slot_for_light_client_store` is triggered every time the current slot increments.
A light client receives `update`, `finality_update` and `optimistic_update` objects of type `LightClientUpdate`, `LightClientFinalityUpdate` and `LightClientOptimisticUpdate`. Every `update` triggers `process_light_client_update(store, update, current_slot, genesis_validators_root)` where `current_slot` is the current slot based on a local clock. Likewise, every `finality_update` triggers `process_light_client_finality_update`, and every `optimistic_update` triggers `process_light_client_optimistic_update`. `process_slot_for_light_client_store` is triggered every time the current slot increments.
### `process_slot_for_light_client_store`
@ -420,3 +451,41 @@ def process_light_client_update(store: LightClientStore,
apply_light_client_update(store, update)
store.best_valid_update = None
```
### `process_light_client_finality_update`
```python
def process_light_client_finality_update(store: LightClientStore,
finality_update: LightClientFinalityUpdate,
current_slot: Slot,
genesis_validators_root: Root) -> None:
update = LightClientUpdate(
attested_header=finality_update.attested_header,
next_sync_committee=SyncCommittee(),
next_sync_committee_branch=[Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))],
finalized_header=finality_update.finalized_header,
finality_branch=finality_update.finality_branch,
sync_aggregate=finality_update.sync_aggregate,
signature_slot=finality_update.signature_slot,
)
process_light_client_update(store, update, current_slot, genesis_validators_root)
```
### `process_light_client_optimistic_update`
```python
def process_light_client_optimistic_update(store: LightClientStore,
optimistic_update: LightClientOptimisticUpdate,
current_slot: Slot,
genesis_validators_root: Root) -> None:
update = LightClientUpdate(
attested_header=optimistic_update.attested_header,
next_sync_committee=SyncCommittee(),
next_sync_committee_branch=[Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))],
finalized_header=BeaconBlockHeader(),
finality_branch=[Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))],
sync_aggregate=optimistic_update.sync_aggregate,
signature_slot=optimistic_update.signature_slot,
)
process_light_client_update(store, update, current_slot, genesis_validators_root)
```