mirror of
https://github.com/ethereum/consensus-specs.git
synced 2026-02-03 00:24:56 -05:00
Merge pull request #3244 from arnetheduck/back-to-the-decoupled-future
EIP-4844: Free the blobs
This commit is contained in:
4
setup.py
4
setup.py
@@ -653,9 +653,9 @@ T = TypeVar('T') # For generic function
|
||||
@classmethod
|
||||
def sundry_functions(cls) -> str:
|
||||
return super().sundry_functions() + '\n\n' + '''
|
||||
def retrieve_blobs_sidecar(slot: Slot, beacon_block_root: Root) -> PyUnion[BlobsSidecar, str]:
|
||||
def retrieve_blobs_and_proofs(beacon_block_root: Root) -> PyUnion[Tuple[Blob, KZGProof], Tuple[str, str]]:
|
||||
# pylint: disable=unused-argument
|
||||
return "TEST"'''
|
||||
return ("TEST", "TEST")'''
|
||||
|
||||
@classmethod
|
||||
def hardcoded_custom_type_dep_constants(cls, spec_object) -> str:
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
- [Introduction](#introduction)
|
||||
- [Custom types](#custom-types)
|
||||
- [Constants](#constants)
|
||||
- [Domain types](#domain-types)
|
||||
- [Blob](#blob)
|
||||
- [Preset](#preset)
|
||||
- [Execution](#execution)
|
||||
@@ -44,15 +45,22 @@ This upgrade adds blobs to the beacon chain as part of Deneb. This is an extensi
|
||||
| Name | SSZ equivalent | Description |
|
||||
| - | - | - |
|
||||
| `VersionedHash` | `Bytes32` | |
|
||||
| `BlobIndex` | `uint64` | |
|
||||
|
||||
## Constants
|
||||
|
||||
### Domain types
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `DOMAIN_BLOB_SIDECAR` | `DomainType('0x0B000000')` |
|
||||
|
||||
### Blob
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `BLOB_TX_TYPE` | `uint8(0x05)` |
|
||||
| `VERSIONED_HASH_VERSION_KZG` | `Bytes1('0x01')` |
|
||||
| `VERSIONED_HASH_VERSION_KZG` | `Bytes1('0x01')` |
|
||||
|
||||
## Preset
|
||||
|
||||
@@ -249,7 +257,7 @@ def process_blob_kzg_commitments(state: BeaconState, body: BeaconBlockBody) -> N
|
||||
|
||||
*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure Deneb testing only.
|
||||
|
||||
The `BeaconState` initialization is unchanged, except for the use of the updated `deneb.BeaconBlockBody` type
|
||||
The `BeaconState` initialization is unchanged, except for the use of the updated `deneb.BeaconBlockBody` type
|
||||
when initializing the first body-root.
|
||||
|
||||
```python
|
||||
|
||||
@@ -7,9 +7,8 @@
|
||||
|
||||
- [Introduction](#introduction)
|
||||
- [Containers](#containers)
|
||||
- [`BlobsSidecar`](#blobssidecar)
|
||||
- [Helpers](#helpers)
|
||||
- [`validate_blobs_sidecar`](#validate_blobs_sidecar)
|
||||
- [`validate_blobs`](#validate_blobs)
|
||||
- [`is_data_available`](#is_data_available)
|
||||
- [Updated fork-choice handlers](#updated-fork-choice-handlers)
|
||||
- [`on_block`](#on_block)
|
||||
@@ -23,55 +22,40 @@ This is the modification of the fork choice accompanying the Deneb upgrade.
|
||||
|
||||
## Containers
|
||||
|
||||
### `BlobsSidecar`
|
||||
|
||||
```python
|
||||
class BlobsSidecar(Container):
|
||||
beacon_block_root: Root
|
||||
beacon_block_slot: Slot
|
||||
blobs: List[Blob, MAX_BLOBS_PER_BLOCK]
|
||||
kzg_aggregated_proof: KZGProof
|
||||
```
|
||||
|
||||
## Helpers
|
||||
|
||||
#### `validate_blobs_sidecar`
|
||||
#### `validate_blobs`
|
||||
|
||||
```python
|
||||
def validate_blobs_sidecar(slot: Slot,
|
||||
beacon_block_root: Root,
|
||||
expected_kzg_commitments: Sequence[KZGCommitment],
|
||||
blobs_sidecar: BlobsSidecar) -> None:
|
||||
assert slot == blobs_sidecar.beacon_block_slot
|
||||
assert beacon_block_root == blobs_sidecar.beacon_block_root
|
||||
blobs = blobs_sidecar.blobs
|
||||
# kzg_aggregated_proof = blobs_sidecar.kzg_aggregated_proof
|
||||
def validate_blobs(expected_kzg_commitments: Sequence[KZGCommitment],
|
||||
blobs: Sequence[Blob],
|
||||
proofs: Sequence[KZGProof]) -> None:
|
||||
assert len(expected_kzg_commitments) == len(blobs)
|
||||
assert len(blobs) == len(proofs)
|
||||
|
||||
# Disabled because not available before switch to single blob sidecars
|
||||
# assert verify_aggregate_kzg_proof(blobs, expected_kzg_commitments, kzg_aggregated_proof)
|
||||
assert verify_blob_kzg_proof_batch(blobs, expected_kzg_commitments, proofs)
|
||||
```
|
||||
|
||||
#### `is_data_available`
|
||||
|
||||
The implementation of `is_data_available` will become more sophisticated during later scaling upgrades.
|
||||
Initially, verification requires every verifying actor to retrieve the matching `BlobsSidecar`,
|
||||
and validate the sidecar with `validate_blobs_sidecar`.
|
||||
Initially, verification requires every verifying actor to retrieve all matching `Blob`s and `KZGProof`s, and validate them with `validate_blobs`.
|
||||
|
||||
The block MUST NOT be considered valid until a valid `BlobsSidecar` has been downloaded. Blocks that have been previously validated as available SHOULD be considered available even if the associated `BlobsSidecar` has subsequently been pruned.
|
||||
The block MUST NOT be considered valid until all valid `Blob`s have been downloaded. Blocks that have been previously validated as available SHOULD be considered available even if the associated `Blob`s have subsequently been pruned.
|
||||
|
||||
```python
|
||||
def is_data_available(slot: Slot, beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]) -> bool:
|
||||
# `retrieve_blobs_sidecar` is implementation and context dependent, raises an exception if not available.
|
||||
def is_data_available(beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]) -> bool:
|
||||
# `retrieve_blobs_and_proofs` is implementation and context dependent
|
||||
# It returns all the blobs for the given block root, and raises an exception if not available
|
||||
# Note: the p2p network does not guarantee sidecar retrieval outside of `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS`
|
||||
sidecar = retrieve_blobs_sidecar(slot, beacon_block_root)
|
||||
blobs, proofs = retrieve_blobs_and_proofs(beacon_block_root)
|
||||
|
||||
# For testing, `retrieve_blobs_sidecar` returns "TEST".
|
||||
# TODO: Remove it once we have a way to inject `BlobsSidecar` into tests.
|
||||
if isinstance(sidecar, str):
|
||||
# For testing, `retrieve_blobs_and_proofs` returns ("TEST", "TEST").
|
||||
# TODO: Remove it once we have a way to inject `BlobSidecar` into tests.
|
||||
if isinstance(blobs, str) or isinstance(proofs, str):
|
||||
return True
|
||||
|
||||
validate_blobs_sidecar(slot, beacon_block_root, blob_kzg_commitments, sidecar)
|
||||
validate_blobs(blob_kzg_commitments, blobs, proofs)
|
||||
return True
|
||||
```
|
||||
|
||||
@@ -103,7 +87,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
||||
# [New in Deneb]
|
||||
# Check if blob data is available
|
||||
# If not, this block MAY be queued and subsequently considered when blob data becomes available
|
||||
assert is_data_available(block.slot, hash_tree_root(block), block.body.blob_kzg_commitments)
|
||||
assert is_data_available(hash_tree_root(block), block.body.blob_kzg_commitments)
|
||||
|
||||
# Check the block is valid and compute the post-state
|
||||
state = pre_state.copy()
|
||||
|
||||
@@ -10,21 +10,23 @@ The specification of these changes continues in the same format as the network s
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
|
||||
- [Configuration](#configuration)
|
||||
- [Containers](#containers)
|
||||
- [`SignedBeaconBlockAndBlobsSidecar`](#signedbeaconblockandblobssidecar)
|
||||
- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub)
|
||||
- [Topics and messages](#topics-and-messages)
|
||||
- [Global topics](#global-topics)
|
||||
- [`beacon_block`](#beacon_block)
|
||||
- [`beacon_block_and_blobs_sidecar`](#beacon_block_and_blobs_sidecar)
|
||||
- [Transitioning the gossip](#transitioning-the-gossip)
|
||||
- [The Req/Resp domain](#the-reqresp-domain)
|
||||
- [Messages](#messages)
|
||||
- [BeaconBlocksByRange v2](#beaconblocksbyrange-v2)
|
||||
- [BeaconBlocksByRoot v2](#beaconblocksbyroot-v2)
|
||||
- [BeaconBlockAndBlobsSidecarByRoot v1](#beaconblockandblobssidecarbyroot-v1)
|
||||
- [BlobsSidecarsByRange v1](#blobssidecarsbyrange-v1)
|
||||
- [Configuration](#configuration)
|
||||
- [Containers](#containers)
|
||||
- [`BlobSidecar`](#blobsidecar)
|
||||
- [`SignedBlobSidecar`](#signedblobsidecar)
|
||||
- [`BlobIdentifier`](#blobidentifier)
|
||||
- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub)
|
||||
- [Topics and messages](#topics-and-messages)
|
||||
- [Global topics](#global-topics)
|
||||
- [`beacon_block`](#beacon_block)
|
||||
- [`blob_sidecar_{index}`](#blob_sidecar_index)
|
||||
- [Transitioning the gossip](#transitioning-the-gossip)
|
||||
- [The Req/Resp domain](#the-reqresp-domain)
|
||||
- [Messages](#messages)
|
||||
- [BeaconBlocksByRange v2](#beaconblocksbyrange-v2)
|
||||
- [BeaconBlocksByRoot v2](#beaconblocksbyroot-v2)
|
||||
- [BlobSidecarsByRoot v1](#blobsidecarsbyroot-v1)
|
||||
- [BlobSidecarsByRange v1](#blobsidecarsbyrange-v1)
|
||||
- [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)
|
||||
|
||||
@@ -35,17 +37,40 @@ The specification of these changes continues in the same format as the network s
|
||||
|
||||
| Name | Value | Description |
|
||||
|------------------------------------------|-----------------------------------|---------------------------------------------------------------------|
|
||||
| `MAX_REQUEST_BLOBS_SIDECARS` | `2**7` (= 128) | Maximum number of blobs sidecars in a single request |
|
||||
| `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` | `2**12` (= 4096 epochs, ~18 days) | The minimum epoch range over which a node must serve blobs sidecars |
|
||||
| `MAX_REQUEST_BLOCKS_DENEB` | `2**7` (= 128) | Maximum number of blocks in a single request |
|
||||
| `MAX_REQUEST_BLOB_SIDECARS` | `2**7` (= 128) | Maximum number of blob sidecars in a single request |
|
||||
| `MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS` | `2**12` (= 4096 epochs, ~18 days) | The minimum epoch range over which a node must serve blob sidecars |
|
||||
|
||||
## Containers
|
||||
|
||||
### `SignedBeaconBlockAndBlobsSidecar`
|
||||
### `BlobSidecar`
|
||||
|
||||
```python
|
||||
class SignedBeaconBlockAndBlobsSidecar(Container):
|
||||
beacon_block: SignedBeaconBlock
|
||||
blobs_sidecar: BlobsSidecar
|
||||
class BlobSidecar(Container):
|
||||
block_root: Root
|
||||
index: BlobIndex # Index of blob in block
|
||||
slot: Slot
|
||||
block_parent_root: Root # Proposer shuffling determinant
|
||||
proposer_index: ValidatorIndex
|
||||
blob: Blob
|
||||
kzg_commitment: KZGCommitment
|
||||
kzg_proof: KZGProof # Allows for quick verification of kzg_commitment
|
||||
```
|
||||
|
||||
### `SignedBlobSidecar`
|
||||
|
||||
```python
|
||||
class SignedBlobSidecar(Container):
|
||||
message: BlobSidecar
|
||||
signature: BLSSignature
|
||||
```
|
||||
|
||||
### `BlobIdentifier`
|
||||
|
||||
```python
|
||||
class BlobIdentifier(Container):
|
||||
block_root: Root
|
||||
index: BlobIndex
|
||||
```
|
||||
|
||||
## The gossip domain: gossipsub
|
||||
@@ -55,7 +80,8 @@ Some gossip meshes are upgraded in the fork of Deneb to support upgraded types.
|
||||
### Topics and messages
|
||||
|
||||
Topics follow the same specification as in prior upgrades.
|
||||
The `beacon_block` topic is deprecated and replaced by the `beacon_block_and_blobs_sidecar` topic. All other topics remain stable.
|
||||
|
||||
The `beacon_block` topic is modified to also support deneb blocks and new topics are added per table below. All other topics remain stable.
|
||||
|
||||
The specification around the creation, validation, and dissemination of messages has not changed from the Capella document unless explicitly noted here.
|
||||
|
||||
@@ -65,34 +91,32 @@ The new topics along with the type of the `data` field of a gossipsub message ar
|
||||
|
||||
| Name | Message Type |
|
||||
| - | - |
|
||||
| `beacon_block_and_blobs_sidecar` | `SignedBeaconBlockAndBlobsSidecar` (new) |
|
||||
| `blob_sidecar_{index}` | `SignedBlobSidecar` (new) |
|
||||
|
||||
#### Global topics
|
||||
|
||||
Deneb introduces a new global topic for beacon block and blobs-sidecars.
|
||||
Deneb introduces new global topics for blob sidecars.
|
||||
|
||||
##### `beacon_block`
|
||||
|
||||
This topic is deprecated and clients **MUST NOT** expose in their topic set to any peer. Implementers do not need to do
|
||||
anything beyond simply skip implementation, and it is explicitly called out as it is a departure from previous versioning
|
||||
of this topic.
|
||||
The *type* of the payload of this topic changes to the (modified) `SignedBeaconBlock` found in deneb.
|
||||
|
||||
Refer to [the section below](#transitioning-the-gossip) for details on how to transition the gossip.
|
||||
##### `blob_sidecar_{index}`
|
||||
|
||||
##### `beacon_block_and_blobs_sidecar`
|
||||
This topic is used to propagate signed blob sidecars, one for each sidecar index. The number of indices is defined by `MAX_BLOBS_PER_BLOCK`.
|
||||
|
||||
This topic is used to propagate new signed and coupled beacon blocks and blobs sidecars to all nodes on the networks.
|
||||
The following validations MUST pass before forwarding the `sidecar` on the network, assuming the alias `sidecar = signed_blob_sidecar.message`:
|
||||
|
||||
In addition to the gossip validations for the `beacon_block` topic from prior specifications, the following validations MUST pass before forwarding the `signed_beacon_block_and_blobs_sidecar` on the network.
|
||||
Alias `signed_beacon_block = signed_beacon_block_and_blobs_sidecar.beacon_block`, `block = signed_beacon_block.message`, `execution_payload = block.body.execution_payload`.
|
||||
- _[REJECT]_ The KZG commitments correspond to the versioned hashes in the transactions list
|
||||
-- i.e. `verify_kzg_commitments_against_transactions(block.body.execution_payload.transactions, block.body.blob_kzg_commitments)`
|
||||
- _[REJECT]_ The sidecar is for the correct topic -- i.e. `sidecar.index` matches the topic `{index}`.
|
||||
- _[IGNORE]_ The sidecar is not from a future slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. validate that `sidecar.slot <= current_slot` (a client MAY queue future blocks for processing at the appropriate slot).
|
||||
- _[IGNORE]_ The sidecar is from a slot greater than the latest finalized slot -- i.e. validate that `sidecar.slot > compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)`
|
||||
- _[IGNORE]_ The blob's block's parent (defined by `sidecar.block_parent_root`) has been seen (via both gossip and non-gossip sources) (a client MAY queue blocks for processing once the parent block is retrieved).
|
||||
- _[REJECT]_ The blob's block's parent (defined by `sidecar.block_parent_root`) passes validation.
|
||||
- _[REJECT]_ The proposer signature, `signed_blob_sidecar.signature`, is valid with respect to the `sidecar.proposer_index` pubkey.
|
||||
- _[IGNORE]_ The sidecar is the only sidecar with valid signature received for the tuple `(sidecar.block_root, sidecar.index)`.
|
||||
- _[REJECT]_ The sidecar is proposed by the expected `proposer_index` for the block's slot in the context of the current shuffling (defined by `block_parent_root`/`slot`).
|
||||
If the `proposer_index` cannot immediately be verified against the expected shuffling, the sidecar MAY be queued for later processing while proposers for the block's branch are calculated -- in such a case _do not_ `REJECT`, instead `IGNORE` this message.
|
||||
|
||||
Alias `sidecar = signed_beacon_block_and_blobs_sidecar.blobs_sidecar`.
|
||||
- _[IGNORE]_ the `sidecar.beacon_block_slot` is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance)
|
||||
-- i.e. `sidecar.beacon_block_slot == block.slot`.
|
||||
- _[REJECT]_ The KZG commitments in the block are valid against the provided blobs sidecar
|
||||
-- i.e. `validate_blobs_sidecar(block.slot, hash_tree_root(block), block.body.blob_kzg_commitments, sidecar)`
|
||||
|
||||
### Transitioning the gossip
|
||||
|
||||
@@ -121,13 +145,12 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
|
||||
| `CAPELLA_FORK_VERSION` | `capella.SignedBeaconBlock` |
|
||||
| `DENEB_FORK_VERSION` | `deneb.SignedBeaconBlock` |
|
||||
|
||||
No more than `MAX_REQUEST_BLOCKS_DENEB` may be requested at a time.
|
||||
|
||||
#### BeaconBlocksByRoot v2
|
||||
|
||||
**Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_root/2/`
|
||||
|
||||
After `DENEB_FORK_EPOCH`, `BeaconBlocksByRootV2` is replaced by `BeaconBlockAndBlobsSidecarByRootV1`.
|
||||
Clients MUST support requesting blocks by root for pre-fork-epoch blocks.
|
||||
|
||||
Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
|
||||
|
||||
[1]: # (eth2spec: skip)
|
||||
@@ -138,16 +161,21 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
|
||||
| `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` |
|
||||
| `BELLATRIX_FORK_VERSION` | `bellatrix.SignedBeaconBlock` |
|
||||
| `CAPELLA_FORK_VERSION` | `capella.SignedBeaconBlock` |
|
||||
| `DENEB_FORK_VERSION` | `deneb.SignedBeaconBlock` |
|
||||
|
||||
#### BeaconBlockAndBlobsSidecarByRoot v1
|
||||
No more than `MAX_REQUEST_BLOCKS_DENEB` may be requested at a time.
|
||||
|
||||
**Protocol ID:** `/eth2/beacon_chain/req/beacon_block_and_blobs_sidecar_by_root/1/`
|
||||
#### BlobSidecarsByRoot v1
|
||||
|
||||
**Protocol ID:** `/eth2/beacon_chain/req/blob_sidecars_by_root/1/`
|
||||
|
||||
New in deneb.
|
||||
|
||||
Request Content:
|
||||
|
||||
```
|
||||
(
|
||||
List[Root, MAX_REQUEST_BLOCKS]
|
||||
List[BlobIdentifier, MAX_REQUEST_BLOBS_SIDECARS * MAX_BLOBS_PER_BLOCK]
|
||||
)
|
||||
```
|
||||
|
||||
@@ -155,29 +183,34 @@ Response Content:
|
||||
|
||||
```
|
||||
(
|
||||
List[SignedBeaconBlockAndBlobsSidecar, MAX_REQUEST_BLOCKS]
|
||||
List[BlobSidecar, MAX_REQUEST_BLOBS_SIDECARS * MAX_BLOBS_PER_BLOCK]
|
||||
)
|
||||
```
|
||||
|
||||
Requests blocks by block root (= `hash_tree_root(SignedBeaconBlockAndBlobsSidecar.beacon_block.message)`).
|
||||
The response is a list of `SignedBeaconBlockAndBlobsSidecar` whose length is less than or equal to the number of requests.
|
||||
It may be less in the case that the responding peer is missing blocks and sidecars.
|
||||
Requests sidecars by block root and index.
|
||||
The response is a list of `BlobSidecar` whose length is less than or equal to the number of requests.
|
||||
It may be less in the case that the responding peer is missing blocks or sidecars.
|
||||
|
||||
No more than `MAX_REQUEST_BLOCKS` may be requested at a time.
|
||||
The response is unsigned, i.e. `BlobSidecar`, as the signature of the beacon block proposer
|
||||
may not be available beyond the initial distribution via gossip.
|
||||
|
||||
`BeaconBlockAndBlobsSidecarByRoot` is primarily used to recover recent blocks and sidecars (e.g. when receiving a block or attestation whose parent is unknown).
|
||||
No more than `MAX_REQUEST_BLOBS_SIDECARS * MAX_BLOBS_PER_BLOCK` may be requested at a time.
|
||||
|
||||
`BlobSidecarsByRoot` is primarily used to recover recent blobs (e.g. when receiving a block with a transaction whose corresponding blob is missing).
|
||||
|
||||
The response MUST consist of zero or more `response_chunk`.
|
||||
Each _successful_ `response_chunk` MUST contain a single `SignedBeaconBlockAndBlobsSidecar` payload.
|
||||
Each _successful_ `response_chunk` MUST contain a single `BlobSidecar` payload.
|
||||
|
||||
Clients MUST support requesting blocks and sidecars since `minimum_request_epoch`, where `minimum_request_epoch = max(finalized_epoch, current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS, DENEB_FORK_EPOCH)`. If any root in the request content references a block earlier than `minimum_request_epoch`, peers SHOULD respond with error code `3: ResourceUnavailable`.
|
||||
Clients MUST support requesting sidecars since `minimum_request_epoch`, where `minimum_request_epoch = max(finalized_epoch, current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS, DENEB_FORK_EPOCH)`. If any root in the request content references a block earlier than `minimum_request_epoch`, peers MAY respond with error code `3: ResourceUnavailable` or not include the blob in the response.
|
||||
|
||||
Clients MUST respond with at least one block and sidecar, if they have it.
|
||||
Clients MUST respond with at least one sidecar, if they have it.
|
||||
Clients MAY limit the number of blocks and sidecars in the response.
|
||||
|
||||
#### BlobsSidecarsByRange v1
|
||||
#### BlobSidecarsByRange v1
|
||||
|
||||
**Protocol ID:** `/eth2/beacon_chain/req/blobs_sidecars_by_range/1/`
|
||||
**Protocol ID:** `/eth2/beacon_chain/req/blob_sidecars_by_range/1/`
|
||||
|
||||
New in deneb.
|
||||
|
||||
Request Content:
|
||||
```
|
||||
@@ -190,72 +223,66 @@ Request Content:
|
||||
Response Content:
|
||||
```
|
||||
(
|
||||
List[BlobsSidecar, MAX_REQUEST_BLOBS_SIDECARS]
|
||||
List[BlobSidecar, MAX_REQUEST_BLOB_SIDECARS * MAX_BLOBS_PER_BLOCK]
|
||||
)
|
||||
```
|
||||
|
||||
Requests blobs sidecars in the slot range `[start_slot, start_slot + count)`,
|
||||
leading up to the current head block as selected by fork choice.
|
||||
Requests blob sidecars in the slot range `[start_slot, start_slot + count)`, leading up to the current head block as selected by fork choice.
|
||||
|
||||
The response is unsigned, i.e. `BlobsSidecarsByRange`, as the signature of the beacon block proposer
|
||||
may not be available beyond the initial distribution via gossip.
|
||||
The response is unsigned, i.e. `BlobSidecarsByRange`, as the signature of the beacon block proposer may not be available beyond the initial distribution via gossip.
|
||||
|
||||
Before consuming the next response chunk, the response reader SHOULD verify the blobs sidecar is well-formatted and
|
||||
correct w.r.t. the expected KZG commitments through `validate_blobs_sidecar`.
|
||||
Before consuming the next response chunk, the response reader SHOULD verify the blob sidecar is well-formatted and correct w.r.t. the expected KZG commitments through `validate_blobs`.
|
||||
|
||||
`BlobsSidecarsByRange` is primarily used to sync blobs that may have been missed on gossip and to sync within the `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` window.
|
||||
`BlobSidecarsByRange` is primarily used to sync blobs that may have been missed on gossip and to sync within the `MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS` window.
|
||||
|
||||
The request MUST be encoded as an SSZ-container.
|
||||
|
||||
The response MUST consist of zero or more `response_chunk`.
|
||||
Each _successful_ `response_chunk` MUST contain a single `BlobsSidecar` payload.
|
||||
Each _successful_ `response_chunk` MUST contain a single `BlobSidecar` payload.
|
||||
|
||||
Clients MUST keep a record of signed blobs sidecars seen on the epoch range
|
||||
`[max(current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS, DENEB_FORK_EPOCH), current_epoch]`
|
||||
`[max(current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS, DENEB_FORK_EPOCH), current_epoch]`
|
||||
where `current_epoch` is defined by the current wall-clock time,
|
||||
and clients MUST support serving requests of blobs on this range.
|
||||
|
||||
Peers that are unable to reply to blobs sidecars requests within the `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS`
|
||||
Peers that are unable to reply to blob sidecar requests within the `MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS`
|
||||
epoch range SHOULD respond with error code `3: ResourceUnavailable`.
|
||||
Such peers that are unable to successfully reply to this range of requests MAY get descored
|
||||
or disconnected at any time.
|
||||
|
||||
*Note*: The above requirement implies that nodes that start from a recent weak subjectivity checkpoint
|
||||
MUST backfill the local blobs database to at least epoch `current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS`
|
||||
to be fully compliant with `BlobsSidecarsByRange` requests.
|
||||
MUST backfill the local blobs database to at least epoch `current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS`
|
||||
to be fully compliant with `BlobSidecarsByRange` requests.
|
||||
|
||||
*Note*: Although clients that bootstrap from a weak subjectivity checkpoint can begin
|
||||
participating in the networking immediately, other peers MAY
|
||||
disconnect and/or temporarily ban such an un-synced or semi-synced client.
|
||||
|
||||
Clients MUST respond with at least the first blobs sidecar that exists in the range, if they have it,
|
||||
and no more than `MAX_REQUEST_BLOBS_SIDECARS` sidecars.
|
||||
Clients MUST respond with at least the blob sidecars of the first blob-carrying block that exists in the range, if they have it, and no more than `MAX_REQUEST_BLOB_SIDECARS * MAX_BLOBS_PER_BLOCK` sidecars.
|
||||
|
||||
The following blobs sidecars, where they exist, MUST be sent in consecutive order.
|
||||
Clients MUST include all blob sidecars of each block from which they include blob sidecars.
|
||||
|
||||
Clients MAY limit the number of blobs sidecars in the response.
|
||||
The following blob sidecars, where they exist, MUST be sent in consecutive `(slot, index)` order.
|
||||
|
||||
An empty `BlobSidecar` is one that does not contain any blobs, but contains non-zero `beacon_block_root`, `beacon_block_slot` and a valid `kzg_aggregated_proof`.
|
||||
Clients MAY NOT want to consider empty `BlobSidecar`s in rate limiting logic.
|
||||
Clients MAY limit the number of blob sidecars in the response.
|
||||
|
||||
The response MUST contain no more than `count` blobs sidecars.
|
||||
The response MUST contain no more than `count * MAX_BLOBS_PER_BLOCK` blob sidecars.
|
||||
|
||||
Clients MUST respond with blobs sidecars from their view of the current fork choice
|
||||
-- that is, blobs sidecars as included by blocks from the single chain defined by the current head.
|
||||
Clients MUST respond with blob sidecars from their view of the current fork choice
|
||||
-- that is, blob sidecars as included by blocks from the single chain defined by the current head.
|
||||
Of note, blocks from slots before the finalization MUST lead to the finalized block reported in the `Status` handshake.
|
||||
|
||||
Clients MUST respond with blobs sidecars that are consistent from a single chain within the context of the request.
|
||||
Clients MUST respond with blob sidecars that are consistent from a single chain within the context of the request.
|
||||
|
||||
After the initial blobs sidecar, clients MAY stop in the process of responding
|
||||
if their fork choice changes the view of the chain in the context of the request.
|
||||
After the initial blob sidecar, clients MAY stop in the process of responding if their fork choice changes the view of the chain in the context of the request.
|
||||
|
||||
# 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?
|
||||
|
||||
This "sidecar" design provides forward compatibility for further data increases by black-boxing `is_data_available()`:
|
||||
with full sharding `is_data_available()` can be replaced by data-availability-sampling (DAS)
|
||||
thus avoiding all blobs being downloaded by all beacon nodes on the network.
|
||||
|
||||
Such sharding design may introduce an updated `BlobsSidecar` to identify the shard,
|
||||
Such sharding design may introduce an updated `BlobSidecar` to identify the shard,
|
||||
but does not affect the `BeaconBlock` structure.
|
||||
|
||||
@@ -411,15 +411,18 @@ def verify_kzg_proof_batch(commitments: Sequence[KZGCommitment],
|
||||
# Verify: e(sum r^i proof_i, [s]) ==
|
||||
# e(sum r^i (commitment_i - [y_i]) + sum r^i z_i proof_i, [1])
|
||||
proof_lincomb = g1_lincomb(proofs, r_powers)
|
||||
proof_z_lincomb = g1_lincomb(proofs, [z * r_power for z, r_power in zip(zs, r_powers)])
|
||||
proof_z_lincomb = g1_lincomb(
|
||||
proofs,
|
||||
[BLSFieldElement((int(z) * int(r_power)) % BLS_MODULUS) for z, r_power in zip(zs, r_powers)],
|
||||
)
|
||||
C_minus_ys = [bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1, BLS_MODULUS - y))
|
||||
for commitment, y in zip(commitments, ys)]
|
||||
C_minus_y_as_KZGCommitments = [KZGCommitment(bls.G1_to_bytes48(x)) for x in C_minus_ys]
|
||||
C_minus_y_lincomb = g1_lincomb(C_minus_y_as_KZGCommitments, r_powers)
|
||||
|
||||
return bls.pairing_check([
|
||||
[proof_lincomb, bls.neg(KZG_SETUP_G2[1])],
|
||||
[bls.add(C_minus_y_lincomb, proof_z_lincomb), bls.G2]
|
||||
[bls.bytes48_to_G1(proof_lincomb), bls.neg(bls.bytes96_to_G2(KZG_SETUP_G2[1]))],
|
||||
[bls.add(bls.bytes48_to_G1(C_minus_y_lincomb), bls.bytes48_to_G1(proof_z_lincomb)), bls.G2]
|
||||
])
|
||||
```
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
- [Block and sidecar proposal](#block-and-sidecar-proposal)
|
||||
- [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody)
|
||||
- [Blob KZG commitments](#blob-kzg-commitments)
|
||||
- [Constructing the `SignedBeaconBlockAndBlobsSidecar`](#constructing-the-signedbeaconblockandblobssidecar)
|
||||
- [Block](#block)
|
||||
- [Constructing the `SignedBlobSidecar`s](#constructing-the-signedblobsidecars)
|
||||
- [Sidecar](#sidecar)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
@@ -53,7 +52,6 @@ def get_blobs_and_kzg_commitments(payload_id: PayloadId) -> Tuple[Sequence[BLSFi
|
||||
## Beacon chain responsibilities
|
||||
|
||||
All validator responsibilities remain unchanged other than those noted below.
|
||||
Namely, the blob handling and the addition of `SignedBeaconBlockAndBlobsSidecar`.
|
||||
|
||||
### Block and sidecar proposal
|
||||
|
||||
@@ -79,30 +77,47 @@ def validate_blobs_and_kzg_commitments(execution_payload: ExecutionPayload,
|
||||
|
||||
3. If valid, set `block.body.blob_kzg_commitments = blob_kzg_commitments`.
|
||||
|
||||
#### Constructing the `SignedBeaconBlockAndBlobsSidecar`
|
||||
To construct a `SignedBeaconBlockAndBlobsSidecar`, a `signed_beacon_block_and_blobs_sidecar` is defined with the necessary context for block and sidecar proposal.
|
||||
#### Constructing the `SignedBlobSidecar`s
|
||||
|
||||
##### Block
|
||||
Set `signed_beacon_block_and_blobs_sidecar.beacon_block = block` where `block` is obtained above.
|
||||
To construct a `SignedBlobSidecar`, a `signed_blob_sidecar` is defined with the necessary context for block and sidecar proposal.
|
||||
|
||||
##### Sidecar
|
||||
Coupled with block, the corresponding blobs are packaged into a sidecar object for distribution to the network.
|
||||
|
||||
Set `signed_beacon_block_and_blobs_sidecar.blobs_sidecar = sidecar` where `sidecar` is obtained from:
|
||||
Blobs associated with a block are packaged into sidecar objects for distribution to the network.
|
||||
|
||||
Each `sidecar` is obtained from:
|
||||
```python
|
||||
def get_blobs_sidecar(block: BeaconBlock, blobs: Sequence[Blob]) -> BlobsSidecar:
|
||||
return BlobsSidecar(
|
||||
beacon_block_root=hash_tree_root(block),
|
||||
beacon_block_slot=block.slot,
|
||||
blobs=blobs,
|
||||
# Disabled because not available before switch to single blob sidecars
|
||||
kzg_aggregated_proof=KZGProof(), # compute_aggregate_kzg_proof(blobs),
|
||||
)
|
||||
def get_blob_sidecars(block: BeaconBlock, blobs: Sequence[Blob]) -> Sequence[BlobSidecar]:
|
||||
return [
|
||||
BlobSidecar(
|
||||
block_root=hash_tree_root(block),
|
||||
index=index,
|
||||
slot=block.slot,
|
||||
block_parent_root=block.parent_root,
|
||||
blob=blob,
|
||||
kzg_commitment=block.body.blob_kzg_commitments[index],
|
||||
kzg_proof=compute_blob_kzg_proof(blob),
|
||||
)
|
||||
for index, blob in enumerate(blobs)
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
This `signed_beacon_block_and_blobs_sidecar` is then published to the global `beacon_block_and_blobs_sidecar` topic.
|
||||
Then for each sidecar, `signed_sidecar = SignedBlobSidecar(message=sidecar, signature=signature)` is constructed and published to the `blob_sidecar_{index}` topics according to its index.
|
||||
|
||||
`signature` is obtained from:
|
||||
|
||||
```python
|
||||
def get_blob_sidecar_signature(state: BeaconState,
|
||||
sidecar: BlobSidecar,
|
||||
privkey: int) -> BLSSignature:
|
||||
domain = get_domain(state, DOMAIN_BLOB_SIDECAR, compute_epoch_at_slot(sidecar.slot))
|
||||
signing_root = compute_signing_root(sidecar, domain)
|
||||
return bls.Sign(privkey, signing_root)
|
||||
```
|
||||
|
||||
After publishing the peers on the network may request the sidecar through sync-requests, or a local user may be interested.
|
||||
|
||||
The validator MUST hold on to sidecars for `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS` epochs and serve when capable,
|
||||
to ensure the data-availability of these blobs throughout the network.
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ from eth2spec.test.helpers.sharding import (
|
||||
)
|
||||
|
||||
|
||||
def _run_validate_blobs_sidecar_test(spec, state, blob_count):
|
||||
def _run_validate_blobs(spec, state, blob_count):
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
opaque_tx, blobs, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=blob_count)
|
||||
block.body.blob_kzg_commitments = blob_kzg_commitments
|
||||
@@ -24,30 +24,32 @@ def _run_validate_blobs_sidecar_test(spec, state, blob_count):
|
||||
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload)
|
||||
state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
blobs_sidecar = spec.get_blobs_sidecar(block, blobs)
|
||||
expected_commitments = [spec.blob_to_kzg_commitment(blobs[i]) for i in range(blob_count)]
|
||||
spec.validate_blobs_sidecar(block.slot, block.hash_tree_root(), expected_commitments, blobs_sidecar)
|
||||
# Also test the proof generation in `get_blob_sidecars`
|
||||
blob_sidecars = spec.get_blob_sidecars(block, blobs)
|
||||
blobs = [sidecar.blob for sidecar in blob_sidecars]
|
||||
kzg_proofs = [sidecar.kzg_proof for sidecar in blob_sidecars]
|
||||
spec.validate_blobs(blob_kzg_commitments, blobs, kzg_proofs)
|
||||
|
||||
|
||||
@with_deneb_and_later
|
||||
@spec_state_test
|
||||
def test_validate_blobs_sidecar_zero_blobs(spec, state):
|
||||
_run_validate_blobs_sidecar_test(spec, state, blob_count=0)
|
||||
def test_validate_blobs_zero_blobs(spec, state):
|
||||
_run_validate_blobs(spec, state, blob_count=0)
|
||||
|
||||
|
||||
@with_deneb_and_later
|
||||
@spec_state_test
|
||||
def test_validate_blobs_sidecar_one_blob(spec, state):
|
||||
_run_validate_blobs_sidecar_test(spec, state, blob_count=1)
|
||||
def test_validate_blobs_one_blob(spec, state):
|
||||
_run_validate_blobs(spec, state, blob_count=1)
|
||||
|
||||
|
||||
@with_deneb_and_later
|
||||
@spec_state_test
|
||||
def test_validate_blobs_sidecar_two_blobs(spec, state):
|
||||
_run_validate_blobs_sidecar_test(spec, state, blob_count=2)
|
||||
def test_validate_blobs_two_blobs(spec, state):
|
||||
_run_validate_blobs(spec, state, blob_count=2)
|
||||
|
||||
|
||||
@with_deneb_and_later
|
||||
@spec_state_test
|
||||
def test_validate_blobs_sidecar_max_blobs(spec, state):
|
||||
_run_validate_blobs_sidecar_test(spec, state, blob_count=spec.MAX_BLOBS_PER_BLOCK)
|
||||
def test_validate_blobs_max_blobs(spec, state):
|
||||
_run_validate_blobs(spec, state, blob_count=spec.MAX_BLOBS_PER_BLOCK)
|
||||
Reference in New Issue
Block a user