diff --git a/specs/_features/eip7594/polynomial-commitments-sampling.md b/specs/_features/eip7594/polynomial-commitments-sampling.md index 601144abd..d3a8cd23b 100644 --- a/specs/_features/eip7594/polynomial-commitments-sampling.md +++ b/specs/_features/eip7594/polynomial-commitments-sampling.md @@ -12,6 +12,8 @@ - [Preset](#preset) - [Cells](#cells) - [Helper functions](#helper-functions) + - [BLS12-381 helpers](#bls12-381-helpers) + - [`bytes_to_cell`](#bytes_to_cell) - [Linear combinations](#linear-combinations) - [`g2_lincomb`](#g2_lincomb) - [FFTs](#ffts) @@ -81,6 +83,18 @@ Cells are the smallest unit of blob data that can come with their own KZG proofs ## Helper functions +### 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] +``` + ### Linear combinations #### `g2_lincomb` @@ -244,7 +258,7 @@ def interpolate_polynomialcoeff(xs: Sequence[BLSFieldElement], ys: Sequence[BLSF summand, [(- int(weight_adjustment) * int(xs[j])) % BLS_MODULUS, weight_adjustment] ) r = add_polynomialcoeff(r, summand) - + return r ``` @@ -332,7 +346,7 @@ def verify_kzg_proof_multi_impl(commitment: KZGCommitment, #### `coset_for_cell` ```python -def coset_for_cell(cell_id: int) -> Cell: +def coset_for_cell(cell_id: CellID) -> Cell: """ Get the coset for a given ``cell_id`` """ @@ -387,7 +401,7 @@ def compute_cells(blob: Blob) -> Vector[Cell, CELLS_PER_BLOB]: polynomial = blob_to_polynomial(blob) polynomial_coeff = polynomial_eval_to_coeff(polynomial) - extended_data = fft_field(polynomial_coeff + [0] * FIELD_ELEMENTS_PER_BLOB, + extended_data = fft_field(polynomial_coeff + [0] * FIELD_ELEMENTS_PER_BLOB, compute_roots_of_unity(2 * FIELD_ELEMENTS_PER_BLOB)) extended_data_rbo = bit_reversal_permutation(extended_data) return [extended_data_rbo[i * FIELD_ELEMENTS_PER_CELL:(i + 1) * FIELD_ELEMENTS_PER_CELL] @@ -399,10 +413,10 @@ def compute_cells(blob: Blob) -> Vector[Cell, CELLS_PER_BLOB]: #### `verify_cell_proof` ```python -def verify_cell_proof(commitment: KZGCommitment, - cell_id: int, - cell: Cell, - proof: KZGProof) -> bool: +def verify_cell_proof(commitment_bytes: Bytes48, + cell_id: CellID, + cell_bytes: Vector[Bytes32, FIELD_ELEMENTS_PER_CELL], + proof_bytes: Bytes48) -> bool: """ Check a cell proof @@ -410,19 +424,26 @@ def verify_cell_proof(commitment: KZGCommitment, """ coset = coset_for_cell(cell_id) - return verify_kzg_proof_multi_impl(commitment, coset, cell, proof) + return verify_kzg_proof_multi_impl( + bytes_to_kzg_commitment(commitment_bytes), + coset, + bytes_to_cell(cell_bytes), + bytes_to_kzg_proof(proof_bytes)) ``` #### `verify_cell_proof_batch` ```python -def verify_cell_proof_batch(row_commitments: Sequence[KZGCommitment], - row_ids: Sequence[int], - column_ids: Sequence[int], - cells: Sequence[Cell], - proofs: Sequence[KZGProof]) -> bool: +def verify_cell_proof_batch(row_commitments_bytes: Sequence[Bytes48], + row_ids: Sequence[uint64], + column_ids: Sequence[uint64], + cells_bytes: Sequence[Vector[Bytes32, FIELD_ELEMENTS_PER_CELL]], + proofs_bytes: Sequence[Bytes48]) -> bool: """ - Check multiple cell proofs. This function implements the naive algorithm of checking every cell + 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 individually; an efficient algorithm can be found here: https://ethresear.ch/t/a-universal-verification-equation-for-data-availability-sampling/13240 @@ -432,10 +453,16 @@ def verify_cell_proof_batch(row_commitments: Sequence[KZGCommitment], Public method. """ + assert len(cells_bytes) == len(proofs_bytes) == len(row_ids) == len(column_ids) # Get commitments via row IDs - commitments = [row_commitments[row_id] for row_id in row_ids] - + commitments_bytes = [row_commitments_bytes[row_id] for row_id in row_ids] + + # 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] + return all( verify_kzg_proof_multi_impl(commitment, coset_for_cell(column_id), cell, proof) for commitment, column_id, cell, proof in zip(commitments, column_ids, cells, proofs) @@ -447,7 +474,8 @@ def verify_cell_proof_batch(row_commitments: Sequence[KZGCommitment], ### `recover_polynomial` ```python -def recover_polynomial(cell_ids: Sequence[CellID], cells: Sequence[Cell]) -> Polynomial: +def recover_polynomial(cell_ids: Sequence[CellID], + cells_bytes: Sequence[Vector[Bytes32, FIELD_ELEMENTS_PER_CELL]]) -> Polynomial: """ Recovers a polynomial from 2 * FIELD_ELEMENTS_PER_CELL evaluations, half of which can be missing. @@ -457,7 +485,10 @@ def recover_polynomial(cell_ids: Sequence[CellID], cells: Sequence[Cell]) -> Pol Public method. """ - assert len(cell_ids) == len(cells) + assert len(cell_ids) == len(cells_bytes) + + cells = [bytes_to_cell(cell_bytes) for cell_bytes in cells_bytes] + assert len(cells) >= CELLS_PER_BLOB // 2 missing_cell_ids = [cell_id for cell_id in range(CELLS_PER_BLOB) if cell_id not in cell_ids] roots_of_unity_reduced = compute_roots_of_unity(CELLS_PER_BLOB) @@ -506,7 +537,7 @@ def recover_polynomial(cell_ids: Sequence[CellID], cells: Sequence[Cell]) -> Pol 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) - + eval_shifted_reconstructed_poly = [ div(a, b) for a, b in zip(eval_shifted_extended_evaluation, eval_shifted_zero_poly) diff --git a/specs/deneb/polynomial-commitments.md b/specs/deneb/polynomial-commitments.md index d7a0bc503..33945d249 100644 --- a/specs/deneb/polynomial-commitments.md +++ b/specs/deneb/polynomial-commitments.md @@ -578,7 +578,7 @@ def verify_blob_kzg_proof_batch(blobs: Sequence[Blob], """ assert len(blobs) == len(commitments_bytes) == len(proofs_bytes) - + commitments, evaluation_challenges, ys, proofs = [], [], [], [] for blob, commitment_bytes, proof_bytes in zip(blobs, commitments_bytes, proofs_bytes): assert len(blob) == BYTES_PER_BLOB diff --git a/tests/core/pyspec/eth2spec/test/eip7594/unittests/polynomial_commitments/test_polynomial_commitments.py b/tests/core/pyspec/eth2spec/test/eip7594/unittests/polynomial_commitments/test_polynomial_commitments.py index d3e848772..0b60cfd28 100644 --- a/tests/core/pyspec/eth2spec/test/eip7594/unittests/polynomial_commitments/test_polynomial_commitments.py +++ b/tests/core/pyspec/eth2spec/test/eip7594/unittests/polynomial_commitments/test_polynomial_commitments.py @@ -10,6 +10,10 @@ from eth2spec.test.helpers.sharding import ( from eth2spec.utils.bls import BLS_MODULUS +def field_element_bytes(x): + return int.to_bytes(x % BLS_MODULUS, 32, "big") + + @with_eip7594_and_later @spec_test @single_phase @@ -34,10 +38,13 @@ 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[cell_id], proofs[cell_id]) + 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[cell_id], proofs[cell_id]) + assert spec.verify_cell_proof(commitment, cell_id, cells_bytes[cell_id], proofs[cell_id]) @with_eip7594_and_later @@ -47,13 +54,16 @@ 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 = [[field_element_bytes(element) for element in cell] for cell in cells] + + assert len(cells) == len(proofs) assert spec.verify_cell_proof_batch( - row_commitments=[commitment], - row_ids=[0], - column_ids=[0, 1], - cells=cells[0:1], - proofs=proofs, + row_commitments_bytes=[commitment], + row_ids=[0, 0], + column_ids=[0, 4], + cells_bytes=[cells_bytes[0], cells_bytes[4]], + proofs_bytes=[proofs[0], proofs[4]], ) @@ -73,10 +83,10 @@ def test_recover_polynomial(spec): # Extend data with Reed-Solomon and split the extended data in cells cells = spec.compute_cells(blob) + cells_bytes = [[field_element_bytes(element) for element in cell] for cell in cells] # Compute the cells we will be recovering from cell_ids = [] - known_cells = [] # First figure out just the indices of the cells for i in range(N_SAMPLES): j = rng.randint(0, spec.CELLS_PER_BLOB) @@ -84,10 +94,10 @@ def test_recover_polynomial(spec): j = rng.randint(0, spec.CELLS_PER_BLOB) cell_ids.append(j) # Now the cells themselves - known_cells = [cells[cell_id] for cell_id in cell_ids] + known_cells_bytes = [cells_bytes[cell_id] for cell_id in cell_ids] # Recover the data - recovered_data = spec.recover_polynomial(cell_ids, known_cells) + recovered_data = spec.recover_polynomial(cell_ids, known_cells_bytes) # Check that the original data match the non-extended portion of the recovered data assert original_polynomial == recovered_data[:len(recovered_data) // 2]