|
38 | 38 | - [`verify_kzg_proof`](#verify_kzg_proof) |
39 | 39 | - [`verify_kzg_proof_impl`](#verify_kzg_proof_impl) |
40 | 40 | - [`compute_kzg_proof`](#compute_kzg_proof) |
| 41 | + - [`compute_quotient_eval_within_domain`](#compute_quotient_eval_within_domain) |
41 | 42 | - [`compute_kzg_proof_impl`](#compute_kzg_proof_impl) |
42 | 43 | - [`compute_aggregated_poly_and_commitment`](#compute_aggregated_poly_and_commitment) |
43 | 44 | - [`compute_aggregate_kzg_proof`](#compute_aggregate_kzg_proof) |
@@ -427,23 +428,61 @@ def compute_kzg_proof(blob: Blob, z: Bytes32) -> KZGProof: |
427 | 428 | return compute_kzg_proof_impl(polynomial, bytes_to_bls_field(z)) |
428 | 429 | ``` |
429 | 430 |
|
| 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 | + |
430 | 459 | #### `compute_kzg_proof_impl` |
431 | 460 |
|
432 | 461 | ```python |
433 | 462 | def compute_kzg_proof_impl(polynomial: Polynomial, z: BLSFieldElement) -> KZGProof: |
434 | 463 | """ |
435 | 464 | Helper function for compute_kzg_proof() and compute_aggregate_kzg_proof(). |
436 | 465 | """ |
| 466 | + roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY) |
| 467 | + |
| 468 | + # For all x_i, compute p(x_i) - p(z) |
437 | 469 | y = evaluate_polynomial_in_evaluation_form(polynomial, z) |
438 | 470 | polynomial_shifted = [BLSFieldElement((int(p) - int(y)) % BLS_MODULUS) for p in polynomial] |
439 | 471 |
|
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) |
442 | 473 | denominator_poly = [BLSFieldElement((int(x) - int(z)) % BLS_MODULUS) |
443 | 474 | for x in bit_reversal_permutation(ROOTS_OF_UNITY)] |
444 | 475 |
|
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 | + |
447 | 486 | return KZGProof(g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), quotient_polynomial)) |
448 | 487 | ``` |
449 | 488 |
|
|
0 commit comments