Create honest validator specs for Fulu (#4135)

This commit is contained in:
Justin Traglia
2025-03-20 09:40:33 -05:00
committed by GitHub
parent ccd5d8fd2d
commit ff99bc03d6
4 changed files with 145 additions and 56 deletions

View File

@@ -28,7 +28,7 @@ Features are researched and developed in parallel, and then consolidated into se
| Seq. | Code Name | Fork Epoch | Specs |
| - | - | - | - |
| 5 | **Electra** | TBD | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/electra/beacon-chain.md)</li><li>[Electra 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), [networking](specs/electra/light-client/p2p-interface.md))</li><li>[Honest validator guide changes](specs/electra/validator.md)</li><li>[P2P networking](specs/electra/p2p-interface.md)</li></ul></ul> |
| 6 | **Fulu** | TBD | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/fulu/beacon-chain.md)</li><li>[Fulu fork](specs/fulu/fork.md)</li><li>[Data availability sampling core](specs/fulu/das-core.md)</li><li>[Polynomial commitments sampling](specs/fulu/polynomial-commitments-sampling.md)</li><li>[Fork choice changes](specs/fulu/fork-choice.md)</li></ul><li>Additions</li><ul><li>[P2P networking](specs/fulu/p2p-interface.md)</li><li>[Peer sampling](specs/fulu/peer-sampling.md)</li></ul></ul> |
| 6 | **Fulu** | TBD | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/fulu/beacon-chain.md)</li><li>[Fulu fork](specs/fulu/fork.md)</li><li>[Data availability sampling core](specs/fulu/das-core.md)</li><li>[Polynomial commitments sampling](specs/fulu/polynomial-commitments-sampling.md)</li><li>[Fork choice changes](specs/fulu/fork-choice.md)</li></ul><li>Additions</li><ul><li>[Honest validator guide changes](specs/fulu/validator.md)</li><li>[P2P networking](specs/fulu/p2p-interface.md)</li><li>[Peer sampling](specs/fulu/peer-sampling.md)</li></ul></ul> |
### Accompanying documents can be found in [specs](specs) and include:

View File

@@ -177,6 +177,7 @@ def get_blob_sidecars(signed_block: SignedBeaconBlock,
```
The `subnet_id` for the `blob_sidecar` is calculated with:
- Let `blob_index = blob_sidecar.index`.
- Let `subnet_id = compute_subnet_for_blob_sidecar(blob_index)`.

View File

@@ -22,10 +22,8 @@
- [`compute_columns_for_custody_group`](#compute_columns_for_custody_group)
- [`compute_matrix`](#compute_matrix)
- [`recover_matrix`](#recover_matrix)
- [`get_data_column_sidecars`](#get_data_column_sidecars)
- [Custody](#custody)
- [Custody requirement](#custody-requirement)
- [Validator custody](#validator-custody)
- [Public, deterministic selection](#public-deterministic-selection)
- [Custody sampling](#custody-sampling)
- [Extended data](#extended-data)
@@ -73,8 +71,6 @@ The following values are (non-configurable) constants used throughout the specif
| `SAMPLES_PER_SLOT` | `8` | Number of `DataColumnSidecar` random samples a node queries per slot |
| `NUMBER_OF_CUSTODY_GROUPS` | `128` | Number of custody groups available for nodes to custody |
| `CUSTODY_REQUIREMENT` | `4` | Minimum number of custody groups an honest node custodies and serves samples from |
| `VALIDATOR_CUSTODY_REQUIREMENT` | `8` | Minimum number of custody groups an honest node with validators attached custodies and serves samples from |
| `BALANCE_PER_ADDITIONAL_CUSTODY_GROUP` | `Gwei(32 * 10**9)` | Balance increment corresponding to one additional group to custody |
### Containers
@@ -187,42 +183,6 @@ def recover_matrix(partial_matrix: Sequence[MatrixEntry], blob_count: uint64) ->
return matrix
```
### `get_data_column_sidecars`
```python
def get_data_column_sidecars(signed_block: SignedBeaconBlock,
cells_and_kzg_proofs: Sequence[Tuple[
Vector[Cell, CELLS_PER_EXT_BLOB],
Vector[KZGProof, CELLS_PER_EXT_BLOB]]]) -> Sequence[DataColumnSidecar]:
"""
Given a signed block and the cells/proofs associated with each blob in the
block, assemble the sidecars which can be distributed to peers.
"""
blob_kzg_commitments = signed_block.message.body.blob_kzg_commitments
assert len(cells_and_kzg_proofs) == len(blob_kzg_commitments)
signed_block_header = compute_signed_block_header(signed_block)
kzg_commitments_inclusion_proof = compute_merkle_proof(
signed_block.message.body,
get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments'),
)
sidecars = []
for column_index in range(NUMBER_OF_COLUMNS):
column_cells, column_proofs = [], []
for cells, proofs in cells_and_kzg_proofs:
column_cells.append(cells[column_index])
column_proofs.append(proofs[column_index])
sidecars.append(DataColumnSidecar(
index=column_index,
column=column_cells,
kzg_commitments=blob_kzg_commitments,
kzg_proofs=column_proofs,
signed_block_header=signed_block_header,
kzg_commitments_inclusion_proof=kzg_commitments_inclusion_proof,
))
return sidecars
```
## Custody
### Custody requirement
@@ -233,19 +193,6 @@ A node *may* choose to custody and serve more than the minimum honesty requireme
A node stores the custodied columns for the duration of the pruning period and responds to peer requests for samples on those columns.
### Validator custody
A node with validators attached downloads and custodies a higher minimum of custody groups per slot, determined by `get_validators_custody_requirement(state, validator_indices)`. Here, `state` is the current `BeaconState` and `validator_indices` is the list of indices corresponding to validators attached to the node. Any node with at least one validator attached, and with the sum of the balances of all attached validators being `total_node_balance`, downloads and custodies `total_node_balance // BALANCE_PER_ADDITIONAL_CUSTODY_GROUP` custody groups per slot, with a minimum of `VALIDATOR_CUSTODY_REQUIREMENT` and of course a maximum of `NUMBER_OF_CUSTODY_GROUPS`.
```python
def get_validators_custody_requirement(state: BeaconState, validator_indices: Sequence[ValidatorIndex]) -> uint64:
total_node_balance = sum(state.balances[index] for index in validator_indices)
count = total_node_balance // BALANCE_PER_ADDITIONAL_CUSTODY_GROUP
return min(max(count, VALIDATOR_CUSTODY_REQUIREMENT), NUMBER_OF_CUSTODY_GROUPS)
```
This higher custody is advertised in the node's Metadata by setting a higher `custody_group_count` and in the node's ENR by setting a higher `cgc`. As with the regular custody requirement, a node with validators *may* still choose to custody, advertise and serve more than this minimum. As with the regular custody requirement, a node MUST backfill columns when syncing. In addition, when the validator custody requirement increases, due to an increase in the total balance of the attached validators, a node MUST backfill columns from the new custody groups. However, a node *may* wait to advertise a higher custody in its Metadata and ENR until backfilling is complete.
### Public, deterministic selection
The particular columns/groups that a node custodies are selected pseudo-randomly as a function (`get_custody_groups`) of the node-id and custody size -- importantly this function can be run by any party as the inputs are all public.
@@ -264,8 +211,6 @@ In this construction, we extend the blobs using a one-dimensional erasure coding
### Parameters
For each column -- use `data_column_sidecar_{subnet_id}` subnets, where `subnet_id` can be computed with the `compute_subnet_for_data_column_sidecar(column_index: ColumnIndex)` helper. The sidecars can be computed with `cells_and_kzg_proofs = [compute_cells_and_kzg_proofs(blob) for blob in blobs]` and then `get_data_column_sidecars(signed_block, cells_and_kzg_proofs)`.
Verifiable samples from their respective column are distributed on the assigned subnet. To custody columns in a particular custody group, a node joins the respective gossipsub subnets. If a node fails to get columns on the column subnets, a node can also utilize the Req/Resp protocol to query the missing columns from other peers.
## Reconstruction and cross-seeding

143
specs/fulu/validator.md Normal file
View File

@@ -0,0 +1,143 @@
# Fulu -- Honest Validator
## 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)
- [Prerequisites](#prerequisites)
- [Configuration](#configuration)
- [Custody setting](#custody-setting)
- [Beacon chain responsibilities](#beacon-chain-responsibilities)
- [Validator custody](#validator-custody)
- [Block and sidecar proposal](#block-and-sidecar-proposal)
- [Constructing the sidecars](#constructing-the-sidecars)
- [`get_data_column_sidecars`](#get_data_column_sidecars)
- [Sidecar publishing](#sidecar-publishing)
- [Sidecar retention](#sidecar-retention)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
This document represents the changes to be made in the code of an "honest validator" to implement Fulu.
## Prerequisites
This document is an extension of the [Electra -- Honest Validator](../electra/validator.md) guide.
All behaviors and definitions defined in this document, and documents it extends, carry over unless
explicitly noted or overridden.
All terminology, constants, functions, and protocol mechanics defined in [Fulu -- Beacon
Chain](./beacon-chain.md) and [Fulu -- Data Availability Sampling Core] are requisite for this
document and used throughout.
## Configuration
### Custody setting
| Name | Value | Description |
| - | - | - |
| `VALIDATOR_CUSTODY_REQUIREMENT` | `8` | Minimum number of custody groups an honest node with validators attached custodies and serves samples from |
| `BALANCE_PER_ADDITIONAL_CUSTODY_GROUP` | `Gwei(32 * 10**9)` | Balance increment corresponding to one additional group to custody |
## Beacon chain responsibilities
### Validator custody
*[New in Fulu:EIP7594]*
A node with validators attached downloads and custodies a higher minimum of custody groups per slot,
determined by `get_validators_custody_requirement(state, validator_indices)`. Here, `state` is the
current `BeaconState` and `validator_indices` is the list of indices corresponding to validators
attached to the node. Any node with at least one validator attached, and with the sum of the
balances of all attached validators being `total_node_balance`, downloads and custodies
`total_node_balance // BALANCE_PER_ADDITIONAL_CUSTODY_GROUP` custody groups per slot, with a minimum
of `VALIDATOR_CUSTODY_REQUIREMENT` and of course a maximum of `NUMBER_OF_CUSTODY_GROUPS`.
```python
def get_validators_custody_requirement(state: BeaconState, validator_indices: Sequence[ValidatorIndex]) -> uint64:
total_node_balance = sum(state.balances[index] for index in validator_indices)
count = total_node_balance // BALANCE_PER_ADDITIONAL_CUSTODY_GROUP
return min(max(count, VALIDATOR_CUSTODY_REQUIREMENT), NUMBER_OF_CUSTODY_GROUPS)
```
This higher custody is advertised in the node's Metadata by setting a higher `custody_group_count`
and in the node's ENR by setting a higher `custody_group_count`. As with the regular custody
requirement, a node with validators *may* still choose to custody, advertise and serve more than
this minimum. As with the regular custody requirement, a node MUST backfill columns when syncing. In
addition, when the validator custody requirement increases, due to an increase in the total balance
of the attached validators, a node MUST backfill columns from the new custody groups. However, a
node *may* wait to advertise a higher custody in its Metadata and ENR until backfilling is complete.
### Block and sidecar proposal
#### Constructing the sidecars
*[New in Fulu:EIP7594]*
For a block proposal, blobs associated with a block are packaged into many `DataColumnSidecar`
objects for distribution to the associated sidecar topic, the `data_column_sidecar_{subnet_id}`
pubsub topic. A `DataColumnSidecar` can be viewed as vertical slice of all blobs stacked on top of
each other, with extra fields for the necessary context.
##### `get_data_column_sidecars`
The sequence of sidecars associated with a block and can be obtained by first computing
`cells_and_kzg_proofs = [compute_cells_and_kzg_proofs(blob) for blob in blobs]` and then calling
`get_data_column_sidecars(signed_block, cells_and_kzg_proofs)`.
```python
def get_data_column_sidecars(signed_block: SignedBeaconBlock,
cells_and_kzg_proofs: Sequence[Tuple[
Vector[Cell, CELLS_PER_EXT_BLOB],
Vector[KZGProof, CELLS_PER_EXT_BLOB]]]) -> Sequence[DataColumnSidecar]:
"""
Given a signed block and the cells/proofs associated with each blob in the
block, assemble the sidecars which can be distributed to peers.
"""
blob_kzg_commitments = signed_block.message.body.blob_kzg_commitments
assert len(cells_and_kzg_proofs) == len(blob_kzg_commitments)
signed_block_header = compute_signed_block_header(signed_block)
kzg_commitments_inclusion_proof = compute_merkle_proof(
signed_block.message.body,
get_generalized_index(BeaconBlockBody, 'blob_kzg_commitments'),
)
sidecars = []
for column_index in range(NUMBER_OF_COLUMNS):
column_cells, column_proofs = [], []
for cells, proofs in cells_and_kzg_proofs:
column_cells.append(cells[column_index])
column_proofs.append(proofs[column_index])
sidecars.append(DataColumnSidecar(
index=column_index,
column=column_cells,
kzg_commitments=blob_kzg_commitments,
kzg_proofs=column_proofs,
signed_block_header=signed_block_header,
kzg_commitments_inclusion_proof=kzg_commitments_inclusion_proof,
))
return sidecars
```
#### Sidecar publishing
The `subnet_id` for the `data_column_sidecar` is calculated with:
- Let `column_index = data_column_sidecar.index`.
- Let `subnet_id = compute_subnet_for_data_column_sidecar(column_index)`.
After publishing all columns to their respective subnets, peers on the network may request the
sidecar through sync-requests, or a local user may be interested.
#### Sidecar retention
The validator MUST hold on to sidecars for `MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS` epochs and
serve when capable, to ensure the data-availability of these blobs throughout the network.
After `MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS` nodes MAY prune the sidecars and/or stop
serving them.