Skip to content

Commit

Permalink
Implement Goldilocks example (#147)
Browse files Browse the repository at this point in the history
  • Loading branch information
cupicmarko authored Dec 11, 2024
1 parent 8899ed1 commit b343d97
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 4 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = [ "latticefold", "cyclotomic-rings"]
members = [ "latticefold", "cyclotomic-rings" ]
resolver = "2"

[workspace.package]
Expand All @@ -19,7 +19,7 @@ lattirust-ring = { git = "ssh://[email protected]/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
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
3 changes: 2 additions & 1 deletion latticefold/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -59,6 +59,7 @@ implicit_clone = "warn"
[dev-dependencies]
criterion = "0.5.1"
dhat = "0.3.2"
humansize = "2.1.3"

[[bench]]
name = "ajtai"
Expand Down
48 changes: 48 additions & 0 deletions latticefold/build.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
87 changes: 87 additions & 0 deletions latticefold/examples/README.md
Original file line number Diff line number Diff line change
@@ -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 <example_name>
```

3. Replace `<example_name>` 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 <example_name>
```

3. Replace `<example_name>` 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 <example_name>
```

- For detailed instructions on each example, refer to the example's source code or inline comments.
164 changes: 164 additions & 0 deletions latticefold/examples/goldilocks.rs
Original file line number Diff line number Diff line change
@@ -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<R: Ring>() -> R1CS<R> {
let a = to_F_matrix::<R>(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::<R>(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::<R>(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<R: Ring>(w: usize, l: usize) -> CCS<R> {
let r1cs = get_test_r1cs::<R>();
CCS::<R>::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<RqNTT>,
>() -> (
LCCCS<C, RqNTT>,
Witness<RqNTT>,
CCCS<C, RqNTT>,
Witness<RqNTT>,
CCS<RqNTT>,
AjtaiCommitmentScheme<C, W, RqNTT>,
) {
let ccs = get_test_ccs::<RqNTT>(W, DP::L);
let mut rng = test_rng();
let (_, x_ccs, w_ccs) = get_test_z_split::<RqNTT>(rng.gen_range(0..64));
let scheme = AjtaiCommitmentScheme::rand(&mut rng);

let wit_i = Witness::from_w_ccs::<DP>(w_ccs);
let cm_i = CCCS {
cm: wit_i.commit::<C, W, DP>(&scheme).unwrap(),
x_ccs: x_ccs.clone(),
};

let rand_w_ccs: Vec<RqNTT> = (0..WIT_LEN).map(|i| RqNTT::from(i as u64)).collect();
let wit_acc = Witness::from_w_ccs::<DP>(rand_w_ccs);

let mut transcript = PoseidonTranscript::<RqNTT, CS>::default();
let (acc, _) = LFLinearizationProver::<_, PoseidonTranscript<RqNTT, CS>>::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<RqNTT, CS>;

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::<C, RqNTT, ExampleDP, W, WIT_LEN, CS>();

let mut prover_transcript = PoseidonTranscript::<RqNTT, CS>::default();
let mut verifier_transcript = PoseidonTranscript::<RqNTT, CS>::default();
println!("Generating proof...");
let start = Instant::now();

let (_, _, proof) = NIFSProver::<C, W, RqNTT, ExampleDP, T>::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::<C, RqNTT, ExampleDP, T>::verify(
&acc,
&cm_i,
&proof,
&mut verifier_transcript,
&ccs,
)
.unwrap();
let duration = start.elapsed();
println!("Proof verified in {:?}", duration);
}
4 changes: 3 additions & 1 deletion latticefold/src/nifs.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<const C: usize, NTT: OverField> {
pub linearization_proof: LinearizationProof<NTT>,
pub decomposition_proof_l: DecompositionProof<C, NTT>,
Expand Down

0 comments on commit b343d97

Please sign in to comment.