diff --git a/.gitignore b/.gitignore index 5fe2876..25f8874 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.a *_test *_bench +*_debug *.prof *.out *.log diff --git a/README.md b/README.md index ef61a23..8f19b6d 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Done so far: - FFTs over the G1 group - Polynomial single commitment and verification - Polynomial multi commitment and verification + - [FK20](https://github.com/khovratovich/Kate/blob/master/Kate_amortized.pdf) single proof method (needs a tidy up) ## Installation @@ -69,8 +70,20 @@ make fft_fr_bench Doing `make clean` should resolve any weird build issues. +## Make debug builds of the tests + +The default build is designed not to exit on errors, and will (should) return fairly coarse error codes for any issue. This is good for a utility library, but unhelpful for debugging. The `-DDEBUG` compiler flag builds a version such that any assertion failure aborts the run and outputs file and line info. This is much more useful for tracking down deeply buried errors. + +Each test suite can be compiled into its debug version. For example, as follows: + +``` +make fk20_proofs_test_debug +./fk20_proofs_test_debug fk_single_strided +``` + ## Prerequisites - Blst library (see above) - `clang` compiler. I'm using Clang 10.0.0. I'll likely add `gcc` options in future. + - The Makefile is GNU make compatible. - I'm developing on Ubuntu 20.04. Will check portability later. diff --git a/src/Makefile b/src/Makefile index b67685c..c8f0127 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,10 +1,10 @@ TESTS = blst_util_test c_kzg_util_test fft_common_test fft_fr_test fft_g1_test \ - kzg_proofs_test poly_test + fk20_proofs_test kzg_proofs_test poly_test BENCH = fft_fr_bench fft_g1_bench -LIB_SRC = blst_util.c c_kzg_util.c fft_common.c fft_fr.c fft_g1.c kzg_proofs.c poly.c +LIB_SRC = blst_util.c c_kzg_util.c fft_common.c fft_fr.c fft_g1.c fk20_proofs.c kzg_proofs.c poly.c LIB_OBJ = $(LIB_SRC:.c=.o) -CFLAGS = -g +CFLAGS = .PRECIOUS: %.o @@ -14,22 +14,24 @@ CFLAGS = -g libckzg.a: $(LIB_OBJ) Makefile ar rc libckzg.a $(LIB_OBJ) -%_test: %_test.c debug_util.o libckzg.a Makefile - clang -Wall $(CFLAGS) -o $@ $@.c debug_util.o libckzg.a -L../lib -lblst +%_test: %_test.c debug_util.o test_util.o libckzg.a Makefile + clang -Wall $(CFLAGS) -o $@ $@.c debug_util.o test_util.o libckzg.a -L../lib -lblst ./$@ +# This version will abort on error and print the file and line number +%_test_debug: CFLAGS += -g -O0 -DDEBUG +%_test_debug: %_test.c debug_util.o test_util.o libckzg.a Makefile + clang -Wall $(CFLAGS) -o $@ $*_test.c debug_util.o test_util.o libckzg.a -L../lib -lblst + +# Benchmarks +%_bench: CFLAGS += -O3 %_bench: %_bench.c bench_util.o $(LIB_OBJ) Makefile clang -Wall $(CFLAGS) -o $@ $@.c bench_util.o $(LIB_OBJ) -L../lib -lblst ./$@ +lib: CFLAGS += -O3 lib: clean libckzg.a -debuglib: CFLAGS += -O1 -DDEBUG -debuglib: clean libckzg.a - -optlib: CFLAGS += -O2 -optlib: clean libckzg.a - profilelib: CFLAGS += -fprofile-instr-generate -fcoverage-mapping profilelib: clean libckzg.a @@ -42,4 +44,5 @@ clean: rm -f libckzg.a rm -f $(TESTS) rm -f $(BENCH) + rm -f *_debug rm -f a.out diff --git a/src/blst_util_test.c b/src/blst_util_test.c index a3e2d23..559c35a 100644 --- a/src/blst_util_test.c +++ b/src/blst_util_test.c @@ -16,13 +16,12 @@ #include "../inc/acutest.h" #include "debug_util.h" +#include "test_util.h" #include "blst_util.h" // This is -1 (the second root of unity) uint64_t m1[] = {0xffffffff00000000L, 0x53bda402fffe5bfeL, 0x3339d80809a1d805L, 0x73eda753299d7d48L}; -void title(void) {} - void fr_is_zero_works(void) { blst_fr zero; fr_from_uint64(&zero, 0); diff --git a/src/c_kzg_util_test.c b/src/c_kzg_util_test.c index 81d506c..081b6c4 100644 --- a/src/c_kzg_util_test.c +++ b/src/c_kzg_util_test.c @@ -16,10 +16,9 @@ #include "../inc/acutest.h" #include "debug_util.h" +#include "test_util.h" #include "c_kzg_util.h" -void title(void) {} - void malloc_works(void) { int *p; TEST_CHECK(C_KZG_OK == c_kzg_malloc((void **)&p, 4)); diff --git a/src/fft_common.c b/src/fft_common.c index 424aecd..bd9da86 100644 --- a/src/fft_common.c +++ b/src/fft_common.c @@ -49,19 +49,20 @@ C_KZG_RET reverse(blst_fr *out, const blst_fr *roots, const uint64_t width) { } C_KZG_RET new_fft_settings(FFTSettings *fs, const unsigned int max_scale) { - C_KZG_RET ret; fs->max_width = (uint64_t)1 << max_scale; blst_fr_from_uint64(&fs->root_of_unity, scale2_root_of_unity[max_scale]); - ASSERT(c_kzg_malloc((void **)&fs->expanded_roots_of_unity, (fs->max_width + 1) * sizeof(blst_fr)) == C_KZG_OK, + ASSERT(c_kzg_malloc((void **)&fs->expanded_roots_of_unity, + (fs->max_width + 1) * sizeof *fs->expanded_roots_of_unity) == C_KZG_OK, C_KZG_MALLOC); - ASSERT(c_kzg_malloc((void **)&fs->reverse_roots_of_unity, (fs->max_width + 1) * sizeof(blst_fr)) == C_KZG_OK, + ASSERT(c_kzg_malloc((void **)&fs->reverse_roots_of_unity, + (fs->max_width + 1) * sizeof *fs->reverse_roots_of_unity) == C_KZG_OK, C_KZG_MALLOC); - ret = expand_root_of_unity(fs->expanded_roots_of_unity, &fs->root_of_unity, fs->max_width); - if (ret != C_KZG_OK) return ret; - ret = reverse(fs->reverse_roots_of_unity, fs->expanded_roots_of_unity, fs->max_width); - return ret; + ASSERT(expand_root_of_unity(fs->expanded_roots_of_unity, &fs->root_of_unity, fs->max_width) == C_KZG_OK, + C_KZG_ERROR); + ASSERT(reverse(fs->reverse_roots_of_unity, fs->expanded_roots_of_unity, fs->max_width) == C_KZG_OK, C_KZG_ERROR); + return C_KZG_OK; } void free_fft_settings(FFTSettings *fs) { diff --git a/src/fft_common.h b/src/fft_common.h index 22097f4..94e0b98 100644 --- a/src/fft_common.h +++ b/src/fft_common.h @@ -14,6 +14,9 @@ * limitations under the License. */ +#ifndef FFT_COMMON +#define FFT_COMMON + #include "c_kzg.h" // MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513 @@ -67,3 +70,5 @@ C_KZG_RET expand_root_of_unity(blst_fr *roots, const blst_fr *root_of_unity, con C_KZG_RET reverse(blst_fr *out, const blst_fr *roots, const uint64_t width); C_KZG_RET new_fft_settings(FFTSettings *s, const unsigned int max_scale); void free_fft_settings(FFTSettings *s); + +#endif \ No newline at end of file diff --git a/src/fft_common_test.c b/src/fft_common_test.c index a238db1..c80a9e7 100644 --- a/src/fft_common_test.c +++ b/src/fft_common_test.c @@ -16,13 +16,12 @@ #include "../inc/acutest.h" #include "debug_util.h" +#include "test_util.h" #include "fft_common.h" #include "blst_util.h" #define NUM_ROOTS 32 -void title(void) {} - void roots_of_unity_is_the_expected_size(void) { TEST_CHECK(NUM_ROOTS == sizeof(scale2_root_of_unity) / sizeof(scale2_root_of_unity[0])); } diff --git a/src/fft_fr_test.c b/src/fft_fr_test.c index e66e5f0..5590ed6 100644 --- a/src/fft_fr_test.c +++ b/src/fft_fr_test.c @@ -16,6 +16,7 @@ #include "../inc/acutest.h" #include "debug_util.h" +#include "test_util.h" #include "fft_fr.h" #include "blst_util.h" @@ -37,8 +38,6 @@ const uint64_t inv_fft_expected[][4] = { {0xe92ffdda2306c7d4L, 0x54dd2afcd2dfb16bL, 0xf6554603677e87beL, 0x5dbc603bc53c7a39L}, {0xd8cda22e753b3117L, 0x880454ec72238f55L, 0xcaf6199fc14a5353L, 0x197df7c2f05866d4L}}; -void title(void) {} - void compare_sft_fft(void) { // Initialise: ascending values of i (could be anything), and arbitrary size unsigned int size = 12; @@ -126,6 +125,9 @@ void stride_fft(void) { for (int i = 0; i < width; i++) { TEST_CHECK(fr_equal(coeffs1 + i, coeffs2 + i)); } + + free_fft_settings(&fs1); + free_fft_settings(&fs2); } TEST_LIST = { diff --git a/src/fft_g1.c b/src/fft_g1.c index 41f4727..cf63556 100644 --- a/src/fft_g1.c +++ b/src/fft_g1.c @@ -18,7 +18,8 @@ #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) { +void fft_g1_slow(blst_p1 *out, const blst_p1 *in, uint64_t stride, const blst_fr *roots, uint64_t roots_stride, + uint64_t l) { blst_p1 v, last, jv; blst_fr r; for (uint64_t i = 0; i < l; i++) { @@ -34,7 +35,8 @@ void fft_g1_slow(blst_p1 *out, blst_p1 *in, uint64_t stride, blst_fr *roots, uin } // Fast Fourier Transform -void fft_g1_fast(blst_p1 *out, blst_p1 *in, uint64_t stride, blst_fr *roots, uint64_t roots_stride, uint64_t l) { +void fft_g1_fast(blst_p1 *out, const blst_p1 *in, uint64_t stride, const blst_fr *roots, uint64_t roots_stride, + uint64_t l) { uint64_t half = l / 2; if (half > 0) { // Tunable parameter fft_g1_fast(out, in, stride * 2, roots, roots_stride * 2, half); @@ -51,7 +53,7 @@ void fft_g1_fast(blst_p1 *out, blst_p1 *in, uint64_t stride, blst_fr *roots, uin } // The main entry point for forward and reverse FFTs -C_KZG_RET fft_g1(blst_p1 *out, blst_p1 *in, FFTSettings *fs, bool inv, uint64_t n) { +C_KZG_RET fft_g1(blst_p1 *out, const blst_p1 *in, const FFTSettings *fs, bool inv, uint64_t n) { uint64_t stride = fs->max_width / n; ASSERT(n <= fs->max_width, C_KZG_BADARGS); ASSERT(is_power_of_two(n), C_KZG_BADARGS); diff --git a/src/fft_g1.h b/src/fft_g1.h index 75da165..532b503 100644 --- a/src/fft_g1.h +++ b/src/fft_g1.h @@ -17,6 +17,8 @@ #include "c_kzg.h" #include "fft_common.h" -void fft_g1_slow(blst_p1 *out, blst_p1 *in, uint64_t stride, blst_fr *roots, uint64_t roots_stride, uint64_t l); -void fft_g1_fast(blst_p1 *out, blst_p1 *in, uint64_t stride, blst_fr *roots, uint64_t roots_stride, uint64_t l); -C_KZG_RET fft_g1(blst_p1 *out, blst_p1 *in, FFTSettings *fs, bool inv, uint64_t n); +void fft_g1_slow(blst_p1 *out, const blst_p1 *in, uint64_t stride, const blst_fr *roots, uint64_t roots_stride, + uint64_t l); +void fft_g1_fast(blst_p1 *out, const blst_p1 *in, uint64_t stride, const blst_fr *roots, uint64_t roots_stride, + uint64_t l); +C_KZG_RET fft_g1(blst_p1 *out, const blst_p1 *in, const FFTSettings *fs, bool inv, uint64_t n); diff --git a/src/fft_g1_test.c b/src/fft_g1_test.c index 00c9cdb..61c6ea5 100644 --- a/src/fft_g1_test.c +++ b/src/fft_g1_test.c @@ -16,10 +16,9 @@ #include "../inc/acutest.h" #include "debug_util.h" +#include "test_util.h" #include "fft_g1.h" -void title(void) {} - void make_data(blst_p1 *out, uint64_t n) { // Multiples of g1_gen if (n == 0) return; @@ -85,6 +84,9 @@ void stride_fft(void) { for (int i = 0; i < width; i++) { TEST_CHECK(blst_p1_is_equal(coeffs1 + i, coeffs2 + i)); } + + free_fft_settings(&fs1); + free_fft_settings(&fs2); } TEST_LIST = { diff --git a/src/fk20_proofs.c b/src/fk20_proofs.c new file mode 100644 index 0000000..c0febea --- /dev/null +++ b/src/fk20_proofs.c @@ -0,0 +1,222 @@ +/* + * 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. + */ + +// FK20 refers to this technique: https://github.com/khovratovich/Kate/blob/master/Kate_amortized.pdf + +#include // free() +#include // memcpy() +#include "fk20_proofs.h" +#include "fft_g1.h" +#include "c_kzg_util.h" + +// Log base 2 - only works for n a power of two +int log2_pow2(uint32_t n) { + const uint32_t b[] = {0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000}; + register uint32_t r; + r = (n & b[0]) != 0; + r |= ((n & b[1]) != 0) << 1; + r |= ((n & b[2]) != 0) << 2; + r |= ((n & b[3]) != 0) << 3; + r |= ((n & b[4]) != 0) << 4; + return r; +} + +// This simply wraps the macro to enforce the type check +uint32_t reverse_bits(uint32_t a) { + return rev_4byte(a); +} + +uint32_t reverse_bits_limited(uint32_t length, uint32_t value) { + int unused_bit_len = 32 - log2_pow2(length); + return reverse_bits(value) >> unused_bit_len; +} + +// In-place re-ordering of an array by the bit-reversal of the indices +// Can handle arrays of any type: provide the element size in `size` +C_KZG_RET reverse_bit_order(void *values, size_t size, uint64_t n) { + ASSERT(n >> 32 == 0, C_KZG_BADARGS); + ASSERT(is_power_of_two(n), C_KZG_BADARGS); + + byte tmp[size]; + int unused_bit_len = 32 - log2_pow2(n); + for (uint32_t i = 0; i < n; i++) { + uint32_t r = reverse_bits(i) >> unused_bit_len; + if (r > i) { + // Swap the two elements + memcpy(tmp, values + (i * size), size); + memcpy(values + (i * size), values + (r * size), size); + memcpy(values + (r * size), tmp, size); + } + } + + return C_KZG_OK; +} + +// Performs the first part of the Toeplitz matrix multiplication algorithm, which is a Fourier +// transform of the vector x extended +C_KZG_RET toeplitz_part_1(blst_p1 *out, const blst_p1 *x, uint64_t n, KZGSettings *ks) { + uint64_t n2 = n * 2; + blst_p1 identity_g1, *x_ext; + + ASSERT(c_kzg_malloc((void **)&x_ext, n2 * sizeof *x_ext) == C_KZG_OK, C_KZG_MALLOC); + + blst_p1_from_affine(&identity_g1, &identity_g1_affine); + for (uint64_t i = 0; i < n; i++) { + x_ext[i] = x[i]; + } + for (uint64_t i = n; i < n2; i++) { + x_ext[i] = identity_g1; + } + + ASSERT(fft_g1(out, x_ext, ks->fs, false, n2) == C_KZG_OK, C_KZG_ERROR); + + free(x_ext); + return C_KZG_OK; +} + +// Performs the second part of the Toeplitz matrix multiplication algorithm +C_KZG_RET toeplitz_part_2(blst_p1 *out, const poly *toeplitz_coeffs, const FK20SingleSettings *fk) { + blst_fr *toeplitz_coeffs_fft; + + ASSERT(toeplitz_coeffs->length == fk->x_ext_fft_len, C_KZG_BADARGS); + ASSERT(c_kzg_malloc((void **)&toeplitz_coeffs_fft, toeplitz_coeffs->length * sizeof *toeplitz_coeffs_fft) == + C_KZG_OK, + C_KZG_MALLOC); + + ASSERT(fft_fr(toeplitz_coeffs_fft, toeplitz_coeffs->coeffs, fk->ks->fs, false, toeplitz_coeffs->length) == C_KZG_OK, + C_KZG_ERROR); + + for (uint64_t i = 0; i < toeplitz_coeffs->length; i++) { + p1_mul(&out[i], &fk->x_ext_fft[i], &toeplitz_coeffs_fft[i]); + } + + free(toeplitz_coeffs_fft); + return C_KZG_OK; +} + +// Transform back and return the first half of the vector +C_KZG_RET toeplitz_part_3(blst_p1 *out, const blst_p1 *h_ext_fft, uint64_t length, const FK20SingleSettings *fk) { + ASSERT(fft_g1(out, h_ext_fft, fk->ks->fs, true, length) == C_KZG_OK, C_KZG_ERROR); + + return C_KZG_OK; +} + +void toeplitz_coeffs_step(poly *out, const poly *in) { + uint64_t n = in->length, n2 = n * 2; + + out->coeffs[0] = in->coeffs[n - 1]; + for (uint64_t i = 1; i <= n + 1; i++) { + out->coeffs[i] = fr_zero; + } + for (uint64_t i = n + 2; i < n2; i++) { + out->coeffs[i] = in->coeffs[i - (n + 1)]; + } +} + +// Special version of the FK20 for the situation of data availability checks: +// The upper half of the polynomial coefficients is always 0, so we do not need to extend to twice the size +// for Toeplitz matrix multiplication +C_KZG_RET fk20_single_da_opt(blst_p1 *out, const poly *p, FK20SingleSettings *fk) { + uint64_t n2 = p->length, n = n2 / 2; + blst_p1 *h, *h_ext_fft, identity_g1; + poly reduced_poly, toeplitz_coeffs; + C_KZG_RET ret; + + ASSERT(n2 <= fk->ks->fs->max_width, C_KZG_BADARGS); + ASSERT(is_power_of_two(n2), C_KZG_BADARGS); + + for (uint64_t i = n; i < n2; i++) { + ASSERT(fr_is_zero(&p->coeffs[i]), C_KZG_BADARGS); + } + + // The first half of the input polynomial + reduced_poly.coeffs = p->coeffs; + reduced_poly.length = n; + + ASSERT(init_poly(&toeplitz_coeffs, n2) == C_KZG_OK, C_KZG_MALLOC); + toeplitz_coeffs_step(&toeplitz_coeffs, &reduced_poly); + + ASSERT(c_kzg_malloc((void **)&h_ext_fft, toeplitz_coeffs.length * sizeof *h_ext_fft) == C_KZG_OK, C_KZG_MALLOC); + ASSERT((ret = toeplitz_part_2(h_ext_fft, &toeplitz_coeffs, fk)) == C_KZG_OK, + ret == C_KZG_MALLOC ? ret : C_KZG_ERROR); + ASSERT(c_kzg_malloc((void **)&h, toeplitz_coeffs.length * sizeof *h) == C_KZG_OK, C_KZG_MALLOC); + ASSERT(toeplitz_part_3(h, h_ext_fft, n2, fk) == C_KZG_OK, C_KZG_ERROR); + + blst_p1_from_affine(&identity_g1, &identity_g1_affine); + for (uint64_t i = n; i < n2; i++) { + h[i] = identity_g1; + } + + ASSERT(fft_g1(out, h, fk->ks->fs, false, n2) == C_KZG_OK, C_KZG_ERROR); + + free(h); + free(h_ext_fft); + free_poly(&toeplitz_coeffs); + return C_KZG_OK; +} + +C_KZG_RET da_using_fk20_single(blst_p1 *out, const poly *p, FK20SingleSettings *fk) { + uint64_t n = p->length, n2 = n * 2; + poly extended_poly; + + ASSERT(n2 <= fk->ks->fs->max_width, C_KZG_BADARGS); + ASSERT(is_power_of_two(n), C_KZG_BADARGS); + + ASSERT(init_poly(&extended_poly, n2) == C_KZG_OK, C_KZG_MALLOC); + + for (uint64_t i = 0; i < n; i++) { + extended_poly.coeffs[i] = p->coeffs[i]; + } + for (uint64_t i = n; i < n2; i++) { + extended_poly.coeffs[i] = fr_zero; + } + + ASSERT(fk20_single_da_opt(out, &extended_poly, fk) == C_KZG_OK, C_KZG_ERROR); + + ASSERT(reverse_bit_order(out, sizeof out[0], n2) == C_KZG_OK, C_KZG_ERROR); + + free_poly(&extended_poly); + return C_KZG_OK; +} + +C_KZG_RET new_fk20_single_settings(FK20SingleSettings *fk, uint64_t n2, KZGSettings *ks) { + int n = n2 / 2; + blst_p1 *x; + + ASSERT(n2 <= ks->fs->max_width, C_KZG_BADARGS); + ASSERT(is_power_of_two(n2), C_KZG_BADARGS); + ASSERT(n2 >= 2, C_KZG_BADARGS); + + fk->ks = ks; + fk->x_ext_fft_len = n2; + + ASSERT(c_kzg_malloc((void **)&x, n * sizeof *x) == C_KZG_OK, C_KZG_MALLOC); + ASSERT(c_kzg_malloc((void **)&fk->x_ext_fft, n2 * sizeof *fk->x_ext_fft) == C_KZG_OK, C_KZG_MALLOC); + + for (uint64_t i = 0; i < n - 1; i++) { + x[i] = ks->secret_g1[n - 2 - i]; + } + blst_p1_from_affine(&x[n - 1], &identity_g1_affine); + + ASSERT(toeplitz_part_1(fk->x_ext_fft, x, n, ks) == C_KZG_OK, C_KZG_ERROR); + + free(x); + return C_KZG_OK; +} + +void free_fk20_single_settings(FK20SingleSettings *fk) { + free(fk->x_ext_fft); +} \ No newline at end of file diff --git a/src/fk20_proofs.h b/src/fk20_proofs.h new file mode 100644 index 0000000..51a86c7 --- /dev/null +++ b/src/fk20_proofs.h @@ -0,0 +1,47 @@ +/* + * 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_proofs.h" + +// TODO: Benchmark some of the other options at https://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable +#define rev_byte(a) ((((a)&0xff) * 0x0202020202ULL & 0x010884422010ULL) % 1023) +#define rev_4byte(a) (rev_byte(a) << 24 | rev_byte((a) >> 8) << 16 | rev_byte((a) >> 16) << 8 | rev_byte((a) >> 24)) + +typedef struct { + KZGSettings *ks; + blst_p1 *x_ext_fft; + uint64_t x_ext_fft_len; +} FK20SingleSettings; + +typedef struct { + KZGSettings *ks; + uint64_t chunk_len; + blst_p1 **x_ext_fft_files; + uint64_t length; +} FK20MultiSettings; + +int log2_pow2(uint32_t n); +uint32_t reverse_bits(uint32_t a); +uint32_t reverse_bits_limited(uint32_t length, uint32_t value); +C_KZG_RET reverse_bit_order(void *values, size_t size, uint64_t n); +C_KZG_RET toeplitz_part_1(blst_p1 *out, const blst_p1 *x, uint64_t n, KZGSettings *ks); +C_KZG_RET toeplitz_part_2(blst_p1 *out, const poly *toeplitz_coeffs, const FK20SingleSettings *fk); +C_KZG_RET toeplitz_part_3(blst_p1 *out, const blst_p1 *h_ext_fft, uint64_t length, const FK20SingleSettings *fk); +C_KZG_RET fk20_single_da_opt(blst_p1 *out, const poly *p, FK20SingleSettings *fk); +C_KZG_RET da_using_fk20_single(blst_p1 *out, const poly *p, FK20SingleSettings *fk); +C_KZG_RET new_fk20_single_settings(FK20SingleSettings *fk, uint64_t n2, KZGSettings *ks); +void free_fk20_single_settings(FK20SingleSettings *fk); diff --git a/src/fk20_proofs_test.c b/src/fk20_proofs_test.c new file mode 100644 index 0000000..7e57084 --- /dev/null +++ b/src/fk20_proofs_test.c @@ -0,0 +1,208 @@ +/* + * 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 "test_util.h" +#include "fk20_proofs.h" + +void test_reverse_bits_macros(void) { + TEST_CHECK(128 == rev_byte(1)); + TEST_CHECK(128 == rev_byte(257)); + TEST_CHECK((uint32_t)1 << 31 == rev_4byte(1)); + TEST_CHECK(0x1e6a2c48 == rev_4byte(0x12345678)); + TEST_CHECK(0x00000000 == rev_4byte(0x00000000)); + TEST_CHECK(0xffffffff == rev_4byte(0xffffffff)); +} + +void test_reverse_bits_0(void) { + uint32_t actual, expected; + for (int i = 0; i < 32; i++) { + expected = (uint32_t)1 << (31 - i); + actual = reverse_bits((uint32_t)1 << i); + TEST_CHECK(expected == actual); + } +} + +void test_reverse_bits_1(void) { + TEST_CHECK(0x84c2a6e1 == reverse_bits(0x87654321)); +} + +void test_log2_pow2(void) { + int actual, expected; + for (int i = 0; i < 32; i++) { + expected = i; + actual = log2_pow2((uint32_t)1 << i); + TEST_CHECK(expected == actual); + } +} + +void test_reverse_bit_order_g1(void) { + int size = 10, n = 1 << size; + blst_p1 a[n], b[n]; + blst_fr tmp; + + for (int i = 0; i < n; i++) { + fr_from_uint64(&tmp, i); + p1_mul(&a[i], blst_p1_generator(), &tmp); + b[i] = a[i]; + } + + TEST_CHECK(C_KZG_OK == reverse_bit_order(a, sizeof(blst_p1), n)); + for (int i = 0; i < n; i++) { + TEST_CHECK(true == blst_p1_is_equal(&b[reverse_bits(i) >> (32 - size)], &a[i])); + } + + // Hand check a few select values + TEST_CHECK(true == blst_p1_is_equal(&b[0], &a[0])); + TEST_CHECK(false == blst_p1_is_equal(&b[1], &a[1])); + TEST_CHECK(true == blst_p1_is_equal(&b[n - 1], &a[n - 1])); +} + +void test_reverse_bit_order_fr(void) { + int size = 12, n = 1 << size; + blst_fr a[n], b[n]; + + for (int i = 0; i < n; i++) { + fr_from_uint64(&a[i], i); + b[i] = a[i]; + } + + TEST_CHECK(C_KZG_OK == reverse_bit_order(a, sizeof(blst_fr), n)); + for (int i = 0; i < n; i++) { + TEST_CHECK(true == fr_equal(&b[reverse_bits(i) >> (32 - size)], &a[i])); + } + + // Hand check a few select values + TEST_CHECK(true == fr_equal(&b[0], &a[0])); + TEST_CHECK(false == fr_equal(&b[1], &a[1])); + TEST_CHECK(true == fr_equal(&b[n - 1], &a[n - 1])); +} + +void fk_single(void) { + // 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}; + int poly_len = sizeof coeffs / sizeof coeffs[0]; + + // The FFT settings size + uint64_t n = 5, n_len = (uint64_t)1 << n; + + FFTSettings fs; + KZGSettings ks; + FK20SingleSettings fk; + uint64_t secrets_len = n_len + 1; + blst_p1 *s1; + blst_p2 *s2; + poly p; + blst_p1 commitment, all_proofs[2 * poly_len], proof; + blst_fr x, y; + bool result; + + TEST_CHECK(n_len >= 2 * poly_len); + TEST_CHECK(init_poly_with_coeffs(&p, coeffs, poly_len) == C_KZG_OK); + + // Initialise the secrets and data structures + generate_trusted_setup(&s1, &s2, &secret, secrets_len); + TEST_CHECK(C_KZG_OK == new_fft_settings(&fs, n)); + TEST_CHECK(C_KZG_OK == new_kzg_settings(&ks, &fs, s1, s2, secrets_len)); + TEST_CHECK(C_KZG_OK == new_fk20_single_settings(&fk, 2 * poly_len, &ks)); + + // Generate the proofs + commit_to_poly(&commitment, &p, &ks); + TEST_CHECK(da_using_fk20_single(all_proofs, &p, &fk) == C_KZG_OK); + + // Verify the proof at each position + for (uint64_t i = 0; i < 2 * poly_len; i++) { + x = fs.expanded_roots_of_unity[i]; + eval_poly(&y, &p, &x); + proof = all_proofs[reverse_bits_limited(2 * poly_len, i)]; + + TEST_CHECK(C_KZG_OK == check_proof_single(&result, &ks, &commitment, &proof, &x, &y)); + TEST_CHECK(true == result); + } + + // Clean up + free_poly(&p); + free_fft_settings(&fs); + free_kzg_settings(&ks); + free_fk20_single_settings(&fk); + free_trusted_setup(s1, s2); +} + +void fk_single_strided(void) { + // 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}; + int poly_len = sizeof coeffs / sizeof coeffs[0]; + + // We can set up the FFTs for bigger widths if we wish. + // This is a useful canary for issues elsewhere in the code. + uint64_t n = 8, n_len = (uint64_t)1 << n; + uint64_t stride = n_len / (2 * poly_len); + + FFTSettings fs; + KZGSettings ks; + FK20SingleSettings fk; + uint64_t secrets_len = n_len + 1; + blst_p1 *s1; + blst_p2 *s2; + poly p; + blst_p1 commitment, all_proofs[2 * poly_len], proof; + blst_fr x, y; + bool result; + + TEST_CHECK(n_len >= 2 * poly_len); + TEST_CHECK(init_poly_with_coeffs(&p, coeffs, poly_len) == C_KZG_OK); + + // Initialise the secrets and data structures + generate_trusted_setup(&s1, &s2, &secret, secrets_len); + TEST_CHECK(C_KZG_OK == new_fft_settings(&fs, n)); + TEST_CHECK(C_KZG_OK == new_kzg_settings(&ks, &fs, s1, s2, secrets_len)); + TEST_CHECK(C_KZG_OK == new_fk20_single_settings(&fk, 2 * poly_len, &ks)); + + // Generate the proofs + commit_to_poly(&commitment, &p, &ks); + TEST_CHECK(da_using_fk20_single(all_proofs, &p, &fk) == C_KZG_OK); + + // Verify the proof at each position + for (uint64_t i = 0; i < 2 * poly_len; i++) { + x = fs.expanded_roots_of_unity[i * stride]; + eval_poly(&y, &p, &x); + proof = all_proofs[reverse_bits_limited(2 * poly_len, i)]; + + TEST_CHECK(C_KZG_OK == check_proof_single(&result, &ks, &commitment, &proof, &x, &y)); + TEST_CHECK(true == result); + } + + // Clean up + free_poly(&p); + free_fft_settings(&fs); + free_kzg_settings(&ks); + free_fk20_single_settings(&fk); + free_trusted_setup(s1, s2); +} + +TEST_LIST = { + {"FK20_PROOFS_TEST", title}, + {"test_reverse_bits_macros", test_reverse_bits_macros}, + {"test_reverse_bits_0", test_reverse_bits_0}, + {"test_reverse_bits_1", test_reverse_bits_1}, + {"test_log2_pow2", test_log2_pow2}, + {"test_reverse_bit_order_g1", test_reverse_bit_order_g1}, + {"test_reverse_bit_order_fr", test_reverse_bit_order_fr}, + {"fk_single", fk_single}, + {"fk_single_strided", fk_single_strided}, + {NULL, NULL} /* zero record marks the end of the list */ +}; diff --git a/src/kzg_proofs.c b/src/kzg_proofs.c index f978cc3..58a2ee2 100644 --- a/src/kzg_proofs.c +++ b/src/kzg_proofs.c @@ -17,17 +17,7 @@ #include // NULL #include "kzg_proofs.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; // What's this for? - ks->secret_g2 = secret_g2; - ks->length = length; - return C_KZG_OK; -} - -void commit_to_poly(blst_p1 *out, const KZGSettings *ks, const poly *p) { +void commit_to_poly(blst_p1 *out, const poly *p, const KZGSettings *ks) { linear_combination_g1(out, ks->secret_g1, p->coeffs, p->length); } @@ -75,7 +65,7 @@ C_KZG_RET compute_proof_multi(blst_p1 *out, const KZGSettings *ks, poly *p, cons // Calculate q = p / (x^n - x0^n) ASSERT(poly_long_div(&q, p, &divisor) == C_KZG_OK, C_KZG_ERROR); - commit_to_poly(out, ks, &q); + commit_to_poly(out, &q, ks); free_poly(&q); free_poly(&divisor); @@ -112,7 +102,7 @@ C_KZG_RET check_proof_multi(bool *out, const KZGSettings *ks, const blst_p1 *com p2_sub(&xn_minus_yn, &ks->secret_g2[n], &xn2); // [interpolation_polynomial(s)]_1 - commit_to_poly(&is1, ks, &interp); + commit_to_poly(&is1, &interp, ks); // [commitment - interpolation_polynomial(s)]_1 = [commit]_1 - [interpolation_polynomial(s)]_1 p1_sub(&commit_minus_interp, commitment, &is1); @@ -122,3 +112,18 @@ C_KZG_RET check_proof_multi(bool *out, const KZGSettings *ks, const blst_p1 *com free_poly(&interp); return C_KZG_OK; } + +// We don't allocate space for the secrets s1 and s2 here as they are assumed to be pre-generated +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; // What's this for? + ks->secret_g2 = secret_g2; + ks->length = length; + return C_KZG_OK; +} + +// This is a no-op since s1 and s2 are assumed to have been provided externally +void free_kzg_settings(KZGSettings *ks) {} \ No newline at end of file diff --git a/src/kzg_proofs.h b/src/kzg_proofs.h index 5c7b24e..515ad3c 100644 --- a/src/kzg_proofs.h +++ b/src/kzg_proofs.h @@ -26,11 +26,12 @@ typedef struct { 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); -void commit_to_poly(blst_p1 *out, const KZGSettings *ks, const poly *p); +void commit_to_poly(blst_p1 *out, const poly *p, const KZGSettings *ks); C_KZG_RET compute_proof_single(blst_p1 *out, const KZGSettings *ks, poly *p, const blst_fr *x0); C_KZG_RET check_proof_single(bool *out, const KZGSettings *ks, const blst_p1 *commitment, const blst_p1 *proof, const blst_fr *x, blst_fr *y); C_KZG_RET compute_proof_multi(blst_p1 *out, const KZGSettings *ks, poly *p, const blst_fr *x0, uint64_t n); C_KZG_RET check_proof_multi(bool *out, const KZGSettings *ks, const blst_p1 *commitment, const blst_p1 *proof, const blst_fr *x, const blst_fr *ys, uint64_t n); +C_KZG_RET new_kzg_settings(KZGSettings *ks, FFTSettings *fs, blst_p1 *secret_g1, blst_p2 *secret_g2, uint64_t length); +void free_kzg_settings(KZGSettings *ks); diff --git a/src/kzg_proofs_test.c b/src/kzg_proofs_test.c index 88922cd..82d59fe 100644 --- a/src/kzg_proofs_test.c +++ b/src/kzg_proofs_test.c @@ -16,28 +16,9 @@ #include "../inc/acutest.h" #include "debug_util.h" +#include "test_util.h" #include "kzg_proofs.h" -// The generator for our "trusted" setup -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? - -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 = fr_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 title(void) { - ; -} - void proof_single(void) { // 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}; @@ -46,10 +27,10 @@ void proof_single(void) { FFTSettings fs; KZGSettings ks; + blst_p1 *s1; + blst_p2 *s2; poly p; 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, value; bool result; @@ -60,13 +41,13 @@ void proof_single(void) { } // Initialise the secrets and data structures - generate_setup(s1, s2, &secret, secrets_len); + generate_trusted_setup(&s1, &s2, &secret, secrets_len); TEST_CHECK(C_KZG_OK == new_fft_settings(&fs, 4)); // ln_2 of poly_len TEST_CHECK(C_KZG_OK == new_kzg_settings(&ks, &fs, s1, s2, secrets_len)); // Compute the proof for x = 25 fr_from_uint64(&x, 25); - commit_to_poly(&commitment, &ks, &p); + commit_to_poly(&commitment, &p, &ks); TEST_CHECK(C_KZG_OK == compute_proof_single(&proof, &ks, &p, &x)); eval_poly(&value, &p, &x); @@ -81,6 +62,7 @@ void proof_single(void) { TEST_CHECK(false == result); free_fft_settings(&fs); + free_kzg_settings(&ks); free_poly(&p); free(s1); free(s2); @@ -93,6 +75,8 @@ void proof_multi(void) { FFTSettings fs1, fs2; KZGSettings ks1, ks2; + blst_p1 *s1; + blst_p2 *s2; poly p; blst_p1 commitment, proof; blst_fr x, tmp; @@ -103,8 +87,6 @@ void proof_multi(void) { 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 init_poly(&p, poly_len); @@ -113,12 +95,12 @@ void proof_multi(void) { } // Initialise the secrets and data structures - generate_setup(s1, s2, &secret, secrets_len); + generate_trusted_setup(&s1, &s2, &secret, secrets_len); TEST_CHECK(C_KZG_OK == new_fft_settings(&fs1, 4)); // ln_2 of poly_len TEST_CHECK(C_KZG_OK == new_kzg_settings(&ks1, &fs1, s1, s2, secrets_len)); // Commit to the polynomial - commit_to_poly(&commitment, &ks1, &p); + commit_to_poly(&commitment, &p, &ks1); TEST_CHECK(C_KZG_OK == new_fft_settings(&fs2, coset_scale)); TEST_CHECK(C_KZG_OK == new_kzg_settings(&ks2, &fs2, s1, s2, secrets_len)); @@ -144,6 +126,8 @@ void proof_multi(void) { free_fft_settings(&fs1); free_fft_settings(&fs2); + free_kzg_settings(&ks1); + free_kzg_settings(&ks2); free_poly(&p); free(s1); free(s2); @@ -154,20 +138,25 @@ void commit_to_nil_poly(void) { FFTSettings fs; KZGSettings ks; uint64_t secrets_len = 16; - blst_p1 *s1 = malloc(secrets_len * sizeof(blst_p1)); - blst_p2 *s2 = malloc(secrets_len * sizeof(blst_p2)); + blst_p1 *s1; + blst_p2 *s2; blst_p1 result; blst_p1_affine result_affine; // Initialise the (arbitrary) secrets and data structures - generate_setup(s1, s2, &secret, secrets_len); + generate_trusted_setup(&s1, &s2, &secret, secrets_len); TEST_CHECK(C_KZG_OK == new_fft_settings(&fs, 4)); TEST_CHECK(C_KZG_OK == new_kzg_settings(&ks, &fs, s1, s2, secrets_len)); init_poly(&a, 0); - commit_to_poly(&result, &ks, &a); + commit_to_poly(&result, &a, &ks); blst_p1_to_affine(&result_affine, &result); TEST_CHECK(blst_p1_affine_is_equal(&identity_g1_affine, &result_affine)); + + free_fft_settings(&fs); + free_kzg_settings(&ks); + free(s1); + free(s2); } TEST_LIST = { diff --git a/src/poly.c b/src/poly.c index 9ed6609..9281ff9 100644 --- a/src/poly.c +++ b/src/poly.c @@ -25,7 +25,15 @@ static void poly_factor_div(blst_fr *out, const blst_fr *a, const blst_fr *b) { C_KZG_RET init_poly(poly *out, const uint64_t length) { out->length = length; - return c_kzg_malloc((void **)&out->coeffs, length * sizeof(blst_fr)); + return c_kzg_malloc((void **)&out->coeffs, length * sizeof *out->coeffs); +} + +C_KZG_RET init_poly_with_coeffs(poly *out, const uint64_t *coeffs, const uint64_t length) { + ASSERT(init_poly(out, length) == C_KZG_OK, C_KZG_MALLOC); + for (uint64_t i = 0; i < length; i++) { + fr_from_uint64(&out->coeffs[i], coeffs[i]); + } + return C_KZG_OK; } void free_poly(poly *p) { diff --git a/src/poly.h b/src/poly.h index ef20d15..6ef0f3b 100644 --- a/src/poly.h +++ b/src/poly.h @@ -23,6 +23,7 @@ typedef struct { } poly; C_KZG_RET init_poly(poly *out, const uint64_t length); +C_KZG_RET init_poly_with_coeffs(poly *out, const uint64_t *coeffs, const uint64_t length); void free_poly(poly *p); void eval_poly(blst_fr *out, const poly *p, const blst_fr *x); uint64_t poly_quotient_length(const poly *dividend, const poly *divisor); diff --git a/src/poly_test.c b/src/poly_test.c index 206cb4c..f3bd03e 100644 --- a/src/poly_test.c +++ b/src/poly_test.c @@ -16,12 +16,9 @@ #include "../inc/acutest.h" #include "debug_util.h" +#include "test_util.h" #include "poly.h" -void title(void) { - ; -} - void poly_div_length(void) { poly a, b; init_poly(&a, 17); diff --git a/src/test_util.c b/src/test_util.c new file mode 100644 index 0000000..291b7d2 --- /dev/null +++ b/src/test_util.c @@ -0,0 +1,42 @@ +/* + * 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 // malloc() +#include "test_util.h" +#include "blst_util.h" + +void generate_trusted_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 = fr_one; + + *s1 = malloc(n * sizeof(blst_p1)); + *s2 = malloc(n * sizeof(blst_p2)); + + 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 free_trusted_setup(blst_p1 *s1, blst_p2 *s2) { + free(s1); + free(s2); +} + +// Dummy function used to get the test-suite to print a title +void title(void) {} diff --git a/src/test_util.h b/src/test_util.h new file mode 100644 index 0000000..51c7cdf --- /dev/null +++ b/src/test_util.h @@ -0,0 +1,26 @@ +/* + * 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" + +// The generator for our "trusted" setup +static const 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? + +void generate_trusted_setup(blst_p1 **s1, blst_p2 **s2, const blst_scalar *secret, const uint64_t n); +void free_trusted_setup(blst_p1 *s1, blst_p2 *s2); +void title(void);