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

Allow empty blobs and commitments #3093

Merged
merged 16 commits into from
Nov 23, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
54 changes: 36 additions & 18 deletions specs/eip4844/polynomial-commitments.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
- [BLS12-381 helpers](#bls12-381-helpers)
- [`bytes_to_bls_field`](#bytes_to_bls_field)
- [`blob_to_polynomial`](#blob_to_polynomial)
- [`hash_to_bls_field`](#hash_to_bls_field)
- [`compute_challenges`](#compute_challenges)
- [`bls_modular_inverse`](#bls_modular_inverse)
- [`div`](#div)
- [`g1_lincomb`](#g1_lincomb)
Expand All @@ -41,7 +41,6 @@
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->


## Introduction

This document specifies basic polynomial operations and KZG polynomial commitment operations as they are needed for the EIP-4844 specification. The implementations are not optimized for performance, but readability. All practical implementations should optimize the polynomial operations.
Expand Down Expand Up @@ -163,31 +162,44 @@ def blob_to_polynomial(blob: Blob) -> Polynomial:
return polynomial
```

#### `hash_to_bls_field`
#### `compute_challenges`

```python
def hash_to_bls_field(polys: Sequence[Polynomial],
comms: Sequence[KZGCommitment]) -> BLSFieldElement:
def compute_challenges(polynomials: Sequence[Polynomial],
commitments: Sequence[KZGCommitment]) -> BLSFieldElement:
"""
Compute 32-byte hash of serialized polynomials and commitments concatenated.
This hash is then converted to a BLS field element, where the result is not uniform over the BLS field.
Return the BLS field element.
Return the Fiat-Shamir challenges required by the rest of the protocol.
The Fiat-Shamir logic works as per the following pseudocode:

hashed_data = hash(DOMAIN_SEPARATOR, polynomials, commitments)
r = hash(hashed_data, 0)
r_powers = [r, r**2, r**3, ...]
eval_challenge = hash(hashed_data, 1)

Then return `r_powers` and `eval_challenge` after converting them to BLS field elements.
The resulting field elements are not uniform over the BLS field.
"""
# Append the number of polynomials and the degree of each polynomial as a domain separator
num_polys = int.to_bytes(len(polys), 8, ENDIANNESS)
num_polynomials = int.to_bytes(len(polynomials), 8, ENDIANNESS)
degree_poly = int.to_bytes(FIELD_ELEMENTS_PER_BLOB, 8, ENDIANNESS)
data = FIAT_SHAMIR_PROTOCOL_DOMAIN + degree_poly + num_polys
data = FIAT_SHAMIR_PROTOCOL_DOMAIN + degree_poly + num_polynomials

# Append each polynomial which is composed by field elements
for poly in polys:
for poly in polynomials:
for field_element in poly:
data += int.to_bytes(field_element, BYTES_PER_FIELD_ELEMENT, ENDIANNESS)

# Append serialized G1 points
for commitment in comms:
for commitment in commitments:
data += commitment

return bytes_to_bls_field(hash(data))
# Transcript has been prepared: time to create the challenges
hashed_data = hash(data)
r = hash(hashed_data + b'\x00')
r_powers = compute_powers(bytes_to_bls_field(r), len(commitments))
eval_challenge = hash(hashed_data + b'\x01')

return r_powers, bytes_to_bls_field(eval_challenge)
```

#### `bls_modular_inverse`
Expand Down Expand Up @@ -234,7 +246,8 @@ def poly_lincomb(polys: Sequence[Polynomial],
Given a list of ``polynomials``, interpret it as a 2D matrix and compute the linear combination
of each column with `scalars`: return the resulting polynomials.
"""
result = [0] * len(polys[0])
assert len(polys) == len(scalars)
result = [0] * FIELD_ELEMENTS_PER_BLOB
for v, s in zip(polys, scalars):
for i, x in enumerate(v):
result[i] = (result[i] + int(s) * int(x)) % BLS_MODULUS
Expand All @@ -256,6 +269,7 @@ def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]:
return powers
```


### Polynomials

#### `evaluate_polynomial_in_evaluation_form`
Expand Down Expand Up @@ -367,14 +381,15 @@ def compute_aggregated_poly_and_commitment(
"""
Return (1) the aggregated polynomial, (2) the aggregated KZG commitment,
and (3) the polynomial evaluation random challenge.
This function should also work with blobs == [] and kzg_commitments == []
"""
assert len(blobs) == len(kzg_commitments)

# Convert blobs to polynomials
polynomials = [blob_to_polynomial(blob) for blob in blobs]

# Generate random linear combination challenges
r = hash_to_bls_field(polynomials, kzg_commitments)
r_powers = compute_powers(r, len(kzg_commitments))
evaluation_challenge = int(r_powers[-1]) * r % BLS_MODULUS
# Generate random linear combination and evaluation challenges
r_powers, evaluation_challenge = compute_challenges(polynomials, kzg_commitments)

# Create aggregated polynomial in evaluation form
aggregated_poly = Polynomial(poly_lincomb(polynomials, r_powers))
Expand All @@ -390,6 +405,7 @@ def compute_aggregated_poly_and_commitment(
```python
def compute_aggregate_kzg_proof(blobs: Sequence[Blob]) -> KZGProof:
"""
Given a list of blobs, return the aggregated KZG proof that is used to verify them against their commitments.
Public method.
"""
commitments = [blob_to_kzg_commitment(blob) for blob in blobs]
Expand All @@ -407,6 +423,8 @@ def verify_aggregate_kzg_proof(blobs: Sequence[Blob],
expected_kzg_commitments: Sequence[KZGCommitment],
kzg_aggregated_proof: KZGProof) -> bool:
"""
Given a list of blobs and an aggregated KZG proof, verify that they correspond to the provided commitments.

Public method.
"""
aggregated_poly, aggregated_poly_commitment, evaluation_challenge = compute_aggregated_poly_and_commitment(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ def _run_validate_blobs_sidecar_test(spec, state, blob_count):
spec.validate_blobs_sidecar(block.slot, block.hash_tree_root(), expected_commitments, blobs_sidecar)


@with_eip4844_and_later
@spec_state_test
def test_validate_blobs_sidecar_zero_blobs(spec, state):
_run_validate_blobs_sidecar_test(spec, state, blob_count=0)


@with_eip4844_and_later
@spec_state_test
def test_validate_blobs_sidecar_one_blob(spec, state):
Expand Down