mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-01-19 07:03:12 +00:00
12 KiB
12 KiB
Capella Light Client -- Sync Protocol
Notice: This document is a work-in-progress for researchers and implementers.
Table of contents
- Introduction
- Constants
- Containers
- Helper functions
- Light client initialization
- Light client state updates
Introduction
This upgrade adds information about the execution payload to light client data as part of the Capella upgrade. It extends the Altair Light Client specifications. The fork document explains how to upgrade existing Altair based deployments to Capella.
Additional documents describes the impact of the upgrade on certain roles:
Constants
Name | Value |
---|---|
EXECUTION_PAYLOAD_INDEX |
get_generalized_index(BeaconBlockBody, 'execution_payload') (= 25) |
Containers
LightClientHeader
class LightClientHeader(Container):
# Beacon block header
beacon: BeaconBlockHeader
# Execution payload header corresponding to `beacon.body_root` (from Capella onward)
execution: ExecutionPayloadHeader
execution_branch: Vector[Bytes32, floorlog2(EXECUTION_PAYLOAD_INDEX)]
Modified LightClientBootstrap
class LightClientBootstrap(Container):
# Header matching the requested beacon block root
header: LightClientHeader # [Modified in Capella]
# Current sync committee corresponding to `header.beacon.state_root`
current_sync_committee: SyncCommittee
current_sync_committee_branch: Vector[Bytes32, floorlog2(CURRENT_SYNC_COMMITTEE_INDEX)]
Modified LightClientUpdate
class LightClientUpdate(Container):
# Header attested to by the sync committee
attested_header: LightClientHeader # [Modified in Capella]
# Next sync committee corresponding to `attested_header.beacon.state_root`
next_sync_committee: SyncCommittee
next_sync_committee_branch: Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_INDEX)]
# Finalized header corresponding to `attested_header.beacon.state_root`
finalized_header: LightClientHeader # [Modified in Capella]
finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)]
# Sync committee aggregate signature
sync_aggregate: SyncAggregate
# Slot at which the aggregate signature was created (untrusted)
signature_slot: Slot
Modified LightClientFinalityUpdate
class LightClientFinalityUpdate(Container):
# Header attested to by the sync committee
attested_header: LightClientHeader # [Modified in Capella]
# Finalized header corresponding to `attested_header.beacon.state_root`
finalized_header: LightClientHeader # [Modified in Capella]
finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)]
# Sync committee aggregate signature
sync_aggregate: SyncAggregate
# Slot at which the aggregate signature was created (untrusted)
signature_slot: Slot
Modified LightClientOptimisticUpdate
class LightClientOptimisticUpdate(Container):
# Header attested to by the sync committee
attested_header: LightClientHeader # [Modified in Capella]
# Sync committee aggregate signature
sync_aggregate: SyncAggregate
# Slot at which the aggregate signature was created (untrusted)
signature_slot: Slot
Modified LightClientStore
@dataclass
class LightClientStore(object):
# Header that is finalized
finalized_header: LightClientHeader # [Modified in Capella]
# Sync committees corresponding to the finalized header
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
# Best available header to switch finalized head to if we see nothing else
best_valid_update: Optional[LightClientUpdate]
# Most recent available reasonably-safe header
optimistic_header: LightClientHeader # [Modified in Capella]
# Max number of active participants in a sync committee (used to calculate safety threshold)
previous_max_active_participants: uint64
current_max_active_participants: uint64
Helper functions
Modified get_lc_beacon_slot
def get_lc_beacon_slot(header: LightClientHeader) -> Slot:
return header.beacon.slot
Modified get_lc_beacon_root
def get_lc_beacon_root(header: LightClientHeader) -> Root:
return hash_tree_root(header.beacon)
get_lc_execution_root
def get_lc_execution_root(header: LightClientHeader) -> Root:
epoch = compute_epoch_at_slot(get_lc_beacon_slot(header))
if epoch >= CAPELLA_FORK_EPOCH:
return hash_tree_root(header.execution)
return Root()
is_valid_light_client_header
def is_valid_light_client_header(header: LightClientHeader) -> bool:
epoch = compute_epoch_at_slot(get_lc_beacon_slot(header))
if epoch < CAPELLA_FORK_EPOCH:
return (
header.execution == ExecutionPayloadHeader()
and header.execution_branch == [Bytes32() for _ in range(floorlog2(EXECUTION_PAYLOAD_INDEX))]
)
return is_valid_merkle_branch(
leaf=get_lc_execution_root(header),
branch=header.execution_branch,
depth=floorlog2(EXECUTION_PAYLOAD_INDEX),
index=get_subtree_index(EXECUTION_PAYLOAD_INDEX),
root=header.beacon.body_root,
)
Light client initialization
Modified initialize_light_client_store
def initialize_light_client_store(trusted_block_root: Root,
bootstrap: LightClientBootstrap) -> LightClientStore:
assert is_valid_light_client_header(bootstrap.header) # [New in Capella]
assert get_lc_beacon_root(bootstrap.header) == trusted_block_root
assert is_valid_merkle_branch(
leaf=hash_tree_root(bootstrap.current_sync_committee),
branch=bootstrap.current_sync_committee_branch,
depth=floorlog2(CURRENT_SYNC_COMMITTEE_INDEX),
index=get_subtree_index(CURRENT_SYNC_COMMITTEE_INDEX),
root=bootstrap.header.beacon.state_root, # [Modified in Capella]
)
return LightClientStore(
finalized_header=bootstrap.header,
current_sync_committee=bootstrap.current_sync_committee,
next_sync_committee=SyncCommittee(),
best_valid_update=None,
optimistic_header=bootstrap.header,
previous_max_active_participants=0,
current_max_active_participants=0,
)
Light client state updates
Modified validate_light_client_update
def validate_light_client_update(store: LightClientStore,
update: LightClientUpdate,
current_slot: Slot,
genesis_validators_root: Root) -> None:
# Verify sync committee has sufficient participants
sync_aggregate = update.sync_aggregate
assert sum(sync_aggregate.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS
# Verify update does not skip a sync committee period
assert is_valid_light_client_header(update.attested_header) # [New in Capella]
update_attested_slot = get_lc_beacon_slot(update.attested_header)
update_finalized_slot = get_lc_beacon_slot(update.finalized_header)
assert current_slot >= update.signature_slot > update_attested_slot >= update_finalized_slot
store_period = compute_sync_committee_period_at_slot(get_lc_beacon_slot(store.finalized_header))
update_signature_period = compute_sync_committee_period_at_slot(update.signature_slot)
if is_next_sync_committee_known(store):
assert update_signature_period in (store_period, store_period + 1)
else:
assert update_signature_period == store_period
# Verify update is relevant
update_attested_period = compute_sync_committee_period_at_slot(update_attested_slot)
update_has_next_sync_committee = not is_next_sync_committee_known(store) and (
is_sync_committee_update(update) and update_attested_period == store_period
)
assert update_attested_slot > update_finalized_slot or update_has_next_sync_committee
# Verify that the `finality_branch`, if present, confirms `finalized_header.beacon`
# to match the finalized checkpoint root saved in the state of `attested_header.beacon`.
# Note that the genesis finalized checkpoint root is represented as a zero hash.
if not is_finality_update(update):
assert update.finalized_header == LightClientHeader() # [Modified in Capella]
else:
if update_finalized_slot == GENESIS_SLOT:
assert update.finalized_header == LightClientHeader() # [Modified in Capella]
finalized_root = Bytes32()
else:
assert is_valid_light_client_header(update.finalized_header) # [New in Capella]
finalized_root = get_lc_beacon_root(update.finalized_header)
assert is_valid_merkle_branch(
leaf=finalized_root,
branch=update.finality_branch,
depth=floorlog2(FINALIZED_ROOT_INDEX),
index=get_subtree_index(FINALIZED_ROOT_INDEX),
root=update.attested_header.beacon.state_root, # [Modified in Capella]
)
# Verify that the `next_sync_committee`, if present, actually is the next sync committee saved in the
# state of the `attested_header.beacon`
if not is_sync_committee_update(update):
assert update.next_sync_committee == SyncCommittee()
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(
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.attested_header.beacon.state_root, # [Modified in Capella]
)
# Verify sync committee aggregate signature
if update_signature_period == store_period:
sync_committee = store.current_sync_committee
else:
sync_committee = store.next_sync_committee
participant_pubkeys = [
pubkey for (bit, pubkey) in zip(sync_aggregate.sync_committee_bits, sync_committee.pubkeys)
if bit
]
fork_version = compute_fork_version(compute_epoch_at_slot(update.signature_slot))
domain = compute_domain(DOMAIN_SYNC_COMMITTEE, fork_version, genesis_validators_root)
signing_root = compute_signing_root(update.attested_header.beacon, domain) # [Modified in Capella]
assert bls.FastAggregateVerify(participant_pubkeys, signing_root, sync_aggregate.sync_committee_signature)