#include "eip7594/recovery.h"
#include "common/alloc.h"
#include "common/fr.h"
#include "common/utils.h"
#include "eip7594/cell.h"
#include "eip7594/fft.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
static C_KZG_RET compute_vanishing_polynomial_from_roots(
fr_t *poly, size_t *poly_len, const fr_t *roots, size_t roots_len
) {
fr_t neg_root;
if (roots_len == 0) {
return C_KZG_BADARGS;
}
fr_neg(&poly[0], &roots[0]);
for (size_t i = 1; i < roots_len; i++) {
fr_neg(&neg_root, &roots[i]);
poly[i] = neg_root;
fr_add(&poly[i], &poly[i], &poly[i - 1]);
for (size_t j = i - 1; j > 0; j--) {
fr_mul(&poly[j], &poly[j], &neg_root);
fr_add(&poly[j], &poly[j], &poly[j - 1]);
}
fr_mul(&poly[0], &poly[0], &neg_root);
}
poly[roots_len] = FR_ONE;
*poly_len = roots_len + 1;
return C_KZG_OK;
}
static C_KZG_RET vanishing_polynomial_for_missing_cells(
fr_t *vanishing_poly,
const uint64_t *missing_cell_indices,
size_t len_missing_cells,
const KZGSettings *s
) {
C_KZG_RET ret;
fr_t *roots = NULL;
fr_t *short_vanishing_poly = NULL;
size_t short_vanishing_poly_len = 0;
if (len_missing_cells == 0 || len_missing_cells >= CELLS_PER_EXT_BLOB) {
ret = C_KZG_BADARGS;
goto out;
}
ret = new_fr_array(&roots, len_missing_cells);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&short_vanishing_poly, (len_missing_cells + 1));
if (ret != C_KZG_OK) goto out;
size_t stride = FIELD_ELEMENTS_PER_EXT_BLOB / CELLS_PER_EXT_BLOB;
for (size_t i = 0; i < len_missing_cells; i++) {
roots[i] = s->roots_of_unity[missing_cell_indices[i] * stride];
}
ret = compute_vanishing_polynomial_from_roots(
short_vanishing_poly, &short_vanishing_poly_len, roots, len_missing_cells
);
if (ret != C_KZG_OK) goto out;
for (size_t i = 0; i < FIELD_ELEMENTS_PER_EXT_BLOB; i++) {
vanishing_poly[i] = FR_ZERO;
}
for (size_t i = 0; i < short_vanishing_poly_len; i++) {
vanishing_poly[i * FIELD_ELEMENTS_PER_CELL] = short_vanishing_poly[i];
}
out:
c_kzg_free(roots);
c_kzg_free(short_vanishing_poly);
return ret;
}
static bool is_in_array(const uint64_t *arr, size_t arr_size, uint64_t value) {
for (size_t i = 0; i < arr_size; i++) {
if (arr[i] == value) {
return true;
}
}
return false;
}
C_KZG_RET recover_cells(
fr_t *reconstructed_data_out,
const uint64_t *cell_indices,
size_t num_cells,
fr_t *cells,
const KZGSettings *s
) {
C_KZG_RET ret;
uint64_t *missing_cell_indices = NULL;
fr_t *vanishing_poly_eval = NULL;
fr_t *vanishing_poly_coeff = NULL;
fr_t *extended_evaluation_times_zero = NULL;
fr_t *extended_evaluation_times_zero_coeffs = NULL;
fr_t *extended_evaluations_over_coset = NULL;
fr_t *vanishing_poly_over_coset = NULL;
fr_t *reconstructed_poly_coeff = NULL;
fr_t *cells_brp = NULL;
ret = c_kzg_calloc(
(void **)&missing_cell_indices, FIELD_ELEMENTS_PER_EXT_BLOB, sizeof(uint64_t)
);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&vanishing_poly_eval, FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&vanishing_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&extended_evaluation_times_zero, FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&extended_evaluation_times_zero_coeffs, FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&extended_evaluations_over_coset, FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&vanishing_poly_over_coset, FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&reconstructed_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&cells_brp, FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out;
memcpy(cells_brp, cells, FIELD_ELEMENTS_PER_EXT_BLOB * sizeof(fr_t));
ret = bit_reversal_permutation(cells_brp, sizeof(fr_t), FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out;
size_t len_missing = 0;
for (size_t i = 0; i < CELLS_PER_EXT_BLOB; i++) {
if (!is_in_array(cell_indices, num_cells, i)) {
uint64_t brp_i = reverse_bits_limited(CELLS_PER_EXT_BLOB, i);
missing_cell_indices[len_missing++] = brp_i;
}
}
assert(CELLS_PER_EXT_BLOB - len_missing >= CELLS_PER_BLOB);
ret = vanishing_polynomial_for_missing_cells(
vanishing_poly_coeff, missing_cell_indices, len_missing, s
);
if (ret != C_KZG_OK) goto out;
ret = fr_fft(vanishing_poly_eval, vanishing_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB, s);
if (ret != C_KZG_OK) goto out;
for (size_t i = 0; i < FIELD_ELEMENTS_PER_EXT_BLOB; i++) {
fr_mul(&extended_evaluation_times_zero[i], &cells_brp[i], &vanishing_poly_eval[i]);
}
ret = fr_ifft(
extended_evaluation_times_zero_coeffs,
extended_evaluation_times_zero,
FIELD_ELEMENTS_PER_EXT_BLOB,
s
);
if (ret != C_KZG_OK) goto out;
ret = coset_fft(
extended_evaluations_over_coset,
extended_evaluation_times_zero_coeffs,
FIELD_ELEMENTS_PER_EXT_BLOB,
s
);
if (ret != C_KZG_OK) goto out;
ret = coset_fft(
vanishing_poly_over_coset, vanishing_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB, s
);
if (ret != C_KZG_OK) goto out;
for (size_t i = 0; i < FIELD_ELEMENTS_PER_EXT_BLOB; i++) {
fr_div(
&extended_evaluations_over_coset[i],
&extended_evaluations_over_coset[i],
&vanishing_poly_over_coset[i]
);
}
ret = coset_ifft(
reconstructed_poly_coeff, extended_evaluations_over_coset, FIELD_ELEMENTS_PER_EXT_BLOB, s
);
if (ret != C_KZG_OK) goto out;
ret = fr_fft(reconstructed_data_out, reconstructed_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB, s);
if (ret != C_KZG_OK) goto out;
ret = bit_reversal_permutation(
reconstructed_data_out, sizeof(fr_t), FIELD_ELEMENTS_PER_EXT_BLOB
);
if (ret != C_KZG_OK) goto out;
out:
c_kzg_free(missing_cell_indices);
c_kzg_free(vanishing_poly_eval);
c_kzg_free(extended_evaluation_times_zero);
c_kzg_free(extended_evaluation_times_zero_coeffs);
c_kzg_free(extended_evaluations_over_coset);
c_kzg_free(vanishing_poly_over_coset);
c_kzg_free(reconstructed_poly_coeff);
c_kzg_free(vanishing_poly_coeff);
c_kzg_free(cells_brp);
return ret;
}