Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update light client specifications for Electra #3811

Merged
merged 10 commits into from
Jul 19, 2024
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Features are researched and developed in parallel, and then consolidated into se
### In-development Specifications
| Code Name or Topic | Specs | Notes |
| - | - | - |
| Electra | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/electra/beacon-chain.md)</li><li>[EIP-6110 fork](specs/electra/fork.md)</li></ul><li>Additions</li><ul><li>[Honest validator guide changes](specs/electra/validator.md)</li></ul></ul> |
| Electra | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/electra/beacon-chain.md)</li><li>[EIP-6110 fork](specs/electra/fork.md)</li></ul><li>Additions</li><ul><li>[Light client sync protocol changes](specs/electra/light-client/sync-protocol.md) ([fork](specs/electra/light-client/fork.md), [full node](specs/electra/light-client/full-node.md), [networking](specs/electra/light-client/p2p-interface.md))</li></ul><ul><li>[Honest validator guide changes](specs/electra/validator.md)</li></ul></ul> |
| Sharding (outdated) | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/_features/sharding/beacon-chain.md)</li></ul><li>Additions</li><ul><li>[P2P networking](specs/_features/sharding/p2p-interface.md)</li></ul></ul> |
| Custody Game (outdated) | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/_features/custody_game/beacon-chain.md)</li></ul><li>Additions</li><ul><li>[Honest validator guide changes](specs/_features/custody_game/validator.md)</li></ul></ul> | Dependent on sharding |
| Data Availability Sampling (outdated) | <ul><li>Core</li><ul><li>[Core types and functions](specs/_features/das/das-core.md)</li><li>[Fork choice changes](specs/_features/das/fork-choice.md)</li></ul><li>Additions</li><ul><li>[P2P Networking](specs/_features/das/p2p-interface.md)</li><li>[Sampling process](specs/_features/das/sampling.md)</li></ul></ul> | <ul><li> Dependent on sharding</li><li>[Technical explainer](https://hackmd.io/@HWeNw8hNRimMm2m2GH56Cw/B1YJPGkpD)</li></ul> |
Expand Down
8 changes: 3 additions & 5 deletions pysetup/spec_builders/electra.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ def imports(cls, preset_name: str):
from eth2spec.deneb import {preset_name} as deneb
'''

## TODO: deal with changed gindices

@classmethod
def hardcoded_ssz_dep_constants(cls) -> Dict[str, str]:
return {
'FINALIZED_ROOT_GINDEX': 'GeneralizedIndex(169)',
'CURRENT_SYNC_COMMITTEE_GINDEX': 'GeneralizedIndex(86)',
'NEXT_SYNC_COMMITTEE_GINDEX': 'GeneralizedIndex(87)',
'FINALIZED_ROOT_GINDEX_ELECTRA': 'GeneralizedIndex(169)',
'CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA': 'GeneralizedIndex(86)',
'NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA': 'GeneralizedIndex(87)',
}
6 changes: 3 additions & 3 deletions specs/altair/light-client/full-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def create_light_client_bootstrap(state: BeaconState,
header=block_to_light_client_header(block),
current_sync_committee=state.current_sync_committee,
current_sync_committee_branch=CurrentSyncCommitteeBranch(
compute_merkle_proof(state, CURRENT_SYNC_COMMITTEE_GINDEX)),
compute_merkle_proof(state, current_sync_committee_gindex_at_slot(state.slot))),
)
```

Expand Down Expand Up @@ -124,7 +124,7 @@ def create_light_client_update(state: BeaconState,
if update_attested_period == update_signature_period:
update.next_sync_committee = attested_state.next_sync_committee
update.next_sync_committee_branch = NextSyncCommitteeBranch(
compute_merkle_proof(attested_state, NEXT_SYNC_COMMITTEE_GINDEX))
compute_merkle_proof(attested_state, next_sync_committee_gindex_at_slot(attested_state.slot)))

# Indicate finality whenever possible
if finalized_block is not None:
Expand All @@ -134,7 +134,7 @@ def create_light_client_update(state: BeaconState,
else:
assert attested_state.finalized_checkpoint.root == Bytes32()
update.finality_branch = FinalityBranch(
compute_merkle_proof(attested_state, FINALIZED_ROOT_GINDEX))
compute_merkle_proof(attested_state, finalized_root_gindex_at_slot(attested_state.slot)))

update.sync_aggregate = block.message.body.sync_aggregate
update.signature_slot = block.message.slot
Expand Down
59 changes: 50 additions & 9 deletions specs/altair/light-client/sync-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@
- [`LightClientOptimisticUpdate`](#lightclientoptimisticupdate)
- [`LightClientStore`](#lightclientstore)
- [Helper functions](#helper-functions)
- [`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)
- [`is_valid_light_client_header`](#is_valid_light_client_header)
- [`is_sync_committee_update`](#is_sync_committee_update)
- [`is_finality_update`](#is_finality_update)
- [`is_better_update`](#is_better_update)
- [`is_next_sync_committee_known`](#is_next_sync_committee_known)
- [`get_safety_threshold`](#get_safety_threshold)
- [`get_subtree_index`](#get_subtree_index)
- [`is_valid_normalized_merkle_branch`](#is_valid_normalized_merkle_branch)
- [`compute_sync_committee_period_at_slot`](#compute_sync_committee_period_at_slot)
- [Light client initialization](#light-client-initialization)
- [`initialize_light_client_store`](#initialize_light_client_store)
Expand Down Expand Up @@ -171,6 +175,30 @@ class LightClientStore(object):

## Helper functions

### `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
```

### `is_valid_light_client_header`

```python
Expand Down Expand Up @@ -273,6 +301,22 @@ def get_subtree_index(generalized_index: GeneralizedIndex) -> uint64:
return uint64(generalized_index % 2**(floorlog2(generalized_index)))
```

### `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)
```

### `compute_sync_committee_period_at_slot`

```python
Expand All @@ -292,11 +336,10 @@ def initialize_light_client_store(trusted_block_root: Root,
assert is_valid_light_client_header(bootstrap.header)
assert hash_tree_root(bootstrap.header.beacon) == trusted_block_root

assert is_valid_merkle_branch(
assert is_valid_normalized_merkle_branch(
leaf=hash_tree_root(bootstrap.current_sync_committee),
branch=bootstrap.current_sync_committee_branch,
depth=floorlog2(CURRENT_SYNC_COMMITTEE_GINDEX),
index=get_subtree_index(CURRENT_SYNC_COMMITTEE_GINDEX),
gindex=current_sync_committee_gindex_at_slot(bootstrap.header.beacon.slot),
root=bootstrap.header.beacon.state_root,
)

Expand Down Expand Up @@ -364,11 +407,10 @@ def validate_light_client_update(store: LightClientStore,
else:
assert is_valid_light_client_header(update.finalized_header)
finalized_root = hash_tree_root(update.finalized_header.beacon)
assert is_valid_merkle_branch(
assert is_valid_normalized_merkle_branch(
leaf=finalized_root,
branch=update.finality_branch,
depth=floorlog2(FINALIZED_ROOT_GINDEX),
index=get_subtree_index(FINALIZED_ROOT_GINDEX),
gindex=finalized_root_gindex_at_slot(update.attested_header.beacon.slot),
root=update.attested_header.beacon.state_root,
)

Expand All @@ -379,11 +421,10 @@ def validate_light_client_update(store: LightClientStore,
else:
if update_attested_period == store_period and is_next_sync_committee_known(store):
assert update.next_sync_committee == store.next_sync_committee
assert is_valid_merkle_branch(
assert is_valid_normalized_merkle_branch(
leaf=hash_tree_root(update.next_sync_committee),
branch=update.next_sync_committee_branch,
depth=floorlog2(NEXT_SYNC_COMMITTEE_GINDEX),
index=get_subtree_index(NEXT_SYNC_COMMITTEE_GINDEX),
gindex=next_sync_committee_gindex_at_slot(update.attested_header.beacon.slot),
root=update.attested_header.beacon.state_root,
)

Expand Down
8 changes: 4 additions & 4 deletions specs/capella/light-client/fork.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Introduction](#introduction)
- [Upgrading light client data](#upgrading-light-client-data)
- [Upgrading the store](#upgrading-the-store)
- [Upgrading light client data](#upgrading-light-client-data)
- [Upgrading the store](#upgrading-the-store)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
Expand All @@ -17,7 +17,7 @@

This document describes how to upgrade existing light client objects based on the [Altair specification](../../altair/light-client/sync-protocol.md) to Capella. This is necessary when processing pre-Capella data with a post-Capella `LightClientStore`. Note that the data being exchanged over the network protocols uses the original format.

### Upgrading light client data
## Upgrading light client data

A Capella `LightClientStore` can still process earlier light client data. In order to do so, that pre-Capella data needs to be locally upgraded to Capella before processing.

Expand Down Expand Up @@ -70,7 +70,7 @@ def upgrade_lc_optimistic_update_to_capella(pre: bellatrix.LightClientOptimistic
)
```

### Upgrading the store
## Upgrading the store

Existing `LightClientStore` objects based on Altair MUST be upgraded to Capella before Capella based light client data can be processed. The `LightClientStore` upgrade MAY be performed before `CAPELLA_FORK_EPOCH`.

Expand Down
8 changes: 4 additions & 4 deletions specs/deneb/light-client/fork.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Introduction](#introduction)
- [Upgrading light client data](#upgrading-light-client-data)
- [Upgrading the store](#upgrading-the-store)
- [Upgrading light client data](#upgrading-light-client-data)
- [Upgrading the store](#upgrading-the-store)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
Expand All @@ -17,7 +17,7 @@

This document describes how to upgrade existing light client objects based on the [Capella specification](../../capella/light-client/sync-protocol.md) to Deneb. This is necessary when processing pre-Deneb data with a post-Deneb `LightClientStore`. Note that the data being exchanged over the network protocols uses the original format.

### Upgrading light client data
## Upgrading light client data

A Deneb `LightClientStore` can still process earlier light client data. In order to do so, that pre-Deneb data needs to be locally upgraded to Deneb before processing.

Expand Down Expand Up @@ -90,7 +90,7 @@ def upgrade_lc_optimistic_update_to_deneb(pre: capella.LightClientOptimisticUpda
)
```

### Upgrading the store
## Upgrading the store

Existing `LightClientStore` objects based on Capella MUST be upgraded to Deneb before Deneb based light client data can be processed. The `LightClientStore` upgrade MAY be performed before `DENEB_FORK_EPOCH`.

Expand Down
2 changes: 1 addition & 1 deletion specs/deneb/light-client/full-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

## Introduction

This upgrade adds information about the execution payload to light client data as part of the Deneb upgrade.
Execution payload data is updated to account for the Deneb upgrade.

## Helper functions

Expand Down
133 changes: 133 additions & 0 deletions specs/electra/light-client/fork.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Electra Light Client -- Fork Logic

## 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)
- [`normalize_merkle_branch`](#normalize_merkle_branch)
- [Upgrading light client data](#upgrading-light-client-data)
- [Upgrading the store](#upgrading-the-store)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->

## Introduction

This document describes how to upgrade existing light client objects based on the [Deneb specification](../../deneb/light-client/sync-protocol.md) to Electra. This is necessary when processing pre-Electra data with a post-Electra `LightClientStore`. Note that the data being exchanged over the network protocols uses the original format.

## Helper functions

### `normalize_merkle_branch`

```python
def normalize_merkle_branch(branch: Sequence[Bytes32],
gindex: GeneralizedIndex) -> Sequence[Bytes32]:
depth = floorlog2(gindex)
num_extra = depth - len(branch)
return [Bytes32()] * num_extra + [*branch]
```

## Upgrading light client data

A Electra `LightClientStore` can still process earlier light client data. In order to do so, that pre-Electra data needs to be locally upgraded to Electra before processing.

```python
def upgrade_lc_header_to_electra(pre: deneb.LightClientHeader) -> LightClientHeader:
return LightClientHeader(
beacon=pre.beacon,
execution=ExecutionPayloadHeader(
parent_hash=pre.execution.parent_hash,
fee_recipient=pre.execution.fee_recipient,
state_root=pre.execution.state_root,
receipts_root=pre.execution.receipts_root,
logs_bloom=pre.execution.logs_bloom,
prev_randao=pre.execution.prev_randao,
block_number=pre.execution.block_number,
gas_limit=pre.execution.gas_limit,
gas_used=pre.execution.gas_used,
timestamp=pre.execution.timestamp,
extra_data=pre.execution.extra_data,
base_fee_per_gas=pre.execution.base_fee_per_gas,
block_hash=pre.execution.block_hash,
transactions_root=pre.execution.transactions_root,
withdrawals_root=pre.execution.withdrawals_root,
blob_gas_used=pre.execution.blob_gas_used,
excess_blob_gas=pre.execution.blob_gas_used,
deposit_requests_root=Root(), # [New in Electra:EIP6110]
withdrawal_requests_root=Root(), # [New in Electra:EIP7002:EIP7251]
consolidation_requests_root=Root(), # [New in Electra:EIP7251]
),
execution_branch=pre.execution_branch,
)
```

```python
def upgrade_lc_bootstrap_to_electra(pre: deneb.LightClientBootstrap) -> LightClientBootstrap:
return LightClientBootstrap(
header=upgrade_lc_header_to_electra(pre.header),
current_sync_committee=pre.current_sync_committee,
current_sync_committee_branch=normalize_merkle_branch(
pre.current_sync_committee_branch, CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA),
)
```

```python
def upgrade_lc_update_to_electra(pre: deneb.LightClientUpdate) -> LightClientUpdate:
return LightClientUpdate(
attested_header=upgrade_lc_header_to_electra(pre.attested_header),
next_sync_committee=pre.next_sync_committee,
next_sync_committee_branch=normalize_merkle_branch(
pre.next_sync_committee_branch, NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA),
finalized_header=upgrade_lc_header_to_electra(pre.finalized_header),
finality_branch=normalize_merkle_branch(
pre.finality_branch, FINALIZED_ROOT_GINDEX_ELECTRA),
sync_aggregate=pre.sync_aggregate,
signature_slot=pre.signature_slot,
)
```

```python
def upgrade_lc_finality_update_to_electra(pre: deneb.LightClientFinalityUpdate) -> LightClientFinalityUpdate:
return LightClientFinalityUpdate(
attested_header=upgrade_lc_header_to_electra(pre.attested_header),
finalized_header=upgrade_lc_header_to_electra(pre.finalized_header),
finality_branch=normalize_merkle_branch(
pre.finality_branch, FINALIZED_ROOT_GINDEX_ELECTRA),
sync_aggregate=pre.sync_aggregate,
signature_slot=pre.signature_slot,
)
```

```python
def upgrade_lc_optimistic_update_to_electra(pre: deneb.LightClientOptimisticUpdate) -> LightClientOptimisticUpdate:
return LightClientOptimisticUpdate(
attested_header=upgrade_lc_header_to_electra(pre.attested_header),
sync_aggregate=pre.sync_aggregate,
signature_slot=pre.signature_slot,
)
```

## Upgrading the store

Existing `LightClientStore` objects based on Deneb MUST be upgraded to Electra before Electra based light client data can be processed. The `LightClientStore` upgrade MAY be performed before `ELECTRA_FORK_EPOCH`.

```python
def upgrade_lc_store_to_electra(pre: deneb.LightClientStore) -> LightClientStore:
if pre.best_valid_update is None:
best_valid_update = None
else:
best_valid_update = upgrade_lc_update_to_electra(pre.best_valid_update)
return LightClientStore(
finalized_header=upgrade_lc_header_to_electra(pre.finalized_header),
current_sync_committee=pre.current_sync_committee,
next_sync_committee=pre.next_sync_committee,
best_valid_update=best_valid_update,
optimistic_header=upgrade_lc_header_to_electra(pre.optimistic_header),
previous_max_active_participants=pre.previous_max_active_participants,
current_max_active_participants=pre.current_max_active_participants,
)
```
Loading