eth2.0-specs/specs/altair/sync-protocol.md

196 lines
7.7 KiB
Markdown
Raw Normal View History

# Minimal Light Client
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)
- [Constants](#constants)
- [Preset](#preset)
- [Misc](#misc)
2020-11-16 07:02:11 +00:00
- [Containers](#containers)
2021-03-11 06:27:23 +00:00
- [`LightClientSnapshot`](#lightclientsnapshot)
- [`LightClientUpdate`](#lightclientupdate)
- [`LightClientStore`](#lightclientstore)
- [Helper functions](#helper-functions)
- [`get_subtree_index`](#get_subtree_index)
2020-11-16 07:02:11 +00:00
- [Light client state updates](#light-client-state-updates)
2021-01-19 12:44:21 +00:00
- [`validate_light_client_update`](#validate_light_client_update)
- [`apply_light_client_update`](#apply_light_client_update)
- [`process_light_client_update`](#process_light_client_update)
2020-11-16 07:02:11 +00:00
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
2020-12-07 15:10:39 +00:00
Eth2 is designed to be light client friendly for constrained environments to
access Eth2 with reasonable safety and liveness.
Such environments include resource-constrained devices (e.g. phones for trust-minimised wallets)
and metered VMs (e.g. blockchain VMs for cross-chain bridges).
2020-12-07 15:10:39 +00:00
This document suggests a minimal light client design for the beacon chain that
uses sync committees introduced in [this beacon chain extension](./beacon-chain.md).
2020-11-16 07:02:11 +00:00
## Constants
| Name | Value |
| - | - |
| `FINALIZED_ROOT_INDEX` | `get_generalized_index(BeaconState, 'finalized_checkpoint', 'root')` |
| `NEXT_SYNC_COMMITTEE_INDEX` | `get_generalized_index(BeaconState, 'next_sync_committee')` |
## Preset
### Misc
| Name | Value |
| - | - |
2020-11-17 15:30:09 +00:00
| `MIN_SYNC_COMMITTEE_PARTICIPANTS` | `1` |
2020-11-16 07:02:11 +00:00
## Containers
2021-03-11 06:27:23 +00:00
### `LightClientSnapshot`
2020-11-16 07:02:11 +00:00
```python
class LightClientSnapshot(Container):
# Beacon block header
2020-11-16 07:02:11 +00:00
header: BeaconBlockHeader
# Sync committees corresponding to the header
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
2020-11-16 07:02:11 +00:00
```
2021-03-11 06:27:23 +00:00
### `LightClientUpdate`
2020-11-16 07:02:11 +00:00
```python
class LightClientUpdate(Container):
# Update beacon block header
header: BeaconBlockHeader
# Next sync committee corresponding to the header
next_sync_committee: SyncCommittee
next_sync_committee_branch: Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_INDEX)]
# Finality proof for the update header
finality_header: BeaconBlockHeader
finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)]
# Sync committee aggregate signature
2020-11-18 09:31:41 +00:00
sync_committee_bits: Bitvector[SYNC_COMMITTEE_SIZE]
sync_committee_signature: BLSSignature
2020-11-17 15:30:09 +00:00
# Fork version for the aggregate signature
fork_version: Version
```
2021-03-11 06:27:23 +00:00
### `LightClientStore`
2020-11-16 07:02:11 +00:00
```python
@dataclass
class LightClientStore(object):
snapshot: LightClientSnapshot
valid_updates: Set[LightClientUpdate]
2020-11-16 07:02:11 +00:00
```
## Helper functions
### `get_subtree_index`
```python
def get_subtree_index(generalized_index: GeneralizedIndex) -> uint64:
return uint64(generalized_index % 2**(floorlog2(generalized_index)))
```
2020-11-16 07:02:11 +00:00
## Light client state updates
executable light client patch: beacon-chain.md (#2141) * Bump remerkleable to 0.1.18 * Disable `sync-protocol.md` for now. Make linter pass * Enable lightclient tests * Use *new* `optional_fast_aggregate_verify` * Fix ToC and codespell * Do not run phase1 tests with Lightclient patch * Fix the Eth1Data casting bug. Add a workaround. * Fix `run_on_attestation` testing helper * Revert * Rename `optional_fast_aggregate_verify` to `eth2_fast_aggregate_verify` * Apply Proto's suggestion * Apply Danny's suggestion * Fixing tests * Fix after rebasing * Rename `LIGHTCLIENT` -> `LIGHTCLIENT_PATCH` * New doctoc * Add lightclient patch configs * fix gitignore light client patch generator output * Upgrade state for light client patch * Add `lightclient-fork.md` to deal the fork boundary and fix `process_block_header` * Misc cleanups 1) Add a summary note for every function that is changed. 2) Avoid changing `process_block` (instead only change `process_block_header`). 3) Rename `G2_INFINITY_POINT_SIG` to `G2_POINT_AT_INFINITY` to avoid `SIG` contraction. 4) Misc cleanups * Update block.py * Update beacon-chain.md * Fix typo "minimal" -> "mainnet" Co-authored-by: Marin Petrunić <mpetrunic@users.noreply.github.com> * Use the new `BeaconBlockHeader` instead of phase 0 version * Update config files * Move `sync_committee_bits` and `sync_committee_signature` back to `BeaconBlockBody` Co-authored-by: protolambda <proto@protolambda.com> Co-authored-by: Justin <drakefjustin@gmail.com> Co-authored-by: Marin Petrunić <mpetrunic@users.noreply.github.com>
2020-12-15 05:18:20 +00:00
A light client maintains its state in a `store` object of type `LightClientStore` and receives `update` objects of type `LightClientUpdate`. Every `update` triggers `process_light_client_update(store, update, current_slot)` where `current_slot` is the current slot based on some local clock.
2020-11-16 07:02:11 +00:00
2021-01-19 12:44:21 +00:00
#### `validate_light_client_update`
2020-11-16 07:02:11 +00:00
```python
2021-05-04 13:39:22 +00:00
def validate_light_client_update(snapshot: LightClientSnapshot,
update: LightClientUpdate,
2021-03-11 13:02:05 +00:00
genesis_validators_root: Root) -> None:
# Verify update slot is larger than snapshot slot
assert update.header.slot > snapshot.header.slot
# Verify update does not skip a sync committee period
snapshot_period = compute_epoch_at_slot(snapshot.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
update_period = compute_epoch_at_slot(update.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
assert update_period in (snapshot_period, snapshot_period + 1)
# Verify update header root is the finalized root of the finality header, if specified
if update.finality_header == BeaconBlockHeader():
signed_header = update.header
assert update.finality_branch == [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))]
else:
signed_header = update.finality_header
assert is_valid_merkle_branch(
leaf=hash_tree_root(update.header),
branch=update.finality_branch,
depth=floorlog2(FINALIZED_ROOT_INDEX),
index=get_subtree_index(FINALIZED_ROOT_INDEX),
root=update.finality_header.state_root,
)
# Verify update next sync committee if the update period incremented
if update_period == snapshot_period:
sync_committee = snapshot.current_sync_committee
assert update.next_sync_committee_branch == [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))]
2020-11-18 09:31:41 +00:00
else:
sync_committee = snapshot.next_sync_committee
assert is_valid_merkle_branch(
leaf=hash_tree_root(update.next_sync_committee),
branch=update.next_sync_committee_branch,
depth=floorlog2(NEXT_SYNC_COMMITTEE_INDEX),
index=get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX),
root=update.header.state_root,
)
# Verify sync committee has sufficient participants
2020-11-18 09:31:41 +00:00
assert sum(update.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS
# Verify sync committee aggregate signature
participant_pubkeys = [pubkey for (bit, pubkey) in zip(update.sync_committee_bits, sync_committee.pubkeys) if bit]
2021-03-11 13:02:05 +00:00
domain = compute_domain(DOMAIN_SYNC_COMMITTEE, update.fork_version, genesis_validators_root)
signing_root = compute_signing_root(signed_header, domain)
assert bls.FastAggregateVerify(participant_pubkeys, signing_root, update.sync_committee_signature)
2020-11-16 07:02:11 +00:00
```
#### `apply_light_client_update`
```python
def apply_light_client_update(snapshot: LightClientSnapshot, update: LightClientUpdate) -> None:
snapshot_period = compute_epoch_at_slot(snapshot.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
update_period = compute_epoch_at_slot(update.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
if update_period == snapshot_period + 1:
snapshot.current_sync_committee = snapshot.next_sync_committee
snapshot.next_sync_committee = update.next_sync_committee
snapshot.header = update.header
```
#### `process_light_client_update`
2020-11-16 07:02:11 +00:00
2020-11-16 07:07:40 +00:00
```python
2021-03-11 13:02:05 +00:00
def process_light_client_update(store: LightClientStore, update: LightClientUpdate, current_slot: Slot,
genesis_validators_root: Root) -> None:
validate_light_client_update(store.snapshot, update, genesis_validators_root)
store.valid_updates.add(update)
update_timeout = SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD
if (
sum(update.sync_committee_bits) * 3 >= len(update.sync_committee_bits) * 2
2021-01-19 12:12:36 +00:00
and update.finality_header != BeaconBlockHeader()
):
2021-01-19 12:12:36 +00:00
# Apply update if (1) 2/3 quorum is reached and (2) we have a finality proof.
# Note that (2) means that the current light client design needs finality.
# It may be changed to re-organizable light client design. See the on-going issue eth2.0-specs#2182.
2021-03-11 06:27:23 +00:00
apply_light_client_update(store.snapshot, update)
store.valid_updates = set()
elif current_slot > store.snapshot.header.slot + update_timeout:
2020-11-17 15:30:09 +00:00
# Forced best update when the update timeout has elapsed
2021-03-11 06:27:23 +00:00
apply_light_client_update(store.snapshot,
max(store.valid_updates, key=lambda update: sum(update.sync_committee_bits)))
store.valid_updates = set()
2020-11-16 07:02:11 +00:00
```