Update light client specifications for Electra
Electra introduces two changes that affect light client data handling: 1. The `ExecutionPayloadHeader` is extended with new fields. This is handled similarly as before with the Deneb fork. 2. The `BeaconState` generalized indices change due to lack of EIP-6493. This is handled by making the generalized index be fork dependent via a helper function that computes it dynamically. Furthermore, the case where pre-Electra light client data is consumed by an Electra based `LightClientStore` requires normalizing the shorter proof of the pre-Electra data to fit into the Electra data structure by prepending a zero hash.
This commit is contained in:
parent
29d3a24f46
commit
2035a9fcad
|
@ -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> |
|
||||
|
|
|
@ -12,8 +12,6 @@ class ElectraSpecBuilder(BaseSpecBuilder):
|
|||
from eth2spec.deneb import {preset_name} as deneb
|
||||
'''
|
||||
|
||||
## TODO: deal with changed gindices
|
||||
|
||||
@classmethod
|
||||
def hardcoded_ssz_dep_constants(cls) -> Dict[str, str]:
|
||||
return {
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
- [`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)
|
||||
|
@ -28,6 +31,7 @@
|
|||
- [`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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
)
|
||||
|
||||
|
@ -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,
|
||||
)
|
||||
|
||||
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
@ -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 -->
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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`.
|
||||
|
||||
|
|
|
@ -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 -->
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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`.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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),
|
||||
)
|
||||
```
|
||||
|
||||
```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),
|
||||
finalized_header=upgrade_lc_header_to_electra(pre.finalized_header),
|
||||
finality_branch=normalize_merkle_branch(
|
||||
pre.finality_branch, FINALIZED_ROOT_GINDEX),
|
||||
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),
|
||||
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,
|
||||
)
|
||||
```
|
|
@ -0,0 +1,80 @@
|
|||
# Electra 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)
|
||||
- [Modified `block_to_light_client_header`](#modified-block_to_light_client_header)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- /TOC -->
|
||||
|
||||
## Introduction
|
||||
|
||||
Execution payload data is updated to account for the Electra upgrade.
|
||||
|
||||
## Helper functions
|
||||
|
||||
### Modified `block_to_light_client_header`
|
||||
|
||||
```python
|
||||
def block_to_light_client_header(block: SignedBeaconBlock) -> LightClientHeader:
|
||||
epoch = compute_epoch_at_slot(block.message.slot)
|
||||
|
||||
if epoch >= CAPELLA_FORK_EPOCH:
|
||||
payload = block.message.body.execution_payload
|
||||
execution_header = ExecutionPayloadHeader(
|
||||
parent_hash=payload.parent_hash,
|
||||
fee_recipient=payload.fee_recipient,
|
||||
state_root=payload.state_root,
|
||||
receipts_root=payload.receipts_root,
|
||||
logs_bloom=payload.logs_bloom,
|
||||
prev_randao=payload.prev_randao,
|
||||
block_number=payload.block_number,
|
||||
gas_limit=payload.gas_limit,
|
||||
gas_used=payload.gas_used,
|
||||
timestamp=payload.timestamp,
|
||||
extra_data=payload.extra_data,
|
||||
base_fee_per_gas=payload.base_fee_per_gas,
|
||||
block_hash=payload.block_hash,
|
||||
transactions_root=hash_tree_root(payload.transactions),
|
||||
withdrawals_root=hash_tree_root(payload.withdrawals),
|
||||
)
|
||||
if epoch >= DENEB_FORK_EPOCH:
|
||||
execution_header.blob_gas_used = payload.blob_gas_used
|
||||
execution_header.excess_blob_gas = payload.excess_blob_gas
|
||||
|
||||
# [New in Electra:EIP6110:EIP7002:EIP7251]
|
||||
if epoch >= ELECTRA_FORK_EPOCH:
|
||||
execution_header.deposit_requests_root = hash_tree_root(payload.deposit_requests)
|
||||
execution_header.withdrawal_requests_root = hash_tree_root(payload.withdrawal_requests)
|
||||
execution_header.consolidation_requests_root = hash_tree_root(payload.consolidation_requests)
|
||||
|
||||
execution_branch = ExecutionBranch(
|
||||
compute_merkle_proof(block.message.body, EXECUTION_PAYLOAD_GINDEX))
|
||||
else:
|
||||
# Note that during fork transitions, `finalized_header` may still point to earlier forks.
|
||||
# While Bellatrix blocks also contain an `ExecutionPayload` (minus `withdrawals_root`),
|
||||
# it was not included in the corresponding light client data. To ensure compatibility
|
||||
# with legacy data going through `upgrade_lc_header_to_capella`, leave out execution data.
|
||||
execution_header = ExecutionPayloadHeader()
|
||||
execution_branch = ExecutionBranch()
|
||||
|
||||
return LightClientHeader(
|
||||
beacon=BeaconBlockHeader(
|
||||
slot=block.message.slot,
|
||||
proposer_index=block.message.proposer_index,
|
||||
parent_root=block.message.parent_root,
|
||||
state_root=block.message.state_root,
|
||||
body_root=hash_tree_root(block.message.body),
|
||||
),
|
||||
execution=execution_header,
|
||||
execution_branch=execution_branch,
|
||||
)
|
||||
```
|
|
@ -0,0 +1,111 @@
|
|||
# Electra Light Client -- Networking
|
||||
|
||||
**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 -->
|
||||
|
||||
- [Networking](#networking)
|
||||
- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub)
|
||||
- [Topics and messages](#topics-and-messages)
|
||||
- [Global topics](#global-topics)
|
||||
- [`light_client_finality_update`](#light_client_finality_update)
|
||||
- [`light_client_optimistic_update`](#light_client_optimistic_update)
|
||||
- [The Req/Resp domain](#the-reqresp-domain)
|
||||
- [Messages](#messages)
|
||||
- [GetLightClientBootstrap](#getlightclientbootstrap)
|
||||
- [LightClientUpdatesByRange](#lightclientupdatesbyrange)
|
||||
- [GetLightClientFinalityUpdate](#getlightclientfinalityupdate)
|
||||
- [GetLightClientOptimisticUpdate](#getlightclientoptimisticupdate)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- /TOC -->
|
||||
|
||||
## Networking
|
||||
|
||||
The [Deneb light client networking specification](../../deneb/light-client/p2p-interface.md) is extended to exchange [Electra light client data](./sync-protocol.md).
|
||||
|
||||
### The gossip domain: gossipsub
|
||||
|
||||
#### Topics and messages
|
||||
|
||||
##### Global topics
|
||||
|
||||
###### `light_client_finality_update`
|
||||
|
||||
[0]: # (eth2spec: skip)
|
||||
|
||||
| `fork_version` | Message SSZ type |
|
||||
|--------------------------------------------------------|-------------------------------------|
|
||||
| `GENESIS_FORK_VERSION` | n/a |
|
||||
| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientFinalityUpdate` |
|
||||
| `CAPELLA_FORK_VERSION` | `capella.LightClientFinalityUpdate` |
|
||||
| `DENEB_FORK_VERSION` | `deneb.LightClientFinalityUpdate` |
|
||||
| `ELECTRA_FORK_VERSION` and later | `electra.LightClientFinalityUpdate` |
|
||||
|
||||
###### `light_client_optimistic_update`
|
||||
|
||||
[0]: # (eth2spec: skip)
|
||||
|
||||
| `fork_version` | Message SSZ type |
|
||||
|--------------------------------------------------------|---------------------------------------|
|
||||
| `GENESIS_FORK_VERSION` | n/a |
|
||||
| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientOptimisticUpdate` |
|
||||
| `CAPELLA_FORK_VERSION` | `capella.LightClientOptimisticUpdate` |
|
||||
| `DENEB_FORK_VERSION` | `deneb.LightClientOptimisticUpdate` |
|
||||
| `ELECTRA_FORK_VERSION` and later | `electra.LightClientOptimisticUpdate` |
|
||||
|
||||
### The Req/Resp domain
|
||||
|
||||
#### Messages
|
||||
|
||||
##### GetLightClientBootstrap
|
||||
|
||||
[0]: # (eth2spec: skip)
|
||||
|
||||
| `fork_version` | Response SSZ type |
|
||||
|--------------------------------------------------------|------------------------------------|
|
||||
| `GENESIS_FORK_VERSION` | n/a |
|
||||
| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientBootstrap` |
|
||||
| `CAPELLA_FORK_VERSION` | `capella.LightClientBootstrap` |
|
||||
| `DENEB_FORK_VERSION` | `deneb.LightClientBootstrap` |
|
||||
| `ELECTRA_FORK_VERSION` and later | `electra.LightClientBootstrap` |
|
||||
|
||||
##### LightClientUpdatesByRange
|
||||
|
||||
[0]: # (eth2spec: skip)
|
||||
|
||||
| `fork_version` | Response chunk SSZ type |
|
||||
|--------------------------------------------------------|----------------------------------|
|
||||
| `GENESIS_FORK_VERSION` | n/a |
|
||||
| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientUpdate` |
|
||||
| `CAPELLA_FORK_VERSION` | `capella.LightClientUpdate` |
|
||||
| `DENEB_FORK_VERSION` | `deneb.LightClientUpdate` |
|
||||
| `ELECTRA_FORK_VERSION` and later | `electra.LightClientUpdate` |
|
||||
|
||||
##### GetLightClientFinalityUpdate
|
||||
|
||||
[0]: # (eth2spec: skip)
|
||||
|
||||
| `fork_version` | Response SSZ type |
|
||||
|--------------------------------------------------------|-------------------------------------|
|
||||
| `GENESIS_FORK_VERSION` | n/a |
|
||||
| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientFinalityUpdate` |
|
||||
| `CAPELLA_FORK_VERSION` | `capella.LightClientFinalityUpdate` |
|
||||
| `DENEB_FORK_VERSION` | `deneb.LightClientFinalityUpdate` |
|
||||
| `ELECTRA_FORK_VERSION` and later | `electra.LightClientFinalityUpdate` |
|
||||
|
||||
##### GetLightClientOptimisticUpdate
|
||||
|
||||
[0]: # (eth2spec: skip)
|
||||
|
||||
| `fork_version` | Response SSZ type |
|
||||
|--------------------------------------------------------|---------------------------------------|
|
||||
| `GENESIS_FORK_VERSION` | n/a |
|
||||
| `ALTAIR_FORK_VERSION` through `BELLATRIX_FORK_VERSION` | `altair.LightClientOptimisticUpdate` |
|
||||
| `CAPELLA_FORK_VERSION` | `capella.LightClientOptimisticUpdate` |
|
||||
| `DENEB_FORK_VERSION` | `deneb.LightClientOptimisticUpdate` |
|
||||
| `ELECTRA_FORK_VERSION` and later | `electra.LightClientOptimisticUpdate` |
|
|
@ -0,0 +1,165 @@
|
|||
# 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)
|
||||
- [Constants](#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)
|
||||
|
||||
## Constants
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `FINALIZED_ROOT_GINDEX` | `get_generalized_index(BeaconState, 'finalized_checkpoint', 'root')` (= 169) |
|
||||
| `CURRENT_SYNC_COMMITTEE_GINDEX` | `get_generalized_index(BeaconState, 'current_sync_committee')` (= 86) |
|
||||
| `NEXT_SYNC_COMMITTEE_GINDEX` | `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
|
||||
return capella.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
|
||||
return capella.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
|
||||
return capella.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)
|
||||
|
||||
# [New in Electra:EIP6110:EIP7002:EIP7251]
|
||||
if epoch < ELECTRA_FORK_EPOCH:
|
||||
if (
|
||||
header.execution.deposit_requests_root != Root()
|
||||
or header.execution.withdrawal_requests_root != Root()
|
||||
or header.execution.consolidation_requests_root != Root()
|
||||
):
|
||||
return False
|
||||
|
||||
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,
|
||||
)
|
||||
```
|
|
@ -16,15 +16,16 @@ from eth2spec.test.helpers.attestations import (
|
|||
state_transition_with_full_block,
|
||||
)
|
||||
from eth2spec.test.helpers.constants import (
|
||||
ALTAIR, BELLATRIX, CAPELLA, DENEB,
|
||||
ALTAIR, BELLATRIX, CAPELLA, DENEB, ELECTRA,
|
||||
MINIMAL,
|
||||
)
|
||||
from eth2spec.test.helpers.fork_transition import (
|
||||
do_fork,
|
||||
transition_across_forks,
|
||||
)
|
||||
from eth2spec.test.helpers.forks import (
|
||||
get_spec_for_fork_version,
|
||||
is_post_capella, is_post_deneb,
|
||||
is_post_capella, is_post_deneb, is_post_electra,
|
||||
)
|
||||
from eth2spec.test.helpers.light_client import (
|
||||
compute_start_slot_at_next_sync_committee_period,
|
||||
|
@ -47,6 +48,8 @@ class LightClientSyncTest(object):
|
|||
|
||||
|
||||
def get_store_fork_version(s_spec):
|
||||
if is_post_electra(s_spec):
|
||||
return s_spec.config.ELECTRA_FORK_VERSION
|
||||
if is_post_deneb(s_spec):
|
||||
return s_spec.config.DENEB_FORK_VERSION
|
||||
if is_post_capella(s_spec):
|
||||
|
@ -60,6 +63,11 @@ def setup_test(spec, state, s_spec=None, phases=None):
|
|||
|
||||
if s_spec is None:
|
||||
s_spec = spec
|
||||
if phases is None:
|
||||
phases = {
|
||||
spec.fork: spec,
|
||||
s_spec.fork: s_spec,
|
||||
}
|
||||
test.s_spec = s_spec
|
||||
|
||||
yield "genesis_validators_root", "meta", "0x" + state.genesis_validators_root.hex()
|
||||
|
@ -77,7 +85,7 @@ def setup_test(spec, state, s_spec=None, phases=None):
|
|||
yield "bootstrap_fork_digest", "meta", encode_hex(data_fork_digest)
|
||||
yield "bootstrap", data
|
||||
|
||||
upgraded = upgrade_lc_bootstrap_to_new_spec(d_spec, test.s_spec, data)
|
||||
upgraded = upgrade_lc_bootstrap_to_new_spec(d_spec, test.s_spec, data, phases)
|
||||
test.store = test.s_spec.initialize_light_client_store(trusted_block_root, upgraded)
|
||||
store_fork_version = get_store_fork_version(test.s_spec)
|
||||
store_fork_digest = test.s_spec.compute_fork_digest(store_fork_version, test.genesis_validators_root)
|
||||
|
@ -153,7 +161,7 @@ def emit_update(test, spec, state, block, attested_state, attested_block, finali
|
|||
[spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_GINDEX))]
|
||||
current_slot = state.slot
|
||||
|
||||
upgraded = upgrade_lc_update_to_new_spec(d_spec, test.s_spec, data)
|
||||
upgraded = upgrade_lc_update_to_new_spec(d_spec, test.s_spec, data, phases)
|
||||
test.s_spec.process_light_client_update(test.store, upgraded, current_slot, test.genesis_validators_root)
|
||||
|
||||
yield get_update_file_name(d_spec, data), data
|
||||
|
@ -169,7 +177,7 @@ def emit_update(test, spec, state, block, attested_state, attested_block, finali
|
|||
|
||||
|
||||
def emit_upgrade_store(test, new_s_spec, phases=None):
|
||||
test.store = upgrade_lc_store_to_new_spec(test.s_spec, new_s_spec, test.store)
|
||||
test.store = upgrade_lc_store_to_new_spec(test.s_spec, new_s_spec, test.store, phases)
|
||||
test.s_spec = new_s_spec
|
||||
store_fork_version = get_store_fork_version(test.s_spec)
|
||||
store_fork_digest = test.s_spec.compute_fork_digest(store_fork_version, test.genesis_validators_root)
|
||||
|
@ -561,7 +569,7 @@ def run_test_single_fork(spec, phases, state, fork):
|
|||
# Upgrade to post-fork spec, attested block is still before the fork
|
||||
attested_block = block.copy()
|
||||
attested_state = state.copy()
|
||||
sync_aggregate, _ = get_sync_aggregate(phases[fork], state, phases=phases)
|
||||
sync_aggregate, _ = get_sync_aggregate(spec, state, phases=phases)
|
||||
state, block = do_fork(state, spec, phases[fork], fork_epoch, sync_aggregate=sync_aggregate)
|
||||
spec = phases[fork]
|
||||
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block, phases=phases)
|
||||
|
@ -635,6 +643,18 @@ def test_deneb_fork(spec, phases, state):
|
|||
yield from run_test_single_fork(spec, phases, state, DENEB)
|
||||
|
||||
|
||||
@with_phases(phases=[DENEB], other_phases=[ELECTRA])
|
||||
@spec_test
|
||||
@with_config_overrides({
|
||||
'ELECTRA_FORK_EPOCH': 3, # `setup_test` advances to epoch 2
|
||||
}, emit=False)
|
||||
@with_state
|
||||
@with_matching_spec_config(emitted_fork=ELECTRA)
|
||||
@with_presets([MINIMAL], reason="too slow")
|
||||
def test_electra_fork(spec, phases, state):
|
||||
yield from run_test_single_fork(spec, phases, state, ELECTRA)
|
||||
|
||||
|
||||
def run_test_multi_fork(spec, phases, state, fork_1, fork_2):
|
||||
# Start test
|
||||
test = yield from setup_test(spec, state, phases[fork_2], phases)
|
||||
|
@ -646,17 +666,28 @@ def run_test_multi_fork(spec, phases, state, fork_1, fork_2):
|
|||
|
||||
# ..., attested is from `fork_1`, ...
|
||||
fork_1_epoch = getattr(phases[fork_1].config, fork_1.upper() + '_FORK_EPOCH')
|
||||
transition_to(spec, state, spec.compute_start_slot_at_epoch(fork_1_epoch) - 1)
|
||||
state, attested_block = do_fork(state, spec, phases[fork_1], fork_1_epoch)
|
||||
spec = phases[fork_1]
|
||||
spec, state, attested_block = transition_across_forks(
|
||||
spec,
|
||||
state,
|
||||
spec.compute_start_slot_at_epoch(fork_1_epoch),
|
||||
phases,
|
||||
with_block=True,
|
||||
)
|
||||
attested_state = state.copy()
|
||||
|
||||
# ..., and signature is from `fork_2`
|
||||
fork_2_epoch = getattr(phases[fork_2].config, fork_2.upper() + '_FORK_EPOCH')
|
||||
transition_to(spec, state, spec.compute_start_slot_at_epoch(fork_2_epoch) - 1)
|
||||
sync_aggregate, _ = get_sync_aggregate(phases[fork_2], state)
|
||||
state, block = do_fork(state, spec, phases[fork_2], fork_2_epoch, sync_aggregate=sync_aggregate)
|
||||
spec = phases[fork_2]
|
||||
spec, state, _ = transition_across_forks(
|
||||
spec, state, spec.compute_start_slot_at_epoch(fork_2_epoch) - 1, phases)
|
||||
sync_aggregate, _ = get_sync_aggregate(spec, state, phases=phases)
|
||||
spec, state, block = transition_across_forks(
|
||||
spec,
|
||||
state,
|
||||
spec.compute_start_slot_at_epoch(fork_2_epoch),
|
||||
phases,
|
||||
with_block=True,
|
||||
sync_aggregate=sync_aggregate,
|
||||
)
|
||||
|
||||
# Check that update applies
|
||||
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block, phases=phases)
|
||||
|
@ -682,6 +713,33 @@ def test_capella_deneb_fork(spec, phases, state):
|
|||
yield from run_test_multi_fork(spec, phases, state, CAPELLA, DENEB)
|
||||
|
||||
|
||||
@with_phases(phases=[BELLATRIX], other_phases=[CAPELLA, DENEB, ELECTRA])
|
||||
@spec_test
|
||||
@with_config_overrides({
|
||||
'CAPELLA_FORK_EPOCH': 3, # `setup_test` advances to epoch 2
|
||||
'DENEB_FORK_EPOCH': 4,
|
||||
'ELECTRA_FORK_EPOCH': 5,
|
||||
}, emit=False)
|
||||
@with_state
|
||||
@with_matching_spec_config(emitted_fork=ELECTRA)
|
||||
@with_presets([MINIMAL], reason="too slow")
|
||||
def test_capella_electra_fork(spec, phases, state):
|
||||
yield from run_test_multi_fork(spec, phases, state, CAPELLA, ELECTRA)
|
||||
|
||||
|
||||
@with_phases(phases=[CAPELLA], other_phases=[DENEB, ELECTRA])
|
||||
@spec_test
|
||||
@with_config_overrides({
|
||||
'DENEB_FORK_EPOCH': 3, # `setup_test` advances to epoch 2
|
||||
'ELECTRA_FORK_EPOCH': 4,
|
||||
}, emit=False)
|
||||
@with_state
|
||||
@with_matching_spec_config(emitted_fork=ELECTRA)
|
||||
@with_presets([MINIMAL], reason="too slow")
|
||||
def test_deneb_electra_fork(spec, phases, state):
|
||||
yield from run_test_multi_fork(spec, phases, state, DENEB, ELECTRA)
|
||||
|
||||
|
||||
def run_test_upgraded_store_with_legacy_data(spec, phases, state, fork):
|
||||
# Start test (Legacy bootstrap with an upgraded store)
|
||||
test = yield from setup_test(spec, state, phases[fork], phases)
|
||||
|
@ -713,10 +771,19 @@ def test_capella_store_with_legacy_data(spec, phases, state):
|
|||
yield from run_test_upgraded_store_with_legacy_data(spec, phases, state, CAPELLA)
|
||||
|
||||
|
||||
@with_phases(phases=[ALTAIR, BELLATRIX, CAPELLA], other_phases=[DENEB])
|
||||
@with_phases(phases=[ALTAIR, BELLATRIX, CAPELLA], other_phases=[CAPELLA, DENEB])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_matching_spec_config(emitted_fork=DENEB)
|
||||
@with_presets([MINIMAL], reason="too slow")
|
||||
def test_deneb_store_with_legacy_data(spec, phases, state):
|
||||
yield from run_test_upgraded_store_with_legacy_data(spec, phases, state, DENEB)
|
||||
|
||||
|
||||
@with_phases(phases=[ALTAIR, BELLATRIX, CAPELLA, DENEB], other_phases=[CAPELLA, DENEB, ELECTRA])
|
||||
@spec_test
|
||||
@with_state
|
||||
@with_matching_spec_config(emitted_fork=ELECTRA)
|
||||
@with_presets([MINIMAL], reason="too slow")
|
||||
def test_electra_store_with_legacy_data(spec, phases, state):
|
||||
yield from run_test_upgraded_store_with_legacy_data(spec, phases, state, ELECTRA)
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
from eth2spec.test.helpers.constants import (
|
||||
CAPELLA, DENEB, ELECTRA,
|
||||
)
|
||||
from eth2spec.test.helpers.fork_transition import (
|
||||
transition_across_forks,
|
||||
)
|
||||
from eth2spec.test.helpers.forks import (
|
||||
is_post_capella, is_post_deneb,
|
||||
is_post_capella, is_post_deneb, is_post_electra
|
||||
)
|
||||
from eth2spec.test.helpers.sync_committee import (
|
||||
compute_aggregate_sync_committee_signature,
|
||||
|
@ -88,6 +91,20 @@ def needs_upgrade_to_deneb(spec, new_spec):
|
|||
return is_post_deneb(new_spec) and not is_post_deneb(spec)
|
||||
|
||||
|
||||
def needs_upgrade_to_electra(spec, new_spec):
|
||||
return is_post_electra(new_spec) and not is_post_electra(spec)
|
||||
|
||||
|
||||
def check_merkle_branch_equal(spec, new_spec, data, upgraded, gindex):
|
||||
if is_post_electra(new_spec):
|
||||
assert (
|
||||
new_spec.normalize_merkle_branch(upgraded, gindex)
|
||||
== new_spec.normalize_merkle_branch(data, gindex)
|
||||
)
|
||||
else:
|
||||
assert upgraded == data
|
||||
|
||||
|
||||
def check_lc_header_equal(spec, new_spec, data, upgraded):
|
||||
assert upgraded.beacon.slot == data.beacon.slot
|
||||
assert upgraded.beacon.hash_tree_root() == data.beacon.hash_tree_root()
|
||||
|
@ -98,15 +115,19 @@ def check_lc_header_equal(spec, new_spec, data, upgraded):
|
|||
assert new_spec.get_lc_execution_root(upgraded) == new_spec.Root()
|
||||
|
||||
|
||||
def upgrade_lc_header_to_new_spec(spec, new_spec, data):
|
||||
def upgrade_lc_header_to_new_spec(spec, new_spec, data, phases):
|
||||
upgraded = data
|
||||
|
||||
if needs_upgrade_to_capella(spec, new_spec):
|
||||
upgraded = new_spec.upgrade_lc_header_to_capella(upgraded)
|
||||
upgraded = phases[CAPELLA].upgrade_lc_header_to_capella(upgraded)
|
||||
check_lc_header_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
if needs_upgrade_to_deneb(spec, new_spec):
|
||||
upgraded = new_spec.upgrade_lc_header_to_deneb(upgraded)
|
||||
upgraded = phases[DENEB].upgrade_lc_header_to_deneb(upgraded)
|
||||
check_lc_header_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
if needs_upgrade_to_electra(spec, new_spec):
|
||||
upgraded = phases[ELECTRA].upgrade_lc_header_to_electra(upgraded)
|
||||
check_lc_header_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
return upgraded
|
||||
|
@ -115,18 +136,28 @@ def upgrade_lc_header_to_new_spec(spec, new_spec, data):
|
|||
def check_lc_bootstrap_equal(spec, new_spec, data, upgraded):
|
||||
check_lc_header_equal(spec, new_spec, data.header, upgraded.header)
|
||||
assert upgraded.current_sync_committee == data.current_sync_committee
|
||||
assert upgraded.current_sync_committee_branch == data.current_sync_committee_branch
|
||||
check_merkle_branch_equal(
|
||||
spec,
|
||||
new_spec,
|
||||
data.current_sync_committee_branch,
|
||||
upgraded.current_sync_committee_branch,
|
||||
new_spec.CURRENT_SYNC_COMMITTEE_GINDEX,
|
||||
)
|
||||
|
||||
|
||||
def upgrade_lc_bootstrap_to_new_spec(spec, new_spec, data):
|
||||
def upgrade_lc_bootstrap_to_new_spec(spec, new_spec, data, phases):
|
||||
upgraded = data
|
||||
|
||||
if needs_upgrade_to_capella(spec, new_spec):
|
||||
upgraded = new_spec.upgrade_lc_bootstrap_to_capella(upgraded)
|
||||
upgraded = phases[CAPELLA].upgrade_lc_bootstrap_to_capella(upgraded)
|
||||
check_lc_bootstrap_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
if needs_upgrade_to_deneb(spec, new_spec):
|
||||
upgraded = new_spec.upgrade_lc_bootstrap_to_deneb(upgraded)
|
||||
upgraded = phases[DENEB].upgrade_lc_bootstrap_to_deneb(upgraded)
|
||||
check_lc_bootstrap_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
if needs_upgrade_to_electra(spec, new_spec):
|
||||
upgraded = phases[ELECTRA].upgrade_lc_bootstrap_to_electra(upgraded)
|
||||
check_lc_bootstrap_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
return upgraded
|
||||
|
@ -135,21 +166,38 @@ def upgrade_lc_bootstrap_to_new_spec(spec, new_spec, data):
|
|||
def check_lc_update_equal(spec, new_spec, data, upgraded):
|
||||
check_lc_header_equal(spec, new_spec, data.attested_header, upgraded.attested_header)
|
||||
assert upgraded.next_sync_committee == data.next_sync_committee
|
||||
assert upgraded.next_sync_committee_branch == data.next_sync_committee_branch
|
||||
check_merkle_branch_equal(
|
||||
spec,
|
||||
new_spec,
|
||||
data.next_sync_committee_branch,
|
||||
upgraded.next_sync_committee_branch,
|
||||
new_spec.NEXT_SYNC_COMMITTEE_GINDEX,
|
||||
)
|
||||
check_lc_header_equal(spec, new_spec, data.finalized_header, upgraded.finalized_header)
|
||||
check_merkle_branch_equal(
|
||||
spec,
|
||||
new_spec,
|
||||
data.finality_branch,
|
||||
upgraded.finality_branch,
|
||||
new_spec.FINALIZED_ROOT_GINDEX,
|
||||
)
|
||||
assert upgraded.sync_aggregate == data.sync_aggregate
|
||||
assert upgraded.signature_slot == data.signature_slot
|
||||
|
||||
|
||||
def upgrade_lc_update_to_new_spec(spec, new_spec, data):
|
||||
def upgrade_lc_update_to_new_spec(spec, new_spec, data, phases):
|
||||
upgraded = data
|
||||
|
||||
if needs_upgrade_to_capella(spec, new_spec):
|
||||
upgraded = new_spec.upgrade_lc_update_to_capella(upgraded)
|
||||
upgraded = phases[CAPELLA].upgrade_lc_update_to_capella(upgraded)
|
||||
check_lc_update_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
if needs_upgrade_to_deneb(spec, new_spec):
|
||||
upgraded = new_spec.upgrade_lc_update_to_deneb(upgraded)
|
||||
upgraded = phases[DENEB].upgrade_lc_update_to_deneb(upgraded)
|
||||
check_lc_update_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
if needs_upgrade_to_electra(spec, new_spec):
|
||||
upgraded = phases[ELECTRA].upgrade_lc_update_to_electra(upgraded)
|
||||
check_lc_update_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
return upgraded
|
||||
|
@ -158,19 +206,30 @@ def upgrade_lc_update_to_new_spec(spec, new_spec, data):
|
|||
def check_lc_finality_update_equal(spec, new_spec, data, upgraded):
|
||||
check_lc_header_equal(spec, new_spec, data.attested_header, upgraded.attested_header)
|
||||
check_lc_header_equal(spec, new_spec, data.finalized_header, upgraded.finalized_header)
|
||||
check_merkle_branch_equal(
|
||||
spec,
|
||||
new_spec,
|
||||
data.finality_branch,
|
||||
upgraded.finality_branch,
|
||||
new_spec.FINALIZED_ROOT_GINDEX,
|
||||
)
|
||||
assert upgraded.sync_aggregate == data.sync_aggregate
|
||||
assert upgraded.signature_slot == data.signature_slot
|
||||
|
||||
|
||||
def upgrade_lc_finality_update_to_new_spec(spec, new_spec, data):
|
||||
def upgrade_lc_finality_update_to_new_spec(spec, new_spec, data, phases):
|
||||
upgraded = data
|
||||
|
||||
if needs_upgrade_to_capella(spec, new_spec):
|
||||
upgraded = new_spec.upgrade_lc_finality_update_to_capella(upgraded)
|
||||
upgraded = phases[CAPELLA].upgrade_lc_finality_update_to_capella(upgraded)
|
||||
check_lc_finality_update_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
if needs_upgrade_to_deneb(spec, new_spec):
|
||||
upgraded = new_spec.upgrade_lc_finality_update_to_deneb(upgraded)
|
||||
upgraded = phases[DENEB].upgrade_lc_finality_update_to_deneb(upgraded)
|
||||
check_lc_finality_update_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
if needs_upgrade_to_electra(spec, new_spec):
|
||||
upgraded = phases[ELECTRA].upgrade_lc_finality_update_to_electra(upgraded)
|
||||
check_lc_finality_update_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
return upgraded
|
||||
|
@ -189,15 +248,19 @@ def check_lc_store_equal(spec, new_spec, data, upgraded):
|
|||
assert upgraded.current_max_active_participants == data.current_max_active_participants
|
||||
|
||||
|
||||
def upgrade_lc_store_to_new_spec(spec, new_spec, data):
|
||||
def upgrade_lc_store_to_new_spec(spec, new_spec, data, phases):
|
||||
upgraded = data
|
||||
|
||||
if needs_upgrade_to_capella(spec, new_spec):
|
||||
upgraded = new_spec.upgrade_lc_store_to_capella(upgraded)
|
||||
upgraded = phases[CAPELLA].upgrade_lc_store_to_capella(upgraded)
|
||||
check_lc_store_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
if needs_upgrade_to_deneb(spec, new_spec):
|
||||
upgraded = new_spec.upgrade_lc_store_to_deneb(upgraded)
|
||||
upgraded = phases[DENEB].upgrade_lc_store_to_deneb(upgraded)
|
||||
check_lc_store_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
if needs_upgrade_to_electra(spec, new_spec):
|
||||
upgraded = phases[ELECTRA].upgrade_lc_store_to_electra(upgraded)
|
||||
check_lc_store_equal(spec, new_spec, data, upgraded)
|
||||
|
||||
return upgraded
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, DENEB
|
||||
from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, DENEB, ELECTRA
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import combine_mods, run_state_test_generators
|
||||
|
||||
|
||||
|
@ -15,12 +15,14 @@ if __name__ == "__main__":
|
|||
]}
|
||||
capella_mods = combine_mods(_new_capella_mods, bellatrix_mods)
|
||||
deneb_mods = capella_mods
|
||||
electra_mods = deneb_mods
|
||||
|
||||
all_mods = {
|
||||
ALTAIR: altair_mods,
|
||||
BELLATRIX: bellatrix_mods,
|
||||
CAPELLA: capella_mods,
|
||||
DENEB: deneb_mods,
|
||||
ELECTRA: electra_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="light_client", all_mods=all_mods)
|
||||
|
|
Loading…
Reference in New Issue