From ff4f3b731f9c150784e6b86212c1d3d052f3330a Mon Sep 17 00:00:00 2001 From: Justin Traglia <95511699+jtraglia@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:50:06 -0500 Subject: [PATCH] Split C code into more files (#471) * Split C code into more files * Fix rust bindings * Add common/fr.* files * Do more common splits * More common split/cleanup * Fix lincomb definition * Get rid of common/constants.h * Get rid of common/types.h * Move common/blob to eip4844/blob * Delete test/debug.h * Add dir to all includes * Generate rust bindings * Set InsertNewlineAtEOF to true * Move preprocessor thing down * Merge g1/g2 into ec * Move KZGCommitment/Proof to eip4844 * Remove ret.c & settings.c * Rename helpers to utils * Move settings.h to setup * Rename api.c/h to .c/h * Create new eip7594/poly files * Split eip7594 into fk20 & recovery files * Move verification helpers to verify section --- bindings/node.js/Makefile | 17 +- bindings/rust/build.rs | 17 +- bindings/rust/src/bindings/generated.rs | 14 +- src/.clang-format | 1 + src/Makefile | 18 +- src/ckzg.c | 20 +- src/ckzg.h | 6 +- src/common.c | 610 ------------- src/common.h | 283 ------ src/common/alloc.c | 106 +++ src/common/alloc.h | 58 ++ src/common/bytes.c | 151 ++++ src/common/bytes.h | 78 ++ src/common/ec.c | 57 ++ src/common/ec.h | 52 ++ src/common/fr.c | 129 +++ src/common/fr.h | 74 ++ src/common/lincomb.c | 134 +++ src/{debug.h => common/lincomb.h} | 22 +- src/common/ret.h | 29 + src/common/utils.c | 188 ++++ src/common/utils.h | 44 + src/{debug.c => eip4844/blob.c} | 53 +- src/eip4844/blob.h | 59 ++ src/{ => eip4844}/eip4844.c | 15 +- src/{ => eip4844}/eip4844.h | 20 +- src/eip7594/cell.c | 32 + src/{eip7594.h => eip7594/cell.h} | 39 +- src/{ => eip7594}/eip7594.c | 1061 +++++------------------ src/eip7594/eip7594.h | 59 ++ src/{ => eip7594}/fft.c | 102 ++- src/{ => eip7594}/fft.h | 16 +- src/eip7594/fk20.c | 178 ++++ src/eip7594/fk20.h | 36 + src/eip7594/poly.c | 78 ++ src/{setup.h => eip7594/poly.h} | 28 +- src/eip7594/recovery.c | 352 ++++++++ src/eip7594/recovery.h | 41 + src/setup/settings.h | 68 ++ src/{ => setup}/setup.c | 12 +- src/setup/setup.h | 110 +++ src/{ => test}/tests.c | 1 - src/{ => test}/tinytest.h | 0 43 files changed, 2564 insertions(+), 1904 deletions(-) delete mode 100644 src/common.c delete mode 100644 src/common.h create mode 100644 src/common/alloc.c create mode 100644 src/common/alloc.h create mode 100644 src/common/bytes.c create mode 100644 src/common/bytes.h create mode 100644 src/common/ec.c create mode 100644 src/common/ec.h create mode 100644 src/common/fr.c create mode 100644 src/common/fr.h create mode 100644 src/common/lincomb.c rename src/{debug.h => common/lincomb.h} (76%) create mode 100644 src/common/ret.h create mode 100644 src/common/utils.c create mode 100644 src/common/utils.h rename src/{debug.c => eip4844/blob.c} (51%) create mode 100644 src/eip4844/blob.h rename src/{ => eip4844}/eip4844.c (98%) rename src/{ => eip4844}/eip4844.h (81%) create mode 100644 src/eip7594/cell.c rename src/{eip7594.h => eip7594/cell.h} (74%) rename src/{ => eip7594}/eip7594.c (54%) create mode 100644 src/eip7594/eip7594.h rename src/{ => eip7594}/fft.c (72%) rename src/{ => eip7594}/fft.h (81%) create mode 100644 src/eip7594/fk20.c create mode 100644 src/eip7594/fk20.h create mode 100644 src/eip7594/poly.c rename src/{setup.h => eip7594/poly.h} (66%) create mode 100644 src/eip7594/recovery.c create mode 100644 src/eip7594/recovery.h create mode 100644 src/setup/settings.h rename src/{ => setup}/setup.c (98%) create mode 100644 src/setup/setup.h rename src/{ => test}/tests.c (99%) rename src/{ => test}/tinytest.h (100%) diff --git a/bindings/node.js/Makefile b/bindings/node.js/Makefile index 597bb2eee..cbc559f4f 100644 --- a/bindings/node.js/Makefile +++ b/bindings/node.js/Makefile @@ -31,20 +31,13 @@ build: install clean @# Prepare the dependencies directory @mkdir -p deps/c-kzg @cp -r ../../blst deps - @# Copy source files + @# Copy files @cp ../../src/ckzg.c deps/c-kzg - @cp ../../src/common.c deps/c-kzg - @cp ../../src/eip4844.c deps/c-kzg - @cp ../../src/eip7594.c deps/c-kzg - @cp ../../src/fft.c deps/c-kzg - @cp ../../src/setup.c deps/c-kzg - @# Copy header files @cp ../../src/ckzg.h deps/c-kzg - @cp ../../src/common.h deps/c-kzg - @cp ../../src/eip4844.h deps/c-kzg - @cp ../../src/eip7594.h deps/c-kzg - @cp ../../src/fft.h deps/c-kzg - @cp ../../src/setup.h deps/c-kzg + @cp -r ../../src/common deps/c-kzg + @cp -r ../../src/eip4844 deps/c-kzg + @cp -r ../../src/eip7594 deps/c-kzg + @cp -r ../../src/setup deps/c-kzg @# Copy trusted setup @cp ../../src/trusted_setup.txt deps/c-kzg @# Build the bindings diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs index 984f4fa1e..45eb72de1 100644 --- a/bindings/rust/build.rs +++ b/bindings/rust/build.rs @@ -22,6 +22,7 @@ fn main() { cc.flag("/std:c11"); } + cc.include(c_src_dir.clone()); cc.include(blst_headers_dir.clone()); cc.warnings(false); cc.file(c_src_dir.join("ckzg.c")); @@ -36,6 +37,7 @@ fn main() { let bindings_out_path = root_dir.join("bindings/rust/src/bindings/generated.rs"); make_bindings( header_path.to_str().expect("valid header path"), + c_src_dir.to_str().expect("valid c src path"), blst_headers_dir.to_str().expect("valid blst header path"), &bindings_out_path, ); @@ -46,7 +48,12 @@ fn main() { } #[cfg(feature = "generate-bindings")] -fn make_bindings(header_path: &str, blst_headers_dir: &str, bindings_out_path: &std::path::Path) { +fn make_bindings( + header_path: &str, + c_src_dir: &str, + blst_headers_dir: &str, + bindings_out_path: &std::path::Path, +) { use bindgen::Builder; #[derive(Debug)] @@ -77,13 +84,15 @@ fn make_bindings(header_path: &str, blst_headers_dir: &str, bindings_out_path: & * Header definitions. */ .header(header_path) + .clang_args([format!("-I{c_src_dir}")]) .clang_args([format!("-I{blst_headers_dir}")]) // Get bindings for functions defined in the EIP files. .allowlist_type("C_KZG_RET") .allowlist_var("BYTES_PER_.*") - .allowlist_var("FIELD_ELEMENTS_PER_BLOB") - .allowlist_var("FIELD_ELEMENTS_PER_EXT_BLOB") - .allowlist_file(".*eip.*.h") + .allowlist_var("FIELD_ELEMENTS_PER_.*") + .allowlist_var("CELLS_PER_EXT_BLOB") + .allowlist_file(".*eip4844.h") + .allowlist_file(".*eip7594.h") .allowlist_file(".*setup.h") /* * Cleanup instructions. diff --git a/bindings/rust/src/bindings/generated.rs b/bindings/rust/src/bindings/generated.rs index 82f110968..27b9cecd5 100644 --- a/bindings/rust/src/bindings/generated.rs +++ b/bindings/rust/src/bindings/generated.rs @@ -2,15 +2,15 @@ use libc::FILE; +pub const BYTES_PER_FIELD_ELEMENT: usize = 32; pub const BYTES_PER_COMMITMENT: usize = 48; pub const BYTES_PER_PROOF: usize = 48; -pub const BYTES_PER_FIELD_ELEMENT: usize = 32; pub const FIELD_ELEMENTS_PER_BLOB: usize = 4096; -pub const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 8192; pub const BYTES_PER_BLOB: usize = 131072; +pub const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 8192; pub const FIELD_ELEMENTS_PER_CELL: usize = 64; -pub const CELLS_PER_EXT_BLOB: usize = 128; pub const BYTES_PER_CELL: usize = 2048; +pub const CELLS_PER_EXT_BLOB: usize = 128; pub type limb_t = u64; #[repr(C)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] @@ -47,6 +47,9 @@ pub struct blst_p2 { y: blst_fp2, z: blst_fp2, } +pub type fr_t = blst_fr; +pub type g1_t = blst_p1; +pub type g2_t = blst_p2; #[repr(C)] #[doc = " The common return type for all routines in which something can go wrong."] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] @@ -60,9 +63,6 @@ pub enum C_KZG_RET { #[doc = "< Could not allocate memory."] C_KZG_MALLOC = 3, } -pub type g1_t = blst_p1; -pub type g2_t = blst_p2; -pub type fr_t = blst_fr; #[doc = " An array of 32 bytes. Represents an untrusted (potentially invalid) field element."] #[repr(C)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] @@ -177,6 +177,8 @@ extern "C" { num_cells: usize, s: *const KZGSettings, ) -> C_KZG_RET; + #[doc = " The first 32 roots of unity in the finite field F_r. SCALE2_ROOT_OF_UNITY[i] is a 2^i'th root of\n unity.\n\n For element `{A, B, C, D}`, the field element value is `A + B * 2^64 + C * 2^128 + D * 2^192`.\n This format may be converted to an `fr_t` type via the blst_fr_from_uint64() function.\n\n The decimal values may be calculated with the following Python code:\n @code{.py}\n MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513\n PRIMITIVE_ROOT = 7\n [pow(PRIMITIVE_ROOT, (MODULUS - 1) // (2**i), MODULUS) for i in range(32)]\n @endcode\n\n Note: Being a \"primitive root\" in this context means that `r^k != 1` for any `k < q-1` where q is\n the modulus. So powers of r generate the field. This is also known as being a \"primitive\n element\".\n\n In the formula above, the restriction can be slightly relaxed to `r` being a non-square. This is\n easy to check: We just require that r^((q-1)/2) == -1. Instead of 7, we could use 10, 13, 14, 15,\n 20... to create the 2^i'th roots of unity below. Generally, there are a lot of primitive roots:\n https://crypto.stanford.edu/pbc/notes/numbertheory/gen.html"] + pub static mut SCALE2_ROOT_OF_UNITY: [[u64; 4usize]; 32usize]; pub fn load_trusted_setup( out: *mut KZGSettings, g1_monomial_bytes: *const u8, diff --git a/src/.clang-format b/src/.clang-format index 66ea6ec78..145633f88 100644 --- a/src/.clang-format +++ b/src/.clang-format @@ -11,3 +11,4 @@ BinPackParameters: False PenaltyReturnTypeOnItsOwnLine: 1000 PenaltyBreakAssignment: 100 ColumnLimit: 100 +InsertNewlineAtEOF: true diff --git a/src/Makefile b/src/Makefile index c587443a2..c56565144 100644 --- a/src/Makefile +++ b/src/Makefile @@ -20,7 +20,7 @@ ifeq ($(PLATFORM),Darwin) endif # The base compiler flags. More can be added on command line. -CFLAGS += -I../inc -O2 -Wall -Wextra +CFLAGS += -I. -I../inc -O2 -Wall -Wextra # Cross-platform compilation settings. ifeq ($(PLATFORM),Windows) @@ -41,13 +41,13 @@ LIBS = $(BLST_LIBRARY) # Create file lists. SOURCE_FILES := $(shell find . -name '*.c' | sed 's|^\./||' | sort) -HEADER_FILES := $(patsubst %.c, %.h, $(SOURCE_FILES)) +HEADER_FILES := $(shell find . -name '*.h' | sed 's|^\./||' | sort) OBJECT_FILES := $(patsubst %.c, %.o, $(SOURCE_FILES)) # There is no tests header file. -HEADER_FILES := $(filter-out tests.h, $(HEADER_FILES)) +HEADER_FILES := $(filter-out test/tests.h, $(HEADER_FILES)) # We don't want to format this and it is not expected to change. -HEADER_FILES := $(filter-out tinytest.h, $(HEADER_FILES)) +HEADER_FILES := $(filter-out test/tinytest.h, $(HEADER_FILES)) ############################################################################### # Core @@ -69,7 +69,7 @@ blst: $(BLST_LIBRARY) tests: CFLAGS += -O0 tests: $(SOURCE_FILES) $(HEADER_FILES) $(BLST_LIBRARY) - @$(CC) $(CFLAGS) -o $@ tests.c $(LIBS) + @$(CC) $(CFLAGS) -o $@ test/tests.c $(LIBS) .PHONY: test test: tests @@ -81,7 +81,7 @@ test: tests tests_cov: CFLAGS += -O0 -fprofile-instr-generate -fcoverage-mapping tests_cov: $(SOURCE_FILES) $(HEADER_FILES) $(BLST_LIBRARY) - @$(CC) $(CFLAGS) -o $@ tests.c $(LIBS) + @$(CC) $(CFLAGS) -o $@ test/tests.c $(LIBS) .PHONY: coverage coverage: tests_cov @@ -103,7 +103,7 @@ tests_prof: CFLAGS += -L$(shell brew --prefix gperftools)/lib tests_prof: CFLAGS += -I$(shell brew --prefix gperftools)/include endif tests_prof: $(SOURCE_FILES) $(HEADER_FILES) $(BLST_LIBRARY) - @$(CC) $(CFLAGS) -o $@ tests.c $(LIBS) + @$(CC) $(CFLAGS) -o $@ test/tests.c $(LIBS) .PHONY: run_profiler run_profiler: tests_prof @@ -135,7 +135,7 @@ profile: \ sanitize_%: CFLAGS += -O0 -fsanitize=$* sanitize_%: $(SOURCE_FILES) $(HEADER_FILES) $(BLST_LIBRARY) @echo Running sanitize=$*... - @$(CC) $(CFLAGS) -o $@ tests.c $(LIBS) + @$(CC) $(CFLAGS) -o $@ test/tests.c $(LIBS) @ASAN_OPTIONS=allocator_may_return_null=1 \ LSAN_OPTIONS=allocator_may_return_null=1 \ ./$@; rm $@ @@ -173,6 +173,6 @@ format: .PHONY: clean clean: - @rm -f *.o *.profraw *.profdata *.html xray-log.* *.prof *.pdf \ + @rm -f *.o */*.o *.profraw *.profdata *.html xray-log.* *.prof *.pdf \ tests tests_cov tests_prof @rm -rf analysis-report diff --git a/src/ckzg.c b/src/ckzg.c index e3285ecdb..0b68749f6 100644 --- a/src/ckzg.c +++ b/src/ckzg.c @@ -14,8 +14,18 @@ * limitations under the License. */ -#include "common.c" -#include "eip4844.c" -#include "eip7594.c" -#include "fft.c" -#include "setup.c" +#include "common/alloc.c" +#include "common/bytes.c" +#include "common/ec.c" +#include "common/fr.c" +#include "common/lincomb.c" +#include "common/utils.c" +#include "eip4844/blob.c" +#include "eip4844/eip4844.c" +#include "eip7594/cell.c" +#include "eip7594/eip7594.c" +#include "eip7594/fft.c" +#include "eip7594/fk20.c" +#include "eip7594/poly.c" +#include "eip7594/recovery.c" +#include "setup/setup.c" diff --git a/src/ckzg.h b/src/ckzg.h index 95388bedc..e792ea33d 100644 --- a/src/ckzg.h +++ b/src/ckzg.h @@ -16,6 +16,6 @@ #pragma once -#include "eip4844.h" -#include "eip7594.h" -#include "setup.h" +#include "eip4844/eip4844.h" +#include "eip7594/eip7594.h" +#include "setup/setup.h" diff --git a/src/common.c b/src/common.c deleted file mode 100644 index e0e833680..000000000 --- a/src/common.c +++ /dev/null @@ -1,610 +0,0 @@ -/* - * Copyright 2024 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 "common.h" - -#include -#include -#include -#include - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Memory Allocation -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Wrapped malloc() that reports failures to allocate. - * - * @remark Will return C_KZG_BADARGS if the requested size is zero. - * - * @param[out] out Pointer to the allocated space - * @param[in] size The number of bytes to be allocated - */ -C_KZG_RET c_kzg_malloc(void **out, size_t size) { - *out = NULL; - if (size == 0) return C_KZG_BADARGS; - *out = malloc(size); - return *out != NULL ? C_KZG_OK : C_KZG_MALLOC; -} - -/** - * Wrapped calloc() that reports failures to allocate. - * - * @param[out] out Pointer to the allocated space - * @param[in] count The number of elements - * @param[in] size The size of each element - * - * @remark Will return C_KZG_BADARGS if the requested size is zero. - */ -C_KZG_RET c_kzg_calloc(void **out, size_t count, size_t size) { - *out = NULL; - if (count == 0 || size == 0) return C_KZG_BADARGS; - *out = calloc(count, size); - return *out != NULL ? C_KZG_OK : C_KZG_MALLOC; -} - -/** - * Allocate memory for an array of G1 group elements. - * - * @param[out] x Pointer to the allocated space - * @param[in] n The number of G1 elements to be allocated - * - * @remark Free the space later using c_kzg_free(). - */ -C_KZG_RET new_g1_array(g1_t **x, size_t n) { - return c_kzg_calloc((void **)x, n, sizeof(g1_t)); -} - -/** - * Allocate memory for an array of G2 group elements. - * - * @param[out] x Pointer to the allocated space - * @param[in] n The number of G2 elements to be allocated - * - * @remark Free the space later using c_kzg_free(). - */ -C_KZG_RET new_g2_array(g2_t **x, size_t n) { - return c_kzg_calloc((void **)x, n, sizeof(g2_t)); -} - -/** - * Allocate memory for an array of field elements. - * - * @param[out] x Pointer to the allocated space - * @param[in] n The number of field elements to be allocated - * - * @remark Free the space later using c_kzg_free(). - */ -C_KZG_RET new_fr_array(fr_t **x, size_t n) { - return c_kzg_calloc((void **)x, n, sizeof(fr_t)); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// General Helper Functions -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Utility function to test whether the argument is a power of two. - * - * @param[in] n The number to test - * - * @return True if `n` is zero or a power of two, otherwise false. - * - * @remark This method returns true for is_power_of_two(0) which is a bit weird, but not an issue in - * the contexts in which we use it. - * - */ -bool is_power_of_two(uint64_t n) { - return (n & (n - 1)) == 0; -} - -/** - * Calculate log base two of a power of two. - * - * @param[in] n The power of two - * - * @return The log base two of n. - * - * @remark In other words, the bit index of the one bit. - * @remark Works only for n a power of two, and only for n up to 2^31. - * @remark Not the fastest implementation, but it doesn't need to be fast. - */ -int log2_pow2(uint32_t n) { - int position = 0; - while (n >>= 1) - position++; - return position; -} - -/** - * Reverse the bit order in a 32-bit integer. - * - * @param[in] n The integer to be reversed - * - * @return An integer with the bits of `n` reversed. - */ -uint32_t reverse_bits(uint32_t n) { - uint32_t result = 0; - for (int i = 0; i < 32; ++i) { - result <<= 1; - result |= (n & 1); - n >>= 1; - } - return result; -} - -/** - * Reorder an array in reverse bit order of its indices. - * - * @param[in,out] values The array, which is re-ordered in-place - * @param[in] size The size in bytes of an element of the array - * @param[in] n The length of the array, must be a power of two - * strictly greater than 1 and less than 2^32. - * - * @remark Operates in-place on the array. - * @remark Can handle arrays of any type: provide the element size in `size`. - * @remark This means that `input[n] == output[n']`, where input and output denote the input and - * output array and n' is obtained from n by bit-reversing n. As opposed to reverse_bits, this - * bit-reversal operates on log2(n)-bit numbers. - */ -C_KZG_RET bit_reversal_permutation(void *values, size_t size, uint64_t n) { - C_KZG_RET ret; - byte *tmp = NULL; - byte *v = values; - - /* Some sanity checks */ - if (n < 2 || n >= UINT32_MAX || !is_power_of_two(n)) { - ret = C_KZG_BADARGS; - goto out; - } - - /* Scratch space for swapping an entry of the values array */ - ret = c_kzg_malloc((void **)&tmp, size); - if (ret != C_KZG_OK) goto out; - - /* Reorder elements */ - 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, v + (i * size), size); - memcpy(v + (i * size), v + (r * size), size); - memcpy(v + (r * size), tmp, size); - } - } - -out: - c_kzg_free(tmp); - return ret; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Conversion and Validation -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Serialize a G1 group element into bytes. - * - * @param[out] out A 48-byte array to store the serialized G1 element - * @param[in] in The G1 element to be serialized - */ -void bytes_from_g1(Bytes48 *out, const g1_t *in) { - blst_p1_compress(out->bytes, in); -} - -/** - * Serialize a BLS field element into bytes. - * - * @param[out] out A 32-byte array to store the serialized field element - * @param[in] in The field element to be serialized - */ -void bytes_from_bls_field(Bytes32 *out, const fr_t *in) { - blst_scalar s; - blst_scalar_from_fr(&s, in); - blst_bendian_from_scalar(out->bytes, &s); -} - -/** - * Serialize a 64-bit unsigned integer into bytes. - * - * @param[out] out An 8-byte array to store the serialized integer - * @param[in] n The integer to be serialized - * - * @remark The output format is big-endian. - */ -void bytes_from_uint64(uint8_t out[8], uint64_t n) { - for (int i = 7; i >= 0; i--) { - out[i] = n & 0xFF; - n >>= 8; - } -} - -/** - * Perform BLS validation required by the types KZGProof and KZGCommitment. - * - * @param[out] out The output g1 point - * @param[in] b The proof/commitment bytes - * - * @remark This function deviates from the spec because it returns (via an output argument) the g1 - * point. This way is more efficient (faster) but the function name is a bit misleading. - */ -static C_KZG_RET validate_kzg_g1(g1_t *out, const Bytes48 *b) { - blst_p1_affine p1_affine; - - /* Convert the bytes to a p1 point */ - /* The uncompress routine checks that the point is on the curve */ - if (blst_p1_uncompress(&p1_affine, b->bytes) != BLST_SUCCESS) return C_KZG_BADARGS; - blst_p1_from_affine(out, &p1_affine); - - /* The point at infinity is accepted! */ - if (blst_p1_is_inf(out)) return C_KZG_OK; - /* The point must be on the right subgroup */ - if (!blst_p1_in_g1(out)) return C_KZG_BADARGS; - - return C_KZG_OK; -} - -/** - * Convert untrusted bytes to a trusted and validated BLS scalar field element. - * - * @param[out] out The field element to store the deserialized data - * @param[in] b A 32-byte array containing the serialized field element - */ -C_KZG_RET bytes_to_bls_field(fr_t *out, const Bytes32 *b) { - blst_scalar tmp; - blst_scalar_from_bendian(&tmp, b->bytes); - if (!blst_scalar_fr_check(&tmp)) return C_KZG_BADARGS; - blst_fr_from_scalar(out, &tmp); - return C_KZG_OK; -} - -/** - * Convert untrusted bytes into a trusted and validated KZGCommitment. - * - * @param[out] out The output commitment - * @param[in] b The commitment bytes - */ -C_KZG_RET bytes_to_kzg_commitment(g1_t *out, const Bytes48 *b) { - return validate_kzg_g1(out, b); -} - -/** - * Convert untrusted bytes into a trusted and validated KZGProof. - * - * @param[out] out The output proof - * @param[in] b The proof bytes - */ -C_KZG_RET bytes_to_kzg_proof(g1_t *out, const Bytes48 *b) { - return validate_kzg_g1(out, b); -} - -/** - * Create a field element from a single 64-bit unsigned integer. - * - * @param[out] out The field element equivalent of `n` - * @param[in] n The 64-bit integer to be converted - * - * @remark This can only generate a tiny fraction of possible field elements, - * and is mostly useful for testing. - */ -void fr_from_uint64(fr_t *out, uint64_t n) { - uint64_t vals[] = {n, 0, 0, 0}; - blst_fr_from_uint64(out, vals); -} - -/** - * Map bytes to a BLS field element. - * - * @param[out] out The field element to store the result - * @param[in] b A 32-byte array containing the input - */ -void hash_to_bls_field(fr_t *out, const Bytes32 *b) { - blst_scalar tmp; - blst_scalar_from_bendian(&tmp, b->bytes); - blst_fr_from_scalar(out, &tmp); -} - -/** - * Deserialize a blob (array of bytes) into a polynomial (array of field elements). - * - * @param[out] p The output polynomial (array of field elements) - * @param[in] blob The blob (an array of bytes) - * @param[in] n The number of field elements in the polynomial/blob - */ -C_KZG_RET blob_to_polynomial(fr_t *p, const Blob *blob) { - C_KZG_RET ret; - for (size_t i = 0; i < FIELD_ELEMENTS_PER_BLOB; i++) { - ret = bytes_to_bls_field(&p[i], (Bytes32 *)&blob->bytes[i * BYTES_PER_FIELD_ELEMENT]); - if (ret != C_KZG_OK) return ret; - } - return C_KZG_OK; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Field Operations -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Test whether two field elements are equal. - * - * @param[in] a The first element - * @param[in] b The second element - * - * @retval true The two elements are equal. - * @retval false The two elements are not equal. - */ -bool fr_equal(const fr_t *a, const fr_t *b) { - uint64_t _a[4], _b[4]; - blst_uint64_from_fr(_a, a); - blst_uint64_from_fr(_b, b); - return _a[0] == _b[0] && _a[1] == _b[1] && _a[2] == _b[2] && _a[3] == _b[3]; -} - -/** - * Test whether the operand is one in the finite field. - * - * @param[in] p The field element to be checked - * - * @retval true The element is one - * @retval false The element is not one - */ -bool fr_is_one(const fr_t *p) { - uint64_t a[4]; - blst_uint64_from_fr(a, p); - return a[0] == 1 && a[1] == 0 && a[2] == 0 && a[3] == 0; -} - -/** - * Divide a field element by another. - * - * @param[out] out `a` divided by `b` in the field - * @param[in] a The dividend - * @param[in] b The divisor - * - * @remark The behavior for `b == 0` is unspecified. - * @remark This function supports in-place computation. - */ -void fr_div(fr_t *out, const fr_t *a, const fr_t *b) { - blst_fr tmp; - blst_fr_eucl_inverse(&tmp, b); - blst_fr_mul(out, a, &tmp); -} - -/** - * Exponentiation of a field element. - * - * Uses square and multiply for log(n) performance. - * - * @param[out] out `a` raised to the power of `n` - * @param[in] a The field element to be exponentiated - * @param[in] n The exponent - * - * @remark A 64-bit exponent is sufficient for our needs here. - * @remark This function does support in-place computation. - */ -void fr_pow(fr_t *out, const fr_t *a, uint64_t n) { - fr_t tmp = *a; - *out = FR_ONE; - - while (true) { - if (n & 1) { - blst_fr_mul(out, out, &tmp); - } - if ((n >>= 1) == 0) break; - blst_fr_sqr(&tmp, &tmp); - } -} - -/** - * Compute and return [ x^0, x^1, ..., x^{n-1} ]. - * - * @param[out] out The array to store the powers - * @param[in] x The field element to raise to powers - * @param[in] n The number of powers to compute - * - * @remark `out` is left untouched if `n == 0`. - */ -void compute_powers(fr_t *out, const fr_t *x, uint64_t n) { - fr_t current_power = FR_ONE; - for (uint64_t i = 0; i < n; i++) { - out[i] = current_power; - blst_fr_mul(¤t_power, ¤t_power, x); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Point Operations -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Subtraction of G1 group elements. - * - * @param[out] out `a - b` - * @param[in] a A G1 group element - * @param[in] b The G1 group element to be subtracted - */ -void g1_sub(g1_t *out, const g1_t *a, const g1_t *b) { - g1_t bneg = *b; - blst_p1_cneg(&bneg, true); - blst_p1_add_or_double(out, a, &bneg); -} - -/** - * Multiply a G1 group element by a field element. - * - * @param[out] out `a * b` - * @param[in] a The G1 group element - * @param[in] b The multiplier - */ -void g1_mul(g1_t *out, const g1_t *a, const fr_t *b) { - blst_scalar s; - blst_scalar_from_fr(&s, b); - blst_p1_mult(out, a, s.b, BITS_PER_FIELD_ELEMENT); -} - -/** - * Perform pairings and test whether the outcomes are equal in G_T. - * - * Tests whether `e(a1, a2) == e(b1, b2)`. - * - * @param[in] a1 A G1 group point for the first pairing - * @param[in] a2 A G2 group point for the first pairing - * @param[in] b1 A G1 group point for the second pairing - * @param[in] b2 A G2 group point for the second pairing - * - * @retval true The pairings were equal - * @retval false The pairings were not equal - */ -bool pairings_verify(const g1_t *a1, const g2_t *a2, const g1_t *b1, const g2_t *b2) { - blst_fp12 loop0, loop1, gt_point; - blst_p1_affine aa1, bb1; - blst_p2_affine aa2, bb2; - - /* - * As an optimisation, we want to invert one of the pairings, - * so we negate one of the points. - */ - g1_t a1neg = *a1; - blst_p1_cneg(&a1neg, true); - - blst_p1_to_affine(&aa1, &a1neg); - blst_p1_to_affine(&bb1, b1); - blst_p2_to_affine(&aa2, a2); - blst_p2_to_affine(&bb2, b2); - - blst_miller_loop(&loop0, &aa2, &aa1); - blst_miller_loop(&loop1, &bb2, &bb1); - - blst_fp12_mul(>_point, &loop0, &loop1); - blst_final_exp(>_point, >_point); - - return blst_fp12_is_one(>_point); -} - -/** - * Calculate a linear combination of G1 group elements. - * - * Calculates `[coeffs_0]p_0 + [coeffs_1]p_1 + ... + [coeffs_n]p_n` - * where `n` is `len - 1`. - * - * This function computes the result naively without using Pippenger's algorithm. - */ -void g1_lincomb_naive(g1_t *out, const g1_t *p, const fr_t *coeffs, uint64_t len) { - g1_t tmp; - *out = G1_IDENTITY; - for (uint64_t i = 0; i < len; i++) { - g1_mul(&tmp, &p[i], &coeffs[i]); - blst_p1_add_or_double(out, out, &tmp); - } -} - -/** - * Calculate a linear combination of G1 group elements. - * - * Calculates `[coeffs_0]p_0 + [coeffs_1]p_1 + ... + [coeffs_n]p_n` where `n` is `len - 1`. - * - * @param[out] out The resulting sum-product - * @param[in] p Array of G1 group elements, length `len` - * @param[in] coeffs Array of field elements, length `len` - * @param[in] len The number of group/field elements - * - * @remark This function CAN be called with the point at infinity in `p`. - * @remark While this function is significantly faster than g1_lincomb_naive(), we refrain from - * using it in security-critical places (like verification) because the blst Pippenger code has not - * been audited. In those critical places, we prefer using g1_lincomb_naive() which is much simpler. - * - * For the benefit of future generations (since blst has no documentation to speak of), there are - * two ways to pass the arrays of scalars and points into blst_p1s_mult_pippenger(). - * - * 1. Pass `points` as an array of pointers to the points, and pass `scalars` as an array of - * pointers to the scalars, each of length `len`. - * 2. Pass an array where the first element is a pointer to the contiguous array of points and the - * second is null, and similarly for scalars. - * - * We do the second of these to save memory here. - */ -C_KZG_RET g1_lincomb_fast(g1_t *out, const g1_t *p, const fr_t *coeffs, size_t len) { - C_KZG_RET ret; - void *scratch = NULL; - blst_p1 *p_filtered = NULL; - blst_p1_affine *p_affine = NULL; - blst_scalar *scalars = NULL; - - /* Tunable parameter: must be at least 2 since blst fails for 0 or 1 */ - const size_t min_length_threshold = 8; - - /* Use naive method if it's less than the threshold */ - if (len < min_length_threshold) { - g1_lincomb_naive(out, p, coeffs, len); - ret = C_KZG_OK; - goto out; - } - - /* Allocate space for arrays */ - ret = c_kzg_calloc((void **)&p_filtered, len, sizeof(blst_p1)); - if (ret != C_KZG_OK) goto out; - ret = c_kzg_calloc((void **)&p_affine, len, sizeof(blst_p1_affine)); - if (ret != C_KZG_OK) goto out; - ret = c_kzg_calloc((void **)&scalars, len, sizeof(blst_scalar)); - if (ret != C_KZG_OK) goto out; - - /* Allocate space for Pippenger scratch */ - size_t scratch_size = blst_p1s_mult_pippenger_scratch_sizeof(len); - ret = c_kzg_malloc(&scratch, scratch_size); - if (ret != C_KZG_OK) goto out; - - /* Transform the field elements to 256-bit scalars */ - for (size_t i = 0; i < len; i++) { - blst_scalar_from_fr(&scalars[i], &coeffs[i]); - } - - /* Filter out zero points: make a new list p_filtered that contains only non-zero points */ - size_t new_len = 0; - for (size_t i = 0; i < len; i++) { - if (!blst_p1_is_inf(&p[i])) { - /* Copy valid points to the new position */ - p_filtered[new_len] = p[i]; - scalars[new_len] = scalars[i]; - new_len++; - } - } - - /* Check if the new length is fine */ - if (new_len < min_length_threshold) { - /* We must use the original inputs */ - g1_lincomb_naive(out, p, coeffs, len); - ret = C_KZG_OK; - goto out; - } - - /* Transform the points to affine representation */ - const blst_p1 *p_arg[2] = {p_filtered, NULL}; - blst_p1s_to_affine(p_affine, p_arg, new_len); - - /* Call the Pippenger implementation */ - const byte *scalars_arg[2] = {(byte *)scalars, NULL}; - const blst_p1_affine *points_arg[2] = {p_affine, NULL}; - blst_p1s_mult_pippenger(out, points_arg, new_len, scalars_arg, BITS_PER_FIELD_ELEMENT, scratch); - ret = C_KZG_OK; - -out: - c_kzg_free(scratch); - c_kzg_free(p_filtered); - c_kzg_free(p_affine); - c_kzg_free(scalars); - return ret; -} diff --git a/src/common.h b/src/common.h deleted file mode 100644 index 8cd938fcf..000000000 --- a/src/common.h +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright 2024 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. - */ - -#pragma once - -#include "blst.h" - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Macros -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** The number of bytes in a KZG commitment. */ -#define BYTES_PER_COMMITMENT 48 - -/** The number of bytes in a KZG proof. */ -#define BYTES_PER_PROOF 48 - -/** The number of bytes in a BLS scalar field element. */ -#define BYTES_PER_FIELD_ELEMENT 32 - -/** The number of field elements in a blob. */ -#define FIELD_ELEMENTS_PER_BLOB 4096 - -/** The number of field elements in an extended blob */ -#define FIELD_ELEMENTS_PER_EXT_BLOB (FIELD_ELEMENTS_PER_BLOB * 2) - -/** The number of bytes in a blob. */ -#define BYTES_PER_BLOB (FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD_ELEMENT) - -/** The number of bits in a BLS scalar field element. */ -#define BITS_PER_FIELD_ELEMENT 255 - -/** Length of the domain strings above. */ -#define DOMAIN_STR_LENGTH 16 - -/** Returns number of elements in a statically defined array. */ -#define NUM_ELEMENTS(a) (sizeof(a) / sizeof(a[0])) - -/** - * Helper macro to release memory allocated on the heap. Unlike free(), c_kzg_free() macro sets the - * pointer value to NULL after freeing it. - */ -#define c_kzg_free(p) \ - do { \ - free(p); \ - (p) = NULL; \ - } while (0) - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Types -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** The common return type for all routines in which something can go wrong. */ -typedef enum { - C_KZG_OK = 0, /**< Success! */ - C_KZG_BADARGS, /**< The supplied data is invalid in some way. */ - C_KZG_ERROR, /**< Internal error - this should never occur. */ - C_KZG_MALLOC, /**< Could not allocate memory. */ -} C_KZG_RET; - -typedef blst_p1 g1_t; /**< Internal G1 group element type. */ -typedef blst_p2 g2_t; /**< Internal G2 group element type. */ -typedef blst_fr fr_t; /**< Internal Fr field element type. */ - -/** An array of 32 bytes. Represents an untrusted (potentially invalid) field element. */ -typedef struct { - uint8_t bytes[32]; -} Bytes32; - -/** An array of 48 bytes. Represents an untrusted (potentially invalid) commitment/proof. */ -typedef struct { - uint8_t bytes[48]; -} Bytes48; - -/** A basic blob data. */ -typedef struct { - uint8_t bytes[BYTES_PER_BLOB]; -} Blob; - -/** A trusted (valid) KZG commitment. */ -typedef Bytes48 KZGCommitment; - -/** A trusted (valid) KZG proof. */ -typedef Bytes48 KZGProof; - -/** Stores the setup and parameters needed for computing KZG proofs. */ -typedef struct { - /** - * Roots of unity for the subgroup of size `domain_size`. - * - * The array contains `domain_size + 1` elements, it starts and ends with Fr::one(). - */ - fr_t *roots_of_unity; - /** - * Roots of unity for the subgroup of size `domain_size` in bit-reversed order. - * - * This array is derived by applying a bit-reversal permutation to `roots_of_unity` - * excluding the last element. Essentially: - * `brp_roots_of_unity = bit_reversal_permutation(roots_of_unity[:-1])` - * - * The array contains `domain_size` elements. - */ - fr_t *brp_roots_of_unity; - /** - * Roots of unity for the subgroup of size `domain_size` in reversed order. - * - * It is the reversed version of `roots_of_unity`. Essentially: - * `reverse_roots_of_unity = reverse(roots_of_unity)` - * - * This array is primarily used in FFTs. - * The array contains `domain_size + 1` elements, it starts and ends with Fr::one(). - */ - fr_t *reverse_roots_of_unity; - /** G1 group elements from the trusted setup in monomial form. */ - g1_t *g1_values_monomial; - /** G1 group elements from the trusted setup in Lagrange form and bit-reversed order. */ - g1_t *g1_values_lagrange_brp; - /** G2 group elements from the trusted setup in monomial form. */ - g2_t *g2_values_monomial; - /** Data used during FK20 proof generation. */ - g1_t **x_ext_fft_columns; - /** The precomputed tables for fixed-base MSM. */ - blst_p1_affine **tables; - /** The window size for the fixed-base MSM. */ - size_t wbits; - /** The scratch size for the fixed-base MSM. */ - size_t scratch_size; -} KZGSettings; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Constants -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** Deserialized form of the G1 identity/infinity point. */ -static const g1_t G1_IDENTITY = { - {0L, 0L, 0L, 0L, 0L, 0L}, {0L, 0L, 0L, 0L, 0L, 0L}, {0L, 0L, 0L, 0L, 0L, 0L} -}; - -/** The zero field element. */ -static const fr_t FR_ZERO = {0L, 0L, 0L, 0L}; - -/** This is 1 in blst's `blst_fr` limb representation. Crazy but true. */ -static const fr_t FR_ONE = { - 0x00000001fffffffeL, 0x5884b7fa00034802L, 0x998c4fefecbc4ff5L, 0x1824b159acc5056fL -}; - -/** This used to represent a missing element. It's an invalid value. */ -static const fr_t FR_NULL = { - 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL -}; - -/** - * The first 32 roots of unity in the finite field F_r. SCALE2_ROOT_OF_UNITY[i] is a 2^i'th root of - * unity. - * - * For element `{A, B, C, D}`, the field element value is `A + B * 2^64 + C * 2^128 + D * 2^192`. - * This format may be converted to an `fr_t` type via the blst_fr_from_uint64() function. - * - * The decimal values may be calculated with the following Python code: - * @code{.py} - * MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513 - * PRIMITIVE_ROOT = 7 - * [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". - * - * In the formula above, the restriction can be slightly relaxed to `r` being a non-square. This is - * easy to check: We just require that r^((q-1)/2) == -1. Instead of 7, we could use 10, 13, 14, 15, - * 20... to create the 2^i'th roots of unity below. Generally, 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}, - {0xffffffff00000000L, 0x53bda402fffe5bfeL, 0x3339d80809a1d805L, 0x73eda753299d7d48L}, - {0x0001000000000000L, 0xec03000276030000L, 0x8d51ccce760304d0L, 0x0000000000000000L}, - {0x7228fd3397743f7aL, 0xb38b21c28713b700L, 0x8c0625cd70d77ce2L, 0x345766f603fa66e7L}, - {0x53ea61d87742bcceL, 0x17beb312f20b6f76L, 0xdd1c0af834cec32cL, 0x20b1ce9140267af9L}, - {0x360c60997369df4eL, 0xbf6e88fb4c38fb8aL, 0xb4bcd40e22f55448L, 0x50e0903a157988baL}, - {0x8140d032f0a9ee53L, 0x2d967f4be2f95155L, 0x14a1e27164d8fdbdL, 0x45af6345ec055e4dL}, - {0x5130c2c1660125beL, 0x98d0caac87f5713cL, 0xb7c68b4d7fdd60d0L, 0x6898111413588742L}, - {0x4935bd2f817f694bL, 0x0a0865a899e8deffL, 0x6b368121ac0cf4adL, 0x4f9b4098e2e9f12eL}, - {0x4541b8ff2ee0434eL, 0xd697168a3a6000feL, 0x39feec240d80689fL, 0x095166525526a654L}, - {0x3c28d666a5c2d854L, 0xea437f9626fc085eL, 0x8f4de02c0f776af3L, 0x325db5c3debf77a1L}, - {0x4a838b5d59cd79e5L, 0x55ea6811be9c622dL, 0x09f1ca610a08f166L, 0x6d031f1b5c49c834L}, - {0xe206da11a5d36306L, 0x0ad1347b378fbf96L, 0xfc3e8acfe0f8245fL, 0x564c0a11a0f704f4L}, - {0x6fdd00bfc78c8967L, 0x146b58bc434906acL, 0x2ccddea2972e89edL, 0x485d512737b1da3dL}, - {0x034d2ff22a5ad9e1L, 0xae4622f6a9152435L, 0xdc86b01c0d477fa6L, 0x56624634b500a166L}, - {0xfbd047e11279bb6eL, 0xc8d5f51db3f32699L, 0x483405417a0cbe39L, 0x3291357ee558b50dL}, - {0xd7118f85cd96b8adL, 0x67a665ae1fcadc91L, 0x88f39a78f1aeb578L, 0x2155379d12180caaL}, - {0x08692405f3b70f10L, 0xcd7f2bd6d0711b7dL, 0x473a2eef772c33d6L, 0x224262332d8acbf4L}, - {0x6f421a7d8ef674fbL, 0xbb97a3bf30ce40fdL, 0x652f717ae1c34bb0L, 0x2d3056a530794f01L}, - {0x194e8c62ecb38d9dL, 0xad8e16e84419c750L, 0xdf625e80d0adef90L, 0x520e587a724a6955L}, - {0xfece7e0e39898d4bL, 0x2f69e02d265e09d9L, 0xa57a6e07cb98de4aL, 0x03e1c54bcb947035L}, - {0xcd3979122d3ea03aL, 0x46b3105f04db5844L, 0xc70d0874b0691d4eL, 0x47c8b5817018af4fL}, - {0xc6e7a6ffb08e3363L, 0xe08fec7c86389beeL, 0xf2d38f10fbb8d1bbL, 0x0abe6a5e5abcaa32L}, - {0x5616c57de0ec9eaeL, 0xc631ffb2585a72dbL, 0x5121af06a3b51e3cL, 0x73560252aa0655b2L}, - {0x92cf4deb77bd779cL, 0x72cf6a8029b7d7bcL, 0x6e0bcd91ee762730L, 0x291cf6d68823e687L}, - {0xce32ef844e11a51eL, 0xc0ba12bb3da64ca5L, 0x0454dc1edc61a1a3L, 0x019fe632fd328739L}, - {0x531a11a0d2d75182L, 0x02c8118402867ddcL, 0x116168bffbedc11dL, 0x0a0a77a3b1980c0dL}, - {0xe2d0a7869f0319edL, 0xb94f1101b1d7a628L, 0xece8ea224f31d25dL, 0x23397a9300f8f98bL}, - {0xd7b688830a4f2089L, 0x6558e9e3f6ac7b41L, 0x99e276b571905a7dL, 0x52dd465e2f094256L}, - {0x474650359d8e211bL, 0x84d37b826214abc6L, 0x8da40c1ef2bb4598L, 0x0c83ea7744bf1beeL}, - {0x694341f608c9dd56L, 0xed3a181fabb30adcL, 0x1339a815da8b398fL, 0x2c6d4e4511657e1eL}, - {0x63e7cb4906ffc93fL, 0xf070bb00e28a193dL, 0xad1715b02e5713b5L, 0x4b5371495990693fL} -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Public Functions -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/* - * Memory Allocation: - */ -C_KZG_RET c_kzg_malloc(void **out, size_t size); -C_KZG_RET c_kzg_calloc(void **out, size_t count, size_t size); -C_KZG_RET new_g1_array(g1_t **x, size_t n); -C_KZG_RET new_g2_array(g2_t **x, size_t n); -C_KZG_RET new_fr_array(fr_t **x, size_t n); - -/* - * General Helper Functions: - */ -bool is_power_of_two(uint64_t n); -int log2_pow2(uint32_t n); -uint32_t reverse_bits(uint32_t n); -C_KZG_RET bit_reversal_permutation(void *values, size_t size, uint64_t n); - -/* - * Conversion and Validation: - */ -void bytes_from_g1(Bytes48 *out, const g1_t *in); -void bytes_from_bls_field(Bytes32 *out, const fr_t *in); -void bytes_from_uint64(uint8_t out[8], uint64_t n); -C_KZG_RET bytes_to_bls_field(fr_t *out, const Bytes32 *b); -C_KZG_RET bytes_to_kzg_commitment(g1_t *out, const Bytes48 *b); -C_KZG_RET bytes_to_kzg_proof(g1_t *out, const Bytes48 *b); -void fr_from_uint64(fr_t *out, uint64_t n); -void hash_to_bls_field(fr_t *out, const Bytes32 *b); -C_KZG_RET blob_to_polynomial(fr_t *p, const Blob *blob); - -/* - * Field Operations: - */ -bool fr_equal(const fr_t *a, const fr_t *b); -bool fr_is_one(const fr_t *p); -void fr_div(fr_t *out, const fr_t *a, const fr_t *b); -void fr_pow(fr_t *out, const fr_t *a, uint64_t n); -void compute_powers(fr_t *out, const fr_t *x, uint64_t n); - -/* - * Point Operations: - */ -void g1_sub(g1_t *out, const g1_t *a, const g1_t *b); -void g1_mul(g1_t *out, const g1_t *a, const fr_t *b); -bool pairings_verify(const g1_t *a1, const g2_t *a2, const g1_t *b1, const g2_t *b2); -void g1_lincomb_naive(g1_t *out, const g1_t *p, const fr_t *coeffs, uint64_t len); -C_KZG_RET g1_lincomb_fast(g1_t *out, const g1_t *p, const fr_t *coeffs, size_t len); - -#ifdef __cplusplus -} -#endif diff --git a/src/common/alloc.c b/src/common/alloc.c new file mode 100644 index 000000000..bc54ee9be --- /dev/null +++ b/src/common/alloc.c @@ -0,0 +1,106 @@ +/* + * Copyright 2024 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 "common/alloc.h" +#include "common/ec.h" +#include "common/fr.h" + +#include /* For bool */ +#include /* For size_t & NULL */ +#include /* For malloc */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Memory Allocation +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Wrapped malloc() that reports failures to allocate. + * + * @remark Will return C_KZG_BADARGS if the requested size is zero. + * + * @param[out] out Pointer to the allocated space + * @param[in] size The number of bytes to be allocated + */ +C_KZG_RET c_kzg_malloc(void **out, size_t size) { + *out = NULL; + if (size == 0) return C_KZG_BADARGS; + *out = malloc(size); + return *out != NULL ? C_KZG_OK : C_KZG_MALLOC; +} + +/** + * Wrapped calloc() that reports failures to allocate. + * + * @param[out] out Pointer to the allocated space + * @param[in] count The number of elements + * @param[in] size The size of each element + * + * @remark Will return C_KZG_BADARGS if the requested size is zero. + */ +C_KZG_RET c_kzg_calloc(void **out, size_t count, size_t size) { + *out = NULL; + if (count == 0 || size == 0) return C_KZG_BADARGS; + *out = calloc(count, size); + return *out != NULL ? C_KZG_OK : C_KZG_MALLOC; +} + +/** + * Allocate memory for an array of G1 group elements. + * + * @param[out] x Pointer to the allocated space + * @param[in] n The number of G1 elements to be allocated + * + * @remark Free the space later using c_kzg_free(). + */ +C_KZG_RET new_g1_array(g1_t **x, size_t n) { + return c_kzg_calloc((void **)x, n, sizeof(g1_t)); +} + +/** + * Allocate memory for an array of G2 group elements. + * + * @param[out] x Pointer to the allocated space + * @param[in] n The number of G2 elements to be allocated + * + * @remark Free the space later using c_kzg_free(). + */ +C_KZG_RET new_g2_array(g2_t **x, size_t n) { + return c_kzg_calloc((void **)x, n, sizeof(g2_t)); +} + +/** + * Allocate memory for an array of field elements. + * + * @param[out] x Pointer to the allocated space + * @param[in] n The number of field elements to be allocated + * + * @remark Free the space later using c_kzg_free(). + */ +C_KZG_RET new_fr_array(fr_t **x, size_t n) { + return c_kzg_calloc((void **)x, n, sizeof(fr_t)); +} + +/** + * Allocate memory for an array of booleans. + * + * @param[out] x Pointer to the allocated space + * @param[in] n The number of booleans to be allocated + * + * @remark Free the space later using c_kzg_free(). + */ +C_KZG_RET new_bool_array(bool **x, size_t n) { + return c_kzg_calloc((void **)x, n, sizeof(bool)); +} diff --git a/src/common/alloc.h b/src/common/alloc.h new file mode 100644 index 000000000..ba9eb7cdc --- /dev/null +++ b/src/common/alloc.h @@ -0,0 +1,58 @@ +/* + * Copyright 2024 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. + */ + +#pragma once + +#include "common/ec.h" +#include "common/fr.h" +#include "common/ret.h" + +#include /* For bool */ +#include /* For size_t */ +#include /* For free */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Helper macro to release memory allocated on the heap. Unlike free(), c_kzg_free() macro sets the + * pointer value to NULL after freeing it. + */ +#define c_kzg_free(p) \ + do { \ + free(p); \ + (p) = NULL; \ + } while (0) + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Public Functions +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +C_KZG_RET c_kzg_malloc(void **out, size_t size); +C_KZG_RET c_kzg_calloc(void **out, size_t count, size_t size); +C_KZG_RET new_g1_array(g1_t **x, size_t n); +C_KZG_RET new_g2_array(g2_t **x, size_t n); +C_KZG_RET new_fr_array(fr_t **x, size_t n); +C_KZG_RET new_bool_array(bool **x, size_t n); + +#ifdef __cplusplus +} +#endif diff --git a/src/common/bytes.c b/src/common/bytes.c new file mode 100644 index 000000000..e59c0e558 --- /dev/null +++ b/src/common/bytes.c @@ -0,0 +1,151 @@ +/* + * Copyright 2024 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 "common/bytes.h" + +#include /* For printf */ + +/** + * Serialize a 64-bit unsigned integer into bytes. + * + * @param[out] out An 8-byte array to store the serialized integer + * @param[in] n The integer to be serialized + * + * @remark The output format is big-endian. + */ +void bytes_from_uint64(uint8_t out[8], uint64_t n) { + for (int i = 7; i >= 0; i--) { + out[i] = n & 0xFF; + n >>= 8; + } +} + +/** + * Serialize a G1 group element into bytes. + * + * @param[out] out A 48-byte array to store the serialized G1 element + * @param[in] in The G1 element to be serialized + */ +void bytes_from_g1(Bytes48 *out, const g1_t *in) { + blst_p1_compress(out->bytes, in); +} + +/** + * Serialize a BLS field element into bytes. + * + * @param[out] out A 32-byte array to store the serialized field element + * @param[in] in The field element to be serialized + */ +void bytes_from_bls_field(Bytes32 *out, const fr_t *in) { + blst_scalar s; + blst_scalar_from_fr(&s, in); + blst_bendian_from_scalar(out->bytes, &s); +} + +/** + * Convert untrusted bytes to a trusted and validated BLS scalar field element. + * + * @param[out] out The field element to store the deserialized data + * @param[in] b A 32-byte array containing the serialized field element + */ +C_KZG_RET bytes_to_bls_field(fr_t *out, const Bytes32 *b) { + blst_scalar tmp; + blst_scalar_from_bendian(&tmp, b->bytes); + if (!blst_scalar_fr_check(&tmp)) return C_KZG_BADARGS; + blst_fr_from_scalar(out, &tmp); + return C_KZG_OK; +} + +/** + * Perform BLS validation required by the types KZGProof and KZGCommitment. + * + * @param[out] out The output g1 point + * @param[in] b The proof/commitment bytes + * + * @remark This function deviates from the spec because it returns (via an output argument) the g1 + * point. This way is more efficient (faster) but the function name is a bit misleading. + */ +static C_KZG_RET validate_kzg_g1(g1_t *out, const Bytes48 *b) { + blst_p1_affine p1_affine; + + /* Convert the bytes to a p1 point */ + /* The uncompress routine checks that the point is on the curve */ + if (blst_p1_uncompress(&p1_affine, b->bytes) != BLST_SUCCESS) return C_KZG_BADARGS; + blst_p1_from_affine(out, &p1_affine); + + /* The point at infinity is accepted! */ + if (blst_p1_is_inf(out)) return C_KZG_OK; + /* The point must be on the right subgroup */ + if (!blst_p1_in_g1(out)) return C_KZG_BADARGS; + + return C_KZG_OK; +} + +/** + * Convert untrusted bytes into a trusted and validated KZGCommitment. + * + * @param[out] out The output commitment + * @param[in] b The commitment bytes + */ +C_KZG_RET bytes_to_kzg_commitment(g1_t *out, const Bytes48 *b) { + return validate_kzg_g1(out, b); +} + +/** + * Convert untrusted bytes into a trusted and validated KZGProof. + * + * @param[out] out The output proof + * @param[in] b The proof bytes + */ +C_KZG_RET bytes_to_kzg_proof(g1_t *out, const Bytes48 *b) { + return validate_kzg_g1(out, b); +} + +/** + * Map bytes to a BLS field element. + * + * @param[out] out The field element to store the result + * @param[in] b A 32-byte array containing the input + */ +void hash_to_bls_field(fr_t *out, const Bytes32 *b) { + blst_scalar tmp; + blst_scalar_from_bendian(&tmp, b->bytes); + blst_fr_from_scalar(out, &tmp); +} + +/** + * Print a Bytes32 to the console. + * + * @param[in] bytes The Bytes32 to print + */ +void print_bytes32(const Bytes32 *bytes) { + for (size_t i = 0; i < 32; i++) { + printf("%02x", bytes->bytes[i]); + } + printf("\n"); +} + +/** + * Print a Bytes48 to the console. + * + * @param[in] bytes The Bytes48 to print + */ +void print_bytes48(const Bytes48 *bytes) { + for (size_t i = 0; i < 48; i++) { + printf("%02x", bytes->bytes[i]); + } + printf("\n"); +} diff --git a/src/common/bytes.h b/src/common/bytes.h new file mode 100644 index 000000000..4ee9c2e30 --- /dev/null +++ b/src/common/bytes.h @@ -0,0 +1,78 @@ +/* + * Copyright 2024 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. + */ + +#pragma once + +#include "common/ec.h" +#include "common/fr.h" +#include "common/ret.h" + +#include /* For uint*_t */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** The number of bytes in a KZG commitment. */ +#define BYTES_PER_COMMITMENT 48 + +/** The number of bytes in a KZG proof. */ +#define BYTES_PER_PROOF 48 + +/** The number of bytes in a BLS scalar field element. */ +#define BYTES_PER_FIELD_ELEMENT 32 + +/** The number of field elements in a blob. */ +#define FIELD_ELEMENTS_PER_BLOB 4096 + +/** The number of bytes in a blob. */ +#define BYTES_PER_BLOB (FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD_ELEMENT) + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** An array of 32 bytes. Represents an untrusted (potentially invalid) field element. */ +typedef struct { + uint8_t bytes[32]; +} Bytes32; + +/** An array of 48 bytes. Represents an untrusted (potentially invalid) commitment/proof. */ +typedef struct { + uint8_t bytes[48]; +} Bytes48; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Public Functions +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +void bytes_from_uint64(uint8_t out[8], uint64_t n); +void bytes_from_g1(Bytes48 *out, const g1_t *in); +void bytes_from_bls_field(Bytes32 *out, const fr_t *in); +C_KZG_RET bytes_to_bls_field(fr_t *out, const Bytes32 *b); +C_KZG_RET bytes_to_kzg_commitment(g1_t *out, const Bytes48 *b); +C_KZG_RET bytes_to_kzg_proof(g1_t *out, const Bytes48 *b); +void hash_to_bls_field(fr_t *out, const Bytes32 *b); +void print_bytes32(const Bytes32 *bytes); +void print_bytes48(const Bytes48 *bytes); + +#ifdef __cplusplus +} +#endif diff --git a/src/common/ec.c b/src/common/ec.c new file mode 100644 index 000000000..9d60897d9 --- /dev/null +++ b/src/common/ec.c @@ -0,0 +1,57 @@ +/* + * Copyright 2024 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 "common/ec.h" +#include "common/bytes.h" + +#include /* For printf */ + +/** + * Subtraction of G1 group elements. + * + * @param[out] out `a - b` + * @param[in] a A G1 group element + * @param[in] b The G1 group element to be subtracted + */ +void g1_sub(g1_t *out, const g1_t *a, const g1_t *b) { + g1_t bneg = *b; + blst_p1_cneg(&bneg, true); + blst_p1_add_or_double(out, a, &bneg); +} + +/** + * Multiply a G1 group element by a field element. + * + * @param[out] out `a * b` + * @param[in] a The G1 group element + * @param[in] b The multiplier + */ +void g1_mul(g1_t *out, const g1_t *a, const fr_t *b) { + blst_scalar s; + blst_scalar_from_fr(&s, b); + blst_p1_mult(out, a, s.b, BITS_PER_FIELD_ELEMENT); +} + +/** + * Print a G1 point to the console. + * + * @param[in] g The g1 point to print + */ +void print_g1(const g1_t *g) { + Bytes48 bytes; + bytes_from_g1(&bytes, g); + print_bytes48(&bytes); +} diff --git a/src/common/ec.h b/src/common/ec.h new file mode 100644 index 000000000..c2cc0f0cb --- /dev/null +++ b/src/common/ec.h @@ -0,0 +1,52 @@ +/* + * Copyright 2024 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. + */ + +#pragma once + +#include "blst.h" +#include "common/fr.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef blst_p1 g1_t; /**< Internal G1 group element type. */ +typedef blst_p2 g2_t; /**< Internal G2 group element type. */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Constants +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** Deserialized form of the G1 identity/infinity point. */ +static const g1_t G1_IDENTITY = { + {0L, 0L, 0L, 0L, 0L, 0L}, {0L, 0L, 0L, 0L, 0L, 0L}, {0L, 0L, 0L, 0L, 0L, 0L} +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Public Functions +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +void g1_sub(g1_t *out, const g1_t *a, const g1_t *b); +void g1_mul(g1_t *out, const g1_t *a, const fr_t *b); +void print_g1(const g1_t *g); + +#ifdef __cplusplus +} +#endif diff --git a/src/common/fr.c b/src/common/fr.c new file mode 100644 index 000000000..ca48dc5c4 --- /dev/null +++ b/src/common/fr.c @@ -0,0 +1,129 @@ +/* + * Copyright 2024 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 "common/fr.h" +#include "common/bytes.h" + +#include /* For uint*_t */ +#include /* For bool */ + +/** + * Test whether two field elements are equal. + * + * @param[in] a The first element + * @param[in] b The second element + * + * @retval true The two elements are equal. + * @retval false The two elements are not equal. + */ +bool fr_equal(const fr_t *a, const fr_t *b) { + uint64_t _a[4], _b[4]; + blst_uint64_from_fr(_a, a); + blst_uint64_from_fr(_b, b); + return _a[0] == _b[0] && _a[1] == _b[1] && _a[2] == _b[2] && _a[3] == _b[3]; +} + +/** + * Test whether the operand is one in the finite field. + * + * @param[in] p The field element to be checked + * + * @retval true The element is one + * @retval false The element is not one + */ +bool fr_is_one(const fr_t *p) { + uint64_t a[4]; + blst_uint64_from_fr(a, p); + return a[0] == 1 && a[1] == 0 && a[2] == 0 && a[3] == 0; +} + +/** + * Test whether the operand is null (all 0xff's). + * + * @param[in] p The field element to be checked + * + * @retval true The element is null + * @retval false The element is not null + */ +bool fr_is_null(const fr_t *p) { + return fr_equal(p, &FR_NULL); +} + +/** + * Divide a field element by another. + * + * @param[out] out `a` divided by `b` in the field + * @param[in] a The dividend + * @param[in] b The divisor + * + * @remark The behavior for `b == 0` is unspecified. + * @remark This function supports in-place computation. + */ +void fr_div(fr_t *out, const fr_t *a, const fr_t *b) { + fr_t tmp; + blst_fr_eucl_inverse(&tmp, b); + blst_fr_mul(out, a, &tmp); +} + +/** + * Exponentiation of a field element. + * + * Uses square and multiply for log(n) performance. + * + * @param[out] out `a` raised to the power of `n` + * @param[in] a The field element to be exponentiated + * @param[in] n The exponent + * + * @remark A 64-bit exponent is sufficient for our needs here. + * @remark This function does support in-place computation. + */ +void fr_pow(fr_t *out, const fr_t *a, uint64_t n) { + fr_t tmp = *a; + *out = FR_ONE; + + while (true) { + if (n & 1) { + blst_fr_mul(out, out, &tmp); + } + if ((n >>= 1) == 0) break; + blst_fr_sqr(&tmp, &tmp); + } +} + +/** + * Create a field element from a single 64-bit unsigned integer. + * + * @param[out] out The field element equivalent of `n` + * @param[in] n The 64-bit integer to be converted + * + * @remark This can only generate a tiny fraction of possible field elements, + * and is mostly useful for testing. + */ +void fr_from_uint64(fr_t *out, uint64_t n) { + uint64_t vals[] = {n, 0, 0, 0}; + blst_fr_from_uint64(out, vals); +} + +/** + * Print a field element to the console. + * + * @param[in] f The field element to print + */ +void print_fr(const fr_t *f) { + Bytes32 bytes; + bytes_from_bls_field(&bytes, f); + print_bytes32(&bytes); +} diff --git a/src/common/fr.h b/src/common/fr.h new file mode 100644 index 000000000..0e97242bb --- /dev/null +++ b/src/common/fr.h @@ -0,0 +1,74 @@ +/* + * Copyright 2024 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. + */ + +#pragma once + +#include "blst.h" + +#include /* For bool */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef blst_fr fr_t; /**< Internal Fr field element type. */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** The number of bits in a BLS scalar field element. */ +#define BITS_PER_FIELD_ELEMENT 255 + +/** The number of bytes in a BLS scalar field element. */ +#define BYTES_PER_FIELD_ELEMENT 32 + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Constants +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** The zero field element. */ +static const fr_t FR_ZERO = {0L, 0L, 0L, 0L}; + +/** This is 1 in blst's `blst_fr` limb representation. Crazy but true. */ +static const fr_t FR_ONE = { + 0x00000001fffffffeL, 0x5884b7fa00034802L, 0x998c4fefecbc4ff5L, 0x1824b159acc5056fL +}; + +/** This used to represent a missing element. It's an invalid value. */ +static const fr_t FR_NULL = { + 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Public Functions +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +bool fr_equal(const fr_t *a, const fr_t *b); +bool fr_is_one(const fr_t *p); +bool fr_is_null(const fr_t *p); +void fr_div(fr_t *out, const fr_t *a, const fr_t *b); +void fr_pow(fr_t *out, const fr_t *a, uint64_t n); +void fr_from_uint64(fr_t *out, uint64_t n); +void print_fr(const fr_t *f); + +#ifdef __cplusplus +} +#endif diff --git a/src/common/lincomb.c b/src/common/lincomb.c new file mode 100644 index 000000000..948a1eac4 --- /dev/null +++ b/src/common/lincomb.c @@ -0,0 +1,134 @@ +/* + * Copyright 2024 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 "common/lincomb.h" +#include "common/alloc.h" + +#include /* For NULL */ + +/** + * Calculate a linear combination of G1 group elements. + * + * Calculates `[coeffs_0]p_0 + [coeffs_1]p_1 + ... + [coeffs_n]p_n` + * where `n` is `len - 1`. + * + * This function computes the result naively without using Pippenger's algorithm. + */ +void g1_lincomb_naive(g1_t *out, const g1_t *p, const fr_t *coeffs, uint64_t len) { + g1_t tmp; + *out = G1_IDENTITY; + for (uint64_t i = 0; i < len; i++) { + g1_mul(&tmp, &p[i], &coeffs[i]); + blst_p1_add_or_double(out, out, &tmp); + } +} + +/** + * Calculate a linear combination of G1 group elements. + * + * Calculates `[coeffs_0]p_0 + [coeffs_1]p_1 + ... + [coeffs_n]p_n` where `n` is `len - 1`. + * + * @param[out] out The resulting sum-product + * @param[in] p Array of G1 group elements, length `len` + * @param[in] coeffs Array of field elements, length `len` + * @param[in] len The number of group/field elements + * + * @remark This function CAN be called with the point at infinity in `p`. + * @remark While this function is significantly faster than g1_lincomb_naive(), we refrain from + * using it in security-critical places (like verification) because the blst Pippenger code has not + * been audited. In those critical places, we prefer using g1_lincomb_naive() which is much simpler. + * + * For the benefit of future generations (since blst has no documentation to speak of), there are + * two ways to pass the arrays of scalars and points into blst_p1s_mult_pippenger(). + * + * 1. Pass `points` as an array of pointers to the points, and pass `scalars` as an array of + * pointers to the scalars, each of length `len`. + * 2. Pass an array where the first element is a pointer to the contiguous array of points and the + * second is null, and similarly for scalars. + * + * We do the second of these to save memory here. + */ +C_KZG_RET g1_lincomb_fast(g1_t *out, const g1_t *p, const fr_t *coeffs, size_t len) { + C_KZG_RET ret; + void *scratch = NULL; + blst_p1 *p_filtered = NULL; + blst_p1_affine *p_affine = NULL; + blst_scalar *scalars = NULL; + + /* Tunable parameter: must be at least 2 since blst fails for 0 or 1 */ + const size_t min_length_threshold = 8; + + /* Use naive method if it's less than the threshold */ + if (len < min_length_threshold) { + g1_lincomb_naive(out, p, coeffs, len); + ret = C_KZG_OK; + goto out; + } + + /* Allocate space for arrays */ + ret = c_kzg_calloc((void **)&p_filtered, len, sizeof(blst_p1)); + if (ret != C_KZG_OK) goto out; + ret = c_kzg_calloc((void **)&p_affine, len, sizeof(blst_p1_affine)); + if (ret != C_KZG_OK) goto out; + ret = c_kzg_calloc((void **)&scalars, len, sizeof(blst_scalar)); + if (ret != C_KZG_OK) goto out; + + /* Allocate space for Pippenger scratch */ + size_t scratch_size = blst_p1s_mult_pippenger_scratch_sizeof(len); + ret = c_kzg_malloc(&scratch, scratch_size); + if (ret != C_KZG_OK) goto out; + + /* Transform the field elements to 256-bit scalars */ + for (size_t i = 0; i < len; i++) { + blst_scalar_from_fr(&scalars[i], &coeffs[i]); + } + + /* Filter out zero points: make a new list p_filtered that contains only non-zero points */ + size_t new_len = 0; + for (size_t i = 0; i < len; i++) { + if (!blst_p1_is_inf(&p[i])) { + /* Copy valid points to the new position */ + p_filtered[new_len] = p[i]; + scalars[new_len] = scalars[i]; + new_len++; + } + } + + /* Check if the new length is fine */ + if (new_len < min_length_threshold) { + /* We must use the original inputs */ + g1_lincomb_naive(out, p, coeffs, len); + ret = C_KZG_OK; + goto out; + } + + /* Transform the points to affine representation */ + const blst_p1 *p_arg[2] = {p_filtered, NULL}; + blst_p1s_to_affine(p_affine, p_arg, new_len); + + /* Call the Pippenger implementation */ + const byte *scalars_arg[2] = {(byte *)scalars, NULL}; + const blst_p1_affine *points_arg[2] = {p_affine, NULL}; + blst_p1s_mult_pippenger(out, points_arg, new_len, scalars_arg, BITS_PER_FIELD_ELEMENT, scratch); + ret = C_KZG_OK; + +out: + c_kzg_free(scratch); + c_kzg_free(p_filtered); + c_kzg_free(p_affine); + c_kzg_free(scalars); + return ret; +} diff --git a/src/debug.h b/src/common/lincomb.h similarity index 76% rename from src/debug.h rename to src/common/lincomb.h index 68f3ba84c..da2371ea1 100644 --- a/src/debug.h +++ b/src/common/lincomb.h @@ -14,24 +14,22 @@ * limitations under the License. */ -#include "common.h" -#include "eip4844.h" -#include "eip7594.h" +#pragma once -#ifdef __cplusplus -extern "C" { -#endif +#include "common/ec.h" +#include "common/fr.h" +#include "common/ret.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Public Functions //////////////////////////////////////////////////////////////////////////////////////////////////// -void print_bytes32(const Bytes32 *bytes); -void print_bytes48(const Bytes48 *bytes); -void print_fr(const fr_t *f); -void print_g1(const g1_t *g); -void print_blob(const Blob *blob); -void print_cell(const Cell *cell); +#ifdef __cplusplus +extern "C" { +#endif + +void g1_lincomb_naive(g1_t *out, const g1_t *p, const fr_t *coeffs, uint64_t len); +C_KZG_RET g1_lincomb_fast(g1_t *out, const g1_t *p, const fr_t *coeffs, size_t len); #ifdef __cplusplus } diff --git a/src/common/ret.h b/src/common/ret.h new file mode 100644 index 000000000..aa54ec7f8 --- /dev/null +++ b/src/common/ret.h @@ -0,0 +1,29 @@ +/* + * Copyright 2024 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. + */ + +#pragma once + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** The common return type for all routines in which something can go wrong. */ +typedef enum { + C_KZG_OK = 0, /**< Success! */ + C_KZG_BADARGS, /**< The supplied data is invalid in some way. */ + C_KZG_ERROR, /**< Internal error - this should never occur. */ + C_KZG_MALLOC, /**< Could not allocate memory. */ +} C_KZG_RET; diff --git a/src/common/utils.c b/src/common/utils.c new file mode 100644 index 000000000..d38f93426 --- /dev/null +++ b/src/common/utils.c @@ -0,0 +1,188 @@ +/* + * Copyright 2024 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 "common/utils.h" +#include "common/alloc.h" + +#include /* For NULL */ +#include /* For memcpy */ + +/** + * Utility function to test whether the argument is a power of two. + * + * @param[in] n The number to test + * + * @return True if `n` is zero or a power of two, otherwise false. + * + * @remark This method returns true for is_power_of_two(0) which is a bit weird, but not an issue in + * the contexts in which we use it. + * + */ +bool is_power_of_two(uint64_t n) { + return (n & (n - 1)) == 0; +} + +/** + * Calculate log base two of a power of two. + * + * @param[in] n The power of two + * + * @return The log base two of n. + * + * @remark In other words, the bit index of the one bit. + * @remark Works only for n a power of two, and only for n up to 2^31. + * @remark Not the fastest implementation, but it doesn't need to be fast. + */ +int log2_pow2(uint32_t n) { + int position = 0; + while (n >>= 1) + position++; + return position; +} + +/** + * Reverse the bit order in a 32-bit integer. + * + * @param[in] n The integer to be reversed + * + * @return An integer with the bits of `n` reversed. + */ +uint32_t reverse_bits(uint32_t n) { + uint32_t result = 0; + for (int i = 0; i < 32; ++i) { + result <<= 1; + result |= (n & 1); + n >>= 1; + } + return result; +} + +/** + * Reverse the low-order bits in a 32-bit integer. + * + * @param[in] n To reverse `b` bits, set `n = 2 ^ b` + * @param[in] value The bits to be reversed + * + * @return The reversal of the lowest log_2(n) bits of the input value. + * + * @remark n must be a power of two. + */ +uint32_t reverse_bits_limited(uint32_t n, uint32_t value) { + size_t unused_bit_len = 32 - log2_pow2(n); + return reverse_bits(value) >> unused_bit_len; +} + +/** + * Reorder an array in reverse bit order of its indices. + * + * @param[in,out] values The array, which is re-ordered in-place + * @param[in] size The size in bytes of an element of the array + * @param[in] n The length of the array, must be a power of two + * strictly greater than 1 and less than 2^32. + * + * @remark Operates in-place on the array. + * @remark Can handle arrays of any type: provide the element size in `size`. + * @remark This means that `input[n] == output[n']`, where input and output denote the input and + * output array and n' is obtained from n by bit-reversing n. As opposed to reverse_bits, this + * bit-reversal operates on log2(n)-bit numbers. + */ +C_KZG_RET bit_reversal_permutation(void *values, size_t size, uint64_t n) { + C_KZG_RET ret; + byte *tmp = NULL; + byte *v = values; + + /* Some sanity checks */ + if (n < 2 || n >= UINT32_MAX || !is_power_of_two(n)) { + ret = C_KZG_BADARGS; + goto out; + } + + /* Scratch space for swapping an entry of the values array */ + ret = c_kzg_malloc((void **)&tmp, size); + if (ret != C_KZG_OK) goto out; + + /* Reorder elements */ + 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, v + (i * size), size); + memcpy(v + (i * size), v + (r * size), size); + memcpy(v + (r * size), tmp, size); + } + } + +out: + c_kzg_free(tmp); + return ret; +} + +/** + * Compute and return [ x^0, x^1, ..., x^{n-1} ]. + * + * @param[out] out The array to store the powers + * @param[in] x The field element to raise to powers + * @param[in] n The number of powers to compute + * + * @remark `out` is left untouched if `n == 0`. + */ +void compute_powers(fr_t *out, const fr_t *x, uint64_t n) { + fr_t current_power = FR_ONE; + for (uint64_t i = 0; i < n; i++) { + out[i] = current_power; + blst_fr_mul(¤t_power, ¤t_power, x); + } +} + +/** + * Perform pairings and test whether the outcomes are equal in G_T. + * + * Tests whether `e(a1, a2) == e(b1, b2)`. + * + * @param[in] a1 A G1 group point for the first pairing + * @param[in] a2 A G2 group point for the first pairing + * @param[in] b1 A G1 group point for the second pairing + * @param[in] b2 A G2 group point for the second pairing + * + * @retval true The pairings were equal + * @retval false The pairings were not equal + */ +bool pairings_verify(const g1_t *a1, const g2_t *a2, const g1_t *b1, const g2_t *b2) { + blst_fp12 loop0, loop1, gt_point; + blst_p1_affine aa1, bb1; + blst_p2_affine aa2, bb2; + + /* + * As an optimisation, we want to invert one of the pairings, + * so we negate one of the points. + */ + g1_t a1neg = *a1; + blst_p1_cneg(&a1neg, true); + + blst_p1_to_affine(&aa1, &a1neg); + blst_p1_to_affine(&bb1, b1); + blst_p2_to_affine(&aa2, a2); + blst_p2_to_affine(&bb2, b2); + + blst_miller_loop(&loop0, &aa2, &aa1); + blst_miller_loop(&loop1, &bb2, &bb1); + + blst_fp12_mul(>_point, &loop0, &loop1); + blst_final_exp(>_point, >_point); + + return blst_fp12_is_one(>_point); +} diff --git a/src/common/utils.h b/src/common/utils.h new file mode 100644 index 000000000..acd56477f --- /dev/null +++ b/src/common/utils.h @@ -0,0 +1,44 @@ +/* + * Copyright 2024 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. + */ + +#pragma once + +#include "common/ec.h" +#include "common/ret.h" + +#include /* For uint*_t */ +#include /* For bool */ +#include /* For size_t */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Public Functions +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +bool is_power_of_two(uint64_t n); +int log2_pow2(uint32_t n); +uint32_t reverse_bits(uint32_t n); +uint32_t reverse_bits_limited(uint32_t n, uint32_t value); +C_KZG_RET bit_reversal_permutation(void *values, size_t size, uint64_t n); +void compute_powers(fr_t *out, const fr_t *x, uint64_t n); +bool pairings_verify(const g1_t *a1, const g2_t *a2, const g1_t *b1, const g2_t *b2); + +#ifdef __cplusplus +} +#endif diff --git a/src/debug.c b/src/eip4844/blob.c similarity index 51% rename from src/debug.c rename to src/eip4844/blob.c index a478acbbf..4e2689e99 100644 --- a/src/debug.c +++ b/src/eip4844/blob.c @@ -14,46 +14,35 @@ * limitations under the License. */ -#include "debug.h" +#include "eip4844/blob.h" +#include "common/bytes.h" -#include +#include /* For printf */ -void print_bytes32(const Bytes32 *bytes) { - for (size_t i = 0; i < 32; i++) { - printf("%02x", bytes->bytes[i]); - } - printf("\n"); -} - -void print_bytes48(const Bytes48 *bytes) { - for (size_t i = 0; i < 48; i++) { - printf("%02x", bytes->bytes[i]); +/** + * Deserialize a blob (array of bytes) into a polynomial (array of field elements). + * + * @param[out] p The output polynomial (array of field elements) + * @param[in] blob The blob (an array of bytes) + * @param[in] n The number of field elements in the polynomial/blob + */ +C_KZG_RET blob_to_polynomial(fr_t *p, const Blob *blob) { + C_KZG_RET ret; + for (size_t i = 0; i < FIELD_ELEMENTS_PER_BLOB; i++) { + ret = bytes_to_bls_field(&p[i], (Bytes32 *)&blob->bytes[i * BYTES_PER_FIELD_ELEMENT]); + if (ret != C_KZG_OK) return ret; } - printf("\n"); -} - -void print_fr(const fr_t *f) { - Bytes32 bytes; - bytes_from_bls_field(&bytes, f); - print_bytes32(&bytes); -} - -void print_g1(const g1_t *g) { - Bytes48 bytes; - bytes_from_g1(&bytes, g); - print_bytes48(&bytes); + return C_KZG_OK; } +/** + * Print a Blob to the console. + * + * @param[in] blob The Blob to print + */ void print_blob(const Blob *blob) { for (size_t i = 0; i < FIELD_ELEMENTS_PER_BLOB; i++) { Bytes32 *field = (Bytes32 *)&blob->bytes[i * BYTES_PER_FIELD_ELEMENT]; print_bytes32(field); } } - -void print_cell(const Cell *cell) { - for (size_t i = 0; i < FIELD_ELEMENTS_PER_CELL; i++) { - Bytes32 *field = (Bytes32 *)&cell->bytes[i * BYTES_PER_FIELD_ELEMENT]; - print_bytes32(field); - } -} diff --git a/src/eip4844/blob.h b/src/eip4844/blob.h new file mode 100644 index 000000000..9c582ffc4 --- /dev/null +++ b/src/eip4844/blob.h @@ -0,0 +1,59 @@ +/* + * Copyright 2024 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. + */ + +#pragma once + +#include "common/fr.h" +#include "common/ret.h" + +#include /* For uint*_t */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** The number of field elements in a blob. */ +#define FIELD_ELEMENTS_PER_BLOB 4096 + +/** The number of field elements in an extended blob */ +#define FIELD_ELEMENTS_PER_EXT_BLOB (FIELD_ELEMENTS_PER_BLOB * 2) + +/** The number of bytes in a blob. */ +#define BYTES_PER_BLOB (FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD_ELEMENT) + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** A basic blob data. */ +typedef struct { + uint8_t bytes[BYTES_PER_BLOB]; +} Blob; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Public Functions +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +C_KZG_RET blob_to_polynomial(fr_t *p, const Blob *blob); +void print_blob(const Blob *blob); + +#ifdef __cplusplus +} +#endif diff --git a/src/eip4844.c b/src/eip4844/eip4844.c similarity index 98% rename from src/eip4844.c rename to src/eip4844/eip4844.c index 3b6d5b062..399988a97 100644 --- a/src/eip4844.c +++ b/src/eip4844/eip4844.c @@ -14,10 +14,14 @@ * limitations under the License. */ -#include "eip4844.h" -#include "common.h" - -#include "blst.h" +#include "eip4844/eip4844.h" +#include "common/alloc.h" +#include "common/ec.h" +#include "common/fr.h" +#include "common/lincomb.h" +#include "common/ret.h" +#include "common/utils.h" +#include "setup/settings.h" #include /* For assert */ #include /* For NULL */ @@ -27,6 +31,9 @@ // Macros //////////////////////////////////////////////////////////////////////////////////////////////////// +/** Length of the domain string. */ +#define DOMAIN_STR_LENGTH 16 + /* Input size to the Fiat-Shamir challenge computation. */ #define CHALLENGE_INPUT_SIZE (DOMAIN_STR_LENGTH + 16 + BYTES_PER_BLOB + BYTES_PER_COMMITMENT) diff --git a/src/eip4844.h b/src/eip4844/eip4844.h similarity index 81% rename from src/eip4844.h rename to src/eip4844/eip4844.h index 950f6c780..d890f27d1 100644 --- a/src/eip4844.h +++ b/src/eip4844/eip4844.h @@ -16,16 +16,28 @@ #pragma once -#include "common.h" +#include "common/bytes.h" +#include "eip4844/blob.h" +#include "setup/settings.h" -#ifdef __cplusplus -extern "C" { -#endif +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** A trusted (valid) KZG commitment. */ +typedef Bytes48 KZGCommitment; + +/** A trusted (valid) KZG proof. */ +typedef Bytes48 KZGProof; //////////////////////////////////////////////////////////////////////////////////////////////////// // Public Functions //////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +extern "C" { +#endif + C_KZG_RET blob_to_kzg_commitment(KZGCommitment *out, const Blob *blob, const KZGSettings *s); C_KZG_RET compute_kzg_proof( diff --git a/src/eip7594/cell.c b/src/eip7594/cell.c new file mode 100644 index 000000000..627b8abef --- /dev/null +++ b/src/eip7594/cell.c @@ -0,0 +1,32 @@ +/* + * Copyright 2024 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 "eip7594/cell.h" +#include "common/bytes.h" + +#include /* For printf */ + +/** + * Print Cell to the console. + * + * @param[in] cell The Cell to print + */ +void print_cell(const Cell *cell) { + for (size_t i = 0; i < FIELD_ELEMENTS_PER_CELL; i++) { + Bytes32 *field = (Bytes32 *)&cell->bytes[i * BYTES_PER_FIELD_ELEMENT]; + print_bytes32(field); + } +} diff --git a/src/eip7594.h b/src/eip7594/cell.h similarity index 74% rename from src/eip7594.h rename to src/eip7594/cell.h index b04287b95..c6b7a7279 100644 --- a/src/eip7594.h +++ b/src/eip7594/cell.h @@ -16,13 +16,9 @@ #pragma once -#include "common.h" +#include "eip4844/blob.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif +#include /* For uint8_t */ //////////////////////////////////////////////////////////////////////////////////////////////////// // Macros @@ -31,12 +27,12 @@ extern "C" { /** The number of field elements in a cell. */ #define FIELD_ELEMENTS_PER_CELL 64 -/** The number of cells in an extended blob. */ -#define CELLS_PER_EXT_BLOB (FIELD_ELEMENTS_PER_EXT_BLOB / FIELD_ELEMENTS_PER_CELL) - /** The number of bytes in a single cell. */ #define BYTES_PER_CELL (FIELD_ELEMENTS_PER_CELL * BYTES_PER_FIELD_ELEMENT) +/** The number of cells in an extended blob. */ +#define CELLS_PER_EXT_BLOB (FIELD_ELEMENTS_PER_EXT_BLOB / FIELD_ELEMENTS_PER_CELL) + //////////////////////////////////////////////////////////////////////////////////////////////////// // Types //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -50,28 +46,11 @@ typedef struct { // Public Functions //////////////////////////////////////////////////////////////////////////////////////////////////// -C_KZG_RET compute_cells_and_kzg_proofs( - Cell *cells, KZGProof *proofs, const Blob *blob, const KZGSettings *s -); - -C_KZG_RET recover_cells_and_kzg_proofs( - Cell *recovered_cells, - KZGProof *recovered_proofs, - const uint64_t *cell_indices, - const Cell *cells, - size_t num_cells, - const KZGSettings *s -); +#ifdef __cplusplus +extern "C" { +#endif -C_KZG_RET verify_cell_kzg_proof_batch( - bool *ok, - const Bytes48 *commitments_bytes, - const uint64_t *cell_indices, - const Cell *cells, - const Bytes48 *proofs_bytes, - size_t num_cells, - const KZGSettings *s -); +void print_cell(const Cell *cell); #ifdef __cplusplus } diff --git a/src/eip7594.c b/src/eip7594/eip7594.c similarity index 54% rename from src/eip7594.c rename to src/eip7594/eip7594.c index e827d3830..5499272d4 100644 --- a/src/eip7594.c +++ b/src/eip7594/eip7594.c @@ -14,871 +14,35 @@ * limitations under the License. */ -#include "eip7594.h" +#include "eip7594/eip7594.h" +#include "common/alloc.h" +#include "common/fr.h" +#include "common/lincomb.h" +#include "common/utils.h" +#include "eip7594/fft.h" +#include "eip7594/fk20.h" +#include "eip7594/poly.h" +#include "eip7594/recovery.h" -#include "blst.h" -#include "common.h" -#include "fft.h" - -#include /* For assert */ -#include /* For NULL */ -#include /* For memcpy */ - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Constants -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** The domain separator for verify_cell_kzg_proof_batch's random challenge. */ -static const char *RANDOM_CHALLENGE_DOMAIN_VERIFY_CELL_KZG_PROOF_BATCH = "RCKZGCBATCH__V1_"; - -/** - * The coset shift factor for the cell recovery code. - * - * fr_t a; - * fr_from_uint64(&a, 7); - * for (size_t i = 0; i < 4; i++) - * printf("%#018llxL,\n", a.l[i]); - */ -static const fr_t RECOVERY_SHIFT_FACTOR = { - 0x0000000efffffff1L, - 0x17e363d300189c0fL, - 0xff9c57876f8457b0L, - 0x351332208fc5a8c4L, -}; - -/** - * The inverse of RECOVERY_SHIFT_FACTOR. - * - * fr_t a; - * fr_from_uint64(&a, 7); - * fr_div(&a, &FR_ONE, &a); - * for (size_t i = 0; i < 4; i++) - * printf("%#018llxL,\n", a.l[i]); - */ -static const fr_t INV_RECOVERY_SHIFT_FACTOR = { - 0xdb6db6dadb6db6dcL, - 0xe6b5824adb6cc6daL, - 0xf8b356e005810db9L, - 0x66d0f1e660ec4796L, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Memory Allocation Functions -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Allocate memory for an array of booleans. - * - * @param[out] x Pointer to the allocated space - * @param[in] n The number of booleans to be allocated - * - * @remark Free the space later using c_kzg_free(). - */ -static C_KZG_RET new_bool_array(bool **x, size_t n) { - return c_kzg_calloc((void **)x, n, sizeof(bool)); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Helper Functions -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Test whether the operand is null (all 0xff's). - * - * @param[in] p The field element to be checked - * - * @retval true The element is null - * @retval false The element is not null - */ -static bool fr_is_null(const fr_t *p) { - return fr_equal(p, &FR_NULL); -} - -/** - * Reverse the low-order bits in a 32-bit integer. - * - * @param[in] n To reverse `b` bits, set `n = 2 ^ b` - * @param[in] value The bits to be reversed - * - * @return The reversal of the lowest log_2(n) bits of the input value. - * - * @remark n must be a power of two. - */ -static uint32_t reverse_bits_limited(uint32_t n, uint32_t value) { - size_t unused_bit_len = 32 - log2_pow2(n); - return reverse_bits(value) >> unused_bit_len; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Vanishing Polynomial -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Calculates the minimal polynomial that evaluates to zero for each root. - * - * Uses straightforward long multiplication to calculate the product of `(x - r_i)` where `r_i` is - * the i'th root. This results in a poly of degree roots_len. - * - * @param[in,out] poly The zero polynomial for roots - * @param[in,out] poly_len The length of poly - * @param[in] roots The array of roots - * @param[in] roots_len The number of roots - * @param[in] s The trusted setup - * - * @remark These do not have to be roots of unity. They are roots of a polynomial. - * @remark `poly_len` must be at least `roots_len + 1` in length. - */ -static C_KZG_RET compute_vanishing_polynomial_from_roots( - fr_t *poly, size_t *poly_len, const fr_t *roots, size_t roots_len -) { - fr_t neg_root; - - if (roots_len == 0) { - return C_KZG_BADARGS; - } - - /* Initialize with -root[0] */ - blst_fr_cneg(&poly[0], &roots[0], true); - - for (size_t i = 1; i < roots_len; i++) { - blst_fr_cneg(&neg_root, &roots[i], true); - - poly[i] = neg_root; - blst_fr_add(&poly[i], &poly[i], &poly[i - 1]); - - for (size_t j = i - 1; j > 0; j--) { - blst_fr_mul(&poly[j], &poly[j], &neg_root); - blst_fr_add(&poly[j], &poly[j], &poly[j - 1]); - } - blst_fr_mul(&poly[0], &poly[0], &neg_root); - } - - poly[roots_len] = FR_ONE; - *poly_len = roots_len + 1; - - return C_KZG_OK; -} - -/** - * Computes the minimal polynomial that evaluates to zero at equally spaced chosen roots of unity in - * the domain of size `FIELD_ELEMENTS_PER_BLOB`. - * - * The roots of unity are chosen based on the missing cell indices. If the i'th cell is missing, - * then the i'th root of unity from `roots_of_unity` will be zero on the polynomial - * computed, along with every `CELLS_PER_EXT_BLOB` spaced root of unity in the domain. - * - * @param[in,out] vanishing_poly The vanishing polynomial - * @param[in] missing_cell_indices The array of missing cell indices - * @param[in] len_missing_cells The number of missing cell indices - * @param[in] s The trusted setup - * - * @remark When all of the cells are missing, this algorithm has an edge case. We return - * C_KZG_BADARGS in that case. - * @remark When none of the cells are missing, recovery is trivial. We expect the caller to handle - * this case, and return C_KZG_BADARGS if not. - * @remark `missing_cell_indices` are assumed to be less than `CELLS_PER_EXT_BLOB`. - */ -static C_KZG_RET vanishing_polynomial_for_missing_cells( - fr_t *vanishing_poly, - const uint64_t *missing_cell_indices, - size_t len_missing_cells, - const KZGSettings *s -) { - C_KZG_RET ret; - fr_t *roots = NULL; - fr_t *short_vanishing_poly = NULL; - size_t short_vanishing_poly_len = 0; - - /* Return early if none or all of the cells are missing */ - if (len_missing_cells == 0 || len_missing_cells == CELLS_PER_EXT_BLOB) { - ret = C_KZG_BADARGS; - goto out; - } - - /* Allocate arrays */ - ret = new_fr_array(&roots, len_missing_cells); - if (ret != C_KZG_OK) goto out; - ret = new_fr_array(&short_vanishing_poly, (len_missing_cells + 1)); - if (ret != C_KZG_OK) goto out; - - /* - * For each missing cell index, choose the corresponding root of unity from the subgroup of - * size `CELLS_PER_EXT_BLOB`. - * - * In other words, if the missing index is `i`, then we add \omega^i to the roots array, where - * \omega is a primitive `CELLS_PER_EXT_BLOB` root of unity. - */ - size_t stride = FIELD_ELEMENTS_PER_EXT_BLOB / CELLS_PER_EXT_BLOB; - for (size_t i = 0; i < len_missing_cells; i++) { - roots[i] = s->roots_of_unity[missing_cell_indices[i] * stride]; - } - - /* Compute the polynomial that evaluates to zero on the roots */ - ret = compute_vanishing_polynomial_from_roots( - short_vanishing_poly, &short_vanishing_poly_len, roots, len_missing_cells - ); - if (ret != C_KZG_OK) goto out; - - /* - * For each root \omega^i in `short_vanishing_poly`, we compute a polynomial that has roots at - * - * H = { - * \omega^i * \gamma^0, - * \omega^i * \gamma^1, - * ..., - * \omega^i * \gamma^{FIELD_ELEMENTS_PER_CELL-1} - * } - * - * where \gamma is a primitive `FIELD_ELEMENTS_PER_EXT_BLOB`-th root of unity. - * - * This is done by shifting the degree of all coefficients in `short_vanishing_poly` up by - * `FIELD_ELEMENTS_PER_CELL` amount. - */ - for (size_t i = 0; i < short_vanishing_poly_len; i++) { - vanishing_poly[i * FIELD_ELEMENTS_PER_CELL] = short_vanishing_poly[i]; - } - -out: - c_kzg_free(roots); - c_kzg_free(short_vanishing_poly); - return ret; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Cell Recovery -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Shift a polynomial in place. - * - * Multiplies each coefficient by `shift_factor ^ i`. Equivalent to creating a polynomial that - * evaluates at `x * shift_factor` rather than `x`. - * - * @param[in,out] p The polynomial coefficients to be scaled - * @param[in] len Length of the polynomial coefficients - * @param[in] shift_factor Shift factor - */ -static void shift_poly(fr_t *p, size_t len, const fr_t *shift_factor) { - fr_t factor_power = FR_ONE; - for (size_t i = 1; i < len; i++) { - blst_fr_mul(&factor_power, &factor_power, shift_factor); - blst_fr_mul(&p[i], &p[i], &factor_power); - } -} - -/** - * Do an FFT over a coset of the roots of unity. - * - * @param[out] out The results (array of length n) - * @param[in] in The input data (array of length n) - * @param[in] n Length of the arrays - * @param[in] s The trusted setup - * - * @remark The coset shift factor is RECOVERY_SHIFT_FACTOR. - */ -static C_KZG_RET coset_fft(fr_t *out, const fr_t *in, size_t n, const KZGSettings *s) { - C_KZG_RET ret; - fr_t *in_shifted = NULL; - - /* Create some room to shift the polynomial */ - ret = new_fr_array(&in_shifted, n); - if (ret != C_KZG_OK) goto out; - - /* Shift the poly */ - memcpy(in_shifted, in, n * sizeof(fr_t)); - shift_poly(in_shifted, n, &RECOVERY_SHIFT_FACTOR); - - ret = fr_fft(out, in_shifted, n, s); - if (ret != C_KZG_OK) goto out; - -out: - c_kzg_free(in_shifted); - return ret; -} - -/** - * Do an inverse FFT over a coset of the roots of unity. - * - * @param[out] out The results (array of length n) - * @param[in] in The input data (array of length n) - * @param[in] n Length of the arrays - * @param[in] s The trusted setup - * - * @remark The coset shift factor is RECOVERY_SHIFT_FACTOR. In this function we use its inverse to - * implement the IFFT. - */ -static C_KZG_RET coset_ifft(fr_t *out, const fr_t *in, size_t n, const KZGSettings *s) { - C_KZG_RET ret; - - ret = fr_ifft(out, in, n, s); - if (ret != C_KZG_OK) goto out; - - shift_poly(out, n, &INV_RECOVERY_SHIFT_FACTOR); - -out: - return ret; -} - -/** - * Helper function to check if a uint64 value is in an array. - * - * @param[in] arr The array - * @param[in] arr_size The size of the array - * @param[in] value The value we want to search - * - * @return True if the value is in the array, otherwise false. - */ -static bool is_in_array(const uint64_t *arr, size_t arr_size, uint64_t value) { - for (size_t i = 0; i < arr_size; i++) { - if (arr[i] == value) { - return true; - } - } - return false; -} - -/** - * Given a dataset with up to half the entries missing, return the reconstructed original. Assumes - * that the inverse FFT of the original data has the upper half of its values equal to zero. - * - * @param[out] reconstructed_data_out Preallocated array for recovered cells - * @param[in] cell_indices The cell indices you have - * @param[in] num_cells The number of cells that you have - * @param[in] cells The cells that you have - * @param[in] s The trusted setup - * - * @remark `recovered` and `cells` can point to the same memory. - * @remark The array of cells must be 2n length and in the correct order. - * @remark Missing cells should be equal to FR_NULL. - */ -static C_KZG_RET recover_cells_impl( - fr_t *reconstructed_data_out, - const uint64_t *cell_indices, - size_t num_cells, - fr_t *cells, - const KZGSettings *s -) { - C_KZG_RET ret; - uint64_t *missing_cell_indices = NULL; - fr_t *vanishing_poly_eval = NULL; - fr_t *vanishing_poly_coeff = NULL; - fr_t *extended_evaluation_times_zero = NULL; - fr_t *extended_evaluation_times_zero_coeffs = NULL; - fr_t *extended_evaluations_over_coset = NULL; - fr_t *vanishing_poly_over_coset = NULL; - fr_t *reconstructed_poly_coeff = NULL; - fr_t *cells_brp = NULL; - - /* Allocate space for arrays */ - ret = c_kzg_calloc( - (void **)&missing_cell_indices, FIELD_ELEMENTS_PER_EXT_BLOB, sizeof(uint64_t) - ); - if (ret != C_KZG_OK) goto out; - ret = new_fr_array(&vanishing_poly_eval, FIELD_ELEMENTS_PER_EXT_BLOB); - if (ret != C_KZG_OK) goto out; - ret = new_fr_array(&vanishing_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB); - if (ret != C_KZG_OK) goto out; - ret = new_fr_array(&extended_evaluation_times_zero, FIELD_ELEMENTS_PER_EXT_BLOB); - if (ret != C_KZG_OK) goto out; - ret = new_fr_array(&extended_evaluation_times_zero_coeffs, FIELD_ELEMENTS_PER_EXT_BLOB); - if (ret != C_KZG_OK) goto out; - ret = new_fr_array(&extended_evaluations_over_coset, FIELD_ELEMENTS_PER_EXT_BLOB); - if (ret != C_KZG_OK) goto out; - ret = new_fr_array(&vanishing_poly_over_coset, FIELD_ELEMENTS_PER_EXT_BLOB); - if (ret != C_KZG_OK) goto out; - ret = new_fr_array(&reconstructed_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB); - if (ret != C_KZG_OK) goto out; - ret = new_fr_array(&cells_brp, FIELD_ELEMENTS_PER_EXT_BLOB); - if (ret != C_KZG_OK) goto out; - - /* Bit-reverse the data points, stored in new array */ - memcpy(cells_brp, cells, FIELD_ELEMENTS_PER_EXT_BLOB * sizeof(fr_t)); - ret = bit_reversal_permutation(cells_brp, sizeof(fr_t), FIELD_ELEMENTS_PER_EXT_BLOB); - if (ret != C_KZG_OK) goto out; - - /* Identify missing cells */ - size_t len_missing = 0; - for (size_t i = 0; i < CELLS_PER_EXT_BLOB; i++) { - /* Iterate over each cell index and check if we have received it */ - if (!is_in_array(cell_indices, num_cells, i)) { - /* - * If the cell is missing, bit reverse the index and add it to the - * missing array. - */ - uint32_t brp_i = reverse_bits_limited(CELLS_PER_EXT_BLOB, i); - missing_cell_indices[len_missing++] = brp_i; - } - } - - /* Check that we have enough cells */ - assert(len_missing <= CELLS_PER_EXT_BLOB / 2); - - /* Compute Z(x) in monomial form */ - ret = vanishing_polynomial_for_missing_cells( - vanishing_poly_coeff, missing_cell_indices, len_missing, s - ); - if (ret != C_KZG_OK) goto out; - - /* Convert Z(x) to evaluation form */ - ret = fr_fft(vanishing_poly_eval, vanishing_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB, s); - if (ret != C_KZG_OK) goto out; - - /* Compute (E*Z)(x) = E(x) * Z(x) in evaluation form over the FFT domain */ - for (size_t i = 0; i < FIELD_ELEMENTS_PER_EXT_BLOB; i++) { - if (fr_is_null(&cells_brp[i])) { - extended_evaluation_times_zero[i] = FR_ZERO; - } else { - blst_fr_mul(&extended_evaluation_times_zero[i], &cells_brp[i], &vanishing_poly_eval[i]); - } - } - - /* Convert (E*Z)(x) to monomial form */ - ret = fr_ifft( - extended_evaluation_times_zero_coeffs, - extended_evaluation_times_zero, - FIELD_ELEMENTS_PER_EXT_BLOB, - s - ); - if (ret != C_KZG_OK) goto out; - - /* - * Polynomial division by convolution: Q3 = Q1 / Q2 where - * Q1 = (D * Z_r,I)(k * x) - * Q2 = Z_r,I(k * x) - * Q3 = D(k * x) - */ - ret = coset_fft( - extended_evaluations_over_coset, - extended_evaluation_times_zero_coeffs, - FIELD_ELEMENTS_PER_EXT_BLOB, - s - ); - if (ret != C_KZG_OK) goto out; - - ret = coset_fft( - vanishing_poly_over_coset, vanishing_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB, s - ); - if (ret != C_KZG_OK) goto out; - - /* The result of the division is Q3 */ - for (size_t i = 0; i < FIELD_ELEMENTS_PER_EXT_BLOB; i++) { - fr_div( - &extended_evaluations_over_coset[i], - &extended_evaluations_over_coset[i], - &vanishing_poly_over_coset[i] - ); - } - - /* - * Note: After the above polynomial division, extended_evaluations_over_coset is the same - * polynomial as reconstructed_poly_over_coset in the spec. - */ - - /* Convert the evaluations back to coefficents */ - ret = coset_ifft( - reconstructed_poly_coeff, extended_evaluations_over_coset, FIELD_ELEMENTS_PER_EXT_BLOB, s - ); - if (ret != C_KZG_OK) goto out; - - /* - * After unscaling the reconstructed polynomial, we have D(x) which evaluates to our original - * data at the roots of unity. Next, we evaluate the polynomial to get the original data. - */ - ret = fr_fft(reconstructed_data_out, reconstructed_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB, s); - if (ret != C_KZG_OK) goto out; - - /* Bit-reverse the recovered data points */ - ret = bit_reversal_permutation( - reconstructed_data_out, sizeof(fr_t), FIELD_ELEMENTS_PER_EXT_BLOB - ); - if (ret != C_KZG_OK) goto out; - -out: - c_kzg_free(missing_cell_indices); - c_kzg_free(vanishing_poly_eval); - c_kzg_free(extended_evaluation_times_zero); - c_kzg_free(extended_evaluation_times_zero_coeffs); - c_kzg_free(extended_evaluations_over_coset); - c_kzg_free(vanishing_poly_over_coset); - c_kzg_free(reconstructed_poly_coeff); - c_kzg_free(vanishing_poly_coeff); - c_kzg_free(cells_brp); - return ret; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Polynomial Conversion Functions -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Bit reverses and converts a polynomial in lagrange form to monomial form. - * - * @param[out] monomial The result, an array of `len` fields - * @param[in] lagrange The input poly, an array of `len` fields - * @param[in] len The length of both polynomials - * @param[in] s The trusted setup - * - * @remark This method converts a lagrange-form polynomial to a monomial-form polynomial, by inverse - * FFTing the bit-reverse-permuted lagrange polynomial. - */ -static C_KZG_RET poly_lagrange_to_monomial( - fr_t *monomial_out, const fr_t *lagrange, size_t len, const KZGSettings *s -) { - C_KZG_RET ret; - fr_t *lagrange_brp = NULL; - - /* Allocate space for the intermediate BRP poly */ - ret = new_fr_array(&lagrange_brp, len); - if (ret != C_KZG_OK) goto out; - - /* Copy the values and perform a bit reverse permutation */ - memcpy(lagrange_brp, lagrange, sizeof(fr_t) * len); - ret = bit_reversal_permutation(lagrange_brp, sizeof(fr_t), len); - if (ret != C_KZG_OK) goto out; - - /* Perform an inverse FFT on the BRP'd polynomial */ - ret = fr_ifft(monomial_out, lagrange_brp, len, s); - if (ret != C_KZG_OK) goto out; - -out: - c_kzg_free(lagrange_brp); - return ret; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Cell Proofs -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Reorder and extend polynomial coefficients for the toeplitz method, strided version. - * - * @param[out] out The reordered polynomial, size `n * 2 / stride` - * @param[in] in The input polynomial, size `n` - * @param[in] n The size of the input polynomial - * @param[in] offset The offset - * @param[in] stride The stride - */ -static C_KZG_RET toeplitz_coeffs_stride( - fr_t *out, const fr_t *in, size_t n, uint64_t offset, uint64_t stride -) { - uint64_t k, k2; - - if (stride == 0) return C_KZG_BADARGS; - - k = n / stride; - k2 = k * 2; - - out[0] = in[n - 1 - offset]; - for (uint64_t i = 1; i <= k + 1 && i < k2; i++) { - out[i] = FR_ZERO; - } - for (uint64_t i = k + 2, j = 2 * stride - offset - 1; i < k2; i++, j += stride) { - out[i] = in[j]; - } - - return C_KZG_OK; -} - -/** - * Compute FK20 cell-proofs for a polynomial. - * - * @param[out] out An array of CELLS_PER_EXT_BLOB proofs - * @param[in] p The polynomial, an array of coefficients - * @param[in] n The length of the polynomial - * @param[in] s The trusted setup - * - * @remark The polynomial should have FIELD_ELEMENTS_PER_BLOB coefficients. Only the lower half of - * the extended polynomial is supplied because the upper half is assumed to be zero. - */ -static C_KZG_RET compute_fk20_proofs(g1_t *out, const fr_t *p, size_t n, const KZGSettings *s) { - C_KZG_RET ret; - uint64_t k, k2; - - blst_scalar *scalars = NULL; - fr_t **coeffs = NULL; - fr_t *toeplitz_coeffs = NULL; - fr_t *toeplitz_coeffs_fft = NULL; - g1_t *h = NULL; - g1_t *h_ext_fft = NULL; - void *scratch = NULL; - bool precompute = s->wbits != 0; - - /* Initialize length variables */ - k = n / FIELD_ELEMENTS_PER_CELL; - k2 = k * 2; - - /* Do allocations */ - ret = new_fr_array(&toeplitz_coeffs, k2); - if (ret != C_KZG_OK) goto out; - ret = new_fr_array(&toeplitz_coeffs_fft, k2); - if (ret != C_KZG_OK) goto out; - ret = new_g1_array(&h_ext_fft, k2); - if (ret != C_KZG_OK) goto out; - ret = new_g1_array(&h, k2); - if (ret != C_KZG_OK) goto out; - - if (precompute) { - /* Allocations for fixed-base MSM */ - ret = c_kzg_malloc(&scratch, s->scratch_size); - if (ret != C_KZG_OK) goto out; - ret = c_kzg_calloc((void **)&scalars, FIELD_ELEMENTS_PER_CELL, sizeof(blst_scalar)); - if (ret != C_KZG_OK) goto out; - } - - /* Allocate 2d array for coefficients by column */ - ret = c_kzg_calloc((void **)&coeffs, k2, sizeof(void *)); - if (ret != C_KZG_OK) goto out; - for (uint64_t i = 0; i < k2; i++) { - ret = new_fr_array(&coeffs[i], k); - if (ret != C_KZG_OK) goto out; - } - - /* Initialize values to zero */ - for (uint64_t i = 0; i < k2; i++) { - h_ext_fft[i] = G1_IDENTITY; - } - - /* Compute toeplitz coefficients and organize by column */ - for (uint64_t i = 0; i < FIELD_ELEMENTS_PER_CELL; i++) { - ret = toeplitz_coeffs_stride(toeplitz_coeffs, p, n, i, FIELD_ELEMENTS_PER_CELL); - if (ret != C_KZG_OK) goto out; - ret = fr_fft(toeplitz_coeffs_fft, toeplitz_coeffs, k2, s); - if (ret != C_KZG_OK) goto out; - for (uint64_t j = 0; j < k2; j++) { - coeffs[j][i] = toeplitz_coeffs_fft[j]; - } - } - - /* Compute h_ext_fft via MSM */ - for (uint64_t i = 0; i < k2; i++) { - if (precompute) { - /* Transform the field elements to 255-bit scalars */ - for (uint64_t j = 0; j < FIELD_ELEMENTS_PER_CELL; j++) { - blst_scalar_from_fr(&scalars[j], &coeffs[i][j]); - } - const byte *scalars_arg[2] = {(byte *)scalars, NULL}; - - /* A fixed-base MSM with precomputation */ - blst_p1s_mult_wbits( - &h_ext_fft[i], - s->tables[i], - s->wbits, - FIELD_ELEMENTS_PER_CELL, - scalars_arg, - BITS_PER_FIELD_ELEMENT, - scratch - ); - } else { - /* A pretty fast MSM without precomputation */ - ret = g1_lincomb_fast( - &h_ext_fft[i], s->x_ext_fft_columns[i], coeffs[i], FIELD_ELEMENTS_PER_CELL - ); - if (ret != C_KZG_OK) goto out; - } - } - - ret = g1_ifft(h, h_ext_fft, k2, s); - if (ret != C_KZG_OK) goto out; - - /* Zero the second half of h */ - for (uint64_t i = k; i < k2; i++) { - h[i] = G1_IDENTITY; - } - - ret = g1_fft(out, h, k2, s); - if (ret != C_KZG_OK) goto out; - -out: - c_kzg_free(scalars); - if (coeffs != NULL) { - for (uint64_t i = 0; i < k2; i++) { - c_kzg_free(coeffs[i]); - } - c_kzg_free(coeffs); - } - c_kzg_free(toeplitz_coeffs); - c_kzg_free(toeplitz_coeffs_fft); - c_kzg_free(h); - c_kzg_free(h_ext_fft); - c_kzg_free(scratch); - return ret; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Helper Functions for Batch Cell Verification -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Compute random linear combination challenge scalars for verify_cell_kzg_proof_batch. In this, we - * must hash EVERYTHING that the prover can control. - * - * @param[out] r_powers_out The output challenges - * @param[in] commitments_bytes The input commitments - * @param[in] num_commitments The number of commitments - * @param[in] commitment_indices The cell commitment indices - * @param[in] cell_indices The cell indices - * @param[in] cells The cell - * @param[in] proofs_bytes The cell proof - * @param[in] num_cells The number of cells - */ -static C_KZG_RET compute_r_powers_for_verify_cell_kzg_proof_batch( - fr_t *r_powers_out, - const Bytes48 *commitments_bytes, - size_t num_commitments, - const uint64_t *commitment_indices, - const uint64_t *cell_indices, - const Cell *cells, - const Bytes48 *proofs_bytes, - size_t num_cells -) { - C_KZG_RET ret; - uint8_t *bytes = NULL; - Bytes32 r_bytes; - fr_t r; - - /* Calculate the size of the data we're going to hash */ - size_t input_size = DOMAIN_STR_LENGTH /* The domain separator */ - + sizeof(uint64_t) /* FIELD_ELEMENTS_PER_CELL */ - + sizeof(uint64_t) /* num_commitments */ - + sizeof(uint64_t) /* num_cells */ - + (num_commitments * BYTES_PER_COMMITMENT) /* comms */ - + (num_cells * sizeof(uint64_t)) /* commitment_indices */ - + (num_cells * sizeof(uint64_t)) /* cell_indices */ - + (num_cells * BYTES_PER_CELL) /* cells */ - + (num_cells * BYTES_PER_PROOF); /* proofs_bytes */ - - /* Allocate space to copy this data into */ - ret = c_kzg_malloc((void **)&bytes, input_size); - if (ret != C_KZG_OK) goto out; - - /* Pointer tracking `bytes` for writing on top of it */ - uint8_t *offset = bytes; - - /* Copy domain separator */ - memcpy(offset, RANDOM_CHALLENGE_DOMAIN_VERIFY_CELL_KZG_PROOF_BATCH, DOMAIN_STR_LENGTH); - offset += DOMAIN_STR_LENGTH; - - /* Copy field elements per cell */ - bytes_from_uint64(offset, FIELD_ELEMENTS_PER_CELL); - offset += sizeof(uint64_t); - - /* Copy number of commitments */ - bytes_from_uint64(offset, num_commitments); - offset += sizeof(uint64_t); - - /* Copy number of cells */ - bytes_from_uint64(offset, num_cells); - offset += sizeof(uint64_t); - - for (size_t i = 0; i < num_commitments; i++) { - /* Copy commitment */ - memcpy(offset, &commitments_bytes[i], BYTES_PER_COMMITMENT); - offset += BYTES_PER_COMMITMENT; - } - - for (size_t i = 0; i < num_cells; i++) { - /* Copy row id */ - bytes_from_uint64(offset, commitment_indices[i]); - offset += sizeof(uint64_t); - - /* Copy column id */ - bytes_from_uint64(offset, cell_indices[i]); - offset += sizeof(uint64_t); - - /* Copy cell */ - memcpy(offset, &cells[i], BYTES_PER_CELL); - offset += BYTES_PER_CELL; - - /* Copy proof */ - memcpy(offset, &proofs_bytes[i], BYTES_PER_PROOF); - offset += BYTES_PER_PROOF; - } - - /* Now let's create the challenge! */ - blst_sha256(r_bytes.bytes, bytes, input_size); - hash_to_bls_field(&r, &r_bytes); - - /* Raise power of r for each cell */ - compute_powers(r_powers_out, &r, num_cells); - - /* Make sure we wrote the entire buffer */ - assert(offset == bytes + input_size); - -out: - c_kzg_free(bytes); - return ret; -} - -/** - * Helper function to compare two commitments. - * - * @param[in] a The first commitment - * @param[in] b The second commitment - * - * @return True if the commitments are the same, otherwise false. - */ -static bool commitments_equal(const Bytes48 *a, const Bytes48 *b) { - return memcmp(a->bytes, b->bytes, BYTES_PER_COMMITMENT) == 0; -} - -/** - * Helper function to copy one commitment's bytes to another. - * - * @param[in] dst The destination commitment - * @param[in] src The source commitment - */ -static void commitments_copy(Bytes48 *dst, const Bytes48 *src) { - memcpy(dst->bytes, src->bytes, BYTES_PER_COMMITMENT); -} +#include /* For assert */ +#include /* For memcpy */ -/** - * Convert a list of commitments with potential duplicates to a list of unique commitments. Also - * returns a list of indices which point to those new unique commitments. - * - * @param[in,out] commitments_out Updated to only contain unique commitments - * @param[out] indices_out Used as map between old/new commitments - * @param[in,out] count_out Number of commitments before and after - * - * @remark The input arrays are re-used. - * @remark The number of commitments/indices must be the same. - * @remark The length of `indices_out` is unchanged. - * @remark `count_out` is updated to be the number of unique commitments. - */ -static void deduplicate_commitments( - Bytes48 *commitments_out, uint64_t *indices_out, size_t *count_out -) { - /* Bail early if there are no commitments */ - if (*count_out == 0) return; +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Macros +//////////////////////////////////////////////////////////////////////////////////////////////////// - /* The first commitment is always new */ - indices_out[0] = 0; - size_t new_count = 1; +/** Length of the domain string. */ +#define DOMAIN_STR_LENGTH 16 - /* Create list of unique commitments & indices to them */ - for (size_t i = 1; i < *count_out; i++) { - bool exist = false; - for (size_t j = 0; j < new_count; j++) { - if (commitments_equal(&commitments_out[i], &commitments_out[j])) { - /* This commitment already exists */ - indices_out[i] = j; - exist = true; - break; - } - } - if (!exist) { - /* This is a new commitment */ - commitments_copy(&commitments_out[new_count], &commitments_out[i]); - indices_out[i] = new_count; - new_count++; - } - } +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Constants +//////////////////////////////////////////////////////////////////////////////////////////////////// - /* Update the count */ - *count_out = new_count; -} +/** The domain separator for verify_cell_kzg_proof_batch's random challenge. */ +static const char *RANDOM_CHALLENGE_DOMAIN_VERIFY_CELL_KZG_PROOF_BATCH = "RCKZGCBATCH__V1_"; //////////////////////////////////////////////////////////////////////////////////////////////////// -// Functions for EIP-7594 +// Compute //////////////////////////////////////////////////////////////////////////////////////////////////// /** @@ -977,6 +141,10 @@ C_KZG_RET compute_cells_and_kzg_proofs( return ret; } +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Recover +//////////////////////////////////////////////////////////////////////////////////////////////////// + /** * Given some cells for a blob, recover all cells/proofs. * @@ -1064,9 +232,7 @@ C_KZG_RET recover_cells_and_kzg_proofs( memcpy(recovered_cells, cells, CELLS_PER_EXT_BLOB * sizeof(Cell)); } else { /* Perform cell recovery */ - ret = recover_cells_impl( - recovered_cells_fr, cell_indices, num_cells, recovered_cells_fr, s - ); + ret = recover_cells(recovered_cells_fr, cell_indices, num_cells, recovered_cells_fr, s); if (ret != C_KZG_OK) goto out; /* Convert the recovered data points to byte-form */ @@ -1115,6 +281,179 @@ C_KZG_RET recover_cells_and_kzg_proofs( return ret; } +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Verify +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Helper function to compare two commitments. + * + * @param[in] a The first commitment + * @param[in] b The second commitment + * + * @return True if the commitments are the same, otherwise false. + */ +static bool commitments_equal(const Bytes48 *a, const Bytes48 *b) { + return memcmp(a->bytes, b->bytes, BYTES_PER_COMMITMENT) == 0; +} + +/** + * Helper function to copy one commitment's bytes to another. + * + * @param[in] dst The destination commitment + * @param[in] src The source commitment + */ +static void commitments_copy(Bytes48 *dst, const Bytes48 *src) { + memcpy(dst->bytes, src->bytes, BYTES_PER_COMMITMENT); +} + +/** + * Convert a list of commitments with potential duplicates to a list of unique commitments. Also + * returns a list of indices which point to those new unique commitments. + * + * @param[in,out] commitments_out Updated to only contain unique commitments + * @param[out] indices_out Used as map between old/new commitments + * @param[in,out] count_out Number of commitments before and after + * + * @remark The input arrays are re-used. + * @remark The number of commitments/indices must be the same. + * @remark The length of `indices_out` is unchanged. + * @remark `count_out` is updated to be the number of unique commitments. + */ +static void deduplicate_commitments( + Bytes48 *commitments_out, uint64_t *indices_out, size_t *count_out +) { + /* Bail early if there are no commitments */ + if (*count_out == 0) return; + + /* The first commitment is always new */ + indices_out[0] = 0; + size_t new_count = 1; + + /* Create list of unique commitments & indices to them */ + for (size_t i = 1; i < *count_out; i++) { + bool exist = false; + for (size_t j = 0; j < new_count; j++) { + if (commitments_equal(&commitments_out[i], &commitments_out[j])) { + /* This commitment already exists */ + indices_out[i] = j; + exist = true; + break; + } + } + if (!exist) { + /* This is a new commitment */ + commitments_copy(&commitments_out[new_count], &commitments_out[i]); + indices_out[i] = new_count; + new_count++; + } + } + + /* Update the count */ + *count_out = new_count; +} + +/** + * Compute random linear combination challenge scalars for verify_cell_kzg_proof_batch. In this, we + * must hash EVERYTHING that the prover can control. + * + * @param[out] r_powers_out The output challenges + * @param[in] commitments_bytes The input commitments + * @param[in] num_commitments The number of commitments + * @param[in] commitment_indices The cell commitment indices + * @param[in] cell_indices The cell indices + * @param[in] cells The cell + * @param[in] proofs_bytes The cell proof + * @param[in] num_cells The number of cells + */ +static C_KZG_RET compute_r_powers_for_verify_cell_kzg_proof_batch( + fr_t *r_powers_out, + const Bytes48 *commitments_bytes, + size_t num_commitments, + const uint64_t *commitment_indices, + const uint64_t *cell_indices, + const Cell *cells, + const Bytes48 *proofs_bytes, + size_t num_cells +) { + C_KZG_RET ret; + uint8_t *bytes = NULL; + Bytes32 r_bytes; + fr_t r; + + /* Calculate the size of the data we're going to hash */ + size_t input_size = DOMAIN_STR_LENGTH /* The domain separator */ + + sizeof(uint64_t) /* FIELD_ELEMENTS_PER_CELL */ + + sizeof(uint64_t) /* num_commitments */ + + sizeof(uint64_t) /* num_cells */ + + (num_commitments * BYTES_PER_COMMITMENT) /* comms */ + + (num_cells * sizeof(uint64_t)) /* commitment_indices */ + + (num_cells * sizeof(uint64_t)) /* cell_indices */ + + (num_cells * BYTES_PER_CELL) /* cells */ + + (num_cells * BYTES_PER_PROOF); /* proofs_bytes */ + + /* Allocate space to copy this data into */ + ret = c_kzg_malloc((void **)&bytes, input_size); + if (ret != C_KZG_OK) goto out; + + /* Pointer tracking `bytes` for writing on top of it */ + uint8_t *offset = bytes; + + /* Copy domain separator */ + memcpy(offset, RANDOM_CHALLENGE_DOMAIN_VERIFY_CELL_KZG_PROOF_BATCH, DOMAIN_STR_LENGTH); + offset += DOMAIN_STR_LENGTH; + + /* Copy field elements per cell */ + bytes_from_uint64(offset, FIELD_ELEMENTS_PER_CELL); + offset += sizeof(uint64_t); + + /* Copy number of commitments */ + bytes_from_uint64(offset, num_commitments); + offset += sizeof(uint64_t); + + /* Copy number of cells */ + bytes_from_uint64(offset, num_cells); + offset += sizeof(uint64_t); + + for (size_t i = 0; i < num_commitments; i++) { + /* Copy commitment */ + memcpy(offset, &commitments_bytes[i], BYTES_PER_COMMITMENT); + offset += BYTES_PER_COMMITMENT; + } + + for (size_t i = 0; i < num_cells; i++) { + /* Copy row id */ + bytes_from_uint64(offset, commitment_indices[i]); + offset += sizeof(uint64_t); + + /* Copy column id */ + bytes_from_uint64(offset, cell_indices[i]); + offset += sizeof(uint64_t); + + /* Copy cell */ + memcpy(offset, &cells[i], BYTES_PER_CELL); + offset += BYTES_PER_CELL; + + /* Copy proof */ + memcpy(offset, &proofs_bytes[i], BYTES_PER_PROOF); + offset += BYTES_PER_PROOF; + } + + /* Now let's create the challenge! */ + blst_sha256(r_bytes.bytes, bytes, input_size); + hash_to_bls_field(&r, &r_bytes); + + /* Raise power of r for each cell */ + compute_powers(r_powers_out, &r, num_cells); + + /* Make sure we wrote the entire buffer */ + assert(offset == bytes + input_size); + +out: + c_kzg_free(bytes); + return ret; +} + /** * Given some cells, verify that their proofs are valid. * diff --git a/src/eip7594/eip7594.h b/src/eip7594/eip7594.h new file mode 100644 index 000000000..411c134c6 --- /dev/null +++ b/src/eip7594/eip7594.h @@ -0,0 +1,59 @@ +/* + * Copyright 2024 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. + */ + +#pragma once + +#include "common/bytes.h" +#include "common/ret.h" +#include "eip4844/blob.h" +#include "eip4844/eip4844.h" +#include "eip7594/cell.h" +#include "setup/settings.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Public Functions +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +C_KZG_RET compute_cells_and_kzg_proofs( + Cell *cells, KZGProof *proofs, const Blob *blob, const KZGSettings *s +); + +C_KZG_RET recover_cells_and_kzg_proofs( + Cell *recovered_cells, + KZGProof *recovered_proofs, + const uint64_t *cell_indices, + const Cell *cells, + size_t num_cells, + const KZGSettings *s +); + +C_KZG_RET verify_cell_kzg_proof_batch( + bool *ok, + const Bytes48 *commitments_bytes, + const uint64_t *cell_indices, + const Cell *cells, + const Bytes48 *proofs_bytes, + size_t num_cells, + const KZGSettings *s +); + +#ifdef __cplusplus +} +#endif diff --git a/src/fft.c b/src/eip7594/fft.c similarity index 72% rename from src/fft.c rename to src/eip7594/fft.c index df332fc13..50c0d0b6f 100644 --- a/src/fft.c +++ b/src/eip7594/fft.c @@ -14,7 +14,48 @@ * limitations under the License. */ -#include "fft.h" +#include "eip7594/fft.h" +#include "common/alloc.h" +#include "common/utils.h" +#include "eip4844/blob.h" +#include "eip7594/poly.h" + +#include /* For memcpy */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Constants +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * The coset shift factor for the cell recovery code. + * + * fr_t a; + * fr_from_uint64(&a, 7); + * for (size_t i = 0; i < 4; i++) + * printf("%#018llxL,\n", a.l[i]); + */ +static const fr_t RECOVERY_SHIFT_FACTOR = { + 0x0000000efffffff1L, + 0x17e363d300189c0fL, + 0xff9c57876f8457b0L, + 0x351332208fc5a8c4L, +}; + +/** + * The inverse of RECOVERY_SHIFT_FACTOR. + * + * fr_t a; + * fr_from_uint64(&a, 7); + * fr_div(&a, &FR_ONE, &a); + * for (size_t i = 0; i < 4; i++) + * printf("%#018llxL,\n", a.l[i]); + */ +static const fr_t INV_RECOVERY_SHIFT_FACTOR = { + 0xdb6db6dadb6db6dcL, + 0xe6b5824adb6cc6daL, + 0xf8b356e005810db9L, + 0x66d0f1e660ec4796L, +}; //////////////////////////////////////////////////////////////////////////////////////////////////// // FFT Functions for Field Elements @@ -197,4 +238,61 @@ C_KZG_RET g1_ifft(g1_t *out, const g1_t *in, size_t n, const KZGSettings *s) { } return C_KZG_OK; -} \ No newline at end of file +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FFT Functions for Cosets +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Do an FFT over a coset of the roots of unity. + * + * @param[out] out The results (array of length n) + * @param[in] in The input data (array of length n) + * @param[in] n Length of the arrays + * @param[in] s The trusted setup + * + * @remark The coset shift factor is RECOVERY_SHIFT_FACTOR. + */ +C_KZG_RET coset_fft(fr_t *out, const fr_t *in, size_t n, const KZGSettings *s) { + C_KZG_RET ret; + fr_t *in_shifted = NULL; + + /* Create some room to shift the polynomial */ + ret = new_fr_array(&in_shifted, n); + if (ret != C_KZG_OK) goto out; + + /* Shift the poly */ + memcpy(in_shifted, in, n * sizeof(fr_t)); + shift_poly(in_shifted, n, &RECOVERY_SHIFT_FACTOR); + + ret = fr_fft(out, in_shifted, n, s); + if (ret != C_KZG_OK) goto out; + +out: + c_kzg_free(in_shifted); + return ret; +} + +/** + * Do an inverse FFT over a coset of the roots of unity. + * + * @param[out] out The results (array of length n) + * @param[in] in The input data (array of length n) + * @param[in] n Length of the arrays + * @param[in] s The trusted setup + * + * @remark The coset shift factor is RECOVERY_SHIFT_FACTOR. In this function we use its inverse to + * implement the IFFT. + */ +C_KZG_RET coset_ifft(fr_t *out, const fr_t *in, size_t n, const KZGSettings *s) { + C_KZG_RET ret; + + ret = fr_ifft(out, in, n, s); + if (ret != C_KZG_OK) goto out; + + shift_poly(out, n, &INV_RECOVERY_SHIFT_FACTOR); + +out: + return ret; +} diff --git a/src/fft.h b/src/eip7594/fft.h similarity index 81% rename from src/fft.h rename to src/eip7594/fft.h index f77db8a8c..494cc235e 100644 --- a/src/fft.h +++ b/src/eip7594/fft.h @@ -14,22 +14,30 @@ * limitations under the License. */ -#include "common.h" +#pragma once -#ifdef __cplusplus -extern "C" { -#endif +#include "common/ec.h" +#include "common/fr.h" +#include "common/ret.h" +#include "setup/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Public Functions //////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +extern "C" { +#endif + C_KZG_RET fr_fft(fr_t *out, const fr_t *in, size_t n, const KZGSettings *s); C_KZG_RET fr_ifft(fr_t *out, const fr_t *in, size_t n, const KZGSettings *s); C_KZG_RET g1_fft(g1_t *out, const g1_t *in, size_t n, const KZGSettings *s); C_KZG_RET g1_ifft(g1_t *out, const g1_t *in, size_t n, const KZGSettings *s); +C_KZG_RET coset_fft(fr_t *out, const fr_t *in, size_t n, const KZGSettings *s); +C_KZG_RET coset_ifft(fr_t *out, const fr_t *in, size_t n, const KZGSettings *s); + #ifdef __cplusplus } #endif diff --git a/src/eip7594/fk20.c b/src/eip7594/fk20.c new file mode 100644 index 000000000..4350fc6ba --- /dev/null +++ b/src/eip7594/fk20.c @@ -0,0 +1,178 @@ +/* + * Copyright 2024 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 "eip7594/fk20.h" +#include "common/alloc.h" +#include "common/lincomb.h" +#include "eip7594/cell.h" +#include "eip7594/fft.h" + +#include /* For NULL */ + +/** + * Reorder and extend polynomial coefficients for the toeplitz method, strided version. + * + * @param[out] out The reordered polynomial, size `n * 2 / stride` + * @param[in] in The input polynomial, size `n` + * @param[in] n The size of the input polynomial + * @param[in] offset The offset + * @param[in] stride The stride + */ +static C_KZG_RET toeplitz_coeffs_stride( + fr_t *out, const fr_t *in, size_t n, uint64_t offset, uint64_t stride +) { + uint64_t k, k2; + + if (stride == 0) return C_KZG_BADARGS; + + k = n / stride; + k2 = k * 2; + + out[0] = in[n - 1 - offset]; + for (uint64_t i = 1; i <= k + 1 && i < k2; i++) { + out[i] = FR_ZERO; + } + for (uint64_t i = k + 2, j = 2 * stride - offset - 1; i < k2; i++, j += stride) { + out[i] = in[j]; + } + + return C_KZG_OK; +} + +/** + * Compute FK20 cell-proofs for a polynomial. + * + * @param[out] out An array of CELLS_PER_EXT_BLOB proofs + * @param[in] p The polynomial, an array of coefficients + * @param[in] n The length of the polynomial + * @param[in] s The trusted setup + * + * @remark The polynomial should have FIELD_ELEMENTS_PER_BLOB coefficients. Only the lower half of + * the extended polynomial is supplied because the upper half is assumed to be zero. + */ +C_KZG_RET compute_fk20_proofs(g1_t *out, const fr_t *p, size_t n, const KZGSettings *s) { + C_KZG_RET ret; + uint64_t k, k2; + + blst_scalar *scalars = NULL; + fr_t **coeffs = NULL; + fr_t *toeplitz_coeffs = NULL; + fr_t *toeplitz_coeffs_fft = NULL; + g1_t *h = NULL; + g1_t *h_ext_fft = NULL; + void *scratch = NULL; + bool precompute = s->wbits != 0; + + /* Initialize length variables */ + k = n / FIELD_ELEMENTS_PER_CELL; + k2 = k * 2; + + /* Do allocations */ + ret = new_fr_array(&toeplitz_coeffs, k2); + if (ret != C_KZG_OK) goto out; + ret = new_fr_array(&toeplitz_coeffs_fft, k2); + if (ret != C_KZG_OK) goto out; + ret = new_g1_array(&h_ext_fft, k2); + if (ret != C_KZG_OK) goto out; + ret = new_g1_array(&h, k2); + if (ret != C_KZG_OK) goto out; + + if (precompute) { + /* Allocations for fixed-base MSM */ + ret = c_kzg_malloc(&scratch, s->scratch_size); + if (ret != C_KZG_OK) goto out; + ret = c_kzg_calloc((void **)&scalars, FIELD_ELEMENTS_PER_CELL, sizeof(blst_scalar)); + if (ret != C_KZG_OK) goto out; + } + + /* Allocate 2d array for coefficients by column */ + ret = c_kzg_calloc((void **)&coeffs, k2, sizeof(void *)); + if (ret != C_KZG_OK) goto out; + for (uint64_t i = 0; i < k2; i++) { + ret = new_fr_array(&coeffs[i], k); + if (ret != C_KZG_OK) goto out; + } + + /* Initialize values to zero */ + for (uint64_t i = 0; i < k2; i++) { + h_ext_fft[i] = G1_IDENTITY; + } + + /* Compute toeplitz coefficients and organize by column */ + for (uint64_t i = 0; i < FIELD_ELEMENTS_PER_CELL; i++) { + ret = toeplitz_coeffs_stride(toeplitz_coeffs, p, n, i, FIELD_ELEMENTS_PER_CELL); + if (ret != C_KZG_OK) goto out; + ret = fr_fft(toeplitz_coeffs_fft, toeplitz_coeffs, k2, s); + if (ret != C_KZG_OK) goto out; + for (uint64_t j = 0; j < k2; j++) { + coeffs[j][i] = toeplitz_coeffs_fft[j]; + } + } + + /* Compute h_ext_fft via MSM */ + for (uint64_t i = 0; i < k2; i++) { + if (precompute) { + /* Transform the field elements to 255-bit scalars */ + for (uint64_t j = 0; j < FIELD_ELEMENTS_PER_CELL; j++) { + blst_scalar_from_fr(&scalars[j], &coeffs[i][j]); + } + const byte *scalars_arg[2] = {(byte *)scalars, NULL}; + + /* A fixed-base MSM with precomputation */ + blst_p1s_mult_wbits( + &h_ext_fft[i], + s->tables[i], + s->wbits, + FIELD_ELEMENTS_PER_CELL, + scalars_arg, + BITS_PER_FIELD_ELEMENT, + scratch + ); + } else { + /* A pretty fast MSM without precomputation */ + ret = g1_lincomb_fast( + &h_ext_fft[i], s->x_ext_fft_columns[i], coeffs[i], FIELD_ELEMENTS_PER_CELL + ); + if (ret != C_KZG_OK) goto out; + } + } + + ret = g1_ifft(h, h_ext_fft, k2, s); + if (ret != C_KZG_OK) goto out; + + /* Zero the second half of h */ + for (uint64_t i = k; i < k2; i++) { + h[i] = G1_IDENTITY; + } + + ret = g1_fft(out, h, k2, s); + if (ret != C_KZG_OK) goto out; + +out: + c_kzg_free(scalars); + if (coeffs != NULL) { + for (uint64_t i = 0; i < k2; i++) { + c_kzg_free(coeffs[i]); + } + c_kzg_free(coeffs); + } + c_kzg_free(toeplitz_coeffs); + c_kzg_free(toeplitz_coeffs_fft); + c_kzg_free(h); + c_kzg_free(h_ext_fft); + c_kzg_free(scratch); + return ret; +} diff --git a/src/eip7594/fk20.h b/src/eip7594/fk20.h new file mode 100644 index 000000000..76db037ed --- /dev/null +++ b/src/eip7594/fk20.h @@ -0,0 +1,36 @@ +/* + * Copyright 2024 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. + */ + +#pragma once + +#include "common/ec.h" +#include "common/fr.h" +#include "common/ret.h" +#include "setup/settings.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Public Functions +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +C_KZG_RET compute_fk20_proofs(g1_t *out, const fr_t *p, size_t n, const KZGSettings *s); + +#ifdef __cplusplus +} +#endif diff --git a/src/eip7594/poly.c b/src/eip7594/poly.c new file mode 100644 index 000000000..ae69b181e --- /dev/null +++ b/src/eip7594/poly.c @@ -0,0 +1,78 @@ +/* + * Copyright 2024 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 "common/alloc.h" +#include "common/ec.h" +#include "common/ret.h" +#include "common/utils.h" +#include "eip7594/fft.h" +#include "setup/settings.h" + +#include /* For NULL */ +#include /* For memcpy */ + +/** + * Shift a polynomial in place. + * + * Multiplies each coefficient by `shift_factor ^ i`. Equivalent to creating a polynomial that + * evaluates at `x * shift_factor` rather than `x`. + * + * @param[in,out] p The polynomial coefficients to be scaled + * @param[in] len Length of the polynomial coefficients + * @param[in] shift_factor Shift factor + */ +void shift_poly(fr_t *p, size_t len, const fr_t *shift_factor) { + fr_t factor_power = FR_ONE; + for (size_t i = 1; i < len; i++) { + blst_fr_mul(&factor_power, &factor_power, shift_factor); + blst_fr_mul(&p[i], &p[i], &factor_power); + } +} + +/** + * Bit reverses and converts a polynomial in lagrange form to monomial form. + * + * @param[out] monomial The result, an array of `len` fields + * @param[in] lagrange The input poly, an array of `len` fields + * @param[in] len The length of both polynomials + * @param[in] s The trusted setup + * + * @remark This method converts a lagrange-form polynomial to a monomial-form polynomial, by inverse + * FFTing the bit-reverse-permuted lagrange polynomial. + */ +C_KZG_RET poly_lagrange_to_monomial( + fr_t *monomial_out, const fr_t *lagrange, size_t len, const KZGSettings *s +) { + C_KZG_RET ret; + fr_t *lagrange_brp = NULL; + + /* Allocate space for the intermediate BRP poly */ + ret = new_fr_array(&lagrange_brp, len); + if (ret != C_KZG_OK) goto out; + + /* Copy the values and perform a bit reverse permutation */ + memcpy(lagrange_brp, lagrange, sizeof(fr_t) * len); + ret = bit_reversal_permutation(lagrange_brp, sizeof(fr_t), len); + if (ret != C_KZG_OK) goto out; + + /* Perform an inverse FFT on the BRP'd polynomial */ + ret = fr_ifft(monomial_out, lagrange_brp, len, s); + if (ret != C_KZG_OK) goto out; + +out: + c_kzg_free(lagrange_brp); + return ret; +} diff --git a/src/setup.h b/src/eip7594/poly.h similarity index 66% rename from src/setup.h rename to src/eip7594/poly.h index 2a2f1ee40..ddff86698 100644 --- a/src/setup.h +++ b/src/eip7594/poly.h @@ -13,35 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #pragma once -#include "common.h" +#include "common/fr.h" +#include "common/ret.h" +#include "setup/settings.h" -#include +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Public Functions +//////////////////////////////////////////////////////////////////////////////////////////////////// #ifdef __cplusplus extern "C" { #endif -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Public Functions -//////////////////////////////////////////////////////////////////////////////////////////////////// +void shift_poly(fr_t *p, size_t len, const fr_t *shift_factor); -C_KZG_RET load_trusted_setup( - KZGSettings *out, - const uint8_t *g1_monomial_bytes, - size_t num_g1_monomial_bytes, - const uint8_t *g1_lagrange_bytes, - size_t num_g1_lagrange_bytes, - const uint8_t *g2_monomial_bytes, - size_t num_g2_monomial_bytes, - size_t precompute +C_KZG_RET poly_lagrange_to_monomial( + fr_t *monomial_out, const fr_t *lagrange, size_t len, const KZGSettings *s ); -C_KZG_RET load_trusted_setup_file(KZGSettings *out, FILE *in, size_t precompute); - -void free_trusted_setup(KZGSettings *s); - #ifdef __cplusplus } #endif diff --git a/src/eip7594/recovery.c b/src/eip7594/recovery.c new file mode 100644 index 000000000..af63a90ac --- /dev/null +++ b/src/eip7594/recovery.c @@ -0,0 +1,352 @@ +/* + * Copyright 2024 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 "eip7594/recovery.h" +#include "common/alloc.h" +#include "common/fr.h" +#include "common/utils.h" +#include "eip7594/cell.h" +#include "eip7594/fft.h" + +#include /* For assert */ +#include /* For NULL */ +#include /* For memcpy */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Vanishing Polynomial +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Calculates the minimal polynomial that evaluates to zero for each root. + * + * Uses straightforward long multiplication to calculate the product of `(x - r_i)` where `r_i` is + * the i'th root. This results in a poly of degree roots_len. + * + * @param[in,out] poly The zero polynomial for roots + * @param[in,out] poly_len The length of poly + * @param[in] roots The array of roots + * @param[in] roots_len The number of roots + * @param[in] s The trusted setup + * + * @remark These do not have to be roots of unity. They are roots of a polynomial. + * @remark `poly_len` must be at least `roots_len + 1` in length. + */ +static C_KZG_RET compute_vanishing_polynomial_from_roots( + fr_t *poly, size_t *poly_len, const fr_t *roots, size_t roots_len +) { + fr_t neg_root; + + if (roots_len == 0) { + return C_KZG_BADARGS; + } + + /* Initialize with -root[0] */ + blst_fr_cneg(&poly[0], &roots[0], true); + + for (size_t i = 1; i < roots_len; i++) { + blst_fr_cneg(&neg_root, &roots[i], true); + + poly[i] = neg_root; + blst_fr_add(&poly[i], &poly[i], &poly[i - 1]); + + for (size_t j = i - 1; j > 0; j--) { + blst_fr_mul(&poly[j], &poly[j], &neg_root); + blst_fr_add(&poly[j], &poly[j], &poly[j - 1]); + } + blst_fr_mul(&poly[0], &poly[0], &neg_root); + } + + poly[roots_len] = FR_ONE; + *poly_len = roots_len + 1; + + return C_KZG_OK; +} + +/** + * Computes the minimal polynomial that evaluates to zero at equally spaced chosen roots of unity in + * the domain of size `FIELD_ELEMENTS_PER_BLOB`. + * + * The roots of unity are chosen based on the missing cell indices. If the i'th cell is missing, + * then the i'th root of unity from `roots_of_unity` will be zero on the polynomial + * computed, along with every `CELLS_PER_EXT_BLOB` spaced root of unity in the domain. + * + * @param[in,out] vanishing_poly The vanishing polynomial + * @param[in] missing_cell_indices The array of missing cell indices + * @param[in] len_missing_cells The number of missing cell indices + * @param[in] s The trusted setup + * + * @remark When all of the cells are missing, this algorithm has an edge case. We return + * C_KZG_BADARGS in that case. + * @remark When none of the cells are missing, recovery is trivial. We expect the caller to handle + * this case, and return C_KZG_BADARGS if not. + * @remark `missing_cell_indices` are assumed to be less than `CELLS_PER_EXT_BLOB`. + */ +static C_KZG_RET vanishing_polynomial_for_missing_cells( + fr_t *vanishing_poly, + const uint64_t *missing_cell_indices, + size_t len_missing_cells, + const KZGSettings *s +) { + C_KZG_RET ret; + fr_t *roots = NULL; + fr_t *short_vanishing_poly = NULL; + size_t short_vanishing_poly_len = 0; + + /* Return early if none or all of the cells are missing */ + if (len_missing_cells == 0 || len_missing_cells == CELLS_PER_EXT_BLOB) { + ret = C_KZG_BADARGS; + goto out; + } + + /* Allocate arrays */ + ret = new_fr_array(&roots, len_missing_cells); + if (ret != C_KZG_OK) goto out; + ret = new_fr_array(&short_vanishing_poly, (len_missing_cells + 1)); + if (ret != C_KZG_OK) goto out; + + /* + * For each missing cell index, choose the corresponding root of unity from the subgroup of + * size `CELLS_PER_EXT_BLOB`. + * + * In other words, if the missing index is `i`, then we add \omega^i to the roots array, where + * \omega is a primitive `CELLS_PER_EXT_BLOB` root of unity. + */ + size_t stride = FIELD_ELEMENTS_PER_EXT_BLOB / CELLS_PER_EXT_BLOB; + for (size_t i = 0; i < len_missing_cells; i++) { + roots[i] = s->roots_of_unity[missing_cell_indices[i] * stride]; + } + + /* Compute the polynomial that evaluates to zero on the roots */ + ret = compute_vanishing_polynomial_from_roots( + short_vanishing_poly, &short_vanishing_poly_len, roots, len_missing_cells + ); + if (ret != C_KZG_OK) goto out; + + /* + * For each root \omega^i in `short_vanishing_poly`, we compute a polynomial that has roots at + * + * H = { + * \omega^i * \gamma^0, + * \omega^i * \gamma^1, + * ..., + * \omega^i * \gamma^{FIELD_ELEMENTS_PER_CELL-1} + * } + * + * where \gamma is a primitive `FIELD_ELEMENTS_PER_EXT_BLOB`-th root of unity. + * + * This is done by shifting the degree of all coefficients in `short_vanishing_poly` up by + * `FIELD_ELEMENTS_PER_CELL` amount. + */ + for (size_t i = 0; i < short_vanishing_poly_len; i++) { + vanishing_poly[i * FIELD_ELEMENTS_PER_CELL] = short_vanishing_poly[i]; + } + +out: + c_kzg_free(roots); + c_kzg_free(short_vanishing_poly); + return ret; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Cell Recovery +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Helper function to check if a uint64 value is in an array. + * + * @param[in] arr The array + * @param[in] arr_size The size of the array + * @param[in] value The value we want to search + * + * @return True if the value is in the array, otherwise false. + */ +static bool is_in_array(const uint64_t *arr, size_t arr_size, uint64_t value) { + for (size_t i = 0; i < arr_size; i++) { + if (arr[i] == value) { + return true; + } + } + return false; +} + +/** + * Given a dataset with up to half the entries missing, return the reconstructed original. Assumes + * that the inverse FFT of the original data has the upper half of its values equal to zero. + * + * @param[out] reconstructed_data_out Preallocated array for recovered cells + * @param[in] cell_indices The cell indices you have + * @param[in] num_cells The number of cells that you have + * @param[in] cells The cells that you have + * @param[in] s The trusted setup + * + * @remark `recovered` and `cells` can point to the same memory. + * @remark The array of cells must be 2n length and in the correct order. + * @remark Missing cells should be equal to FR_NULL. + */ +C_KZG_RET recover_cells( + fr_t *reconstructed_data_out, + const uint64_t *cell_indices, + size_t num_cells, + fr_t *cells, + const KZGSettings *s +) { + C_KZG_RET ret; + uint64_t *missing_cell_indices = NULL; + fr_t *vanishing_poly_eval = NULL; + fr_t *vanishing_poly_coeff = NULL; + fr_t *extended_evaluation_times_zero = NULL; + fr_t *extended_evaluation_times_zero_coeffs = NULL; + fr_t *extended_evaluations_over_coset = NULL; + fr_t *vanishing_poly_over_coset = NULL; + fr_t *reconstructed_poly_coeff = NULL; + fr_t *cells_brp = NULL; + + /* Allocate space for arrays */ + ret = c_kzg_calloc( + (void **)&missing_cell_indices, FIELD_ELEMENTS_PER_EXT_BLOB, sizeof(uint64_t) + ); + if (ret != C_KZG_OK) goto out; + ret = new_fr_array(&vanishing_poly_eval, FIELD_ELEMENTS_PER_EXT_BLOB); + if (ret != C_KZG_OK) goto out; + ret = new_fr_array(&vanishing_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB); + if (ret != C_KZG_OK) goto out; + ret = new_fr_array(&extended_evaluation_times_zero, FIELD_ELEMENTS_PER_EXT_BLOB); + if (ret != C_KZG_OK) goto out; + ret = new_fr_array(&extended_evaluation_times_zero_coeffs, FIELD_ELEMENTS_PER_EXT_BLOB); + if (ret != C_KZG_OK) goto out; + ret = new_fr_array(&extended_evaluations_over_coset, FIELD_ELEMENTS_PER_EXT_BLOB); + if (ret != C_KZG_OK) goto out; + ret = new_fr_array(&vanishing_poly_over_coset, FIELD_ELEMENTS_PER_EXT_BLOB); + if (ret != C_KZG_OK) goto out; + ret = new_fr_array(&reconstructed_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB); + if (ret != C_KZG_OK) goto out; + ret = new_fr_array(&cells_brp, FIELD_ELEMENTS_PER_EXT_BLOB); + if (ret != C_KZG_OK) goto out; + + /* Bit-reverse the data points, stored in new array */ + memcpy(cells_brp, cells, FIELD_ELEMENTS_PER_EXT_BLOB * sizeof(fr_t)); + ret = bit_reversal_permutation(cells_brp, sizeof(fr_t), FIELD_ELEMENTS_PER_EXT_BLOB); + if (ret != C_KZG_OK) goto out; + + /* Identify missing cells */ + size_t len_missing = 0; + for (size_t i = 0; i < CELLS_PER_EXT_BLOB; i++) { + /* Iterate over each cell index and check if we have received it */ + if (!is_in_array(cell_indices, num_cells, i)) { + /* + * If the cell is missing, bit reverse the index and add it to the + * missing array. + */ + uint32_t brp_i = reverse_bits_limited(CELLS_PER_EXT_BLOB, i); + missing_cell_indices[len_missing++] = brp_i; + } + } + + /* Check that we have enough cells */ + assert(len_missing <= CELLS_PER_EXT_BLOB / 2); + + /* Compute Z(x) in monomial form */ + ret = vanishing_polynomial_for_missing_cells( + vanishing_poly_coeff, missing_cell_indices, len_missing, s + ); + if (ret != C_KZG_OK) goto out; + + /* Convert Z(x) to evaluation form */ + ret = fr_fft(vanishing_poly_eval, vanishing_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB, s); + if (ret != C_KZG_OK) goto out; + + /* Compute (E*Z)(x) = E(x) * Z(x) in evaluation form over the FFT domain */ + for (size_t i = 0; i < FIELD_ELEMENTS_PER_EXT_BLOB; i++) { + if (fr_is_null(&cells_brp[i])) { + extended_evaluation_times_zero[i] = FR_ZERO; + } else { + blst_fr_mul(&extended_evaluation_times_zero[i], &cells_brp[i], &vanishing_poly_eval[i]); + } + } + + /* Convert (E*Z)(x) to monomial form */ + ret = fr_ifft( + extended_evaluation_times_zero_coeffs, + extended_evaluation_times_zero, + FIELD_ELEMENTS_PER_EXT_BLOB, + s + ); + if (ret != C_KZG_OK) goto out; + + /* + * Polynomial division by convolution: Q3 = Q1 / Q2 where + * Q1 = (D * Z_r,I)(k * x) + * Q2 = Z_r,I(k * x) + * Q3 = D(k * x) + */ + ret = coset_fft( + extended_evaluations_over_coset, + extended_evaluation_times_zero_coeffs, + FIELD_ELEMENTS_PER_EXT_BLOB, + s + ); + if (ret != C_KZG_OK) goto out; + + ret = coset_fft( + vanishing_poly_over_coset, vanishing_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB, s + ); + if (ret != C_KZG_OK) goto out; + + /* The result of the division is Q3 */ + for (size_t i = 0; i < FIELD_ELEMENTS_PER_EXT_BLOB; i++) { + fr_div( + &extended_evaluations_over_coset[i], + &extended_evaluations_over_coset[i], + &vanishing_poly_over_coset[i] + ); + } + + /* + * Note: After the above polynomial division, extended_evaluations_over_coset is the same + * polynomial as reconstructed_poly_over_coset in the spec. + */ + + /* Convert the evaluations back to coefficents */ + ret = coset_ifft( + reconstructed_poly_coeff, extended_evaluations_over_coset, FIELD_ELEMENTS_PER_EXT_BLOB, s + ); + if (ret != C_KZG_OK) goto out; + + /* + * After unscaling the reconstructed polynomial, we have D(x) which evaluates to our original + * data at the roots of unity. Next, we evaluate the polynomial to get the original data. + */ + ret = fr_fft(reconstructed_data_out, reconstructed_poly_coeff, FIELD_ELEMENTS_PER_EXT_BLOB, s); + if (ret != C_KZG_OK) goto out; + + /* Bit-reverse the recovered data points */ + ret = bit_reversal_permutation( + reconstructed_data_out, sizeof(fr_t), FIELD_ELEMENTS_PER_EXT_BLOB + ); + if (ret != C_KZG_OK) goto out; + +out: + c_kzg_free(missing_cell_indices); + c_kzg_free(vanishing_poly_eval); + c_kzg_free(extended_evaluation_times_zero); + c_kzg_free(extended_evaluation_times_zero_coeffs); + c_kzg_free(extended_evaluations_over_coset); + c_kzg_free(vanishing_poly_over_coset); + c_kzg_free(reconstructed_poly_coeff); + c_kzg_free(vanishing_poly_coeff); + c_kzg_free(cells_brp); + return ret; +} diff --git a/src/eip7594/recovery.h b/src/eip7594/recovery.h new file mode 100644 index 000000000..f7363080c --- /dev/null +++ b/src/eip7594/recovery.h @@ -0,0 +1,41 @@ +/* + * Copyright 2024 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. + */ + +#pragma once + +#include "common/fr.h" +#include "common/ret.h" +#include "setup/settings.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Public Functions +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +C_KZG_RET recover_cells( + fr_t *reconstructed_data_out, + const uint64_t *cell_indices, + size_t num_cells, + fr_t *cells, + const KZGSettings *s +); + +#ifdef __cplusplus +} +#endif diff --git a/src/setup/settings.h b/src/setup/settings.h new file mode 100644 index 000000000..fc902aa0f --- /dev/null +++ b/src/setup/settings.h @@ -0,0 +1,68 @@ +/* + * Copyright 2024 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. + */ + +#pragma once + +#include "common/ec.h" +#include "common/fr.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** Stores the setup and parameters needed for computing KZG proofs. */ +typedef struct { + /** + * Roots of unity for the subgroup of size `domain_size`. + * + * The array contains `domain_size + 1` elements, it starts and ends with Fr::one(). + */ + fr_t *roots_of_unity; + /** + * Roots of unity for the subgroup of size `domain_size` in bit-reversed order. + * + * This array is derived by applying a bit-reversal permutation to `roots_of_unity` + * excluding the last element. Essentially: + * `brp_roots_of_unity = bit_reversal_permutation(roots_of_unity[:-1])` + * + * The array contains `domain_size` elements. + */ + fr_t *brp_roots_of_unity; + /** + * Roots of unity for the subgroup of size `domain_size` in reversed order. + * + * It is the reversed version of `roots_of_unity`. Essentially: + * `reverse_roots_of_unity = reverse(roots_of_unity)` + * + * This array is primarily used in FFTs. + * The array contains `domain_size + 1` elements, it starts and ends with Fr::one(). + */ + fr_t *reverse_roots_of_unity; + /** G1 group elements from the trusted setup in monomial form. */ + g1_t *g1_values_monomial; + /** G1 group elements from the trusted setup in Lagrange form and bit-reversed order. */ + g1_t *g1_values_lagrange_brp; + /** G2 group elements from the trusted setup in monomial form. */ + g2_t *g2_values_monomial; + /** Data used during FK20 proof generation. */ + g1_t **x_ext_fft_columns; + /** The precomputed tables for fixed-base MSM. */ + blst_p1_affine **tables; + /** The window size for the fixed-base MSM. */ + size_t wbits; + /** The scratch size for the fixed-base MSM. */ + size_t scratch_size; +} KZGSettings; diff --git a/src/setup.c b/src/setup/setup.c similarity index 98% rename from src/setup.c rename to src/setup/setup.c index fee3f72a3..d0c973beb 100644 --- a/src/setup.c +++ b/src/setup/setup.c @@ -14,10 +14,11 @@ * limitations under the License. */ -#include "setup.h" -#include "common.h" -#include "eip7594.h" -#include "fft.h" +#include "setup/setup.h" +#include "common/alloc.h" +#include "common/utils.h" +#include "eip7594/eip7594.h" +#include "eip7594/fft.h" #include /* For assert */ #include /* For SCNu64 */ @@ -41,6 +42,9 @@ /** The number of g2 points in a trusted setup. */ #define NUM_G2_POINTS 65 +/** Returns number of elements in a statically defined array. */ +#define NUM_ELEMENTS(a) (sizeof(a) / sizeof(a[0])) + //////////////////////////////////////////////////////////////////////////////////////////////////// // Trusted Setup Functions //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/setup/setup.h b/src/setup/setup.h new file mode 100644 index 000000000..02d0df362 --- /dev/null +++ b/src/setup/setup.h @@ -0,0 +1,110 @@ +/* + * Copyright 2024 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. + */ +#pragma once + +#include "common/ret.h" +#include "setup/settings.h" + +#include /* For FILE */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Constants +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * The first 32 roots of unity in the finite field F_r. SCALE2_ROOT_OF_UNITY[i] is a 2^i'th root of + * unity. + * + * For element `{A, B, C, D}`, the field element value is `A + B * 2^64 + C * 2^128 + D * 2^192`. + * This format may be converted to an `fr_t` type via the blst_fr_from_uint64() function. + * + * The decimal values may be calculated with the following Python code: + * @code{.py} + * MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513 + * PRIMITIVE_ROOT = 7 + * [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". + * + * In the formula above, the restriction can be slightly relaxed to `r` being a non-square. This is + * easy to check: We just require that r^((q-1)/2) == -1. Instead of 7, we could use 10, 13, 14, 15, + * 20... to create the 2^i'th roots of unity below. Generally, 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}, + {0xffffffff00000000L, 0x53bda402fffe5bfeL, 0x3339d80809a1d805L, 0x73eda753299d7d48L}, + {0x0001000000000000L, 0xec03000276030000L, 0x8d51ccce760304d0L, 0x0000000000000000L}, + {0x7228fd3397743f7aL, 0xb38b21c28713b700L, 0x8c0625cd70d77ce2L, 0x345766f603fa66e7L}, + {0x53ea61d87742bcceL, 0x17beb312f20b6f76L, 0xdd1c0af834cec32cL, 0x20b1ce9140267af9L}, + {0x360c60997369df4eL, 0xbf6e88fb4c38fb8aL, 0xb4bcd40e22f55448L, 0x50e0903a157988baL}, + {0x8140d032f0a9ee53L, 0x2d967f4be2f95155L, 0x14a1e27164d8fdbdL, 0x45af6345ec055e4dL}, + {0x5130c2c1660125beL, 0x98d0caac87f5713cL, 0xb7c68b4d7fdd60d0L, 0x6898111413588742L}, + {0x4935bd2f817f694bL, 0x0a0865a899e8deffL, 0x6b368121ac0cf4adL, 0x4f9b4098e2e9f12eL}, + {0x4541b8ff2ee0434eL, 0xd697168a3a6000feL, 0x39feec240d80689fL, 0x095166525526a654L}, + {0x3c28d666a5c2d854L, 0xea437f9626fc085eL, 0x8f4de02c0f776af3L, 0x325db5c3debf77a1L}, + {0x4a838b5d59cd79e5L, 0x55ea6811be9c622dL, 0x09f1ca610a08f166L, 0x6d031f1b5c49c834L}, + {0xe206da11a5d36306L, 0x0ad1347b378fbf96L, 0xfc3e8acfe0f8245fL, 0x564c0a11a0f704f4L}, + {0x6fdd00bfc78c8967L, 0x146b58bc434906acL, 0x2ccddea2972e89edL, 0x485d512737b1da3dL}, + {0x034d2ff22a5ad9e1L, 0xae4622f6a9152435L, 0xdc86b01c0d477fa6L, 0x56624634b500a166L}, + {0xfbd047e11279bb6eL, 0xc8d5f51db3f32699L, 0x483405417a0cbe39L, 0x3291357ee558b50dL}, + {0xd7118f85cd96b8adL, 0x67a665ae1fcadc91L, 0x88f39a78f1aeb578L, 0x2155379d12180caaL}, + {0x08692405f3b70f10L, 0xcd7f2bd6d0711b7dL, 0x473a2eef772c33d6L, 0x224262332d8acbf4L}, + {0x6f421a7d8ef674fbL, 0xbb97a3bf30ce40fdL, 0x652f717ae1c34bb0L, 0x2d3056a530794f01L}, + {0x194e8c62ecb38d9dL, 0xad8e16e84419c750L, 0xdf625e80d0adef90L, 0x520e587a724a6955L}, + {0xfece7e0e39898d4bL, 0x2f69e02d265e09d9L, 0xa57a6e07cb98de4aL, 0x03e1c54bcb947035L}, + {0xcd3979122d3ea03aL, 0x46b3105f04db5844L, 0xc70d0874b0691d4eL, 0x47c8b5817018af4fL}, + {0xc6e7a6ffb08e3363L, 0xe08fec7c86389beeL, 0xf2d38f10fbb8d1bbL, 0x0abe6a5e5abcaa32L}, + {0x5616c57de0ec9eaeL, 0xc631ffb2585a72dbL, 0x5121af06a3b51e3cL, 0x73560252aa0655b2L}, + {0x92cf4deb77bd779cL, 0x72cf6a8029b7d7bcL, 0x6e0bcd91ee762730L, 0x291cf6d68823e687L}, + {0xce32ef844e11a51eL, 0xc0ba12bb3da64ca5L, 0x0454dc1edc61a1a3L, 0x019fe632fd328739L}, + {0x531a11a0d2d75182L, 0x02c8118402867ddcL, 0x116168bffbedc11dL, 0x0a0a77a3b1980c0dL}, + {0xe2d0a7869f0319edL, 0xb94f1101b1d7a628L, 0xece8ea224f31d25dL, 0x23397a9300f8f98bL}, + {0xd7b688830a4f2089L, 0x6558e9e3f6ac7b41L, 0x99e276b571905a7dL, 0x52dd465e2f094256L}, + {0x474650359d8e211bL, 0x84d37b826214abc6L, 0x8da40c1ef2bb4598L, 0x0c83ea7744bf1beeL}, + {0x694341f608c9dd56L, 0xed3a181fabb30adcL, 0x1339a815da8b398fL, 0x2c6d4e4511657e1eL}, + {0x63e7cb4906ffc93fL, 0xf070bb00e28a193dL, 0xad1715b02e5713b5L, 0x4b5371495990693fL} +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Public Functions +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +C_KZG_RET load_trusted_setup( + KZGSettings *out, + const uint8_t *g1_monomial_bytes, + size_t num_g1_monomial_bytes, + const uint8_t *g1_lagrange_bytes, + size_t num_g1_lagrange_bytes, + const uint8_t *g2_monomial_bytes, + size_t num_g2_monomial_bytes, + size_t precompute +); + +C_KZG_RET load_trusted_setup_file(KZGSettings *out, FILE *in, size_t precompute); + +void free_trusted_setup(KZGSettings *s); + +#ifdef __cplusplus +} +#endif diff --git a/src/tests.c b/src/test/tests.c similarity index 99% rename from src/tests.c rename to src/test/tests.c index c4128d96a..dabd03e60 100644 --- a/src/tests.c +++ b/src/test/tests.c @@ -2,7 +2,6 @@ * This file contains unit tests for C-KZG-4844. */ #include "ckzg.c" -#include "debug.h" #include "tinytest.h" #include diff --git a/src/tinytest.h b/src/test/tinytest.h similarity index 100% rename from src/tinytest.h rename to src/test/tinytest.h