mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-02-03 14:24:24 +00:00
Extract full node specifics to separate doc
This commit is contained in:
parent
d06f44ef55
commit
88206117c6
@ -18,7 +18,7 @@ Features are researched and developed in parallel, and then consolidated into se
|
||||
| Seq. | Code Name | Fork Epoch | Specs |
|
||||
| - | - | - | - |
|
||||
| 0 | **Phase0** |`0` | <ul><li>Core</li><ul><li>[The beacon chain](specs/phase0/beacon-chain.md)</li><li>[Deposit contract](specs/phase0/deposit-contract.md)</li><li>[Beacon chain fork choice](specs/phase0/fork-choice.md)</li></ul><li>Additions</li><ul><li>[Honest validator guide](specs/phase0/validator.md)</li><li>[P2P networking](specs/phase0/p2p-interface.md)</li><li>[Weak subjectivity](specs/phase0/weak-subjectivity.md)</li></ul></ul> |
|
||||
| 1 | **Altair** | `74240` | <ul><li>Core</li><ul><li>[Beacon chain changes](specs/altair/beacon-chain.md)</li><li>[Altair fork](specs/altair/fork.md)</li></ul><li>Additions</li><ul><li>[Light client sync protocol](specs/altair/light-client/sync-protocol.md)</li><li>[Honest validator guide changes](specs/altair/validator.md)</li><li>[P2P networking](specs/altair/p2p-interface.md)</li></ul></ul> |
|
||||
| 1 | **Altair** | `74240` | <ul><li>Core</li><ul><li>[Beacon chain changes](specs/altair/beacon-chain.md)</li><li>[Altair fork](specs/altair/fork.md)</li></ul><li>Additions</li><ul><li>[Light client sync protocol](specs/altair/light-client/sync-protocol.md) ([full node](specs/altair/light-client/full-node.md))</li><li>[Honest validator guide changes](specs/altair/validator.md)</li><li>[P2P networking](specs/altair/p2p-interface.md)</li></ul></ul> |
|
||||
| 2 | **Bellatrix** <br/> (["The Merge"](https://ethereum.org/en/upgrades/merge/)) | TBD | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/bellatrix/beacon-chain.md)</li><li>[Bellatrix fork](specs/bellatrix/fork.md)</li><li>[Fork choice changes](specs/bellatrix/fork-choice.md)</li></ul><li>Additions</li><ul><li>[Honest validator guide changes](specs/bellatrix/validator.md)</li><li>[P2P networking](specs/bellatrix/p2p-interface.md)</li></ul></ul> |
|
||||
|
||||
### In-development Specifications
|
||||
|
1
setup.py
1
setup.py
@ -889,6 +889,7 @@ class PySpecCommand(Command):
|
||||
"""
|
||||
if self.spec_fork in (ALTAIR, BELLATRIX, CAPELLA):
|
||||
self.md_doc_paths += """
|
||||
specs/altair/light-client/full-node.md
|
||||
specs/altair/light-client/sync-protocol.md
|
||||
specs/altair/beacon-chain.md
|
||||
specs/altair/bls.md
|
||||
|
135
specs/altair/light-client/full-node.md
Normal file
135
specs/altair/light-client/full-node.md
Normal file
@ -0,0 +1,135 @@
|
||||
# Altair Light Client -- Full Node
|
||||
|
||||
**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)
|
||||
- [Helper functions](#helper-functions)
|
||||
- [`compute_merkle_proof_for_state`](#compute_merkle_proof_for_state)
|
||||
- [Deriving light client data](#deriving-light-client-data)
|
||||
- [`create_light_client_bootstrap`](#create_light_client_bootstrap)
|
||||
- [`create_light_client_update`](#create_light_client_update)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- /TOC -->
|
||||
|
||||
## Introduction
|
||||
|
||||
This document provides helper functions to enable full nodes to serve light client data. Full nodes SHOULD implement the described functionality to enable light clients to sync with the network.
|
||||
|
||||
## Helper functions
|
||||
|
||||
### `compute_merkle_proof_for_state`
|
||||
|
||||
```python
|
||||
def compute_merkle_proof_for_state(state: BeaconState,
|
||||
index: GeneralizedIndex) -> Sequence[Bytes32]:
|
||||
...
|
||||
```
|
||||
|
||||
## Deriving light client data
|
||||
|
||||
Full nodes are expected to derive light client data from historic blocks and states and provide it to other clients.
|
||||
|
||||
### `create_light_client_bootstrap`
|
||||
|
||||
```python
|
||||
def create_light_client_bootstrap(state: BeaconState) -> LightClientBootstrap:
|
||||
assert compute_epoch_at_slot(state.slot) >= ALTAIR_FORK_EPOCH
|
||||
assert state.slot == state.latest_block_header.slot
|
||||
|
||||
return LightClientBootstrap(
|
||||
header=BeaconBlockHeader(
|
||||
slot=state.latest_block_header.slot,
|
||||
proposer_index=state.latest_block_header.proposer_index,
|
||||
parent_root=state.latest_block_header.parent_root,
|
||||
state_root=hash_tree_root(state),
|
||||
body_root=state.latest_block_header.body_root,
|
||||
),
|
||||
current_sync_committee=state.current_sync_committee,
|
||||
current_sync_committee_branch=compute_merkle_proof_for_state(state, CURRENT_SYNC_COMMITTEE_INDEX)
|
||||
)
|
||||
```
|
||||
|
||||
Full nodes SHOULD provide `LightClientBootstrap` for all finalized epoch boundary blocks in the epoch range `[max(ALTAIR_FORK_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOCK_REQUESTS), current_epoch]` where `current_epoch` is defined by the current wall-clock time. Full nodes MAY also provide `LightClientBootstrap` for other blocks.
|
||||
|
||||
Blocks are considered to be epoch boundary blocks if their block root can occur as part of a valid `Checkpoint`, i.e., if their slot is the initial slot of an epoch, or if all following slots through the initial slot of the next epoch are empty (no block proposed / orphaned).
|
||||
|
||||
`LightClientBootstrap` is computed from the block's immediate post state (without applying empty slots).
|
||||
|
||||
### `create_light_client_update`
|
||||
|
||||
To form a `LightClientUpdate`, the following historical states and blocks are needed:
|
||||
- `state`: the post state of any block with a post-Altair parent block
|
||||
- `block`: the corresponding block
|
||||
- `attested_state`: the post state of the block referred to by `block.parent_root`
|
||||
- `finalized_block`: the block referred to by `attested_state.finalized_checkpoint.root`, if locally available (may be unavailable, e.g., when using checkpoint sync, or if it was pruned locally)
|
||||
|
||||
```python
|
||||
def create_light_client_update(state: BeaconState,
|
||||
block: SignedBeaconBlock,
|
||||
attested_state: BeaconState,
|
||||
finalized_block: Optional[SignedBeaconBlock]) -> LightClientUpdate:
|
||||
assert compute_epoch_at_slot(attested_state.slot) >= ALTAIR_FORK_EPOCH
|
||||
assert sum(block.message.body.sync_aggregate.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS
|
||||
|
||||
assert state.slot == state.latest_block_header.slot
|
||||
header = state.latest_block_header.copy()
|
||||
header.state_root = hash_tree_root(state)
|
||||
assert hash_tree_root(header) == hash_tree_root(block.message)
|
||||
update_signature_period = compute_sync_committee_period(compute_epoch_at_slot(block.message.slot))
|
||||
|
||||
assert attested_state.slot == attested_state.latest_block_header.slot
|
||||
attested_header = attested_state.latest_block_header.copy()
|
||||
attested_header.state_root = hash_tree_root(attested_state)
|
||||
assert hash_tree_root(attested_header) == block.message.parent_root
|
||||
update_attested_period = compute_sync_committee_period(compute_epoch_at_slot(attested_header.slot))
|
||||
|
||||
# `next_sync_committee` is only useful if the message is signed by the current sync committee
|
||||
if update_attested_period == update_signature_period:
|
||||
next_sync_committee = attested_state.next_sync_committee
|
||||
next_sync_committee_branch = compute_merkle_proof_for_state(attested_state, NEXT_SYNC_COMMITTEE_INDEX)
|
||||
else:
|
||||
next_sync_committee = SyncCommittee()
|
||||
next_sync_committee_branch = [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))]
|
||||
|
||||
# Indicate finality whenever possible
|
||||
if finalized_block is not None:
|
||||
if finalized_block.message.slot != GENESIS_SLOT:
|
||||
finalized_header = BeaconBlockHeader(
|
||||
slot=finalized_block.message.slot,
|
||||
proposer_index=finalized_block.message.proposer_index,
|
||||
parent_root=finalized_block.message.parent_root,
|
||||
state_root=finalized_block.message.state_root,
|
||||
body_root=hash_tree_root(finalized_block.message.body),
|
||||
)
|
||||
assert hash_tree_root(finalized_header) == attested_state.finalized_checkpoint.root
|
||||
else:
|
||||
assert attested_state.finalized_checkpoint.root == Bytes32()
|
||||
finalized_header = BeaconBlockHeader()
|
||||
finality_branch = compute_merkle_proof_for_state(attested_state, FINALIZED_ROOT_INDEX)
|
||||
else:
|
||||
finalized_header = BeaconBlockHeader()
|
||||
finality_branch = [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))]
|
||||
|
||||
return LightClientUpdate(
|
||||
attested_header=attested_header,
|
||||
next_sync_committee=next_sync_committee,
|
||||
next_sync_committee_branch=next_sync_committee_branch,
|
||||
finalized_header=finalized_header,
|
||||
finality_branch=finality_branch,
|
||||
sync_aggregate=block.message.body.sync_aggregate,
|
||||
signature_slot=block.message.slot,
|
||||
)
|
||||
```
|
||||
|
||||
Full nodes SHOULD provide the best derivable `LightClientUpdate` (according to `is_better_update`) for each sync committee period covering any epochs in range `[max(ALTAIR_FORK_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOCK_REQUESTS), current_epoch]` where `current_epoch` is defined by the current wall-clock time. Full nodes MAY also provide `LightClientUpdate` for other sync committee periods.
|
||||
|
||||
- `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.
|
@ -1,4 +1,4 @@
|
||||
# Altair -- Minimal Light Client
|
||||
# Altair Light Client -- Sync Protocol
|
||||
|
||||
**Notice**: This document is a work-in-progress for researchers and implementers.
|
||||
|
||||
@ -24,7 +24,6 @@
|
||||
- [`get_safety_threshold`](#get_safety_threshold)
|
||||
- [`compute_sync_committee_period_at_slot`](#compute_sync_committee_period_at_slot)
|
||||
- [`get_subtree_index`](#get_subtree_index)
|
||||
- [`compute_merkle_proof_for_state`](#compute_merkle_proof_for_state)
|
||||
- [Light client initialization](#light-client-initialization)
|
||||
- [`initialize_light_client_store`](#initialize_light_client_store)
|
||||
- [Light client state updates](#light-client-state-updates)
|
||||
@ -32,9 +31,6 @@
|
||||
- [`validate_light_client_update`](#validate_light_client_update)
|
||||
- [`apply_light_client_update`](#apply_light_client_update)
|
||||
- [`process_light_client_update`](#process_light_client_update)
|
||||
- [Deriving light client data](#deriving-light-client-data)
|
||||
- [`create_light_client_bootstrap`](#create_light_client_bootstrap)
|
||||
- [`create_light_client_update`](#create_light_client_update)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- /TOC -->
|
||||
@ -49,6 +45,9 @@ and metered VMs (e.g. blockchain VMs for cross-chain bridges).
|
||||
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).
|
||||
|
||||
Additional documents describe how the light client sync protocol can be used:
|
||||
- [Full node](./full-node.md)
|
||||
|
||||
## Constants
|
||||
|
||||
| Name | Value |
|
||||
@ -219,14 +218,6 @@ def get_subtree_index(generalized_index: GeneralizedIndex) -> uint64:
|
||||
return uint64(generalized_index % 2**(floorlog2(generalized_index)))
|
||||
```
|
||||
|
||||
### `compute_merkle_proof_for_state`
|
||||
|
||||
```python
|
||||
def compute_merkle_proof_for_state(state: BeaconState,
|
||||
index: GeneralizedIndex) -> Sequence[Bytes32]:
|
||||
...
|
||||
```
|
||||
|
||||
## 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`.
|
||||
@ -429,105 +420,3 @@ def process_light_client_update(store: LightClientStore,
|
||||
apply_light_client_update(store, update)
|
||||
store.best_valid_update = None
|
||||
```
|
||||
|
||||
## Deriving light client data
|
||||
|
||||
Full nodes are expected to derive light client data from historic blocks and states and provide it to other clients.
|
||||
|
||||
### `create_light_client_bootstrap`
|
||||
|
||||
```python
|
||||
def create_light_client_bootstrap(state: BeaconState) -> LightClientBootstrap:
|
||||
assert compute_epoch_at_slot(state.slot) >= ALTAIR_FORK_EPOCH
|
||||
assert state.slot == state.latest_block_header.slot
|
||||
|
||||
return LightClientBootstrap(
|
||||
header=BeaconBlockHeader(
|
||||
slot=state.latest_block_header.slot,
|
||||
proposer_index=state.latest_block_header.proposer_index,
|
||||
parent_root=state.latest_block_header.parent_root,
|
||||
state_root=hash_tree_root(state),
|
||||
body_root=state.latest_block_header.body_root,
|
||||
),
|
||||
current_sync_committee=state.current_sync_committee,
|
||||
current_sync_committee_branch=compute_merkle_proof_for_state(state, CURRENT_SYNC_COMMITTEE_INDEX)
|
||||
)
|
||||
```
|
||||
|
||||
Full nodes SHOULD provide `LightClientBootstrap` for all finalized epoch boundary blocks in the epoch range `[max(ALTAIR_FORK_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOCK_REQUESTS), current_epoch]` where `current_epoch` is defined by the current wall-clock time. Full nodes MAY also provide `LightClientBootstrap` for other blocks.
|
||||
|
||||
Blocks are considered to be epoch boundary blocks if their block root can occur as part of a valid `Checkpoint`, i.e., if their slot is the initial slot of an epoch, or if all following slots through the initial slot of the next epoch are empty (no block proposed / orphaned).
|
||||
|
||||
`LightClientBootstrap` is computed from the block's immediate post state (without applying empty slots).
|
||||
|
||||
### `create_light_client_update`
|
||||
|
||||
To form a `LightClientUpdate`, the following historical states and blocks are needed:
|
||||
- `state`: the post state of any block with a post-Altair parent block
|
||||
- `block`: the corresponding block
|
||||
- `attested_state`: the post state of the block referred to by `block.parent_root`
|
||||
- `finalized_block`: the block referred to by `attested_state.finalized_checkpoint.root`, if locally available (may be unavailable, e.g., when using checkpoint sync, or if it was pruned locally)
|
||||
|
||||
```python
|
||||
def create_light_client_update(state: BeaconState,
|
||||
block: SignedBeaconBlock,
|
||||
attested_state: BeaconState,
|
||||
finalized_block: Optional[SignedBeaconBlock]) -> LightClientUpdate:
|
||||
assert compute_epoch_at_slot(attested_state.slot) >= ALTAIR_FORK_EPOCH
|
||||
assert sum(block.message.body.sync_aggregate.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS
|
||||
|
||||
assert state.slot == state.latest_block_header.slot
|
||||
header = state.latest_block_header.copy()
|
||||
header.state_root = hash_tree_root(state)
|
||||
assert hash_tree_root(header) == hash_tree_root(block.message)
|
||||
update_signature_period = compute_sync_committee_period(compute_epoch_at_slot(block.message.slot))
|
||||
|
||||
assert attested_state.slot == attested_state.latest_block_header.slot
|
||||
attested_header = attested_state.latest_block_header.copy()
|
||||
attested_header.state_root = hash_tree_root(attested_state)
|
||||
assert hash_tree_root(attested_header) == block.message.parent_root
|
||||
update_attested_period = compute_sync_committee_period(compute_epoch_at_slot(attested_header.slot))
|
||||
|
||||
# `next_sync_committee` is only useful if the message is signed by the current sync committee
|
||||
if update_attested_period == update_signature_period:
|
||||
next_sync_committee = attested_state.next_sync_committee
|
||||
next_sync_committee_branch = compute_merkle_proof_for_state(attested_state, NEXT_SYNC_COMMITTEE_INDEX)
|
||||
else:
|
||||
next_sync_committee = SyncCommittee()
|
||||
next_sync_committee_branch = [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))]
|
||||
|
||||
# Indicate finality whenever possible
|
||||
if finalized_block is not None:
|
||||
if finalized_block.message.slot != GENESIS_SLOT:
|
||||
finalized_header = BeaconBlockHeader(
|
||||
slot=finalized_block.message.slot,
|
||||
proposer_index=finalized_block.message.proposer_index,
|
||||
parent_root=finalized_block.message.parent_root,
|
||||
state_root=finalized_block.message.state_root,
|
||||
body_root=hash_tree_root(finalized_block.message.body),
|
||||
)
|
||||
assert hash_tree_root(finalized_header) == attested_state.finalized_checkpoint.root
|
||||
else:
|
||||
assert attested_state.finalized_checkpoint.root == Bytes32()
|
||||
finalized_header = BeaconBlockHeader()
|
||||
finality_branch = compute_merkle_proof_for_state(attested_state, FINALIZED_ROOT_INDEX)
|
||||
else:
|
||||
finalized_header = BeaconBlockHeader()
|
||||
finality_branch = [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))]
|
||||
|
||||
return LightClientUpdate(
|
||||
attested_header=attested_header,
|
||||
next_sync_committee=next_sync_committee,
|
||||
next_sync_committee_branch=next_sync_committee_branch,
|
||||
finalized_header=finalized_header,
|
||||
finality_branch=finality_branch,
|
||||
sync_aggregate=block.message.body.sync_aggregate,
|
||||
signature_slot=block.message.slot,
|
||||
)
|
||||
```
|
||||
|
||||
Full nodes SHOULD provide the best derivable `LightClientUpdate` (according to `is_better_update`) for each sync committee period covering any epochs in range `[max(ALTAIR_FORK_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOCK_REQUESTS), current_epoch]` where `current_epoch` is defined by the current wall-clock time. Full nodes MAY also provide `LightClientUpdate` for other sync committee periods.
|
||||
|
||||
- `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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user