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

Add light client functionality to ePBS #18

Open
wants to merge 2 commits into
base: epbs_fix_tests
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion presets/minimal/eip-7732.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Execution
# ---------------------------------------------------------------
# 2**1(= 2)
# 2**1 (= 2)
PTC_SIZE: 2
# 2**2 (= 4)
MAX_PAYLOAD_ATTESTATIONS: 4
Expand Down
11 changes: 5 additions & 6 deletions pysetup/spec_builders/eip7732.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ def concat_generalized_indices(*indices: GeneralizedIndex) -> GeneralizedIndex:
o = GeneralizedIndex(o * bit_floor(i) + (i - bit_floor(i)))
return o'''

@classmethod
def hardcoded_ssz_dep_constants(cls) -> Dict[str, str]:
return {
'EXECUTION_PAYLOAD_GINDEX_EIP7732': 'GeneralizedIndex(101)',
}

@classmethod
def hardcoded_custom_type_dep_constants(cls, spec_object) -> Dict[str, str]:
Expand All @@ -32,12 +37,6 @@ def hardcoded_custom_type_dep_constants(cls, spec_object) -> Dict[str, str]:
spec_object.preset_vars['KZG_COMMITMENT_INCLUSION_PROOF_DEPTH_EIP7732'].value,
}

@classmethod
def deprecate_constants(cls) -> Set[str]:
return set([
'EXECUTION_PAYLOAD_GINDEX',
])

@classmethod
def deprecate_presets(cls) -> Set[str]:
return set([
Expand Down
57 changes: 37 additions & 20 deletions specs/_features/eip7732/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@
- [`remove_flag`](#remove_flag)
- [Predicates](#predicates)
- [`is_valid_indexed_payload_attestation`](#is_valid_indexed_payload_attestation)
- [`is_parent_block_full`](#is_parent_block_full)
- [Beacon State accessors](#beacon-state-accessors)
- [`get_ptc`](#get_ptc)
- [Modified `get_attesting_indices`](#modified-get_attesting_indices)
- [`get_payload_attesting_indices`](#get_payload_attesting_indices)
- [`get_indexed_payload_attestation`](#get_indexed_payload_attestation)
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [Block processing](#block-processing)
- [RANDAO](#randao)
- [Modified `process_randao`](#modified-process_randao)
- [Withdrawals](#withdrawals)
- [Modified `process_withdrawals`](#modified-process_withdrawals)
- [Execution payload header](#execution-payload-header)
Expand Down Expand Up @@ -187,15 +188,15 @@ class BeaconBlockBody(Container):
graffiti: Bytes32 # Arbitrary data
# Operations
proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS]
attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS]
attestations: List[Attestation, MAX_ATTESTATIONS]
attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS_ELECTRA]
attestations: List[Attestation, MAX_ATTESTATIONS_ELECTRA]
deposits: List[Deposit, MAX_DEPOSITS]
voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
sync_aggregate: SyncAggregate
# Execution
# Removed execution_payload [Removed in EIP-7732]
# Removed blob_kzg_commitments [Removed in EIP-7732]
bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES]
# Removed blob_kzg_commitments [Removed in EIP-7732]
# PBS
signed_execution_payload_header: SignedExecutionPayloadHeader # [New in EIP-7732]
payload_attestations: List[PayloadAttestation, MAX_PAYLOAD_ATTESTATIONS] # [New in EIP-7732]
Expand Down Expand Up @@ -274,8 +275,10 @@ class BeaconState(Container):
pending_partial_withdrawals: List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT]
pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT]
# PBS
latest_execution_payload_root: Root # [New in EIP-7732]
latest_block_hash: Hash32 # [New in EIP-7732]
latest_full_slot: Slot # [New in EIP-7732]
latest_prev_randao: Bytes32 # [New in EIP-7732]
latest_withdrawals_root: Root # [New in EIP-7732]
```

Expand Down Expand Up @@ -333,15 +336,6 @@ def is_valid_indexed_payload_attestation(
return bls.FastAggregateVerify(pubkeys, signing_root, indexed_payload_attestation.signature)
```

#### `is_parent_block_full`

This function returns true if the last committed payload header was fulfilled with a payload, this can only happen when both beacon block and payload were present. This function must be called on a beacon state before processing the execution payload header in the block.

```python
def is_parent_block_full(state: BeaconState) -> bool:
return state.latest_execution_payload_header.block_hash == state.latest_block_hash
```

### Beacon State accessors

#### `get_ptc`
Expand Down Expand Up @@ -428,15 +422,35 @@ The post-state corresponding to a pre-state `state` and a signed execution paylo

```python
def process_block(state: BeaconState, block: BeaconBlock) -> None:
parent_slot = state.latest_block_header.slot # [New in EIP-7732]
process_block_header(state, block)
process_withdrawals(state) # [Modified in EIP-7732]
if state.latest_full_slot == parent_slot: # [Modified in EIP-7732, conditional and removed payload]
process_withdrawals(state) # [Modified in EIP-7732]
process_execution_payload_header(state, block) # [Modified in EIP-7732, removed process_execution_payload]
process_randao(state, block.body)
process_eth1_data(state, block.body)
process_operations(state, block.body) # [Modified in EIP-7732]
process_sync_aggregate(state, block.body.sync_aggregate)
```

#### RANDAO

##### Modified `process_randao`

```python
def process_randao(state: BeaconState, body: BeaconBlockBody) -> None:
epoch = get_current_epoch(state)
# Verify RANDAO reveal
proposer = state.validators[get_beacon_proposer_index(state)]
signing_root = compute_signing_root(epoch, get_domain(state, DOMAIN_RANDAO))
assert bls.Verify(proposer.pubkey, signing_root, body.randao_reveal)
# Cache latest RANDAO mix [New in EIP-7732]
state.latest_prev_randao = get_randao_mix(state, epoch)
# Mix in RANDAO reveal
mix = xor(get_randao_mix(state, epoch), hash(body.randao_reveal))
state.randao_mixes[epoch % EPOCHS_PER_HISTORICAL_VECTOR] = mix
```

#### Withdrawals

##### Modified `process_withdrawals`
Expand All @@ -445,10 +459,6 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:

```python
def process_withdrawals(state: BeaconState) -> None:
# return early if the parent block was empty
if not is_parent_block_full(state):
return

withdrawals, partial_withdrawals_count = get_expected_withdrawals(state)
withdrawals_list = List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD](withdrawals)
state.latest_withdrawals_root = hash_tree_root(withdrawals_list)
Expand Down Expand Up @@ -642,7 +652,7 @@ def process_execution_payload(state: BeaconState,
# Verify consistency of the parent hash with respect to the previous execution payload
assert payload.parent_hash == state.latest_block_hash
# Verify prev_randao
assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state))
assert payload.prev_randao == state.latest_prev_randao
# Verify timestamp
assert payload.timestamp == compute_timestamp_at_slot(state, state.slot)
# Verify commitments are under limit
Expand All @@ -665,9 +675,10 @@ def process_execution_payload(state: BeaconState,

for_ops(payload.deposit_requests, process_deposit_request)
for_ops(payload.withdrawal_requests, process_withdrawal_request)
for_ops(payload, process_consolidation_request)
for_ops(payload.consolidation_requests, process_consolidation_request)

# Cache the execution payload header and proposer
state.latest_execution_payload_root = hash_tree_root(payload)
state.latest_block_hash = payload.block_hash
state.latest_full_slot = state.slot

Expand Down Expand Up @@ -725,6 +736,7 @@ def validate_merge_block(block: BeaconBlock) -> None:
*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure EIP-7732 testing only.
Modifications include:
1. Use `EIP7732_FORK_VERSION` as the previous and current fork version.
2. Initialize `latest_execution_payload_root`, `latest_prev_randao` and `latest_withdrawals_root`.

```python
def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32,
Expand Down Expand Up @@ -779,6 +791,11 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32,
# Initialize the execution payload header
state.latest_execution_payload_header = execution_payload_header

# Initialize ePBS [New in EIP-7332]
state.latest_execution_payload_root = hash_tree_root(electra.ExecutionPayloadHeader())
state.latest_prev_randao = get_randao_mix(state, GENESIS_EPOCH)
state.latest_withdrawals_root = hash_tree_root(List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD]())

return state
```

Expand Down
6 changes: 4 additions & 2 deletions specs/_features/eip7732/fork.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,11 @@ def upgrade_to_eip7732(pre: electra.BeaconState) -> BeaconState:
pending_partial_withdrawals=pre.pending_partial_withdrawals,
pending_consolidations=pre.pending_consolidations,
# ePBS
latest_execution_payload_root=hash_tree_root(pre.latest_execution_payload_header), # [New in EIP-7732]
latest_block_hash=pre.latest_execution_payload_header.block_hash, # [New in EIP-7732]
latest_full_slot=pre.slot, # [New in EIP-7732]
latest_withdrawals_root=Root(), # [New in EIP-7732]
latest_full_slot=pre.latest_block_header.slot, # [New in EIP-7732]
latest_prev_randao=electra.get_randao_mix(pre, epoch), # [New in EIP-7732]
latest_withdrawals_root=pre.latest_execution_payload_header.withdrawals_root, # [New in EIP-7732]
)

return post
Expand Down
119 changes: 119 additions & 0 deletions specs/_features/eip7732/light-client/fork.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# EIP-7732 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)
- [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 [Electra specification](../../../electra/light-client/sync-protocol.md) to EIP-7732. This is necessary when processing pre-EIP-7732 data with a post-EIP-7732 `LightClientStore`. Note that the data being exchanged over the network protocols uses the original format.

## Helper functions

## Upgrading light client data

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

```python
def upgrade_lc_header_to_eip7732(pre: electra.LightClientHeader) -> LightClientHeader:
return LightClientHeader(
beacon=pre.beacon,
execution=LightClientExecutionHeader(
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=pre.execution.deposit_requests_root,
withdrawal_requests_root=pre.execution.withdrawal_requests_root,
consolidation_requests_root=pre.execution.consolidation_requests_root,
),
execution_branch=normalize_merkle_branch(
pre.execution_branch, EXECUTION_PAYLOAD_GINDEX_EIP7732),
)
```

```python
def upgrade_lc_bootstrap_to_eip7732(pre: electra.LightClientBootstrap) -> LightClientBootstrap:
return LightClientBootstrap(
header=upgrade_lc_header_to_eip7732(pre.header),
current_sync_committee=pre.current_sync_committee,
current_sync_committee_branch=pre.current_sync_committee_branch,
)
```

```python
def upgrade_lc_update_to_eip7732(pre: electra.LightClientUpdate) -> LightClientUpdate:
return LightClientUpdate(
attested_header=upgrade_lc_header_to_eip7732(pre.attested_header),
next_sync_committee=pre.next_sync_committee,
next_sync_committee_branch=pre.next_sync_committee_branch,
finalized_header=upgrade_lc_header_to_eip7732(pre.finalized_header),
finality_branch=pre.finality_branch,
sync_aggregate=pre.sync_aggregate,
signature_slot=pre.signature_slot,
)
```

```python
def upgrade_lc_finality_update_to_eip7732(pre: electra.LightClientFinalityUpdate) -> LightClientFinalityUpdate:
return LightClientFinalityUpdate(
attested_header=upgrade_lc_header_to_eip7732(pre.attested_header),
finalized_header=upgrade_lc_header_to_eip7732(pre.finalized_header),
finality_branch=pre.finality_branch,
sync_aggregate=pre.sync_aggregate,
signature_slot=pre.signature_slot,
)
```

```python
def upgrade_lc_optimistic_update_to_eip7732(pre: electra.LightClientOptimisticUpdate) -> LightClientOptimisticUpdate:
return LightClientOptimisticUpdate(
attested_header=upgrade_lc_header_to_eip7732(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 EIP-7732 before EIP-7732 based light client data can be processed. The `LightClientStore` upgrade MAY be performed before `EIP7732_FORK_EPOCH`.

```python
def upgrade_lc_store_to_eip7732(pre: electra.LightClientStore) -> LightClientStore:
if pre.best_valid_update is None:
best_valid_update = None
else:
best_valid_update = upgrade_lc_update_to_eip7732(pre.best_valid_update)
return LightClientStore(
finalized_header=upgrade_lc_header_to_eip7732(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_eip7732(pre.optimistic_header),
previous_max_active_participants=pre.previous_max_active_participants,
current_max_active_participants=pre.current_max_active_participants,
)
```
Loading