2022-07-15 11:37:27 +00:00
# Altair Light Client -- Sync Protocol
2020-11-16 07:02:11 +00:00
**Notice**: This document is a work-in-progress for researchers and implementers.
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE - RUN doctoc TO UPDATE -->
- [Introduction ](#introduction )
2024-01-09 13:55:22 +00:00
- [Custom types ](#custom-types )
2020-11-16 07:02:11 +00:00
- [Constants ](#constants )
2021-05-06 13:52:52 +00:00
- [Preset ](#preset )
2020-11-17 12:42:09 +00:00
- [Misc ](#misc )
2020-11-16 07:02:11 +00:00
- [Containers ](#containers )
2023-01-05 12:36:04 +00:00
- [`LightClientHeader` ](#lightclientheader )
2022-05-02 21:24:26 +00:00
- [`LightClientBootstrap` ](#lightclientbootstrap )
2021-03-11 06:27:23 +00:00
- [`LightClientUpdate` ](#lightclientupdate )
2022-05-03 10:15:04 +00:00
- [`LightClientFinalityUpdate` ](#lightclientfinalityupdate )
- [`LightClientOptimisticUpdate` ](#lightclientoptimisticupdate )
2021-03-11 06:27:23 +00:00
- [`LightClientStore` ](#lightclientstore )
2020-12-10 09:04:11 +00:00
- [Helper functions ](#helper-functions )
2024-06-21 08:59:13 +00:00
- [`finalized_root_gindex_at_slot` ](#finalized_root_gindex_at_slot )
- [`current_sync_committee_gindex_at_slot` ](#current_sync_committee_gindex_at_slot )
- [`next_sync_committee_gindex_at_slot` ](#next_sync_committee_gindex_at_slot )
2023-01-06 19:01:02 +00:00
- [`is_valid_light_client_header` ](#is_valid_light_client_header )
2022-04-27 14:07:49 +00:00
- [`is_sync_committee_update` ](#is_sync_committee_update )
2022-02-10 01:06:10 +00:00
- [`is_finality_update` ](#is_finality_update )
2022-04-29 14:22:26 +00:00
- [`is_better_update` ](#is_better_update )
2022-05-02 21:24:26 +00:00
- [`is_next_sync_committee_known` ](#is_next_sync_committee_known )
2022-07-09 04:46:34 +00:00
- [`get_safety_threshold` ](#get_safety_threshold )
2022-07-15 09:54:47 +00:00
- [`get_subtree_index` ](#get_subtree_index )
2024-06-21 08:59:13 +00:00
- [`is_valid_normalized_merkle_branch` ](#is_valid_normalized_merkle_branch )
2022-07-15 11:49:19 +00:00
- [`compute_sync_committee_period_at_slot` ](#compute_sync_committee_period_at_slot )
2022-05-02 21:24:26 +00:00
- [Light client initialization ](#light-client-initialization )
- [`initialize_light_client_store` ](#initialize_light_client_store )
2020-11-16 07:02:11 +00:00
- [Light client state updates ](#light-client-state-updates )
2022-05-02 21:24:26 +00:00
- [`validate_light_client_update` ](#validate_light_client_update )
- [`apply_light_client_update` ](#apply_light_client_update )
2022-07-22 09:49:07 +00:00
- [`process_light_client_store_force_update` ](#process_light_client_store_force_update )
2022-05-02 21:24:26 +00:00
- [`process_light_client_update` ](#process_light_client_update )
2022-05-03 10:15:04 +00:00
- [`process_light_client_finality_update` ](#process_light_client_finality_update )
- [`process_light_client_optimistic_update` ](#process_light_client_optimistic_update )
2020-11-16 07:02:11 +00:00
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
2021-08-18 23:11:38 +00:00
The beacon chain is designed to be light client friendly for constrained environments to
access Ethereum with reasonable safety and liveness.
2022-05-02 21:24:26 +00:00
Such environments include resource-constrained devices (e.g. phones for trust-minimized wallets)
2020-12-07 15:10:39 +00:00
and metered VMs (e.g. blockchain VMs for cross-chain bridges).
2020-11-17 12:42:09 +00:00
2020-12-07 15:10:39 +00:00
This document suggests a minimal light client design for the beacon chain that
2022-09-15 14:21:06 +00:00
uses sync committees introduced in [this beacon chain extension ](../beacon-chain.md ).
2020-11-16 07:02:11 +00:00
2022-07-15 11:37:27 +00:00
Additional documents describe how the light client sync protocol can be used:
- [Full node ](./full-node.md )
2022-05-03 12:21:07 +00:00
- [Light client ](./light-client.md )
2022-05-03 13:30:45 +00:00
- [Networking ](./p2p-interface.md )
2022-07-15 11:37:27 +00:00
2024-01-09 13:55:22 +00:00
## Custom types
| Name | SSZ equivalent | Description |
| - | - | - |
2024-01-15 11:09:28 +00:00
| `FinalityBranch` | `Vector[Bytes32, floorlog2(FINALIZED_ROOT_GINDEX)]` | Merkle branch of `finalized_checkpoint.root` within `BeaconState` |
| `CurrentSyncCommitteeBranch` | `Vector[Bytes32, floorlog2(CURRENT_SYNC_COMMITTEE_GINDEX)]` | Merkle branch of `current_sync_committee` within `BeaconState` |
| `NextSyncCommitteeBranch` | `Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_GINDEX)]` | Merkle branch of `next_sync_committee` within `BeaconState` |
2024-01-09 13:55:22 +00:00
2020-11-16 07:02:11 +00:00
## Constants
| Name | Value |
| - | - |
2023-11-01 12:30:55 +00:00
| `FINALIZED_ROOT_GINDEX` | `get_generalized_index(BeaconState, 'finalized_checkpoint', 'root')` (= 105) |
| `CURRENT_SYNC_COMMITTEE_GINDEX` | `get_generalized_index(BeaconState, 'current_sync_committee')` (= 54) |
| `NEXT_SYNC_COMMITTEE_GINDEX` | `get_generalized_index(BeaconState, 'next_sync_committee')` (= 55) |
2020-11-17 12:42:09 +00:00
2023-12-27 11:54:36 +00:00
## Preset
### Misc
| Name | Value | Unit | Duration |
| - | - | - | - |
| `MIN_SYNC_COMMITTEE_PARTICIPANTS` | `1` | validators | |
| `UPDATE_TIMEOUT` | `SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD` | slots | ~27.3 hours |
2020-11-16 07:02:11 +00:00
## Containers
2023-01-05 12:36:04 +00:00
### `LightClientHeader`
```python
class LightClientHeader(Container):
# Beacon block header
beacon: BeaconBlockHeader
```
2023-01-06 18:58:23 +00:00
Future upgrades may introduce additional fields to this structure, and validate them by extending [`is_valid_light_client_header` ](#is_valid_light_client_header ).
2022-05-02 21:24:26 +00:00
### `LightClientBootstrap`
```python
class LightClientBootstrap(Container):
2022-10-27 19:44:38 +00:00
# Header matching the requested beacon block root
2023-01-05 12:36:04 +00:00
header: LightClientHeader
# Current sync committee corresponding to `header.beacon.state_root`
2022-05-02 21:24:26 +00:00
current_sync_committee: SyncCommittee
2023-12-27 11:42:29 +00:00
current_sync_committee_branch: CurrentSyncCommitteeBranch
2022-05-02 21:24:26 +00:00
```
2021-03-11 06:27:23 +00:00
### `LightClientUpdate`
2020-11-16 07:02:11 +00:00
2020-11-17 12:42:09 +00:00
```python
class LightClientUpdate(Container):
2022-10-27 19:44:38 +00:00
# Header attested to by the sync committee
2023-01-05 12:36:04 +00:00
attested_header: LightClientHeader
# Next sync committee corresponding to `attested_header.beacon.state_root`
2020-11-18 10:33:42 +00:00
next_sync_committee: SyncCommittee
2023-12-27 11:42:29 +00:00
next_sync_committee_branch: NextSyncCommitteeBranch
2023-01-05 12:36:04 +00:00
# Finalized header corresponding to `attested_header.beacon.state_root`
finalized_header: LightClientHeader
2023-12-27 11:42:29 +00:00
finality_branch: FinalityBranch
2020-11-17 12:42:09 +00:00
# Sync committee aggregate signature
2022-01-11 10:22:39 +00:00
sync_aggregate: SyncAggregate
2022-04-26 20:32:25 +00:00
# Slot at which the aggregate signature was created (untrusted)
signature_slot: Slot
2020-11-17 12:42:09 +00:00
```
2022-05-03 10:15:04 +00:00
### `LightClientFinalityUpdate`
```python
class LightClientFinalityUpdate(Container):
2022-10-27 19:44:38 +00:00
# Header attested to by the sync committee
2023-01-05 12:36:04 +00:00
attested_header: LightClientHeader
# Finalized header corresponding to `attested_header.beacon.state_root`
finalized_header: LightClientHeader
2023-12-27 11:42:29 +00:00
finality_branch: FinalityBranch
2022-05-03 10:15:04 +00:00
# Sync committee aggregate signature
sync_aggregate: SyncAggregate
# Slot at which the aggregate signature was created (untrusted)
signature_slot: Slot
```
### `LightClientOptimisticUpdate`
```python
class LightClientOptimisticUpdate(Container):
2022-10-27 19:44:38 +00:00
# Header attested to by the sync committee
2023-01-05 12:36:04 +00:00
attested_header: LightClientHeader
2022-05-03 10:15:04 +00:00
# Sync committee aggregate signature
sync_aggregate: SyncAggregate
# Slot at which the aggregate signature was created (untrusted)
signature_slot: Slot
```
2021-03-11 06:27:23 +00:00
### `LightClientStore`
2020-11-16 07:02:11 +00:00
```python
2021-11-30 12:38:42 +00:00
@dataclass
2021-05-06 17:00:04 +00:00
class LightClientStore(object):
2022-10-27 19:44:38 +00:00
# Header that is finalized
2023-01-05 12:36:04 +00:00
finalized_header: LightClientHeader
2022-10-27 19:44:38 +00:00
# Sync committees corresponding to the finalized header
2021-11-27 13:25:27 +00:00
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
# Best available header to switch finalized head to if we see nothing else
2021-11-26 21:11:19 +00:00
best_valid_update: Optional[LightClientUpdate]
2021-11-27 13:25:27 +00:00
# Most recent available reasonably-safe header
2023-01-05 12:36:04 +00:00
optimistic_header: LightClientHeader
2021-11-29 13:04:05 +00:00
# Max number of active participants in a sync committee (used to calculate safety threshold)
previous_max_active_participants: uint64
current_max_active_participants: uint64
2020-11-16 07:02:11 +00:00
```
2020-12-10 09:04:11 +00:00
## Helper functions
2024-06-21 08:59:13 +00:00
### `finalized_root_gindex_at_slot`
```python
def finalized_root_gindex_at_slot(slot: Slot) -> GeneralizedIndex:
# pylint: disable=unused-argument
return FINALIZED_ROOT_GINDEX
```
### `current_sync_committee_gindex_at_slot`
```python
def current_sync_committee_gindex_at_slot(slot: Slot) -> GeneralizedIndex:
# pylint: disable=unused-argument
return CURRENT_SYNC_COMMITTEE_GINDEX
```
### `next_sync_committee_gindex_at_slot`
```python
def next_sync_committee_gindex_at_slot(slot: Slot) -> GeneralizedIndex:
# pylint: disable=unused-argument
return NEXT_SYNC_COMMITTEE_GINDEX
```
2023-01-06 19:01:02 +00:00
### `is_valid_light_client_header`
```python
def is_valid_light_client_header(header: LightClientHeader) -> bool:
# pylint: disable=unused-argument
return True
```
2022-04-27 14:07:49 +00:00
### `is_sync_committee_update`
```python
def is_sync_committee_update(update: LightClientUpdate) -> bool:
2023-12-27 12:59:31 +00:00
return update.next_sync_committee_branch != NextSyncCommitteeBranch()
2022-04-27 14:07:49 +00:00
```
2022-02-10 01:06:10 +00:00
### `is_finality_update`
```python
def is_finality_update(update: LightClientUpdate) -> bool:
2023-12-27 12:59:31 +00:00
return update.finality_branch != FinalityBranch()
2022-02-10 01:06:10 +00:00
```
2022-04-29 14:22:26 +00:00
### `is_better_update`
```python
def is_better_update(new_update: LightClientUpdate, old_update: LightClientUpdate) -> bool:
# Compare supermajority (> 2/3) sync committee participation
max_active_participants = len(new_update.sync_aggregate.sync_committee_bits)
new_num_active_participants = sum(new_update.sync_aggregate.sync_committee_bits)
old_num_active_participants = sum(old_update.sync_aggregate.sync_committee_bits)
new_has_supermajority = new_num_active_participants * 3 >= max_active_participants * 2
old_has_supermajority = old_num_active_participants * 3 >= max_active_participants * 2
if new_has_supermajority != old_has_supermajority:
return new_has_supermajority > old_has_supermajority
if not new_has_supermajority and new_num_active_participants != old_num_active_participants:
return new_num_active_participants > old_num_active_participants
2022-05-02 21:24:26 +00:00
# Compare presence of relevant sync committee
new_has_relevant_sync_committee = is_sync_committee_update(new_update) and (
2023-01-05 12:36:04 +00:00
compute_sync_committee_period_at_slot(new_update.attested_header.beacon.slot)
2022-05-02 21:24:26 +00:00
== compute_sync_committee_period_at_slot(new_update.signature_slot)
)
old_has_relevant_sync_committee = is_sync_committee_update(old_update) and (
2023-01-05 12:36:04 +00:00
compute_sync_committee_period_at_slot(old_update.attested_header.beacon.slot)
2022-05-02 21:24:26 +00:00
== compute_sync_committee_period_at_slot(old_update.signature_slot)
)
if new_has_relevant_sync_committee != old_has_relevant_sync_committee:
2022-07-12 05:37:12 +00:00
return new_has_relevant_sync_committee
2022-05-02 21:24:26 +00:00
2022-04-29 14:22:26 +00:00
# Compare indication of any finality
new_has_finality = is_finality_update(new_update)
old_has_finality = is_finality_update(old_update)
if new_has_finality != old_has_finality:
2022-07-09 05:01:56 +00:00
return new_has_finality
2022-04-29 14:22:26 +00:00
# Compare sync committee finality
if new_has_finality:
new_has_sync_committee_finality = (
2023-01-05 12:36:04 +00:00
compute_sync_committee_period_at_slot(new_update.finalized_header.beacon.slot)
== compute_sync_committee_period_at_slot(new_update.attested_header.beacon.slot)
2022-04-29 14:22:26 +00:00
)
old_has_sync_committee_finality = (
2023-01-05 12:36:04 +00:00
compute_sync_committee_period_at_slot(old_update.finalized_header.beacon.slot)
== compute_sync_committee_period_at_slot(old_update.attested_header.beacon.slot)
2022-04-29 14:22:26 +00:00
)
if new_has_sync_committee_finality != old_has_sync_committee_finality:
2022-07-09 05:05:48 +00:00
return new_has_sync_committee_finality
2022-04-29 14:22:26 +00:00
# Tiebreaker 1: Sync committee participation beyond supermajority
if new_num_active_participants != old_num_active_participants:
return new_num_active_participants > old_num_active_participants
# Tiebreaker 2: Prefer older data (fewer changes to best)
2023-01-05 12:36:04 +00:00
if new_update.attested_header.beacon.slot != old_update.attested_header.beacon.slot:
return new_update.attested_header.beacon.slot < old_update.attested_header.beacon.slot
2022-04-29 14:22:26 +00:00
return new_update.signature_slot < old_update.signature_slot
```
2022-05-02 21:24:26 +00:00
### `is_next_sync_committee_known`
```python
def is_next_sync_committee_known(store: LightClientStore) -> bool:
return store.next_sync_committee != SyncCommittee()
```
2022-07-09 04:46:34 +00:00
### `get_safety_threshold`
```python
def get_safety_threshold(store: LightClientStore) -> uint64:
return max(
store.previous_max_active_participants,
store.current_max_active_participants,
) // 2
```
2022-07-15 11:45:34 +00:00
### `get_subtree_index`
2022-07-15 09:54:47 +00:00
```python
2022-07-15 11:45:34 +00:00
def get_subtree_index(generalized_index: GeneralizedIndex) -> uint64:
return uint64(generalized_index % 2**(floorlog2(generalized_index)))
2022-07-15 09:54:47 +00:00
```
2024-06-21 08:59:13 +00:00
### `is_valid_normalized_merkle_branch`
```python
def is_valid_normalized_merkle_branch(leaf: Bytes32,
branch: Sequence[Bytes32],
gindex: GeneralizedIndex,
root: Root) -> bool:
depth = floorlog2(gindex)
index = get_subtree_index(gindex)
num_extra = len(branch) - depth
for i in range(num_extra):
if branch[i] != Bytes32():
return False
return is_valid_merkle_branch(leaf, branch[num_extra:], depth, index, root)
```
2022-07-15 11:45:34 +00:00
### `compute_sync_committee_period_at_slot`
2022-07-09 04:46:34 +00:00
```python
2022-07-15 11:45:34 +00:00
def compute_sync_committee_period_at_slot(slot: Slot) -> uint64:
return compute_sync_committee_period(compute_epoch_at_slot(slot))
2022-07-09 04:46:34 +00:00
```
2022-05-02 21:24:26 +00:00
## Light client initialization
A light client maintains its state in a `store` object of type `LightClientStore` . `initialize_light_client_store` initializes a new `store` with a received `LightClientBootstrap` derived from a given `trusted_block_root` .
### `initialize_light_client_store`
```python
def initialize_light_client_store(trusted_block_root: Root,
bootstrap: LightClientBootstrap) -> LightClientStore:
2023-01-06 18:58:23 +00:00
assert is_valid_light_client_header(bootstrap.header)
2023-01-05 12:36:04 +00:00
assert hash_tree_root(bootstrap.header.beacon) == trusted_block_root
2022-05-02 21:24:26 +00:00
2024-06-21 08:59:13 +00:00
assert is_valid_normalized_merkle_branch(
2022-05-02 21:24:26 +00:00
leaf=hash_tree_root(bootstrap.current_sync_committee),
branch=bootstrap.current_sync_committee_branch,
2024-06-21 08:59:13 +00:00
gindex=current_sync_committee_gindex_at_slot(bootstrap.header.beacon.slot),
2023-01-05 12:36:04 +00:00
root=bootstrap.header.beacon.state_root,
2022-05-02 21:24:26 +00:00
)
return LightClientStore(
finalized_header=bootstrap.header,
current_sync_committee=bootstrap.current_sync_committee,
next_sync_committee=SyncCommittee(),
best_valid_update=None,
optimistic_header=bootstrap.header,
previous_max_active_participants=0,
current_max_active_participants=0,
)
```
2020-11-16 07:02:11 +00:00
## Light client state updates
2022-07-20 15:50:18 +00:00
- A light client receives objects of type `LightClientUpdate` , `LightClientFinalityUpdate` and `LightClientOptimisticUpdate` :
- **`update: 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.
- **`finality_update: LightClientFinalityUpdate`**: Every `finality_update` triggers `process_light_client_finality_update(store, finality_update, current_slot, genesis_validators_root)` .
- **`optimistic_update: LightClientOptimisticUpdate`**: Every `optimistic_update` triggers `process_light_client_optimistic_update(store, optimistic_update, current_slot, genesis_validators_root)` .
2022-07-22 09:49:07 +00:00
- `process_light_client_store_force_update` MAY be called based on use case dependent heuristics if light client sync appears stuck.
2020-11-16 07:02:11 +00:00
2022-05-02 21:24:26 +00:00
### `validate_light_client_update`
2020-11-16 07:02:11 +00:00
```python
2021-11-27 13:25:27 +00:00
def validate_light_client_update(store: LightClientStore,
2021-05-04 13:39:22 +00:00
update: LightClientUpdate,
2021-11-29 13:04:05 +00:00
current_slot: Slot,
2021-03-11 13:02:05 +00:00
genesis_validators_root: Root) -> None:
2022-04-29 14:22:26 +00:00
# Verify sync committee has sufficient participants
sync_aggregate = update.sync_aggregate
assert sum(sync_aggregate.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS
2020-11-17 12:42:09 +00:00
# Verify update does not skip a sync committee period
2023-01-06 18:58:23 +00:00
assert is_valid_light_client_header(update.attested_header)
2023-01-05 12:36:04 +00:00
update_attested_slot = update.attested_header.beacon.slot
update_finalized_slot = update.finalized_header.beacon.slot
assert current_slot >= update.signature_slot > update_attested_slot >= update_finalized_slot
store_period = compute_sync_committee_period_at_slot(store.finalized_header.beacon.slot)
2022-07-11 05:05:18 +00:00
update_signature_period = compute_sync_committee_period_at_slot(update.signature_slot)
2022-05-02 21:24:26 +00:00
if is_next_sync_committee_known(store):
assert update_signature_period in (store_period, store_period + 1)
else:
assert update_signature_period == store_period
2022-04-29 14:22:26 +00:00
# Verify update is relevant
2023-01-05 12:36:04 +00:00
update_attested_period = compute_sync_committee_period_at_slot(update_attested_slot)
2022-07-13 12:08:51 +00:00
update_has_next_sync_committee = not is_next_sync_committee_known(store) and (
is_sync_committee_update(update) and update_attested_period == store_period
)
2022-05-02 21:24:26 +00:00
assert (
2023-01-05 12:36:04 +00:00
update_attested_slot > store.finalized_header.beacon.slot
2022-07-13 12:08:51 +00:00
or update_has_next_sync_committee
2022-05-02 21:24:26 +00:00
)
2020-11-18 10:33:42 +00:00
2022-06-27 20:36:55 +00:00
# Verify that the `finality_branch` , if present, confirms `finalized_header`
# to match the finalized checkpoint root saved in the state of `attested_header` .
# Note that the genesis finalized checkpoint root is represented as a zero hash.
2022-02-10 01:06:10 +00:00
if not is_finality_update(update):
2023-01-05 12:36:04 +00:00
assert update.finalized_header == LightClientHeader()
2020-11-18 08:27:19 +00:00
else:
2023-01-05 12:36:04 +00:00
if update_finalized_slot == GENESIS_SLOT:
assert update.finalized_header == LightClientHeader()
2022-07-15 10:03:09 +00:00
finalized_root = Bytes32()
2022-06-27 20:26:35 +00:00
else:
2023-01-06 18:58:23 +00:00
assert is_valid_light_client_header(update.finalized_header)
2023-01-05 12:36:04 +00:00
finalized_root = hash_tree_root(update.finalized_header.beacon)
2024-06-21 08:59:13 +00:00
assert is_valid_normalized_merkle_branch(
2022-05-09 12:37:54 +00:00
leaf=finalized_root,
2020-11-18 10:33:42 +00:00
branch=update.finality_branch,
2024-06-21 08:59:13 +00:00
gindex=finalized_root_gindex_at_slot(update.attested_header.beacon.slot),
2023-01-05 12:36:04 +00:00
root=update.attested_header.beacon.state_root,
2020-11-18 10:33:42 +00:00
)
2020-11-17 12:42:09 +00:00
2022-04-27 14:07:49 +00:00
# Verify that the `next_sync_committee` , if present, actually is the next sync committee saved in the
2022-04-29 14:22:26 +00:00
# state of the `attested_header`
2022-04-27 14:07:49 +00:00
if not is_sync_committee_update(update):
assert update.next_sync_committee == SyncCommittee()
2020-11-18 09:31:41 +00:00
else:
2022-05-02 21:24:26 +00:00
if update_attested_period == store_period and is_next_sync_committee_known(store):
2022-04-27 14:07:49 +00:00
assert update.next_sync_committee == store.next_sync_committee
2024-06-21 08:59:13 +00:00
assert is_valid_normalized_merkle_branch(
2020-11-18 10:33:42 +00:00
leaf=hash_tree_root(update.next_sync_committee),
2020-11-17 12:42:09 +00:00
branch=update.next_sync_committee_branch,
2024-06-21 08:59:13 +00:00
gindex=next_sync_committee_gindex_at_slot(update.attested_header.beacon.slot),
2023-01-05 12:36:04 +00:00
root=update.attested_header.beacon.state_root,
2020-11-17 12:42:09 +00:00
)
2022-01-11 10:22:39 +00:00
2020-11-17 12:42:09 +00:00
# Verify sync committee aggregate signature
2022-07-11 05:05:18 +00:00
if update_signature_period == store_period:
2022-04-26 20:32:25 +00:00
sync_committee = store.current_sync_committee
else:
sync_committee = store.next_sync_committee
2021-12-14 13:38:58 +00:00
participant_pubkeys = [
pubkey for (bit, pubkey) in zip(sync_aggregate.sync_committee_bits, sync_committee.pubkeys)
if bit
]
2023-03-08 14:42:56 +00:00
fork_version_slot = max(update.signature_slot, Slot(1)) - Slot(1)
2023-03-08 14:34:56 +00:00
fork_version = compute_fork_version(compute_epoch_at_slot(fork_version_slot))
2022-04-27 07:50:27 +00:00
domain = compute_domain(DOMAIN_SYNC_COMMITTEE, fork_version, genesis_validators_root)
2023-01-05 12:36:04 +00:00
signing_root = compute_signing_root(update.attested_header.beacon, domain)
2021-12-10 13:48:14 +00:00
assert bls.FastAggregateVerify(participant_pubkeys, signing_root, sync_aggregate.sync_committee_signature)
2020-11-16 07:02:11 +00:00
```
2022-05-02 21:24:26 +00:00
### `apply_light_client_update`
2020-11-18 10:33:42 +00:00
```python
2021-11-27 13:25:27 +00:00
def apply_light_client_update(store: LightClientStore, update: LightClientUpdate) -> None:
2023-01-05 12:36:04 +00:00
store_period = compute_sync_committee_period_at_slot(store.finalized_header.beacon.slot)
update_finalized_period = compute_sync_committee_period_at_slot(update.finalized_header.beacon.slot)
2022-05-02 21:24:26 +00:00
if not is_next_sync_committee_known(store):
assert update_finalized_period == store_period
store.next_sync_committee = update.next_sync_committee
elif update_finalized_period == store_period + 1:
2021-11-27 13:25:27 +00:00
store.current_sync_committee = store.next_sync_committee
store.next_sync_committee = update.next_sync_committee
2022-05-03 10:31:26 +00:00
store.previous_max_active_participants = store.current_max_active_participants
store.current_max_active_participants = 0
2023-01-05 12:36:04 +00:00
if update.finalized_header.beacon.slot > store.finalized_header.beacon.slot:
2022-05-02 21:24:26 +00:00
store.finalized_header = update.finalized_header
2023-01-05 12:36:04 +00:00
if store.finalized_header.beacon.slot > store.optimistic_header.beacon.slot:
2022-05-02 21:24:26 +00:00
store.optimistic_header = store.finalized_header
2020-11-18 10:33:42 +00:00
```
2022-07-22 09:49:07 +00:00
### `process_light_client_store_force_update`
2022-05-03 10:31:26 +00:00
```python
2022-07-22 09:49:07 +00:00
def process_light_client_store_force_update(store: LightClientStore, current_slot: Slot) -> None:
2022-05-03 10:31:26 +00:00
if (
2023-01-05 12:36:04 +00:00
current_slot > store.finalized_header.beacon.slot + UPDATE_TIMEOUT
2022-05-03 10:31:26 +00:00
and store.best_valid_update is not None
):
# Forced best update when the update timeout has elapsed.
2023-01-05 12:36:04 +00:00
# Because the apply logic waits for `finalized_header.beacon.slot` to indicate sync committee finality,
2022-05-03 10:31:26 +00:00
# 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` .
2023-01-05 12:36:04 +00:00
if store.best_valid_update.finalized_header.beacon.slot < = store.finalized_header.beacon.slot:
2022-05-03 10:31:26 +00:00
store.best_valid_update.finalized_header = store.best_valid_update.attested_header
apply_light_client_update(store, store.best_valid_update)
store.best_valid_update = None
```
2022-05-02 21:24:26 +00:00
### `process_light_client_update`
2020-11-16 07:02:11 +00:00
2020-11-16 07:07:40 +00:00
```python
2021-11-26 21:11:19 +00:00
def process_light_client_update(store: LightClientStore,
update: LightClientUpdate,
current_slot: Slot,
2021-03-11 13:02:05 +00:00
genesis_validators_root: Root) -> None:
2021-11-29 13:04:05 +00:00
validate_light_client_update(store, update, current_slot, genesis_validators_root)
2021-12-14 14:05:09 +00:00
2022-01-11 10:22:39 +00:00
sync_committee_bits = update.sync_aggregate.sync_committee_bits
2021-12-14 14:05:09 +00:00
2021-11-26 21:11:19 +00:00
# Update the best update in case we have to force-update to it if the timeout elapses
2021-11-30 12:38:42 +00:00
if (
store.best_valid_update is None
2022-04-29 14:22:26 +00:00
or is_better_update(update, store.best_valid_update)
2021-11-30 12:38:42 +00:00
):
2021-11-29 13:04:05 +00:00
store.best_valid_update = update
2021-12-15 15:43:54 +00:00
2021-11-30 12:38:42 +00:00
# Track the maximum number of active participants in the committee signatures
2021-11-29 13:04:05 +00:00
store.current_max_active_participants = max(
2021-11-30 12:38:42 +00:00
store.current_max_active_participants,
2021-12-14 14:05:09 +00:00
sum(sync_committee_bits),
2021-11-26 21:11:19 +00:00
)
2021-12-15 15:43:54 +00:00
2021-11-26 21:11:19 +00:00
# Update the optimistic header
if (
2021-12-15 15:43:54 +00:00
sum(sync_committee_bits) > get_safety_threshold(store)
2023-01-05 12:36:04 +00:00
and update.attested_header.beacon.slot > store.optimistic_header.beacon.slot
2021-11-26 21:11:19 +00:00
):
2021-11-27 13:25:27 +00:00
store.optimistic_header = update.attested_header
2021-12-15 15:43:54 +00:00
2021-11-26 21:11:19 +00:00
# Update finalized header
2022-07-13 12:08:51 +00:00
update_has_finalized_next_sync_committee = (
not is_next_sync_committee_known(store)
and is_sync_committee_update(update) and is_finality_update(update) and (
2023-01-05 12:36:04 +00:00
compute_sync_committee_period_at_slot(update.finalized_header.beacon.slot)
== compute_sync_committee_period_at_slot(update.attested_header.beacon.slot)
2022-07-13 12:08:51 +00:00
)
)
2020-12-09 09:35:22 +00:00
if (
2021-12-14 14:05:09 +00:00
sum(sync_committee_bits) * 3 >= len(sync_committee_bits) * 2
2022-05-02 21:24:26 +00:00
and (
2023-01-05 12:36:04 +00:00
update.finalized_header.beacon.slot > store.finalized_header.beacon.slot
2022-07-13 12:08:51 +00:00
or update_has_finalized_next_sync_committee
2022-05-02 21:24:26 +00:00
)
2020-12-09 09:35:22 +00:00
):
2021-11-26 21:11:19 +00:00
# Normal update through 2/3 threshold
2021-11-27 13:25:27 +00:00
apply_light_client_update(store, update)
2021-11-26 21:11:19 +00:00
store.best_valid_update = None
2020-11-16 07:02:11 +00:00
```
2022-05-03 10:15:04 +00:00
### `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(),
2023-12-27 12:59:31 +00:00
next_sync_committee_branch=NextSyncCommitteeBranch(),
2022-05-03 10:15:04 +00:00
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(),
2023-12-27 12:59:31 +00:00
next_sync_committee_branch=NextSyncCommitteeBranch(),
2023-01-05 12:36:04 +00:00
finalized_header=LightClientHeader(),
2023-12-27 12:59:31 +00:00
finality_branch=FinalityBranch(),
2022-05-03 10:15:04 +00:00
sync_aggregate=optimistic_update.sync_aggregate,
signature_slot=optimistic_update.signature_slot,
)
process_light_client_update(store, update, current_slot, genesis_validators_root)
```