2023-11-14 09:04:42 +00:00
# EIP-7594 -- Polynomial Commitments
2023-12-04 01:12:07 +00:00
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE - RUN doctoc TO UPDATE -->
- [Introduction ](#introduction )
- [Custom types ](#custom-types )
- [Constants ](#constants )
- [Preset ](#preset )
2023-12-23 14:52:39 +00:00
- [Cells ](#cells )
2023-12-04 01:12:07 +00:00
- [Helper functions ](#helper-functions )
2024-01-15 16:27:55 +00:00
- [BLS12-381 helpers ](#bls12-381-helpers )
- [`bytes_to_cell` ](#bytes_to_cell )
2023-12-04 01:12:07 +00:00
- [Linear combinations ](#linear-combinations )
- [`g2_lincomb` ](#g2_lincomb )
- [FFTs ](#ffts )
- [`_fft_field` ](#_fft_field )
- [`fft_field` ](#fft_field )
- [Polynomials in coefficient form ](#polynomials-in-coefficient-form )
- [`polynomial_eval_to_coeff` ](#polynomial_eval_to_coeff )
- [`add_polynomialcoeff` ](#add_polynomialcoeff )
- [`neg_polynomialcoeff` ](#neg_polynomialcoeff )
- [`multiply_polynomialcoeff` ](#multiply_polynomialcoeff )
- [`divide_polynomialcoeff` ](#divide_polynomialcoeff )
- [`shift_polynomialcoeff` ](#shift_polynomialcoeff )
- [`interpolate_polynomialcoeff` ](#interpolate_polynomialcoeff )
2023-12-23 15:08:02 +00:00
- [`vanishing_polynomialcoeff` ](#vanishing_polynomialcoeff )
2023-12-04 01:12:07 +00:00
- [`evaluate_polynomialcoeff` ](#evaluate_polynomialcoeff )
- [KZG multiproofs ](#kzg-multiproofs )
- [`compute_kzg_proof_multi_impl` ](#compute_kzg_proof_multi_impl )
- [`verify_kzg_proof_multi_impl` ](#verify_kzg_proof_multi_impl )
2023-12-23 14:52:39 +00:00
- [Cell cosets ](#cell-cosets )
- [`coset_for_cell` ](#coset_for_cell )
- [Cells ](#cells-1 )
- [Cell computation ](#cell-computation )
- [`compute_cells_and_proofs` ](#compute_cells_and_proofs )
- [`compute_cells` ](#compute_cells )
- [Cell verification ](#cell-verification )
- [`verify_cell_proof` ](#verify_cell_proof )
- [`verify_cell_proof_batch` ](#verify_cell_proof_batch )
2023-12-04 01:12:07 +00:00
- [Reconstruction ](#reconstruction )
2024-01-23 16:22:05 +00:00
- [`construct_vanishing_polynomial` ](#construct_vanishing_polynomial )
- [`recover_shifted_data` ](#recover_shifted_data )
- [`recover_original_data` ](#recover_original_data )
2024-01-08 15:18:51 +00:00
- [`recover_polynomial` ](#recover_polynomial )
2023-12-04 01:12:07 +00:00
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
This document extends [polynomial-commitments.md ](polynomial-commitments.md ) with the functions required for data availability sampling (DAS). It is not part of the core Deneb spec but an extension that can be optionally implemented to allow nodes to reduce their load using DAS.
For any KZG library extended to support DAS, functions flagged as "Public method" MUST be provided by the underlying KZG library as public functions. All other functions are private functions used internally by the KZG library.
Public functions MUST accept raw bytes as input and perform the required cryptographic normalization before invoking any internal functions.
## Custom types
| Name | SSZ equivalent | Description |
| - | - | - |
2024-03-12 13:07:42 +00:00
| `PolynomialCoeff` | `List[BLSFieldElement, FIELD_ELEMENTS_PER_EXT_BLOB]` | A polynomial in coefficient form |
2024-01-08 15:18:51 +00:00
| `Cell` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL]` | The unit of blob data that can come with their own KZG proofs |
| `CellID` | `uint64` | Cell identifier |
2024-01-19 10:51:36 +00:00
| `RowIndex` | `uint64` | Row identifier |
| `ColumnIndex` | `uint64` | Column identifier |
2023-12-04 01:12:07 +00:00
## Constants
| Name | Value | Notes |
| - | - | - |
## Preset
2023-12-23 14:52:39 +00:00
### Cells
Cells are the smallest unit of blob data that can come with their own KZG proofs. Samples can be constructed from one or several cells (e.g. an individual cell or line).
2023-12-04 01:12:07 +00:00
| Name | Value | Description |
| - | - | - |
2024-02-05 14:53:59 +00:00
| `FIELD_ELEMENTS_PER_EXT_BLOB` | `2 * FIELD_ELEMENTS_PER_BLOB` | Number of field elements in a Reed-Solomon extended blob |
2023-12-23 14:44:58 +00:00
| `FIELD_ELEMENTS_PER_CELL` | `uint64(64)` | Number of field elements in a cell |
| `BYTES_PER_CELL` | `FIELD_ELEMENTS_PER_CELL * BYTES_PER_FIELD_ELEMENT` | The number of bytes in a cell |
2024-04-18 16:38:04 +00:00
| `CELLS_PER_EXT_BLOB` | `FIELD_ELEMENTS_PER_EXT_BLOB // FIELD_ELEMENTS_PER_CELL` | The number of cells in an extended blob |
2024-01-10 19:13:33 +00:00
| `RANDOM_CHALLENGE_KZG_CELL_BATCH_DOMAIN` | `b'RCKZGCBATCH__V1_'` |
2023-12-04 01:12:07 +00:00
## Helper functions
2024-01-15 16:27:55 +00:00
### BLS12-381 helpers
#### `bytes_to_cell`
```python
def bytes_to_cell(cell_bytes: Vector[Bytes32, FIELD_ELEMENTS_PER_CELL]) -> Cell:
"""
Convert untrusted bytes into a Cell.
"""
return [bytes_to_bls_field(element) for element in cell_bytes]
```
2023-12-04 01:12:07 +00:00
### Linear combinations
#### `g2_lincomb`
```python
def g2_lincomb(points: Sequence[KZGCommitment], scalars: Sequence[BLSFieldElement]) -> Bytes96:
"""
BLS multiscalar multiplication in G2. This function can be optimized using Pippenger's algorithm and variants.
"""
assert len(points) == len(scalars)
result = bls.Z2()
for x, a in zip(points, scalars):
result = bls.add(result, bls.multiply(bls.bytes96_to_G2(x), a))
return Bytes96(bls.G2_to_bytes96(result))
```
### FFTs
#### `_fft_field`
```python
2024-01-05 12:31:09 +00:00
def _fft_field(vals: Sequence[BLSFieldElement],
roots_of_unity: Sequence[BLSFieldElement]) -> Sequence[BLSFieldElement]:
if len(vals) == 1:
2023-12-23 14:44:58 +00:00
return vals
2023-12-04 01:12:07 +00:00
L = _fft_field(vals[::2], roots_of_unity[::2])
R = _fft_field(vals[1::2], roots_of_unity[::2])
2024-01-05 12:31:09 +00:00
o = [BLSFieldElement(0) for _ in vals]
2023-12-04 01:12:07 +00:00
for i, (x, y) in enumerate(zip(L, R)):
2024-01-05 12:31:09 +00:00
y_times_root = (int(y) * int(roots_of_unity[i])) % BLS_MODULUS
o[i] = BLSFieldElement((int(x) + y_times_root) % BLS_MODULUS)
o[i + len(L)] = BLSFieldElement((int(x) - y_times_root + BLS_MODULUS) % BLS_MODULUS)
2023-12-04 01:12:07 +00:00
return o
```
#### `fft_field`
```python
2024-01-05 12:31:09 +00:00
def fft_field(vals: Sequence[BLSFieldElement],
roots_of_unity: Sequence[BLSFieldElement],
inv: bool=False) -> Sequence[BLSFieldElement]:
2023-12-04 01:12:07 +00:00
if inv:
# Inverse FFT
invlen = pow(len(vals), BLS_MODULUS - 2, BLS_MODULUS)
2024-01-05 12:31:09 +00:00
return [BLSFieldElement((int(x) * invlen) % BLS_MODULUS)
2024-01-08 17:37:24 +00:00
for x in _fft_field(vals, list(roots_of_unity[0:1]) + list(roots_of_unity[:0:-1]))]
2023-12-04 01:12:07 +00:00
else:
# Regular FFT
return _fft_field(vals, roots_of_unity)
```
### Polynomials in coefficient form
#### `polynomial_eval_to_coeff`
```python
def polynomial_eval_to_coeff(polynomial: Polynomial) -> PolynomialCoeff:
"""
Interpolates a polynomial (given in evaluation form) to a polynomial in coefficient form.
"""
2024-01-08 17:37:24 +00:00
roots_of_unity = compute_roots_of_unity(FIELD_ELEMENTS_PER_BLOB)
polynomial_coeff = fft_field(bit_reversal_permutation(list(polynomial)), roots_of_unity, inv=True)
2023-12-04 01:12:07 +00:00
return polynomial_coeff
```
#### `add_polynomialcoeff`
```python
def add_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> PolynomialCoeff:
"""
Sum the coefficient form polynomials ``a`` and ``b``.
"""
a, b = (a, b) if len(a) >= len(b) else (b, a)
2024-01-16 08:51:05 +00:00
length_a = len(a)
length_b = len(b)
return [(a[i] + (b[i] if i < length_b else 0 ) ) % BLS_MODULUS for i in range ( length_a ) ]
2023-12-04 01:12:07 +00:00
```
#### `neg_polynomialcoeff`
```python
def neg_polynomialcoeff(a: PolynomialCoeff) -> PolynomialCoeff:
"""
Negative of coefficient form polynomial ``a``
"""
return [(BLS_MODULUS - x) % BLS_MODULUS for x in a]
```
#### `multiply_polynomialcoeff`
```python
def multiply_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> PolynomialCoeff:
"""
Multiplies the coefficient form polynomials ``a`` and ``b``
"""
2024-03-08 16:04:55 +00:00
assert len(a) + len(b) < = FIELD_ELEMENTS_PER_EXT_BLOB
2023-12-04 01:12:07 +00:00
r = [0]
for power, coef in enumerate(a):
summand = [0] * power + [int(coef) * int(x) % BLS_MODULUS for x in b]
r = add_polynomialcoeff(r, summand)
return r
```
2024-03-08 16:04:55 +00:00
2023-12-04 01:12:07 +00:00
#### `divide_polynomialcoeff`
```python
def divide_polynomialcoeff(a: PolynomialCoeff, b: PolynomialCoeff) -> PolynomialCoeff:
"""
Long polynomial division for two coefficient form polynomials ``a`` and ``b``
"""
a = [x for x in a]
o = []
apos = len(a) - 1
bpos = len(b) - 1
diff = apos - bpos
while diff >= 0:
quot = div(a[apos], b[bpos])
o.insert(0, quot)
for i in range(bpos, -1, -1):
a[diff + i] = (int(a[diff + i]) - int(b[i]) * int(quot)) % BLS_MODULUS
apos -= 1
diff -= 1
return [x % BLS_MODULUS for x in o]
```
#### `shift_polynomialcoeff`
```python
2024-01-08 17:37:24 +00:00
def shift_polynomialcoeff(polynomial_coeff: PolynomialCoeff, factor: BLSFieldElement) -> PolynomialCoeff:
2023-12-04 01:12:07 +00:00
"""
Shift the evaluation of a polynomial in coefficient form by factor.
This results in a new polynomial g(x) = f(factor * x)
"""
factor_power = 1
inv_factor = pow(int(factor), BLS_MODULUS - 2, BLS_MODULUS)
o = []
2024-01-08 17:37:24 +00:00
for p in polynomial_coeff:
2023-12-04 01:12:07 +00:00
o.append(int(p) * factor_power % BLS_MODULUS)
factor_power = factor_power * inv_factor % BLS_MODULUS
return o
```
#### `interpolate_polynomialcoeff`
```python
def interpolate_polynomialcoeff(xs: Sequence[BLSFieldElement], ys: Sequence[BLSFieldElement]) -> PolynomialCoeff:
"""
Lagrange interpolation: Finds the lowest degree polynomial that takes the value ``ys[i]`` at ``x[i]``
for all i.
Outputs a coefficient form polynomial. Leading coefficients may be zero.
"""
assert len(xs) == len(ys)
r = [0]
for i in range(len(xs)):
summand = [ys[i]]
for j in range(len(ys)):
if j != i:
weight_adjustment = bls_modular_inverse(int(xs[i]) - int(xs[j]))
summand = multiply_polynomialcoeff(
summand, [(- int(weight_adjustment) * int(xs[j])) % BLS_MODULUS, weight_adjustment]
)
r = add_polynomialcoeff(r, summand)
2024-01-15 15:34:28 +00:00
2023-12-04 01:12:07 +00:00
return r
```
2023-12-23 15:08:02 +00:00
#### `vanishing_polynomialcoeff`
2023-12-04 01:12:07 +00:00
```python
2023-12-23 15:08:02 +00:00
def vanishing_polynomialcoeff(xs: Sequence[BLSFieldElement]) -> PolynomialCoeff:
2023-12-04 01:12:07 +00:00
"""
2023-12-23 15:08:02 +00:00
Compute the vanishing polynomial on ``xs`` (in coefficient form)
2023-12-04 01:12:07 +00:00
"""
p = [1]
for x in xs:
p = multiply_polynomialcoeff(p, [-int(x), 1])
return p
```
#### `evaluate_polynomialcoeff`
```python
def evaluate_polynomialcoeff(polynomial_coeff: PolynomialCoeff, z: BLSFieldElement) -> BLSFieldElement:
"""
Evaluate a coefficient form polynomial at ``z`` using Horner's schema
"""
y = 0
for coef in polynomial_coeff[::-1]:
2024-01-05 12:31:09 +00:00
y = (int(y) * int(z) + int(coef)) % BLS_MODULUS
2023-12-04 01:12:07 +00:00
return BLSFieldElement(y % BLS_MODULUS)
```
### KZG multiproofs
Extended KZG functions for multiproofs
#### `compute_kzg_proof_multi_impl`
```python
2023-12-12 16:25:30 +00:00
def compute_kzg_proof_multi_impl(
polynomial_coeff: PolynomialCoeff,
zs: Sequence[BLSFieldElement]) -> Tuple[KZGProof, Sequence[BLSFieldElement]]:
2023-12-04 01:12:07 +00:00
"""
Helper function that computes multi-evaluation KZG proofs.
"""
# For all x_i, compute p(x_i) - p(z)
ys = [evaluate_polynomialcoeff(polynomial_coeff, z) for z in zs]
interpolation_polynomial = interpolate_polynomialcoeff(zs, ys)
polynomial_shifted = add_polynomialcoeff(polynomial_coeff, neg_polynomialcoeff(interpolation_polynomial))
# For all x_i, compute (x_i - z)
2023-12-23 15:08:02 +00:00
denominator_poly = vanishing_polynomialcoeff(zs)
2023-12-04 01:12:07 +00:00
# Compute the quotient polynomial directly in evaluation form
quotient_polynomial = divide_polynomialcoeff(polynomial_shifted, denominator_poly)
2024-01-05 10:21:15 +00:00
return KZGProof(g1_lincomb(KZG_SETUP_G1_MONOMIAL[:len(quotient_polynomial)], quotient_polynomial)), ys
2023-12-04 01:12:07 +00:00
```
#### `verify_kzg_proof_multi_impl`
```python
def verify_kzg_proof_multi_impl(commitment: KZGCommitment,
2024-01-11 20:57:16 +00:00
zs: Sequence[BLSFieldElement],
ys: Sequence[BLSFieldElement],
2023-12-04 01:12:07 +00:00
proof: KZGProof) -> bool:
"""
Helper function that verifies a KZG multiproof
"""
2024-01-11 20:57:16 +00:00
assert len(zs) == len(ys)
2024-01-05 10:21:15 +00:00
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))
2023-12-04 01:12:07 +00:00
2023-12-12 16:25:30 +00:00
return (bls.pairing_check([
[bls.bytes48_to_G1(proof), bls.bytes96_to_G2(zero_poly)],
[
bls.add(bls.bytes48_to_G1(commitment), bls.neg(bls.bytes48_to_G1(interpolated_poly))),
2024-01-05 10:21:15 +00:00
bls.neg(bls.bytes96_to_G2(KZG_SETUP_G2_MONOMIAL[0])),
2023-12-12 16:25:30 +00:00
],
]))
2023-12-04 01:12:07 +00:00
```
2023-12-23 14:52:39 +00:00
### Cell cosets
2023-12-04 01:12:07 +00:00
2023-12-23 14:52:39 +00:00
#### `coset_for_cell`
2023-12-04 01:12:07 +00:00
```python
2024-01-16 14:58:14 +00:00
def coset_for_cell(cell_id: CellID) -> Cell:
2023-12-04 01:12:07 +00:00
"""
2023-12-23 14:52:39 +00:00
Get the coset for a given ``cell_id``
2023-12-04 01:12:07 +00:00
"""
2024-04-18 16:38:04 +00:00
assert cell_id < CELLS_PER_EXT_BLOB
2024-01-11 15:42:10 +00:00
roots_of_unity_brp = bit_reversal_permutation(
2024-02-05 14:53:59 +00:00
compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB)
2024-01-11 15:42:10 +00:00
)
2024-01-08 15:18:51 +00:00
return Cell(roots_of_unity_brp[FIELD_ELEMENTS_PER_CELL * cell_id:FIELD_ELEMENTS_PER_CELL * (cell_id + 1)])
2023-12-04 01:12:07 +00:00
```
2023-12-23 14:52:39 +00:00
## Cells
2023-12-04 01:12:07 +00:00
2023-12-23 14:52:39 +00:00
### Cell computation
2023-12-04 01:12:07 +00:00
2023-12-23 14:52:39 +00:00
#### `compute_cells_and_proofs`
2023-12-04 01:12:07 +00:00
```python
2023-12-23 14:52:39 +00:00
def compute_cells_and_proofs(blob: Blob) -> Tuple[
2024-04-18 16:38:04 +00:00
Vector[Cell, CELLS_PER_EXT_BLOB],
Vector[KZGProof, CELLS_PER_EXT_BLOB]]:
2023-12-04 01:12:07 +00:00
"""
2023-12-23 14:52:39 +00:00
Compute all the cell proofs for one blob. This is an inefficient O(n^2) algorithm,
2023-12-04 01:12:07 +00:00
for performant implementation the FK20 algorithm that runs in O(n log n) should be
used instead.
2023-12-04 01:26:07 +00:00
Public method.
2023-12-04 01:12:07 +00:00
"""
polynomial = blob_to_polynomial(blob)
polynomial_coeff = polynomial_eval_to_coeff(polynomial)
2023-12-23 14:52:39 +00:00
cells = []
2023-12-12 15:51:50 +00:00
proofs = []
2023-12-04 01:12:07 +00:00
2024-04-18 16:38:04 +00:00
for i in range(CELLS_PER_EXT_BLOB):
2023-12-23 14:52:39 +00:00
coset = coset_for_cell(i)
2023-12-04 01:12:07 +00:00
proof, ys = compute_kzg_proof_multi_impl(polynomial_coeff, coset)
2023-12-23 14:52:39 +00:00
cells.append(ys)
2023-12-04 01:12:07 +00:00
proofs.append(proof)
2023-12-23 14:52:39 +00:00
return cells, proofs
2023-12-04 01:12:07 +00:00
```
2023-12-23 14:52:39 +00:00
#### `compute_cells`
2023-12-04 01:12:07 +00:00
```python
2024-04-18 16:38:04 +00:00
def compute_cells(blob: Blob) -> Vector[Cell, CELLS_PER_EXT_BLOB]:
2023-12-04 01:26:07 +00:00
"""
2023-12-23 14:52:39 +00:00
Compute the cell data for a blob (without computing the proofs).
2023-12-04 01:26:07 +00:00
Public method.
"""
2023-12-04 01:12:07 +00:00
polynomial = blob_to_polynomial(blob)
polynomial_coeff = polynomial_eval_to_coeff(polynomial)
2024-01-15 15:34:28 +00:00
extended_data = fft_field(polynomial_coeff + [0] * FIELD_ELEMENTS_PER_BLOB,
2024-02-05 14:53:59 +00:00
compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB))
2023-12-04 01:12:07 +00:00
extended_data_rbo = bit_reversal_permutation(extended_data)
2023-12-23 14:44:58 +00:00
return [extended_data_rbo[i * FIELD_ELEMENTS_PER_CELL:(i + 1) * FIELD_ELEMENTS_PER_CELL]
2024-04-18 16:38:04 +00:00
for i in range(CELLS_PER_EXT_BLOB)]
2023-12-04 01:12:07 +00:00
```
2023-12-23 14:52:39 +00:00
### Cell verification
2023-12-04 01:12:07 +00:00
2023-12-23 14:52:39 +00:00
#### `verify_cell_proof`
2023-12-04 01:12:07 +00:00
```python
2024-01-15 16:27:55 +00:00
def verify_cell_proof(commitment_bytes: Bytes48,
2024-01-17 12:18:03 +00:00
cell_id: CellID,
2024-01-15 16:27:55 +00:00
cell_bytes: Vector[Bytes32, FIELD_ELEMENTS_PER_CELL],
2024-01-17 12:18:03 +00:00
proof_bytes: Bytes48) -> bool:
2023-12-04 01:12:07 +00:00
"""
2023-12-23 14:52:39 +00:00
Check a cell proof
2023-12-04 01:26:07 +00:00
2023-12-23 14:44:58 +00:00
Public method.
2023-12-04 01:12:07 +00:00
"""
2023-12-23 14:52:39 +00:00
coset = coset_for_cell(cell_id)
2023-12-04 01:12:07 +00:00
2024-01-15 16:27:55 +00:00
return verify_kzg_proof_multi_impl(
bytes_to_kzg_commitment(commitment_bytes),
coset,
bytes_to_cell(cell_bytes),
2024-01-17 12:18:03 +00:00
bytes_to_kzg_proof(proof_bytes))
2023-12-04 01:12:07 +00:00
```
2023-12-23 14:52:39 +00:00
#### `verify_cell_proof_batch`
2023-12-04 01:12:07 +00:00
```python
2024-01-15 16:27:55 +00:00
def verify_cell_proof_batch(row_commitments_bytes: Sequence[Bytes48],
2024-01-19 10:51:36 +00:00
row_indices: Sequence[RowIndex],
column_indices: Sequence[ColumnIndex],
2024-01-15 16:27:55 +00:00
cells_bytes: Sequence[Vector[Bytes32, FIELD_ELEMENTS_PER_CELL]],
proofs_bytes: Sequence[Bytes48]) -> bool:
2023-12-04 01:12:07 +00:00
"""
2024-01-17 15:20:36 +00:00
Verify a set of cells, given their corresponding proofs and their coordinates (row_id, column_id) in the blob
matrix. The list of all commitments is also provided in row_commitments_bytes.
This function implements the naive algorithm of checking every cell
2023-12-04 01:12:07 +00:00
individually; an efficient algorithm can be found here:
https://ethresear.ch/t/a-universal-verification-equation-for-data-availability-sampling/13240
2023-12-04 01:26:07 +00:00
2024-01-10 19:13:33 +00:00
This implementation does not require randomness, but for the algorithm that
requires it, `RANDOM_CHALLENGE_KZG_CELL_BATCH_DOMAIN` should be used to compute
the challenge value.
2023-12-04 01:26:07 +00:00
Public method.
2023-12-04 01:12:07 +00:00
"""
2024-01-29 09:29:20 +00:00
assert len(cells_bytes) == len(proofs_bytes) == len(row_indices) == len(column_indices)
2023-12-04 01:12:07 +00:00
# Get commitments via row IDs
2024-01-29 09:29:20 +00:00
commitments_bytes = [row_commitments_bytes[row_index] for row_index in row_indices]
2024-01-15 16:27:55 +00:00
# Get objects from bytes
commitments = [bytes_to_kzg_commitment(commitment_bytes) for commitment_bytes in commitments_bytes]
cells = [bytes_to_cell(cell_bytes) for cell_bytes in cells_bytes]
proofs = [bytes_to_kzg_proof(proof_bytes) for proof_bytes in proofs_bytes]
2024-01-08 15:18:51 +00:00
return all(
2024-01-19 10:51:36 +00:00
verify_kzg_proof_multi_impl(commitment, coset_for_cell(column_index), cell, proof)
for commitment, column_index, cell, proof in zip(commitments, column_indices, cells, proofs)
2024-01-08 15:18:51 +00:00
)
2023-12-04 01:12:07 +00:00
```
## Reconstruction
2024-01-31 11:45:49 +00:00
### `construct_vanishing_polynomial`
2023-12-04 01:12:07 +00:00
```python
2024-01-31 11:32:23 +00:00
def construct_vanishing_polynomial(missing_cell_ids: Sequence[CellID]) -> Tuple[
Sequence[BLSFieldElement],
Sequence[BLSFieldElement]]:
2023-12-04 01:12:07 +00:00
"""
2024-01-31 11:32:23 +00:00
Given the cells that are missing from the data, compute the polynomial that vanishes at every point that
2024-02-01 10:09:37 +00:00
corresponds to a missing field element.
2023-12-04 01:12:07 +00:00
"""
2024-01-31 11:32:23 +00:00
# Get the small domain
2024-04-18 16:38:04 +00:00
roots_of_unity_reduced = compute_roots_of_unity(CELLS_PER_EXT_BLOB)
2024-01-31 11:32:23 +00:00
# Compute polynomial that vanishes at all the missing cells (over the small domain)
2024-01-05 10:33:01 +00:00
short_zero_poly = vanishing_polynomialcoeff([
2024-04-18 16:38:04 +00:00
roots_of_unity_reduced[reverse_bits(missing_cell_id, CELLS_PER_EXT_BLOB)]
2024-01-31 11:32:23 +00:00
for missing_cell_id in missing_cell_ids
2024-01-05 10:33:01 +00:00
])
2023-12-04 01:12:07 +00:00
2024-01-31 11:32:23 +00:00
# Extend vanishing polynomial to full domain using the closed form of the vanishing polynomial over a coset
2024-02-06 11:10:24 +00:00
zero_poly_coeff = [0] * FIELD_ELEMENTS_PER_EXT_BLOB
2024-02-01 10:09:37 +00:00
for i, coeff in enumerate(short_zero_poly):
2024-01-31 11:32:23 +00:00
zero_poly_coeff[i * FIELD_ELEMENTS_PER_CELL] = coeff
2023-12-04 01:12:07 +00:00
2024-01-31 11:32:23 +00:00
# Compute evaluations of the extended vanishing polynomial
2024-01-31 11:45:49 +00:00
zero_poly_eval = fft_field(zero_poly_coeff,
2024-02-05 14:53:59 +00:00
compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB))
2023-12-04 01:12:07 +00:00
zero_poly_eval_brp = bit_reversal_permutation(zero_poly_eval)
2024-01-31 11:32:23 +00:00
# Sanity check
2024-04-18 16:38:04 +00:00
for cell_id in range(CELLS_PER_EXT_BLOB):
2024-01-05 10:33:01 +00:00
start = cell_id * FIELD_ELEMENTS_PER_CELL
end = (cell_id + 1) * FIELD_ELEMENTS_PER_CELL
2024-01-31 11:32:23 +00:00
if cell_id in missing_cell_ids:
2024-02-01 10:09:37 +00:00
assert all(a == 0 for a in zero_poly_eval_brp[start:end])
2024-01-31 11:32:23 +00:00
else: # cell_id in cell_ids
assert all(a != 0 for a in zero_poly_eval_brp[start:end])
2023-12-04 01:12:07 +00:00
2024-01-31 11:45:49 +00:00
return zero_poly_coeff, zero_poly_eval, zero_poly_eval_brp
```
2023-12-04 01:12:07 +00:00
2024-01-31 11:45:49 +00:00
### `recover_shifted_data`
```python
2024-01-23 16:22:05 +00:00
def recover_shifted_data(cell_ids: Sequence[CellID],
cells: Sequence[Cell],
zero_poly_eval: Sequence[BLSFieldElement],
zero_poly_coeff: Sequence[BLSFieldElement],
roots_of_unity_extended: Sequence[BLSFieldElement]) -> Tuple[
Sequence[BLSFieldElement],
Sequence[BLSFieldElement],
BLSFieldElement]:
2024-01-31 11:45:04 +00:00
"""
2024-02-01 10:09:37 +00:00
Given Z(x), return polynomial Q_1(x)=(E*Z)(k*x) and Q_2(x)=Z(k*x) and k^{-1}.
2024-01-31 11:45:04 +00:00
"""
shift_factor = BLSFieldElement(PRIMITIVE_ROOT_OF_UNITY)
shift_inv = div(BLSFieldElement(1), shift_factor)
2024-02-06 11:10:31 +00:00
extended_evaluation_rbo = [0] * FIELD_ELEMENTS_PER_EXT_BLOB
2024-01-08 15:18:51 +00:00
for cell_id, cell in zip(cell_ids, cells):
start = cell_id * FIELD_ELEMENTS_PER_CELL
end = (cell_id + 1) * FIELD_ELEMENTS_PER_CELL
extended_evaluation_rbo[start:end] = cell
2023-12-04 01:12:07 +00:00
extended_evaluation = bit_reversal_permutation(extended_evaluation_rbo)
2024-01-31 11:45:04 +00:00
# Compute (E*Z)(x)
2024-01-09 13:41:36 +00:00
extended_evaluation_times_zero = [BLSFieldElement(int(a) * int(b) % BLS_MODULUS)
2024-01-08 17:37:24 +00:00
for a, b in zip(zero_poly_eval, extended_evaluation)]
2023-12-04 01:12:07 +00:00
2024-01-11 15:42:10 +00:00
extended_evaluations_fft = fft_field(extended_evaluation_times_zero, roots_of_unity_extended, inv=True)
2023-12-04 01:12:07 +00:00
2024-01-31 11:45:04 +00:00
# Compute (E*Z)(k*x)
2023-12-04 01:12:07 +00:00
shifted_extended_evaluation = shift_polynomialcoeff(extended_evaluations_fft, shift_factor)
2024-01-31 11:45:04 +00:00
# Compute Z(k*x)
2024-01-31 11:45:49 +00:00
shifted_zero_poly = shift_polynomialcoeff(zero_poly_coeff, shift_factor)
2023-12-12 16:25:30 +00:00
2024-01-11 15:42:10 +00:00
eval_shifted_extended_evaluation = fft_field(shifted_extended_evaluation, roots_of_unity_extended)
eval_shifted_zero_poly = fft_field(shifted_zero_poly, roots_of_unity_extended)
2024-01-15 15:34:28 +00:00
2024-01-23 16:22:05 +00:00
return eval_shifted_extended_evaluation, eval_shifted_zero_poly, shift_inv
```
### `recover_original_data`
```python
def recover_original_data(eval_shifted_extended_evaluation: Sequence[BLSFieldElement],
eval_shifted_zero_poly: Sequence[BLSFieldElement],
shift_inv: BLSFieldElement,
roots_of_unity_extended: Sequence[BLSFieldElement]) -> Sequence[BLSFieldElement]:
2024-01-31 11:45:04 +00:00
"""
2024-02-01 10:09:37 +00:00
Given Q_1, Q_2 and k^{-1}, compute P(x).
2024-01-31 11:45:04 +00:00
"""
# Compute Q_3 = Q_1(x)/Q_2(x) = P(k*x)
2023-12-12 16:25:30 +00:00
eval_shifted_reconstructed_poly = [
div(a, b)
for a, b in zip(eval_shifted_extended_evaluation, eval_shifted_zero_poly)
]
2024-01-11 15:42:10 +00:00
shifted_reconstructed_poly = fft_field(eval_shifted_reconstructed_poly, roots_of_unity_extended, inv=True)
2023-12-12 16:25:30 +00:00
2024-01-31 11:45:04 +00:00
# Unshift P(k*x) by k^{-1} to get P(x)
2023-12-04 01:12:07 +00:00
reconstructed_poly = shift_polynomialcoeff(shifted_reconstructed_poly, shift_inv)
2024-01-11 15:42:10 +00:00
reconstructed_data = bit_reversal_permutation(fft_field(reconstructed_poly, roots_of_unity_extended))
2023-12-04 01:12:07 +00:00
2024-01-23 16:22:05 +00:00
return reconstructed_data
```
### `recover_polynomial`
```python
def recover_polynomial(cell_ids: Sequence[CellID],
cells_bytes: Sequence[Vector[Bytes32, FIELD_ELEMENTS_PER_CELL]]) -> Polynomial:
"""
2024-02-05 14:53:59 +00:00
Recover original polynomial from FIELD_ELEMENTS_PER_EXT_BLOB evaluations, half of which can be missing. This
2024-01-31 11:45:04 +00:00
algorithm uses FFTs to recover cells faster than using Lagrange implementation, as can be seen here:
https://ethresear.ch/t/reed-solomon-erasure-code-recovery-in-n-log-2-n-time-with-ffts/3039
2024-01-23 16:22:05 +00:00
2024-01-31 11:45:04 +00:00
A faster version thanks to Qi Zhou can be found here:
2024-01-23 16:22:05 +00:00
https://github.com/ethereum/research/blob/51b530a53bd4147d123ab3e390a9d08605c2cdb8/polynomial_reconstruction/polynomial_reconstruction_danksharding.py
Public method.
"""
assert len(cell_ids) == len(cells_bytes)
2024-02-05 14:53:28 +00:00
# Check we have enough cells to be able to perform the reconstruction
2024-04-18 16:38:04 +00:00
assert CELLS_PER_EXT_BLOB / 2 < = len(cell_ids) < = CELLS_PER_EXT_BLOB
2024-02-05 14:53:28 +00:00
# Check for duplicates
assert len(cell_ids) == len(set(cell_ids))
2024-01-23 16:22:05 +00:00
2024-01-31 11:32:23 +00:00
# Get the extended domain
2024-02-05 14:53:59 +00:00
roots_of_unity_extended = compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB)
2024-01-31 11:32:23 +00:00
# Convert from bytes to cells
2024-01-23 16:22:05 +00:00
cells = [bytes_to_cell(cell_bytes) for cell_bytes in cells_bytes]
2024-04-18 16:38:04 +00:00
missing_cell_ids = [cell_id for cell_id in range(CELLS_PER_EXT_BLOB) if cell_id not in cell_ids]
2024-01-31 11:32:23 +00:00
zero_poly_coeff, zero_poly_eval, zero_poly_eval_brp = construct_vanishing_polynomial(missing_cell_ids)
2024-01-23 16:22:05 +00:00
2024-02-14 12:51:50 +00:00
eval_shifted_extended_evaluation, eval_shifted_zero_poly, shift_inv = recover_shifted_data(
cell_ids,
cells,
zero_poly_eval,
zero_poly_coeff,
roots_of_unity_extended,
)
2024-01-23 16:22:05 +00:00
2024-02-05 14:59:08 +00:00
reconstructed_data = recover_original_data(
eval_shifted_extended_evaluation,
eval_shifted_zero_poly,
shift_inv,
roots_of_unity_extended,
)
2024-01-23 16:22:05 +00:00
2024-01-08 15:18:51 +00:00
for cell_id, cell in zip(cell_ids, cells):
start = cell_id * FIELD_ELEMENTS_PER_CELL
end = (cell_id + 1) * FIELD_ELEMENTS_PER_CELL
assert reconstructed_data[start:end] == cell
2023-12-04 01:12:07 +00:00
return reconstructed_data
2023-12-12 16:25:30 +00:00
```