diff --git a/Cargo.lock b/Cargo.lock index c6b04806..7c397b11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -601,6 +601,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + [[package]] name = "is-terminal" version = "0.4.13" @@ -649,6 +658,7 @@ dependencies = [ "cyclotomic-rings", "dhat", "hashbrown 0.15.2", + "humansize", "lattirust-linear-algebra", "lattirust-poly", "lattirust-ring", @@ -729,6 +739,12 @@ version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + [[package]] name = "lock_api" version = "0.4.12" diff --git a/Cargo.toml b/Cargo.toml index 12908a4f..cc205c3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = [ "latticefold", "cyclotomic-rings"] +members = [ "latticefold", "cyclotomic-rings" ] resolver = "2" [workspace.package] @@ -19,7 +19,7 @@ lattirust-ring = { git = "ssh://git@github.com/NethermindEth/lattirust.git", bra num-bigint = { version = "0.4.5", default-features = false } rand = { version = "0.8.5", default-features = false } thiserror = { version = "2.0.3", default-features = false } - +cyclotomic-rings = { path = "cyclotomic-rings", default-features = false } [workspace.metadata.docs.rs] # To build locally, use # RUSTDOCFLAGS="--html-in-header docs-header.html" cargo doc --no-deps --document-private-items --open diff --git a/README.md b/README.md index bd81ba7f..951476ce 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,10 @@ Available packages: - `latticefold`: main crate, contains the non-interactive folding scheme implementation, together with the Ajtai commitment scheme, R1CS/CCS structures, Fiat-Shamir transcript machinery, etc. - `cyclotomic-rings`: contains the trait definition of a ring suitable to be used in the LatticeFold protocol, a few ready-to-use rings and short challenge set machinery. +## Examples + +Check [latticefold-examples/README.md](examples/README.md) for examples. + ## Frontends Currently, the only way to define a circuit to be folded is by specifying it as a [rank-1 constraint system (R1CS)](https://github.com/NethermindEth/latticefold/blob/main/latticefold/src/arith/r1cs.rs) or a [customizable constraint system (CCS)](https://github.com/NethermindEth/latticefold/blob/main/latticefold/src/arith.rs). diff --git a/latticefold/Cargo.toml b/latticefold/Cargo.toml index c35aba1c..3a1a3697 100644 --- a/latticefold/Cargo.toml +++ b/latticefold/Cargo.toml @@ -9,7 +9,7 @@ ark-crypto-primitives = { workspace = true } ark-ff = { workspace = true } ark-serialize = { workspace = true } ark-std = { workspace = true } -cyclotomic-rings = { path = "../cyclotomic-rings", default-features = false } +cyclotomic-rings = { workspace = true } hashbrown = "0.15" lattirust-linear-algebra = { workspace = true } lattirust-poly = { workspace = true } @@ -59,6 +59,7 @@ implicit_clone = "warn" [dev-dependencies] criterion = "0.5.1" dhat = "0.3.2" +humansize = "2.1.3" [[bench]] name = "ajtai" diff --git a/latticefold/build.rs b/latticefold/build.rs new file mode 100644 index 00000000..1897d132 --- /dev/null +++ b/latticefold/build.rs @@ -0,0 +1,48 @@ +use std::fs; +use std::path::Path; + +fn main() -> Result<(), String> { + let out_dir = std::env::var("OUT_DIR").unwrap(); + + let dest_path = Path::new(&out_dir).join("generated.rs"); + + let generated_code = format!( + r#" + // This file was automatically generated by build.rs script. + // It can be used in examples with include! macro: + // include!(concat!(env!("OUT_DIR"), "/generated.rs")); + // + // Check examples/README.md file how to use environment variables to modify the file + + use latticefold::decomposition_parameters::DecompositionParams; + + #[derive(Clone)] + pub struct ExampleDP {{}} + + impl DecompositionParams for ExampleDP {{ + const B: u128 = {}; // Default: 1 << 15 + const L: usize = {}; // Default: 5 + const B_SMALL: usize = {}; // Default = 2 + const K: usize = {}; // Default = 15 + }} + + const C: usize = {}; // Default = 4 + const WIT_LEN: usize = {}; // Default = 4 + const W: usize = WIT_LEN * ExampleDP::L; + + "#, + std::env::var("PARAM_B").unwrap_or("1 << 15".to_string()), + std::env::var("PARAM_L").unwrap_or("5".to_string()), + std::env::var("PARAM_B_SMALL").unwrap_or("2".to_string()), + std::env::var("PARAM_K").unwrap_or("15".to_string()), + std::env::var("PARAM_C").unwrap_or("4".to_string()), + std::env::var("PARAM_WIT_LEN").unwrap_or("4".to_string()), + ); + + fs::write(&dest_path, generated_code).unwrap(); + + // Optionally tell Cargo when to rerun the build script + println!("cargo:rerun-if-changed=build.rs"); + + Ok(()) +} diff --git a/latticefold/examples/README.md b/latticefold/examples/README.md new file mode 100644 index 00000000..32fa8e52 --- /dev/null +++ b/latticefold/examples/README.md @@ -0,0 +1,87 @@ +# Examples README + +This file explains how to use the examples in this repository. Examples demonstrate functionality and can be customized using environment variables. Instructions are provided for Linux/MacOS (bash/zsh) and Windows (PowerShell). + +## Implemented examples +* goldilocks + +## Customization with Environment Variables + +The examples in this repository support customization via environment variables. You can modify the following parameters to tailor the behavior of the examples: + +- **`DECOMPOSITION_B`**: Sets the value of `B` in `DecompositionParams`. + - Default: `32768` (equivalent to `1 << 15`) +- **`DECOMPOSITION_L`**: Sets the value of `L` in `DecompositionParams`. + - Default: `5` +- **`DECOMPOSITION_B_SMALL`**: Sets the value of `B_SMALL` in `DecompositionParams`. + - Default: `2` +- **`DECOMPOSITION_K`**: Sets the value of `K` in `DecompositionParams`. + - Default: `15` +- **`DECOMPOSITION_C`**: Sets the value of `C`, controlling challenge set parameters. + - Default: `4` +- **`DECOMPOSITION_WIT_LEN`**: Sets the witness length. + - Default: `4` + +These parameters influence the behavior and output of the examples. + +## Setting Environment Variables + +### Linux/MacOS (bash/zsh) + +1. Open a terminal. +2. Export the desired environment variables before running the examples. For example: + + ```bash + export DECOMPOSITION_B=65536 + export DECOMPOSITION_L=6 + export DECOMPOSITION_B_SMALL=3 + export DECOMPOSITION_K=16 + export DECOMPOSITION_C=5 + export DECOMPOSITION_WIT_LEN=5 + + cargo run --example + ``` + +3. Replace `` with the name of the example you want to run. + +### Windows (PowerShell) + +1. Open PowerShell. +2. Set the desired environment variables before running the examples. For example: + + ```powershell + $env:DECOMPOSITION_B=65536 + $env:DECOMPOSITION_L=6 + $env:DECOMPOSITION_B_SMALL=3 + $env:DECOMPOSITION_K=16 + $env:DECOMPOSITION_C=5 + $env:DECOMPOSITION_WIT_LEN=5 + + cargo run --example + ``` + +3. Replace `` with the name of the example you want to run. + +## Example Output + +When you modify environment variables, the generated parameters are automatically updated in the example's output. This allows for testing different configurations and validating results under various conditions. + +## Default Values + +If no environment variables are specified, the examples will run with the following defaults: + +- `DECOMPOSITION_B`: `32768` +- `DECOMPOSITION_L`: `5` +- `DECOMPOSITION_B_SMALL`: `2` +- `DECOMPOSITION_K`: `15` +- `DECOMPOSITION_C`: `4` +- `DECOMPOSITION_WIT_LEN`: `4` + +## Notes + +- Ensure you rebuild the examples after modifying environment variables to see the changes. + ```bash + cargo clean && cargo run --example + ``` + +- For detailed instructions on each example, refer to the example's source code or inline comments. \ No newline at end of file diff --git a/latticefold/examples/goldilocks.rs b/latticefold/examples/goldilocks.rs new file mode 100644 index 00000000..746088a8 --- /dev/null +++ b/latticefold/examples/goldilocks.rs @@ -0,0 +1,164 @@ +use ark_serialize::{CanonicalSerialize, Compress}; +use ark_std::rand::Rng; +use ark_std::test_rng; +use ark_std::vec::Vec; +use cyclotomic_rings::challenge_set::LatticefoldChallengeSet; +use cyclotomic_rings::rings::{GoldilocksChallengeSet, GoldilocksRingNTT, SuitableRing}; +use latticefold::arith::r1cs::{get_test_z_split, to_F_matrix, R1CS}; +use latticefold::arith::{Witness, CCCS, CCS, LCCCS}; +use latticefold::commitment::AjtaiCommitmentScheme; +use latticefold::nifs::linearization::{LFLinearizationProver, LinearizationProver}; +use latticefold::nifs::{NIFSProver, NIFSVerifier}; +use latticefold::transcript::poseidon::PoseidonTranscript; +use lattirust_ring::Ring; +use std::time::Instant; + +include!(concat!(env!("OUT_DIR"), "/generated.rs")); + +/// Generates a test R1CS matrix for the equation x^3 + x + 5 = y +/// This will be changed to be easier to customized +pub fn get_test_r1cs() -> R1CS { + let a = to_F_matrix::(vec![ + vec![1, 0, 0, 0, 0, 0], + vec![0, 0, 0, 1, 0, 0], + vec![1, 0, 0, 0, 1, 0], + vec![0, 5, 0, 0, 0, 1], + ]); + let b = to_F_matrix::(vec![ + vec![1, 0, 0, 0, 0, 0], + vec![1, 0, 0, 0, 0, 0], + vec![0, 1, 0, 0, 0, 0], + vec![0, 1, 0, 0, 0, 0], + ]); + let c = to_F_matrix::(vec![ + vec![0, 0, 0, 1, 0, 0], + vec![0, 0, 0, 0, 1, 0], + vec![0, 0, 0, 0, 0, 1], + vec![0, 0, 1, 0, 0, 0], + ]); + + R1CS { + l: 1, + A: a, + B: b, + C: c, + } +} + +/// Generates a CCS from a test R1CS matrix with specified parameters +pub fn get_test_ccs(w: usize, l: usize) -> CCS { + let r1cs = get_test_r1cs::(); + CCS::::from_r1cs_padded(r1cs, w, l) +} + +#[allow(clippy::type_complexity)] +fn setup_example_environment< + const C: usize, + RqNTT: SuitableRing, + DP: DecompositionParams, + const W: usize, + const WIT_LEN: usize, + CS: LatticefoldChallengeSet, +>() -> ( + LCCCS, + Witness, + CCCS, + Witness, + CCS, + AjtaiCommitmentScheme, +) { + let ccs = get_test_ccs::(W, DP::L); + let mut rng = test_rng(); + let (_, x_ccs, w_ccs) = get_test_z_split::(rng.gen_range(0..64)); + let scheme = AjtaiCommitmentScheme::rand(&mut rng); + + let wit_i = Witness::from_w_ccs::(w_ccs); + let cm_i = CCCS { + cm: wit_i.commit::(&scheme).unwrap(), + x_ccs: x_ccs.clone(), + }; + + let rand_w_ccs: Vec = (0..WIT_LEN).map(|i| RqNTT::from(i as u64)).collect(); + let wit_acc = Witness::from_w_ccs::(rand_w_ccs); + + let mut transcript = PoseidonTranscript::::default(); + let (acc, _) = LFLinearizationProver::<_, PoseidonTranscript>::prove( + &cm_i, + &wit_acc, + &mut transcript, + &ccs, + ) + .unwrap(); + + (acc, wit_acc, cm_i, wit_i, ccs, scheme) +} + +type RqNTT = GoldilocksRingNTT; +type CS = GoldilocksChallengeSet; +type T = PoseidonTranscript; + +fn main() { + println!("Setting up example environment..."); + + println!("Decomposition parameters:"); + println!("\tB: {}", ExampleDP::B); + println!("\tL: {}", ExampleDP::L); + println!("\tB_SMALL: {}", ExampleDP::B_SMALL); + println!("\tK: {}", ExampleDP::K); + + let (acc, wit_acc, cm_i, wit_i, ccs, scheme) = + setup_example_environment::(); + + let mut prover_transcript = PoseidonTranscript::::default(); + let mut verifier_transcript = PoseidonTranscript::::default(); + println!("Generating proof..."); + let start = Instant::now(); + + let (_, _, proof) = NIFSProver::::prove( + &acc, + &wit_acc, + &cm_i, + &wit_i, + &mut prover_transcript, + &ccs, + &scheme, + ) + .unwrap(); + let duration = start.elapsed(); + println!("Proof generated in {:?}", duration); + + let mut serialized_proof = Vec::new(); + + println!("Serializing proof (with compression)..."); + proof + .serialize_with_mode(&mut serialized_proof, Compress::Yes) + .unwrap(); + let compressed_size = serialized_proof.len(); + println!( + "Proof size (with compression) size: {}", + humansize::format_size(compressed_size, humansize::BINARY) + ); + + println!("Serializing proof (without compression)..."); + proof + .serialize_with_mode(&mut serialized_proof, Compress::No) + .unwrap(); + let uncompressed_size = serialized_proof.len(); + println!( + "Proof (without compression) size: {}", + humansize::format_size(uncompressed_size, humansize::BINARY) + ); + + println!("Verifying proof"); + let start = Instant::now(); + NIFSVerifier::::verify( + &acc, + &cm_i, + &proof, + &mut verifier_transcript, + &ccs, + ) + .unwrap(); + let duration = start.elapsed(); + println!("Proof verified in {:?}", duration); +} diff --git a/latticefold/src/nifs.rs b/latticefold/src/nifs.rs index 6f0d244f..14d62d7b 100644 --- a/latticefold/src/nifs.rs +++ b/latticefold/src/nifs.rs @@ -1,4 +1,6 @@ +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::marker::PhantomData; +use ark_std::vec::Vec; use cyclotomic_rings::rings::SuitableRing; use lattirust_ring::OverField; @@ -27,7 +29,7 @@ mod tests; /// `C` is the length of Ajtai commitment vectors. /// `NTT` is a cyclotomic ring in the NTT form. -#[derive(Clone)] +#[derive(Clone, CanonicalSerialize, CanonicalDeserialize)] pub struct LFProof { pub linearization_proof: LinearizationProof, pub decomposition_proof_l: DecompositionProof,