Add FK20 single proofs

This commit is contained in:
Ben Edgington 2021-02-10 11:55:38 +00:00
parent 8986f1cde3
commit f476a0f6ce
23 changed files with 660 additions and 86 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
*.a
*_test
*_bench
*_debug
*.prof
*.out
*.log

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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));

View File

@ -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) {

View File

@ -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

View File

@ -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]));
}

View File

@ -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 = {

View File

@ -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);

View File

@ -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);

View File

@ -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 = {

222
src/fk20_proofs.c Normal file
View File

@ -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 <stdlib.h> // free()
#include <string.h> // 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);
}

47
src/fk20_proofs.h Normal file
View File

@ -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);

208
src/fk20_proofs_test.c Normal file
View File

@ -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 */
};

View File

@ -17,17 +17,7 @@
#include <stddef.h> // 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) {}

View File

@ -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);

View File

@ -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 = {

View File

@ -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) {

View File

@ -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);

View File

@ -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);

42
src/test_util.c Normal file
View File

@ -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 <stdlib.h> // 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) {}

26
src/test_util.h Normal file
View File

@ -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);