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

Optimize verify_kzg_proof_multi_impl #3584

Closed
wants to merge 16 commits into from
Closed
64 changes: 49 additions & 15 deletions specs/_features/eip7594/polynomial-commitments-sampling.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
- [`verify_kzg_proof_multi_impl`](#verify_kzg_proof_multi_impl)
- [Cell cosets](#cell-cosets)
- [`coset_for_cell`](#coset_for_cell)
- [Bitwise manipulation](#bitwise-manipulation)
- [`reverse_bits_limited`](#reverse_bits_limited)
- [Cells](#cells-1)
- [Cell computation](#cell-computation)
- [`compute_cells_and_proofs`](#compute_cells_and_proofs)
Expand Down Expand Up @@ -327,20 +329,30 @@ def compute_kzg_proof_multi_impl(

```python
def verify_kzg_proof_multi_impl(commitment: KZGCommitment,
zs: Sequence[BLSFieldElement],
x: BLSFieldElement,
ys: Sequence[BLSFieldElement],
proof: KZGProof) -> bool:
"""
Helper function that verifies a KZG multiproof
"""
assert len(ys) == FIELD_ELEMENTS_PER_CELL
roots_of_unity = compute_roots_of_unity(FIELD_ELEMENTS_PER_CELL)
interp = fft_field(ys, roots_of_unity, inv=True)

assert len(zs) == len(ys)
inv_x = bls_modular_inverse(x)
inv_x_pow = inv_x
for i in range(1, FIELD_ELEMENTS_PER_CELL):
interp[i] = BLSFieldElement(int(interp[i]) * int(inv_x_pow) % BLS_MODULUS)
inv_x_pow = BLSFieldElement(int(inv_x_pow) * int(inv_x) % BLS_MODULUS)

zero_poly = g2_lincomb(KZG_SETUP_G2_MONOMIAL[:len(zs) + 1], vanishing_polynomialcoeff(zs))
interpolated_poly = g1_lincomb(KZG_SETUP_G1_MONOMIAL[:len(zs)], interpolate_polynomialcoeff(zs, ys))
x_pow = bls_modular_inverse(inv_x_pow)
xn2 = bls.multiply(bls.G2(), x_pow)
xn_minus_yn = bls.add(bls.bytes96_to_G2(KZG_SETUP_G2_MONOMIAL[FIELD_ELEMENTS_PER_CELL]), bls.neg(xn2))

interpolated_poly = g1_lincomb(KZG_SETUP_G1_MONOMIAL[:FIELD_ELEMENTS_PER_CELL], interp)

return (bls.pairing_check([
[bls.bytes48_to_G1(proof), bls.bytes96_to_G2(zero_poly)],
[bls.bytes48_to_G1(proof), xn_minus_yn],
[
bls.add(bls.bytes48_to_G1(commitment), bls.neg(bls.bytes48_to_G1(interpolated_poly))),
bls.neg(bls.bytes96_to_G2(KZG_SETUP_G2_MONOMIAL[0])),
Expand All @@ -355,7 +367,7 @@ def verify_kzg_proof_multi_impl(commitment: KZGCommitment,
```python
def coset_for_cell(cell_id: CellID) -> Cell:
"""
Get the coset for a given ``cell_id``
Get the coset for a given ``cell_id``.
"""
assert cell_id < CELLS_PER_BLOB
roots_of_unity_brp = bit_reversal_permutation(
Expand All @@ -364,6 +376,26 @@ def coset_for_cell(cell_id: CellID) -> Cell:
return Cell(roots_of_unity_brp[FIELD_ELEMENTS_PER_CELL * cell_id:FIELD_ELEMENTS_PER_CELL * (cell_id + 1)])
```

### Bitwise manipulation

#### `reverse_bits_limited`

```python
def reverse_bits_limited(length: uint64, value: uint64) -> uint64:
"""
Reverse the low-order bits in an integer.
"""
assert value < length
assert is_power_of_two(length)
max_value = length - 1
num_bits = max_value.bit_length()
reversed_bits = uint64(0)
for _ in range(num_bits):
reversed_bits = (reversed_bits << 1) | (value & 1)
value = value >> 1
return reversed_bits
```

## Cells

### Cell computation
Expand Down Expand Up @@ -425,17 +457,14 @@ def verify_cell_proof(commitment_bytes: Bytes48,
cell_bytes: Vector[Bytes32, FIELD_ELEMENTS_PER_CELL],
proof_bytes: Bytes48) -> bool:
"""
Check a cell proof
Check a cell proof.

Public method.
"""
coset = coset_for_cell(cell_id)

return verify_kzg_proof_multi_impl(
bytes_to_kzg_commitment(commitment_bytes),
coset,
bytes_to_cell(cell_bytes),
bytes_to_kzg_proof(proof_bytes))
roots_of_unity = compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB)
x = roots_of_unity[reverse_bits_limited(CELLS_PER_BLOB, cell_id)]
ys = bit_reversal_permutation(bytes_to_cell(cell_bytes))
return verify_kzg_proof_multi_impl(commitment_bytes, x, ys, proof_bytes)
```

#### `verify_cell_proof_batch`
Expand All @@ -461,6 +490,7 @@ def verify_cell_proof_batch(row_commitments_bytes: Sequence[Bytes48],
Public method.
"""
assert len(cells_bytes) == len(proofs_bytes) == len(row_ids) == len(column_ids)
roots_of_unity = compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB)

# Get commitments via row IDs
commitments_bytes = [row_commitments_bytes[row_id] for row_id in row_ids]
Expand All @@ -471,7 +501,11 @@ def verify_cell_proof_batch(row_commitments_bytes: Sequence[Bytes48],
proofs = [bytes_to_kzg_proof(proof_bytes) for proof_bytes in proofs_bytes]

return all(
verify_kzg_proof_multi_impl(commitment, coset_for_cell(column_id), cell, proof)
verify_kzg_proof_multi_impl(
commitment,
roots_of_unity[reverse_bits_limited(CELLS_PER_BLOB, column_id)],
bit_reversal_permutation(cell),
proof)
for commitment, column_id, cell, proof in zip(commitments, column_ids, cells, proofs)
)
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,27 @@ def test_fft(spec):
assert poly_coeff_inversed == poly_coeff


@with_eip7594_and_later
@spec_test
@single_phase
def test_reverse_bits_limited(spec):
rng = random.Random(5566)
for exp in range(64):
length = 2**exp
value = rng.randrange(0, length)
assert value == spec.reverse_bits_limited(length, spec.reverse_bits_limited(length, value))


@with_eip7594_and_later
@spec_test
@single_phase
def test_verify_cell_proof(spec):
blob = get_sample_blob(spec)
commitment = spec.blob_to_kzg_commitment(blob)
cells, proofs = spec.compute_cells_and_proofs(blob)

cells_bytes = [[field_element_bytes(element) for element in cell] for cell in cells]

cell_id = 0
assert spec.verify_cell_proof(commitment, cell_id, cells_bytes[cell_id], proofs[cell_id])
cell_id = 1
assert spec.verify_cell_proof(commitment, cell_id, cells_bytes[cell_id], proofs[cell_id])
for cell_id in range(spec.CELLS_PER_BLOB):
assert spec.verify_cell_proof(commitment, cell_id, cells_bytes[cell_id], proofs[cell_id])


@with_eip7594_and_later
Expand Down