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