From 686d1c5ca71ccabd67c3cf83eb71cc8cc442b0aa Mon Sep 17 00:00:00 2001 From: Ben Edgington Date: Thu, 4 Feb 2021 21:23:34 +0000 Subject: [PATCH] Implement single polynomial commitment --- README.md | 1 + src/Makefile | 6 ++- src/fft_common.c | 2 + src/fft_common.h | 2 - src/fft_common_test.c | 1 + src/fft_fr.c | 1 + src/fft_fr_test.c | 1 + src/fft_g1.c | 1 + src/kzg_common.c | 28 ++++++++++++++ src/kzg_common.h | 28 ++++++++++++++ src/kzg_single_proofs.c | 53 ++++++++++++++++++++++++++ src/kzg_single_proofs.h | 23 +++++++++++ src/kzg_single_proofs_test.c | 74 ++++++++++++++++++++++++++++++++++++ src/poly.c | 25 ++++++++++++ src/poly.h | 5 +++ src/poly_test.c | 23 +++++++++-- 16 files changed, 266 insertions(+), 8 deletions(-) create mode 100644 src/kzg_common.c create mode 100644 src/kzg_common.h create mode 100644 src/kzg_single_proofs.c create mode 100644 src/kzg_single_proofs.h create mode 100644 src/kzg_single_proofs_test.c diff --git a/README.md b/README.md index 4cd1f3b..53482dc 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ Initially, at least, this largely follows the [go-kzg](https://github.com/protol Done so far: - FFT and inverse FFT over the finite field. - FFTs over the G1 group + - Single polynomial commitment and verification ## Installation diff --git a/src/Makefile b/src/Makefile index cbd9a2d..65d8fa8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,5 +1,7 @@ -TESTS = blst_util_test fft_common_test fft_fr_test fft_g1_test poly_test -LIB_SRC = poly.c blst_util.c fft_common.c fft_fr.c fft_g1.c +TESTS = blst_util_test fft_common_test fft_fr_test fft_g1_test \ + kzg_single_proofs_test poly_test +LIB_SRC = blst_util.c fft_common.c fft_fr.c fft_g1.c kzg_common.c \ + kzg_single_proofs.c poly.c LIB_OBJ = $(LIB_SRC:.c=.o) CFLAGS = diff --git a/src/fft_common.c b/src/fft_common.c index 6bd7e66..d6d918e 100644 --- a/src/fft_common.c +++ b/src/fft_common.c @@ -14,7 +14,9 @@ * limitations under the License. */ +#include #include "fft_common.h" +#include "blst_util.h" bool is_power_of_two(const uint64_t n) { return (n & (n - 1)) == 0; diff --git a/src/fft_common.h b/src/fft_common.h index 2dba473..b0f290c 100644 --- a/src/fft_common.h +++ b/src/fft_common.h @@ -14,9 +14,7 @@ * limitations under the License. */ -#include #include "c_kzg.h" -#include "blst_util.h" // MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513 // PRIMITIVE_ROOT = 5 diff --git a/src/fft_common_test.c b/src/fft_common_test.c index df21bbb..45d2ef3 100644 --- a/src/fft_common_test.c +++ b/src/fft_common_test.c @@ -17,6 +17,7 @@ #include "../inc/acutest.h" #include "debug_util.h" #include "fft_common.h" +#include "blst_util.h" #define NUM_ROOTS 32 diff --git a/src/fft_fr.c b/src/fft_fr.c index 64ad8ea..006944a 100644 --- a/src/fft_fr.c +++ b/src/fft_fr.c @@ -15,6 +15,7 @@ */ #include "fft_fr.h" +#include "blst_util.h" // Slow Fourier Transform (simple, good for small sizes) void fft_fr_slow(blst_fr *out, blst_fr *in, uint64_t stride, blst_fr *roots, uint64_t roots_stride, uint64_t l) { diff --git a/src/fft_fr_test.c b/src/fft_fr_test.c index 236ecd8..5477a84 100644 --- a/src/fft_fr_test.c +++ b/src/fft_fr_test.c @@ -17,6 +17,7 @@ #include "../inc/acutest.h" #include "debug_util.h" #include "fft_fr.h" +#include "blst_util.h" const uint64_t inv_fft_expected[][4] = { diff --git a/src/fft_g1.c b/src/fft_g1.c index 2228626..6d8d520 100644 --- a/src/fft_g1.c +++ b/src/fft_g1.c @@ -15,6 +15,7 @@ */ #include "fft_g1.h" +#include "blst_util.h" // Slow Fourier Transform (simple, good for small sizes) void fft_g1_slow(blst_p1 *out, blst_p1 *in, uint64_t stride, blst_fr *roots, uint64_t roots_stride, uint64_t l) { diff --git a/src/kzg_common.c b/src/kzg_common.c new file mode 100644 index 0000000..dc6bfeb --- /dev/null +++ b/src/kzg_common.c @@ -0,0 +1,28 @@ +/* + * Copyright 2021 Benjamin Edgington + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "kzg_common.h" + +C_KZG_RET new_kzg_settings(KZGSettings *ks, FFTSettings *fs, blst_p1 *secret_g1, blst_p2 *secret_g2, uint64_t length) { + ASSERT(length >= fs->max_width, C_KZG_BADARGS); + ks->fs = fs; + ks->secret_g1 = secret_g1; + ks->extended_secret_g1 = NULL; + ks->secret_g2 = secret_g2; + ks->length = length; + return C_KZG_SUCCESS; +} diff --git a/src/kzg_common.h b/src/kzg_common.h new file mode 100644 index 0000000..bb895ea --- /dev/null +++ b/src/kzg_common.h @@ -0,0 +1,28 @@ +/* + * Copyright 2021 Benjamin Edgington + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "c_kzg.h" +#include "fft_common.h" + +typedef struct { + FFTSettings *fs; + blst_p1 *secret_g1; + blst_p1 *extended_secret_g1; + blst_p2 *secret_g2; + uint64_t length; +} KZGSettings; + +C_KZG_RET new_kzg_settings(KZGSettings *ks, FFTSettings *fs, blst_p1 *secret_g1, blst_p2 *secret_g2, uint64_t length); diff --git a/src/kzg_single_proofs.c b/src/kzg_single_proofs.c new file mode 100644 index 0000000..f4da450 --- /dev/null +++ b/src/kzg_single_proofs.c @@ -0,0 +1,53 @@ +/* + * Copyright 2021 Benjamin Edgington + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kzg_single_proofs.h" + +void commit_to_poly(blst_p1 *out, const KZGSettings *ks, const poly *p) { + linear_combination_g1(out, ks->secret_g1, p->coeffs, p->length); +} + +// Compute KZG proof for polynomial at position x0 +void compute_proof_single(blst_p1 *out, const KZGSettings *ks, poly *p, const uint64_t x0) { + poly divisor, q; + blst_fr tmp; + + // The divisor is x - x0 + init_poly(&divisor, 2); + fr_from_uint64(&tmp, x0); + fr_negate(&divisor.coeffs[0],&tmp); + divisor.coeffs[1] = one; + + // Calculate q = p / (x - x0) + init_poly(&q, poly_long_div_length(p->length, 2)); + poly_long_div(&q, p, &divisor); + + linear_combination_g1(out, ks->secret_g1, q.coeffs, q.length); + + free_poly(q); + free_poly(divisor); +} + +bool check_proof_single(const KZGSettings *ks, const blst_p1 *commitment, const blst_p1 *proof, const blst_fr *x, blst_fr *y) { + blst_p2 x_g2, s_minus_x; + blst_p1 y_g1, commitment_minus_y; + p2_mul(&x_g2, blst_p2_generator(), x); + p2_sub(&s_minus_x, &ks->secret_g2[1], &x_g2); + p1_mul(&y_g1, blst_p1_generator(), y); + p1_sub(&commitment_minus_y, commitment, &y_g1); + + return pairings_verify(&commitment_minus_y, blst_p2_generator(), proof, &s_minus_x); +} diff --git a/src/kzg_single_proofs.h b/src/kzg_single_proofs.h new file mode 100644 index 0000000..84fa203 --- /dev/null +++ b/src/kzg_single_proofs.h @@ -0,0 +1,23 @@ +/* + * Copyright 2021 Benjamin Edgington + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "c_kzg.h" +#include "kzg_common.h" +#include "poly.h" + +void commit_to_poly(blst_p1 *out, const KZGSettings *ks, const poly *p); +void compute_proof_single(blst_p1 *out, const KZGSettings *ks, poly *p, const uint64_t x0); +bool check_proof_single(const KZGSettings *ks, const blst_p1 *commitment, const blst_p1 *proof, const blst_fr *x, blst_fr *y); diff --git a/src/kzg_single_proofs_test.c b/src/kzg_single_proofs_test.c new file mode 100644 index 0000000..1f158f4 --- /dev/null +++ b/src/kzg_single_proofs_test.c @@ -0,0 +1,74 @@ +/* + * Copyright 2021 Benjamin Edgington + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../inc/acutest.h" +#include "debug_util.h" +#include "kzg_single_proofs.h" + +void generate_setup(blst_p1 *s1, blst_p2 *s2, const blst_scalar *secret, const uint64_t n) { + blst_fr s_pow, s; + blst_fr_from_scalar(&s, secret); + s_pow = one; + for (uint64_t i = 0; i < n; i++) { + p1_mul(&s1[i], blst_p1_generator(), &s_pow); + p2_mul(&s2[i], blst_p2_generator(), &s_pow); + blst_fr_mul(&s_pow, &s_pow, &s); + } +} + +void proof_single(void) { + FFTSettings fs; + KZGSettings ks; + poly p; + blst_p1 commitment, proof; + blst_p1 *s1 = malloc(17 * sizeof(blst_p1)); + blst_p2 *s2 = malloc(17 * sizeof(blst_p2)); + blst_scalar secret = + { + 0xa4, 0x73, 0x31, 0x95, 0x28, 0xc8, 0xb6, 0xea, + 0x4d, 0x08,0xcc, 0x53, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; // Little-endian? + uint64_t coeffs_u[16] = {1, 2, 3, 4, 7, 7, 7, 7, 13, 13, 13, 13, 13, 13, 13, 13}; + blst_fr x, value; + + init_poly(&p, 16); + for (int i = 0; i < 16; i++) { + fr_from_uint64(&p.coeffs[i], coeffs_u[i]); + } + + generate_setup(s1, s2, &secret, 17); + new_fft_settings(&fs, 4); + new_kzg_settings(&ks, &fs, s1, s2, 17); + + commit_to_poly(&commitment, &ks, &p); + compute_proof_single(&proof, &ks, &p, 17); + + fr_from_uint64(&x, 17); + eval_poly_at(&value, &p, &x); + + TEST_CHECK(true == check_proof_single(&ks, &commitment, &proof, &x, &value)); + + free(s1); + free(s2); +} + +TEST_LIST = + { + {"proof_single", proof_single}, + { NULL, NULL } /* zero record marks the end of the list */ + }; diff --git a/src/poly.c b/src/poly.c index 4414f2c..5cacc37 100644 --- a/src/poly.c +++ b/src/poly.c @@ -21,6 +21,31 @@ static void poly_factor_div(blst_fr *out, const blst_fr *a, const blst_fr *b) { blst_fr_mul(out, out, a); } +void init_poly(poly *out, const uint64_t length) { + out->length = length; + out->coeffs = malloc(length * sizeof(blst_fr)); +} + +void free_poly(poly p) { + free(p.coeffs); +} + +void eval_poly_at(blst_fr *out, const poly *p, const blst_fr *x) { + blst_fr tmp; + + if (p->length == 0) { + fr_from_uint64(out, 0); + } + // TODO x = 0 case + + // Horner's method + *out = p->coeffs[p->length - 1]; + for (int i = p->length - 2; i >= 0; i--) { // needs to be uint64_t? + blst_fr_mul(&tmp, out, x); + blst_fr_add(out, &tmp, &p->coeffs[i]); + } +} + // Call this to find out how much space to allocate for the result uint64_t poly_long_div_length(const uint64_t len_dividend, const uint64_t len_divisor) { return len_dividend - len_divisor + 1; diff --git a/src/poly.h b/src/poly.h index e4fe36d..5a755c8 100644 --- a/src/poly.h +++ b/src/poly.h @@ -14,12 +14,17 @@ * limitations under the License. */ +#include // malloc, free #include "c_kzg.h" +#include "blst_util.h" typedef struct { blst_fr *coeffs; // coeffs[i] is the x^i term uint64_t length; // one more than the polynomial's degree } poly; +void init_poly(poly *out, const uint64_t length); +void free_poly(poly p); +void eval_poly_at(blst_fr *out, const poly *p, const blst_fr *x); uint64_t poly_long_div_length(const uint64_t len_dividend, const uint64_t len_divisor); C_KZG_RET poly_long_div(poly *out, const poly *dividend, const poly *divisor); diff --git a/src/poly_test.c b/src/poly_test.c index f65887e..6bb43cf 100644 --- a/src/poly_test.c +++ b/src/poly_test.c @@ -16,14 +16,13 @@ #include "../inc/acutest.h" #include "debug_util.h" -#include "blst_util.h" #include "poly.h" -void poly_div_length() { +void poly_div_length(void) { TEST_CHECK(3 == poly_long_div_length(4, 2)); } -void poly_div_0() { +void poly_div_0(void) { blst_fr a[3], b[2], c[2], expected[2]; poly dividend, divisor, actual; @@ -56,7 +55,7 @@ void poly_div_0() { TEST_CHECK(fr_equal(&expected[1], &actual.coeffs[1])); } -void poly_div_1() { +void poly_div_1(void) { blst_fr a[4], b[2], c[3], expected[3]; poly dividend, divisor, actual; @@ -97,11 +96,27 @@ void poly_wrong_size(void) { TEST_CHECK(poly_long_div(&result, ÷nd, &divisor) == C_KZG_BADARGS); } +void eval_poly(void) { + uint64_t n = 10; + blst_fr res, expected; + poly p; + init_poly(&p, n); + for (uint64_t i = 0; i < n; i++) { + fr_from_uint64(&p.coeffs[i], i + 1); + } + fr_from_uint64(&expected, n * (n + 1) / 2); + + eval_poly_at(&res, &p, &one); + + TEST_CHECK(fr_equal(&expected, &res)); +} + TEST_LIST = { {"poly_div_length", poly_div_length}, {"poly_div_0", poly_div_0}, {"poly_div_1", poly_div_1}, {"poly_wrong_size", poly_wrong_size}, + {"eval_poly", eval_poly}, { NULL, NULL } /* zero record marks the end of the list */ };