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

Adopt EIP-7688: Forward compatible consensus data structures #3844

Draft
wants to merge 21 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
5 changes: 5 additions & 0 deletions presets/mainnet/electra.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,8 @@ MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 8
# ---------------------------------------------------------------
# 2**4 ( = 4) pending deposits
MAX_PENDING_DEPOSITS_PER_EPOCH: 16

# Misc
# ---------------------------------------------------------------
# `floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments')) + 1 + ceillog2(MAX_BLOB_COMMITMENTS_PER_BLOCK)` = 7 + 1 + 12 = 20
KZG_COMMITMENT_INCLUSION_PROOF_DEPTH_ELECTRA: 20
2 changes: 1 addition & 1 deletion presets/mainnet/fulu.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ FIELD_ELEMENTS_PER_CELL: 64
# `uint64(2 * 4096)` (= 8192)
FIELD_ELEMENTS_PER_EXT_BLOB: 8192
# uint64(floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments'))
KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 4
KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 7
5 changes: 5 additions & 0 deletions presets/minimal/electra.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,8 @@ MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 2
# ---------------------------------------------------------------
# 2**4 ( = 4) pending deposits
MAX_PENDING_DEPOSITS_PER_EPOCH: 16

# Misc
# ---------------------------------------------------------------
# [customized] `floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments')) + 1 + ceillog2(MAX_BLOB_COMMITMENTS_PER_BLOCK)` = 7 + 1 + 5 = 13
KZG_COMMITMENT_INCLUSION_PROOF_DEPTH_ELECTRA: 13
2 changes: 1 addition & 1 deletion presets/minimal/fulu.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ FIELD_ELEMENTS_PER_CELL: 64
# `uint64(2 * 4096)` (= 8192)
FIELD_ELEMENTS_PER_EXT_BLOB: 8192
# uint64(floorlog2(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments'))
KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 4
KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH: 7
4 changes: 4 additions & 0 deletions pysetup/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ def dependency_order_class_objects(objects: Dict[str, str], custom_types: Dict[s
for key, value in items:
dependencies = []
for line in value.split('\n'):
profile_match = re.match(r'class\s+\w+\s*\(Profile\[\s*(\w+)\s*\]\s*\)\s*:', line)
if profile_match is not None:
dependencies.append(profile_match.group(1)) # SSZ `Profile` base
continue
if not re.match(r'\s+\w+: .+', line):
continue # skip whitespace etc.
line = line[line.index(':') + 1:] # strip of field name
Expand Down
5 changes: 0 additions & 5 deletions pysetup/spec_builders/deneb.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,5 @@ def hardcoded_custom_type_dep_constants(cls, spec_object) -> Dict[str, str]:
'FIELD_ELEMENTS_PER_BLOB': spec_object.preset_vars['FIELD_ELEMENTS_PER_BLOB'].value,
'MAX_BLOBS_PER_BLOCK': spec_object.config_vars['MAX_BLOBS_PER_BLOCK'].value,
'MAX_BLOB_COMMITMENTS_PER_BLOCK': spec_object.preset_vars['MAX_BLOB_COMMITMENTS_PER_BLOCK'].value,
}

@classmethod
def hardcoded_func_dep_presets(cls, spec_object) -> Dict[str, str]:
return {
'KZG_COMMITMENT_INCLUSION_PROOF_DEPTH': spec_object.preset_vars['KZG_COMMITMENT_INCLUSION_PROOF_DEPTH'].value,
}
4 changes: 2 additions & 2 deletions pysetup/spec_builders/eip7732.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ def hardcoded_custom_type_dep_constants(cls, spec_object) -> Dict[str, str]:
@classmethod
def deprecate_constants(cls) -> Set[str]:
return set([
'EXECUTION_PAYLOAD_GINDEX',
'EXECUTION_PAYLOAD_GINDEX_ELECTRA',
])

@classmethod
def deprecate_presets(cls) -> Set[str]:
return set([
'KZG_COMMITMENT_INCLUSION_PROOF_DEPTH',
'KZG_COMMITMENT_INCLUSION_PROOF_DEPTH_ELECTRA',
])
16 changes: 12 additions & 4 deletions pysetup/spec_builders/electra.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,22 @@ def imports(cls, preset_name: str):
return f'''
from eth2spec.deneb import {preset_name} as deneb
from eth2spec.utils.ssz.ssz_impl import ssz_serialize, ssz_deserialize
from eth2spec.utils.ssz.ssz_typing import StableContainer, Profile
'''

@classmethod
def hardcoded_ssz_dep_constants(cls) -> Dict[str, str]:
return {
'FINALIZED_ROOT_GINDEX_ELECTRA': 'GeneralizedIndex(169)',
'CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA': 'GeneralizedIndex(86)',
'NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA': 'GeneralizedIndex(87)',
'FINALIZED_ROOT_GINDEX_ELECTRA': 'GeneralizedIndex(553)',
'CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA': 'GeneralizedIndex(278)',
'NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA': 'GeneralizedIndex(279)',
'EXECUTION_PAYLOAD_GINDEX_ELECTRA': 'GeneralizedIndex(137)',
}

@classmethod
def hardcoded_custom_type_dep_constants(cls, spec_object) -> Dict[str, str]:
return {
'KZG_COMMITMENT_INCLUSION_PROOF_DEPTH_ELECTRA': spec_object.preset_vars['KZG_COMMITMENT_INCLUSION_PROOF_DEPTH_ELECTRA'].value,
}


Expand Down Expand Up @@ -58,4 +66,4 @@ def verify_and_notify_new_payload(self: ExecutionEngine,
return True


EXECUTION_ENGINE = NoopExecutionEngine()"""
EXECUTION_ENGINE = NoopExecutionEngine()"""
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str], pr
raise

if parent_class:
assert parent_class == "Container"
assert parent_class in ["Container", "StableContainer", "Profile"]
# NOTE: trim whitespace from spec
ssz_objects[current_name] = "\n".join(line.rstrip() for line in source.splitlines())
else:
Expand Down Expand Up @@ -571,7 +571,7 @@ def run(self):
"pycryptodome>=3.19.1",
"py_ecc==6.0.0",
"milagro_bls_binding==1.9.0",
"remerkleable==0.1.28",
"remerkleable @ git+https://github.com/etan-status/remerkleable@dev/etan/sc-default",
"trie>=3,<4",
RUAMEL_YAML_VERSION,
"lru-dict==1.2.0",
Expand Down
108 changes: 104 additions & 4 deletions specs/_features/eip7732/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@
- [`ExecutionPayloadEnvelope`](#executionpayloadenvelope)
- [`SignedExecutionPayloadEnvelope`](#signedexecutionpayloadenvelope)
- [Modified containers](#modified-containers)
- [`StableBeaconBlockBody`](#stablebeaconblockbody)
- [`BeaconBlockBody`](#beaconblockbody)
- [`StableExecutionPayloadHeader`](#stableexecutionpayloadheader)
- [`ExecutionPayloadHeader`](#executionpayloadheader)
- [`StableBeaconState`](#stablebeaconstate)
- [`BeaconState`](#beaconstate)
- [Helper functions](#helper-functions)
- [Math](#math)
Expand Down Expand Up @@ -175,12 +178,34 @@ class SignedExecutionPayloadEnvelope(Container):

### Modified containers

#### `BeaconBlockBody`
#### `StableBeaconBlockBody`

**Note:** The Beacon Block body is modified to contain a `Signed ExecutionPayloadHeader`. The containers `BeaconBlock` and `SignedBeaconBlock` are modified indirectly. The field `execution_requests` is removed from the beacon block body and moved into the signed execution payload envelope.

```python
class BeaconBlockBody(Container):
class StableBeaconBlockBody(StableContainer[MAX_BEACON_BLOCK_BODY_FIELDS]):
randao_reveal: Optional[BLSSignature]
eth1_data: Optional[Eth1Data] # Eth1 data vote
graffiti: Optional[Bytes32] # Arbitrary data
proposer_slashings: Optional[List[ProposerSlashing, MAX_PROPOSER_SLASHINGS]]
attester_slashings: Optional[List[StableAttesterSlashing, MAX_ATTESTER_SLASHINGS_ELECTRA]]
attestations: Optional[List[Attestation, MAX_ATTESTATIONS_ELECTRA]]
deposits: Optional[List[Deposit, MAX_DEPOSITS]]
voluntary_exits: Optional[List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]]
sync_aggregate: Optional[SyncAggregate]
execution_payload: Optional[StableExecutionPayload] # [Removed in EIP-7732]
bls_to_execution_changes: Optional[List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES]]
blob_kzg_commitments: Optional[List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]] # [Removed in EIP-7732]
execution_requests: Optional[StableExecutionRequests] # [Removed in EIP-7732]
# PBS
signed_execution_payload_header: Optional[SignedExecutionPayloadHeader] # [New in EIP-7732]
payload_attestations: Optional[List[PayloadAttestation, MAX_PAYLOAD_ATTESTATIONS]] # [New in EIP-7732]
```

#### `BeaconBlockBody`

```python
class BeaconBlockBody(Profile[StableBeaconBlockBody]):
randao_reveal: BLSSignature
eth1_data: Eth1Data # Eth1 data vote
graffiti: Bytes32 # Arbitrary data
Expand All @@ -201,12 +226,26 @@ class BeaconBlockBody(Container):
payload_attestations: List[PayloadAttestation, MAX_PAYLOAD_ATTESTATIONS] # [New in EIP-7732]
```

#### `StableExecutionPayloadHeader`

```python
class StableExecutionPayloadHeader(StableContainer[MAX_EXECUTION_PAYLOAD_FIELDS]):
parent_block_hash: Optional[Hash32]
parent_block_root: Optional[Root]
block_hash: Optional[Hash32]
gas_limit: Optional[uint64]
builder_index: Optional[ValidatorIndex]
slot: Optional[Slot]
value: Optional[Gwei]
blob_kzg_commitments_root: Optional[Root]
```

#### `ExecutionPayloadHeader`

**Note:** The `ExecutionPayloadHeader` is modified to only contain the block hash of the committed `ExecutionPayload` in addition to the builder's payment information, gas limit and KZG commitments root to verify the inclusion proofs.

```python
class ExecutionPayloadHeader(Container):
class ExecutionPayloadHeader(Profile[StableExecutionPayloadHeader]):
parent_block_hash: Hash32
parent_block_root: Root
block_hash: Hash32
Expand All @@ -217,12 +256,73 @@ class ExecutionPayloadHeader(Container):
blob_kzg_commitments_root: Root
```

#### `StableBeaconState`

```python
class StableBeaconState(StableContainer[MAX_BEACON_STATE_FIELDS]):
# Versioning
genesis_time: Optional[uint64]
genesis_validators_root: Optional[Root]
slot: Optional[Slot]
fork: Optional[Fork]
# History
latest_block_header: Optional[BeaconBlockHeader]
block_roots: Optional[Vector[Root, SLOTS_PER_HISTORICAL_ROOT]]
state_roots: Optional[Vector[Root, SLOTS_PER_HISTORICAL_ROOT]]
# Frozen in Capella, replaced by historical_summaries
historical_roots: Optional[List[Root, HISTORICAL_ROOTS_LIMIT]]
# Eth1
eth1_data: Optional[Eth1Data]
eth1_data_votes: Optional[List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH]]
eth1_deposit_index: Optional[uint64]
# Registry
validators: Optional[List[Validator, VALIDATOR_REGISTRY_LIMIT]]
balances: Optional[List[Gwei, VALIDATOR_REGISTRY_LIMIT]]
# Randomness
randao_mixes: Optional[Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR]]
# Slashings
slashings: Optional[Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR]] # Per-epoch sums of slashed effective balances
# Participation
previous_epoch_participation: Optional[List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT]]
current_epoch_participation: Optional[List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT]]
# Finality
justification_bits: Optional[Bitvector[JUSTIFICATION_BITS_LENGTH]] # Bit set for every recent justified epoch
previous_justified_checkpoint: Optional[Checkpoint]
current_justified_checkpoint: Optional[Checkpoint]
finalized_checkpoint: Optional[Checkpoint]
# Inactivity
inactivity_scores: Optional[List[uint64, VALIDATOR_REGISTRY_LIMIT]]
# Sync
current_sync_committee: Optional[SyncCommittee]
next_sync_committee: Optional[SyncCommittee]
# Execution
latest_execution_payload_header: Optional[StableExecutionPayloadHeader]
# Withdrawals
next_withdrawal_index: Optional[WithdrawalIndex]
next_withdrawal_validator_index: Optional[ValidatorIndex]
# Deep history valid from Capella onwards
historical_summaries: Optional[List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT]]
deposit_requests_start_index: Optional[uint64]
deposit_balance_to_consume: Optional[Gwei]
exit_balance_to_consume: Optional[Gwei]
earliest_exit_epoch: Optional[Epoch]
consolidation_balance_to_consume: Optional[Gwei]
earliest_consolidation_epoch: Optional[Epoch]
pending_deposits: Optional[List[PendingDeposit, PENDING_DEPOSITS_LIMIT]]
pending_partial_withdrawals: Optional[List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT]]
pending_consolidations: Optional[List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT]]
# PBS
latest_block_hash: Optional[Hash32] # [New in EIP-7732]
latest_full_slot: Optional[Slot] # [New in EIP-7732]
latest_withdrawals_root: Optional[Root] # [New in EIP-7732]
```

#### `BeaconState`

*Note*: The `BeaconState` is modified to track the last withdrawals honored in the CL. The `latest_execution_payload_header` is modified semantically to refer not to a past committed `ExecutionPayload` but instead it corresponds to the state's slot builder's bid. Another addition is to track the last committed block hash and the last slot that was full, that is in which there were both consensus and execution blocks included.

```python
class BeaconState(Container):
class BeaconState(Profile[StableBeaconState]):
# Versioning
genesis_time: uint64
genesis_validators_root: Root
Expand Down
2 changes: 1 addition & 1 deletion specs/capella/light-client/full-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def block_to_light_client_header(block: SignedBeaconBlock) -> LightClientHeader:
withdrawals_root=hash_tree_root(payload.withdrawals),
)
execution_branch = ExecutionBranch(
compute_merkle_proof(block.message.body, EXECUTION_PAYLOAD_GINDEX))
compute_merkle_proof(block.message.body, execution_payload_gindex_at_slot(block.message.slot)))
else:
# Note that during fork transitions, `finalized_header` may still point to earlier forks.
# While Bellatrix blocks also contain an `ExecutionPayload` (minus `withdrawals_root`),
Expand Down
16 changes: 13 additions & 3 deletions specs/capella/light-client/sync-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- [Containers](#containers)
- [Modified `LightClientHeader`](#modified-lightclientheader)
- [Helper functions](#helper-functions)
- [`execution_payload_gindex_at_slot`](#execution_payload_gindex_at_slot)
- [`get_lc_execution_root`](#get_lc_execution_root)
- [Modified `is_valid_light_client_header`](#modified-is_valid_light_client_header)

Expand Down Expand Up @@ -55,6 +56,16 @@ class LightClientHeader(Container):

## Helper functions

### `execution_payload_gindex_at_slot`

```python
def execution_payload_gindex_at_slot(slot: Slot) -> GeneralizedIndex:
epoch = compute_epoch_at_slot(slot)
assert epoch >= CAPELLA_FORK_EPOCH

return EXECUTION_PAYLOAD_GINDEX
```

### `get_lc_execution_root`

```python
Expand All @@ -79,11 +90,10 @@ def is_valid_light_client_header(header: LightClientHeader) -> bool:
and header.execution_branch == ExecutionBranch()
)

return is_valid_merkle_branch(
return is_valid_normalized_merkle_branch(
leaf=get_lc_execution_root(header),
branch=header.execution_branch,
depth=floorlog2(EXECUTION_PAYLOAD_GINDEX),
index=get_subtree_index(EXECUTION_PAYLOAD_GINDEX),
gindex=execution_payload_gindex_at_slot(header.beacon.slot),
root=header.beacon.body_root,
)
```
2 changes: 1 addition & 1 deletion specs/deneb/light-client/full-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def block_to_light_client_header(block: SignedBeaconBlock) -> LightClientHeader:
execution_header.excess_blob_gas = payload.excess_blob_gas

execution_branch = ExecutionBranch(
compute_merkle_proof(block.message.body, EXECUTION_PAYLOAD_GINDEX))
compute_merkle_proof(block.message.body, execution_payload_gindex_at_slot(block.message.slot)))
else:
# Note that during fork transitions, `finalized_header` may still point to earlier forks.
# While Bellatrix blocks also contain an `ExecutionPayload` (minus `withdrawals_root`),
Expand Down
5 changes: 2 additions & 3 deletions specs/deneb/light-client/sync-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,10 @@ def is_valid_light_client_header(header: LightClientHeader) -> bool:
and header.execution_branch == ExecutionBranch()
)

return is_valid_merkle_branch(
return is_valid_normalized_merkle_branch(
leaf=get_lc_execution_root(header),
branch=header.execution_branch,
depth=floorlog2(EXECUTION_PAYLOAD_GINDEX),
index=get_subtree_index(EXECUTION_PAYLOAD_GINDEX),
gindex=execution_payload_gindex_at_slot(header.beacon.slot),
root=header.beacon.body_root,
)
```
15 changes: 11 additions & 4 deletions specs/deneb/p2p-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- [Constant](#constant)
- [Preset](#preset)
- [Configuration](#configuration)
- [Custom types](#custom-types)
- [Containers](#containers)
- [`BlobSidecar`](#blobsidecar)
- [`BlobIdentifier`](#blobidentifier)
Expand Down Expand Up @@ -70,6 +71,12 @@ The specification of these changes continues in the same format as the network s
| `MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS` | `2**12` (= 4096 epochs, ~18 days) | The minimum epoch range over which a node must serve blob sidecars |
| `BLOB_SIDECAR_SUBNET_COUNT` | `6` | The number of blob sidecar subnets used in the gossipsub protocol. |

### Custom types

| Name | SSZ equivalent | Description |
| - | - | - |
| `KZGCommitmentInclusionProof` | `Vector[Bytes32, KZG_COMMITMENT_INCLUSION_PROOF_DEPTH]` | Merkle branch of a single `blob_kzg_commitments` list item within `BeaconBlockBody` |

### Containers

#### `BlobSidecar`
Expand All @@ -83,7 +90,7 @@ class BlobSidecar(Container):
kzg_commitment: KZGCommitment
kzg_proof: KZGProof # Allows for quick verification of kzg_commitment
signed_block_header: SignedBeaconBlockHeader
kzg_commitment_inclusion_proof: Vector[Bytes32, KZG_COMMITMENT_INCLUSION_PROOF_DEPTH]
kzg_commitment_inclusion_proof: KZGCommitmentInclusionProof
```

#### `BlobIdentifier`
Expand All @@ -102,12 +109,12 @@ class BlobIdentifier(Container):

```python
def verify_blob_sidecar_inclusion_proof(blob_sidecar: BlobSidecar) -> bool:
gindex = get_subtree_index(get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments', blob_sidecar.index))
gindex = get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments', blob_sidecar.index)
return is_valid_merkle_branch(
leaf=blob_sidecar.kzg_commitment.hash_tree_root(),
branch=blob_sidecar.kzg_commitment_inclusion_proof,
depth=KZG_COMMITMENT_INCLUSION_PROOF_DEPTH,
index=gindex,
depth=floorlog2(gindex),
index=get_subtree_index(gindex),
root=blob_sidecar.signed_block_header.message.body_root,
)
```
Expand Down
Loading