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

verify_cell_kzg_proof_batch(): Abstract commitment to interpolation poly #494

Merged
merged 9 commits into from
Aug 16, 2024
256 changes: 155 additions & 101 deletions src/eip7594/eip7594.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,154 @@ static C_KZG_RET compute_r_powers_for_verify_cell_kzg_proof_batch(
return ret;
}

/**
* Aggregate columns, compute the sum of interpolation polynomials, and commit to the result.
*
* This function computes `RLI = [sum_k r^k interpolation_poly_k(s)]` from the spec.
*
* @param[out] commitment_out Commitment to the aggregated interpolation poly
* @param[in] r_powers Precomputed powers of the random challenge
* @param[in] cell_indices Indices of the cells
* @param[in] cells Array of cells
* @param[in] num_cells Number of cells
* @param[in] s The trusted setup
*/
static C_KZG_RET compute_commitment_to_aggregated_interpolation_poly(
g1_t *commitment_out,
const fr_t *r_powers,
const uint64_t *cell_indices,
const Cell *cells,
uint64_t num_cells,
const KZGSettings *s
) {
C_KZG_RET ret;
bool *is_cell_used = NULL;
fr_t *aggregated_column_cells = NULL;
fr_t *column_interpolation_poly = NULL;
fr_t *aggregated_interpolation_poly = NULL;

////////////////////////////////////////////////////////////////////////////////////////////////
// Array allocations
////////////////////////////////////////////////////////////////////////////////////////////////

ret = new_bool_array(&is_cell_used, FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&aggregated_column_cells, FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&column_interpolation_poly, FIELD_ELEMENTS_PER_CELL);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&aggregated_interpolation_poly, FIELD_ELEMENTS_PER_CELL);
if (ret != C_KZG_OK) goto out;

////////////////////////////////////////////////////////////////////////////////////////////////
// Aggregates cells from the same column
////////////////////////////////////////////////////////////////////////////////////////////////

/* Start with zeroed out columns */
for (size_t i = 0; i < CELLS_PER_EXT_BLOB; i++) {
for (size_t j = 0; j < FIELD_ELEMENTS_PER_CELL; j++) {
size_t index = i * FIELD_ELEMENTS_PER_CELL + j;
aggregated_column_cells[index] = FR_ZERO;
is_cell_used[index] = false;
}
}

/* Scale each cell with the corresponding powers of the random challenge */
for (uint64_t i = 0; i < num_cells; i++) {
for (size_t j = 0; j < FIELD_ELEMENTS_PER_CELL; j++) {
fr_t original_fr, scaled_fr;

/* Get the field element at this offset */
size_t offset = j * BYTES_PER_FIELD_ELEMENT;
ret = bytes_to_bls_field(&original_fr, (const Bytes32 *)&cells[i].bytes[offset]);
if (ret != C_KZG_OK) goto out;

/* Scale the field element by the power for that cell */
blst_fr_mul(&scaled_fr, &original_fr, &r_powers[i]);

/* Aggregate the scaled field element into the column */
size_t index = cell_indices[i] * FIELD_ELEMENTS_PER_CELL + j;
blst_fr_add(
&aggregated_column_cells[index], &aggregated_column_cells[index], &scaled_fr
);

/* Mark the cell as being used */
is_cell_used[index] = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this PR only moved this, this is a comment that probably should be addressed in a separate PR:
We set is_cell_used at indices which are not a multiple of FIELD_ELEMENTS_PER_CELL, but only read it at multiples of FIELD_ELEMENTS_PER_CELL.
This seems unnecessary. Why not just set is_cell_used[cell_indices[i]] = true in the outer loop and making the is_cell_used array smaller?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Let's handle this on another PR.

}
}

////////////////////////////////////////////////////////////////////////////////////////////////
// Compute interpolation polynomials using the aggregated cells
////////////////////////////////////////////////////////////////////////////////////////////////

/* Start with a zeroed out poly */
for (size_t i = 0; i < FIELD_ELEMENTS_PER_CELL; i++) {
aggregated_interpolation_poly[i] = FR_ZERO;
}

/* Interpolate each column */
for (size_t i = 0; i < CELLS_PER_EXT_BLOB; i++) {
/* Offset to the first cell for this column */
size_t index = i * FIELD_ELEMENTS_PER_CELL;

/* We only care about initialized cells */
if (!is_cell_used[index]) continue;

/* We don't need to copy this because it's not used again */
ret = bit_reversal_permutation(
&aggregated_column_cells[index], sizeof(fr_t), FIELD_ELEMENTS_PER_CELL
);
if (ret != C_KZG_OK) goto out;

/*
* Get interpolation polynomial for this column. To do so we first do an IDFT over the roots
* of unity and then we scale by the coset factor. We can't do an IDFT directly over the
* coset because it's not a subgroup.
*/
ret = fr_ifft(
column_interpolation_poly, &aggregated_column_cells[index], FIELD_ELEMENTS_PER_CELL, s
);
if (ret != C_KZG_OK) goto out;

/*
* To unscale, divide by the coset. It's faster to multiply with the inverse. We can skip
* the first iteration because its dividing by one.
*/
uint64_t pos = reverse_bits_limited(CELLS_PER_EXT_BLOB, i);
fr_t inv_coset_factor;
blst_fr_eucl_inverse(&inv_coset_factor, &s->roots_of_unity[pos]);
shift_poly(column_interpolation_poly, FIELD_ELEMENTS_PER_CELL, &inv_coset_factor);

/* Update the aggregated poly */
for (size_t k = 0; k < FIELD_ELEMENTS_PER_CELL; k++) {
blst_fr_add(
&aggregated_interpolation_poly[k],
&aggregated_interpolation_poly[k],
&column_interpolation_poly[k]
);
}
}

////////////////////////////////////////////////////////////////////////////////////////////////
// Commit to the aggregated interpolation polynomial
////////////////////////////////////////////////////////////////////////////////////////////////

ret = g1_lincomb_fast(
commitment_out,
s->g1_values_monomial,
aggregated_interpolation_poly,
FIELD_ELEMENTS_PER_CELL
);
if (ret != C_KZG_OK) goto out;

out:
c_kzg_free(is_cell_used);
c_kzg_free(aggregated_column_cells);
c_kzg_free(column_interpolation_poly);
c_kzg_free(aggregated_interpolation_poly);
return ret;
}

/**
* Given some cells, verify that their proofs are valid.
*
Expand All @@ -477,7 +625,7 @@ C_KZG_RET verify_cell_kzg_proof_batch(
const KZGSettings *s
) {
C_KZG_RET ret;
g1_t evaluation;
g1_t interpolation_poly_commit;
g1_t final_g1_sum;
g1_t proof_lincomb;
g1_t weighted_proof_lincomb;
Expand All @@ -487,10 +635,6 @@ C_KZG_RET verify_cell_kzg_proof_batch(
/* Arrays */
Bytes48 *unique_commitments = NULL;
uint64_t *commitment_indices = NULL;
bool *is_cell_used = NULL;
fr_t *aggregated_column_cells = NULL;
fr_t *aggregated_interpolation_poly = NULL;
fr_t *column_interpolation_poly = NULL;
fr_t *commitment_weights = NULL;
fr_t *r_powers = NULL;
fr_t *weighted_powers_of_r = NULL;
Expand Down Expand Up @@ -537,14 +681,6 @@ C_KZG_RET verify_cell_kzg_proof_batch(
// Array allocations
////////////////////////////////////////////////////////////////////////////////////////////////

ret = new_bool_array(&is_cell_used, FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&aggregated_column_cells, FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&aggregated_interpolation_poly, FIELD_ELEMENTS_PER_CELL);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&column_interpolation_poly, FIELD_ELEMENTS_PER_CELL);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&commitment_weights, num_commitments);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&r_powers, num_cells);
Expand Down Expand Up @@ -615,95 +751,17 @@ C_KZG_RET verify_cell_kzg_proof_batch(
if (ret != C_KZG_OK) goto out;

////////////////////////////////////////////////////////////////////////////////////////////////
// Compute aggregated columns
////////////////////////////////////////////////////////////////////////////////////////////////

/* Start with zeroed out columns */
for (size_t i = 0; i < CELLS_PER_EXT_BLOB; i++) {
for (size_t j = 0; j < FIELD_ELEMENTS_PER_CELL; j++) {
size_t index = i * FIELD_ELEMENTS_PER_CELL + j;
aggregated_column_cells[index] = FR_ZERO;
}
}

/* Scale each cell's data points */
for (size_t i = 0; i < num_cells; i++) {
for (size_t j = 0; j < FIELD_ELEMENTS_PER_CELL; j++) {
fr_t cell_fr, scaled_fr;
size_t offset = j * BYTES_PER_FIELD_ELEMENT;
ret = bytes_to_bls_field(&cell_fr, (const Bytes32 *)&cells[i].bytes[offset]);
if (ret != C_KZG_OK) goto out;
blst_fr_mul(&scaled_fr, &cell_fr, &r_powers[i]);
size_t index = cell_indices[i] * FIELD_ELEMENTS_PER_CELL + j;
blst_fr_add(
&aggregated_column_cells[index], &aggregated_column_cells[index], &scaled_fr
);

/* Mark the cell as being used */
is_cell_used[index] = true;
}
}

////////////////////////////////////////////////////////////////////////////////////////////////
// Compute sum of the interpolation polynomials
// Commmit to aggregated interpolation polynomial
////////////////////////////////////////////////////////////////////////////////////////////////

/* Start with a zeroed out poly */
for (size_t i = 0; i < FIELD_ELEMENTS_PER_CELL; i++) {
aggregated_interpolation_poly[i] = FR_ZERO;
}

/* Interpolate each column */
for (size_t i = 0; i < CELLS_PER_EXT_BLOB; i++) {
/* Offset to the first cell for this column */
size_t index = i * FIELD_ELEMENTS_PER_CELL;

/* We only care about initialized cells */
if (!is_cell_used[index]) continue;

/* We don't need to copy this because it's not used again */
ret = bit_reversal_permutation(
&aggregated_column_cells[index], sizeof(fr_t), FIELD_ELEMENTS_PER_CELL
);
if (ret != C_KZG_OK) goto out;

/*
* Get interpolation polynomial for this column. To do so we first do an IDFT over the roots
* of unity and then we scale by the coset factor. We can't do an IDFT directly over the
* coset because it's not a subgroup.
*/
ret = fr_ifft(
column_interpolation_poly, &aggregated_column_cells[index], FIELD_ELEMENTS_PER_CELL, s
);
if (ret != C_KZG_OK) goto out;

/*
* To unscale, divide by the coset. It's faster to multiply with the inverse. We can skip
* the first iteration because its dividing by one.
*/
uint64_t pos = reverse_bits_limited(CELLS_PER_EXT_BLOB, i);
fr_t inv_coset_factor;
blst_fr_eucl_inverse(&inv_coset_factor, &s->roots_of_unity[pos]);
shift_poly(column_interpolation_poly, FIELD_ELEMENTS_PER_CELL, &inv_coset_factor);

/* Update the aggregated poly */
for (size_t k = 0; k < FIELD_ELEMENTS_PER_CELL; k++) {
blst_fr_add(
&aggregated_interpolation_poly[k],
&aggregated_interpolation_poly[k],
&column_interpolation_poly[k]
);
}
}

/* Commit to the final aggregated interpolation polynomial */
ret = g1_lincomb_fast(
&evaluation, s->g1_values_monomial, aggregated_interpolation_poly, FIELD_ELEMENTS_PER_CELL
/* Aggregate cells from same columns, sum interpolation polynomials, and commit */
ret = compute_commitment_to_aggregated_interpolation_poly(
&interpolation_poly_commit, r_powers, cell_indices, cells, num_cells, s
);
if (ret != C_KZG_OK) goto out;

blst_p1_cneg(&evaluation, true);
blst_p1_add(&final_g1_sum, &final_g1_sum, &evaluation);
blst_p1_cneg(&interpolation_poly_commit, true);
blst_p1_add(&final_g1_sum, &final_g1_sum, &interpolation_poly_commit);

////////////////////////////////////////////////////////////////////////////////////////////////
// Compute sum of the proofs scaled by the coset factors
Expand All @@ -730,10 +788,6 @@ C_KZG_RET verify_cell_kzg_proof_batch(
out:
c_kzg_free(unique_commitments);
c_kzg_free(commitment_indices);
c_kzg_free(is_cell_used);
c_kzg_free(aggregated_column_cells);
c_kzg_free(aggregated_interpolation_poly);
c_kzg_free(column_interpolation_poly);
c_kzg_free(commitment_weights);
c_kzg_free(r_powers);
c_kzg_free(weighted_powers_of_r);
Expand Down
Loading