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 blob sidecar inclusion proof #3531

Merged
merged 31 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
530efa8
Add blob sidecar inclusion proof
dapplion Oct 27, 2023
e8bccec
remove verify_blob_sidecar_signature
dapplion Oct 27, 2023
29bbdf4
compute KZG_COMMITMENT_INCLUSION_PROOF_DEPTH
dapplion Oct 27, 2023
a4a29a1
List typo
dapplion Oct 27, 2023
3dbe54e
doctoc
dapplion Oct 27, 2023
c2a64a1
pass lint
dapplion Oct 27, 2023
caa79a5
build tree
dapplion Oct 27, 2023
8712451
Update specs/deneb/p2p-interface.md
dapplion Oct 27, 2023
f2649f6
fix unit tests
dapplion Oct 27, 2023
83e5930
doctoc
dapplion Oct 27, 2023
0bf9e75
review PR
dapplion Oct 27, 2023
26516ec
Move `KZG_COMMITMENT_INCLUSION_PROOF_DEPTH` to preset and cast `int()`
hwwhww Oct 27, 2023
1657d16
Add `BLOB_KZG_COMMITMENTS_GINDEX` to "Constant". Use pyspec parser tr…
hwwhww Oct 28, 2023
de3b6a2
Fix toc
hwwhww Oct 28, 2023
b7e0b88
Fix test
hwwhww Oct 28, 2023
b018fbc
Remove `BLOB_KZG_COMMITMENTS_GINDEX` from the preset files
hwwhww Oct 28, 2023
ae6a9eb
Fix lint
hwwhww Oct 28, 2023
0e4737e
Add a general `compute_merkle_proof` helper to replace container-spec…
hwwhww Oct 30, 2023
c680212
drop is_valid_merkle_path
dapplion Oct 30, 2023
126e807
Update specs/deneb/p2p-interface.md
dapplion Oct 30, 2023
b803f1c
Update specs/deneb/p2p-interface.md
dapplion Oct 30, 2023
d323f05
drop sidecar alias
dapplion Oct 30, 2023
a124414
Enhance `blob_sidecar_inclusion_proof` tests
hwwhww Oct 30, 2023
51343f5
Fix typing and delete the `signed_sidecar`
hwwhww Oct 30, 2023
1bac25a
Add Merkle proof test
hwwhww Oct 30, 2023
19883ec
Add verify_blob_kzg_proof condition
dapplion Oct 31, 2023
7f63f00
Merge branch 'dev' into blob-p2p-proof
dapplion Oct 31, 2023
4a609ce
rename to kzg_commitment_inclusion_proof
dapplion Nov 1, 2023
71106f1
Remove `BLOB_KZG_COMMITMENTS_GINDEX`
hwwhww Nov 2, 2023
3492c0a
minor refactoring
hwwhww Nov 2, 2023
7118c30
a few cleanups to sidecar gossip conditions
djrtwo Nov 2, 2023
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
79 changes: 50 additions & 29 deletions specs/deneb/p2p-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ The specification of these changes continues in the same format as the network s
- [Configuration](#configuration)
- [Containers](#containers)
- [`BlobSidecar`](#blobsidecar)
- [`SignedBlobSidecar`](#signedblobsidecar)
- [`BlobIdentifier`](#blobidentifier)
- [Helpers](#helpers)
- [`verify_blob_sidecar_signature`](#verify_blob_sidecar_signature)
- [`verify_blob_sidecar_inclusion_proof`](#verify_blob_sidecar_inclusion_proof)
- [`is_valid_merkle_path`](#is_valid_merkle_path)
- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub)
- [Topics and messages](#topics-and-messages)
- [Global topics](#global-topics)
Expand Down Expand Up @@ -51,6 +52,8 @@ The specification of these changes continues in the same format as the network s
| `MAX_REQUEST_BLOB_SIDECARS` | `MAX_REQUEST_BLOCKS_DENEB * MAX_BLOBS_PER_BLOCK` | 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 |
| `BLOB_SIDECAR_SUBNET_COUNT` | `6` | The number of blob sidecar subnets used in the gossipsub protocol. |
| `KZG_COMMITMENT_INCLUSION_PROOF_DEPTH` | `17` | Merkle proof for `blob_kzg_commitments` list item |
mkalinin marked this conversation as resolved.
Show resolved Hide resolved
| `BLOB_KZG_COMMITMENT_GINDEX` | `27` | Gindex path to `blob_kzg_commitments` on `BeaconBlockBody` container

### Containers

Expand All @@ -60,24 +63,12 @@ The specification of these changes continues in the same format as the network s

```python
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`

*[New in Deneb:EIP4844]*

```python
class SignedBlobSidecar(Container):
message: BlobSidecar
signature: BLSSignature
commitment_inclusion_proof: [Bytes32, KZG_COMMITMENT_INCLUSION_PROOF_DEPTH]
dapplion marked this conversation as resolved.
Show resolved Hide resolved
signed_block_header: SignedBeaconBlockHeader
```

#### `BlobIdentifier`
Expand All @@ -95,10 +86,39 @@ class BlobIdentifier(Container):
##### `verify_blob_sidecar_signature`

```python
def verify_blob_sidecar_signature(state: BeaconState, signed_blob_sidecar: SignedBlobSidecar) -> bool:
proposer = state.validators[signed_blob_sidecar.message.proposer_index]
signing_root = compute_signing_root(signed_blob_sidecar.message, get_domain(state, DOMAIN_BLOB_SIDECAR))
return bls.Verify(proposer.pubkey, signing_root, signed_blob_sidecar.signature)
def verify_blob_sidecar_signature(state: BeaconState, blob_sidecar: BlobSidecar) -> bool:
block_header = blob_sidecar.signed_block_header.message
proposer = state.validators[block_header.proposer_index]
domain = get_domain(state, DOMAIN_BEACON_PROPOSER, compute_epoch_at_slot(block_header.slot))
signing_root = compute_signing_root(block_header, domain)
return bls.Verify(proposer.pubkey, signing_root, blob_sidecar.signed_block_header.signature)
```

##### `verify_blob_sidecar_inclusion_proof`

```python
def verify_blob_sidecar_inclusion_proof(blob_sidecar: BlobSidecar) -> bool:
commitment_item_gindex = MAX_BLOB_COMMITMENTS_PER_BLOCK + blob_sidecar.index
gindex = BLOB_KZG_COMMITMENT_GINDEX + commitment_item_gindex << floorlog2(BLOB_ZKG_COMMITMENT_GINDEX)
return is_valid_merkle_path(
leaf=blob_sidecar.kzg_commitment.hash_tree_root(),
branch=blob_sidecar.commitment_inclusion_proof,
gindex=gindex,
root=blob_sidecar.signed_block_header.message.body_root,
)
```

#### `is_valid_merkle_path`

```python
def is_valid_merkle_path(leaf: Bytes32, branch: Sequence[Bytes32], gindex: int, root: Root) -> bool:
mkalinin marked this conversation as resolved.
Show resolved Hide resolved
value = leaf
for i in range(len(branch)):
if (gindex >> i) & 1 == 0:
value = hash(branch[i] + value)
else:
value = hash(value + branch[i])
return value == root
```

### The gossip domain: gossipsub
Expand All @@ -123,7 +143,7 @@ The new topics along with the type of the `data` field of a gossipsub message ar

| Name | Message Type |
| - | - |
| `blob_sidecar_{subnet_id}` | `SignedBlobSidecar` [New in Deneb:EIP4844] |
| `blob_sidecar_{subnet_id}` | `BlobSidecar` [New in Deneb:EIP4844] |

##### Global topics

Expand All @@ -146,18 +166,19 @@ New validation:

This topic is used to propagate signed blob sidecars, where each blob index maps to some `subnet_id`.

The following validations MUST pass before forwarding the `signed_blob_sidecar` on the network, assuming the alias `sidecar = signed_blob_sidecar.message`:
The following validations MUST pass before forwarding the `blob_sidecar` on the network, assuming the alias `sidecar = blob_sidecar` and `block_header = blob_sidecar.signed_block_header.message`:
dapplion marked this conversation as resolved.
Show resolved Hide resolved

- _[REJECT]_ The sidecar's index is consistent with `MAX_BLOBS_PER_BLOCK` -- i.e. `sidecar.index < MAX_BLOBS_PER_BLOCK`.
- _[REJECT]_ The sidecar is for the correct subnet -- i.e. `compute_subnet_for_blob_sidecar(sidecar.index) == subnet_id`.
- _[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 sidecars 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 sidecar's block's parent (defined by `sidecar.block_parent_root`) has been seen (via both gossip and non-gossip sources) (a client MAY queue sidecars for processing once the parent block is retrieved).
- _[REJECT]_ The sidecar's block's parent (defined by `sidecar.block_parent_root`) passes validation.
- _[REJECT]_ The sidecar is from a higher slot than the sidecar's block's parent (defined by `sidecar.block_parent_root`).
- _[REJECT]_ The proposer signature, `signed_blob_sidecar.signature`, is valid as verified by `verify_blob_sidecar_signature`.
- _[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`).
- _[IGNORE]_ The sidecar is not from a future slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. validate that `block_header.slot <= current_slot` (a client MAY queue future sidecars for processing at the appropriate slot).
- _[IGNORE]_ The sidecar is from a slot greater than the latest finalized slot -- i.e. validate that `block_header.slot > compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)`
- _[IGNORE]_ The sidecar's block's parent (defined by `block_header.parent_root`) has been seen (via both gossip and non-gossip sources) (a client MAY queue sidecars for processing once the parent block is retrieved).
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
- _[REJECT]_ The sidecar's block's parent (defined by `block_header.parent_root`) passes validation.
- _[REJECT]_ The sidecar is from a higher slot than the sidecar's block's parent (defined by `block_header.parent_root`).
dapplion marked this conversation as resolved.
Show resolved Hide resolved
- _[REJECT]_ The proposer signature in `blob_sidecar.signed_block_header`, is valid as verified by `verify_blob_sidecar_signature`.
dapplion marked this conversation as resolved.
Show resolved Hide resolved
- _[REJECT]_ The sidecar's inclusion proof is valid as verified by `verify_blob_sidecar_inclusion_proof`.
- _[IGNORE]_ The sidecar is the only sidecar with valid signature received for the tuple `(hash_tree_root(block_header), sidecar.index)`.
dapplion marked this conversation as resolved.
Show resolved Hide resolved
- _[REJECT]_ The sidecar is proposed by the expected `proposer_index` for the block's slot in the context of the current shuffling (defined by `parent_root`/`slot`).
dapplion marked this conversation as resolved.
Show resolved Hide resolved
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.

###### `beacon_aggregate_and_proof`
Expand Down
44 changes: 21 additions & 23 deletions specs/deneb/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
- [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody)
- [ExecutionPayload](#executionpayload)
- [Blob KZG commitments](#blob-kzg-commitments)
- [Constructing the `SignedBlobSidecar`s](#constructing-the-signedblobsidecars)
- [Sidecar](#sidecar)
- [Constructing the `BlobSidecar`s](#constructing-the-blobsidecars)
- [BlobSidecar](#blob-sidecar)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
Expand Down Expand Up @@ -133,49 +133,47 @@ use the `payload_id` to retrieve `blobs`, `blob_kzg_commitments`, and `blob_kzg_
via `get_payload(payload_id).blobs_bundle`.
2. Set `block.body.blob_kzg_commitments = blob_kzg_commitments`.

#### Constructing the `SignedBlobSidecar`s
#### Constructing the `BlobSidecar`s

*[New in Deneb:EIP4844]*

To construct a `SignedBlobSidecar`, a `signed_blob_sidecar` is defined with the necessary context for block and sidecar proposal.
To construct a `BlobSidecar`, a `blob_sidecar` is defined with the necessary context for block and sidecar proposal.

##### Sidecar

Blobs associated with a block are packaged into sidecar objects for distribution to the network.
Blobs associated with a block are packaged into sidecar objects for distribution to the associated sidecar topic, the `blob_sidecar_{subnet_id}` pubsub topic.

Each `sidecar` is obtained from:
```python
def get_blob_sidecars(block: BeaconBlock,
def get_blob_sidecars(signed_block: SignedBeaconBlock,
blobs: Sequence[Blob],
blob_kzg_proofs: Sequence[KZGProof]) -> Sequence[BlobSidecar]:
block_header = BeaconBlockHeader(
slot=block.slot,
proposer_index=block.proposer_index,
parent_root=block.parent_root,
state_root=Bytes32(), # Overwritten in the next process_slot call
body_root=hash_tree_root(block.body),
)
signed_block_header = SignedBeaconBlockHeader(message=block_header, signature=signed_block.signature)
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_commitment=signed_block.message.body.blob_kzg_commitments[index],
kzg_proof=blob_kzg_proofs[index],
commitment_inclusion_proof=compute_commitment_inclusion_proof(
dapplion marked this conversation as resolved.
Show resolved Hide resolved
dapplion marked this conversation as resolved.
Show resolved Hide resolved
signed_block.message.body,
signed_block.message.body.blob_kzg_commitments[index],
index,
),
signed_block_header=signed_block_header,
)
for index, blob in enumerate(blobs)
]

```

Then for each sidecar, `signed_sidecar = SignedBlobSidecar(message=sidecar, signature=signature)` is constructed and published to the associated sidecar topic, the `blob_sidecar_{subnet_id}` pubsub topic.

`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)
```

The `subnet_id` for the `signed_sidecar` is calculated with:
- Let `blob_index = signed_sidecar.message.index`.
- Let `subnet_id = compute_subnet_for_blob_sidecar(blob_index)`.
Expand Down
Loading