mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-03-01 18:50:35 +00:00
EIP4844: Refactor Fiat-Shamir logic to handle empty sidecars (#3093)
Additionally, it makes the Fiat-Shamir hashing logic more robust by making the challenges independent of each other. It also makes it more efficient to implement by moving both challenge computations to a single function needing a single transcript hash. Co-authored-by: George Kadianakis <desnacked@riseup.net> Co-authored-by: Dankrad Feist <mail@dankradfeist.de>
This commit is contained in:
parent
2d99f1b551
commit
f0ff15c14d
@ -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)
|
||||
@ -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.
|
||||
@ -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`
|
||||
@ -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
|
||||
@ -256,6 +269,7 @@ def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]:
|
||||
return powers
|
||||
```
|
||||
|
||||
|
||||
### Polynomials
|
||||
|
||||
#### `evaluate_polynomial_in_evaluation_form`
|
||||
@ -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))
|
||||
@ -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]
|
||||
@ -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(
|
||||
|
@ -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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user