Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 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
22 changes: 11 additions & 11 deletions specs/deneb/p2p-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
- [`beacon_aggregate_and_proof`](#beacon_aggregate_and_proof)
- [Blob subnets](#blob-subnets)
- [`blob_sidecar_{subnet_id}`](#blob_sidecar_subnet_id)
- [Blob retrieval via local execution layer client](#blob-retrieval-via-local-execution-layer-client)
- [Attestation subnets](#attestation-subnets)
- [`beacon_attestation_{subnet_id}`](#beacon_attestation_subnet_id)
- [Transitioning the gossip](#transitioning-the-gossip)
Expand All @@ -32,7 +33,6 @@
- [BeaconBlocksByRoot v2](#beaconblocksbyroot-v2)
- [BlobSidecarsByRange v1](#blobsidecarsbyrange-v1)
- [BlobSidecarsByRoot v1](#blobsidecarsbyroot-v1)
- [Blob retrieval via local execution layer client](#blob-retrieval-via-local-execution-layer-client)
- [Design decision rationale](#design-decision-rationale)
- [Why are blobs relayed as a sidecar, separate from beacon blocks?](#why-are-blobs-relayed-as-a-sidecar-separate-from-beacon-blocks)

Expand Down Expand Up @@ -203,6 +203,16 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
|--------------------------------|---------------------|
| `DENEB_FORK_VERSION` and later | `deneb.BlobSidecar` |

###### Blob retrieval via local execution layer client

In addition to `BlobSidecarsByRoot` requests, recent blobs MAY be retrieved by querying the Execution Layer (i.e. via `engine_getBlobsV1`).
Honest nodes SHOULD query `engine_getBlobsV1` as soon as they receive a valid gossip block that contains data, and import the returned blobs.

When clients use the local execution layer to retrieve blobs, they MUST behave as if the corresponding `blob_sidecar` had been received via gossip. In particular they MUST:

* Publish the corresponding `blob_sidecar` on the `blob_sidecar_{subnet_id}` subnet.
* Update gossip rule related data structures (i.e. update the anti-equivocation cache).

##### Attestation subnets

###### `beacon_attestation_{subnet_id}`
Expand Down Expand Up @@ -413,16 +423,6 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
|--------------------------------|---------------------|
| `DENEB_FORK_VERSION` and later | `deneb.BlobSidecar` |

###### Blob retrieval via local execution layer client

In addition to `BlobSidecarsByRoot` requests, recent blobs MAY be retrieved by querying the Execution Layer (i.e. via `engine_getBlobsV1`).
Implementers are encouraged to leverage this method to increase the likelihood of incorporating and attesting to the last block when its proposer is not able to publish blobs on time.

When clients use the local execution layer to retrieve blobs, they MUST behave as if the corresponding `blob_sidecar` had been received via gossip. In particular they MUST:

* publish the corresponding `blob_sidecar` on the `blob_sidecar_{subnet_id}` subnet.
* update gossip rule related data structures (i.e. update the anti-equivocation cache).

## Design decision rationale

### Why are blobs relayed as a sidecar, separate from beacon blocks?
Expand Down
12 changes: 12 additions & 0 deletions specs/fulu/p2p-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- [Blob subnets](#blob-subnets)
- [Deprecated `blob_sidecar_{subnet_id}`](#deprecated-blob_sidecar_subnet_id)
- [`data_column_sidecar_{subnet_id}`](#data_column_sidecar_subnet_id)
- [Distributed Blob Publishing using blobs retrieved from local execution layer client](#distributed-blob-publishing-using-blobs-retrieved-from-local-execution-layer-client)
- [The Req/Resp domain](#the-reqresp-domain)
- [Messages](#messages)
- [DataColumnSidecarsByRange v1](#datacolumnsidecarsbyrange-v1)
Expand Down Expand Up @@ -204,6 +205,17 @@ The following validations MUST pass before forwarding the `sidecar: DataColumnSi

*Note:* In the `verify_data_column_sidecar_inclusion_proof(sidecar)` check, for all the sidecars of the same block, it verifies against the same set of `kzg_commitments` of the given beacon block. Client can choose to cache the result of the arguments tuple `(sidecar.kzg_commitments, sidecar.kzg_commitments_inclusion_proof, sidecar.signed_block_header)`.

###### Distributed Blob Publishing using blobs retrieved from local execution layer client

Honest nodes SHOULD query `engine_getBlobsV2` as soon as they receive a valid `beacon_block` or `data_column_sidecar` from gossip. If ALL blobs matching `kzg_commitments` are retrieved, they should convert the response to data columns, and import the result.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've concerns on requiring it from all. If your custody requirement is just 4, it sounds like too much to follow all these rules and spend computation power. I'd bound it to supernodes only.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also home staker on ADSL wouldn't be happy to try to publish all DataColumns with 48, 64 etc blobs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Full nodes won't actually send all data columns - they only send data columns on topics they're subscribed to. This is the same behaviour as if you receive a data column via gossip - you'd spend the bandwidth to propagate it to mesh peers.

If the node ever add the columns to the "gossip seen cache" without publishing / forwarding the column, it would stop propagation of the columns to the rest of the network.

re 1st point about computing columns - proofs will no longer be computed in the CL, so this would be a cheap operation, and potentially makes the block available sooner than waiting for columns to arrive from gossip.


Implementers are encouraged to leverage this method to increase the likelihood of incorporating and attesting to the last block when its proposer is not able to publish data columns on time.

When clients use the local execution layer to retrieve blob and compute data columns, they MUST behave as if the imported `data_column_sidecar` had been received via gossip. In particular, clients MUST:

* Publish the corresponding `data_column_sidecar` on the `data_column_sidecar_{subnet_id}` topic **if and only if** they are **subscribed** to it, either due to custody requirements or additional sampling.
* Update gossip rule related data structures (i.e. update the anti-equivocation cache).

### The Req/Resp domain

#### Messages
Expand Down
87 changes: 73 additions & 14 deletions specs/fulu/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
- [Block and sidecar proposal](#block-and-sidecar-proposal)
- [Constructing the sidecars](#constructing-the-sidecars)
- [`get_data_column_sidecars`](#get_data_column_sidecars)
- [`get_data_column_sidecars_from_block`](#get_data_column_sidecars_from_block)
- [`get_data_column_sidecars_from_column_sidecar`](#get_data_column_sidecars_from_column_sidecar)
- [Sidecar publishing](#sidecar-publishing)
- [Sidecar retention](#sidecar-retention)

Expand Down Expand Up @@ -148,24 +150,29 @@ each other, with extra fields for the necessary context.

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)`.
`get_data_column_sidecars_from_block(signed_block, cells_and_kzg_proofs)`.

Moreover, the full sequence of sidecars can also be computed from `cells_and_kzg_proofs` and any single
`data_column_sidecar`, by calling `get_data_column_sidecars_from_column_sidecar(sidecar, cells_and_kzg_proofs)`.
This can be used in distributed blob publishing, to reconstruct all sidecars from any sidecar received
on the wire, as well as all cells and kzg proofs retrieved from the local execution layer client.


```python
def get_data_column_sidecars(signed_block: SignedBeaconBlock,
cells_and_kzg_proofs: Sequence[Tuple[
def get_data_column_sidecars(
signed_block_header: SignedBeaconBlockHeader,
kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK],
kzg_commitments_inclusion_proof: Vector[Bytes32, KZG_COMMITMENTS_INCLUSION_PROOF_DEPTH],
cells_and_kzg_proofs: Sequence[Tuple[
Vector[Cell, CELLS_PER_EXT_BLOB],
Vector[KZGProof, CELLS_PER_EXT_BLOB]]]) -> Sequence[DataColumnSidecar]:
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.
Given a signed block header and the commitments, inclusion proof, 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'),
)
assert len(cells_and_kzg_proofs) == len(kzg_commitments)

sidecars = []
for column_index in range(NUMBER_OF_COLUMNS):
Expand All @@ -176,14 +183,66 @@ def get_data_column_sidecars(signed_block: SignedBeaconBlock,
sidecars.append(DataColumnSidecar(
index=column_index,
column=column_cells,
kzg_commitments=blob_kzg_commitments,
kzg_commitments=kzg_commitments,
kzg_proofs=column_proofs,
signed_block_header=signed_block_header,
kzg_commitments_inclusion_proof=kzg_commitments_inclusion_proof,
))
return sidecars
```

##### `get_data_column_sidecars_from_block`

```python
def get_data_column_sidecars_from_block(
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
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'),
)
return get_data_column_sidecars(
signed_block_header,
blob_kzg_commitments,
kzg_commitments_inclusion_proof,
cells_and_kzg_proofs
)
```

##### `get_data_column_sidecars_from_column_sidecar`

```python
def get_data_column_sidecars_from_column_sidecar(
sidecar: DataColumnSidecar,
cells_and_kzg_proofs: Sequence[Tuple[
Vector[Cell, CELLS_PER_EXT_BLOB],
Vector[KZGProof, CELLS_PER_EXT_BLOB]
]]
) -> Sequence[DataColumnSidecar]:
"""
Given a DataColumnSidecar and the cells/proofs associated with each blob corresponding
to the commitments it contains, assemble all sidecars for distribution to peers.
"""
assert len(cells_and_kzg_proofs) == len(sidecar.kzg_commitments)

return get_data_column_sidecars(
sidecar.signed_block_header,
sidecar.kzg_commitments,
sidecar.kzg_commitments_inclusion_proof,
cells_and_kzg_proofs
)
```

#### Sidecar publishing

The `subnet_id` for the `data_column_sidecar` is calculated with:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def _run_blob_kzg_commitments_merkle_proof_test(spec, state, rng=None, blob_coun
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state)
signed_block = sign_block(spec, state, block, proposer_index=0)
cells_and_kzg_proofs = [spec.compute_cells_and_kzg_proofs(blob) for blob in blobs]
column_sidcars = spec.get_data_column_sidecars(signed_block, cells_and_kzg_proofs)
column_sidcars = spec.get_data_column_sidecars_from_block(signed_block, cells_and_kzg_proofs)
column_sidcar = column_sidcars[0]

yield "object", block.body
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def compute_data_column_sidecar(spec, state):
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state)
signed_block = sign_block(spec, state, block, proposer_index=0)
cells_and_kzg_proofs = [spec.compute_cells_and_kzg_proofs(blob) for blob in blobs]
return spec.get_data_column_sidecars(signed_block, cells_and_kzg_proofs)[0]
return spec.get_data_column_sidecars_from_block(signed_block, cells_and_kzg_proofs)[0]


# Tests for verify_data_column_sidecar
Expand Down