#include "setup/setup.h"
#include "common/alloc.h"
#include "common/utils.h"
#include "eip7594/eip7594.h"
#include "eip7594/fft.h"
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BYTES_PER_G1 48
#define BYTES_PER_G2 96
#define NUM_G1_POINTS FIELD_ELEMENTS_PER_BLOB
#define NUM_G2_POINTS 65
static const fr_t ROOT_OF_UNITY = {
{0xa33d279ff0ccffc9L, 0x41fac79f59e91972L, 0x065d227fead1139bL, 0x71db41abda03e055L}
};
static C_KZG_RET expand_root_of_unity(fr_t *out, const fr_t *root, size_t width) {
size_t i;
if (width < 2) {
return C_KZG_BADARGS;
}
out[0] = FR_ONE;
out[1] = *root;
for (i = 2; i <= width; i++) {
fr_mul(&out[i], &out[i - 1], root);
if (fr_is_one(&out[i])) break;
}
if (i != width || !fr_is_one(&out[width])) {
return C_KZG_BADARGS;
}
return C_KZG_OK;
}
static C_KZG_RET compute_roots_of_unity(KZGSettings *s) {
C_KZG_RET ret;
ret = expand_root_of_unity(s->roots_of_unity, &ROOT_OF_UNITY, FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out;
memcpy(s->brp_roots_of_unity, s->roots_of_unity, sizeof(fr_t) * FIELD_ELEMENTS_PER_EXT_BLOB);
ret = bit_reversal_permutation(
s->brp_roots_of_unity, sizeof(fr_t), FIELD_ELEMENTS_PER_EXT_BLOB
);
if (ret != C_KZG_OK) goto out;
for (size_t i = 0; i <= FIELD_ELEMENTS_PER_EXT_BLOB; i++) {
s->reverse_roots_of_unity[i] = s->roots_of_unity[FIELD_ELEMENTS_PER_EXT_BLOB - i];
}
out:
return ret;
}
void free_trusted_setup(KZGSettings *s) {
if (s == NULL) return;
c_kzg_free(s->brp_roots_of_unity);
c_kzg_free(s->roots_of_unity);
c_kzg_free(s->reverse_roots_of_unity);
c_kzg_free(s->g1_values_monomial);
c_kzg_free(s->g1_values_lagrange_brp);
c_kzg_free(s->g2_values_monomial);
if (s->x_ext_fft_columns != NULL) {
for (size_t i = 0; i < CELLS_PER_EXT_BLOB; i++) {
c_kzg_free(s->x_ext_fft_columns[i]);
}
}
if (s->tables != NULL) {
for (size_t i = 0; i < CELLS_PER_EXT_BLOB; i++) {
c_kzg_free(s->tables[i]);
}
}
c_kzg_free(s->x_ext_fft_columns);
c_kzg_free(s->tables);
s->wbits = 0;
s->scratch_size = 0;
}
static C_KZG_RET toeplitz_part_1(g1_t *out, const g1_t *x, size_t n, const KZGSettings *s) {
C_KZG_RET ret;
size_t circulant_domain_size = n * 2;
g1_t *x_ext;
ret = new_g1_array(&x_ext, circulant_domain_size);
if (ret != C_KZG_OK) goto out;
for (size_t i = 0; i < n; i++) {
x_ext[i] = x[i];
}
for (size_t i = n; i < circulant_domain_size; i++) {
x_ext[i] = G1_IDENTITY;
}
ret = g1_fft(out, x_ext, circulant_domain_size, s);
if (ret != C_KZG_OK) goto out;
out:
c_kzg_free(x_ext);
return ret;
}
static C_KZG_RET init_fk20_multi_settings(KZGSettings *s) {
C_KZG_RET ret;
size_t circulant_domain_size;
g1_t *x = NULL;
g1_t *points = NULL;
blst_p1_affine *p_affine = NULL;
bool precompute = s->wbits != 0;
circulant_domain_size = 2 * CELLS_PER_BLOB;
if (FIELD_ELEMENTS_PER_CELL >= NUM_G2_POINTS) {
ret = C_KZG_BADARGS;
goto out;
}
ret = new_g1_array(&x, CELLS_PER_BLOB);
if (ret != C_KZG_OK) goto out;
ret = new_g1_array(&points, circulant_domain_size);
if (ret != C_KZG_OK) goto out;
ret = c_kzg_calloc((void **)&s->x_ext_fft_columns, circulant_domain_size, sizeof(void *));
if (ret != C_KZG_OK) goto out;
for (size_t i = 0; i < circulant_domain_size; i++) {
ret = new_g1_array(&s->x_ext_fft_columns[i], FIELD_ELEMENTS_PER_CELL);
if (ret != C_KZG_OK) goto out;
}
for (size_t offset = 0; offset < FIELD_ELEMENTS_PER_CELL; offset++) {
size_t start = FIELD_ELEMENTS_PER_BLOB - FIELD_ELEMENTS_PER_CELL - 1 - offset;
for (size_t i = 0; i < CELLS_PER_BLOB - 1; i++) {
size_t j = start - i * FIELD_ELEMENTS_PER_CELL;
x[i] = s->g1_values_monomial[j];
}
x[CELLS_PER_BLOB - 1] = G1_IDENTITY;
ret = toeplitz_part_1(points, x, CELLS_PER_BLOB, s);
if (ret != C_KZG_OK) goto out;
for (size_t row = 0; row < circulant_domain_size; row++) {
s->x_ext_fft_columns[row][offset] = points[row];
}
}
if (precompute) {
ret = c_kzg_calloc((void **)&s->tables, circulant_domain_size, sizeof(void *));
if (ret != C_KZG_OK) goto out;
ret = c_kzg_calloc((void **)&p_affine, FIELD_ELEMENTS_PER_CELL, sizeof(blst_p1_affine));
if (ret != C_KZG_OK) goto out;
size_t table_size = blst_p1s_mult_wbits_precompute_sizeof(
s->wbits, FIELD_ELEMENTS_PER_CELL
);
for (size_t i = 0; i < circulant_domain_size; i++) {
const blst_p1 *p_arg[2] = {s->x_ext_fft_columns[i], NULL};
blst_p1s_to_affine(p_affine, p_arg, FIELD_ELEMENTS_PER_CELL);
const blst_p1_affine *points_arg[2] = {p_affine, NULL};
ret = c_kzg_malloc((void **)&s->tables[i], table_size);
if (ret != C_KZG_OK) goto out;
blst_p1s_mult_wbits_precompute(
s->tables[i], s->wbits, points_arg, FIELD_ELEMENTS_PER_CELL
);
}
s->scratch_size = blst_p1s_mult_wbits_scratch_sizeof(FIELD_ELEMENTS_PER_CELL);
}
out:
c_kzg_free(x);
c_kzg_free(points);
c_kzg_free(p_affine);
return ret;
}
static C_KZG_RET is_trusted_setup_in_lagrange_form(const KZGSettings *s, size_t n1, size_t n2) {
if (n1 < 2 || n2 < 2) {
return C_KZG_BADARGS;
}
bool is_monomial_form = pairings_verify(
&s->g1_values_lagrange_brp[1],
&s->g2_values_monomial[0],
&s->g1_values_lagrange_brp[0],
&s->g2_values_monomial[1]
);
return is_monomial_form ? C_KZG_BADARGS : C_KZG_OK;
}
static void init_settings(KZGSettings *out) {
out->roots_of_unity = NULL;
out->brp_roots_of_unity = NULL;
out->reverse_roots_of_unity = NULL;
out->g1_values_monomial = NULL;
out->g1_values_lagrange_brp = NULL;
out->g2_values_monomial = NULL;
out->x_ext_fft_columns = NULL;
out->tables = NULL;
out->wbits = 0;
out->scratch_size = 0;
}
C_KZG_RET load_trusted_setup(
KZGSettings *out,
const uint8_t *g1_monomial_bytes,
uint64_t num_g1_monomial_bytes,
const uint8_t *g1_lagrange_bytes,
uint64_t num_g1_lagrange_bytes,
const uint8_t *g2_monomial_bytes,
uint64_t num_g2_monomial_bytes,
uint64_t precompute
) {
C_KZG_RET ret;
init_settings(out);
if (precompute > 15) {
ret = C_KZG_BADARGS;
goto out_error;
}
out->wbits = precompute;
if (num_g1_monomial_bytes != NUM_G1_POINTS * BYTES_PER_G1 ||
num_g1_lagrange_bytes != NUM_G1_POINTS * BYTES_PER_G1 ||
num_g2_monomial_bytes != NUM_G2_POINTS * BYTES_PER_G2) {
ret = C_KZG_BADARGS;
goto out_error;
}
ret = new_fr_array(&out->brp_roots_of_unity, FIELD_ELEMENTS_PER_EXT_BLOB);
if (ret != C_KZG_OK) goto out_error;
ret = new_fr_array(&out->roots_of_unity, FIELD_ELEMENTS_PER_EXT_BLOB + 1);
if (ret != C_KZG_OK) goto out_error;
ret = new_fr_array(&out->reverse_roots_of_unity, FIELD_ELEMENTS_PER_EXT_BLOB + 1);
if (ret != C_KZG_OK) goto out_error;
ret = new_g1_array(&out->g1_values_monomial, NUM_G1_POINTS);
if (ret != C_KZG_OK) goto out_error;
ret = new_g1_array(&out->g1_values_lagrange_brp, NUM_G1_POINTS);
if (ret != C_KZG_OK) goto out_error;
ret = new_g2_array(&out->g2_values_monomial, NUM_G2_POINTS);
if (ret != C_KZG_OK) goto out_error;
for (size_t i = 0; i < NUM_G1_POINTS; i++) {
blst_p1_affine g1_affine;
BLST_ERROR err = blst_p1_uncompress(&g1_affine, &g1_monomial_bytes[BYTES_PER_G1 * i]);
if (err != BLST_SUCCESS) {
ret = C_KZG_BADARGS;
goto out_error;
}
blst_p1_from_affine(&out->g1_values_monomial[i], &g1_affine);
}
for (size_t i = 0; i < NUM_G1_POINTS; i++) {
blst_p1_affine g1_affine;
BLST_ERROR err = blst_p1_uncompress(&g1_affine, &g1_lagrange_bytes[BYTES_PER_G1 * i]);
if (err != BLST_SUCCESS) {
ret = C_KZG_BADARGS;
goto out_error;
}
blst_p1_from_affine(&out->g1_values_lagrange_brp[i], &g1_affine);
}
for (size_t i = 0; i < NUM_G2_POINTS; i++) {
blst_p2_affine g2_affine;
BLST_ERROR err = blst_p2_uncompress(&g2_affine, &g2_monomial_bytes[BYTES_PER_G2 * i]);
if (err != BLST_SUCCESS) {
ret = C_KZG_BADARGS;
goto out_error;
}
blst_p2_from_affine(&out->g2_values_monomial[i], &g2_affine);
}
ret = is_trusted_setup_in_lagrange_form(out, NUM_G1_POINTS, NUM_G2_POINTS);
if (ret != C_KZG_OK) goto out_error;
ret = compute_roots_of_unity(out);
if (ret != C_KZG_OK) goto out_error;
ret = bit_reversal_permutation(out->g1_values_lagrange_brp, sizeof(g1_t), NUM_G1_POINTS);
if (ret != C_KZG_OK) goto out_error;
ret = init_fk20_multi_settings(out);
if (ret != C_KZG_OK) goto out_error;
goto out_success;
out_error:
free_trusted_setup(out);
out_success:
return ret;
}
C_KZG_RET load_trusted_setup_file(KZGSettings *out, FILE *in, uint64_t precompute) {
C_KZG_RET ret;
int num_matches;
uint64_t num_g1_points;
uint64_t num_g2_points;
uint8_t *g1_monomial_bytes = NULL;
uint8_t *g1_lagrange_bytes = NULL;
uint8_t *g2_monomial_bytes = NULL;
init_settings(out);
ret = c_kzg_calloc((void **)&g1_monomial_bytes, NUM_G1_POINTS, BYTES_PER_G1);
if (ret != C_KZG_OK) goto out;
ret = c_kzg_calloc((void **)&g1_lagrange_bytes, NUM_G1_POINTS, BYTES_PER_G1);
if (ret != C_KZG_OK) goto out;
ret = c_kzg_calloc((void **)&g2_monomial_bytes, NUM_G2_POINTS, BYTES_PER_G2);
if (ret != C_KZG_OK) goto out;
num_matches = fscanf(in, "%" SCNu64, &num_g1_points);
if (num_matches != 1 || num_g1_points != NUM_G1_POINTS) {
ret = C_KZG_BADARGS;
goto out;
}
num_matches = fscanf(in, "%" SCNu64, &num_g2_points);
if (num_matches != 1 || num_g2_points != NUM_G2_POINTS) {
ret = C_KZG_BADARGS;
goto out;
}
for (size_t i = 0; i < NUM_G1_POINTS * BYTES_PER_G1; i++) {
num_matches = fscanf(in, "%2hhx", &g1_lagrange_bytes[i]);
if (num_matches != 1) {
ret = C_KZG_BADARGS;
goto out;
}
}
for (size_t i = 0; i < NUM_G2_POINTS * BYTES_PER_G2; i++) {
num_matches = fscanf(in, "%2hhx", &g2_monomial_bytes[i]);
if (num_matches != 1) {
ret = C_KZG_BADARGS;
goto out;
}
}
for (size_t i = 0; i < NUM_G1_POINTS * BYTES_PER_G1; i++) {
num_matches = fscanf(in, "%2hhx", &g1_monomial_bytes[i]);
if (num_matches != 1) {
ret = C_KZG_BADARGS;
goto out;
}
}
ret = load_trusted_setup(
out,
g1_monomial_bytes,
NUM_G1_POINTS * BYTES_PER_G1,
g1_lagrange_bytes,
NUM_G1_POINTS * BYTES_PER_G1,
g2_monomial_bytes,
NUM_G2_POINTS * BYTES_PER_G2,
precompute
);
out:
c_kzg_free(g1_monomial_bytes);
c_kzg_free(g1_lagrange_bytes);
c_kzg_free(g2_monomial_bytes);
return ret;
}