diff --git a/.github/workflows/backend-benchmarks.yml b/.github/workflows/backend-benchmarks.yml index b6c27ed9..f48e8b03 100644 --- a/.github/workflows/backend-benchmarks.yml +++ b/.github/workflows/backend-benchmarks.yml @@ -11,7 +11,7 @@ jobs: matrix: os: [ubuntu-latest, windows-latest, macos-11] # Excluding mcl for now due to they have different project layout - backend: [blst, zkcrypto, arkworks] + backend: [blst, zkcrypto, arkworks, constantine] include: # Setup exec_once_per_backend flag - os: ubuntu-latest @@ -23,6 +23,8 @@ jobs: support_ckzg: true - backend: arkworks support_ckzg: true + - backend: constantine + support_ckzg: true steps: - uses: actions/checkout@v2 @@ -31,6 +33,20 @@ jobs: distribution: "temurin" java-version: "11" + - if: matrix.backend == 'constantine' + uses: jiro4989/setup-nim-action@v1 + with: + nim-version: '1.6.14' + + # Install constantine backend deps + - name: "constantine - install deps" + if: matrix.backend == 'constantine' && matrix.os == 'ubuntu-latest' + run: | + sudo DEBIAN_FRONTEND='noninteractive' apt-fast install \ + --no-install-recommends -yq \ + libgmp-dev \ + llvm + - uses: actions/setup-go@v2 with: go-version: ^1.19 diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index cf865e4e..6705e25e 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -11,7 +11,7 @@ jobs: matrix: os: [ubuntu-latest, windows-latest, macos-11] # Excluding mcl for now due to they have different project layout - backend: [blst, zkcrypto, arkworks] + backend: [blst, zkcrypto, arkworks, constantine] include: # Set default clippy flag to all-features - clippy-flag: --all-features @@ -36,6 +36,10 @@ jobs: - backend: arkworks support_wasm: true support_ckzg: true + - backend: constantine + # FIXME: Check for wasm support + support_wasm: false + support_ckzg: true steps: - uses: actions/checkout@v2 @@ -43,6 +47,12 @@ jobs: with: distribution: "temurin" java-version: "11" + + - if: matrix.backend == 'constantine' + uses: jiro4989/setup-nim-action@v1 + with: + nim-version: '1.6.14' + - uses: actions/setup-python@v4 with: python-version: '3.10' @@ -55,6 +65,15 @@ jobs: with: go-version: ^1.19 + # Install constantine backend deps + - name: "constantine - install deps" + if: matrix.backend == 'constantine' && matrix.os == 'ubuntu-latest' + run: | + sudo DEBIAN_FRONTEND='noninteractive' apt-fast install \ + --no-install-recommends -yq \ + libgmp-dev \ + llvm + # Check kzg clippy - name: "kzg clippy" if: matrix.exec_once_overall diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index aa163560..0fb4bd1b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - backend: [blst, zkcrypto, arkworks] + backend: [blst, zkcrypto, arkworks, constantine] target: [windows, linux] include: # Set target-name for target builds @@ -30,6 +30,20 @@ jobs: steps: - uses: actions/checkout@v2 + - if: matrix.backend == 'constantine' + uses: jiro4989/setup-nim-action@v1 + with: + nim-version: '1.6.14' + + # Install constantine backend deps + - name: "constantine - install deps" + if: matrix.backend == 'constantine' && matrix.os == 'ubuntu-latest' + run: | + sudo DEBIAN_FRONTEND='noninteractive' apt-fast install \ + --no-install-recommends -yq \ + libgmp-dev \ + llvm + - if: matrix.target == 'windows' name: Install MinGW run: | diff --git a/Cargo.lock b/Cargo.lock index d5f04ea0..f8532c30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -394,6 +394,28 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +[[package]] +name = "constantine-core" +version = "0.1.0" +source = "git+https://github.com/mratsim/constantine.git?branch=constantine-public-sys#9f85b8395456e726afc2633f8d8d1ed5e5877a3e" +dependencies = [ + "constantine-sys", +] + +[[package]] +name = "constantine-ethereum-kzg" +version = "0.1.0" +source = "git+https://github.com/mratsim/constantine.git?branch=constantine-public-sys#9f85b8395456e726afc2633f8d8d1ed5e5877a3e" +dependencies = [ + "constantine-core", + "constantine-sys", +] + +[[package]] +name = "constantine-sys" +version = "0.1.0" +source = "git+https://github.com/mratsim/constantine.git?branch=constantine-public-sys#9f85b8395456e726afc2633f8d8d1ed5e5877a3e" + [[package]] name = "cpufeatures" version = "0.2.11" @@ -1206,6 +1228,25 @@ dependencies = [ "smallvec", ] +[[package]] +name = "rust-kzg-constantine" +version = "0.1.0" +dependencies = [ + "blst", + "constantine-core", + "constantine-ethereum-kzg", + "constantine-sys", + "criterion 0.5.1", + "hex", + "kzg", + "kzg-bench", + "libc", + "once_cell", + "rand", + "rayon", + "smallvec", +] + [[package]] name = "rust-kzg-mcl" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index 1c64289a..38ae210d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "mcl/kzg", "mcl/kzg-bench", "zkcrypto", + "constantine" ] diff --git a/constantine/.gitignore b/constantine/.gitignore new file mode 100644 index 00000000..ab87df8d --- /dev/null +++ b/constantine/.gitignore @@ -0,0 +1 @@ +c-kzg-4844/ \ No newline at end of file diff --git a/constantine/Cargo.toml b/constantine/Cargo.toml new file mode 100644 index 00000000..6078fdaf --- /dev/null +++ b/constantine/Cargo.toml @@ -0,0 +1,93 @@ +[package] +name = "rust-kzg-constantine" +version = "0.1.0" +edition = "2021" + +[dependencies] +blst = "0.3.11" +kzg = { path = "../kzg", default-features = false } +libc = { version = "0.2.148", default-features = false } +once_cell = { version = "1.18.0", features = ["critical-section"], default-features = false } +constantine-ethereum-kzg = { 'git' = 'https://github.com/mratsim/constantine.git' , branch='constantine-public-sys' } +constantine-sys = { 'git' = 'https://github.com/mratsim/constantine.git' , branch='constantine-public-sys' } +constantine-core = { 'git' = 'https://github.com/mratsim/constantine.git' , branch='constantine-public-sys' } +rand = { version = "0.8.5", optional = true } +rayon = { version = "1.8.0", optional = true } +smallvec = { version = "1.11.1", features = ["const_generics"] } +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } + +[dev-dependencies] +criterion = "0.5.1" +kzg-bench = { path = "../kzg-bench" } +rand = "0.8.5" + +[features] +default = [ + "std", + "rand", +] +std = [ + "hex/std", + "kzg/std", + "libc/std", + "once_cell/std", +] +rand = [ + "dep:rand", + "kzg/rand", +] +parallel = [ + "dep:rayon", + "kzg/parallel" +] +constantine_msm = [] +bgmw = [ + "kzg/bgmw" +] +arkmsm = [ + "kzg/arkmsm" +] + +[[bench]] +name = "das" +harness = false + +[[bench]] +name = "fft" +harness = false + +[[bench]] +name = "poly" +harness = false + +[[bench]] +name = "kzg" +harness = false + +[[bench]] +name = "fk_20" +harness = false + +[[bench]] +name = "recover" +harness = false + +[[bench]] +name = "zero_poly" +harness = false + +[[bench]] +name = "eip_4844" +harness = false + +[[bench]] +name = "eip_4844_constantine" +harness = false + +[[bench]] +name = "eip_4844_constantine_no_conv" +harness = false + +[[bench]] +name = "lincomb" +harness = false diff --git a/constantine/README.md b/constantine/README.md new file mode 100644 index 00000000..2802da0e --- /dev/null +++ b/constantine/README.md @@ -0,0 +1,46 @@ +# KZG10-BLST +## [BLS12-381 lib](https://github.com/supranational/blst) + +Planned functionality: + * Finite field FFT (Fast Fourier Transform); + * G1 group FFT; + * Data availability sampling (data extension, recovery from any 1/2 samples); + * Zero polynomials; + * KZG10 single commit/verify; + * KZG10 multi commit/verify; + * FK20 single proof regular/optimised for DAS; + * FK20 multi proof regular/optimised for DAS. + * Test suite (based on C implementation) + * Benchmarks + +## Other implementations +* [C implementation](https://github.com/benjaminion/c-kzg) +* [Go implementation](https://github.com/protolambda/go-kzg) +* [Rust herumi implementation](https://github.com/UndeadRat22/kzg10-rust) + +``` + .-. .-. + / \ / \ + | _ \ / _ | + ; | \ \ / / | ; + \ \ \ \_.._/ / / / + '. '.;' ';,' .' + './ _ _ \.' + .' a __ a '. + '--./ _, \/ ,_ \.--' + ----| \ /\ / |---- + .--'\ '-' '-' /'--. + _>.__ -- _.- `; + .' _ __/ _/ + / '.,:".-\ /:, + | \.' `""`'.\\ + '-,.__/ _ .-. ;|_ + /` `|| _/ `\/_ \_|| `\ + | ||/ \-./` \ / || | + \ ||__/__|___|__|| / + \_ |__BLS12-381__| / + .' \ = _= _ = _= /`\ + / `-;----=--;--' \ + \ _.-' '. / + `""` Happy Easter `""` +``` diff --git a/constantine/benches/das.rs b/constantine/benches/das.rs new file mode 100644 index 00000000..bb5f7a17 --- /dev/null +++ b/constantine/benches/das.rs @@ -0,0 +1,16 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use kzg_bench::benches::das::bench_das_extension; +use rust_kzg_constantine::types::fft_settings::CtFFTSettings; +use rust_kzg_constantine::types::fr::CtFr; + +fn bench_das_extension_(c: &mut Criterion) { + bench_das_extension::(c) +} + +criterion_group! { + name = benches; + config = Criterion::default().sample_size(10); + targets = bench_das_extension_ +} + +criterion_main!(benches); diff --git a/constantine/benches/eip_4844.rs b/constantine/benches/eip_4844.rs new file mode 100644 index 00000000..cad3bd22 --- /dev/null +++ b/constantine/benches/eip_4844.rs @@ -0,0 +1,36 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use kzg::eip_4844::{ + blob_to_kzg_commitment_rust, bytes_to_blob, compute_blob_kzg_proof_rust, + compute_kzg_proof_rust, verify_blob_kzg_proof_batch_rust, verify_blob_kzg_proof_rust, + verify_kzg_proof_rust, +}; +use kzg_bench::benches::eip_4844::bench_eip_4844; +use rust_kzg_constantine::{ + eip_4844::load_trusted_setup_filename_rust, + types::{ + fft_settings::CtFFTSettings, + fp::CtFp, + fr::CtFr, + g1::{CtG1, CtG1Affine}, + g2::CtG2, + kzg_settings::CtKZGSettings, + poly::CtPoly, + }, +}; + +fn bench_eip_4844_(c: &mut Criterion) { + bench_eip_4844::( + c, + &load_trusted_setup_filename_rust, + &blob_to_kzg_commitment_rust, + &bytes_to_blob, + &compute_kzg_proof_rust, + &verify_kzg_proof_rust, + &compute_blob_kzg_proof_rust, + &verify_blob_kzg_proof_rust, + &verify_blob_kzg_proof_batch_rust, + ); +} + +criterion_group!(benches, bench_eip_4844_); +criterion_main!(benches); diff --git a/constantine/benches/eip_4844_constantine.rs b/constantine/benches/eip_4844_constantine.rs new file mode 100644 index 00000000..d0cf5d78 --- /dev/null +++ b/constantine/benches/eip_4844_constantine.rs @@ -0,0 +1,41 @@ +// Same as eip_4844.rs, but using constantine implementations of verification functions + +use criterion::{criterion_group, criterion_main, Criterion}; +use kzg::eip_4844::bytes_to_blob; +use kzg_bench::benches::eip_4844::bench_eip_4844; +use rust_kzg_constantine::{ + mixed_kzg::mixed_eip_4844::load_trusted_setup_filename_mixed, + mixed_kzg::{ + mixed_eip_4844::{ + blob_to_kzg_commitment_mixed, compute_blob_kzg_proof_mixed, compute_kzg_proof_mixed, + verify_blob_kzg_proof_batch_mixed, verify_blob_kzg_proof_mixed, verify_kzg_proof_mixed, + }, + mixed_kzg_settings::MixedKzgSettings, + }, + types::{ + fft_settings::CtFFTSettings, + fp::CtFp, + fr::CtFr, + g1::{CtG1, CtG1Affine}, + g2::CtG2, + poly::CtPoly, + }, +}; + +fn bench_eip_4844_constantine_(c: &mut Criterion) { + // Mixed KZG eip_4844 test - lots of conversions so not indicative of 'true' performance + bench_eip_4844::( + c, + &load_trusted_setup_filename_mixed, + &blob_to_kzg_commitment_mixed, + &bytes_to_blob, + &compute_kzg_proof_mixed, + &verify_kzg_proof_mixed, + &compute_blob_kzg_proof_mixed, + &verify_blob_kzg_proof_mixed, + &verify_blob_kzg_proof_batch_mixed, + ); +} + +criterion_group!(benches, bench_eip_4844_constantine_); +criterion_main!(benches); diff --git a/constantine/benches/eip_4844_constantine_no_conv.rs b/constantine/benches/eip_4844_constantine_no_conv.rs new file mode 100644 index 00000000..cfa31951 --- /dev/null +++ b/constantine/benches/eip_4844_constantine_no_conv.rs @@ -0,0 +1,169 @@ +// Same as eip_4844.rs, but using constantine implementations of verification functions + +use std::path::Path; + +use criterion::{criterion_group, criterion_main}; +use criterion::{BatchSize, BenchmarkId, Criterion, Throughput}; + +use kzg::eip_4844::{BYTES_PER_BLOB, TRUSTED_SETUP_PATH}; + +use kzg_bench::set_trusted_setup_dir; +use kzg_bench::tests::eip_4844::{generate_random_blob_bytes, generate_random_field_element_bytes}; +use rust_kzg_constantine::mixed_kzg::mixed_kzg_settings::CttContext; + +fn bench_eip_4844_constantine_no_conv_(c: &mut Criterion) { + set_trusted_setup_dir(); + let ctx = CttContext::new(Path::new(TRUSTED_SETUP_PATH)).unwrap(); + let mut rng = rand::thread_rng(); + const MAX_COUNT: usize = 64; + + let blobs: Vec<[u8; BYTES_PER_BLOB]> = (0..MAX_COUNT) + .map(|_| generate_random_blob_bytes(&mut rng)) + .collect(); + + let commitments: Vec<[u8; 48]> = blobs + .iter() + .map(|blob| ctx.ctx.blob_to_kzg_commitment(blob).unwrap()) + .collect(); + + let proofs: Vec<[u8; 48]> = blobs + .iter() + .zip(commitments.iter()) + .map(|(blob, commitment)| ctx.ctx.compute_blob_kzg_proof(blob, commitment).unwrap()) + .collect(); + + let fields: Vec<[u8; 32]> = (0..MAX_COUNT) + .map(|_| generate_random_field_element_bytes(&mut rng)) + .collect(); + + c.bench_function("blob_to_kzg_commitment", |b| { + #[cfg(feature = "parallel")] + b.iter(|| { + ctx.ctx + .blob_to_kzg_commitment_parallel(&ctx.pool, blobs.first().unwrap()) + }); + + #[cfg(not(feature = "parallel"))] + b.iter(|| ctx.ctx.blob_to_kzg_commitment(blobs.first().unwrap())); + }); + + c.bench_function("compute_kzg_proof", |b| { + #[cfg(feature = "parallel")] + b.iter(|| { + ctx.ctx.compute_kzg_proof_parallel( + &ctx.pool, + blobs.first().unwrap(), + fields.first().unwrap(), + ) + }); + + #[cfg(not(feature = "parallel"))] + b.iter(|| { + ctx.ctx + .compute_kzg_proof(blobs.first().unwrap(), fields.first().unwrap()) + }); + }); + + c.bench_function("verify_kzg_proof", |b| { + b.iter(|| { + ctx.ctx + .verify_kzg_proof( + commitments.first().unwrap(), + fields.first().unwrap(), + fields.first().unwrap(), + proofs.first().unwrap(), + ) + .unwrap() + }) + }); + + c.bench_function("compute_blob_kzg_proof", |b| { + #[cfg(feature = "parallel")] + b.iter(|| { + ctx.ctx.compute_blob_kzg_proof_parallel( + &ctx.pool, + blobs.first().unwrap(), + commitments.first().unwrap(), + ) + }); + + #[cfg(not(feature = "parallel"))] + b.iter(|| { + ctx.ctx + .compute_blob_kzg_proof(blobs.first().unwrap(), commitments.first().unwrap()) + }); + }); + + c.bench_function("verify_blob_kzg_proof", |b| { + #[cfg(feature = "parallel")] + b.iter(|| { + ctx.ctx + .verify_blob_kzg_proof_parallel( + &ctx.pool, + blobs.first().unwrap(), + commitments.first().unwrap(), + proofs.first().unwrap(), + ) + .unwrap() + }); + + #[cfg(not(feature = "parallel"))] + b.iter(|| { + ctx.ctx + .verify_blob_kzg_proof( + blobs.first().unwrap(), + commitments.first().unwrap(), + proofs.first().unwrap(), + ) + .unwrap() + }); + }); + + let mut group = c.benchmark_group("verify_blob_kzg_proof_batch"); + let rand_thing = [0u8; 32]; + for count in [1, 2, 4, 8, 16, 32, 64] { + group.throughput(Throughput::Elements(count as u64)); + group.bench_with_input(BenchmarkId::from_parameter(count), &count, |b, &count| { + b.iter_batched_ref( + || { + let blobs_subset = blobs.clone().into_iter().take(count).collect::>(); + let commitments_subset = commitments + .clone() + .into_iter() + .take(count) + .collect::>(); + let proofs_subset = proofs.clone().into_iter().take(count).collect::>(); + + (blobs_subset, commitments_subset, proofs_subset) + }, + |(blobs_subset, commitments_subset, proofs_subset)| { + #[cfg(feature = "parallel")] + ctx.ctx + .verify_blob_kzg_proof_batch_parallel( + &ctx.pool, + blobs_subset, + commitments_subset, + proofs_subset, + &rand_thing, + ) + .unwrap(); + + #[cfg(not(feature = "parallel"))] + ctx.ctx + .verify_blob_kzg_proof_batch( + blobs_subset, + commitments_subset, + proofs_subset, + &rand_thing, + ) + .unwrap(); + }, + BatchSize::LargeInput, + ); + }); + } + group.finish(); +} + +criterion_group!(benches, bench_eip_4844_constantine_no_conv_); +criterion_main!(benches); diff --git a/constantine/benches/fft.rs b/constantine/benches/fft.rs new file mode 100644 index 00000000..d6ab270c --- /dev/null +++ b/constantine/benches/fft.rs @@ -0,0 +1,21 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use kzg_bench::benches::fft::{bench_fft_fr, bench_fft_g1}; +use rust_kzg_constantine::types::fft_settings::CtFFTSettings; +use rust_kzg_constantine::types::fr::CtFr; +use rust_kzg_constantine::types::g1::CtG1; + +fn bench_fft_fr_(c: &mut Criterion) { + bench_fft_fr::(c); +} + +fn bench_fft_g1_(c: &mut Criterion) { + bench_fft_g1::(c); +} + +criterion_group! { + name = benches; + config = Criterion::default().sample_size(10); + targets = bench_fft_fr_, bench_fft_g1_ +} + +criterion_main!(benches); diff --git a/constantine/benches/fk_20.rs b/constantine/benches/fk_20.rs new file mode 100644 index 00000000..418159b1 --- /dev/null +++ b/constantine/benches/fk_20.rs @@ -0,0 +1,49 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use kzg_bench::benches::fk20::{bench_fk_multi_da, bench_fk_single_da}; + +use rust_kzg_constantine::types::fft_settings::CtFFTSettings; +use rust_kzg_constantine::types::fk20_multi_settings::CtFK20MultiSettings; +use rust_kzg_constantine::types::fk20_single_settings::CtFK20SingleSettings; +use rust_kzg_constantine::types::fp::CtFp; +use rust_kzg_constantine::types::fr::CtFr; +use rust_kzg_constantine::types::g1::{CtG1, CtG1Affine}; +use rust_kzg_constantine::types::g2::CtG2; +use rust_kzg_constantine::types::kzg_settings::CtKZGSettings; +use rust_kzg_constantine::types::poly::CtPoly; +use rust_kzg_constantine::utils::generate_trusted_setup; + +fn bench_fk_single_da_(c: &mut Criterion) { + bench_fk_single_da::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFK20SingleSettings, + CtFp, + CtG1Affine, + >(c, &generate_trusted_setup) +} + +fn bench_fk_multi_da_(c: &mut Criterion) { + bench_fk_multi_da::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFK20MultiSettings, + CtFp, + CtG1Affine, + >(c, &generate_trusted_setup) +} + +criterion_group! { + name = benches; + config = Criterion::default().sample_size(10); + targets = bench_fk_single_da_, bench_fk_multi_da_ +} + +criterion_main!(benches); diff --git a/constantine/benches/kzg.rs b/constantine/benches/kzg.rs new file mode 100644 index 00000000..32a1b9c8 --- /dev/null +++ b/constantine/benches/kzg.rs @@ -0,0 +1,38 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use kzg_bench::benches::kzg::{bench_commit_to_poly, bench_compute_proof_single}; +use rust_kzg_constantine::types::fft_settings::CtFFTSettings; +use rust_kzg_constantine::types::fp::CtFp; +use rust_kzg_constantine::types::fr::CtFr; +use rust_kzg_constantine::types::g1::{CtG1, CtG1Affine}; +use rust_kzg_constantine::types::g2::CtG2; +use rust_kzg_constantine::types::kzg_settings::CtKZGSettings; +use rust_kzg_constantine::types::poly::CtPoly; +use rust_kzg_constantine::utils::generate_trusted_setup; + +fn bench_commit_to_poly_(c: &mut Criterion) { + bench_commit_to_poly::( + c, + &generate_trusted_setup, + ) +} + +fn bench_compute_proof_single_(c: &mut Criterion) { + bench_compute_proof_single::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >(c, &generate_trusted_setup) +} + +criterion_group! { + name = benches; + config = Criterion::default().sample_size(10); + targets = bench_commit_to_poly_, bench_compute_proof_single_ +} + +criterion_main!(benches); diff --git a/constantine/benches/lincomb.rs b/constantine/benches/lincomb.rs new file mode 100644 index 00000000..e76c1e01 --- /dev/null +++ b/constantine/benches/lincomb.rs @@ -0,0 +1,18 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use kzg_bench::benches::lincomb::bench_g1_lincomb; +use rust_kzg_constantine::kzg_proofs::g1_linear_combination; +use rust_kzg_constantine::types::fp::CtFp; +use rust_kzg_constantine::types::fr::CtFr; +use rust_kzg_constantine::types::g1::{CtG1, CtG1Affine}; + +fn bench_g1_lincomb_(c: &mut Criterion) { + bench_g1_lincomb::(c, &g1_linear_combination); +} + +criterion_group! { + name = benches; + config = Criterion::default().sample_size(10); + targets = bench_g1_lincomb_ +} + +criterion_main!(benches); diff --git a/constantine/benches/poly.rs b/constantine/benches/poly.rs new file mode 100644 index 00000000..dbbe8101 --- /dev/null +++ b/constantine/benches/poly.rs @@ -0,0 +1,16 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use kzg_bench::benches::poly::bench_new_poly_div; +use rust_kzg_constantine::types::fr::CtFr; +use rust_kzg_constantine::types::poly::CtPoly; + +fn bench_new_poly_div_(c: &mut Criterion) { + bench_new_poly_div::(c); +} + +criterion_group! { + name = benches; + config = Criterion::default().sample_size(10); + targets = bench_new_poly_div_ +} + +criterion_main!(benches); diff --git a/constantine/benches/recover.rs b/constantine/benches/recover.rs new file mode 100644 index 00000000..e02e5201 --- /dev/null +++ b/constantine/benches/recover.rs @@ -0,0 +1,15 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use kzg_bench::benches::recover::bench_recover; +use rust_kzg_constantine::types::{fft_settings::CtFFTSettings, fr::CtFr, poly::CtPoly}; + +pub fn bench_recover_(c: &mut Criterion) { + bench_recover::(c) +} + +criterion_group! { + name = benches; + config = Criterion::default().sample_size(10); + targets = bench_recover_ +} + +criterion_main!(benches); diff --git a/constantine/benches/zero_poly.rs b/constantine/benches/zero_poly.rs new file mode 100644 index 00000000..eb7d59d4 --- /dev/null +++ b/constantine/benches/zero_poly.rs @@ -0,0 +1,15 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use kzg_bench::benches::zero_poly::bench_zero_poly; +use rust_kzg_constantine::types::{fft_settings::CtFFTSettings, fr::CtFr, poly::CtPoly}; + +fn bench_zero_poly_(c: &mut Criterion) { + bench_zero_poly::(c); +} + +criterion_group! { + name = benches; + config = Criterion::default().sample_size(10); + targets = bench_zero_poly_ +} + +criterion_main!(benches); diff --git a/constantine/csharp.patch b/constantine/csharp.patch new file mode 100644 index 00000000..9554ea16 --- /dev/null +++ b/constantine/csharp.patch @@ -0,0 +1,25 @@ +From 86aa67b0e3775514cc484ddd2adf6b5dc6e26803 Mon Sep 17 00:00:00 2001 +From: sirse +Date: Thu, 26 Oct 2023 13:40:30 +0300 +Subject: [PATCH] Patch csharp binding + +--- + bindings/csharp/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/bindings/csharp/Makefile b/bindings/csharp/Makefile +index 5158aad..af3b2a8 100644 +--- a/bindings/csharp/Makefile ++++ b/bindings/csharp/Makefile +@@ -39,7 +39,7 @@ else + endif + + INCLUDE_DIRS = ../../src ../../blst/bindings +-TARGETS = ckzg.c ../../src/c_kzg_4844.c ../../blst/$(BLST_OBJ) ++TARGETS = ckzg.c ../../../../target/release/rust_kzg_constantine.a + + CFLAGS += -O2 -Wall -Wextra -shared + CFLAGS += ${addprefix -I,${INCLUDE_DIRS}} +-- +2.34.1 + diff --git a/constantine/go.patch b/constantine/go.patch new file mode 100644 index 00000000..14183710 --- /dev/null +++ b/constantine/go.patch @@ -0,0 +1,43 @@ +From 90e9a518ca03716ef1d9d77e263db25b56030867 Mon Sep 17 00:00:00 2001 +From: sirse +Date: Thu, 26 Oct 2023 14:09:11 +0300 +Subject: [PATCH] Patch go binding + +--- + bindings/go/main.go | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/bindings/go/main.go b/bindings/go/main.go +index bdd5385..155fc81 100644 +--- a/bindings/go/main.go ++++ b/bindings/go/main.go +@@ -2,7 +2,15 @@ package ckzg4844 + + // #cgo CFLAGS: -I${SRCDIR}/../../src + // #cgo CFLAGS: -I${SRCDIR}/blst_headers +-// #include "c_kzg_4844.c" ++// #ifndef BYTES_PER_G1 ++// #define BYTES_PER_G1 48 ++// #endif ++// #ifndef BYTES_PER_G2 ++// #define BYTES_PER_G2 96 ++// #endif ++// #include ++// #include "c_kzg_4844.h" ++// #cgo LDFLAGS: -L${SRCDIR}/../../../../target/release -l:rust_kzg_constantine.a -lm + import "C" + + import ( +@@ -11,9 +19,6 @@ import ( + "fmt" + "strings" + "unsafe" +- +- // So its functions are available during compilation. +- _ "github.com/supranational/blst/bindings/go" + ) + + const ( +-- +2.34.1 + diff --git a/constantine/java.patch b/constantine/java.patch new file mode 100644 index 00000000..12ca3f38 --- /dev/null +++ b/constantine/java.patch @@ -0,0 +1,24 @@ +From b1f8f612f8c1bda0b4ea58e01e9a60235a88cc83 Mon Sep 17 00:00:00 2001 +From: povilassl +Date: Sun, 24 Sep 2023 18:01:51 +0300 +Subject: [PATCH] java patch + +--- + bindings/java/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/bindings/java/Makefile b/bindings/java/Makefile +index 9be2fd6..1e59378 100644 +--- a/bindings/java/Makefile ++++ b/bindings/java/Makefile +@@ -1,6 +1,6 @@ + INCLUDE_DIRS = ../../src ../../blst/bindings + +-TARGETS=c_kzg_4844_jni.c ../../src/c_kzg_4844.c ../../lib/libblst.a ++TARGETS=c_kzg_4844_jni.c ../../../../target/release/rust_kzg_constantine.a + + CC_FLAGS= + OPTIMIZATION_LEVEL=-O2 +-- +2.37.0.windows.1 + diff --git a/constantine/nodejs.patch b/constantine/nodejs.patch new file mode 100644 index 00000000..1cf0e3dc --- /dev/null +++ b/constantine/nodejs.patch @@ -0,0 +1,74 @@ +From 954c55533e265f32eabe0dc863b1add2478bb570 Mon Sep 17 00:00:00 2001 +From: sirse +Date: Thu, 26 Oct 2023 14:02:51 +0300 +Subject: [PATCH] Patch nodejs binding + +--- + bindings/node.js/Makefile | 1 - + bindings/node.js/binding.gyp | 31 +++---------------------------- + 2 files changed, 3 insertions(+), 29 deletions(-) + +diff --git a/bindings/node.js/Makefile b/bindings/node.js/Makefile +index 17850ec..efc9961 100644 +--- a/bindings/node.js/Makefile ++++ b/bindings/node.js/Makefile +@@ -31,7 +31,6 @@ build: install clean + @# Prepare the dependencies directory + @mkdir -p deps/c-kzg + @cp -r ../../blst deps +- @cp ../../src/c_kzg_4844.c deps/c-kzg + @cp ../../src/c_kzg_4844.h deps/c-kzg + @# Build the bindings + @$(YARN) node-gyp --loglevel=warn configure +diff --git a/bindings/node.js/binding.gyp b/bindings/node.js/binding.gyp +index 5ac368e..6cde37f 100644 +--- a/bindings/node.js/binding.gyp ++++ b/bindings/node.js/binding.gyp +@@ -3,9 +3,7 @@ + { + "target_name": "kzg", + "sources": [ +- "src/kzg.cxx", +- "deps/blst/src/server.c", +- "deps/c-kzg/c_kzg_4844.c" ++ "src/kzg.cxx" + ], + "include_dirs": [ + "<(module_root_dir)/deps/blst/bindings", +@@ -16,31 +14,8 @@ + "__BLST_PORTABLE__", + "NAPI_DISABLE_CPP_EXCEPTIONS" + ], +- "conditions": [ +- ["OS!='win'", { +- "sources": ["deps/blst/build/assembly.S"], +- "cflags_cc": [ +- "-std=c++17", +- "-fPIC" +- ] +- }], +- ["OS=='win'", { +- "sources": ["deps/blst/build/win64/*-x86_64.asm"], +- "defines": [ +- "_CRT_SECURE_NO_WARNINGS", +- ], +- "msbuild_settings": { +- "ClCompile": { +- "AdditionalOptions": ["/std:c++17"] +- } +- } +- }], +- ["OS=='mac'", { +- "xcode_settings": { +- "CLANG_CXX_LIBRARY": "libc++", +- "MACOSX_DEPLOYMENT_TARGET": "13.0" +- } +- }] ++ "libraries": [ ++ "<(module_root_dir)/../../../../target/release/rust_kzg_constantine.a" + ] + } + ] +-- +2.34.1 + diff --git a/constantine/python.patch b/constantine/python.patch new file mode 100644 index 00000000..c9ab361f --- /dev/null +++ b/constantine/python.patch @@ -0,0 +1,48 @@ +From a8ff3fe1e4372380f15769bbb8490b0089aa2928 Mon Sep 17 00:00:00 2001 +From: sirse +Date: Thu, 26 Oct 2023 13:51:37 +0300 +Subject: [PATCH] Patch python binding + +--- + bindings/python/Makefile | 5 +---- + bindings/python/setup.py | 6 +++--- + 2 files changed, 4 insertions(+), 7 deletions(-) + +diff --git a/bindings/python/Makefile b/bindings/python/Makefile +index c6bd222..99d6501 100644 +--- a/bindings/python/Makefile ++++ b/bindings/python/Makefile +@@ -1,11 +1,8 @@ + .PHONY: all + all: install test + +-../../src/c_kzg_4844.o: +- make -C../../src c_kzg_4844.o +- + .PHONY: install +-install: setup.py ckzg.c ../../src/c_kzg_4844.o ++install: setup.py ckzg.c + python3 setup.py install + + .PHONY: test +diff --git a/bindings/python/setup.py b/bindings/python/setup.py +index b072833..db37db4 100644 +--- a/bindings/python/setup.py ++++ b/bindings/python/setup.py +@@ -8,10 +8,10 @@ def main(): + ext_modules=[ + Extension( + "ckzg", +- sources=["ckzg.c", "../../src/c_kzg_4844.c"], ++ sources=["ckzg.c"], + include_dirs=["../../inc", "../../src"], +- library_dirs=["../../lib"], +- libraries=["blst"])]) ++ library_dirs=["../../lib", "../../../../target/release"], ++ libraries=[":rust_kzg_constantine.a"])]) + + if __name__ == "__main__": + main() +-- +2.34.1 + diff --git a/constantine/rust.patch b/constantine/rust.patch new file mode 100644 index 00000000..a298fd24 --- /dev/null +++ b/constantine/rust.patch @@ -0,0 +1,71 @@ +From c3d4cb77f5a797bd8f454a0d88e034391514ebd7 Mon Sep 17 00:00:00 2001 +From: sirse +Date: Thu, 26 Oct 2023 13:46:19 +0300 +Subject: [PATCH] Patch rust binding + +--- + bindings/rust/Cargo.toml | 1 + + bindings/rust/build.rs | 29 +++++------------------------ + 2 files changed, 6 insertions(+), 24 deletions(-) + +diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml +index ab1f5b8..44e410e 100644 +--- a/bindings/rust/Cargo.toml ++++ b/bindings/rust/Cargo.toml +@@ -1,3 +1,4 @@ ++[workspace] + [package] + name = "c-kzg" + version = "0.1.0" +diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs +index 692305a..e874ccd 100644 +--- a/bindings/rust/build.rs ++++ b/bindings/rust/build.rs +@@ -15,24 +15,7 @@ fn main() { + + let c_src_dir = root_dir.join("src"); + +- let mut cc = cc::Build::new(); +- +- #[cfg(windows)] +- { +- cc.flag("-D_CRT_SECURE_NO_WARNINGS"); +- +- // In blst, if __STDC_VERSION__ isn't defined as c99 or greater, it will typedef a bool to +- // an int. There is a bug in bindgen associated with this. It assumes that a bool in C is +- // the same size as a bool in Rust. This is the root cause of the issues on Windows. If/when +- // this is fixed in bindgen, it should be safe to remove this compiler flag. +- cc.flag("/std:c11"); +- } +- +- cc.include(blst_headers_dir.clone()); +- cc.warnings(false); +- cc.file(c_src_dir.join("c_kzg_4844.c")); +- +- cc.try_compile("ckzg").expect("Failed to compile ckzg"); ++ let rust_kzg_target_dir = root_dir.join("../../target/release/"); + + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let bindings_out_path = out_dir.join("generated.rs"); +@@ -46,14 +29,12 @@ fn main() { + ); + + // Finally, tell cargo this provides ckzg/ckzg_min +- println!("cargo:rustc-link-lib=ckzg"); ++ println!("cargo:rustc-link-search={}", rust_kzg_target_dir.display()); ++ println!("cargo:rustc-link-arg=-l:rust_kzg_constantine.a"); + } + +-fn make_bindings

( +- header_path: &str, +- blst_headers_dir: &str, +- bindings_out_path: P, +-) where ++fn make_bindings

(header_path: &str, blst_headers_dir: &str, bindings_out_path: P) ++where + P: AsRef, + { + use bindgen::Builder; +-- +2.34.1 + diff --git a/constantine/src/consts.rs b/constantine/src/consts.rs new file mode 100644 index 00000000..903165e1 --- /dev/null +++ b/constantine/src/consts.rs @@ -0,0 +1,295 @@ +//blst_fp = bls12_381_fp, CtG1 = CtG1, blst_p1 = bls12_381_g1_jac, blst_fr = bls12_381_fr +use constantine_sys::{bls12_381_fp, bls12_381_fp2, bls12_381_g1_jac, bls12_381_g2_jac}; + +use crate::types::g1::CtG1; +use crate::types::g2::CtG2; + +pub const G1_IDENTITY: CtG1 = CtG1::from_xyz( + bls12_381_fp { limbs: [0; 6] }, + bls12_381_fp { limbs: [0; 6] }, + bls12_381_fp { limbs: [0; 6] }, +); + +pub const SCALE_FACTOR: u64 = 5; + +pub const NUM_ROOTS: usize = 32; +/// The roots of unity. Every root_i equals 1 when raised to the power of (2 ^ i) +#[rustfmt::skip] +pub const SCALE2_ROOT_OF_UNITY: [[u64; 4]; 32] = [ + [0x0000000000000001, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000], + [0xffffffff00000000, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48], + [0x0001000000000000, 0xec03000276030000, 0x8d51ccce760304d0, 0x0000000000000000], + [0x7228fd3397743f7a, 0xb38b21c28713b700, 0x8c0625cd70d77ce2, 0x345766f603fa66e7], + [0x53ea61d87742bcce, 0x17beb312f20b6f76, 0xdd1c0af834cec32c, 0x20b1ce9140267af9], + [0x360c60997369df4e, 0xbf6e88fb4c38fb8a, 0xb4bcd40e22f55448, 0x50e0903a157988ba], + [0x8140d032f0a9ee53, 0x2d967f4be2f95155, 0x14a1e27164d8fdbd, 0x45af6345ec055e4d], + [0x5130c2c1660125be, 0x98d0caac87f5713c, 0xb7c68b4d7fdd60d0, 0x6898111413588742], + [0x4935bd2f817f694b, 0x0a0865a899e8deff, 0x6b368121ac0cf4ad, 0x4f9b4098e2e9f12e], + [0x4541b8ff2ee0434e, 0xd697168a3a6000fe, 0x39feec240d80689f, 0x095166525526a654], + [0x3c28d666a5c2d854, 0xea437f9626fc085e, 0x8f4de02c0f776af3, 0x325db5c3debf77a1], + [0x4a838b5d59cd79e5, 0x55ea6811be9c622d, 0x09f1ca610a08f166, 0x6d031f1b5c49c834], + [0xe206da11a5d36306, 0x0ad1347b378fbf96, 0xfc3e8acfe0f8245f, 0x564c0a11a0f704f4], + [0x6fdd00bfc78c8967, 0x146b58bc434906ac, 0x2ccddea2972e89ed, 0x485d512737b1da3d], + [0x034d2ff22a5ad9e1, 0xae4622f6a9152435, 0xdc86b01c0d477fa6, 0x56624634b500a166], + [0xfbd047e11279bb6e, 0xc8d5f51db3f32699, 0x483405417a0cbe39, 0x3291357ee558b50d], + [0xd7118f85cd96b8ad, 0x67a665ae1fcadc91, 0x88f39a78f1aeb578, 0x2155379d12180caa], + [0x08692405f3b70f10, 0xcd7f2bd6d0711b7d, 0x473a2eef772c33d6, 0x224262332d8acbf4], + [0x6f421a7d8ef674fb, 0xbb97a3bf30ce40fd, 0x652f717ae1c34bb0, 0x2d3056a530794f01], + [0x194e8c62ecb38d9d, 0xad8e16e84419c750, 0xdf625e80d0adef90, 0x520e587a724a6955], + [0xfece7e0e39898d4b, 0x2f69e02d265e09d9, 0xa57a6e07cb98de4a, 0x03e1c54bcb947035], + [0xcd3979122d3ea03a, 0x46b3105f04db5844, 0xc70d0874b0691d4e, 0x47c8b5817018af4f], + [0xc6e7a6ffb08e3363, 0xe08fec7c86389bee, 0xf2d38f10fbb8d1bb, 0x0abe6a5e5abcaa32], + [0x5616c57de0ec9eae, 0xc631ffb2585a72db, 0x5121af06a3b51e3c, 0x73560252aa0655b2], + [0x92cf4deb77bd779c, 0x72cf6a8029b7d7bc, 0x6e0bcd91ee762730, 0x291cf6d68823e687], + [0xce32ef844e11a51e, 0xc0ba12bb3da64ca5, 0x0454dc1edc61a1a3, 0x019fe632fd328739], + [0x531a11a0d2d75182, 0x02c8118402867ddc, 0x116168bffbedc11d, 0x0a0a77a3b1980c0d], + [0xe2d0a7869f0319ed, 0xb94f1101b1d7a628, 0xece8ea224f31d25d, 0x23397a9300f8f98b], + [0xd7b688830a4f2089, 0x6558e9e3f6ac7b41, 0x99e276b571905a7d, 0x52dd465e2f094256], + [0x474650359d8e211b, 0x84d37b826214abc6, 0x8da40c1ef2bb4598, 0x0c83ea7744bf1bee], + [0x694341f608c9dd56, 0xed3a181fabb30adc, 0x1339a815da8b398f, 0x2c6d4e4511657e1e], + [0x63e7cb4906ffc93f, 0xf070bb00e28a193d, 0xad1715b02e5713b5, 0x4b5371495990693f] +]; + +// pub const G1_GENERATOR_AFFINE: bls12_381_g1_aff = bls12_381_g1_aff { +// x: bls12_381_fp { +// limbs: [ +// 0x17f1d3a73197d794, +// 0x2695638c4fa9ac0f, +// 0xc3688c4f9774b905, +// 0xa14e3a3f171bac58, +// 0x6c55e83ff97a1aef, +// 0xfb3af00adb22c6bb, +// ], +// }, +// y: bls12_381_fp { +// limbs: [ +// 0xbaac93d50ce72271, +// 0x8c22631a7918fd8e, +// 0xdd595f13570725ce, +// 0x51ac582950405194, +// 0x0e1c8c3fad0059c0, +// 0x0bbc3efc5008a26a, +// ], +// }, +// }); + +pub const G1_GENERATOR: CtG1 = CtG1(bls12_381_g1_jac { + x: bls12_381_fp { + limbs: [ + 0x5cb38790fd530c16, + 0x7817fc679976fff5, + 0x154f95c7143ba1c1, + 0xf0ae6acdf3d0e747, + 0xedce6ecc21dbf440, + 0x120177419e0bfb75, + ], + }, + y: bls12_381_fp { + limbs: [ + 0xbaac93d50ce72271, + 0x8c22631a7918fd8e, + 0xdd595f13570725ce, + 0x51ac582950405194, + 0x0e1c8c3fad0059c0, + 0x0bbc3efc5008a26a, + ], + }, + z: bls12_381_fp { + limbs: [ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ], + }, +}); + +pub const G1_NEGATIVE_GENERATOR: CtG1 = CtG1(bls12_381_g1_jac { + x: bls12_381_fp { + limbs: [ + 0x5cb38790fd530c16, + 0x7817fc679976fff5, + 0x154f95c7143ba1c1, + 0xf0ae6acdf3d0e747, + 0xedce6ecc21dbf440, + 0x120177419e0bfb75, + ], + }, + y: bls12_381_fp { + limbs: [ + 0xff526c2af318883a, + 0x92899ce4383b0270, + 0x89d7738d9fa9d055, + 0x12caf35ba344c12a, + 0x3cff1b76964b5317, + 0x0e44d2ede9774430, + ], + }, + z: bls12_381_fp { + limbs: [ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ], + }, +}); + +pub const G2_GENERATOR: CtG2 = CtG2(bls12_381_g2_jac { + x: bls12_381_fp2 { + c: [ + bls12_381_fp { + limbs: [ + 0xf5f28fa202940a10, + 0xb3f5fb2687b4961a, + 0xa1a893b53e2ae580, + 0x9894999d1a3caee9, + 0x6f67b7631863366b, + 0x058191924350bcd7, + ], + }, + bls12_381_fp { + limbs: [ + 0xa5a9c0759e23f606, + 0xaaa0c59dbccd60c3, + 0x3bb17e18e2867806, + 0x1b1ab6cc8541b367, + 0xc2b6ed0ef2158547, + 0x11922a097360edf3, + ], + }, + ], + }, + y: bls12_381_fp2 { + c: [ + bls12_381_fp { + limbs: [ + 0x4c730af860494c4a, + 0x597cfa1f5e369c5a, + 0xe7e6856caa0a635a, + 0xbbefb5e96e0d495f, + 0x07d3a975f0ef25a2, + 0x0083fd8e7e80dae5, + ], + }, + bls12_381_fp { + limbs: [ + 0xadc0fc92df64b05d, + 0x18aa270a2b1461dc, + 0x86adac6a3be4eba0, + 0x79495c4ec93da33a, + 0xe7175850a43ccaed, + 0x0b2bc2a163de1bf2, + ], + }, + ], + }, + z: bls12_381_fp2 { + c: [ + bls12_381_fp { + limbs: [ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ], + }, + bls12_381_fp { + limbs: [ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ], + }, + ], + }, +}); + +pub const G2_NEGATIVE_GENERATOR: CtG2 = CtG2(bls12_381_g2_jac { + x: bls12_381_fp2 { + c: [ + bls12_381_fp { + limbs: [ + 0xf5f28fa202940a10, + 0xb3f5fb2687b4961a, + 0xa1a893b53e2ae580, + 0x9894999d1a3caee9, + 0x6f67b7631863366b, + 0x058191924350bcd7, + ], + }, + bls12_381_fp { + limbs: [ + 0xa5a9c0759e23f606, + 0xaaa0c59dbccd60c3, + 0x3bb17e18e2867806, + 0x1b1ab6cc8541b367, + 0xc2b6ed0ef2158547, + 0x11922a097360edf3, + ], + }, + ], + }, + y: bls12_381_fp2 { + c: [ + bls12_381_fp { + limbs: [ + 0x6d8bf5079fb65e61, + 0xc52f05df531d63a5, + 0x7f4a4d344ca692c9, + 0xa887959b8577c95f, + 0x4347fe40525c8734, + 0x197d145bbaff0bb5, + ], + }, + bls12_381_fp { + limbs: [ + 0x0c3e036d209afa4e, + 0x0601d8f4863f9e23, + 0xe0832636bacc0a84, + 0xeb2def362a476f84, + 0x64044f659f0ee1e9, + 0x0ed54f48d5a1caa7, + ], + }, + ], + }, + z: bls12_381_fp2 { + c: [ + bls12_381_fp { + limbs: [ + 0x760900000002fffd, + 0xebf4000bc40c0002, + 0x5f48985753c758ba, + 0x77ce585370525745, + 0x5c071a97a256ec6d, + 0x15f65ec3fa80e493, + ], + }, + bls12_381_fp { + limbs: [ + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + ], + }, + ], + }, +}); + +pub const TRUSTED_SETUP_GENERATOR: [u8; 32usize] = [ + 0xa4, 0x73, 0x31, 0x95, 0x28, 0xc8, 0xb6, 0xea, 0x4d, 0x08, 0xcc, 0x53, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; diff --git a/constantine/src/data_availability_sampling.rs b/constantine/src/data_availability_sampling.rs new file mode 100644 index 00000000..afad1efb --- /dev/null +++ b/constantine/src/data_availability_sampling.rs @@ -0,0 +1,102 @@ +//blst_fp = bls12_381_fp, CtG1 = CtG1, blst_p1 = bls12_381_g1_jacbls12_381_g1_jac +extern crate alloc; + +use alloc::string::String; +use alloc::vec::Vec; +use core::cmp::Ordering; + +use kzg::{Fr, DAS}; + +use crate::types::fft_settings::CtFFTSettings; +use crate::types::fr::CtFr; + +// TODO: explain algo +impl CtFFTSettings { + pub fn das_fft_extension_stride(&self, evens: &mut [CtFr], stride: usize) { + match evens.len().cmp(&2) { + Ordering::Less => { + return; + } + Ordering::Equal => { + let x = evens[0].add(&evens[1]); + let y = evens[0].sub(&evens[1]); + let y_times_root = y.mul(&self.expanded_roots_of_unity[stride]); + + evens[0] = x.add(&y_times_root); + evens[1] = x.sub(&y_times_root); + + return; + } + Ordering::Greater => {} + } + + let half: usize = evens.len() / 2; + for i in 0..half { + let tmp1 = evens[i].add(&evens[half + i]); + let tmp2 = evens[i].sub(&evens[half + i]); + evens[half + i] = tmp2.mul(&self.reverse_roots_of_unity[i * 2 * stride]); + + evens[i] = tmp1; + } + + #[cfg(feature = "parallel")] + { + if evens.len() > 32 { + let (lo, hi) = evens.split_at_mut(half); + rayon::join( + || self.das_fft_extension_stride(hi, stride * 2), + || self.das_fft_extension_stride(lo, stride * 2), + ); + } else { + // Recurse + self.das_fft_extension_stride(&mut evens[..half], stride * 2); + self.das_fft_extension_stride(&mut evens[half..], stride * 2); + } + } + + #[cfg(not(feature = "parallel"))] + { + // Recurse + self.das_fft_extension_stride(&mut evens[..half], stride * 2); + self.das_fft_extension_stride(&mut evens[half..], stride * 2); + } + + for i in 0..half { + let x = evens[i]; + let y = evens[half + i]; + let y_times_root: CtFr = y.mul(&self.expanded_roots_of_unity[(1 + 2 * i) * stride]); + + evens[i] = x.add(&y_times_root); + evens[half + i] = x.sub(&y_times_root); + } + } +} + +impl DAS for CtFFTSettings { + /// Polynomial extension for data availability sampling. Given values of even indices, produce values of odd indices. + /// FFTSettings must hold at least 2 times the roots of provided evens. + /// The resulting odd indices make the right half of the coefficients of the inverse FFT of the combined indices zero. + fn das_fft_extension(&self, evens: &[CtFr]) -> Result, String> { + if evens.is_empty() { + return Err(String::from("A non-zero list ab expected")); + } else if !evens.len().is_power_of_two() { + return Err(String::from("A list with power-of-two length expected")); + } else if evens.len() * 2 > self.max_width { + return Err(String::from( + "Supplied list is longer than the available max width", + )); + } + + // In case more roots are provided with fft_settings, use a larger stride + let stride = self.max_width / (evens.len() * 2); + let mut odds = evens.to_vec(); + self.das_fft_extension_stride(&mut odds, stride); + + // TODO: explain why each odd member is multiplied by euclidean inverse of length + let mut inv_len = CtFr::from_u64(odds.len() as u64); + inv_len = inv_len.eucl_inverse(); + let odds = odds.iter().map(|f| f.mul(&inv_len)).collect(); + + Ok(odds) + } +} diff --git a/constantine/src/eip_4844.rs b/constantine/src/eip_4844.rs new file mode 100644 index 00000000..2dd70889 --- /dev/null +++ b/constantine/src/eip_4844.rs @@ -0,0 +1,453 @@ +extern crate alloc; + +use alloc::boxed::Box; +use alloc::string::String; +use alloc::vec::Vec; +use core::ptr::null_mut; +use kzg::common_utils::reverse_bit_order; +use kzg::eip_4844::{ + blob_to_kzg_commitment_rust, compute_blob_kzg_proof_rust, compute_kzg_proof_rust, + load_trusted_setup_rust, verify_blob_kzg_proof_batch_rust, verify_blob_kzg_proof_rust, + verify_kzg_proof_rust, +}; +use kzg::{cfg_into_iter, Fr, G1}; +#[cfg(feature = "std")] +use libc::FILE; +#[cfg(feature = "std")] +use std::fs::File; +#[cfg(feature = "std")] +use std::io::Read; + +#[cfg(feature = "std")] +use kzg::eip_4844::load_trusted_setup_string; + +use kzg::eip_4844::{ + Blob, Bytes32, Bytes48, CKZGSettings, KZGCommitment, KZGProof, BYTES_PER_FIELD_ELEMENT, + BYTES_PER_G1, BYTES_PER_G2, C_KZG_RET, C_KZG_RET_BADARGS, C_KZG_RET_OK, + FIELD_ELEMENTS_PER_BLOB, TRUSTED_SETUP_NUM_G1_POINTS, TRUSTED_SETUP_NUM_G2_POINTS, +}; + +use crate::types::fft_settings::CtFFTSettings; +use crate::types::fr::CtFr; +use crate::types::g1::CtG1; + +use crate::types::g2::CtG2; +use crate::types::kzg_settings::CtKZGSettings; + +#[cfg(feature = "parallel")] +use rayon::prelude::*; + +#[cfg(feature = "std")] +pub fn load_trusted_setup_filename_rust(filepath: &str) -> Result { + let mut file = File::open(filepath).map_err(|_| "Unable to open file".to_string())?; + let mut contents = String::new(); + file.read_to_string(&mut contents) + .map_err(|_| "Unable to read file".to_string())?; + + let (g1_bytes, g2_bytes) = load_trusted_setup_string(&contents)?; + load_trusted_setup_rust(g1_bytes.as_slice(), g2_bytes.as_slice()) +} + +fn fft_settings_to_rust(c_settings: *const CKZGSettings) -> Result { + let settings = unsafe { &*c_settings }; + + let roots_of_unity = unsafe { + core::slice::from_raw_parts(settings.roots_of_unity, settings.max_width as usize) + .iter() + .map(|r| CtFr::from_blst_fr(*r)) + .collect::>() + }; + let mut expanded_roots_of_unity = roots_of_unity.clone(); + reverse_bit_order(&mut expanded_roots_of_unity)?; + expanded_roots_of_unity.push(CtFr::one()); + let mut reverse_roots_of_unity = expanded_roots_of_unity.clone(); + reverse_roots_of_unity.reverse(); + + let mut first_root = expanded_roots_of_unity[1]; + let first_root_arr = [first_root; 1]; + first_root = first_root_arr[0]; + + Ok(CtFFTSettings { + max_width: settings.max_width as usize, + root_of_unity: first_root, + expanded_roots_of_unity, + reverse_roots_of_unity, + roots_of_unity, + }) +} + +fn kzg_settings_to_rust(c_settings: &CKZGSettings) -> Result { + let secret_g1 = unsafe { + core::slice::from_raw_parts(c_settings.g1_values, TRUSTED_SETUP_NUM_G1_POINTS) + .iter() + .map(|r| CtG1::from_blst_p1(*r)) + .collect::>() + }; + Ok(CtKZGSettings { + fs: fft_settings_to_rust(c_settings)?, + secret_g1, + secret_g2: unsafe { + core::slice::from_raw_parts(c_settings.g2_values, TRUSTED_SETUP_NUM_G2_POINTS) + .iter() + .map(|r| CtG2::from_blst_p2(*r)) + .collect::>() + }, + precomputation: None, + }) +} + +fn kzg_settings_to_c(rust_settings: &CtKZGSettings) -> CKZGSettings { + let g1_val = rust_settings + .secret_g1 + .iter() + .map(|r| r.to_blst_p1()) + .collect::>(); + let g1_val = Box::new(g1_val); + let g2_val = rust_settings + .secret_g2 + .iter() + .map(|r| r.to_blst_p2()) + .collect::>(); + let x = g2_val.into_boxed_slice(); + let stat_ref = Box::leak(x); + let v = Box::into_raw(g1_val); + + let roots_of_unity = Box::new( + rust_settings + .fs + .roots_of_unity + .iter() + .map(|r| r.to_blst_fr()) + .collect::>(), + ); + + CKZGSettings { + max_width: rust_settings.fs.max_width as u64, + roots_of_unity: unsafe { (*Box::into_raw(roots_of_unity)).as_mut_ptr() }, + g1_values: unsafe { (*v).as_mut_ptr() }, + g2_values: stat_ref.as_mut_ptr(), + } +} + +unsafe fn deserialize_blob(blob: *const Blob) -> Result, C_KZG_RET> { + (*blob) + .bytes + .chunks(BYTES_PER_FIELD_ELEMENT) + .map(|chunk| { + let mut bytes = [0u8; BYTES_PER_FIELD_ELEMENT]; + bytes.copy_from_slice(chunk); + if let Ok(result) = CtFr::from_bytes(&bytes) { + Ok(result) + } else { + Err(C_KZG_RET_BADARGS) + } + }) + .collect::, C_KZG_RET>>() +} + +macro_rules! handle_ckzg_badargs { + ($x: expr) => { + match $x { + Ok(value) => value, + Err(_) => return C_KZG_RET_BADARGS, + } + }; +} + +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn blob_to_kzg_commitment( + out: *mut KZGCommitment, + blob: *const Blob, + s: &CKZGSettings, +) -> C_KZG_RET { + if TRUSTED_SETUP_NUM_G1_POINTS == 0 { + // FIXME: load_trusted_setup should set this value, but if not, it fails + TRUSTED_SETUP_NUM_G1_POINTS = FIELD_ELEMENTS_PER_BLOB + }; + + let deserialized_blob = handle_ckzg_badargs!(deserialize_blob(blob)); + let settings = handle_ckzg_badargs!(kzg_settings_to_rust(s)); + let tmp = handle_ckzg_badargs!(blob_to_kzg_commitment_rust(&deserialized_blob, &settings)); + + (*out).bytes = tmp.to_bytes(); + C_KZG_RET_OK +} + +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn load_trusted_setup( + out: *mut CKZGSettings, + g1_bytes: *const u8, + n1: usize, + g2_bytes: *const u8, + n2: usize, +) -> C_KZG_RET { + let g1_bytes = core::slice::from_raw_parts(g1_bytes, n1 * BYTES_PER_G1); + let g2_bytes = core::slice::from_raw_parts(g2_bytes, n2 * BYTES_PER_G2); + TRUSTED_SETUP_NUM_G1_POINTS = g1_bytes.len() / BYTES_PER_G1; + let settings = handle_ckzg_badargs!(load_trusted_setup_rust(g1_bytes, g2_bytes)); + + *out = kzg_settings_to_c(&settings); + C_KZG_RET_OK +} + +/// # Safety +#[cfg(feature = "std")] +#[no_mangle] +pub unsafe extern "C" fn load_trusted_setup_file( + out: *mut CKZGSettings, + in_: *mut FILE, +) -> C_KZG_RET { + let mut buf = vec![0u8; 1024 * 1024]; + let len: usize = libc::fread(buf.as_mut_ptr() as *mut libc::c_void, 1, buf.len(), in_); + let s = handle_ckzg_badargs!(String::from_utf8(buf[..len].to_vec())); + let (g1_bytes, g2_bytes) = handle_ckzg_badargs!(load_trusted_setup_string(&s)); + TRUSTED_SETUP_NUM_G1_POINTS = g1_bytes.len() / BYTES_PER_G1; + if TRUSTED_SETUP_NUM_G1_POINTS != FIELD_ELEMENTS_PER_BLOB { + // Helps pass the Java test "shouldThrowExceptionOnIncorrectTrustedSetupFromFile", + // as well as 5 others that pass only if this one passes (likely because Java doesn't + // deallocate its KZGSettings pointer when no exception is thrown). + return C_KZG_RET_BADARGS; + } + let settings = handle_ckzg_badargs!(load_trusted_setup_rust( + g1_bytes.as_slice(), + g2_bytes.as_slice() + )); + + *out = kzg_settings_to_c(&settings); + C_KZG_RET_OK +} + +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn compute_blob_kzg_proof( + out: *mut KZGProof, + blob: *const Blob, + commitment_bytes: *const Bytes48, + s: &CKZGSettings, +) -> C_KZG_RET { + let deserialized_blob = match deserialize_blob(blob) { + Ok(value) => value, + Err(err) => return err, + }; + + let commitment_g1 = handle_ckzg_badargs!(CtG1::from_bytes(&(*commitment_bytes).bytes)); + let settings = handle_ckzg_badargs!(kzg_settings_to_rust(s)); + let proof = handle_ckzg_badargs!(compute_blob_kzg_proof_rust( + &deserialized_blob, + &commitment_g1, + &settings + )); + + (*out).bytes = proof.to_bytes(); + C_KZG_RET_OK +} + +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn free_trusted_setup(s: *mut CKZGSettings) { + if s.is_null() { + return; + } + + let max_width = (*s).max_width as usize; + let roots = Box::from_raw(core::slice::from_raw_parts_mut( + (*s).roots_of_unity, + max_width, + )); + drop(roots); + (*s).roots_of_unity = null_mut(); + + let g1 = Box::from_raw(core::slice::from_raw_parts_mut( + (*s).g1_values, + TRUSTED_SETUP_NUM_G1_POINTS, + )); + drop(g1); + (*s).g1_values = null_mut(); + + let g2 = Box::from_raw(core::slice::from_raw_parts_mut( + (*s).g2_values, + TRUSTED_SETUP_NUM_G2_POINTS, + )); + drop(g2); + (*s).g2_values = null_mut(); + (*s).max_width = 0; +} + +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn verify_kzg_proof( + ok: *mut bool, + commitment_bytes: *const Bytes48, + z_bytes: *const Bytes32, + y_bytes: *const Bytes32, + proof_bytes: *const Bytes48, + s: &CKZGSettings, +) -> C_KZG_RET { + let frz = handle_ckzg_badargs!(CtFr::from_bytes(&(*z_bytes).bytes)); + let fry = handle_ckzg_badargs!(CtFr::from_bytes(&(*y_bytes).bytes)); + let g1commitment = handle_ckzg_badargs!(CtG1::from_bytes(&(*commitment_bytes).bytes)); + let g1proof = handle_ckzg_badargs!(CtG1::from_bytes(&(*proof_bytes).bytes)); + + let settings = handle_ckzg_badargs!(kzg_settings_to_rust(s)); + + let result = handle_ckzg_badargs!(verify_kzg_proof_rust( + &g1commitment, + &frz, + &fry, + &g1proof, + &settings + )); + + *ok = result; + C_KZG_RET_OK +} + +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn verify_blob_kzg_proof( + ok: *mut bool, + blob: *const Blob, + commitment_bytes: *const Bytes48, + proof_bytes: *const Bytes48, + s: &CKZGSettings, +) -> C_KZG_RET { + let deserialized_blob = handle_ckzg_badargs!(deserialize_blob(blob)); + let commitment_g1 = handle_ckzg_badargs!(CtG1::from_bytes(&(*commitment_bytes).bytes)); + let proof_g1 = handle_ckzg_badargs!(CtG1::from_bytes(&(*proof_bytes).bytes)); + let settings = handle_ckzg_badargs!(kzg_settings_to_rust(s)); + + let result = handle_ckzg_badargs!(verify_blob_kzg_proof_rust( + &deserialized_blob, + &commitment_g1, + &proof_g1, + &settings, + )); + + *ok = result; + C_KZG_RET_OK +} + +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn verify_blob_kzg_proof_batch( + ok: *mut bool, + blobs: *const Blob, + commitments_bytes: *const Bytes48, + proofs_bytes: *const Bytes48, + n: usize, + s: &CKZGSettings, +) -> C_KZG_RET { + let raw_blobs = core::slice::from_raw_parts(blobs, n); + let raw_commitments = core::slice::from_raw_parts(commitments_bytes, n); + let raw_proofs = core::slice::from_raw_parts(proofs_bytes, n); + + let deserialized_blobs: Result>, C_KZG_RET> = cfg_into_iter!(raw_blobs) + .map(|raw_blob| deserialize_blob(raw_blob).map_err(|_| C_KZG_RET_BADARGS)) + .collect(); + + let commitments_g1: Result, C_KZG_RET> = cfg_into_iter!(raw_commitments) + .map(|raw_commitment| { + CtG1::from_bytes(&raw_commitment.bytes).map_err(|_| C_KZG_RET_BADARGS) + }) + .collect(); + + let proofs_g1: Result, C_KZG_RET> = cfg_into_iter!(raw_proofs) + .map(|raw_proof| CtG1::from_bytes(&raw_proof.bytes).map_err(|_| C_KZG_RET_BADARGS)) + .collect(); + + if let (Ok(blobs), Ok(commitments), Ok(proofs)) = + (deserialized_blobs, commitments_g1, proofs_g1) + { + let settings = match kzg_settings_to_rust(s) { + Ok(value) => value, + Err(_) => return C_KZG_RET_BADARGS, + }; + + let result = + verify_blob_kzg_proof_batch_rust(blobs.as_slice(), &commitments, &proofs, &settings); + + if let Ok(result) = result { + *ok = result; + C_KZG_RET_OK + } else { + C_KZG_RET_BADARGS + } + } else { + *ok = false; + C_KZG_RET_BADARGS + } +} + +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn compute_kzg_proof( + proof_out: *mut KZGProof, + y_out: *mut Bytes32, + blob: *const Blob, + z_bytes: *const Bytes32, + s: &CKZGSettings, +) -> C_KZG_RET { + let deserialized_blob = match deserialize_blob(blob) { + Ok(value) => value, + Err(err) => return err, + }; + + let frz = match CtFr::from_bytes(&(*z_bytes).bytes) { + Ok(value) => value, + Err(_) => return C_KZG_RET_BADARGS, + }; + + let settings = match kzg_settings_to_rust(s) { + Ok(value) => value, + Err(_) => return C_KZG_RET_BADARGS, + }; + + let (proof_out_tmp, fry_tmp) = match compute_kzg_proof_rust(&deserialized_blob, &frz, &settings) + { + Ok(value) => value, + Err(_) => return C_KZG_RET_BADARGS, + }; + + (*proof_out).bytes = proof_out_tmp.to_bytes(); + (*y_out).bytes = fry_tmp.to_bytes(); + C_KZG_RET_OK +} + +#[cfg(test)] +mod tests { + use kzg_bench::tests::utils::get_trusted_setup_path; + + use crate::eip_4844::{kzg_settings_to_c, kzg_settings_to_rust}; + + use super::load_trusted_setup_filename_rust; + + #[test] + fn kzg_settings_to_rust_check_conversion() { + let settings = load_trusted_setup_filename_rust(get_trusted_setup_path().as_str()); + + assert!(settings.is_ok()); + + let settings = settings.unwrap(); + + let converted_settings = kzg_settings_to_rust(&kzg_settings_to_c(&settings)).unwrap(); + + assert_eq!( + settings.fs.root_of_unity, + converted_settings.fs.root_of_unity + ); + assert_eq!( + settings.fs.roots_of_unity, + converted_settings.fs.roots_of_unity + ); + assert_eq!( + settings.fs.expanded_roots_of_unity, + converted_settings.fs.expanded_roots_of_unity + ); + assert_eq!( + settings.fs.reverse_roots_of_unity, + converted_settings.fs.reverse_roots_of_unity + ); + } +} diff --git a/constantine/src/fft_fr.rs b/constantine/src/fft_fr.rs new file mode 100644 index 00000000..0341eef5 --- /dev/null +++ b/constantine/src/fft_fr.rs @@ -0,0 +1,145 @@ +extern crate alloc; + +use alloc::format; +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; + +use kzg::{FFTFr, Fr}; + +use crate::types::fft_settings::CtFFTSettings; +use crate::types::fr::CtFr; + +/// Fast Fourier Transform for finite field elements. Polynomial ret is operated on in reverse order: ret_i * x ^ (len - i - 1) +pub fn fft_fr_fast( + ret: &mut [CtFr], + data: &[CtFr], + stride: usize, + roots: &[CtFr], + roots_stride: usize, +) { + let half: usize = ret.len() / 2; + if half > 0 { + // Recurse + // Offsetting data by stride = 1 on the first iteration forces the even members to the first half + // and the odd members to the second half + #[cfg(not(feature = "parallel"))] + { + fft_fr_fast(&mut ret[..half], data, stride * 2, roots, roots_stride * 2); + fft_fr_fast( + &mut ret[half..], + &data[stride..], + stride * 2, + roots, + roots_stride * 2, + ); + } + + #[cfg(feature = "parallel")] + { + if half > 256 { + let (lo, hi) = ret.split_at_mut(half); + rayon::join( + || fft_fr_fast(lo, data, stride * 2, roots, roots_stride * 2), + || fft_fr_fast(hi, &data[stride..], stride * 2, roots, roots_stride * 2), + ); + } else { + fft_fr_fast(&mut ret[..half], data, stride * 2, roots, roots_stride * 2); + fft_fr_fast( + &mut ret[half..], + &data[stride..], + stride * 2, + roots, + roots_stride * 2, + ); + } + } + + for i in 0..half { + let y_times_root = ret[i + half].mul(&roots[i * roots_stride]); + ret[i + half] = ret[i].sub(&y_times_root); + ret[i] = ret[i].add(&y_times_root); + } + } else { + // When len = 1, return the permuted element + ret[0] = data[0]; + } +} + +impl CtFFTSettings { + /// Fast Fourier Transform for finite field elements, `output` must be zeroes + pub(crate) fn fft_fr_output( + &self, + data: &[CtFr], + inverse: bool, + output: &mut [CtFr], + ) -> Result<(), String> { + if data.len() > self.max_width { + return Err(String::from( + "Supplied list is longer than the available max width", + )); + } + if data.len() != output.len() { + return Err(format!( + "Output length {} doesn't match data length {}", + data.len(), + output.len() + )); + } + if !data.len().is_power_of_two() { + return Err(String::from("A list with power-of-two length expected")); + } + + // In case more roots are provided with fft_settings, use a larger stride + let stride = self.max_width / data.len(); + + // Inverse is same as regular, but all constants are reversed and results are divided by n + // This is a property of the DFT matrix + let roots = if inverse { + &self.reverse_roots_of_unity + } else { + &self.expanded_roots_of_unity + }; + + fft_fr_fast(output, data, 1, roots, stride); + + if inverse { + let inv_fr_len = CtFr::from_u64(data.len() as u64).inverse(); + output.iter_mut().for_each(|f| *f = f.mul(&inv_fr_len)); + } + + Ok(()) + } +} + +impl FFTFr for CtFFTSettings { + /// Fast Fourier Transform for finite field elements + fn fft_fr(&self, data: &[CtFr], inverse: bool) -> Result, String> { + let mut ret = vec![CtFr::default(); data.len()]; + + self.fft_fr_output(data, inverse, &mut ret)?; + + Ok(ret) + } +} + +/// Simplified Discrete Fourier Transform, mainly used for testing +pub fn fft_fr_slow( + ret: &mut [CtFr], + data: &[CtFr], + stride: usize, + roots: &[CtFr], + roots_stride: usize, +) { + for i in 0..data.len() { + // Evaluate first member at 1 + ret[i] = data[0].mul(&roots[0]); + + // Evaluate the rest of members using a step of (i * J) % data.len() over the roots + // This distributes the roots over correct x^n members and saves on multiplication + for j in 1..data.len() { + let v = data[j * stride].mul(&roots[((i * j) % data.len()) * roots_stride]); + ret[i] = ret[i].add(&v); + } + } +} diff --git a/constantine/src/fft_g1.rs b/constantine/src/fft_g1.rs new file mode 100644 index 00000000..a5c18010 --- /dev/null +++ b/constantine/src/fft_g1.rs @@ -0,0 +1,104 @@ +extern crate alloc; + +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; + +use kzg::{Fr, G1Mul, FFTG1, G1}; + +use crate::types::fft_settings::CtFFTSettings; +use crate::types::fr::CtFr; +use crate::types::g1::CtG1; + +pub fn fft_g1_fast( + ret: &mut [CtG1], + data: &[CtG1], + stride: usize, + roots: &[CtFr], + roots_stride: usize, +) { + let half = ret.len() / 2; + if half > 0 { + #[cfg(feature = "parallel")] + { + let (lo, hi) = ret.split_at_mut(half); + rayon::join( + || fft_g1_fast(lo, data, stride * 2, roots, roots_stride * 2), + || fft_g1_fast(hi, &data[stride..], stride * 2, roots, roots_stride * 2), + ); + } + + #[cfg(not(feature = "parallel"))] + { + fft_g1_fast(&mut ret[..half], data, stride * 2, roots, roots_stride * 2); + fft_g1_fast( + &mut ret[half..], + &data[stride..], + stride * 2, + roots, + roots_stride * 2, + ); + } + + for i in 0..half { + let y_times_root = ret[i + half].mul(&roots[i * roots_stride]); + ret[i + half] = ret[i].sub(&y_times_root); + ret[i] = ret[i].add_or_dbl(&y_times_root); + } + } else { + ret[0] = data[0]; + } +} + +impl FFTG1 for CtFFTSettings { + fn fft_g1(&self, data: &[CtG1], inverse: bool) -> Result, String> { + if data.len() > self.max_width { + return Err(String::from( + "Supplied list is longer than the available max width", + )); + } else if !data.len().is_power_of_two() { + return Err(String::from("A list with power-of-two length expected")); + } + + let stride = self.max_width / data.len(); + let mut ret = vec![CtG1::default(); data.len()]; + + let roots = if inverse { + &self.reverse_roots_of_unity + } else { + &self.expanded_roots_of_unity + }; + + fft_g1_fast(&mut ret, data, 1, roots, stride); + + if inverse { + let inv_fr_len = CtFr::from_u64(data.len() as u64).inverse(); + ret[..data.len()] + .iter_mut() + .for_each(|f| *f = f.mul(&inv_fr_len)); + } + + Ok(ret) + } +} + +// Used for testing +pub fn fft_g1_slow( + ret: &mut [CtG1], + data: &[CtG1], + stride: usize, + roots: &[CtFr], + roots_stride: usize, +) { + for i in 0..data.len() { + // Evaluate first member at 1 + ret[i] = data[0].mul(&roots[0]); + + // Evaluate the rest of members using a step of (i * J) % data.len() over the roots + // This distributes the roots over correct x^n members and saves on multiplication + for j in 1..data.len() { + let v = data[j * stride].mul(&roots[((i * j) % data.len()) * roots_stride]); + ret[i] = ret[i].add_or_dbl(&v); + } + } +} diff --git a/constantine/src/fk20_proofs.rs b/constantine/src/fk20_proofs.rs new file mode 100644 index 00000000..21301f9f --- /dev/null +++ b/constantine/src/fk20_proofs.rs @@ -0,0 +1,93 @@ +extern crate alloc; + +use alloc::vec; +use alloc::vec::Vec; + +use kzg::{FFTFr, Fr, G1Mul, Poly, FFTG1, G1}; + +use crate::types::fft_settings::CtFFTSettings; +use crate::types::fr::CtFr; +use crate::types::g1::CtG1; +use crate::types::poly::CtPoly; + +#[cfg(feature = "parallel")] +use rayon::prelude::*; + +impl CtFFTSettings { + pub fn toeplitz_part_1(&self, x: &[CtG1]) -> Vec { + let n = x.len(); + let n2 = n * 2; + let mut x_ext = Vec::with_capacity(n2); + + x_ext.extend(x.iter().take(n)); + x_ext.resize(n2, CtG1::identity()); + + self.fft_g1(&x_ext, false).unwrap() + } + + /// poly and x_ext_fft should be of same length + pub fn toeplitz_part_2(&self, poly: &CtPoly, x_ext_fft: &[CtG1]) -> Vec { + let coeffs_fft = self.fft_fr(&poly.coeffs, false).unwrap(); + + #[cfg(feature = "parallel")] + { + coeffs_fft + .into_par_iter() + .zip(x_ext_fft) + .take(poly.len()) + .map(|(coeff_fft, x_ext_fft)| x_ext_fft.mul(&coeff_fft)) + .collect() + } + + #[cfg(not(feature = "parallel"))] + { + coeffs_fft + .into_iter() + .zip(x_ext_fft) + .take(poly.len()) + .map(|(coeff_fft, x_ext_fft)| x_ext_fft.mul(&coeff_fft)) + .collect() + } + } + + pub fn toeplitz_part_3(&self, h_ext_fft: &[CtG1]) -> Vec { + let n2 = h_ext_fft.len(); + let n = n2 / 2; + + let mut ret = self.fft_g1(h_ext_fft, true).unwrap(); + ret[n..n2].copy_from_slice(&vec![CtG1::identity(); n2 - n]); + + ret + } +} + +impl CtPoly { + pub fn toeplitz_coeffs_stride(&self, offset: usize, stride: usize) -> CtPoly { + let n = self.len(); + let k = n / stride; + let k2 = k * 2; + + let mut ret = CtPoly::default(); + ret.coeffs.push(self.coeffs[n - 1 - offset]); + + let num_of_zeroes = if k + 2 < k2 { k + 2 - 1 } else { k2 - 1 }; + for _ in 0..num_of_zeroes { + ret.coeffs.push(CtFr::zero()); + } + + let mut i = k + 2; + let mut j = 2 * stride - offset - 1; + while i < k2 { + ret.coeffs.push(self.coeffs[j]); + + i += 1; + j += stride; + } + + ret + } + + pub fn toeplitz_coeffs_step(&self) -> CtPoly { + self.toeplitz_coeffs_stride(0, 1) + } +} diff --git a/constantine/src/kzg_proofs.rs b/constantine/src/kzg_proofs.rs new file mode 100644 index 00000000..dabfe0aa --- /dev/null +++ b/constantine/src/kzg_proofs.rs @@ -0,0 +1,113 @@ +extern crate alloc; + +use crate::types::fp::CtFp; +use crate::types::g1::CtG1; +use crate::types::{fr::CtFr, g1::CtG1Affine}; + +use crate::utils::{ptr_transmute, ptr_transmute_mut}; + +#[cfg(feature = "constantine_msm")] +use constantine_sys as constantine; +#[cfg(feature = "constantine_msm")] +use kzg::G1Affine; + +#[cfg(not(feature = "constantine_msm"))] +use kzg::msm::msm_impls::msm; + +#[cfg(not(feature = "constantine_msm"))] +use crate::types::g1::CtG1ProjAddAffine; + +use kzg::msm::precompute::PrecomputationTable; + +use crate::types::g2::CtG2; +use blst::{blst_p1_affine, blst_p1_cneg, blst_p1_to_affine, blst_p2_affine, blst_p2_to_affine}; + +use kzg::PairingVerify; + +impl PairingVerify for CtG1 { + fn verify(a1: &CtG1, a2: &CtG2, b1: &CtG1, b2: &CtG2) -> bool { + pairings_verify(a1, a2, b1, b2) + } +} + +pub fn g1_linear_combination( + out: &mut CtG1, + points: &[CtG1], + scalars: &[CtFr], + len: usize, + _precomputation: Option<&PrecomputationTable>, +) { + #[cfg(feature = "constantine_msm")] + { + if len == 0 { + *out = CtG1::default(); + return; + } + + #[cfg(feature = "parallel")] + let pool = unsafe { + constantine::ctt_threadpool_new(constantine_sys::ctt_cpu_get_num_threads_os()) + }; + + #[cfg(not(feature = "parallel"))] + let pool = unsafe { constantine::ctt_threadpool_new(1) }; + + unsafe { + let points_affine_vec = CtG1Affine::into_affines(points); + let points_transmuted: &[constantine::bls12_381_g1_aff] = + core::mem::transmute(points_affine_vec.as_slice()); + + let frs_transmuted: &[constantine::bls12_381_fr] = core::mem::transmute(scalars); + constantine::ctt_bls12_381_g1_jac_multi_scalar_mul_fr_coefs_vartime_parallel( + pool, + &mut out.0, + frs_transmuted.as_ptr(), + points_transmuted.as_ptr(), + len, + ); + } + + unsafe { + constantine::ctt_threadpool_shutdown(pool); + } + } + + #[cfg(not(feature = "constantine_msm"))] + { + *out = msm::( + points, + scalars, + len, + _precomputation, + ); + } +} + +pub fn pairings_verify(a1: &CtG1, a2: &CtG2, b1: &CtG1, b2: &CtG2) -> bool { + // FIXME: Remove usage of BLST version, though not sure if there's a constantine version of multi miller loop + let mut aa1 = blst_p1_affine::default(); + let mut bb1 = blst_p1_affine::default(); + + let mut aa2 = blst_p2_affine::default(); + let mut bb2 = blst_p2_affine::default(); + + // As an optimisation, we want to invert one of the pairings, + // so we negate one of the points. + let mut a1neg: CtG1 = *a1; + unsafe { + blst_p1_cneg(ptr_transmute_mut(&mut a1neg.0), true); + blst_p1_to_affine(&mut aa1, ptr_transmute(&a1neg.0)); + + blst_p1_to_affine(&mut bb1, ptr_transmute(&b1.0)); + blst_p2_to_affine(&mut aa2, ptr_transmute(&a2.0)); + blst_p2_to_affine(&mut bb2, ptr_transmute(&b2.0)); + + let dst = [0u8; 3]; + let mut pairing_blst = blst::Pairing::new(false, &dst); + pairing_blst.raw_aggregate(&aa2, &aa1); + pairing_blst.raw_aggregate(&bb2, &bb1); + let gt_point = pairing_blst.as_fp12().final_exp(); + + blst::blst_fp12_is_one(>_point) + } +} diff --git a/constantine/src/lib.rs b/constantine/src/lib.rs new file mode 100644 index 00000000..265cc11d --- /dev/null +++ b/constantine/src/lib.rs @@ -0,0 +1,14 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod consts; +pub mod data_availability_sampling; +pub mod eip_4844; +pub mod fft_fr; +pub mod fft_g1; +pub mod fk20_proofs; +pub mod kzg_proofs; +pub mod mixed_kzg; +pub mod recovery; +pub mod types; +pub mod utils; +pub mod zero_poly; diff --git a/constantine/src/mixed_kzg/mixed_eip_4844.rs b/constantine/src/mixed_kzg/mixed_eip_4844.rs new file mode 100644 index 00000000..db331f16 --- /dev/null +++ b/constantine/src/mixed_kzg/mixed_eip_4844.rs @@ -0,0 +1,251 @@ +use std::path::Path; + +// use crate:: +use crate::mixed_kzg::mixed_kzg_settings::MixedKzgSettings; + +use crate::types::{fr::CtFr, g1::CtG1}; + +use kzg::eip_4844::{ + blob_to_kzg_commitment_rust, compute_blob_kzg_proof_rust, compute_kzg_proof_rust, + verify_blob_kzg_proof_batch_rust, verify_blob_kzg_proof_rust, verify_kzg_proof_rust, + BYTES_PER_BLOB, FIELD_ELEMENTS_PER_BLOB, +}; +use kzg::{Fr, G1}; + +use super::mixed_kzg_settings::LocalToStr; + +fn blob_fr_to_byte_inplace(blob: &[CtFr], inplace: &mut [u8; BYTES_PER_BLOB]) -> Option { + if blob.len() != FIELD_ELEMENTS_PER_BLOB { + return Some("blob length is not equal to FIELD_ELEMENTS_PER_BLOB".to_string()); + } + + for i in 0..FIELD_ELEMENTS_PER_BLOB { + inplace[i * 32..(i + 1) * 32].copy_from_slice(&blob[i].to_bytes()); + } + + None +} + +fn blob_fr_to_byte(blob: &[CtFr]) -> Result<[u8; BYTES_PER_BLOB], String> { + if blob.len() != FIELD_ELEMENTS_PER_BLOB { + return Err("blob length is not equal to FIELD_ELEMENTS_PER_BLOB".to_string()); + } + + let mut blob_bytes = [0u8; BYTES_PER_BLOB]; + for i in 0..FIELD_ELEMENTS_PER_BLOB { + blob_bytes[i * 32..(i + 1) * 32].copy_from_slice(&blob[i].to_bytes()); + } + + Ok(blob_bytes) + // unsafe { Ok(std::mem::transmute(blob.as_ptr() as *const [u8; BYTES_PER_BLOB])) } +} + +pub fn load_trusted_setup_filename_mixed(filepath: &str) -> Result { + MixedKzgSettings::new_from_path(Path::new(filepath)) +} + +pub fn blob_to_kzg_commitment_mixed( + blob: &[CtFr], + settings: &MixedKzgSettings, +) -> Result { + match settings { + MixedKzgSettings::Constantine(ctt_context) => { + let blob_bytes = blob_fr_to_byte(blob)?; + + #[cfg(feature = "parallel")] + let res = ctt_context + .ctx + .blob_to_kzg_commitment_parallel(&ctt_context.pool, &blob_bytes); + + #[cfg(not(feature = "parallel"))] + let res = ctt_context.ctx.blob_to_kzg_commitment(&blob_bytes); + + match res { + Ok(commitment) => CtG1::from_bytes(&commitment), + Err(x) => Err(x.to_string()), + } + // return blob_to_kzg_commitment_rust(blob, ctt_context); + } + MixedKzgSettings::Generic(generic_context) => { + blob_to_kzg_commitment_rust(blob, generic_context) + } + } +} + +pub fn compute_kzg_proof_mixed( + blob: &[CtFr], + z: &CtFr, + s: &MixedKzgSettings, +) -> Result<(CtG1, CtFr), String> { + match s { + MixedKzgSettings::Constantine(ctt_context) => { + let blob_bytes = blob_fr_to_byte(blob)?; + + #[cfg(feature = "parallel")] + let res = ctt_context.ctx.compute_kzg_proof_parallel( + &ctt_context.pool, + &blob_bytes, + &z.to_bytes(), + ); + + #[cfg(not(feature = "parallel"))] + let res = ctt_context + .ctx + .compute_kzg_proof(&blob_bytes, &z.to_bytes()); + + match res { + Ok((proof, y)) => Ok((CtG1::from_bytes(&proof)?, CtFr::from_bytes(&y)?)), + Err(x) => Err(x.to_string()), + } + } + MixedKzgSettings::Generic(generic_context) => { + compute_kzg_proof_rust(blob, z, generic_context) + } + } +} + +pub fn compute_blob_kzg_proof_mixed( + blob: &[CtFr], + commitment: &CtG1, + ts: &MixedKzgSettings, +) -> Result { + match ts { + MixedKzgSettings::Constantine(ctt_context) => { + let blob_bytes = blob_fr_to_byte(blob)?; + + #[cfg(feature = "parallel")] + let res = ctt_context.ctx.compute_blob_kzg_proof_parallel( + &ctt_context.pool, + &blob_bytes, + &commitment.to_bytes(), + ); + + #[cfg(not(feature = "parallel"))] + let res = ctt_context + .ctx + .compute_blob_kzg_proof(&blob_bytes, &commitment.to_bytes()); + + match res { + Ok(proof) => CtG1::from_bytes(&proof), + Err(x) => Err(x.to_string()), + } + } + MixedKzgSettings::Generic(generic_context) => { + compute_blob_kzg_proof_rust(blob, commitment, generic_context) + } + } +} + +pub fn verify_kzg_proof_mixed( + commitment: &CtG1, + z: &CtFr, + y: &CtFr, + proof: &CtG1, + s: &MixedKzgSettings, +) -> Result { + match s { + MixedKzgSettings::Constantine(ctt_context) => { + let res = ctt_context.ctx.verify_kzg_proof( + &commitment.to_bytes(), + &z.to_bytes(), + &y.to_bytes(), + &proof.to_bytes(), + ); + match res { + Ok(x) => Ok(x), + Err(x) => Err(x.to_string()), + } + } + MixedKzgSettings::Generic(generic_context) => { + verify_kzg_proof_rust(commitment, z, y, proof, generic_context) + } + } +} + +pub fn verify_blob_kzg_proof_mixed( + blob: &[CtFr], + commitment_g1: &CtG1, + proof_g1: &CtG1, + ts: &MixedKzgSettings, +) -> Result { + match ts { + MixedKzgSettings::Constantine(ctt_context) => { + let blob_bytes = blob_fr_to_byte(blob)?; + + #[cfg(feature = "parallel")] + let res = ctt_context.ctx.verify_blob_kzg_proof_parallel( + &ctt_context.pool, + &blob_bytes, + &commitment_g1.to_bytes(), + &proof_g1.to_bytes(), + ); + + #[cfg(not(feature = "parallel"))] + let res = ctt_context.ctx.verify_blob_kzg_proof( + &blob_bytes, + &commitment_g1.to_bytes(), + &proof_g1.to_bytes(), + ); + + match res { + Ok(x) => Ok(x), + Err(x) => Err(x.to_string()), + } + } + MixedKzgSettings::Generic(generic_context) => { + verify_blob_kzg_proof_rust(blob, commitment_g1, proof_g1, generic_context) + } + } +} + +pub fn verify_blob_kzg_proof_batch_mixed( + blobs: &[Vec], + commitments_g1: &[CtG1], + proofs_g1: &[CtG1], + ts: &MixedKzgSettings, +) -> Result { + match ts { + MixedKzgSettings::Constantine(ctt_context) => { + let mut blobs_storage = vec![[0u8; BYTES_PER_BLOB]; blobs.len()]; + for (i, blob) in blobs.iter().enumerate() { + let res = blob_fr_to_byte_inplace(blob, &mut blobs_storage[i]); + if let Some(res) = res { + return Err(res); + } + } + + let commitments = commitments_g1 + .iter() + .map(|x| x.to_bytes()) + .collect::>(); + let proofs_g1 = proofs_g1.iter().map(|x| x.to_bytes()).collect::>(); + + let rand_thing = [0u8; 32]; + + #[cfg(feature = "parallel")] + let res = ctt_context.ctx.verify_blob_kzg_proof_batch_parallel( + &ctt_context.pool, + blobs_storage.as_slice(), + commitments.as_slice(), + proofs_g1.as_slice(), + &rand_thing, + ); + + #[cfg(not(feature = "parallel"))] + let res = ctt_context.ctx.verify_blob_kzg_proof_batch( + blobs_storage.as_slice(), + commitments.as_slice(), + proofs_g1.as_slice(), + &rand_thing, + ); + + match res { + Ok(x) => Ok(x), + Err(x) => Err(x.to_string()), + } + } + MixedKzgSettings::Generic(generic_context) => { + verify_blob_kzg_proof_batch_rust(blobs, commitments_g1, proofs_g1, generic_context) + } + } +} diff --git a/constantine/src/mixed_kzg/mixed_kzg_settings.rs b/constantine/src/mixed_kzg/mixed_kzg_settings.rs new file mode 100644 index 00000000..3bff3a1c --- /dev/null +++ b/constantine/src/mixed_kzg/mixed_kzg_settings.rs @@ -0,0 +1,234 @@ +use std::path::Path; + +use crate::types::{ + fft_settings::CtFFTSettings, + fp::CtFp, + fr::CtFr, + g1::{CtG1, CtG1Affine}, + g2::CtG2, + kzg_settings::CtKZGSettings as GenericContext, + poly::CtPoly, +}; +use constantine_core::Threadpool as CttThreadpool; +use constantine_ethereum_kzg::EthKzgContext as CttEthKzgContext; +use constantine_sys::{ctt_eth_kzg_status, ctt_eth_trusted_setup_status}; +use kzg::KZGSettings; + +use super::mixed_eip_4844::verify_kzg_proof_mixed; + +pub struct CttContext { + pub ctx: CttEthKzgContext, + pub pool: CttThreadpool, +} + +impl CttContext { + pub fn new(path: &Path) -> Result { + let res = CttEthKzgContext::load_trusted_setup(path); + match res { + Ok(constantine_context) => Ok(Self { + ctx: constantine_context, + pool: CttThreadpool::new(get_thr_count()), + }), + Err(x) => Err(x.to_string()), + } + } +} + +fn get_thr_count() -> usize { + #[cfg(feature = "parallel")] + return constantine_core::hardware::get_num_threads_os(); + + #[cfg(not(feature = "parallel"))] + return 1; +} + +// Constantine requires loading from path + doesn't expose underlying secrets, but sometimes required for tests +#[allow(clippy::large_enum_variant)] +pub enum MixedKzgSettings { + Constantine(CttContext), + Generic(GenericContext), +} + +pub trait LocalToStr { + fn to_string(&self) -> String; +} + +impl LocalToStr for ctt_eth_trusted_setup_status { + fn to_string(&self) -> String { + match self { + ctt_eth_trusted_setup_status::cttEthTS_InvalidFile => "invalid file".to_owned(), + ctt_eth_trusted_setup_status::cttEthTS_MissingOrInaccessibleFile => { + "missing or inaccessible file".to_owned() + } + ctt_eth_trusted_setup_status::cttEthTS_Success => "success".to_owned(), + } + } +} + +impl LocalToStr for ctt_eth_kzg_status { + fn to_string(&self) -> String { + format!("{:?}", self) + } +} + +impl MixedKzgSettings { + pub fn new( + secret_g1: &[CtG1], + secret_g2: &[CtG2], + length: usize, + fft_settings: &CtFFTSettings, + ) -> Result { + let res = GenericContext::new(secret_g1, secret_g2, length, fft_settings); + match res { + Ok(generic_context) => Ok(Self::Generic(generic_context)), + Err(x) => Err(x), + } + } + + pub fn new_from_path(path: &Path) -> Result { + let res = CttEthKzgContext::load_trusted_setup(path); + match res { + Ok(constantine_context) => Ok(Self::Constantine(CttContext { + ctx: constantine_context, + pool: CttThreadpool::new(get_thr_count()), + })), + Err(x) => Err(x.to_string()), + } + } +} + +impl Default for MixedKzgSettings { + fn default() -> Self { + Self::Generic(GenericContext::default()) + } +} + +impl Clone for MixedKzgSettings { + fn clone(&self) -> Self { + match self { + Self::Constantine(_) => panic!("Cannot clone constantine context"), + Self::Generic(arg0) => Self::Generic(arg0.clone()), + } + } +} + +// Allow using MixedKzgSettings as KZGSettings stand-in +impl KZGSettings for MixedKzgSettings { + fn new( + secret_g1: &[CtG1], + secret_g2: &[CtG2], + length: usize, + fs: &CtFFTSettings, + ) -> Result { + MixedKzgSettings::new(secret_g1, secret_g2, length, fs) + } + + fn commit_to_poly(&self, p: &CtPoly) -> Result { + match self { + MixedKzgSettings::Constantine(_) => Err("Context not in generic format".to_string()), + MixedKzgSettings::Generic(generic_context) => generic_context.commit_to_poly(p), + } + } + + fn compute_proof_single(&self, p: &CtPoly, x: &CtFr) -> Result { + match self { + MixedKzgSettings::Constantine(_) => Err("Context not in generic format".to_string()), + MixedKzgSettings::Generic(generic_context) => { + generic_context.compute_proof_single(p, x) + } + } + } + + fn check_proof_single( + &self, + com: &CtG1, + proof: &CtG1, + x: &CtFr, + value: &CtFr, + ) -> Result { + verify_kzg_proof_mixed(com, x, value, proof, self) + } + + fn compute_proof_multi(&self, p: &CtPoly, x: &CtFr, n: usize) -> Result { + match self { + MixedKzgSettings::Constantine(_) => Err("Context not in generic format".to_string()), + MixedKzgSettings::Generic(generic_context) => { + generic_context.compute_proof_multi(p, x, n) + } + } + } + + fn check_proof_multi( + &self, + com: &CtG1, + proof: &CtG1, + x: &CtFr, + values: &[CtFr], + n: usize, + ) -> Result { + match self { + MixedKzgSettings::Constantine(_) => Err("Context not in generic format".to_string()), + MixedKzgSettings::Generic(generic_context) => { + generic_context.check_proof_multi(com, proof, x, values, n) + } + } + } + + fn get_expanded_roots_of_unity_at(&self, i: usize) -> CtFr { + match self { + MixedKzgSettings::Constantine(_) => { + panic!("Context not in generic format") + } + MixedKzgSettings::Generic(generic_context) => { + generic_context.get_expanded_roots_of_unity_at(i) + } + } + } + + fn get_roots_of_unity_at(&self, i: usize) -> CtFr { + match self { + MixedKzgSettings::Constantine(_) => { + panic!("Context not in generic format") + } + MixedKzgSettings::Generic(generic_context) => generic_context.get_roots_of_unity_at(i), + } + } + + fn get_fft_settings(&self) -> &CtFFTSettings { + match self { + MixedKzgSettings::Constantine(_) => { + panic!("Context not in generic format") + } + MixedKzgSettings::Generic(generic_context) => generic_context.get_fft_settings(), + } + } + + fn get_g1_secret(&self) -> &[CtG1] { + match self { + MixedKzgSettings::Constantine(_) => { + panic!("Context not in generic format") + } + MixedKzgSettings::Generic(generic_context) => generic_context.get_g1_secret(), + } + } + + fn get_g2_secret(&self) -> &[CtG2] { + match self { + MixedKzgSettings::Constantine(_) => { + panic!("Context not in generic format") + } + MixedKzgSettings::Generic(generic_context) => generic_context.get_g2_secret(), + } + } + + fn get_precomputation( + &self, + ) -> Option<&kzg::msm::precompute::PrecomputationTable> { + match self { + MixedKzgSettings::Constantine(_) => { + panic!("Context not in generic format") + } + MixedKzgSettings::Generic(generic_context) => generic_context.get_precomputation(), + } + } +} diff --git a/constantine/src/mixed_kzg/mod.rs b/constantine/src/mixed_kzg/mod.rs new file mode 100644 index 00000000..9b6c1154 --- /dev/null +++ b/constantine/src/mixed_kzg/mod.rs @@ -0,0 +1,2 @@ +pub mod mixed_eip_4844; +pub mod mixed_kzg_settings; diff --git a/constantine/src/recovery.rs b/constantine/src/recovery.rs new file mode 100644 index 00000000..97813696 --- /dev/null +++ b/constantine/src/recovery.rs @@ -0,0 +1,195 @@ +extern crate alloc; + +use alloc::string::String; +use alloc::vec::Vec; + +use kzg::{FFTFr, Fr, PolyRecover, ZeroPoly}; + +use crate::types::fft_settings::CtFFTSettings; +use crate::types::fr::CtFr; +use crate::types::poly::CtPoly; +use once_cell::sync::OnceCell; + +#[cfg(feature = "parallel")] +use rayon::prelude::*; + +const SCALE_FACTOR: u64 = 5; +static INVERSE_FACTORS: OnceCell> = OnceCell::new(); +static UNSCALE_FACTOR_POWERS: OnceCell> = OnceCell::new(); + +pub fn scale_poly(p: &mut [CtFr], len_p: usize) { + let factors = INVERSE_FACTORS.get_or_init(|| { + let scale_factor = CtFr::from_u64(SCALE_FACTOR); + let inv_factor = CtFr::inverse(&scale_factor); + let mut temp = Vec::with_capacity(65536); + temp.push(CtFr::one()); + for i in 1..65536 { + temp.push(temp[i - 1].mul(&inv_factor)); + } + temp + }); + + p.iter_mut() + .zip(factors) + .take(len_p) + .skip(1) + .for_each(|(p, factor)| { + *p = p.mul(factor); + }); +} + +pub fn unscale_poly(p: &mut [CtFr], len_p: usize) { + let factors = UNSCALE_FACTOR_POWERS.get_or_init(|| { + let scale_factor = CtFr::from_u64(SCALE_FACTOR); + let mut temp = Vec::with_capacity(65536); + temp.push(CtFr::one()); + for i in 1..65536 { + temp.push(temp[i - 1].mul(&scale_factor)); + } + temp + }); + + p.iter_mut() + .zip(factors) + .take(len_p) + .skip(1) + .for_each(|(p, factor)| { + *p = p.mul(factor); + }); +} + +impl PolyRecover for CtPoly { + fn recover_poly_coeffs_from_samples( + samples: &[Option], + fs: &CtFFTSettings, + ) -> Result { + let len_samples = samples.len(); + + if !len_samples.is_power_of_two() { + return Err(String::from( + "Samples must have a length that is a power of two", + )); + } + + let mut missing = Vec::with_capacity(len_samples / 2); + + for (i, sample) in samples.iter().enumerate() { + if sample.is_none() { + missing.push(i); + } + } + + if missing.len() > len_samples / 2 { + return Err(String::from( + "Impossible to recover, too many shards are missing", + )); + } + + // Calculate `Z_r,I` + let (zero_eval, mut zero_poly) = fs.zero_poly_via_multiplication(len_samples, &missing)?; + + // Construct E * Z_r,I: the loop makes the evaluation polynomial + let poly_evaluations_with_zero = samples + .iter() + .zip(zero_eval) + .map(|(maybe_sample, zero_eval)| { + debug_assert_eq!(maybe_sample.is_none(), zero_eval.is_zero()); + + match maybe_sample { + Some(sample) => sample.mul(&zero_eval), + None => CtFr::zero(), + } + }) + .collect::>(); + + // Now inverse FFT so that poly_with_zero is (E * Z_r,I)(x) = (D * Z_r,I)(x) + let mut poly_with_zero = fs.fft_fr(&poly_evaluations_with_zero, true).unwrap(); + drop(poly_evaluations_with_zero); + + // x -> k * x + let len_zero_poly = zero_poly.coeffs.len(); + scale_poly(&mut poly_with_zero, len_samples); + scale_poly(&mut zero_poly.coeffs, len_zero_poly); + + // Q1 = (D * Z_r,I)(k * x) + let scaled_poly_with_zero = poly_with_zero; + + // Q2 = Z_r,I(k * x) + let scaled_zero_poly = zero_poly.coeffs; + + // Polynomial division by convolution: Q3 = Q1 / Q2 + #[cfg(feature = "parallel")] + let (eval_scaled_poly_with_zero, eval_scaled_zero_poly) = { + if len_zero_poly - 1 > 1024 { + rayon::join( + || fs.fft_fr(&scaled_poly_with_zero, false).unwrap(), + || fs.fft_fr(&scaled_zero_poly, false).unwrap(), + ) + } else { + ( + fs.fft_fr(&scaled_poly_with_zero, false).unwrap(), + fs.fft_fr(&scaled_zero_poly, false).unwrap(), + ) + } + }; + #[cfg(not(feature = "parallel"))] + let (eval_scaled_poly_with_zero, eval_scaled_zero_poly) = { + ( + fs.fft_fr(&scaled_poly_with_zero, false).unwrap(), + fs.fft_fr(&scaled_zero_poly, false).unwrap(), + ) + }; + drop(scaled_zero_poly); + + let mut eval_scaled_reconstructed_poly = eval_scaled_poly_with_zero; + #[cfg(not(feature = "parallel"))] + let eval_scaled_reconstructed_poly_iter = eval_scaled_reconstructed_poly.iter_mut(); + #[cfg(feature = "parallel")] + let eval_scaled_reconstructed_poly_iter = eval_scaled_reconstructed_poly.par_iter_mut(); + + eval_scaled_reconstructed_poly_iter + .zip(eval_scaled_zero_poly) + .for_each( + |(eval_scaled_reconstructed_poly, eval_scaled_poly_with_zero)| { + *eval_scaled_reconstructed_poly = eval_scaled_reconstructed_poly + .div(&eval_scaled_poly_with_zero) + .unwrap(); + }, + ); + + // The result of the division is D(k * x): + let mut scaled_reconstructed_poly = + fs.fft_fr(&eval_scaled_reconstructed_poly, true).unwrap(); + drop(eval_scaled_reconstructed_poly); + + // k * x -> x + unscale_poly(&mut scaled_reconstructed_poly, len_samples); + + // Finally we have D(x) which evaluates to our original data at the powers of roots of unity + Ok(Self { + coeffs: scaled_reconstructed_poly, + }) + } + + fn recover_poly_from_samples( + samples: &[Option], + fs: &CtFFTSettings, + ) -> Result { + let reconstructed_poly = Self::recover_poly_coeffs_from_samples(samples, fs)?; + + // The evaluation polynomial for D(x) is the reconstructed data: + let reconstructed_data = fs.fft_fr(&reconstructed_poly.coeffs, false).unwrap(); + + // Check all is well + samples + .iter() + .zip(&reconstructed_data) + .for_each(|(sample, reconstructed_data)| { + debug_assert!(sample.is_none() || reconstructed_data.equals(&sample.unwrap())); + }); + + Ok(Self { + coeffs: reconstructed_data, + }) + } +} diff --git a/constantine/src/types/fft_settings.rs b/constantine/src/types/fft_settings.rs new file mode 100644 index 00000000..59681515 --- /dev/null +++ b/constantine/src/types/fft_settings.rs @@ -0,0 +1,106 @@ +extern crate alloc; + +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; + +use kzg::common_utils::reverse_bit_order; +use kzg::{FFTSettings, Fr}; + +use crate::consts::SCALE2_ROOT_OF_UNITY; +use crate::types::fr::CtFr; + +#[derive(Clone)] +pub struct CtFFTSettings { + pub max_width: usize, + pub root_of_unity: CtFr, + pub expanded_roots_of_unity: Vec, + pub reverse_roots_of_unity: Vec, + pub roots_of_unity: Vec, +} + +impl Default for CtFFTSettings { + fn default() -> Self { + Self::new(0).unwrap() + } +} + +impl FFTSettings for CtFFTSettings { + /// Create FFTSettings with roots of unity for a selected scale. Resulting roots will have a magnitude of 2 ^ max_scale. + fn new(scale: usize) -> Result { + if scale >= SCALE2_ROOT_OF_UNITY.len() { + return Err(String::from( + "Scale is expected to be within root of unity matrix row size", + )); + } + + // max_width = 2 ^ max_scale + let max_width: usize = 1 << scale; + let root_of_unity = CtFr::from_u64_arr(&SCALE2_ROOT_OF_UNITY[scale]); + + // create max_width of roots & store them reversed as well + let expanded_roots_of_unity = expand_root_of_unity(&root_of_unity, max_width)?; + let mut reverse_roots_of_unity = expanded_roots_of_unity.clone(); + reverse_roots_of_unity.reverse(); + + // Permute the roots of unity + let mut roots_of_unity = expanded_roots_of_unity.clone(); + roots_of_unity.pop(); + reverse_bit_order(&mut roots_of_unity)?; + + Ok(CtFFTSettings { + max_width, + root_of_unity, + expanded_roots_of_unity, + reverse_roots_of_unity, + roots_of_unity, + }) + } + + fn get_max_width(&self) -> usize { + self.max_width + } + + fn get_expanded_roots_of_unity_at(&self, i: usize) -> CtFr { + self.expanded_roots_of_unity[i] + } + + fn get_expanded_roots_of_unity(&self) -> &[CtFr] { + &self.expanded_roots_of_unity + } + + fn get_reverse_roots_of_unity_at(&self, i: usize) -> CtFr { + self.reverse_roots_of_unity[i] + } + + fn get_reversed_roots_of_unity(&self) -> &[CtFr] { + &self.reverse_roots_of_unity + } + + fn get_roots_of_unity_at(&self, i: usize) -> CtFr { + self.roots_of_unity[i] + } + + fn get_roots_of_unity(&self) -> &[CtFr] { + &self.roots_of_unity + } +} + +/// Multiply a given root of unity by itself until it results in a 1 and result all multiplication values in a vector +pub fn expand_root_of_unity(root: &CtFr, width: usize) -> Result, String> { + let mut generated_powers = vec![CtFr::one(), *root]; + + while !(generated_powers.last().unwrap().is_one()) { + if generated_powers.len() > width { + return Err(String::from("Root of unity multiplied for too long")); + } + + generated_powers.push(generated_powers.last().unwrap().mul(root)); + } + + if generated_powers.len() != width + 1 { + return Err(String::from("Root of unity has invalid scale")); + } + + Ok(generated_powers) +} diff --git a/constantine/src/types/fk20_multi_settings.rs b/constantine/src/types/fk20_multi_settings.rs new file mode 100644 index 00000000..8465308a --- /dev/null +++ b/constantine/src/types/fk20_multi_settings.rs @@ -0,0 +1,166 @@ +extern crate alloc; + +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; + +use kzg::common_utils::reverse_bit_order; +use kzg::{FK20MultiSettings, Poly, FFTG1, G1}; + +use crate::types::fft_settings::CtFFTSettings; +use crate::types::fr::CtFr; +use crate::types::g1::CtG1; +use crate::types::g2::CtG2; +use crate::types::kzg_settings::CtKZGSettings; +use crate::types::poly::CtPoly; + +use super::fp::CtFp; +use super::g1::CtG1Affine; + +pub struct CtFK20MultiSettings { + pub kzg_settings: CtKZGSettings, + pub chunk_len: usize, + pub x_ext_fft_files: Vec>, +} + +impl Clone for CtFK20MultiSettings { + fn clone(&self) -> Self { + Self { + kzg_settings: self.kzg_settings.clone(), + chunk_len: self.chunk_len, + x_ext_fft_files: self.x_ext_fft_files.clone(), + } + } +} + +impl Default for CtFK20MultiSettings { + fn default() -> Self { + Self { + kzg_settings: CtKZGSettings::default(), + chunk_len: 1, + x_ext_fft_files: vec![], + } + } +} + +impl FK20MultiSettings + for CtFK20MultiSettings +{ + #[allow(clippy::many_single_char_names)] + fn new(ks: &CtKZGSettings, n2: usize, chunk_len: usize) -> Result { + if n2 > ks.fs.max_width { + return Err(String::from( + "n2 must be less than or equal to kzg settings max width", + )); + } else if !n2.is_power_of_two() { + return Err(String::from("n2 must be a power of two")); + } else if n2 < 2 { + return Err(String::from("n2 must be greater than or equal to 2")); + } else if chunk_len > n2 / 2 { + return Err(String::from("chunk_len must be greater or equal to n2 / 2")); + } else if !chunk_len.is_power_of_two() { + return Err(String::from("chunk_len must be a power of two")); + } + + let n = n2 / 2; + let k = n / chunk_len; + + let mut ext_fft_files = Vec::with_capacity(chunk_len); + { + let mut x = Vec::with_capacity(k); + for offset in 0..chunk_len { + let mut start = 0; + if n >= chunk_len + 1 + offset { + start = n - chunk_len - 1 - offset; + } + + let mut i = 0; + let mut j = start; + + while i + 1 < k { + x.push(ks.secret_g1[j]); + + i += 1; + + if j >= chunk_len { + j -= chunk_len; + } else { + j = 0; + } + } + x.push(CtG1::identity()); + + let ext_fft_file = ks.fs.toeplitz_part_1(&x); + x.clear(); + ext_fft_files.push(ext_fft_file); + } + } + + let ret = Self { + kzg_settings: ks.clone(), + chunk_len, + x_ext_fft_files: ext_fft_files, + }; + + Ok(ret) + } + + fn data_availability(&self, p: &CtPoly) -> Result, String> { + let n = p.len(); + let n2 = n * 2; + + if n2 > self.kzg_settings.fs.max_width { + return Err(String::from( + "n2 must be less than or equal to kzg settings max width", + )); + } + + if !n2.is_power_of_two() { + return Err(String::from("n2 must be a power of two")); + } + + let mut ret = self.data_availability_optimized(p).unwrap(); + reverse_bit_order(&mut ret)?; + + Ok(ret) + } + + fn data_availability_optimized(&self, p: &CtPoly) -> Result, String> { + let n = p.len(); + let n2 = n * 2; + + if n2 > self.kzg_settings.fs.max_width { + return Err(String::from( + "n2 must be less than or equal to kzg settings max width", + )); + } else if !n2.is_power_of_two() { + return Err(String::from("n2 must be a power of two")); + } + + let n = n2 / 2; + let k = n / self.chunk_len; + let k2 = k * 2; + + let mut h_ext_fft = vec![CtG1::identity(); k2]; + + for i in 0..self.chunk_len { + let toeplitz_coeffs = p.toeplitz_coeffs_stride(i, self.chunk_len); + let h_ext_fft_file = self + .kzg_settings + .fs + .toeplitz_part_2(&toeplitz_coeffs, &self.x_ext_fft_files[i]); + + for j in 0..k2 { + h_ext_fft[j] = h_ext_fft[j].add_or_dbl(&h_ext_fft_file[j]); + } + } + + let mut h = self.kzg_settings.fs.toeplitz_part_3(&h_ext_fft); + + h[k..k2].copy_from_slice(&vec![CtG1::identity(); k2 - k]); + + let ret = self.kzg_settings.fs.fft_g1(&h, false).unwrap(); + + Ok(ret) + } +} diff --git a/constantine/src/types/fk20_single_settings.rs b/constantine/src/types/fk20_single_settings.rs new file mode 100644 index 00000000..68652432 --- /dev/null +++ b/constantine/src/types/fk20_single_settings.rs @@ -0,0 +1,102 @@ +extern crate alloc; + +use alloc::string::String; +use alloc::vec::Vec; + +use kzg::common_utils::reverse_bit_order; +use kzg::{FK20SingleSettings, Poly, FFTG1, G1}; + +use crate::types::fft_settings::CtFFTSettings; +use crate::types::fr::CtFr; +use crate::types::g1::CtG1; +use crate::types::g2::CtG2; +use crate::types::kzg_settings::CtKZGSettings; +use crate::types::poly::CtPoly; + +use super::fp::CtFp; +use super::g1::CtG1Affine; + +#[derive(Clone, Default)] +pub struct CtFK20SingleSettings { + pub kzg_settings: CtKZGSettings, + pub x_ext_fft: Vec, +} + +impl FK20SingleSettings + for CtFK20SingleSettings +{ + fn new(kzg_settings: &CtKZGSettings, n2: usize) -> Result { + let n = n2 / 2; + + if n2 > kzg_settings.fs.max_width { + return Err(String::from( + "n2 must be less than or equal to kzg settings max width", + )); + } else if !n2.is_power_of_two() { + return Err(String::from("n2 must be a power of two")); + } else if n2 < 2 { + return Err(String::from("n2 must be greater than or equal to 2")); + } + + let mut x = Vec::with_capacity(n); + for i in 0..n - 1 { + x.push(kzg_settings.secret_g1[n - 2 - i]); + } + x.push(CtG1::identity()); + + let x_ext_fft = kzg_settings.fs.toeplitz_part_1(&x); + drop(x); + let kzg_settings = kzg_settings.clone(); + + let ret = Self { + kzg_settings, + x_ext_fft, + }; + + Ok(ret) + } + + fn data_availability(&self, p: &CtPoly) -> Result, String> { + let n = p.len(); + let n2 = n * 2; + + if n2 > self.kzg_settings.fs.max_width { + return Err(String::from( + "n2 must be less than or equal to kzg settings max width", + )); + } else if !n2.is_power_of_two() { + return Err(String::from("n2 must be a power of two")); + } + + let mut ret = self.data_availability_optimized(p).unwrap(); + reverse_bit_order(&mut ret)?; + + Ok(ret) + } + + fn data_availability_optimized(&self, p: &CtPoly) -> Result, String> { + let n = p.len(); + let n2 = n * 2; + + if n2 > self.kzg_settings.fs.max_width { + return Err(String::from( + "n2 must be less than or equal to kzg settings max width", + )); + } else if !n2.is_power_of_two() { + return Err(String::from("n2 must be a power of two")); + } + + let toeplitz_coeffs = p.toeplitz_coeffs_step(); + + let h_ext_fft = self + .kzg_settings + .fs + .toeplitz_part_2(&toeplitz_coeffs, &self.x_ext_fft); + + let h = self.kzg_settings.fs.toeplitz_part_3(&h_ext_fft); + + let ret = self.kzg_settings.fs.fft_g1(&h, false).unwrap(); + + Ok(ret) + } +} diff --git a/constantine/src/types/fp.rs b/constantine/src/types/fp.rs new file mode 100644 index 00000000..ac66b3ac --- /dev/null +++ b/constantine/src/types/fp.rs @@ -0,0 +1,102 @@ +use constantine_sys as constantine; +use constantine_sys::bls12_381_fp; +use core::fmt::{Debug, Formatter}; +use kzg::G1Fp; + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct CtFp(pub bls12_381_fp); + +impl PartialEq for CtFp { + fn eq(&self, other: &Self) -> bool { + unsafe { constantine::ctt_bls12_381_fp_is_eq(&self.0, &other.0) != 0 } + } +} + +impl Debug for CtFp { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "CtFp({:?})", self.0.limbs) + } +} + +impl G1Fp for CtFp { + const ONE: Self = Self(bls12_381_fp { + limbs: [ + 8505329371266088957, + 17002214543764226050, + 6865905132761471162, + 8632934651105793861, + 6631298214892334189, + 1582556514881692819, + ], + }); + const ZERO: Self = Self(bls12_381_fp { + limbs: [0, 0, 0, 0, 0, 0], + }); + const BLS12_381_RX_P: Self = Self(bls12_381_fp { + limbs: [ + 8505329371266088957, + 17002214543764226050, + 6865905132761471162, + 8632934651105793861, + 6631298214892334189, + 1582556514881692819, + ], + }); + + fn inverse(&self) -> Option { + let mut out: Self = *self; + unsafe { + constantine::ctt_bls12_381_fp_inv(&mut out.0, &self.0); + } + Some(out) + } + + fn square(&self) -> Self { + let mut out: Self = Default::default(); + unsafe { + constantine::ctt_bls12_381_fp_square(&mut out.0, &self.0); + } + out + } + + fn double(&self) -> Self { + let mut out: Self = Default::default(); + unsafe { + constantine::ctt_bls12_381_fp_double(&mut out.0, &self.0); + } + out + } + + fn from_underlying_arr(arr: &[u64; 6]) -> Self { + unsafe { + Self(bls12_381_fp { + limbs: core::mem::transmute(*arr), + }) + } + } + + fn neg_assign(&mut self) { + unsafe { + constantine::ctt_bls12_381_fp_neg_in_place(&mut self.0); + } + } + + fn mul_assign_fp(&mut self, b: &Self) { + unsafe { + constantine::ctt_bls12_381_fp_mul_in_place(&mut self.0, &b.0); + } + } + + fn sub_assign_fp(&mut self, b: &Self) { + unsafe { + constantine::ctt_bls12_381_fp_sub_in_place(&mut self.0, &b.0); + } + } + + fn add_assign_fp(&mut self, b: &Self) { + unsafe { + constantine::ctt_bls12_381_fp_add_in_place(&mut self.0, &b.0); + } + } +} diff --git a/constantine/src/types/fr.rs b/constantine/src/types/fr.rs new file mode 100644 index 00000000..2d429def --- /dev/null +++ b/constantine/src/types/fr.rs @@ -0,0 +1,288 @@ +//blst_fp = bls12_381_fp, CtG1 = CtG1, blst_p1 = bls12_381_g1_jac, blst_fr = bls12_381_fr +extern crate alloc; + +use alloc::format; +use alloc::string::String; +use alloc::string::ToString; + +use blst::blst_fr; +use constantine::ctt_codec_scalar_status; +use core::fmt::{Debug, Formatter}; +use kzg::eip_4844::BYTES_PER_FIELD_ELEMENT; +use kzg::Fr; +use kzg::Scalar256; + +use constantine_sys as constantine; + +use constantine_sys::bls12_381_fr; + +use crate::utils::ptr_transmute; +use crate::utils::ptr_transmute_mut; + +#[repr(C)] +#[derive(Clone, Copy, Default)] +pub struct CtFr(pub bls12_381_fr); + +impl Debug for CtFr { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "CtFr({:?})", self.0.limbs) + } +} + +impl PartialEq for CtFr { + fn eq(&self, other: &Self) -> bool { + self.equals(other) + } +} +impl Eq for CtFr {} + +impl CtFr { + pub fn from_blst_fr(fr: blst::blst_fr) -> Self { + unsafe { + Self(bls12_381_fr { + limbs: core::mem::transmute(fr.l), + }) + } + } + + pub fn to_blst_fr(&self) -> blst_fr { + unsafe { + blst_fr { + l: core::mem::transmute(self.0.limbs), + } + } + } +} + +impl Fr for CtFr { + fn null() -> Self { + Self::from_u64_arr(&[u64::MAX, u64::MAX, u64::MAX, u64::MAX]) + } + + fn zero() -> Self { + Self::from_u64(0) + } + + fn one() -> Self { + Self::from_u64(1) + } + + #[cfg(feature = "rand")] + fn rand() -> Self { + let val = constantine_sys::big255 { + limbs: [ + rand::random(), + rand::random(), + rand::random(), + rand::random(), + ], + }; + let mut ret = Self::default(); + unsafe { + constantine::ctt_bls12_381_fr_from_big255(&mut ret.0, &val); + } + + ret + } + + fn from_bytes(bytes: &[u8]) -> Result { + bytes + .try_into() + .map_err(|_| { + format!( + "Invalid byte length. Expected {}, got {}", + BYTES_PER_FIELD_ELEMENT, + bytes.len() + ) + }) + .and_then(|bytes: &[u8; BYTES_PER_FIELD_ELEMENT]| { + let mut ret: Self = Self::default(); + let mut scalar = constantine::big255::default(); + unsafe { + let status = + constantine::ctt_bls12_381_deserialize_scalar(&mut scalar, bytes.as_ptr()); + if status == ctt_codec_scalar_status::cttCodecScalar_ScalarLargerThanCurveOrder + { + return Err("Invalid scalar".to_string()); + } + constantine::ctt_bls12_381_fr_from_big255(&mut ret.0, &scalar); + } + Ok(ret) + }) + } + + fn from_bytes_unchecked(bytes: &[u8]) -> Result { + bytes + .try_into() + .map_err(|_| { + format!( + "Invalid byte length. Expected {}, got {}", + BYTES_PER_FIELD_ELEMENT, + bytes.len() + ) + }) + .map(|bytes: &[u8; BYTES_PER_FIELD_ELEMENT]| { + let mut ret = Self::default(); + let mut scalar = constantine::big255::default(); + unsafe { + let _ = constantine::ctt_big255_unmarshalBE( + &mut scalar, + bytes.as_ptr(), + BYTES_PER_FIELD_ELEMENT as isize, + ); + constantine::ctt_bls12_381_fr_from_big255(&mut ret.0, &scalar); + } + ret + }) + } + + fn from_hex(hex: &str) -> Result { + let bytes = hex::decode(&hex[2..]).unwrap(); + Self::from_bytes(&bytes) + } + + fn from_u64_arr(u: &[u64; 4]) -> Self { + let mut ret = Self::default(); + unsafe { + constantine::ctt_bls12_381_fr_from_big255(&mut ret.0, ptr_transmute(u)); + } + + ret + } + + fn from_u64(val: u64) -> Self { + Self::from_u64_arr(&[val, 0, 0, 0]) + } + + fn to_bytes(&self) -> [u8; 32] { + let mut scalar = constantine::big255::default(); + let mut bytes = [0u8; 32]; + unsafe { + constantine::ctt_big255_from_bls12_381_fr(&mut scalar, &self.0); + let _ = constantine::ctt_bls12_381_serialize_scalar(bytes.as_mut_ptr(), &scalar); + } + + bytes + } + + fn to_u64_arr(&self) -> [u64; 4] { + let mut val: [u64; 4] = [0; 4]; + unsafe { + constantine::ctt_big255_from_bls12_381_fr(ptr_transmute_mut(&mut val), &self.0); + } + + val + } + + fn is_one(&self) -> bool { + unsafe { constantine::ctt_bls12_381_fr_is_one(&self.0) != 0 } + } + + fn is_zero(&self) -> bool { + unsafe { constantine::ctt_bls12_381_fr_is_zero(&self.0) != 0 } + } + + fn is_null(&self) -> bool { + self.equals(&Self::null()) + } + + fn sqr(&self) -> Self { + let mut ret = Self::default(); + unsafe { constantine::ctt_bls12_381_fr_square(&mut ret.0, &self.0) } + + ret + } + + fn mul(&self, b: &Self) -> Self { + let mut ret = Self::default(); + unsafe { + constantine::ctt_bls12_381_fr_prod(&mut ret.0, &self.0, &b.0); + } + + ret + } + + fn add(&self, b: &Self) -> Self { + let mut ret = Self::default(); + unsafe { + constantine::ctt_bls12_381_fr_sum(&mut ret.0, &self.0, &b.0); + } + + ret + } + + fn sub(&self, b: &Self) -> Self { + let mut ret = Self::default(); + unsafe { + constantine::ctt_bls12_381_fr_diff(&mut ret.0, &self.0, &b.0); + } + + ret + } + + fn eucl_inverse(&self) -> Self { + let mut ret = Self::default(); + unsafe { + constantine::ctt_bls12_381_fr_inv(&mut ret.0, &self.0); + } + + ret + } + + fn negate(&self) -> Self { + let mut ret = *self; + unsafe { + constantine::ctt_bls12_381_fr_neg_in_place(&mut ret.0); + } + + ret + } + + fn inverse(&self) -> Self { + let mut ret = Self::default(); + unsafe { + constantine::ctt_bls12_381_fr_inv(&mut ret.0, &self.0); + } + + ret + } + + fn pow(&self, n: usize) -> Self { + let mut out = Self::one(); + + let mut temp = *self; + let mut n = n; + loop { + if (n & 1) == 1 { + out = out.mul(&temp); + } + n >>= 1; + if n == 0 { + break; + } + + temp = temp.sqr(); + } + + out + } + + fn div(&self, b: &Self) -> Result { + let tmp = b.eucl_inverse(); + let out = self.mul(&tmp); + + Ok(out) + } + + fn equals(&self, b: &Self) -> bool { + unsafe { constantine::ctt_bls12_381_fr_is_eq(&self.0, &b.0) != 0 } + } + + fn to_scalar(&self) -> kzg::Scalar256 { + let mut scalar = constantine::big255::default(); + unsafe { + constantine::ctt_big255_from_bls12_381_fr(&mut scalar, &self.0); + Scalar256::from_u64(core::mem::transmute(scalar.limbs)) + } + } +} diff --git a/constantine/src/types/g1.rs b/constantine/src/types/g1.rs new file mode 100644 index 00000000..c9fdae17 --- /dev/null +++ b/constantine/src/types/g1.rs @@ -0,0 +1,417 @@ +extern crate alloc; + +use alloc::format; +use alloc::string::String; +use alloc::string::ToString; +use constantine::ctt_codec_ecc_status; +use kzg::msm::precompute::PrecomputationTable; +use kzg::G1LinComb; + +use core::fmt::{Debug, Formatter}; + +use crate::kzg_proofs::g1_linear_combination; +use crate::types::fp::CtFp; +use crate::types::fr::CtFr; + +use kzg::eip_4844::BYTES_PER_G1; +use kzg::G1Affine; +use kzg::G1GetFp; +use kzg::G1ProjAddAffine; +use kzg::{G1Mul, G1}; + +use crate::consts::{G1_GENERATOR, G1_IDENTITY, G1_NEGATIVE_GENERATOR}; +// use crate::kzg_proofs::g1_linear_combination; + +use constantine_sys as constantine; + +use constantine_sys::{ + bls12_381_fp, bls12_381_g1_aff, bls12_381_g1_jac, ctt_bls12_381_g1_jac_from_affine, +}; + +#[repr(C)] +#[derive(Clone, Copy, Default)] +pub struct CtG1(pub bls12_381_g1_jac); + +impl PartialEq for CtG1 { + fn eq(&self, other: &Self) -> bool { + unsafe { constantine::ctt_bls12_381_g1_jac_is_eq(&self.0, &other.0) != 0 } + } +} + +impl Debug for CtG1 { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!( + f, + "CtG1({:?}, {:?}, {:?})", + self.0.x.limbs, self.0.y.limbs, self.0.z.limbs + ) + } +} + +impl CtG1 { + pub(crate) const fn from_xyz(x: bls12_381_fp, y: bls12_381_fp, z: bls12_381_fp) -> Self { + CtG1(bls12_381_g1_jac { x, y, z }) + } + + pub const fn from_blst_p1(p1: blst::blst_p1) -> Self { + unsafe { + Self(bls12_381_g1_jac { + x: bls12_381_fp { + limbs: core::mem::transmute(p1.x.l), + }, + y: bls12_381_fp { + limbs: core::mem::transmute(p1.y.l), + }, + z: bls12_381_fp { + limbs: core::mem::transmute(p1.z.l), + }, + }) + } + } + + pub const fn to_blst_p1(&self) -> blst::blst_p1 { + unsafe { + blst::blst_p1 { + x: blst::blst_fp { + l: core::mem::transmute(self.0.x.limbs), + }, + y: blst::blst_fp { + l: core::mem::transmute(self.0.y.limbs), + }, + z: blst::blst_fp { + l: core::mem::transmute(self.0.z.limbs), + }, + } + } + } +} + +impl G1 for CtG1 { + fn identity() -> Self { + G1_IDENTITY + } + + fn generator() -> Self { + G1_GENERATOR + } + + fn negative_generator() -> Self { + G1_NEGATIVE_GENERATOR + } + + #[cfg(feature = "rand")] + fn rand() -> Self { + let result: CtG1 = G1_GENERATOR; + result.mul(&kzg::Fr::rand()) + } + + fn from_bytes(bytes: &[u8]) -> Result { + bytes + .try_into() + .map_err(|_| { + format!( + "Invalid byte length. Expected {}, got {}", + BYTES_PER_G1, + bytes.len() + ) + }) + .and_then(|bytes: &[u8; BYTES_PER_G1]| { + let mut tmp = bls12_381_g1_aff::default(); + let mut g1 = bls12_381_g1_jac::default(); + unsafe { + // The uncompress routine also checks that the point is on the curve + let res = constantine::ctt_bls12_381_deserialize_g1_compressed( + &mut tmp, + bytes.as_ptr(), + ); + if res != ctt_codec_ecc_status::cttCodecEcc_Success + && res != ctt_codec_ecc_status::cttCodecEcc_PointAtInfinity + { + return Err("Failed to uncompress".to_string()); + } + ctt_bls12_381_g1_jac_from_affine(&mut g1, &tmp); + } + Ok(CtG1(g1)) + }) + } + + fn from_hex(hex: &str) -> Result { + let bytes = hex::decode(&hex[2..]).unwrap(); + Self::from_bytes(&bytes) + } + + fn to_bytes(&self) -> [u8; 48] { + let mut out = [0u8; BYTES_PER_G1]; + unsafe { + let _ = constantine::ctt_bls12_381_serialize_g1_compressed( + out.as_mut_ptr(), + &CtG1Affine::into_affine(self).0, + ); + } + out + } + + fn add_or_dbl(&self, b: &Self) -> Self { + let mut ret = Self::default(); + unsafe { + constantine::ctt_bls12_381_g1_jac_sum(&mut ret.0, &self.0, &b.0); + } + ret + } + + fn is_inf(&self) -> bool { + unsafe { constantine::ctt_bls12_381_g1_jac_is_inf(&self.0) != 0 } + } + + fn is_valid(&self) -> bool { + unsafe { + constantine::ctt_bls12_381_validate_g1(&CtG1Affine::into_affine(self).0) + == ctt_codec_ecc_status::cttCodecEcc_Success + } + } + + fn dbl(&self) -> Self { + let mut result = bls12_381_g1_jac::default(); + unsafe { + constantine::ctt_bls12_381_g1_jac_double(&mut result, &self.0); + } + Self(result) + } + + fn add(&self, b: &Self) -> Self { + let mut ret = Self::default(); + unsafe { + constantine::ctt_bls12_381_g1_jac_sum(&mut ret.0, &self.0, &b.0); + } + ret + } + + fn sub(&self, b: &Self) -> Self { + let mut ret = Self::default(); + unsafe { + constantine::ctt_bls12_381_g1_jac_diff(&mut ret.0, &self.0, &b.0); + } + ret + } + + fn equals(&self, b: &Self) -> bool { + unsafe { constantine::ctt_bls12_381_g1_jac_is_eq(&self.0, &b.0) != 0 } + } + + const ZERO: Self = CtG1::from_xyz( + bls12_381_fp { + limbs: [ + 8505329371266088957, + 17002214543764226050, + 6865905132761471162, + 8632934651105793861, + 6631298214892334189, + 1582556514881692819, + ], + }, + bls12_381_fp { + limbs: [ + 8505329371266088957, + 17002214543764226050, + 6865905132761471162, + 8632934651105793861, + 6631298214892334189, + 1582556514881692819, + ], + }, + bls12_381_fp { limbs: [0; 6] }, + ); + + fn add_or_dbl_assign(&mut self, b: &Self) { + unsafe { + constantine::ctt_bls12_381_g1_jac_add_in_place(&mut self.0, &b.0); + } + } + + fn add_assign(&mut self, b: &Self) { + unsafe { + constantine::ctt_bls12_381_g1_jac_add_in_place(&mut self.0, &b.0); + } + } + + fn dbl_assign(&mut self) { + unsafe { + constantine::ctt_bls12_381_g1_jac_double_in_place(&mut self.0); + } + } +} + +impl G1Mul for CtG1 { + fn mul(&self, b: &CtFr) -> Self { + let mut result = *self; + unsafe { + constantine::ctt_bls12_381_g1_jac_scalar_mul_fr_coef(&mut result.0, &b.0); + } + result + } +} + +impl G1LinComb for CtG1 { + fn g1_lincomb( + points: &[Self], + scalars: &[CtFr], + len: usize, + precomputation: Option<&PrecomputationTable>, + ) -> Self { + let mut out = CtG1::default(); + g1_linear_combination(&mut out, points, scalars, len, precomputation); + out + } +} + +impl G1GetFp for CtG1 { + fn x(&self) -> &CtFp { + unsafe { + // Transmute safe due to repr(C) on CtFp + core::mem::transmute(&self.0.x) + } + } + + fn y(&self) -> &CtFp { + unsafe { + // Transmute safe due to repr(C) on CtFp + core::mem::transmute(&self.0.y) + } + } + + fn z(&self) -> &CtFp { + unsafe { + // Transmute safe due to repr(C) on CtFp + core::mem::transmute(&self.0.z) + } + } + + fn x_mut(&mut self) -> &mut CtFp { + unsafe { + // Transmute safe due to repr(C) on CtFp + core::mem::transmute(&mut self.0.x) + } + } + + fn y_mut(&mut self) -> &mut CtFp { + unsafe { + // Transmute safe due to repr(C) on CtFp + core::mem::transmute(&mut self.0.y) + } + } + + fn z_mut(&mut self) -> &mut CtFp { + unsafe { + // Transmute safe due to repr(C) on CtFp + core::mem::transmute(&mut self.0.z) + } + } +} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +pub struct CtG1Affine(pub constantine::bls12_381_g1_aff); + +impl PartialEq for CtG1Affine { + fn eq(&self, other: &Self) -> bool { + unsafe { constantine::ctt_bls12_381_g1_aff_is_eq(&self.0, &other.0) != 0 } + } +} + +impl Debug for CtG1Affine { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "CtG1Affine({:?}, {:?})", self.0.x.limbs, self.0.y.limbs) + } +} + +impl G1Affine for CtG1Affine { + const ZERO: Self = Self(bls12_381_g1_aff { + x: { + bls12_381_fp { + limbs: [0, 0, 0, 0, 0, 0], + } + }, + y: { + bls12_381_fp { + limbs: [0, 0, 0, 0, 0, 0], + } + }, + }); + + fn into_affine(g1: &CtG1) -> Self { + let mut ret: Self = Default::default(); + unsafe { + constantine::ctt_bls12_381_g1_jac_affine(&mut ret.0, &g1.0); + } + ret + } + + fn into_affines_loc(out: &mut [Self], g1: &[CtG1]) { + unsafe { + constantine::ctt_bls12_381_g1_jac_batch_affine( + core::mem::transmute(out.as_mut_ptr()), + core::mem::transmute(g1.as_ptr()), + g1.len(), + ); + } + } + + fn to_proj(&self) -> CtG1 { + let mut ret: CtG1 = Default::default(); + unsafe { + constantine::ctt_bls12_381_g1_jac_from_affine(&mut ret.0, &self.0); + } + ret + } + + fn x(&self) -> &CtFp { + unsafe { + // Transmute safe due to repr(C) on CtFp + core::mem::transmute(&self.0.x) + } + } + + fn y(&self) -> &CtFp { + unsafe { + // Transmute safe due to repr(C) on CtFp + core::mem::transmute(&self.0.y) + } + } + + fn is_infinity(&self) -> bool { + unsafe { constantine::ctt_bls12_381_g1_aff_is_inf(&self.0) != 0 } + } + + fn x_mut(&mut self) -> &mut CtFp { + unsafe { + // Transmute safe due to repr(C) on CtFp + core::mem::transmute(&mut self.0.x) + } + } + + fn y_mut(&mut self) -> &mut CtFp { + unsafe { + // Transmute safe due to repr(C) on CtFp + core::mem::transmute(&mut self.0.y) + } + } +} + +pub struct CtG1ProjAddAffine; +impl G1ProjAddAffine for CtG1ProjAddAffine { + fn add_assign_affine(proj: &mut CtG1, aff: &CtG1Affine) { + let mut g1_jac = bls12_381_g1_jac::default(); + unsafe { + constantine::ctt_bls12_381_g1_jac_from_affine(&mut g1_jac, &aff.0); + constantine::ctt_bls12_381_g1_jac_add_in_place(&mut proj.0, &g1_jac); + } + } + + fn add_or_double_assign_affine(proj: &mut CtG1, aff: &CtG1Affine) { + let mut g1_jac = bls12_381_g1_jac::default(); + unsafe { + constantine::ctt_bls12_381_g1_jac_from_affine(&mut g1_jac, &aff.0); + constantine::ctt_bls12_381_g1_jac_add_in_place(&mut proj.0, &g1_jac); + } + } +} diff --git a/constantine/src/types/g2.rs b/constantine/src/types/g2.rs new file mode 100644 index 00000000..f7ae5dea --- /dev/null +++ b/constantine/src/types/g2.rs @@ -0,0 +1,202 @@ +extern crate alloc; + +use alloc::format; +use alloc::string::String; +use alloc::string::ToString; + +use constantine::ctt_codec_ecc_status; +use kzg::eip_4844::BYTES_PER_G2; +#[cfg(feature = "rand")] +use kzg::Fr; +use kzg::{G2Mul, G2}; + +use crate::consts::{G2_GENERATOR, G2_NEGATIVE_GENERATOR}; +use crate::types::fr::CtFr; + +use constantine_sys::{ + bls12_381_fp, bls12_381_fp2, bls12_381_g2_aff, bls12_381_g2_jac, + ctt_bls12_381_g2_jac_from_affine, +}; + +use constantine_sys as constantine; + +#[derive(Default, Clone, Copy)] +pub struct CtG2(pub bls12_381_g2_jac); + +impl CtG2 { + pub const fn from_blst_p2(p2: blst::blst_p2) -> Self { + unsafe { + Self(bls12_381_g2_jac { + x: bls12_381_fp2 { + c: [ + bls12_381_fp { + limbs: core::mem::transmute(p2.x.fp[0].l), + }, + bls12_381_fp { + limbs: core::mem::transmute(p2.x.fp[1].l), + }, + ], + }, + y: bls12_381_fp2 { + c: [ + bls12_381_fp { + limbs: core::mem::transmute(p2.y.fp[0].l), + }, + bls12_381_fp { + limbs: core::mem::transmute(p2.y.fp[1].l), + }, + ], + }, + z: bls12_381_fp2 { + c: [ + bls12_381_fp { + limbs: core::mem::transmute(p2.z.fp[0].l), + }, + bls12_381_fp { + limbs: core::mem::transmute(p2.z.fp[1].l), + }, + ], + }, + }) + } + } + + pub const fn to_blst_p2(&self) -> blst::blst_p2 { + unsafe { + blst::blst_p2 { + x: blst::blst_fp2 { + fp: [ + blst::blst_fp { + l: core::mem::transmute(self.0.x.c[0].limbs), + }, + blst::blst_fp { + l: core::mem::transmute(self.0.x.c[1].limbs), + }, + ], + }, + y: blst::blst_fp2 { + fp: [ + blst::blst_fp { + l: core::mem::transmute(self.0.y.c[0].limbs), + }, + blst::blst_fp { + l: core::mem::transmute(self.0.y.c[1].limbs), + }, + ], + }, + z: blst::blst_fp2 { + fp: [ + blst::blst_fp { + l: core::mem::transmute(self.0.z.c[0].limbs), + }, + blst::blst_fp { + l: core::mem::transmute(self.0.z.c[1].limbs), + }, + ], + }, + } + } + } +} + +impl G2Mul for CtG2 { + fn mul(&self, b: &CtFr) -> Self { + let mut result = *self; + unsafe { + constantine::ctt_bls12_381_g2_jac_scalar_mul_fr_coef(&mut result.0, &b.0); + } + result + } +} + +impl G2 for CtG2 { + fn generator() -> Self { + G2_GENERATOR + } + + fn negative_generator() -> Self { + G2_NEGATIVE_GENERATOR + } + + fn from_bytes(bytes: &[u8]) -> Result { + bytes + .try_into() + .map_err(|_| { + format!( + "Invalid byte length. Expected {}, got {}", + BYTES_PER_G2, + bytes.len() + ) + }) + .and_then(|bytes: &[u8; BYTES_PER_G2]| { + let mut tmp = bls12_381_g2_aff::default(); + let mut g2 = bls12_381_g2_jac::default(); + unsafe { + // The uncompress routine also checks that the point is on the curve + let res = constantine::ctt_bls12_381_deserialize_g2_compressed( + &mut tmp, + bytes.as_ptr(), + ); + if res != ctt_codec_ecc_status::cttCodecEcc_Success + && res != ctt_codec_ecc_status::cttCodecEcc_PointAtInfinity + { + return Err("Failed to uncompress".to_string()); + } + ctt_bls12_381_g2_jac_from_affine(&mut g2, &tmp); + } + Ok(CtG2(g2)) + }) + } + + fn to_bytes(&self) -> [u8; 96] { + let mut out = [0u8; BYTES_PER_G2]; + let mut tmp = bls12_381_g2_aff::default(); + unsafe { + constantine::ctt_bls12_381_g2_jac_affine(&mut tmp, &self.0); + let _ = constantine::ctt_bls12_381_serialize_g2_compressed(out.as_mut_ptr(), &tmp); + } + out + } + + fn add_or_dbl(&mut self, b: &Self) -> Self { + let mut result = self.0; + unsafe { + constantine::ctt_bls12_381_g2_jac_add_in_place(&mut result, &b.0); + } + Self(result) + } + + fn dbl(&self) -> Self { + let mut result = bls12_381_g2_jac::default(); + unsafe { + constantine::ctt_bls12_381_g2_jac_double(&mut result, &self.0); + } + Self(result) + } + + fn sub(&self, b: &Self) -> Self { + let mut bneg: bls12_381_g2_jac = b.0; + let mut result = self.0; + unsafe { + constantine::ctt_bls12_381_g2_jac_neg_in_place(&mut bneg); + constantine::ctt_bls12_381_g2_jac_add_in_place(&mut result, &bneg); + } + Self(result) + } + + fn equals(&self, b: &Self) -> bool { + unsafe { constantine::ctt_bls12_381_g2_jac_is_eq(&self.0, &b.0) != 0 } + } +} + +impl CtG2 { + pub(crate) fn _from_xyz(x: bls12_381_fp2, y: bls12_381_fp2, z: bls12_381_fp2) -> Self { + CtG2(bls12_381_g2_jac { x, y, z }) + } + + #[cfg(feature = "rand")] + pub fn rand() -> Self { + let result: CtG2 = G2_GENERATOR; + result.mul(&CtFr::rand()) + } +} diff --git a/constantine/src/types/kzg_settings.rs b/constantine/src/types/kzg_settings.rs new file mode 100644 index 00000000..0f2ff600 --- /dev/null +++ b/constantine/src/types/kzg_settings.rs @@ -0,0 +1,207 @@ +extern crate alloc; + +use alloc::string::String; +use alloc::vec::Vec; + +use kzg::msm::precompute::{precompute, PrecomputationTable}; +use kzg::{FFTFr, FFTSettings, Fr, G1Mul, G2Mul, KZGSettings, Poly, G1, G2}; + +use crate::consts::{G1_GENERATOR, G2_GENERATOR}; +use crate::kzg_proofs::{g1_linear_combination, pairings_verify}; +use crate::types::fft_settings::CtFFTSettings; +use crate::types::fr::CtFr; +use crate::types::g1::CtG1; +use crate::types::g2::CtG2; +use crate::types::poly::CtPoly; + +use super::fp::CtFp; +use super::g1::CtG1Affine; + +#[derive(Clone, Default)] +pub struct CtKZGSettings { + pub fs: CtFFTSettings, + pub secret_g1: Vec, + pub secret_g2: Vec, + pub precomputation: Option>, +} + +impl KZGSettings for CtKZGSettings { + fn new( + secret_g1: &[CtG1], + secret_g2: &[CtG2], + _length: usize, + fft_settings: &CtFFTSettings, + ) -> Result { + Ok(Self { + secret_g1: secret_g1.to_vec(), + secret_g2: secret_g2.to_vec(), + fs: fft_settings.clone(), + precomputation: precompute(secret_g1).ok().flatten(), + }) + } + + fn commit_to_poly(&self, poly: &CtPoly) -> Result { + if poly.coeffs.len() > self.secret_g1.len() { + return Err(String::from("Polynomial is longer than secret g1")); + } + + let mut out = CtG1::default(); + g1_linear_combination( + &mut out, + &self.secret_g1, + &poly.coeffs, + poly.coeffs.len(), + self.get_precomputation(), + ); + + Ok(out) + } + + fn compute_proof_single(&self, p: &CtPoly, x: &CtFr) -> Result { + if p.coeffs.is_empty() { + return Err(String::from("Polynomial must not be empty")); + } + + // `-(x0^n)`, where `n` is `1` + let divisor_0 = x.negate(); + + // Calculate `q = p / (x^n - x0^n)` for our reduced case (see `compute_proof_multi` for + // generic implementation) + let mut out_coeffs = Vec::from(&p.coeffs[1..]); + for i in (1..out_coeffs.len()).rev() { + let tmp = out_coeffs[i].mul(&divisor_0); + out_coeffs[i - 1] = out_coeffs[i - 1].sub(&tmp); + } + + let q = CtPoly { coeffs: out_coeffs }; + + let ret = self.commit_to_poly(&q)?; + + Ok(ret) + } + + fn check_proof_single( + &self, + com: &CtG1, + proof: &CtG1, + x: &CtFr, + y: &CtFr, + ) -> Result { + let x_g2: CtG2 = G2_GENERATOR.mul(x); + let s_minus_x: CtG2 = self.secret_g2[1].sub(&x_g2); + let y_g1 = G1_GENERATOR.mul(y); + let commitment_minus_y: CtG1 = com.sub(&y_g1); + + Ok(pairings_verify( + &commitment_minus_y, + &G2_GENERATOR, + proof, + &s_minus_x, + )) + } + + fn compute_proof_multi(&self, p: &CtPoly, x0: &CtFr, n: usize) -> Result { + if p.coeffs.is_empty() { + return Err(String::from("Polynomial must not be empty")); + } + + if !n.is_power_of_two() { + return Err(String::from("n must be a power of two")); + } + + // Construct x^n - x0^n = (x - x0.w^0)(x - x0.w^1)...(x - x0.w^(n-1)) + let mut divisor = CtPoly { + coeffs: Vec::with_capacity(n + 1), + }; + + // -(x0^n) + let x_pow_n = x0.pow(n); + + divisor.coeffs.push(x_pow_n.negate()); + + // Zeros + for _ in 1..n { + divisor.coeffs.push(Fr::zero()); + } + + // x^n + divisor.coeffs.push(Fr::one()); + + let mut new_polina = p.clone(); + + // Calculate q = p / (x^n - x0^n) + // let q = p.div(&divisor).unwrap(); + let q = new_polina.div(&divisor)?; + + let ret = self.commit_to_poly(&q)?; + + Ok(ret) + } + + fn check_proof_multi( + &self, + com: &CtG1, + proof: &CtG1, + x: &CtFr, + ys: &[CtFr], + n: usize, + ) -> Result { + if !n.is_power_of_two() { + return Err(String::from("n is not a power of two")); + } + + // Interpolate at a coset. + let mut interp = CtPoly { + coeffs: self.fs.fft_fr(ys, true)?, + }; + + let inv_x = x.inverse(); // Not euclidean? + let mut inv_x_pow = inv_x; + for i in 1..n { + interp.coeffs[i] = interp.coeffs[i].mul(&inv_x_pow); + inv_x_pow = inv_x_pow.mul(&inv_x); + } + + // [x^n]_2 + let x_pow = inv_x_pow.inverse(); + + let xn2 = G2_GENERATOR.mul(&x_pow); + + // [s^n - x^n]_2 + let xn_minus_yn = self.secret_g2[n].sub(&xn2); + + // [interpolation_polynomial(s)]_1 + let is1 = self.commit_to_poly(&interp).unwrap(); + + // [commitment - interpolation_polynomial(s)]_1 = [commit]_1 - [interpolation_polynomial(s)]_1 + let commit_minus_interp = com.sub(&is1); + + let ret = pairings_verify(&commit_minus_interp, &G2_GENERATOR, proof, &xn_minus_yn); + + Ok(ret) + } + + fn get_expanded_roots_of_unity_at(&self, i: usize) -> CtFr { + self.fs.get_expanded_roots_of_unity_at(i) + } + + fn get_roots_of_unity_at(&self, i: usize) -> CtFr { + self.fs.get_roots_of_unity_at(i) + } + + fn get_fft_settings(&self) -> &CtFFTSettings { + &self.fs + } + + fn get_g1_secret(&self) -> &[CtG1] { + &self.secret_g1 + } + + fn get_g2_secret(&self) -> &[CtG2] { + &self.secret_g2 + } + + fn get_precomputation(&self) -> Option<&PrecomputationTable> { + self.precomputation.as_ref() + } +} diff --git a/constantine/src/types/mod.rs b/constantine/src/types/mod.rs new file mode 100644 index 00000000..3be18fd9 --- /dev/null +++ b/constantine/src/types/mod.rs @@ -0,0 +1,9 @@ +pub mod fft_settings; +pub mod fk20_multi_settings; +pub mod fk20_single_settings; +pub mod fp; +pub mod fr; +pub mod g1; +pub mod g2; +pub mod kzg_settings; +pub mod poly; diff --git a/constantine/src/types/poly.rs b/constantine/src/types/poly.rs new file mode 100644 index 00000000..f248a7b2 --- /dev/null +++ b/constantine/src/types/poly.rs @@ -0,0 +1,406 @@ +extern crate alloc; + +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; + +use kzg::common_utils::{log2_pow2, log2_u64, next_pow_of_2}; +use kzg::{FFTFr, FFTSettings, FFTSettingsPoly, Fr, Poly}; + +use crate::consts::SCALE_FACTOR; +use crate::types::fft_settings::CtFFTSettings; +use crate::types::fr::CtFr; + +#[derive(Debug, Clone, Eq, PartialEq, Default)] +pub struct CtPoly { + pub coeffs: Vec, +} + +impl Poly for CtPoly { + fn new(size: usize) -> Self { + Self { + coeffs: vec![CtFr::default(); size], + } + } + + fn get_coeff_at(&self, i: usize) -> CtFr { + self.coeffs[i] + } + + fn set_coeff_at(&mut self, i: usize, x: &CtFr) { + self.coeffs[i] = *x + } + + fn get_coeffs(&self) -> &[CtFr] { + &self.coeffs + } + + fn len(&self) -> usize { + self.coeffs.len() + } + + fn eval(&self, x: &CtFr) -> CtFr { + if self.coeffs.is_empty() { + return CtFr::zero(); + } else if x.is_zero() { + return self.coeffs[0]; + } + + let mut ret = self.coeffs[self.coeffs.len() - 1]; + let mut i = self.coeffs.len() - 2; + loop { + let temp = ret.mul(x); + ret = temp.add(&self.coeffs[i]); + + if i == 0 { + break; + } + i -= 1; + } + + ret + } + + fn scale(&mut self) { + let scale_factor = CtFr::from_u64(SCALE_FACTOR); + let inv_factor = scale_factor.inverse(); + + let mut factor_power = CtFr::one(); + for i in 0..self.coeffs.len() { + factor_power = factor_power.mul(&inv_factor); + self.coeffs[i] = self.coeffs[i].mul(&factor_power); + } + } + + fn unscale(&mut self) { + let scale_factor = CtFr::from_u64(SCALE_FACTOR); + + let mut factor_power = CtFr::one(); + for i in 0..self.coeffs.len() { + factor_power = factor_power.mul(&scale_factor); + self.coeffs[i] = self.coeffs[i].mul(&factor_power); + } + } + + // TODO: analyze how algo works + fn inverse(&mut self, output_len: usize) -> Result { + if output_len == 0 { + return Err(String::from("Can't produce a zero-length result")); + } else if self.coeffs.is_empty() { + return Err(String::from("Can't inverse a zero-length poly")); + } else if self.coeffs[0].is_zero() { + return Err(String::from( + "First coefficient of polynomial mustn't be zero", + )); + } + + let mut ret = CtPoly { + coeffs: vec![CtFr::zero(); output_len], + }; + // If the input polynomial is constant, the remainder of the series is zero + if self.coeffs.len() == 1 { + ret.coeffs[0] = self.coeffs[0].eucl_inverse(); + + return Ok(ret); + } + + let maxd = output_len - 1; + + // Max space for multiplications is (2 * length - 1) + // Don't need the following as its recalculated inside + // let scale: usize = log2_pow2(next_pow_of_2(2 * output_len - 1)); + // let fft_settings = FsFFTSettings::new(scale).unwrap(); + + // To store intermediate results + + // Base case for d == 0 + ret.coeffs[0] = self.coeffs[0].eucl_inverse(); + let mut d: usize = 0; + let mut mask: usize = 1 << log2_u64(maxd); + while mask != 0 { + d = 2 * d + usize::from((maxd & mask) != 0); + mask >>= 1; + + // b.c -> tmp0 (we're using out for c) + // tmp0.length = min_u64(d + 1, b->length + output->length - 1); + let len_temp = (d + 1).min(self.len() + output_len - 1); + let mut tmp0 = self.mul(&ret, len_temp).unwrap(); + + // 2 - b.c -> tmp0 + for i in 0..tmp0.len() { + tmp0.coeffs[i] = tmp0.coeffs[i].negate(); + } + let fr_two = Fr::from_u64(2); + tmp0.coeffs[0] = tmp0.coeffs[0].add(&fr_two); + + // c.(2 - b.c) -> tmp1; + let tmp1 = ret.mul(&tmp0, d + 1).unwrap(); + + for i in 0..tmp1.len() { + ret.coeffs[i] = tmp1.coeffs[i]; + } + } + + if d + 1 != output_len { + return Err(String::from("D + 1 must be equal to output_len")); + } + + Ok(ret) + } + + fn div(&mut self, divisor: &Self) -> Result { + if divisor.len() >= self.len() || divisor.len() < 128 { + // Tunable parameter + self.long_div(divisor) + } else { + self.fast_div(divisor) + } + } + + fn long_div(&mut self, divisor: &Self) -> Result { + if divisor.coeffs.is_empty() { + return Err(String::from("Can't divide by zero")); + } else if divisor.coeffs[divisor.coeffs.len() - 1].is_zero() { + return Err(String::from("Highest coefficient must be non-zero")); + } + + let out_length = self.poly_quotient_length(divisor); + if out_length == 0 { + return Ok(CtPoly { coeffs: vec![] }); + } + + // Special case for divisor.len() == 2 + if divisor.len() == 2 { + let divisor_0 = divisor.coeffs[0]; + let divisor_1 = divisor.coeffs[1]; + + let mut out_coeffs = Vec::from(&self.coeffs[1..]); + for i in (1..out_length).rev() { + out_coeffs[i] = out_coeffs[i].div(&divisor_1).unwrap(); + + let tmp = out_coeffs[i].mul(&divisor_0); + out_coeffs[i - 1] = out_coeffs[i - 1].sub(&tmp); + } + + out_coeffs[0] = out_coeffs[0].div(&divisor_1).unwrap(); + + Ok(CtPoly { coeffs: out_coeffs }) + } else { + let mut out: CtPoly = CtPoly { + coeffs: vec![CtFr::default(); out_length], + }; + + let mut a_pos = self.len() - 1; + let b_pos = divisor.len() - 1; + let mut diff = a_pos - b_pos; + + let mut a = self.coeffs.clone(); + + while diff > 0 { + out.coeffs[diff] = a[a_pos].div(&divisor.coeffs[b_pos]).unwrap(); + + for i in 0..(b_pos + 1) { + let tmp = out.coeffs[diff].mul(&divisor.coeffs[i]); + a[diff + i] = a[diff + i].sub(&tmp); + } + + diff -= 1; + a_pos -= 1; + } + + out.coeffs[0] = a[a_pos].div(&divisor.coeffs[b_pos]).unwrap(); + Ok(out) + } + } + + fn fast_div(&mut self, divisor: &Self) -> Result { + if divisor.coeffs.is_empty() { + return Err(String::from("Cant divide by zero")); + } else if divisor.coeffs[divisor.coeffs.len() - 1].is_zero() { + return Err(String::from("Highest coefficient must be non-zero")); + } + + let m: usize = self.len() - 1; + let n: usize = divisor.len() - 1; + + // If the divisor is larger than the dividend, the result is zero-length + if n > m { + return Ok(CtPoly { coeffs: Vec::new() }); + } + + // Special case for divisor.length == 1 (it's a constant) + if divisor.len() == 1 { + let mut out = CtPoly { + coeffs: vec![CtFr::zero(); self.len()], + }; + for i in 0..out.len() { + out.coeffs[i] = self.coeffs[i].div(&divisor.coeffs[0]).unwrap(); + } + return Ok(out); + } + + let mut a_flip = self.flip().unwrap(); + let mut b_flip = divisor.flip().unwrap(); + + let inv_b_flip = b_flip.inverse(m - n + 1).unwrap(); + let q_flip = a_flip.mul(&inv_b_flip, m - n + 1).unwrap(); + + let out = q_flip.flip().unwrap(); + Ok(out) + } + + fn mul_direct(&mut self, multiplier: &Self, output_len: usize) -> Result { + if self.len() == 0 || multiplier.len() == 0 { + return Ok(CtPoly::new(0)); + } + + let a_degree = self.len() - 1; + let b_degree = multiplier.len() - 1; + + let mut ret = CtPoly { + coeffs: vec![Fr::zero(); output_len], + }; + + // Truncate the output to the length of the output polynomial + for i in 0..(a_degree + 1) { + let mut j = 0; + while (j <= b_degree) && ((i + j) < output_len) { + let tmp = self.coeffs[i].mul(&multiplier.coeffs[j]); + let tmp = ret.coeffs[i + j].add(&tmp); + ret.coeffs[i + j] = tmp; + + j += 1; + } + } + + Ok(ret) + } +} + +impl FFTSettingsPoly for CtFFTSettings { + fn poly_mul_fft( + a: &CtPoly, + b: &CtPoly, + len: usize, + _fs: Option<&CtFFTSettings>, + ) -> Result { + b.mul_fft(a, len) + } +} + +impl CtPoly { + pub fn _poly_norm(&self) -> Self { + let mut ret = self.clone(); + + let mut temp_len: usize = ret.coeffs.len(); + while temp_len > 0 && ret.coeffs[temp_len - 1].is_zero() { + temp_len -= 1; + } + + if temp_len == 0 { + ret.coeffs = Vec::new(); + } else { + ret.coeffs = ret.coeffs[0..temp_len].to_vec(); + } + + ret + } + + pub fn poly_quotient_length(&self, divisor: &Self) -> usize { + if self.len() >= divisor.len() { + self.len() - divisor.len() + 1 + } else { + 0 + } + } + + pub fn pad(&self, out_length: usize) -> Self { + let mut ret = Self { + coeffs: vec![CtFr::zero(); out_length], + }; + + for i in 0..self.len().min(out_length) { + ret.coeffs[i] = self.coeffs[i]; + } + + ret + } + + pub fn flip(&self) -> Result { + let mut ret = CtPoly { + coeffs: vec![CtFr::default(); self.len()], + }; + for i in 0..self.len() { + ret.coeffs[i] = self.coeffs[self.coeffs.len() - i - 1] + } + + Ok(ret) + } + + pub fn mul_fft(&self, multiplier: &Self, output_len: usize) -> Result { + let length = next_pow_of_2(self.len() + multiplier.len() - 1); + + let scale = log2_pow2(length); + let fft_settings = CtFFTSettings::new(scale).unwrap(); + + let a_pad = self.pad(length); + let b_pad = multiplier.pad(length); + + let a_fft: Vec; + let b_fft: Vec; + + #[cfg(feature = "parallel")] + { + if length > 1024 { + let mut a_fft_temp = vec![]; + let mut b_fft_temp = vec![]; + + rayon::join( + || a_fft_temp = fft_settings.fft_fr(&a_pad.coeffs, false).unwrap(), + || b_fft_temp = fft_settings.fft_fr(&b_pad.coeffs, false).unwrap(), + ); + + a_fft = a_fft_temp; + b_fft = b_fft_temp; + } else { + a_fft = fft_settings.fft_fr(&a_pad.coeffs, false).unwrap(); + b_fft = fft_settings.fft_fr(&b_pad.coeffs, false).unwrap(); + } + } + + #[cfg(not(feature = "parallel"))] + { + // Convert Poly to values + a_fft = fft_settings.fft_fr(&a_pad.coeffs, false).unwrap(); + b_fft = fft_settings.fft_fr(&b_pad.coeffs, false).unwrap(); + } + + // Multiply two value ranges + let mut ab_fft = a_fft; + ab_fft.iter_mut().zip(b_fft).for_each(|(a, b)| { + *a = a.mul(&b); + }); + + // Convert value range multiplication to a resulting polynomial + let ab = fft_settings.fft_fr(&ab_fft, true).unwrap(); + drop(ab_fft); + + let mut ret = CtPoly { + coeffs: vec![CtFr::zero(); output_len], + }; + + let range = ..output_len.min(length); + ret.coeffs[range].clone_from_slice(&ab[range]); + + Ok(ret) + } + + pub fn mul(&mut self, multiplier: &Self, output_len: usize) -> Result { + if self.len() < 64 || multiplier.len() < 64 || output_len < 128 { + // Tunable parameter + self.mul_direct(multiplier, output_len) + } else { + self.mul_fft(multiplier, output_len) + } + } +} diff --git a/constantine/src/utils.rs b/constantine/src/utils.rs new file mode 100644 index 00000000..a4ab9e8e --- /dev/null +++ b/constantine/src/utils.rs @@ -0,0 +1,39 @@ +extern crate alloc; + +use alloc::vec::Vec; + +use kzg::eip_4844::hash_to_bls_field; +use kzg::{Fr, G1Mul, G2Mul}; + +use crate::consts::{G1_GENERATOR, G2_GENERATOR}; +use crate::types::g1::CtG1; +use crate::types::g2::CtG2; + +pub fn generate_trusted_setup(n: usize, secret: [u8; 32usize]) -> (Vec, Vec) { + let s = hash_to_bls_field(&secret); + let mut s_pow = Fr::one(); + + let mut s1 = Vec::with_capacity(n); + let mut s2 = Vec::with_capacity(n); + + for _ in 0..n { + s1.push(G1_GENERATOR.mul(&s_pow)); + s2.push(G2_GENERATOR.mul(&s_pow)); + + s_pow = s_pow.mul(&s); + } + + (s1, s2) +} + +pub fn ptr_transmute(t: &T) -> *const U { + assert_eq!(core::mem::size_of::(), core::mem::size_of::()); + + t as *const T as *const U +} + +pub fn ptr_transmute_mut(t: &mut T) -> *mut U { + assert_eq!(core::mem::size_of::(), core::mem::size_of::()); + + t as *mut T as *mut U +} diff --git a/constantine/src/zero_poly.rs b/constantine/src/zero_poly.rs new file mode 100644 index 00000000..713058e8 --- /dev/null +++ b/constantine/src/zero_poly.rs @@ -0,0 +1,314 @@ +extern crate alloc; + +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; +use core::cmp::{min, Ordering}; + +use kzg::{common_utils::next_pow_of_2, FFTFr, Fr, ZeroPoly}; + +use crate::types::fft_settings::CtFFTSettings; +use crate::types::fr::CtFr; +use crate::types::poly::CtPoly; + +#[cfg(feature = "parallel")] +use rayon::prelude::*; +use smallvec::{smallvec, SmallVec}; + +// Can be tuned & optimized (must be a power of 2) +const DEGREE_OF_PARTIAL: usize = 256; +// Can be tuned & optimized (but must be a power of 2) +const REDUCTION_FACTOR: usize = 4; + +/// Pad given poly it with zeros to new length +pub fn pad_poly(mut poly: Vec, new_length: usize) -> Result, String> { + if new_length < poly.len() { + return Err(String::from( + "new_length must be longer or equal to poly length", + )); + } + + poly.resize(new_length, CtFr::zero()); + + Ok(poly) +} + +/// Pad given poly coefficients it with zeros to new length +pub fn pad_poly_coeffs( + mut coeffs: SmallVec<[T; N]>, + new_length: usize, +) -> Result, String> +where + T: Default + Clone, +{ + if new_length < coeffs.len() { + return Err(String::from( + "new_length must be longer or equal to coeffs length", + )); + } + + coeffs.resize(new_length, T::default()); + + Ok(coeffs) +} + +impl CtFFTSettings { + fn do_zero_poly_mul_partial( + &self, + idxs: &[usize], + stride: usize, + ) -> Result, String> { + if idxs.is_empty() { + return Err(String::from("idx array must not be empty")); + } + + // Makes use of long multiplication in terms of (x - w_0)(x - w_1).. + let mut coeffs = SmallVec::<[CtFr; DEGREE_OF_PARTIAL]>::new(); + + // For the first member, store -w_0 as constant term + coeffs.push(self.expanded_roots_of_unity[idxs[0] * stride].negate()); + + for (i, idx) in idxs.iter().copied().enumerate().skip(1) { + // For member (x - w_i) take coefficient as -(w_i + w_{i-1} + ...) + let neg_di = self.expanded_roots_of_unity[idx * stride].negate(); + coeffs.push(neg_di.add(&coeffs[i - 1])); + + // Multiply all previous members by (x - w_i) + // It equals multiplying by - w_i and adding x^(i - 1) coefficient (implied multiplication by x) + for j in (1..i).rev() { + coeffs[j] = coeffs[j].mul(&neg_di).add(&coeffs[j - 1]); + } + + // Multiply x^0 member by - w_i + coeffs[0] = coeffs[0].mul(&neg_di); + } + + coeffs.resize(idxs.len() + 1, CtFr::one()); + + Ok(coeffs) + } + + fn reduce_partials( + &self, + domain_size: usize, + partial_coeffs: SmallVec<[SmallVec<[CtFr; DEGREE_OF_PARTIAL]>; REDUCTION_FACTOR]>, + ) -> Result, String> { + if !domain_size.is_power_of_two() { + return Err(String::from("Expected domain size to be a power of 2")); + } + + if partial_coeffs.is_empty() { + return Err(String::from("partials must not be empty")); + } + + // Calculate the resulting polynomial degree + // E.g. (a * x^n + ...) (b * x^m + ...) has a degree of x^(n+m) + let out_degree = partial_coeffs + .iter() + .map(|partial| { + // TODO: Not guaranteed by function signature that this doesn't underflow + partial.len() - 1 + }) + .sum::(); + + if out_degree + 1 > domain_size { + return Err(String::from( + "Out degree is longer than possible polynomial size in domain", + )); + } + + let mut partial_coeffs = partial_coeffs.into_iter(); + + // Pad all partial polynomials to same length, compute their FFT and multiply them together + let mut padded_partial = pad_poly_coeffs( + partial_coeffs + .next() + .expect("Not empty, checked above; qed"), + domain_size, + )?; + let mut eval_result: SmallVec<[CtFr; DEGREE_OF_PARTIAL]> = + smallvec![CtFr::zero(); domain_size]; + self.fft_fr_output(&padded_partial, false, &mut eval_result)?; + + for partial in partial_coeffs { + padded_partial = pad_poly_coeffs(partial, domain_size)?; + let mut evaluated_partial: SmallVec<[CtFr; DEGREE_OF_PARTIAL]> = + smallvec![CtFr::zero(); domain_size]; + self.fft_fr_output(&padded_partial, false, &mut evaluated_partial)?; + + eval_result + .iter_mut() + .zip(evaluated_partial.iter()) + .for_each(|(eval_result, evaluated_partial)| { + *eval_result = eval_result.mul(evaluated_partial); + }); + } + + let mut coeffs = smallvec![CtFr::zero(); domain_size]; + // Apply an inverse FFT to produce a new poly. Limit its size to out_degree + 1 + self.fft_fr_output(&eval_result, true, &mut coeffs)?; + coeffs.truncate(out_degree + 1); + + Ok(coeffs) + } +} + +impl ZeroPoly for CtFFTSettings { + fn do_zero_poly_mul_partial(&self, idxs: &[usize], stride: usize) -> Result { + self.do_zero_poly_mul_partial(idxs, stride) + .map(|coeffs| CtPoly { + coeffs: coeffs.into_vec(), + }) + } + + fn reduce_partials(&self, domain_size: usize, partials: &[CtPoly]) -> Result { + self.reduce_partials( + domain_size, + partials + .iter() + .map(|partial| SmallVec::from_slice(&partial.coeffs)) + .collect(), + ) + .map(|coeffs| CtPoly { + coeffs: coeffs.into_vec(), + }) + } + + fn zero_poly_via_multiplication( + &self, + domain_size: usize, + missing_idxs: &[usize], + ) -> Result<(Vec, CtPoly), String> { + let zero_eval: Vec; + let mut zero_poly: CtPoly; + + if missing_idxs.is_empty() { + zero_eval = Vec::new(); + zero_poly = CtPoly { coeffs: Vec::new() }; + return Ok((zero_eval, zero_poly)); + } + + if missing_idxs.len() >= domain_size { + return Err(String::from("Missing idxs greater than domain size")); + } else if domain_size > self.max_width { + return Err(String::from( + "Domain size greater than fft_settings.max_width", + )); + } else if !domain_size.is_power_of_two() { + return Err(String::from("Domain size must be a power of 2")); + } + + let missing_per_partial = DEGREE_OF_PARTIAL - 1; // Number of missing idxs needed per partial + let domain_stride = self.max_width / domain_size; + + let mut partial_count = 1 + (missing_idxs.len() - 1) / missing_per_partial; // TODO: explain why -1 is used here + + let next_pow: usize = next_pow_of_2(partial_count * DEGREE_OF_PARTIAL); + let domain_ceiling = min(next_pow, domain_size); + // Calculate zero poly + if missing_idxs.len() <= missing_per_partial { + // When all idxs fit into a single multiplication + zero_poly = CtPoly { + coeffs: self + .do_zero_poly_mul_partial(missing_idxs, domain_stride)? + .into_vec(), + }; + } else { + // Otherwise, construct a set of partial polynomials + // Save all constructed polynomials in a shared 'work' vector + let mut work = vec![CtFr::zero(); next_pow]; + + let mut partial_lens = vec![DEGREE_OF_PARTIAL; partial_count]; + + #[cfg(not(feature = "parallel"))] + let iter = missing_idxs + .chunks(missing_per_partial) + .zip(work.chunks_exact_mut(DEGREE_OF_PARTIAL)); + #[cfg(feature = "parallel")] + let iter = missing_idxs + .par_chunks(missing_per_partial) + .zip(work.par_chunks_exact_mut(DEGREE_OF_PARTIAL)); + // Insert all generated partial polynomials at degree_of_partial intervals in work vector + iter.for_each(|(missing_idxs, work)| { + let partial_coeffs = self + .do_zero_poly_mul_partial(missing_idxs, domain_stride) + .expect("`missing_idxs` is guaranteed to not be empty; qed"); + + let partial_coeffs = pad_poly_coeffs(partial_coeffs, DEGREE_OF_PARTIAL).expect( + "`partial.coeffs.len()` (same as `missing_idxs.len() + 1`) is \ + guaranteed to be at most `degree_of_partial`; qed", + ); + work[..DEGREE_OF_PARTIAL].copy_from_slice(&partial_coeffs); + }); + + // Adjust last length to match its actual length + partial_lens[partial_count - 1] = + 1 + missing_idxs.len() - (partial_count - 1) * missing_per_partial; + + // Reduce all vectors into one by reducing them w/ varying size multiplications + while partial_count > 1 { + let reduced_count = 1 + (partial_count - 1) / REDUCTION_FACTOR; + let partial_size = next_pow_of_2(partial_lens[0]); + + // Step over polynomial space and produce larger multiplied polynomials + for i in 0..reduced_count { + let start = i * REDUCTION_FACTOR; + let out_end = min((start + REDUCTION_FACTOR) * partial_size, domain_ceiling); + let reduced_len = min(out_end - start * partial_size, domain_size); + let partials_num = min(REDUCTION_FACTOR, partial_count - start); + + // Calculate partial views from lens and offsets + // Also update offsets to match current iteration + let partial_offset = start * partial_size; + let mut partial_coeffs = SmallVec::new(); + for (partial_offset, partial_len) in (partial_offset..) + .step_by(partial_size) + .zip(partial_lens.iter().skip(i).copied()) + .take(partials_num) + { + // We know the capacity required in `reduce_partials()` call below to avoid + // re-allocation + let mut coeffs = SmallVec::with_capacity(reduced_len); + coeffs.extend_from_slice(&work[partial_offset..][..partial_len]); + partial_coeffs.push(coeffs); + } + + if partials_num > 1 { + let mut reduced_coeffs = + self.reduce_partials(reduced_len, partial_coeffs)?; + // Update partial length to match its length after reduction + partial_lens[i] = reduced_coeffs.len(); + reduced_coeffs = + pad_poly_coeffs(reduced_coeffs, partial_size * partials_num)?; + work[partial_offset..][..reduced_coeffs.len()] + .copy_from_slice(&reduced_coeffs); + } else { + // Instead of keeping track of remaining polynomials, reuse i'th partial for start'th one + partial_lens[i] = partial_lens[start]; + } + } + + // Number of steps done equals the number of polynomials that we still need to reduce together + partial_count = reduced_count; + } + + zero_poly = CtPoly { coeffs: work }; + } + + // Pad resulting poly to expected + match zero_poly.coeffs.len().cmp(&domain_size) { + Ordering::Less => { + zero_poly.coeffs = pad_poly(zero_poly.coeffs, domain_size)?; + } + Ordering::Equal => {} + Ordering::Greater => { + zero_poly.coeffs.truncate(domain_size); + } + } + + // Evaluate calculated poly + zero_eval = self.fft_fr(&zero_poly.coeffs, false)?; + + Ok((zero_eval, zero_poly)) + } +} diff --git a/constantine/tests/batch_adder.rs b/constantine/tests/batch_adder.rs new file mode 100644 index 00000000..f42908e1 --- /dev/null +++ b/constantine/tests/batch_adder.rs @@ -0,0 +1,74 @@ +#[cfg(test)] +mod tests { + use kzg_bench::tests::msm::batch_adder::{ + test_batch_add, test_batch_add_indexed, test_batch_add_indexed_single_bucket, + test_batch_add_step_n, test_phase_one_p_add_p, test_phase_one_p_add_q, + test_phase_one_p_add_q_twice, test_phase_one_zero_or_neg, test_phase_two_p_add_neg, + test_phase_two_p_add_p, test_phase_two_p_add_q, test_phase_two_zero_add_p, + }; + use rust_kzg_constantine::types::{ + fp::CtFp, + g1::{CtG1, CtG1Affine}, + }; + // use rust_kzg_constantine::types:: + + #[test] + fn test_phase_one_zero_or_neg_() { + test_phase_one_zero_or_neg::(); + } + + #[test] + fn test_phase_one_p_add_p_() { + test_phase_one_p_add_p::(); + } + + #[test] + fn test_phase_one_p_add_q_() { + test_phase_one_p_add_q::(); + } + + #[test] + fn test_phase_one_p_add_q_twice_() { + test_phase_one_p_add_q_twice::(); + } + + #[test] + fn test_phase_two_zero_add_p_() { + test_phase_two_zero_add_p::(); + } + + #[test] + fn test_phase_two_p_add_neg_() { + test_phase_two_p_add_neg::(); + } + + #[test] + fn test_phase_two_p_add_q_() { + test_phase_two_p_add_q::(); + } + + #[test] + fn test_phase_two_p_add_p_() { + test_phase_two_p_add_p::(); + } + + #[test] + fn test_batch_add_() { + test_batch_add::(); + } + + #[test] + fn test_batch_add_step_n_() { + test_batch_add_step_n::(); + } + + #[test] + fn test_batch_add_indexed_() { + test_batch_add_indexed::(); + } + + #[test] + fn test_batch_add_indexed_single_bucket_() { + test_batch_add_indexed_single_bucket::(); + } +} diff --git a/constantine/tests/bls12_381.rs b/constantine/tests/bls12_381.rs new file mode 100644 index 00000000..cb0743ce --- /dev/null +++ b/constantine/tests/bls12_381.rs @@ -0,0 +1,122 @@ +#[cfg(test)] +mod tests { + use kzg::common_utils::log_2_byte; + use kzg_bench::tests::bls12_381::{ + fr_div_by_zero, fr_div_works, fr_equal_works, fr_from_uint64_works, fr_is_null_works, + fr_is_one_works, fr_is_zero_works, fr_negate_works, fr_pow_works, fr_uint64s_roundtrip, + g1_identity_is_identity, g1_identity_is_infinity, g1_make_linear_combination, + g1_random_linear_combination, log_2_byte_works, p1_mul_works, p1_sub_works, + p2_add_or_dbl_works, p2_mul_works, p2_sub_works, pairings_work, + }; + + use rust_kzg_constantine::kzg_proofs::{g1_linear_combination, pairings_verify}; + use rust_kzg_constantine::types::fp::CtFp; + use rust_kzg_constantine::types::fr::CtFr; + use rust_kzg_constantine::types::g1::{CtG1, CtG1Affine}; + use rust_kzg_constantine::types::g2::CtG2; + + #[test] + fn log_2_byte_works_() { + log_2_byte_works(&log_2_byte) + } + + #[test] + fn fr_is_null_works_() { + fr_is_null_works::() + } + + #[test] + fn fr_is_zero_works_() { + fr_is_zero_works::() + } + + #[test] + fn fr_is_one_works_() { + fr_is_one_works::() + } + + #[test] + fn fr_from_uint64_works_() { + fr_from_uint64_works::() + } + + #[test] + fn fr_equal_works_() { + fr_equal_works::() + } + + #[test] + fn fr_negate_works_() { + fr_negate_works::() + } + + #[test] + fn fr_pow_works_() { + fr_pow_works::() + } + + #[test] + fn fr_div_works_() { + fr_div_works::() + } + + #[test] + fn fr_div_by_zero_() { + fr_div_by_zero::() + } + + #[test] + fn fr_uint64s_roundtrip_() { + fr_uint64s_roundtrip::() + } + + #[test] + fn p1_mul_works_() { + p1_mul_works::() + } + + #[test] + fn p1_sub_works_() { + p1_sub_works::() + } + + #[test] + fn p2_add_or_dbl_works_() { + p2_add_or_dbl_works::() + } + + #[test] + fn p2_mul_works_() { + p2_mul_works::() + } + + #[test] + fn p2_sub_works_() { + p2_sub_works::() + } + + #[test] + fn g1_identity_is_infinity_() { + g1_identity_is_infinity::() + } + + #[test] + fn g1_identity_is_identity_() { + g1_identity_is_identity::() + } + + #[test] + fn g1_make_linear_combination_() { + g1_make_linear_combination::(&g1_linear_combination) + } + + #[test] + fn g1_random_linear_combination_() { + g1_random_linear_combination::(&g1_linear_combination) + } + + #[test] + fn pairings_work_() { + pairings_work::(&pairings_verify) + } +} diff --git a/constantine/tests/c_bindings.rs b/constantine/tests/c_bindings.rs new file mode 100644 index 00000000..0aead562 --- /dev/null +++ b/constantine/tests/c_bindings.rs @@ -0,0 +1,80 @@ +#[cfg(test)] +mod tests { + use kzg_bench::tests::c_bindings::{ + blob_to_kzg_commitment_invalid_blob_test, + compute_blob_kzg_proof_commitment_is_point_at_infinity_test, + compute_blob_kzg_proof_invalid_blob_test, free_trusted_setup_null_ptr_test, + free_trusted_setup_set_all_values_to_null_test, + load_trusted_setup_file_invalid_format_test, load_trusted_setup_file_valid_format_test, + load_trusted_setup_invalid_form_test, load_trusted_setup_invalid_g1_byte_length_test, + load_trusted_setup_invalid_g1_point_test, load_trusted_setup_invalid_g2_byte_length_test, + load_trusted_setup_invalid_g2_point_test, + }; + use rust_kzg_constantine::eip_4844::{ + blob_to_kzg_commitment, compute_blob_kzg_proof, free_trusted_setup, load_trusted_setup, + load_trusted_setup_file, + }; + + #[test] + fn blob_to_kzg_commitment_invalid_blob() { + blob_to_kzg_commitment_invalid_blob_test(blob_to_kzg_commitment, load_trusted_setup_file); + } + + #[test] + fn load_trusted_setup_invalid_g1_byte_length() { + load_trusted_setup_invalid_g1_byte_length_test(load_trusted_setup); + } + + #[test] + fn load_trusted_setup_invalid_g1_point() { + load_trusted_setup_invalid_g1_point_test(load_trusted_setup); + } + + #[test] + fn load_trusted_setup_invalid_g2_byte_length() { + load_trusted_setup_invalid_g2_byte_length_test(load_trusted_setup); + } + + #[test] + fn load_trusted_setup_invalid_g2_point() { + load_trusted_setup_invalid_g2_point_test(load_trusted_setup); + } + + #[test] + fn load_trusted_setup_invalid_form() { + load_trusted_setup_invalid_form_test(load_trusted_setup); + } + + #[test] + fn load_trusted_setup_file_invalid_format() { + load_trusted_setup_file_invalid_format_test(load_trusted_setup_file); + } + + #[test] + fn load_trusted_setup_file_valid_format() { + load_trusted_setup_file_valid_format_test(load_trusted_setup_file); + } + + #[test] + fn free_trusted_setup_null_ptr() { + free_trusted_setup_null_ptr_test(free_trusted_setup); + } + + #[test] + fn free_trusted_setup_set_all_values_to_null() { + free_trusted_setup_set_all_values_to_null_test(free_trusted_setup, load_trusted_setup_file); + } + + #[test] + fn compute_blob_kzg_proof_invalid_blob() { + compute_blob_kzg_proof_invalid_blob_test(compute_blob_kzg_proof, load_trusted_setup_file); + } + + #[test] + fn compute_blob_kzg_proof_commitment_is_point_at_infinity() { + compute_blob_kzg_proof_commitment_is_point_at_infinity_test( + compute_blob_kzg_proof, + load_trusted_setup_file, + ); + } +} diff --git a/constantine/tests/consts.rs b/constantine/tests/consts.rs new file mode 100644 index 00000000..94c65026 --- /dev/null +++ b/constantine/tests/consts.rs @@ -0,0 +1,45 @@ +// #[path = "./local_tests/local_consts.rs"] +// pub mod local_consts; + +#[cfg(test)] +mod tests { + use kzg_bench::tests::consts::{ + expand_roots_is_plausible, new_fft_settings_is_plausible, roots_of_unity_are_plausible, + roots_of_unity_is_the_expected_size, roots_of_unity_out_of_bounds_fails, + }; + use rust_kzg_constantine::consts::SCALE2_ROOT_OF_UNITY; + use rust_kzg_constantine::types::fft_settings::{expand_root_of_unity, CtFFTSettings}; + use rust_kzg_constantine::types::fr::CtFr; + + // Shared tests + #[test] + fn roots_of_unity_is_the_expected_size_() { + roots_of_unity_is_the_expected_size(&SCALE2_ROOT_OF_UNITY); + } + + #[test] + fn roots_of_unity_out_of_bounds_fails_() { + roots_of_unity_out_of_bounds_fails::(); + } + + #[test] + fn roots_of_unity_are_plausible_() { + roots_of_unity_are_plausible::(&SCALE2_ROOT_OF_UNITY); + } + + #[test] + fn expand_roots_is_plausible_() { + expand_roots_is_plausible::(&SCALE2_ROOT_OF_UNITY, &expand_root_of_unity); + } + + #[test] + fn new_fft_settings_is_plausible_() { + new_fft_settings_is_plausible::(); + } + + // Local tests + // #[test] + // fn roots_of_unity_repeat_at_stride_() { + // roots_of_unity_repeat_at_stride::(); + // } +} diff --git a/constantine/tests/das.rs b/constantine/tests/das.rs new file mode 100644 index 00000000..f799af7a --- /dev/null +++ b/constantine/tests/das.rs @@ -0,0 +1,16 @@ +#[cfg(test)] +mod tests { + use kzg_bench::tests::das::{das_extension_test_known, das_extension_test_random}; + use rust_kzg_constantine::types::fft_settings::CtFFTSettings; + use rust_kzg_constantine::types::fr::CtFr; + + #[test] + fn das_extension_test_known_() { + das_extension_test_known::(); + } + + #[test] + fn das_extension_test_random_() { + das_extension_test_random::(); + } +} diff --git a/constantine/tests/eip_4844.rs b/constantine/tests/eip_4844.rs new file mode 100644 index 00000000..5c7b4485 --- /dev/null +++ b/constantine/tests/eip_4844.rs @@ -0,0 +1,421 @@ +#[cfg(test)] +mod tests { + use kzg::eip_4844::{ + blob_to_kzg_commitment_rust, blob_to_polynomial, bytes_to_blob, + compute_blob_kzg_proof_rust, compute_kzg_proof_rust, compute_powers, + evaluate_polynomial_in_evaluation_form, verify_blob_kzg_proof_batch_rust, + verify_blob_kzg_proof_rust, verify_kzg_proof_rust, + }; + use kzg::Fr; + + use kzg_bench::tests::eip_4844::{ + blob_to_kzg_commitment_test, bytes_to_bls_field_test, + compute_and_verify_blob_kzg_proof_fails_with_incorrect_proof_test, + compute_and_verify_blob_kzg_proof_test, + compute_and_verify_kzg_proof_fails_with_incorrect_proof_test, + compute_and_verify_kzg_proof_round_trip_test, + compute_and_verify_kzg_proof_within_domain_test, compute_kzg_proof_empty_blob_vector_test, + compute_kzg_proof_incorrect_blob_length_test, + compute_kzg_proof_incorrect_commitments_len_test, + compute_kzg_proof_incorrect_poly_length_test, compute_kzg_proof_incorrect_proofs_len_test, + compute_kzg_proof_test, compute_powers_test, test_vectors_blob_to_kzg_commitment, + test_vectors_compute_blob_kzg_proof, test_vectors_compute_kzg_proof, + test_vectors_verify_blob_kzg_proof, test_vectors_verify_blob_kzg_proof_batch, + test_vectors_verify_kzg_proof, validate_batched_input_test, + verify_kzg_proof_batch_fails_with_incorrect_proof_test, verify_kzg_proof_batch_test, + }; + use rust_kzg_constantine::consts::SCALE2_ROOT_OF_UNITY; + use rust_kzg_constantine::eip_4844::load_trusted_setup_filename_rust; + use rust_kzg_constantine::types::fft_settings::expand_root_of_unity; + use rust_kzg_constantine::types::g1::CtG1Affine; + use rust_kzg_constantine::types::{ + fft_settings::CtFFTSettings, fp::CtFp, fr::CtFr, g1::CtG1, g2::CtG2, + kzg_settings::CtKZGSettings, poly::CtPoly, + }; + + #[test] + pub fn bytes_to_bls_field_test_() { + bytes_to_bls_field_test::(); + } + + #[test] + pub fn compute_powers_test_() { + compute_powers_test::(&compute_powers); + } + + #[test] + pub fn blob_to_kzg_commitment_test_() { + blob_to_kzg_commitment_test::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_rust, + &blob_to_kzg_commitment_rust, + ); + } + + #[test] + pub fn compute_kzg_proof_test_() { + compute_kzg_proof_test::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_rust, + &compute_kzg_proof_rust, + &blob_to_polynomial, + &evaluate_polynomial_in_evaluation_form, + ); + } + + #[test] + pub fn compute_and_verify_kzg_proof_round_trip_test_() { + compute_and_verify_kzg_proof_round_trip_test::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_rust, + &blob_to_kzg_commitment_rust, + &bytes_to_blob, + &compute_kzg_proof_rust, + &blob_to_polynomial, + &evaluate_polynomial_in_evaluation_form, + &verify_kzg_proof_rust, + ); + } + + #[test] + pub fn compute_and_verify_kzg_proof_within_domain_test_() { + compute_and_verify_kzg_proof_within_domain_test::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_rust, + &blob_to_kzg_commitment_rust, + &bytes_to_blob, + &compute_kzg_proof_rust, + &blob_to_polynomial, + &evaluate_polynomial_in_evaluation_form, + &verify_kzg_proof_rust, + ); + } + + #[test] + pub fn compute_and_verify_kzg_proof_fails_with_incorrect_proof_test_() { + compute_and_verify_kzg_proof_fails_with_incorrect_proof_test::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_rust, + &blob_to_kzg_commitment_rust, + &bytes_to_blob, + &compute_kzg_proof_rust, + &blob_to_polynomial, + &evaluate_polynomial_in_evaluation_form, + &verify_kzg_proof_rust, + ); + } + + #[test] + pub fn compute_and_verify_blob_kzg_proof_test_() { + compute_and_verify_blob_kzg_proof_test::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_rust, + &blob_to_kzg_commitment_rust, + &bytes_to_blob, + &compute_blob_kzg_proof_rust, + &verify_blob_kzg_proof_rust, + ); + } + + #[test] + pub fn compute_and_verify_blob_kzg_proof_fails_with_incorrect_proof_test_() { + compute_and_verify_blob_kzg_proof_fails_with_incorrect_proof_test::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_rust, + &blob_to_kzg_commitment_rust, + &bytes_to_blob, + &compute_blob_kzg_proof_rust, + &verify_blob_kzg_proof_rust, + ); + } + + #[test] + pub fn verify_kzg_proof_batch_test_() { + verify_kzg_proof_batch_test::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_rust, + &blob_to_kzg_commitment_rust, + &bytes_to_blob, + &compute_blob_kzg_proof_rust, + &verify_blob_kzg_proof_batch_rust, + ); + } + + #[test] + pub fn verify_kzg_proof_batch_fails_with_incorrect_proof_test_() { + verify_kzg_proof_batch_fails_with_incorrect_proof_test::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_rust, + &blob_to_kzg_commitment_rust, + &bytes_to_blob, + &compute_blob_kzg_proof_rust, + &verify_blob_kzg_proof_batch_rust, + ); + } + + #[test] + pub fn test_vectors_blob_to_kzg_commitment_() { + test_vectors_blob_to_kzg_commitment::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_rust, + &blob_to_kzg_commitment_rust, + &bytes_to_blob, + ); + } + + #[test] + pub fn test_vectors_compute_kzg_proof_() { + test_vectors_compute_kzg_proof::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_rust, + &compute_kzg_proof_rust, + &bytes_to_blob, + ); + } + + #[test] + pub fn test_vectors_compute_blob_kzg_proof_() { + test_vectors_compute_blob_kzg_proof::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_rust, + &bytes_to_blob, + &compute_blob_kzg_proof_rust, + ); + } + + #[test] + pub fn test_vectors_verify_kzg_proof_() { + test_vectors_verify_kzg_proof::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >(&load_trusted_setup_filename_rust, &verify_kzg_proof_rust); + } + + #[test] + pub fn test_vectors_verify_blob_kzg_proof_() { + test_vectors_verify_blob_kzg_proof::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_rust, + &bytes_to_blob, + &verify_blob_kzg_proof_rust, + ); + } + + #[test] + pub fn test_vectors_verify_blob_kzg_proof_batch_() { + test_vectors_verify_blob_kzg_proof_batch::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_rust, + &bytes_to_blob, + &verify_blob_kzg_proof_batch_rust, + ); + } + + #[test] + pub fn expand_root_of_unity_too_long() { + let out = expand_root_of_unity(&CtFr::from_u64_arr(&SCALE2_ROOT_OF_UNITY[1]), 1); + assert!(out.is_err()); + } + + #[test] + pub fn expand_root_of_unity_too_short() { + let out = expand_root_of_unity(&CtFr::from_u64_arr(&SCALE2_ROOT_OF_UNITY[1]), 3); + assert!(out.is_err()); + } + + #[test] + pub fn compute_kzg_proof_incorrect_blob_length() { + compute_kzg_proof_incorrect_blob_length_test::(&blob_to_polynomial); + } + + #[test] + pub fn compute_kzg_proof_incorrect_poly_length() { + compute_kzg_proof_incorrect_poly_length_test::< + CtPoly, + CtFr, + CtG1, + CtG2, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >(&evaluate_polynomial_in_evaluation_form); + } + + #[test] + pub fn compute_kzg_proof_empty_blob_vector() { + compute_kzg_proof_empty_blob_vector_test::< + CtPoly, + CtFr, + CtG1, + CtG2, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >(&verify_blob_kzg_proof_batch_rust) + } + + #[test] + pub fn compute_kzg_proof_incorrect_commitments_len() { + compute_kzg_proof_incorrect_commitments_len_test::< + CtPoly, + CtFr, + CtG1, + CtG2, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >(&verify_blob_kzg_proof_batch_rust) + } + + #[test] + pub fn compute_kzg_proof_incorrect_proofs_len() { + compute_kzg_proof_incorrect_proofs_len_test::< + CtPoly, + CtFr, + CtG1, + CtG2, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >(&verify_blob_kzg_proof_batch_rust) + } + + #[test] + pub fn validate_batched_input() { + validate_batched_input_test::< + CtPoly, + CtFr, + CtG1, + CtG2, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >( + &verify_blob_kzg_proof_batch_rust, + &load_trusted_setup_filename_rust, + ) + } +} diff --git a/constantine/tests/eip_4844_constantine.rs b/constantine/tests/eip_4844_constantine.rs new file mode 100644 index 00000000..0c582152 --- /dev/null +++ b/constantine/tests/eip_4844_constantine.rs @@ -0,0 +1,406 @@ +// EIP_4844 Tests, but using constantine binding functions +// Some tests are disabled because they rely on data that is not possible to pull from constantine + +#[cfg(test)] +mod tests { + use kzg::eip_4844::{ + blob_to_polynomial, bytes_to_blob, compute_powers, evaluate_polynomial_in_evaluation_form, + }; + use kzg::Fr; + + use kzg_bench::tests::eip_4844::{ + blob_to_kzg_commitment_test, bytes_to_bls_field_test, + compute_and_verify_blob_kzg_proof_fails_with_incorrect_proof_test, + compute_and_verify_blob_kzg_proof_test, compute_kzg_proof_empty_blob_vector_test, + compute_kzg_proof_incorrect_blob_length_test, + compute_kzg_proof_incorrect_commitments_len_test, + compute_kzg_proof_incorrect_poly_length_test, compute_kzg_proof_incorrect_proofs_len_test, + compute_powers_test, test_vectors_blob_to_kzg_commitment, + test_vectors_compute_blob_kzg_proof, test_vectors_compute_kzg_proof, + test_vectors_verify_blob_kzg_proof, test_vectors_verify_blob_kzg_proof_batch, + test_vectors_verify_kzg_proof, validate_batched_input_test, + verify_kzg_proof_batch_fails_with_incorrect_proof_test, verify_kzg_proof_batch_test, + }; + use rust_kzg_constantine::consts::SCALE2_ROOT_OF_UNITY; + + use rust_kzg_constantine::mixed_kzg::mixed_eip_4844::{ + blob_to_kzg_commitment_mixed, compute_blob_kzg_proof_mixed, compute_kzg_proof_mixed, + load_trusted_setup_filename_mixed, verify_blob_kzg_proof_batch_mixed, + verify_blob_kzg_proof_mixed, verify_kzg_proof_mixed, + }; + use rust_kzg_constantine::mixed_kzg::mixed_kzg_settings::MixedKzgSettings; + use rust_kzg_constantine::types::fft_settings::expand_root_of_unity; + use rust_kzg_constantine::types::fp::CtFp; + use rust_kzg_constantine::types::g1::CtG1Affine; + use rust_kzg_constantine::types::{ + fft_settings::CtFFTSettings, fr::CtFr, g1::CtG1, g2::CtG2, poly::CtPoly, + }; + + #[test] + pub fn bytes_to_bls_field_test_() { + bytes_to_bls_field_test::(); + } + + #[test] + pub fn compute_powers_test_() { + compute_powers_test::(&compute_powers); + } + + #[test] + pub fn blob_to_kzg_commitment_test_() { + blob_to_kzg_commitment_test::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_mixed, + &blob_to_kzg_commitment_mixed, + ); + } + + // #[test] + // pub fn compute_kzg_proof_test_() { + // compute_kzg_proof_test::( + // &load_trusted_setup_filename_mixed, + // &compute_kzg_proof_mixed, + // &blob_to_polynomial, + // &evaluate_polynomial_in_evaluation_form, + // ); + // } + + // #[test] + // pub fn compute_and_verify_kzg_proof_round_trip_test_() { + // compute_and_verify_kzg_proof_round_trip_test::< + // CtFr, + // CtG1, + // CtG2, + // CtPoly, + // CtFFTSettings, + // MixedKzgSettings, CtFp, CtG1Affine>( + // &load_trusted_setup_filename_mixed, + // &blob_to_kzg_commitment_mixed, + // &bytes_to_blob, + // &compute_kzg_proof_mixed, + // &blob_to_polynomial, + // &evaluate_polynomial_in_evaluation_form, + // &verify_kzg_proof_mixed, + // ); + // } + + // #[test] + // pub fn compute_and_verify_kzg_proof_within_domain_test_() { + // compute_and_verify_kzg_proof_within_domain_test::< + // CtFr, + // CtG1, + // CtG2, + // CtPoly, + // CtFFTSettings, + // MixedKzgSettings, CtFp, CtG1Affine>( + // &load_trusted_setup_filename_mixed, + // &blob_to_kzg_commitment_mixed, + // &bytes_to_blob, + // &compute_kzg_proof_mixed, + // &blob_to_polynomial, + // &evaluate_polynomial_in_evaluation_form, + // &verify_kzg_proof_mixed, + // ); + // } + + // #[test] + // pub fn compute_and_verify_kzg_proof_fails_with_incorrect_proof_test_() { + // compute_and_verify_kzg_proof_fails_with_incorrect_proof_test::< + // CtFr, + // CtG1, + // CtG2, + // CtPoly, + // CtFFTSettings, + // MixedKzgSettings, CtFp, CtG1Affine>( + // &load_trusted_setup_filename_mixed, + // &blob_to_kzg_commitment_mixed, + // &bytes_to_blob, + // &compute_kzg_proof_mixed, + // &blob_to_polynomial, + // &evaluate_polynomial_in_evaluation_form, + // &verify_kzg_proof_mixed, + // ); + // } + + #[test] + pub fn compute_and_verify_blob_kzg_proof_test_() { + compute_and_verify_blob_kzg_proof_test::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_mixed, + &blob_to_kzg_commitment_mixed, + &bytes_to_blob, + &compute_blob_kzg_proof_mixed, + &verify_blob_kzg_proof_mixed, + ); + } + + #[test] + pub fn compute_and_verify_blob_kzg_proof_fails_with_incorrect_proof_test_() { + compute_and_verify_blob_kzg_proof_fails_with_incorrect_proof_test::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_mixed, + &blob_to_kzg_commitment_mixed, + &bytes_to_blob, + &compute_blob_kzg_proof_mixed, + &verify_blob_kzg_proof_mixed, + ); + } + + #[test] + pub fn verify_kzg_proof_batch_test_() { + verify_kzg_proof_batch_test::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_mixed, + &blob_to_kzg_commitment_mixed, + &bytes_to_blob, + &compute_blob_kzg_proof_mixed, + &verify_blob_kzg_proof_batch_mixed, + ); + } + + #[test] + pub fn verify_kzg_proof_batch_fails_with_incorrect_proof_test_() { + verify_kzg_proof_batch_fails_with_incorrect_proof_test::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_mixed, + &blob_to_kzg_commitment_mixed, + &bytes_to_blob, + &compute_blob_kzg_proof_mixed, + &verify_blob_kzg_proof_batch_mixed, + ); + } + + #[test] + pub fn test_vectors_blob_to_kzg_commitment_() { + test_vectors_blob_to_kzg_commitment::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_mixed, + &blob_to_kzg_commitment_mixed, + &bytes_to_blob, + ); + } + + #[test] + pub fn test_vectors_compute_kzg_proof_() { + test_vectors_compute_kzg_proof::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_mixed, + &compute_kzg_proof_mixed, + &bytes_to_blob, + ); + } + + #[test] + pub fn test_vectors_compute_blob_kzg_proof_() { + test_vectors_compute_blob_kzg_proof::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_mixed, + &bytes_to_blob, + &compute_blob_kzg_proof_mixed, + ); + } + + #[test] + pub fn test_vectors_verify_kzg_proof_() { + test_vectors_verify_kzg_proof::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >(&load_trusted_setup_filename_mixed, &verify_kzg_proof_mixed); + } + + #[test] + pub fn test_vectors_verify_blob_kzg_proof_() { + test_vectors_verify_blob_kzg_proof::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_mixed, + &bytes_to_blob, + &verify_blob_kzg_proof_mixed, + ); + } + + #[test] + pub fn test_vectors_verify_blob_kzg_proof_batch_() { + test_vectors_verify_blob_kzg_proof_batch::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >( + &load_trusted_setup_filename_mixed, + &bytes_to_blob, + &verify_blob_kzg_proof_batch_mixed, + ); + } + + #[test] + pub fn expand_root_of_unity_too_long() { + let out = expand_root_of_unity(&CtFr::from_u64_arr(&SCALE2_ROOT_OF_UNITY[1]), 1); + assert!(out.is_err()); + } + + #[test] + pub fn expand_root_of_unity_too_short() { + let out = expand_root_of_unity(&CtFr::from_u64_arr(&SCALE2_ROOT_OF_UNITY[1]), 3); + assert!(out.is_err()); + } + + #[test] + pub fn compute_kzg_proof_incorrect_blob_length() { + compute_kzg_proof_incorrect_blob_length_test::(&blob_to_polynomial); + } + + #[test] + pub fn compute_kzg_proof_incorrect_poly_length() { + compute_kzg_proof_incorrect_poly_length_test::< + CtPoly, + CtFr, + CtG1, + CtG2, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >(&evaluate_polynomial_in_evaluation_form); + } + + #[test] + pub fn compute_kzg_proof_empty_blob_vector() { + compute_kzg_proof_empty_blob_vector_test::< + CtPoly, + CtFr, + CtG1, + CtG2, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >(&verify_blob_kzg_proof_batch_mixed) + } + + #[test] + pub fn compute_kzg_proof_incorrect_commitments_len() { + compute_kzg_proof_incorrect_commitments_len_test::< + CtPoly, + CtFr, + CtG1, + CtG2, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >(&verify_blob_kzg_proof_batch_mixed) + } + + #[test] + pub fn compute_kzg_proof_incorrect_proofs_len() { + compute_kzg_proof_incorrect_proofs_len_test::< + CtPoly, + CtFr, + CtG1, + CtG2, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >(&verify_blob_kzg_proof_batch_mixed) + } + + #[test] + pub fn validate_batched_input() { + validate_batched_input_test::< + CtPoly, + CtFr, + CtG1, + CtG2, + CtFFTSettings, + MixedKzgSettings, + CtFp, + CtG1Affine, + >( + &verify_blob_kzg_proof_batch_mixed, + &load_trusted_setup_filename_mixed, + ) + } +} diff --git a/constantine/tests/fft_fr.rs b/constantine/tests/fft_fr.rs new file mode 100644 index 00000000..a0ef9bbb --- /dev/null +++ b/constantine/tests/fft_fr.rs @@ -0,0 +1,27 @@ +#[cfg(test)] +mod tests { + use kzg_bench::tests::fft_fr::{compare_sft_fft, inverse_fft, roundtrip_fft, stride_fft}; + use rust_kzg_constantine::fft_fr::{fft_fr_fast, fft_fr_slow}; + use rust_kzg_constantine::types::fft_settings::CtFFTSettings; + use rust_kzg_constantine::types::fr::CtFr; + + #[test] + fn compare_sft_fft_() { + compare_sft_fft::(&fft_fr_slow, &fft_fr_fast); + } + + #[test] + fn roundtrip_fft_() { + roundtrip_fft::(); + } + + #[test] + fn inverse_fft_() { + inverse_fft::(); + } + + #[test] + fn stride_fft_() { + stride_fft::(); + } +} diff --git a/constantine/tests/fft_g1.rs b/constantine/tests/fft_g1.rs new file mode 100644 index 00000000..09e1b008 --- /dev/null +++ b/constantine/tests/fft_g1.rs @@ -0,0 +1,38 @@ +#[cfg(test)] +mod tests { + use kzg::G1; + use kzg_bench::tests::fft_g1::{compare_ft_fft, roundtrip_fft, stride_fft}; + use rust_kzg_constantine::consts::G1_GENERATOR; + use rust_kzg_constantine::fft_g1::{fft_g1_fast, fft_g1_slow}; + use rust_kzg_constantine::types::fft_settings::CtFFTSettings; + use rust_kzg_constantine::types::fr::CtFr; + use rust_kzg_constantine::types::g1::CtG1; + + fn make_data(n: usize) -> Vec { + if n == 0 { + return Vec::new(); + } + let mut result: Vec = vec![CtG1::default(); n]; + result[0] = G1_GENERATOR; + for i in 1..n { + result[i] = result[i - 1].add_or_dbl(&G1_GENERATOR) + } + + result + } + + #[test] + fn roundtrip_fft_() { + roundtrip_fft::(&make_data); + } + + #[test] + fn stride_fft_() { + stride_fft::(&make_data); + } + + #[test] + fn compare_sft_fft_() { + compare_ft_fft::(&fft_g1_slow, &fft_g1_fast, &make_data); + } +} diff --git a/constantine/tests/fk20_proofs.rs b/constantine/tests/fk20_proofs.rs new file mode 100644 index 00000000..6e138c90 --- /dev/null +++ b/constantine/tests/fk20_proofs.rs @@ -0,0 +1,104 @@ +#[cfg(test)] +mod tests { + use kzg_bench::tests::fk20_proofs::*; + use rust_kzg_constantine::types::fft_settings::CtFFTSettings; + use rust_kzg_constantine::types::fk20_multi_settings::CtFK20MultiSettings; + use rust_kzg_constantine::types::fk20_single_settings::CtFK20SingleSettings; + use rust_kzg_constantine::types::fp::CtFp; + use rust_kzg_constantine::types::fr::CtFr; + use rust_kzg_constantine::types::g1::{CtG1, CtG1Affine}; + use rust_kzg_constantine::types::g2::CtG2; + use rust_kzg_constantine::types::kzg_settings::CtKZGSettings; + use rust_kzg_constantine::types::poly::CtPoly; + use rust_kzg_constantine::utils::generate_trusted_setup; + + #[test] + fn test_fk_single() { + fk_single::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFK20SingleSettings, + CtFp, + CtG1Affine, + >(&generate_trusted_setup); + } + + #[test] + fn test_fk_single_strided() { + fk_single_strided::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFK20SingleSettings, + CtFp, + CtG1Affine, + >(&generate_trusted_setup); + } + + #[test] + fn test_fk_multi_settings() { + fk_multi_settings::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFK20MultiSettings, + CtFp, + CtG1Affine, + >(&generate_trusted_setup); + } + + #[test] + fn test_fk_multi_chunk_len_1_512() { + fk_multi_chunk_len_1_512::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFK20MultiSettings, + CtFp, + CtG1Affine, + >(&generate_trusted_setup); + } + + #[test] + fn test_fk_multi_chunk_len_16_512() { + fk_multi_chunk_len_16_512::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFK20MultiSettings, + CtFp, + CtG1Affine, + >(&generate_trusted_setup); + } + + #[test] + fn test_fk_multi_chunk_len_16_16() { + fk_multi_chunk_len_16_16::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFK20MultiSettings, + CtFp, + CtG1Affine, + >(&generate_trusted_setup); + } +} diff --git a/constantine/tests/kzg_proofs.rs b/constantine/tests/kzg_proofs.rs new file mode 100644 index 00000000..e7f32d15 --- /dev/null +++ b/constantine/tests/kzg_proofs.rs @@ -0,0 +1,58 @@ +#[cfg(test)] +mod tests { + + use kzg_bench::tests::kzg_proofs::{ + commit_to_nil_poly, commit_to_too_long_poly_returns_err, proof_multi, proof_single, + }; + + use rust_kzg_constantine::types::fft_settings::CtFFTSettings; + use rust_kzg_constantine::types::fp::CtFp; + use rust_kzg_constantine::types::fr::CtFr; + use rust_kzg_constantine::types::g1::{CtG1, CtG1Affine}; + use rust_kzg_constantine::types::g2::CtG2; + use rust_kzg_constantine::types::kzg_settings::CtKZGSettings; + use rust_kzg_constantine::types::poly::CtPoly; + use rust_kzg_constantine::utils::generate_trusted_setup; + + #[test] + pub fn test_proof_single() { + proof_single::( + &generate_trusted_setup, + ); + } + + #[test] + pub fn test_commit_to_nil_poly() { + commit_to_nil_poly::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >(&generate_trusted_setup); + } + + #[test] + pub fn test_commit_to_too_long_poly() { + commit_to_too_long_poly_returns_err::< + CtFr, + CtG1, + CtG2, + CtPoly, + CtFFTSettings, + CtKZGSettings, + CtFp, + CtG1Affine, + >(&generate_trusted_setup); + } + + #[test] + pub fn test_proof_multi() { + proof_multi::( + &generate_trusted_setup, + ); + } +} diff --git a/constantine/tests/local_tests/local_consts.rs b/constantine/tests/local_tests/local_consts.rs new file mode 100644 index 00000000..450d8c76 --- /dev/null +++ b/constantine/tests/local_tests/local_consts.rs @@ -0,0 +1,16 @@ +use kzg::{FFTSettings, Fr}; + +pub fn roots_of_unity_repeat_at_stride>() { + let fs1 = TFFTSettings::new(15).unwrap(); + let fs2 = TFFTSettings::new(16).unwrap(); + let fs3 = TFFTSettings::new(17).unwrap(); + + for i in 0..fs1.get_max_width() { + assert!(fs1 + .get_expanded_roots_of_unity_at(i) + .equals(&fs2.get_expanded_roots_of_unity_at(i * 2))); + assert!(fs1 + .get_expanded_roots_of_unity_at(i) + .equals(&fs3.get_expanded_roots_of_unity_at(i * 4))); + } +} diff --git a/constantine/tests/local_tests/local_poly.rs b/constantine/tests/local_tests/local_poly.rs new file mode 100644 index 00000000..2fd10c37 --- /dev/null +++ b/constantine/tests/local_tests/local_poly.rs @@ -0,0 +1,371 @@ +use kzg::{Fr, Poly}; +use rand::rngs::StdRng; +use rand::{RngCore, SeedableRng}; + +use rust_kzg_constantine::types::fr::CtFr; +use rust_kzg_constantine::types::poly::CtPoly; + +pub fn create_poly_of_length_ten() { + let poly = CtPoly::new(10); + assert_eq!(poly.len(), 10); +} + +pub fn poly_pad_works_rand() { + let mut rng = StdRng::seed_from_u64(0); + + for _k in 0..256 { + let poly_length: usize = (1 + (rng.next_u64() % 1000)) as usize; + let mut poly = CtPoly::new(poly_length); + for i in 0..poly.len() { + poly.set_coeff_at(i, &CtFr::rand()); + } + + let padded_poly = poly.pad(1000); + for i in 0..poly_length { + assert!(padded_poly.get_coeff_at(i).equals(&poly.get_coeff_at(i))); + } + for i in poly_length..1000 { + assert!(padded_poly.get_coeff_at(i).equals(&Fr::zero())); + } + } +} + +pub fn poly_eval_check() { + let n: usize = 10; + let mut poly = CtPoly::new(n); + for i in 0..n { + let fr = CtFr::from_u64((i + 1) as u64); + poly.set_coeff_at(i, &fr); + } + let expected = CtFr::from_u64((n * (n + 1) / 2) as u64); + let actual = poly.eval(&CtFr::one()); + assert!(expected.equals(&actual)); +} + +pub fn poly_eval_0_check() { + let n: usize = 7; + let a: usize = 597; + let mut poly = CtPoly::new(n); + for i in 0..n { + let fr = CtFr::from_u64((i + a) as u64); + poly.set_coeff_at(i, &fr); + } + let expected = CtFr::from_u64(a as u64); + let actual = poly.eval(&CtFr::zero()); + assert!(expected.equals(&actual)); +} + +pub fn poly_eval_nil_check() { + let n: usize = 0; + let poly = CtPoly::new(n); + let actual = poly.eval(&CtFr::one()); + assert!(actual.equals(&CtFr::zero())); +} + +pub fn poly_inverse_simple_0() { + // 1 / (1 - x) = 1 + x + x^2 + ... + let d: usize = 16; + let mut p = CtPoly::new(2); + p.set_coeff_at(0, &CtFr::one()); + p.set_coeff_at(1, &CtFr::one()); + p.set_coeff_at(1, &CtFr::negate(&p.get_coeff_at(1))); + let result = p.inverse(d); + assert!(result.is_ok()); + let q = result.unwrap(); + for i in 0..d { + assert!(q.get_coeff_at(i).is_one()); + } +} + +pub fn poly_inverse_simple_1() { + // 1 / (1 + x) = 1 - x + x^2 - ... + let d: usize = 16; + let mut p = CtPoly::new(2); + p.set_coeff_at(0, &CtFr::one()); + p.set_coeff_at(1, &CtFr::one()); + let result = p.inverse(d); + assert!(result.is_ok()); + let q = result.unwrap(); + for i in 0..d { + let mut tmp = q.get_coeff_at(i); + if i & 1 != 0 { + tmp = CtFr::negate(&tmp); + } + assert!(tmp.is_one()); + } +} + +pub const NUM_TESTS: u64 = 10; + +fn test_data(a: usize, b: usize) -> Vec { + // (x^2 - 1) / (x + 1) = x - 1 + let test_0_0 = vec![-1, 0, 1]; + let test_0_1 = vec![1, 1]; + let test_0_2 = vec![-1, 1]; + + // (12x^3 - 11x^2 + 9x + 18) / (4x + 3) = 3x^2 - 5x + 6 + let test_1_0 = vec![18, 9, -11, 12]; + let test_1_1 = vec![3, 4]; + let test_1_2 = vec![6, -5, 3]; + + // (x + 1) / (x^2 - 1) = nil + let test_2_0 = vec![1, 1]; + let test_2_1 = vec![-1, 0, 2]; + let test_2_2 = vec![]; + + // (10x^2 + 20x + 30) / 10 = x^2 + 2x + 3 + let test_3_0 = vec![30, 20, 10]; + let test_3_1 = vec![10]; + let test_3_2 = vec![3, 2, 1]; + + // (x^2 + x) / (x + 1) = x + let test_4_0 = vec![0, 1, 1]; + let test_4_1 = vec![1, 1]; + let test_4_2 = vec![0, 1]; + + // (x^2 + x + 1) / 1 = x^2 + x + 1 + let test_5_0 = vec![1, 1, 1]; + let test_5_1 = vec![1]; + let test_5_2 = vec![1, 1, 1]; + + // (x^2 + x + 1) / (0x + 1) = x^2 + x + 1 + let test_6_0 = vec![1, 1, 1]; + let test_6_1 = vec![1, 0]; // The highest coefficient is zero + let test_6_2 = vec![1, 1, 1]; + + // (x^3) / (x) = (x^2) + let test_7_0 = vec![0, 0, 0, 1]; + let test_7_1 = vec![0, 1]; + let test_7_2 = vec![0, 0, 1]; + + // + let test_8_0 = vec![ + 236, + 945, + -297698, + 2489425, + -18556462, + -301325440, + 2473062655, + -20699887353, + ]; + let test_8_1 = vec![4, 11, -5000, 45541, -454533]; + let test_8_2 = vec![59, 74, -878, 45541]; + + // (x^4 + 2x^3 + 3x^2 + 2x + 1) / (-x^2 -x -1) = (-x^2 -x -1) + let test_9_0 = vec![1, 2, 3, 2, 1]; + let test_9_1 = vec![-1, -1, -1]; + let test_9_2 = vec![-1, -1, -1]; + + let test_data = [ + [test_0_0, test_0_1, test_0_2], + [test_1_0, test_1_1, test_1_2], + [test_2_0, test_2_1, test_2_2], + [test_3_0, test_3_1, test_3_2], + [test_4_0, test_4_1, test_4_2], + [test_5_0, test_5_1, test_5_2], + [test_6_0, test_6_1, test_6_2], + [test_7_0, test_7_1, test_7_2], + [test_8_0, test_8_1, test_8_2], + [test_9_0, test_9_1, test_9_2], + ]; + + test_data[a][b].clone() +} + +fn new_test_poly(coeffs: &[i64]) -> CtPoly { + let mut p = CtPoly::new(0); + + for &coeff in coeffs.iter() { + if coeff >= 0 { + let c = CtFr::from_u64(coeff as u64); + p.coeffs.push(c); + } else { + let c = CtFr::from_u64((-coeff) as u64); + let negc = c.negate(); + p.coeffs.push(negc); + } + } + + p +} + +pub fn poly_div_long_test() { + for i in 0..9 { + // Tests are designed to throw an exception when last member is 0 + if i == 6 { + continue; + } + + let divided_data = test_data(i, 0); + let divisor_data = test_data(i, 1); + let expected_data = test_data(i, 2); + let mut dividend: CtPoly = new_test_poly(÷d_data); + let divisor: CtPoly = new_test_poly(&divisor_data); + let expected: CtPoly = new_test_poly(&expected_data); + + let actual = dividend.long_div(&divisor).unwrap(); + + assert_eq!(expected.len(), actual.len()); + for i in 0..actual.len() { + assert!(expected.get_coeff_at(i).equals(&actual.get_coeff_at(i))) + } + } +} + +pub fn poly_div_fast_test() { + for i in 0..9 { + // Tests are designed to throw an exception when last member is 0 + if i == 6 { + continue; + } + + let divided_data = test_data(i, 0); + let divisor_data = test_data(i, 1); + let expected_data = test_data(i, 2); + let mut dividend: CtPoly = new_test_poly(÷d_data); + let divisor: CtPoly = new_test_poly(&divisor_data); + let expected: CtPoly = new_test_poly(&expected_data); + + let actual = dividend.fast_div(&divisor).unwrap(); + + assert_eq!(expected.len(), actual.len()); + for i in 0..actual.len() { + assert!(expected.get_coeff_at(i).equals(&actual.get_coeff_at(i))) + } + } +} + +pub fn test_poly_div_by_zero() { + let mut dividend = CtPoly::new(2); + + dividend.set_coeff_at(0, &CtFr::from_u64(1)); + dividend.set_coeff_at(1, &CtFr::from_u64(1)); + + let divisor = CtPoly::new(0); + + let dummy = dividend.div(&divisor); + assert!(dummy.is_err()); +} + +pub fn poly_mul_direct_test() { + for i in 0..9 { + let coeffs1 = test_data(i, 2); + let coeffs2 = test_data(i, 1); + let coeffs3 = test_data(i, 0); + + let mut multiplicand: CtPoly = new_test_poly(&coeffs1); + let mut multiplier: CtPoly = new_test_poly(&coeffs2); + let expected: CtPoly = new_test_poly(&coeffs3); + + let result0 = multiplicand.mul_direct(&multiplier, coeffs3.len()).unwrap(); + for j in 0..result0.len() { + assert!(expected.get_coeff_at(j).equals(&result0.get_coeff_at(j))) + } + + // Check commutativity + let result1 = multiplier.mul_direct(&multiplicand, coeffs3.len()).unwrap(); + for j in 0..result1.len() { + assert!(expected.get_coeff_at(j).equals(&result1.get_coeff_at(j))) + } + } +} + +pub fn poly_mul_fft_test() { + for i in 0..9 { + // Ignore 0 multiplication case because its incorrect when multiplied backwards + if i == 2 { + continue; + } + + let coeffs1 = test_data(i, 2); + let coeffs2 = test_data(i, 1); + let coeffs3 = test_data(i, 0); + + let multiplicand: CtPoly = new_test_poly(&coeffs1); + let multiplier: CtPoly = new_test_poly(&coeffs2); + let expected: CtPoly = new_test_poly(&coeffs3); + + let result0 = multiplicand.mul_fft(&multiplier, coeffs3.len()).unwrap(); + for j in 0..result0.len() { + assert!(expected.get_coeff_at(j).equals(&result0.get_coeff_at(j))) + } + + // Check commutativity + let result1 = multiplier.mul_fft(&multiplicand, coeffs3.len()).unwrap(); + for j in 0..result1.len() { + assert!(expected.get_coeff_at(j).equals(&result1.get_coeff_at(j))) + } + } +} + +pub fn poly_mul_random() { + let mut rng = StdRng::seed_from_u64(0); + + for _k in 0..256 { + let multiplicand_length: usize = (1 + (rng.next_u64() % 1000)) as usize; + let mut multiplicand = CtPoly::new(multiplicand_length); + for i in 0..multiplicand.len() { + multiplicand.set_coeff_at(i, &CtFr::rand()); + } + + let multiplier_length: usize = (1 + (rng.next_u64() % 1000)) as usize; + let mut multiplier = CtPoly::new(multiplier_length); + for i in 0..multiplier.len() { + multiplier.set_coeff_at(i, &CtFr::rand()); + } + + if multiplicand.get_coeff_at(multiplicand.len() - 1).is_zero() { + multiplicand.set_coeff_at(multiplicand.len() - 1, &Fr::one()); + } + + if multiplier.get_coeff_at(multiplier.len() - 1).is_zero() { + multiplier.set_coeff_at(multiplier.len() - 1, &Fr::one()); + } + + let out_length: usize = (1 + (rng.next_u64() % 1000)) as usize; + let q0 = multiplicand.mul_direct(&multiplier, out_length).unwrap(); + let q1 = multiplicand.mul_fft(&multiplier, out_length).unwrap(); + + assert_eq!(q0.len(), q1.len()); + for i in 0..q0.len() { + assert!(q0.get_coeff_at(i).equals(&q1.get_coeff_at(i))); + } + } +} + +pub fn poly_div_random() { + let mut rng = StdRng::seed_from_u64(0); + for _k in 0..256 { + let dividend_length: usize = (2 + (rng.next_u64() % 1000)) as usize; + let divisor_length: usize = 1 + ((rng.next_u64() as usize) % dividend_length); + + let mut dividend = CtPoly::new(dividend_length); + let mut divisor = CtPoly::new(divisor_length); + + for i in 0..dividend_length { + dividend.set_coeff_at(i, &CtFr::rand()); + } + + for i in 0..divisor_length { + divisor.set_coeff_at(i, &CtFr::rand()); + } + + //Ensure that the polynomials' orders corresponds to their lengths + if dividend.get_coeff_at(dividend.len() - 1).is_zero() { + dividend.set_coeff_at(dividend.len() - 1, &Fr::one()); + } + + if divisor.get_coeff_at(divisor.len() - 1).is_zero() { + divisor.set_coeff_at(divisor.len() - 1, &Fr::one()); + } + + let result0 = dividend.long_div(&divisor).unwrap(); + let result1 = dividend.fast_div(&divisor).unwrap(); + + assert_eq!(result0.len(), result1.len()); + for i in 0..result0.len() { + assert!(result0.get_coeff_at(i).equals(&result1.get_coeff_at(i))); + } + } +} diff --git a/constantine/tests/local_tests/local_recovery.rs b/constantine/tests/local_tests/local_recovery.rs new file mode 100644 index 00000000..3de2e1f4 --- /dev/null +++ b/constantine/tests/local_tests/local_recovery.rs @@ -0,0 +1,158 @@ +use kzg::{FFTFr, FFTSettings, Fr, Poly, PolyRecover}; +use rand::rngs::StdRng; +use rand::{RngCore, SeedableRng}; +use std::convert::TryInto; + +fn shuffle(a: &mut [usize], n: usize) { + let mut i: u64 = n as u64; + let mut j: usize; + let mut tmp: usize; + + let mut rng = StdRng::seed_from_u64(0); + while i > 0 { + j = (rng.next_u64() % i) as usize; + i -= 1; + tmp = a[j]; + a[j] = a[i as usize]; + a[i as usize] = tmp; + } +} + +fn random_missing( + with_missing: &mut [Option], + data: &[TFr], + len_data: usize, + known: usize, +) { + let mut missing_idx = Vec::new(); + for i in 0..len_data { + missing_idx.push(i); + with_missing[i] = Some(data[i].clone()); + } + + shuffle(&mut missing_idx, len_data); + for i in 0..(len_data - known) { + with_missing[missing_idx[i]] = None; + } +} + +pub fn recover_simple< + TFr: Fr, + TFFTSettings: FFTSettings + FFTFr, + TPoly: Poly, + TPolyRecover: PolyRecover, +>() { + let fs_query = TFFTSettings::new(2); + assert!(fs_query.is_ok()); + + let fs: TFFTSettings = fs_query.unwrap(); + let max_width: usize = fs.get_max_width(); + + let poly_query = TPoly::new(max_width); + let mut poly = poly_query; + + for i in 0..(max_width / 2) { + poly.set_coeff_at(i, &TFr::from_u64(i.try_into().unwrap())); + } + + for i in (max_width / 2)..max_width { + poly.set_coeff_at(i, &TFr::zero()); + } + + let data_query = fs.fft_fr(poly.get_coeffs(), false); + assert!(data_query.is_ok()); + let data = data_query.unwrap(); + + let sample: [Option; 4] = [Some(data[0].clone()), None, None, Some(data[3].clone())]; + + let recovered_query = TPolyRecover::recover_poly_from_samples(&sample, &fs); + assert!(recovered_query.is_ok()); + let recovered = recovered_query.unwrap(); + + for (i, item) in data.iter().enumerate().take(max_width) { + assert!(item.equals(&recovered.get_coeff_at(i))); + } + + let mut recovered_vec: Vec = Vec::new(); + + for i in 0..max_width { + recovered_vec.push(recovered.get_coeff_at(i)); + } + + let back_query = fs.fft_fr(&recovered_vec, true); + assert!(back_query.is_ok()); + let back = back_query.unwrap(); + + for (i, back_x) in back[..(max_width / 2)].iter().enumerate() { + assert!(back_x.equals(&poly.get_coeff_at(i))); + } + + for back_x in back[(max_width / 2)..max_width].iter() { + assert!(back_x.is_zero()); + } +} + +pub fn recover_random< + TFr: Fr, + TFFTSettings: FFTSettings + FFTFr, + TPoly: Poly, + TPolyRecover: PolyRecover, +>() { + let fs_query = TFFTSettings::new(12); + assert!(fs_query.is_ok()); + + let fs: TFFTSettings = fs_query.unwrap(); + let max_width: usize = fs.get_max_width(); + // let mut poly = TPoly::default(); + let poly_query = TPoly::new(max_width); + let mut poly = poly_query; + + for i in 0..(max_width / 2) { + poly.set_coeff_at(i, &TFr::from_u64(i.try_into().unwrap())); + } + + for i in (max_width / 2)..max_width { + poly.set_coeff_at(i, &TFr::zero()); + } + + let data_query = fs.fft_fr(poly.get_coeffs(), false); + assert!(data_query.is_ok()); + let data = data_query.unwrap(); + + let mut samples = vec![Some(TFr::default()); max_width]; // std::vec![TFr; max_width]; + + for i in 0..10 { + let known_ratio = 0.5 + (i as f32) * 0.05; + let known: usize = ((max_width as f32) * known_ratio) as usize; + + for _ in 0..4 { + random_missing(&mut samples, &data, max_width, known); + + let recovered_query = TPolyRecover::recover_poly_from_samples(&samples, &fs); + assert!(recovered_query.is_ok()); + let recovered = recovered_query.unwrap(); + + for (j, item) in data.iter().enumerate().take(max_width) { + assert!(item.equals(&recovered.get_coeff_at(j))); + } + + let mut recovered_vec: Vec = Vec::new(); + + for i in 0..max_width { + recovered_vec.push(recovered.get_coeff_at(i)); + } + + let back_query = fs.fft_fr(&recovered_vec, true); + assert!(back_query.is_ok()); + let back = back_query.unwrap(); + + for (i, back_x) in back[..(max_width / 2)].iter().enumerate() { + assert!(back_x.equals(&poly.get_coeff_at(i))); + } + + for back_x in back[(max_width / 2)..max_width].iter() { + assert!(back_x.is_zero()); + } + } + } +} diff --git a/constantine/tests/local_tests/mod.rs b/constantine/tests/local_tests/mod.rs new file mode 100644 index 00000000..ef310fa9 --- /dev/null +++ b/constantine/tests/local_tests/mod.rs @@ -0,0 +1,3 @@ +pub mod local_consts; +pub mod local_poly; +pub mod local_recovery; diff --git a/constantine/tests/mod.rs b/constantine/tests/mod.rs new file mode 100644 index 00000000..d1b7273b --- /dev/null +++ b/constantine/tests/mod.rs @@ -0,0 +1 @@ +pub mod local_tests; diff --git a/constantine/tests/poly.rs b/constantine/tests/poly.rs new file mode 100644 index 00000000..87e9c139 --- /dev/null +++ b/constantine/tests/poly.rs @@ -0,0 +1,155 @@ +// #[path = "./local_tests/local_poly.rs"] +// pub mod local_poly; + +#[cfg(test)] +mod tests { + use kzg_bench::tests::poly::{ + create_poly_of_length_ten, poly_div_by_zero, poly_div_fast_test, poly_div_long_test, + poly_div_random, poly_eval_0_check, poly_eval_check, poly_eval_nil_check, + poly_inverse_simple_0, poly_inverse_simple_1, poly_mul_direct_test, poly_mul_fft_test, + poly_mul_random, poly_test_div, + }; + use rust_kzg_constantine::types::fft_settings::CtFFTSettings; + use rust_kzg_constantine::types::fr::CtFr; + use rust_kzg_constantine::types::poly::CtPoly; + + // Local tests + // #[test] + // fn local_create_poly_of_length_ten_() { + // create_poly_of_length_ten() + // } + // + // #[test] + // fn local_poly_pad_works_rand_() { + // poly_pad_works_rand() + // } + // + // #[test] + // fn local_poly_eval_check_() { + // poly_eval_check() + // } + // + // #[test] + // fn local_poly_eval_0_check_() { poly_eval_0_check() } + // + // #[test] + // fn local_poly_eval_nil_check_() { + // poly_eval_nil_check() + // } + // + // #[test] + // fn local_poly_inverse_simple_0_() { + // poly_inverse_simple_0() + // } + // + // #[test] + // fn local_poly_inverse_simple_1_() { + // poly_inverse_simple_1() + // } + // + // #[test] + // fn local_test_poly_div_by_zero_() { + // test_poly_div_by_zero() + // } + // + // #[test] + // fn local_poly_div_long_test_() { + // poly_div_long_test() + // } + // + // #[test] + // fn local_poly_div_fast_test_() { + // poly_div_fast_test() + // } + // + // #[test] + // fn local_poly_mul_direct_test_() { + // poly_mul_direct_test() + // } + // + // #[test] + // fn local_poly_mul_fft_test_() { + // poly_mul_fft_test() + // } + // + // #[test] + // fn local_poly_mul_random_() { + // poly_mul_random() + // } + // + // #[test] + // fn local_poly_div_random_() { + // poly_div_random() + // } + + // Shared tests + #[test] + fn create_poly_of_length_ten_() { + create_poly_of_length_ten::() + } + + #[test] + fn poly_eval_check_() { + poly_eval_check::() + } + + #[test] + fn poly_eval_0_check_() { + poly_eval_0_check::() + } + + #[test] + fn poly_eval_nil_check_() { + poly_eval_nil_check::() + } + + #[test] + fn poly_inverse_simple_0_() { + poly_inverse_simple_0::() + } + + #[test] + fn poly_inverse_simple_1_() { + poly_inverse_simple_1::() + } + + #[test] + fn poly_test_div_() { + poly_test_div::() + } + + #[test] + fn poly_div_by_zero_() { + poly_div_by_zero::() + } + + #[test] + fn poly_mul_direct_test_() { + poly_mul_direct_test::() + } + + #[test] + fn poly_mul_fft_test_() { + poly_mul_fft_test::() + } + + #[test] + fn poly_mul_random_() { + poly_mul_random::() + } + + #[test] + fn poly_div_random_() { + poly_div_random::() + } + + #[test] + fn poly_div_long_test_() { + poly_div_long_test::() + } + + #[test] + fn poly_div_fast_test_() { + poly_div_fast_test::() + } +} diff --git a/constantine/tests/recovery.rs b/constantine/tests/recovery.rs new file mode 100644 index 00000000..59ccff18 --- /dev/null +++ b/constantine/tests/recovery.rs @@ -0,0 +1,29 @@ +// #[path = "./local_tests/local_recovery.rs"] +// pub mod local_recovery; + +#[cfg(test)] +mod tests { + use kzg_bench::tests::recover::*; + // uncomment to use the local tests + //use crate::local_recovery::{recover_random, recover_simple}; + + use rust_kzg_constantine::types::fft_settings::CtFFTSettings; + use rust_kzg_constantine::types::fr::CtFr; + use rust_kzg_constantine::types::poly::CtPoly; + + // Shared tests + #[test] + fn recover_simple_() { + recover_simple::(); + } + + #[test] + fn recover_random_() { + recover_random::(); + } + + #[test] + fn more_than_half_missing_() { + more_than_half_missing::(); + } +} diff --git a/constantine/tests/zero_poly.rs b/constantine/tests/zero_poly.rs new file mode 100644 index 00000000..8da771e9 --- /dev/null +++ b/constantine/tests/zero_poly.rs @@ -0,0 +1,45 @@ +#[cfg(test)] +mod tests { + use kzg_bench::tests::zero_poly::{ + check_test_data, reduce_partials_random, test_reduce_partials, zero_poly_252, + zero_poly_all_but_one, zero_poly_known, zero_poly_random, + }; + use rust_kzg_constantine::types::fft_settings::CtFFTSettings; + use rust_kzg_constantine::types::fr::CtFr; + use rust_kzg_constantine::types::poly::CtPoly; + + #[test] + fn test_reduce_partials_() { + test_reduce_partials::(); + } + + #[test] + fn reduce_partials_random_() { + reduce_partials_random::(); + } + + #[test] + fn check_test_data_() { + check_test_data::(); + } + + #[test] + fn zero_poly_known_() { + zero_poly_known::(); + } + + #[test] + fn zero_poly_random_() { + zero_poly_random::(); + } + + #[test] + fn zero_poly_all_but_one_() { + zero_poly_all_but_one::(); + } + + #[test] + fn zero_poly_252_() { + zero_poly_252::(); + } +} diff --git a/kzg-bench/src/lib.rs b/kzg-bench/src/lib.rs index 6584df71..f94616d9 100644 --- a/kzg-bench/src/lib.rs +++ b/kzg-bench/src/lib.rs @@ -1,3 +1,9 @@ +use std::env::set_current_dir; + pub mod benches; pub mod test_vectors; pub mod tests; + +pub fn set_trusted_setup_dir() { + set_current_dir(env!("CARGO_MANIFEST_DIR")).unwrap(); +} diff --git a/kzg/src/lib.rs b/kzg/src/lib.rs index 5dccc3a1..bc546c19 100644 --- a/kzg/src/lib.rs +++ b/kzg/src/lib.rs @@ -204,7 +204,7 @@ pub trait G1Fp: Clone + Default + Sync + Copy + PartialEq + Debug + Send { } pub trait G1Affine: - Clone + Default + PartialEq + Sync + Copy + Debug + Send + Clone + Default + PartialEq + Sync + Copy + Send + Debug { const ZERO: Self; diff --git a/run-benchmarks.sh b/run-benchmarks.sh index 5adbca9a..792b21dc 100644 --- a/run-benchmarks.sh +++ b/run-benchmarks.sh @@ -131,6 +131,14 @@ do print_msg "rust-kzg with mcl backend (parallel)" ../"$paste_file" taskset --cpu-list "${taskset_cpu_list[$i]}" cargo bench --manifest-path mcl/kzg-bench/Cargo.toml --features rust-kzg-mcl/parallel >> ../"$paste_file" + # 3.11. rust-kzg with constantine backend (sequential) + print_msg "rust-kzg with constantine backend (sequential)" ../"$paste_file" + taskset --cpu-list "${taskset_cpu_list[$i]}" cargo bench --manifest-path constantine/kzg-bench/Cargo.toml >> ../"$paste_file" + + # 3.12. rust-kzg with constantine backend (parallel) + print_msg "rust-kzg with constantine backend (parallel)" ../"$paste_file" + taskset --cpu-list "${taskset_cpu_list[$i]}" cargo bench --manifest-path constantine/kzg-bench/Cargo.toml --features rust-kzg-constantine/parallel >> ../"$paste_file" + # 3.11. rust binding (rust-kzg with blst backend) print_msg "rust binding (rust-kzg with blst backend)" ../"$paste_file" cd blst/c-kzg-4844/bindings/rust/ || exit diff --git a/run-c-kzg-4844-benches.sh b/run-c-kzg-4844-benches.sh index 6aacec19..6d38aea6 100755 --- a/run-c-kzg-4844-benches.sh +++ b/run-c-kzg-4844-benches.sh @@ -16,7 +16,7 @@ while [[ -n $# ]]; do -p|--parallel) parallel=true ;; - blst|arkworks|mcl|zkcrypto) + blst|arkworks|mcl|zkcrypto|constantine) backend="$1" ;; *) diff --git a/run-c-kzg-4844-tests.sh b/run-c-kzg-4844-tests.sh index 9454dc32..335c8a78 100755 --- a/run-c-kzg-4844-tests.sh +++ b/run-c-kzg-4844-tests.sh @@ -16,7 +16,7 @@ while [[ -n $# ]]; do -p|--parallel) parallel=true ;; - blst|arkworks|mcl|zkcrypto) + blst|arkworks|mcl|zkcrypto|constantine) backend="$1" ;; *)