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

Integrate new CosetEvals type #3701

Merged
merged 17 commits into from
Apr 22, 2024
Merged
3 changes: 1 addition & 2 deletions specs/_features/eip7594/das-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,8 @@ def recover_matrix(cells_dict: Dict[Tuple[BlobIndex, CellID], Cell], blob_count:
for blob_index in range(blob_count):
cell_ids = [cell_id for b_index, cell_id in cells_dict.keys() if b_index == blob_index]
cells = [cells_dict[(blob_index, cell_id)] for cell_id in cell_ids]
cells_bytes = [[bls_field_to_bytes(element) for element in cell] for cell in cells]

all_cells_for_row = recover_all_cells(cell_ids, cells_bytes)
all_cells_for_row = recover_all_cells(cell_ids, cells)
extended_matrix.extend(all_cells_for_row)
return ExtendedMatrix(extended_matrix)
```
Expand Down
87 changes: 56 additions & 31 deletions specs/_features/eip7594/polynomial-commitments-sampling.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
- [Cells](#cells)
- [Helper functions](#helper-functions)
- [BLS12-381 helpers](#bls12-381-helpers)
- [`bytes_to_cell`](#bytes_to_cell)
- [`cell_to_coset_evals`](#cell_to_coset_evals)
- [`coset_evals_to_cell`](#coset_evals_to_cell)
- [Linear combinations](#linear-combinations)
- [`g2_lincomb`](#g2_lincomb)
- [FFTs](#ffts)
Expand Down Expand Up @@ -63,7 +64,9 @@ Public functions MUST accept raw bytes as input and perform the required cryptog
| Name | SSZ equivalent | Description |
| - | - | - |
| `PolynomialCoeff` | `List[BLSFieldElement, FIELD_ELEMENTS_PER_EXT_BLOB]` | A polynomial in coefficient form |
| `Cell` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL]` | The unit of blob data that can come with their own KZG proofs |
| `Coset` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL]` | The evaluation domain of a cell |
| `CosetEvals` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_CELL]` | The internal representation of a cell (the evaluations over its Coset) |
| `Cell` | `ByteVector[BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_CELL]` | The unit of blob data that can come with its own KZG proof |
| `CellID` | `uint64` | Cell identifier |
| `RowIndex` | `uint64` | Row identifier |
| `ColumnIndex` | `uint64` | Column identifier |
Expand Down Expand Up @@ -91,14 +94,33 @@ Cells are the smallest unit of blob data that can come with their own KZG proofs

### BLS12-381 helpers

#### `bytes_to_cell`
#### `cell_to_coset_evals`

```python
def bytes_to_cell(cell_bytes: Vector[Bytes32, FIELD_ELEMENTS_PER_CELL]) -> Cell:
def cell_to_coset_evals(cell: Cell) -> CosetEvals:
"""
Convert untrusted bytes into a Cell.
Convert an untrusted ``Cell`` into a trusted ``CosetEvals``.
"""
return [bytes_to_bls_field(element) for element in cell_bytes]
evals = []
for i in range(FIELD_ELEMENTS_PER_CELL):
start = i * BYTES_PER_FIELD_ELEMENT
end = (i + 1) * BYTES_PER_FIELD_ELEMENT
value = bytes_to_bls_field(cell[start:end])
evals.append(value)
return CosetEvals(evals)
```

#### `coset_evals_to_cell`

```python
def coset_evals_to_cell(coset_evals: CosetEvals) -> Cell:
"""
Convert a trusted ``CosetEval`` into an untrusted ``Cell``.
"""
cell = []
for i in range(FIELD_ELEMENTS_PER_CELL):
cell += bls_field_to_bytes(coset_evals[i])
return Cell(cell)
```

### Linear combinations
Expand Down Expand Up @@ -306,7 +328,7 @@ Extended KZG functions for multiproofs
```python
def compute_kzg_proof_multi_impl(
polynomial_coeff: PolynomialCoeff,
zs: Sequence[BLSFieldElement]) -> Tuple[KZGProof, Sequence[BLSFieldElement]]:
zs: Coset) -> Tuple[KZGProof, CosetEvals]:
"""
Compute a KZG multi-evaluation proof for a set of `k` points.

Expand Down Expand Up @@ -336,8 +358,8 @@ def compute_kzg_proof_multi_impl(

```python
def verify_kzg_proof_multi_impl(commitment: KZGCommitment,
zs: Sequence[BLSFieldElement],
ys: Sequence[BLSFieldElement],
zs: Coset,
ys: CosetEvals,
proof: KZGProof) -> bool:
"""
Helper function that verifies a KZG multiproof
Expand All @@ -362,15 +384,15 @@ def verify_kzg_proof_multi_impl(commitment: KZGCommitment,
#### `coset_for_cell`

```python
def coset_for_cell(cell_id: CellID) -> Cell:
def coset_for_cell(cell_id: CellID) -> Coset:
"""
Get the coset for a given ``cell_id``
"""
assert cell_id < CELLS_PER_EXT_BLOB
roots_of_unity_brp = bit_reversal_permutation(
compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB)
)
return Cell(roots_of_unity_brp[FIELD_ELEMENTS_PER_CELL * cell_id:FIELD_ELEMENTS_PER_CELL * (cell_id + 1)])
return Coset(roots_of_unity_brp[FIELD_ELEMENTS_PER_CELL * cell_id:FIELD_ELEMENTS_PER_CELL * (cell_id + 1)])
```

## Cells
Expand Down Expand Up @@ -399,7 +421,7 @@ def compute_cells_and_proofs(blob: Blob) -> Tuple[
for i in range(CELLS_PER_EXT_BLOB):
coset = coset_for_cell(i)
proof, ys = compute_kzg_proof_multi_impl(polynomial_coeff, coset)
cells.append(ys)
cells.append(coset_evals_to_cell(ys))
proofs.append(proof)

return cells, proofs
Expand All @@ -420,8 +442,12 @@ def compute_cells(blob: Blob) -> Vector[Cell, CELLS_PER_EXT_BLOB]:
extended_data = fft_field(polynomial_coeff + [0] * FIELD_ELEMENTS_PER_BLOB,
compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB))
extended_data_rbo = bit_reversal_permutation(extended_data)
return [extended_data_rbo[i * FIELD_ELEMENTS_PER_CELL:(i + 1) * FIELD_ELEMENTS_PER_CELL]
for i in range(CELLS_PER_EXT_BLOB)]
cells = []
for cell_id in range(CELLS_PER_EXT_BLOB):
start = cell_id * FIELD_ELEMENTS_PER_CELL
end = (cell_id + 1) * FIELD_ELEMENTS_PER_CELL
cells.append(coset_evals_to_cell(extended_data_rbo[start:end]))
return cells
```

### Cell verification
Expand All @@ -431,7 +457,7 @@ def compute_cells(blob: Blob) -> Vector[Cell, CELLS_PER_EXT_BLOB]:
```python
def verify_cell_proof(commitment_bytes: Bytes48,
cell_id: CellID,
cell_bytes: Vector[Bytes32, FIELD_ELEMENTS_PER_CELL],
cell: Cell,
proof_bytes: Bytes48) -> bool:
"""
Check a cell proof
Expand All @@ -443,7 +469,7 @@ def verify_cell_proof(commitment_bytes: Bytes48,
return verify_kzg_proof_multi_impl(
bytes_to_kzg_commitment(commitment_bytes),
coset,
bytes_to_cell(cell_bytes),
cell_to_coset_evals(cell),
bytes_to_kzg_proof(proof_bytes))
```

Expand All @@ -453,7 +479,7 @@ def verify_cell_proof(commitment_bytes: Bytes48,
def verify_cell_proof_batch(row_commitments_bytes: Sequence[Bytes48],
row_indices: Sequence[RowIndex],
column_indices: Sequence[ColumnIndex],
cells_bytes: Sequence[Vector[Bytes32, FIELD_ELEMENTS_PER_CELL]],
cells: Sequence[Cell],
proofs_bytes: Sequence[Bytes48]) -> bool:
"""
Verify a set of cells, given their corresponding proofs and their coordinates (row_id, column_id) in the blob
Expand All @@ -469,19 +495,19 @@ def verify_cell_proof_batch(row_commitments_bytes: Sequence[Bytes48],

Public method.
"""
assert len(cells_bytes) == len(proofs_bytes) == len(row_indices) == len(column_indices)
assert len(cells) == len(proofs_bytes) == len(row_indices) == len(column_indices)

# Get commitments via row IDs
commitments_bytes = [row_commitments_bytes[row_index] for row_index in row_indices]

# 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]
cosets_evals = [cell_to_coset_evals(cell) for cell in cells]
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_index), cell, proof)
for commitment, column_index, cell, proof in zip(commitments, column_indices, cells, proofs)
verify_kzg_proof_multi_impl(commitment, coset_for_cell(column_index), coset_evals, proof)
for commitment, column_index, coset_evals, proof in zip(commitments, column_indices, cosets_evals, proofs)
)
```

Expand Down Expand Up @@ -598,8 +624,7 @@ def recover_original_data(eval_shifted_extended_evaluation: Sequence[BLSFieldEle
### `recover_all_cells`

```python
def recover_all_cells(cell_ids: Sequence[CellID],
cells_bytes: Sequence[Vector[Bytes32, FIELD_ELEMENTS_PER_CELL]]) -> Sequence[Cell]:
def recover_all_cells(cell_ids: Sequence[CellID], cells: Sequence[Cell]) -> Sequence[Cell]:
"""
Recover all of the cells in the extended blob from FIELD_ELEMENTS_PER_EXT_BLOB evaluations,
half of which can be missing.
Expand All @@ -611,7 +636,7 @@ def recover_all_cells(cell_ids: Sequence[CellID],

Public method.
"""
assert len(cell_ids) == len(cells_bytes)
assert len(cell_ids) == len(cells)
# Check we have enough cells to be able to perform the reconstruction
assert CELLS_PER_EXT_BLOB / 2 <= len(cell_ids) <= CELLS_PER_EXT_BLOB
# Check for duplicates
Expand All @@ -620,15 +645,15 @@ def recover_all_cells(cell_ids: Sequence[CellID],
# Get the extended domain
roots_of_unity_extended = compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB)

# Convert from bytes to cells
cells = [bytes_to_cell(cell_bytes) for cell_bytes in cells_bytes]
# Convert cells to coset evals
cosets_evals = [cell_to_coset_evals(cell) for cell in cells]

missing_cell_ids = [cell_id for cell_id in range(CELLS_PER_EXT_BLOB) if cell_id not in cell_ids]
zero_poly_coeff, zero_poly_eval, zero_poly_eval_brp = construct_vanishing_polynomial(missing_cell_ids)

eval_shifted_extended_evaluation, eval_shifted_zero_poly, shift_inv = recover_shifted_data(
cell_ids,
cells,
cosets_evals,
zero_poly_eval,
zero_poly_coeff,
roots_of_unity_extended,
Expand All @@ -641,14 +666,14 @@ def recover_all_cells(cell_ids: Sequence[CellID],
roots_of_unity_extended,
)

for cell_id, cell in zip(cell_ids, cells):
for cell_id, coset_evals in zip(cell_ids, cosets_evals):
start = cell_id * FIELD_ELEMENTS_PER_CELL
end = (cell_id + 1) * FIELD_ELEMENTS_PER_CELL
assert reconstructed_data[start:end] == cell
assert reconstructed_data[start:end] == coset_evals

reconstructed_data_as_cells = [
reconstructed_data[i * FIELD_ELEMENTS_PER_CELL:(i + 1) * FIELD_ELEMENTS_PER_CELL]
coset_evals_to_cell(reconstructed_data[i * FIELD_ELEMENTS_PER_CELL:(i + 1) * FIELD_ELEMENTS_PER_CELL])
for i in range(CELLS_PER_EXT_BLOB)]

return reconstructed_data_as_cells
```
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def test_compute_extended_matrix(spec):
for blob_index, row in enumerate(rows):
extended_blob = []
for cell in row:
extended_blob.extend(cell)
extended_blob.extend(spec.cell_to_coset_evals(cell))
blob_part = extended_blob[0:len(extended_blob) // 2]
blob = b''.join([spec.bls_field_to_bytes(x) for x in blob_part])
assert blob == input_blobs[blob_index]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,10 @@ def test_verify_cell_proof(spec):
commitment = spec.blob_to_kzg_commitment(blob)
cells, proofs = spec.compute_cells_and_proofs(blob)

cells_bytes = [[spec.bls_field_to_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])
assert spec.verify_cell_proof(commitment, cell_id, cells[cell_id], proofs[cell_id])
cell_id = 1
assert spec.verify_cell_proof(commitment, cell_id, cells_bytes[cell_id], proofs[cell_id])
assert spec.verify_cell_proof(commitment, cell_id, cells[cell_id], proofs[cell_id])


@with_eip7594_and_later
Expand All @@ -51,15 +49,14 @@ def test_verify_cell_proof_batch(spec):
blob = get_sample_blob(spec)
commitment = spec.blob_to_kzg_commitment(blob)
cells, proofs = spec.compute_cells_and_proofs(blob)
cells_bytes = [[spec.bls_field_to_bytes(element) for element in cell] for cell in cells]

assert len(cells) == len(proofs)

assert spec.verify_cell_proof_batch(
row_commitments_bytes=[commitment],
row_indices=[0, 0],
column_indices=[0, 4],
cells_bytes=[cells_bytes[0], cells_bytes[4]],
cells=[cells[0], cells[4]],
proofs_bytes=[proofs[0], proofs[4]],
)

Expand All @@ -75,12 +72,9 @@ def test_recover_all_cells(spec):

# Get the data we will be working with
blob = get_sample_blob(spec)
# Get the data in evaluation form
original_polynomial = spec.blob_to_polynomial(blob)

# Extend data with Reed-Solomon and split the extended data in cells
cells = spec.compute_cells(blob)
cells_bytes = [[spec.bls_field_to_bytes(element) for element in cell] for cell in cells]

# Compute the cells we will be recovering from
cell_ids = []
Expand All @@ -91,14 +85,15 @@ def test_recover_all_cells(spec):
j = rng.randint(0, spec.CELLS_PER_EXT_BLOB - 1)
cell_ids.append(j)
# Now the cells themselves
known_cells_bytes = [cells_bytes[cell_id] for cell_id in cell_ids]
known_cells = [cells[cell_id] for cell_id in cell_ids]

# Recover all of the cells
recovered_cells = spec.recover_all_cells(cell_ids, known_cells_bytes)
recovered_cells = spec.recover_all_cells(cell_ids, known_cells)
recovered_data = [x for xs in recovered_cells for x in xs]

# Check that the original data match the non-extended portion of the recovered data
assert original_polynomial == recovered_data[:len(recovered_data) // 2]
blob_byte_array = [b for b in blob]
assert blob_byte_array == recovered_data[:len(recovered_data) // 2]

# Check that the recovered cells match the original cells
assert cells == recovered_cells
Expand Down
Loading