Support zero length polynomials

This commit is contained in:
Ben Edgington 2021-02-06 13:24:17 +00:00
parent b3fd3cbb72
commit f93c33c2db
5 changed files with 81 additions and 64 deletions

View File

@ -51,10 +51,8 @@ bool check_proof_single(const KZGSettings *ks, const blst_p1 *commitment, const
// of several polynomial evaluations) // of several polynomial evaluations)
C_KZG_RET compute_proof_multi(blst_p1 *out, const KZGSettings *ks, poly *p, const blst_fr *x0, uint64_t n) { C_KZG_RET compute_proof_multi(blst_p1 *out, const KZGSettings *ks, poly *p, const blst_fr *x0, uint64_t n) {
poly divisor, q; poly divisor, q;
uint64_t len;
blst_fr x_pow_n; blst_fr x_pow_n;
C_KZG_RET ret;
ASSERT(p->length >= n + 1, C_KZG_BADARGS);
// Construct x^n - x0^n = (x - w^0)(x - w^1)...(x - w^(n-1)) // Construct x^n - x0^n = (x - w^0)(x - w^1)...(x - w^(n-1))
init_poly(&divisor, n + 1); init_poly(&divisor, n + 1);
@ -72,10 +70,10 @@ C_KZG_RET compute_proof_multi(blst_p1 *out, const KZGSettings *ks, poly *p, cons
divisor.coeffs[n] = fr_one; divisor.coeffs[n] = fr_one;
// Calculate q = p / (x^n - x0^n) // Calculate q = p / (x^n - x0^n)
// Discard the return codes since we already checked above that all should be fine. init_poly(&q, poly_quotient_length(p, &divisor));
poly_quotient_length(&len, p, &divisor); if ((ret = poly_long_div(&q, p, &divisor) != C_KZG_OK)) {
init_poly(&q, len); return C_KZG_ERROR;
poly_long_div(&q, p, &divisor); }
commit_to_poly(out, ks, &q); commit_to_poly(out, ks, &q);

View File

@ -75,6 +75,10 @@ void proof_single(void) {
// Verify the proof that the (unknown) polynomial has y = value at x = 25 // Verify the proof that the (unknown) polynomial has y = value at x = 25
TEST_CHECK(true == check_proof_single(&ks, &commitment, &proof, &x, &value)); TEST_CHECK(true == check_proof_single(&ks, &commitment, &proof, &x, &value));
// Change the value and check that the proof fails
blst_fr_add(&value, &value, &fr_one);
TEST_CHECK(false == check_proof_single(&ks, &commitment, &proof, &x, &value));
free_fft_settings(&fs); free_fft_settings(&fs);
free_poly(&p); free_poly(&p);
free(s1); free(s1);
@ -85,20 +89,21 @@ void proof_multi(void) {
// Our polynomial: degree 15, 16 coefficients // Our polynomial: degree 15, 16 coefficients
uint64_t coeffs[] = {1, 2, 3, 4, 7, 7, 7, 7, 13, 13, 13, 13, 13, 13, 13, 13}; uint64_t coeffs[] = {1, 2, 3, 4, 7, 7, 7, 7, 13, 13, 13, 13, 13, 13, 13, 13};
int poly_len = sizeof coeffs / sizeof coeffs[0]; int poly_len = sizeof coeffs / sizeof coeffs[0];
uint64_t secrets_len = poly_len + 1;
FFTSettings fs1, fs2; FFTSettings fs1, fs2;
KZGSettings ks1, ks2; KZGSettings ks1, ks2;
poly p; poly p;
blst_p1 commitment, proof; blst_p1 commitment, proof;
blst_p1 *s1 = malloc(secrets_len * sizeof(blst_p1));
blst_p2 *s2 = malloc(secrets_len * sizeof(blst_p2));
blst_fr x, tmp; blst_fr x, tmp;
// Must have coset_scale < poly_len [TODO: why?] // Compute proof at 2^coset_scale points
int coset_scale = 3, coset_len = (1 << coset_scale); int coset_scale = 7, coset_len = (1 << coset_scale);
blst_fr y[coset_len]; blst_fr y[coset_len];
uint64_t secrets_len = poly_len > coset_len ? poly_len + 1 : coset_len + 1;
blst_p1 *s1 = malloc(secrets_len * sizeof(blst_p1));
blst_p2 *s2 = malloc(secrets_len * sizeof(blst_p2));
// Create the polynomial // Create the polynomial
init_poly(&p, poly_len); init_poly(&p, poly_len);
for (int i = 0; i < poly_len; i++) { for (int i = 0; i < poly_len; i++) {
@ -127,7 +132,11 @@ void proof_multi(void) {
} }
// Verify the proof that the (unknown) polynomial has value y_i at x_i // Verify the proof that the (unknown) polynomial has value y_i at x_i
TEST_CHECK(check_proof_multi(&ks2, &commitment, &proof, &x, y, coset_len)); TEST_CHECK(true == check_proof_multi(&ks2, &commitment, &proof, &x, y, coset_len));
// Change a value and check that the proof fails
blst_fr_add(y + coset_len / 2, y + coset_len / 2, &fr_one);
TEST_CHECK(false == check_proof_multi(&ks2, &commitment, &proof, &x, y, coset_len));
free_fft_settings(&fs1); free_fft_settings(&fs1);
free_fft_settings(&fs2); free_fft_settings(&fs2);
@ -136,26 +145,10 @@ void proof_multi(void) {
free(s2); free(s2);
} }
void proof_single_error(void) {
poly p;
blst_p1 proof;
KZGSettings ks;
blst_fr x;
// Check it barfs on a constant polynomial
init_poly(&p, 1);
fr_from_uint64(&x, 1234);
TEST_CHECK(C_KZG_BADARGS == compute_proof_single(&proof, &ks, &p, &x));
free_poly(&p);
}
TEST_LIST = TEST_LIST =
{ {
{"KZG_PROOFS_TEST", title}, {"KZG_PROOFS_TEST", title},
{"proof_single", proof_single}, {"proof_single", proof_single},
{"proof_multi", proof_multi}, {"proof_multi", proof_multi},
{"proof_single_error", proof_single},
{ NULL, NULL } /* zero record marks the end of the list */ { NULL, NULL } /* zero record marks the end of the list */
}; };

View File

@ -23,11 +23,13 @@ static void poly_factor_div(blst_fr *out, const blst_fr *a, const blst_fr *b) {
void init_poly(poly *out, const uint64_t length) { void init_poly(poly *out, const uint64_t length) {
out->length = length; out->length = length;
out->coeffs = malloc(length * sizeof(blst_fr)); out->coeffs = length > 0 ? malloc(length * sizeof(blst_fr)): NULL;
} }
void free_poly(poly *p) { void free_poly(poly *p) {
free(p->coeffs); if (p->coeffs != NULL) {
free(p->coeffs);
}
} }
void eval_poly(blst_fr *out, const poly *p, const blst_fr *x) { void eval_poly(blst_fr *out, const poly *p, const blst_fr *x) {
@ -55,28 +57,28 @@ void eval_poly(blst_fr *out, const poly *p, const blst_fr *x) {
} }
} }
// Call this to find out how much space to allocate for the result // Call this to find out how much space to allocate for the result of `poly_long_div()`
C_KZG_RET poly_quotient_length(uint64_t *out, const poly *dividend, const poly *divisor) { uint64_t poly_quotient_length(const poly *dividend, const poly *divisor) {
ASSERT(dividend->length >= divisor->length, C_KZG_BADARGS); return dividend->length >= divisor->length ? dividend->length - divisor->length + 1 : 0;
*out = dividend->length - divisor->length + 1;
return C_KZG_OK;
} }
// `out` must have been pre-allocated to the correct size, and the length is provided // `out` must have been pre-allocated to the correct size, see `poly_quotient_length()`
// as a check
C_KZG_RET poly_long_div(poly *out, const poly *dividend, const poly *divisor) { C_KZG_RET poly_long_div(poly *out, const poly *dividend, const poly *divisor) {
uint64_t a_pos = dividend->length - 1; uint64_t a_pos = dividend->length - 1;
uint64_t b_pos = divisor->length - 1; uint64_t b_pos = divisor->length - 1;
uint64_t diff = a_pos - b_pos; uint64_t diff = a_pos - b_pos;
blst_fr a[dividend->length]; blst_fr a[dividend->length];
ASSERT(out->length == diff + 1, C_KZG_BADARGS); ASSERT(out->length == poly_quotient_length(dividend, divisor), C_KZG_BADARGS);
// If the divisor is larger than the dividend, the result is zero-length
if (divisor->length > dividend->length) return C_KZG_OK;
for (uint64_t i = 0; i < dividend->length; i++) { for (uint64_t i = 0; i < dividend->length; i++) {
a[i] = dividend->coeffs[i]; a[i] = dividend->coeffs[i];
} }
while (true) { while (diff > 0) {
poly_factor_div(&out->coeffs[diff], &a[a_pos], &divisor->coeffs[b_pos]); poly_factor_div(&out->coeffs[diff], &a[a_pos], &divisor->coeffs[b_pos]);
for (uint64_t i = 0; i <= b_pos; i++) { for (uint64_t i = 0; i <= b_pos; i++) {
blst_fr tmp; blst_fr tmp;
@ -84,10 +86,10 @@ C_KZG_RET poly_long_div(poly *out, const poly *dividend, const poly *divisor) {
blst_fr_mul(&tmp, &out->coeffs[diff], &divisor->coeffs[i]); blst_fr_mul(&tmp, &out->coeffs[diff], &divisor->coeffs[i]);
blst_fr_sub(&a[diff + i], &a[diff + i], &tmp); blst_fr_sub(&a[diff + i], &a[diff + i], &tmp);
} }
if (diff == 0) break;
--diff; --diff;
--a_pos; --a_pos;
} }
poly_factor_div(&out->coeffs[0], &a[a_pos], &divisor->coeffs[b_pos]);
return C_KZG_OK; return C_KZG_OK;
} }

View File

@ -26,5 +26,5 @@ typedef struct {
void init_poly(poly *out, const uint64_t length); void init_poly(poly *out, const uint64_t length);
void free_poly(poly *p); void free_poly(poly *p);
void eval_poly(blst_fr *out, const poly *p, const blst_fr *x); void eval_poly(blst_fr *out, const poly *p, const blst_fr *x);
C_KZG_RET poly_quotient_length(uint64_t *out, const poly *dividend, const poly *divisor); uint64_t poly_quotient_length(const poly *dividend, const poly *divisor);
C_KZG_RET poly_long_div(poly *out, const poly *dividend, const poly *divisor); C_KZG_RET poly_long_div(poly *out, const poly *dividend, const poly *divisor);

View File

@ -22,19 +22,11 @@ void title(void) {;}
void poly_div_length(void) { void poly_div_length(void) {
poly a, b; poly a, b;
uint64_t len;
init_poly(&a, 17); init_poly(&a, 17);
init_poly(&b, 5); init_poly(&b, 5);
TEST_CHECK(C_KZG_OK == poly_quotient_length(&len, &a, &b)); TEST_CHECK(13 == poly_quotient_length(&a, &b));
TEST_CHECK(13 == len); TEST_CHECK(1 == poly_quotient_length(&a, &a));
} TEST_CHECK(0 == poly_quotient_length(&b, &a));
void poly_div_length_bad(void) {
poly a, b;
uint64_t len;
init_poly(&a, 5);
init_poly(&b, 17);
TEST_CHECK(C_KZG_BADARGS == poly_quotient_length(&len, &a, &b));
} }
void poly_div_0(void) { void poly_div_0(void) {
@ -106,14 +98,35 @@ void poly_div_1(void) {
TEST_CHECK(fr_equal(&expected[2], &actual.coeffs[2])); TEST_CHECK(fr_equal(&expected[2], &actual.coeffs[2]));
} }
void poly_wrong_size(void) { void poly_div_2(void) {
poly dividend, divisor, result; blst_fr a[3], b[2];
TEST_CHECK(C_KZG_BADARGS == poly_long_div(&result, &dividend, &divisor)); poly dividend, divisor, actual;
// Calculate (x + 1) / (x^2 - 1) = nil
// Dividend
fr_from_uint64(&b[0], 1);
fr_from_uint64(&b[1], 1);
dividend.length = 2;
dividend.coeffs = b;
// Divisor
fr_from_uint64(&a[0], 1);
fr_negate(&a[0], &a[0]);
fr_from_uint64(&a[1], 0);
fr_from_uint64(&a[2], 1);
divisor.length = 3;
divisor.coeffs = a;
init_poly(&actual, poly_quotient_length(&dividend, &divisor));
TEST_CHECK(C_KZG_OK == poly_long_div(&actual, &dividend, &divisor));
TEST_CHECK(fr_equal(NULL, actual.coeffs));
} }
void poly_eval_check(void) { void poly_eval_check(void) {
uint64_t n = 10; uint64_t n = 10;
blst_fr res, expected; blst_fr actual, expected;
poly p; poly p;
init_poly(&p, n); init_poly(&p, n);
for (uint64_t i = 0; i < n; i++) { for (uint64_t i = 0; i < n; i++) {
@ -121,14 +134,14 @@ void poly_eval_check(void) {
} }
fr_from_uint64(&expected, n * (n + 1) / 2); fr_from_uint64(&expected, n * (n + 1) / 2);
eval_poly(&res, &p, &fr_one); eval_poly(&actual, &p, &fr_one);
TEST_CHECK(fr_equal(&expected, &res)); TEST_CHECK(fr_equal(&expected, &actual));
} }
void poly_eval_0_check(void) { void poly_eval_0_check(void) {
uint64_t n = 7, a = 597; uint64_t n = 7, a = 597;
blst_fr res, expected; blst_fr actual, expected;
poly p; poly p;
init_poly(&p, n); init_poly(&p, n);
for (uint64_t i = 0; i < n; i++) { for (uint64_t i = 0; i < n; i++) {
@ -136,20 +149,31 @@ void poly_eval_0_check(void) {
} }
fr_from_uint64(&expected, a); fr_from_uint64(&expected, a);
eval_poly(&res, &p, &fr_zero); eval_poly(&actual, &p, &fr_zero);
TEST_CHECK(fr_equal(&expected, &res)); TEST_CHECK(fr_equal(&expected, &actual));
}
void poly_eval_nil_check(void) {
uint64_t n = 0;
blst_fr actual;
poly p;
init_poly(&p, n);
eval_poly(&actual, &p, &fr_one);
TEST_CHECK(fr_equal(&fr_zero, &actual));
} }
TEST_LIST = TEST_LIST =
{ {
{"POLY_TEST", title}, {"POLY_TEST", title},
{"poly_div_length", poly_div_length}, {"poly_div_length", poly_div_length},
{"poly_div_length_bad", poly_div_length_bad},
{"poly_div_0", poly_div_0}, {"poly_div_0", poly_div_0},
{"poly_div_1", poly_div_1}, {"poly_div_1", poly_div_1},
{"poly_wrong_size", poly_wrong_size}, {"poly_div_2", poly_div_1},
{"poly_eval_check", poly_eval_check}, {"poly_eval_check", poly_eval_check},
{"poly_eval_0_check", poly_eval_0_check}, {"poly_eval_0_check", poly_eval_0_check},
{"poly_eval_nil_check", poly_eval_0_check},
{ NULL, NULL } /* zero record marks the end of the list */ { NULL, NULL } /* zero record marks the end of the list */
}; };