7.1 KiB
7.1 KiB
Electra Light Client -- Sync Protocol
Notice: This document is a work-in-progress for researchers and implementers.
Table of contents
Introduction
This upgrade updates light client data to include the Electra changes to the ExecutionPayload
structure and to the generalized indices of surrounding containers. It extends the Deneb Light Client specifications. The fork document explains how to upgrade existing Deneb based deployments to Electra.
Additional documents describes the impact of the upgrade on certain roles:
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 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
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
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
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
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
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,
)