Start improving the docs

This commit is contained in:
Ben Edgington 2021-03-01 11:39:18 +00:00
parent a28860ae39
commit e2cbccdc9d
4 changed files with 51 additions and 16 deletions

View File

@ -15,6 +15,16 @@ Done so far:
- Calculation of zero polynomials
- Data recovery from samples
That's basically all the necessary stuff for Eth2 use cases. Things remaining (aside from tidying up, which is never ending):
- [ ] Document the underlying mathematics to help with checking correctness of the implementation and writing better tests
- [ ] End-to-end tests
- [ ] Performance tuning
- [ ] Robustness checking (don't crash on errors; return the correct error codes; no buffer overflows)
- [ ] Java interface
- [ ] (Optional) Use alternative back-end libraries to Blst (e.g. [Herumi mcl](https://github.com/herumi/mcl))
- [ ] Nice build process
- [ ] Make it portable
## Install
Build the [Blst library](https://github.com/supranational/blst) following the instructions there. Then,
@ -65,7 +75,7 @@ cd src
make bench
```
You can run individual benchmarks, and optionally specify how long to run each test size:
You can run individual benchmarks, and optionally specify how long (in seconds) to run each test size:
```
make fft_fr_bench

View File

@ -33,6 +33,13 @@
* PRIMITIVE_ROOT = 5
* [pow(PRIMITIVE_ROOT, (MODULUS - 1) // (2**i), MODULUS) for i in range(32)]
* @endcode
*
* Note: Being a "primitive root" in this context means that r^k != 1 for any k < q-1 where q is the modulus. So
* powers of r generate the field. This is also known as being a "primitive element".
*
* This is easy to check for: we just require that r^((q-1)/2) != 1. Instead of 5, we could use 7, 10, 13, 14, 15, 20...
* to create the roots of unity below. There are a lot of primitive roots:
* https://crypto.stanford.edu/pbc/notes/numbertheory/gen.html
*/
static const uint64_t scale2_root_of_unity[][4] = {
{0x0000000000000001L, 0x0000000000000000L, 0x0000000000000000L, 0x0000000000000000L},

View File

@ -26,17 +26,21 @@
#include "utility.h"
#include "zero_poly.h"
/** 5 is a primitive element, but actually this can be pretty much anything not 1 or 0*/
#define SHIFT_FACTOR 5
/**
* Shift a polynomial in place.
*
* Multiplies each coefficient by 1 / shift_factor ^ i.
* Multiplies each coefficient by `1 / shift_factor ^ i`. Equivalent to creating a polynomial that evaluates at `x * k`
* rather than `x`.
*
* @param[out,in] p The polynomial coefficients to be shifted
* @param[in] len_p Length of the polynomial coefficients
*/
void shift_poly(fr_t *p, uint64_t len_p) {
fr_t shift_factor, factor_power, inv_factor;
fr_from_uint64(&shift_factor, 5); // primitive root of unity
fr_from_uint64(&shift_factor, SHIFT_FACTOR);
fr_inv(&inv_factor, &shift_factor);
factor_power = fr_one;
@ -49,14 +53,15 @@ void shift_poly(fr_t *p, uint64_t len_p) {
/**
* Unshift a polynomial in place.
*
* Multiplies each coefficient by shift_factor ^ i.
* Multiplies each coefficient by `shift_factor ^ i`. Equivalent to creating a polynomial that evaluates at `x / k`
* rather than `x`.
*
* @param[out,in] p The polynomial coefficients to be unshifted
* @param[in] len_p Length of the polynomial coefficients
*/
void unshift_poly(fr_t *p, uint64_t len_p) {
fr_t shift_factor, factor_power;
fr_from_uint64(&shift_factor, 5); // primitive root of unity
fr_from_uint64(&shift_factor, SHIFT_FACTOR);
factor_power = fr_one;
for (uint64_t i = 1; i < len_p; i++) {
@ -70,6 +75,8 @@ void unshift_poly(fr_t *p, uint64_t len_p) {
*
* Assumes that the inverse FFT of the original data has the upper half of its values equal to zero.
*
* See https://ethresear.ch/t/reed-solomon-erasure-code-recovery-in-n-log-2-n-time-with-ffts/3039
*
* @param[out] reconstructed_data An attempted reconstruction of the original data
* @param[in] samples The data to be reconstructed, with `fr_null` set for missing values
* @param[in] len_samples The length of @p samples and @p reconstructed_data

View File

@ -17,7 +17,7 @@
/**
* @file zero_poly.c
*
* Methods for constructing zero polynomials and reconstructing polynomials from partial evaluations on a subgroup.
* Methods for constructing polynomials that evaluate to zero for given lists of powers of roots of unity.
*/
#include "zero_poly.h"
@ -26,7 +26,10 @@
#include "utility.h"
/**
* Process a slice of missing indices into a leaf.
* Calculates the minimal polynomial that evaluates to zero for powers of roots of unity at the given indices.
*
* Uses straightforward multiplication to calculate the product of `(x - r^i)` where `r` is a root of unity and the `i`s
* are the indices at which it must evaluate to zero. This results in a polynomial of degree @p len_indices.
*
* @param[out] dst The resulting leaf, length @p len_dst
* @param[in] len_dst Length of the output leaf, @p dst
@ -66,7 +69,7 @@ C_KZG_RET do_zero_poly_mul_leaf(fr_t *dst, uint64_t len_dst, const uint64_t *ind
}
/**
* Copy @p p to @p out, padding to length @p n with zeros.
* Copy @p p to @p out, padding to length @p p_len with zeros.
*
* @param[out] out A copy of @p p padded to length @p n with zeros
* @param[in] out_len The length of the desired output data, @p out
@ -87,9 +90,10 @@ C_KZG_RET pad_p(fr_t *out, uint64_t out_len, const fr_t *p, uint64_t p_len) {
}
/**
* Calculate the convolution of the input polynomials.
* Calculate the product of the input polynomials via convolution.
*
* Pad the polynomials in @p ps, perform FFTs, multiply the results together, and apply an inverse FFT to the result.
* Pad the polynomials in @p ps, perform FFTs, point-wise multiply the results together, and apply an inverse FFT to the
* result.
*
* @param[out] dst The result of the convolution
* @param[in] len_dst Length of the output, a power of two
@ -109,7 +113,9 @@ C_KZG_RET reduce_leaves(fr_t *dst, uint64_t len_dst, fr_t *scratch, uint64_t len
const uint64_t *len_p, const FFTSettings *fs) {
CHECK(is_power_of_two(len_dst));
CHECK(len_ps > 0);
CHECK(len_ps * len_p[0] <= len_dst); // needed, or just len_p <= len_dst
// The degree of the output is the sum of the degrees of the input polynomials.
// TODO A more relaxed check should be ok: `len_ps * (len_p[0] - 1) < len_dst` (or even sum up the lengths)
CHECK(len_ps * len_p[0] <= len_dst);
CHECK(len_scratch >= 3 * len_dst);
// Split `scratch` up into three equally sized working arrays
@ -117,7 +123,7 @@ C_KZG_RET reduce_leaves(fr_t *dst, uint64_t len_dst, fr_t *scratch, uint64_t len
fr_t *mul_eval_ps = scratch + len_dst;
fr_t *p_eval = scratch + 2 * len_dst;
// Do the last leaf first: it may be shorter than the others, and the padding can remain in place for the rest.
// Do the last leaf first: it may be shorter than the others and the padding can remain in place for the rest.
TRY(pad_p(p_padded, len_dst, ps[len_ps - 1], len_p[len_ps - 1]));
TRY(fft_fr(mul_eval_ps, p_padded, false, len_dst, fs));
@ -135,12 +141,17 @@ C_KZG_RET reduce_leaves(fr_t *dst, uint64_t len_dst, fr_t *scratch, uint64_t len
}
/**
* Calculate a polynomial that evaluates to zero for powers of roots of one that correspond to missing indices.
* Calculate the minimal polynomial that evaluates to zero for powers of roots of unity that correspond to missing
* indices.
*
* Also calculates the FFT.
* This is done by simply multiplying together `(x - r^i)` for all the `i` that are missing indices, using a combination
* of direct multiplication (#do_zero_poly_mul_leaf) and multiplication via convolution (#reduce_leaves).
*
* Also calculates the FFT (the "evaluation polynomial").
*
* @remark Fails for very high numbers of missing indices. For example, with `fs.max_width = 256` and `length = 256`,
* this will fail for len_missing = 253 or more. In this case, `length` (and maybe `fs.max_width`) needs to be doubled.
* But this failure is probably OK for our use case.
*
* @remark Note that @p zero_poly is used as workspace during calculation.
*
@ -162,7 +173,7 @@ C_KZG_RET zero_polynomial_via_multiplication(fr_t *zero_eval, fr_t *zero_poly, u
const uint64_t *missing_indices, uint64_t len_missing,
const FFTSettings *fs) {
if (len_missing == 0) {
*zero_poly_len = length;
*zero_poly_len = 0;
for (uint64_t i = 0; i < length; i++) {
zero_eval[i] = fr_zero;
zero_poly[i] = fr_zero;
@ -212,9 +223,9 @@ C_KZG_RET zero_polynomial_via_multiplication(fr_t *zero_eval, fr_t *zero_poly, u
int reduction_factor = 4; // must be a power of 2
TRY(new_fr_array(&scratch, n * 3));
// All the leaves are the same length, except possibly the last leaf, but that's ok.
while (leaf_count > 1) {
uint64_t reduced_count = (leaf_count + reduction_factor - 1) / reduction_factor;
// All the leaves are the same length, except possibly the last leaf, but that's ok.
uint64_t leaf_size = leaf_lengths[0];
for (uint64_t i = 0; i < reduced_count; i++) {
uint64_t start = i * reduction_factor;