180 lines
7.1 KiB
Markdown
180 lines
7.1 KiB
Markdown
# Electra Light Client -- Sync Protocol
|
|
|
|
**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)
|
|
- [Custom types](#custom-types)
|
|
- [Constants](#constants)
|
|
- [Frozen constants](#frozen-constants)
|
|
- [New constants](#new-constants)
|
|
- [Helper functions](#helper-functions)
|
|
- [Modified `finalized_root_gindex_at_slot`](#modified-finalized_root_gindex_at_slot)
|
|
- [Modified `current_sync_committee_gindex_at_slot`](#modified-current_sync_committee_gindex_at_slot)
|
|
- [Modified `next_sync_committee_gindex_at_slot`](#modified-next_sync_committee_gindex_at_slot)
|
|
- [Modified `get_lc_execution_root`](#modified-get_lc_execution_root)
|
|
- [Modified `is_valid_light_client_header`](#modified-is_valid_light_client_header)
|
|
|
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
|
<!-- /TOC -->
|
|
|
|
## Introduction
|
|
|
|
This upgrade updates light client data to include the Electra changes to the [`ExecutionPayload`](../beacon-chain.md) structure and to the generalized indices of surrounding containers. It extends the [Deneb Light Client specifications](../../deneb/light-client/sync-protocol.md). The [fork document](./fork.md) explains how to upgrade existing Deneb based deployments to Electra.
|
|
|
|
Additional documents describes the impact of the upgrade on certain roles:
|
|
- [Full node](./full-node.md)
|
|
- [Networking](./p2p-interface.md)
|
|
|
|
## Custom types
|
|
|
|
| Name | SSZ equivalent | Description |
|
|
| - | - | - |
|
|
| `FinalityBranch` | `Vector[Bytes32, floorlog2(FINALIZED_ROOT_GINDEX_ELECTRA)]` | Merkle branch of `finalized_checkpoint.root` within `BeaconState` |
|
|
| `CurrentSyncCommitteeBranch` | `Vector[Bytes32, floorlog2(CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA)]` | Merkle branch of `current_sync_committee` within `BeaconState` |
|
|
| `NextSyncCommitteeBranch` | `Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA)]` | Merkle branch of `next_sync_committee` within `BeaconState` |
|
|
|
|
## Constants
|
|
|
|
### Frozen constants
|
|
|
|
Existing `GeneralizedIndex` constants are frozen at their [Altair](../../altair/light-client/sync-protocol.md#constants) values.
|
|
|
|
| Name | Value |
|
|
| - | - |
|
|
| `FINALIZED_ROOT_GINDEX` | `get_generalized_index(altair.BeaconState, 'finalized_checkpoint', 'root')` (= 105) |
|
|
| `CURRENT_SYNC_COMMITTEE_GINDEX` | `get_generalized_index(altair.BeaconState, 'current_sync_committee')` (= 54) |
|
|
| `NEXT_SYNC_COMMITTEE_GINDEX` | `get_generalized_index(altair.BeaconState, 'next_sync_committee')` (= 55) |
|
|
|
|
### New constants
|
|
|
|
| Name | Value |
|
|
| - | - |
|
|
| `FINALIZED_ROOT_GINDEX_ELECTRA` | `get_generalized_index(BeaconState, 'finalized_checkpoint', 'root')` (= 169) |
|
|
| `CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA` | `get_generalized_index(BeaconState, 'current_sync_committee')` (= 86) |
|
|
| `NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA` | `get_generalized_index(BeaconState, 'next_sync_committee')` (= 87) |
|
|
|
|
## Helper functions
|
|
|
|
### Modified `finalized_root_gindex_at_slot`
|
|
|
|
```python
|
|
def finalized_root_gindex_at_slot(slot: Slot) -> GeneralizedIndex:
|
|
epoch = compute_epoch_at_slot(slot)
|
|
|
|
# [Modified in Electra]
|
|
if epoch >= ELECTRA_FORK_EPOCH:
|
|
return FINALIZED_ROOT_GINDEX_ELECTRA
|
|
return FINALIZED_ROOT_GINDEX
|
|
```
|
|
|
|
### Modified `current_sync_committee_gindex_at_slot`
|
|
|
|
```python
|
|
def current_sync_committee_gindex_at_slot(slot: Slot) -> GeneralizedIndex:
|
|
epoch = compute_epoch_at_slot(slot)
|
|
|
|
# [Modified in Electra]
|
|
if epoch >= ELECTRA_FORK_EPOCH:
|
|
return CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA
|
|
return CURRENT_SYNC_COMMITTEE_GINDEX
|
|
```
|
|
|
|
### Modified `next_sync_committee_gindex_at_slot`
|
|
|
|
```python
|
|
def next_sync_committee_gindex_at_slot(slot: Slot) -> GeneralizedIndex:
|
|
epoch = compute_epoch_at_slot(slot)
|
|
|
|
# [Modified in Electra]
|
|
if epoch >= ELECTRA_FORK_EPOCH:
|
|
return NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA
|
|
return NEXT_SYNC_COMMITTEE_GINDEX
|
|
```
|
|
|
|
### Modified `get_lc_execution_root`
|
|
|
|
```python
|
|
def get_lc_execution_root(header: LightClientHeader) -> Root:
|
|
epoch = compute_epoch_at_slot(header.beacon.slot)
|
|
|
|
# [New in Electra]
|
|
if epoch >= ELECTRA_FORK_EPOCH:
|
|
return hash_tree_root(header.execution)
|
|
|
|
# [Modified in Electra]
|
|
if epoch >= DENEB_FORK_EPOCH:
|
|
execution_header = deneb.ExecutionPayloadHeader(
|
|
parent_hash=header.execution.parent_hash,
|
|
fee_recipient=header.execution.fee_recipient,
|
|
state_root=header.execution.state_root,
|
|
receipts_root=header.execution.receipts_root,
|
|
logs_bloom=header.execution.logs_bloom,
|
|
prev_randao=header.execution.prev_randao,
|
|
block_number=header.execution.block_number,
|
|
gas_limit=header.execution.gas_limit,
|
|
gas_used=header.execution.gas_used,
|
|
timestamp=header.execution.timestamp,
|
|
extra_data=header.execution.extra_data,
|
|
base_fee_per_gas=header.execution.base_fee_per_gas,
|
|
block_hash=header.execution.block_hash,
|
|
transactions_root=header.execution.transactions_root,
|
|
withdrawals_root=header.execution.withdrawals_root,
|
|
blob_gas_used=header.execution.blob_gas_used,
|
|
excess_blob_gas=header.execution.excess_blob_gas,
|
|
)
|
|
return hash_tree_root(execution_header)
|
|
|
|
if epoch >= CAPELLA_FORK_EPOCH:
|
|
execution_header = capella.ExecutionPayloadHeader(
|
|
parent_hash=header.execution.parent_hash,
|
|
fee_recipient=header.execution.fee_recipient,
|
|
state_root=header.execution.state_root,
|
|
receipts_root=header.execution.receipts_root,
|
|
logs_bloom=header.execution.logs_bloom,
|
|
prev_randao=header.execution.prev_randao,
|
|
block_number=header.execution.block_number,
|
|
gas_limit=header.execution.gas_limit,
|
|
gas_used=header.execution.gas_used,
|
|
timestamp=header.execution.timestamp,
|
|
extra_data=header.execution.extra_data,
|
|
base_fee_per_gas=header.execution.base_fee_per_gas,
|
|
block_hash=header.execution.block_hash,
|
|
transactions_root=header.execution.transactions_root,
|
|
withdrawals_root=header.execution.withdrawals_root,
|
|
)
|
|
return hash_tree_root(execution_header)
|
|
|
|
return Root()
|
|
```
|
|
|
|
### Modified `is_valid_light_client_header`
|
|
|
|
```python
|
|
def is_valid_light_client_header(header: LightClientHeader) -> bool:
|
|
epoch = compute_epoch_at_slot(header.beacon.slot)
|
|
|
|
if epoch < DENEB_FORK_EPOCH:
|
|
if header.execution.blob_gas_used != uint64(0) or header.execution.excess_blob_gas != uint64(0):
|
|
return False
|
|
|
|
if epoch < CAPELLA_FORK_EPOCH:
|
|
return (
|
|
header.execution == ExecutionPayloadHeader()
|
|
and header.execution_branch == ExecutionBranch()
|
|
)
|
|
|
|
return is_valid_merkle_branch(
|
|
leaf=get_lc_execution_root(header),
|
|
branch=header.execution_branch,
|
|
depth=floorlog2(EXECUTION_PAYLOAD_GINDEX),
|
|
index=get_subtree_index(EXECUTION_PAYLOAD_GINDEX),
|
|
root=header.beacon.body_root,
|
|
)
|
|
```
|