Skip to content

Commit fc4e1a9

Browse files
authored
EIP4844: compute_kzg_proof() can now create proofs within the domain (#3243)
This will be used by optimistic rollups to create proofs about past data
1 parent 5970ae5 commit fc4e1a9

File tree

2 files changed

+63
-4
lines changed

2 files changed

+63
-4
lines changed

specs/deneb/polynomial-commitments.md

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
- [`verify_kzg_proof`](#verify_kzg_proof)
3939
- [`verify_kzg_proof_impl`](#verify_kzg_proof_impl)
4040
- [`compute_kzg_proof`](#compute_kzg_proof)
41+
- [`compute_quotient_eval_within_domain`](#compute_quotient_eval_within_domain)
4142
- [`compute_kzg_proof_impl`](#compute_kzg_proof_impl)
4243
- [`compute_aggregated_poly_and_commitment`](#compute_aggregated_poly_and_commitment)
4344
- [`compute_aggregate_kzg_proof`](#compute_aggregate_kzg_proof)
@@ -427,23 +428,61 @@ def compute_kzg_proof(blob: Blob, z: Bytes32) -> KZGProof:
427428
return compute_kzg_proof_impl(polynomial, bytes_to_bls_field(z))
428429
```
429430

431+
#### `compute_quotient_eval_within_domain`
432+
433+
```python
434+
def compute_quotient_eval_within_domain(z: BLSFieldElement,
435+
polynomial: Polynomial,
436+
y: BLSFieldElement
437+
) -> BLSFieldElement:
438+
"""
439+
Given `y == p(z)` for a polynomial `p(x)`, compute `q(z)`: the KZG quotient polynomial evaluated at `z` for the
440+
special case where `z` is in `ROOTS_OF_UNITY`.
441+
442+
For more details, read https://dankradfeist.de/ethereum/2021/06/18/pcs-multiproofs.html section "Dividing
443+
when one of the points is zero". The code below computes q(x_m) for the roots of unity special case.
444+
"""
445+
roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY)
446+
result = 0
447+
for i, omega_i in enumerate(roots_of_unity_brp):
448+
if omega_i == z: # skip the evaluation point in the sum
449+
continue
450+
451+
f_i = int(BLS_MODULUS) + int(polynomial[i]) - int(y) % BLS_MODULUS
452+
numerator = f_i * int(omega_i) % BLS_MODULUS
453+
denominator = int(z) * (int(BLS_MODULUS) + int(z) - int(omega_i)) % BLS_MODULUS
454+
result += div(BLSFieldElement(numerator), BLSFieldElement(denominator))
455+
456+
return BLSFieldElement(result % BLS_MODULUS)
457+
```
458+
430459
#### `compute_kzg_proof_impl`
431460

432461
```python
433462
def compute_kzg_proof_impl(polynomial: Polynomial, z: BLSFieldElement) -> KZGProof:
434463
"""
435464
Helper function for compute_kzg_proof() and compute_aggregate_kzg_proof().
436465
"""
466+
roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY)
467+
468+
# For all x_i, compute p(x_i) - p(z)
437469
y = evaluate_polynomial_in_evaluation_form(polynomial, z)
438470
polynomial_shifted = [BLSFieldElement((int(p) - int(y)) % BLS_MODULUS) for p in polynomial]
439471

440-
# Make sure we won't divide by zero during division
441-
assert z not in ROOTS_OF_UNITY
472+
# For all x_i, compute (x_i - z)
442473
denominator_poly = [BLSFieldElement((int(x) - int(z)) % BLS_MODULUS)
443474
for x in bit_reversal_permutation(ROOTS_OF_UNITY)]
444475

445-
# Calculate quotient polynomial by doing point-by-point division
446-
quotient_polynomial = [div(a, b) for a, b in zip(polynomial_shifted, denominator_poly)]
476+
# Compute the quotient polynomial directly in evaluation form
477+
quotient_polynomial = [BLSFieldElement(0)] * FIELD_ELEMENTS_PER_BLOB
478+
for i, (a, b) in enumerate(zip(polynomial_shifted, denominator_poly)):
479+
if b == 0:
480+
# The denominator is zero hence `z` is a root of unity: we must handle it as a special case
481+
quotient_polynomial[i] = compute_quotient_eval_within_domain(roots_of_unity_brp[i], polynomial, y)
482+
else:
483+
# Compute: q(x_i) = (p(x_i) - p(z)) / (x_i - z).
484+
quotient_polynomial[i] = div(a, b)
485+
447486
return KZGProof(g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), quotient_polynomial))
448487
```
449488

tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,23 @@ def test_barycentric_within_domain(spec, state):
8787
# The two evaluations should be agree and p(z) should also be the i-th "coefficient" of the polynomial in
8888
# evaluation form
8989
assert p_z_coeff == p_z_eval == poly_eval[i]
90+
91+
92+
@with_deneb_and_later
93+
@spec_state_test
94+
def test_compute_kzg_proof_within_domain(spec, state):
95+
"""
96+
Create and verify KZG proof that p(z) == y
97+
where z is in the domain of our KZG scheme (i.e. a relevant root of unity).
98+
"""
99+
blob = get_sample_blob(spec)
100+
commitment = spec.blob_to_kzg_commitment(blob)
101+
polynomial = spec.blob_to_polynomial(blob)
102+
103+
roots_of_unity_brp = spec.bit_reversal_permutation(spec.ROOTS_OF_UNITY)
104+
105+
for i, z in enumerate(roots_of_unity_brp):
106+
proof = spec.compute_kzg_proof_impl(polynomial, z)
107+
108+
y = spec.evaluate_polynomial_in_evaluation_form(polynomial, z)
109+
assert spec.verify_kzg_proof_impl(commitment, z, y, proof)

0 commit comments

Comments
 (0)