From 3620a1e52fc49330b721169cada56832462a801a Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 7 Mar 2025 13:26:33 -0700 Subject: [PATCH 1/9] nit: prune labels `true` --- .github/workflows/sync_labels.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync_labels.yaml b/.github/workflows/sync_labels.yaml index 99c9651..7bed2c9 100644 --- a/.github/workflows/sync_labels.yaml +++ b/.github/workflows/sync_labels.yaml @@ -23,4 +23,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: manifest: .github/labels.yaml - prune: false \ No newline at end of file + prune: true \ No newline at end of file From c3f579846df69587759f6083e0da5da6a4292b22 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 11 Mar 2025 06:07:09 -0600 Subject: [PATCH 2/9] feat: setup demo --- Cargo.lock | 100 ++++++++++++++++++- Cargo.toml | 5 +- demo/Cargo.toml | 9 ++ demo/src/main.rs | 164 +++++++++++++++++++++++++++++++ frontend/Cargo.toml | 1 + frontend/src/noir.rs | 5 +- frontend/src/program.rs | 8 +- frontend/tests/end_to_end/mod.rs | 3 +- frontend/tests/ivc/mod.rs | 3 +- prover/Cargo.toml | 5 +- taplo.toml | 2 +- 11 files changed, 287 insertions(+), 18 deletions(-) create mode 100644 demo/Cargo.toml create mode 100644 demo/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 35eddd7..c7e2cf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,12 +114,56 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + [[package]] name = "anstyle" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + [[package]] name = "anyhow" version = "1.0.89" @@ -527,6 +571,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] @@ -535,8 +580,22 @@ version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" dependencies = [ + "anstream", "anstyle", "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.98", ] [[package]] @@ -545,6 +604,12 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "const-oid" version = "0.9.6" @@ -664,6 +729,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "demo" +version = "0.1.0" +dependencies = [ + "bincode", + "clap", + "edge-frontend", +] + [[package]] name = "der" version = "0.6.1" @@ -1103,6 +1177,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1173,6 +1253,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "iter-extended" version = "1.0.0-beta.2" @@ -2067,6 +2153,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.24.1" @@ -2079,7 +2171,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -2367,6 +2459,12 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "valuable" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index b016d36..7548a34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members =["prover", "frontend"] +members =["prover", "frontend","demo"] resolver="2" [workspace.dependencies] @@ -34,9 +34,6 @@ ref-cast ="1.0.20" static_assertions="1.1.0" rayon-scan ="0.1.0" hex ="0.4.3" - -# grumpkin-msm has been patched to support MSMs for the pasta curve cycle -# see: https://github.com/argumentcomputer/grumpkin-msm/pull/3 grumpkin-msm={ git="https://github.com/argumentcomputer/grumpkin-msm", branch="dev" } # wasm32 dependencies diff --git a/demo/Cargo.toml b/demo/Cargo.toml new file mode 100644 index 0000000..123fac8 --- /dev/null +++ b/demo/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name ="demo" +version="0.1.0" +edition="2021" + +[dependencies] +edge-frontend={ path="../frontend", features=["demo"] } +clap = {version = "4.5", features = ["derive"]} +bincode = {workspace = true} diff --git a/demo/src/main.rs b/demo/src/main.rs new file mode 100644 index 0000000..f291abd --- /dev/null +++ b/demo/src/main.rs @@ -0,0 +1,164 @@ +use std::{fs, path::PathBuf}; + +use clap::{Parser, Subcommand}; +use edge_frontend::{ + demo, + demo::{collatz_even, collatz_odd}, + program::{self, Configuration, Switchboard, RAM, Z0_SECONDARY}, + setup::Setup, + CompressedSNARK, Scalar, +}; + +#[derive(Parser)] +#[command(author, version, about = "Demo application for edge-frontend", long_about = None)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// Run the offline setup phase + Setup { + /// Path to save the setup file + #[arg(short, long, default_value = "setup.bytes")] + output: PathBuf, + }, + /// Generate and compress a proof + Prove { + /// Input value for the Collatz program + #[arg(short, long)] + input: u64, + + /// Path to the setup file + #[arg(short, long, default_value = "setup.bytes")] + setup: PathBuf, + + /// Path to save the proof file + #[arg(short, long, default_value = "proof.bytes")] + output: PathBuf, + }, + /// Verify a compressed proof + Verify { + /// Input value for the Collatz program + #[arg(short, long)] + input: u64, + + /// Path to the setup file + #[arg(short, long, default_value = "setup.bytes")] + setup: PathBuf, + + /// Path to the proof file + #[arg(short, long, default_value = "proof.bytes")] + proof: PathBuf, + }, +} + +fn main() -> Result<(), Box> { + let cli = Cli::parse(); + + match cli.command { + Commands::Setup { output } => { + println!("🔧 Running offline setup phase..."); + + // Step 1: Create demo programs + let collatz_even = collatz_even(); + let collatz_odd = collatz_odd(); + println!("✅ Created demo programs"); + + // Step 2: Create switchboard + let switchboard = Switchboard::::new(vec![collatz_even, collatz_odd]); + println!("✅ Created switchboard"); + + // Step 3: Initialize the setup + let setup = Setup::new(switchboard)?; + println!("✅ Initialized setup"); + + // Step 4: Save the setup to a file + setup.store_file(&output)?; + println!("✅ Saved setup to file: {}", output.display()); + + Ok(()) + }, + Commands::Prove { input, setup, output } => { + println!("🔍 Running proving phase..."); + + // Step 1: Read the setup from the file + let psetup = Setup::load_file(&setup)?; + println!("✅ Loaded setup from file: {}", setup.display()); + + // Step 2: Create demo programs + let collatz_even = collatz_even(); + let collatz_odd = collatz_odd(); + println!("✅ Created demo programs"); + + // Step 3: Create and prepare the switchboard for proving + let switchboard = Switchboard::::new( + vec![collatz_even, collatz_odd], + vec![Scalar::from(input)], + (input % 2) as usize, + ); + let psetup = psetup.into_ready(switchboard); + println!("✅ Prepared setup for proving"); + + // Step 4: Generate the proof + let recursive_snark = program::run(&psetup)?; + println!("✅ Generated recursive SNARK"); + + // Step 5: Compress the proof + let compressed_proof = program::compress(&psetup, &recursive_snark)?; + println!("✅ Compressed the proof"); + + // Step 6: Serialize and store the proof + let serialized_proof = bincode::serialize(&compressed_proof)?; + fs::write(&output, &serialized_proof)?; + println!("✅ Saved proof to file: {}", output.display()); + + Ok(()) + }, + Commands::Verify { input, setup, proof } => { + println!("🔐 Running verification phase..."); + + // Step 1: Read the setup from the file + let vsetup = Setup::load_file(&setup)?; + println!("✅ Loaded setup from file: {}", setup.display()); + + // Step 2: Read and deserialize the proof + let proof_bytes = fs::read(&proof)?; + let compressed_proof: CompressedSNARK = bincode::deserialize(&proof_bytes)?; + println!("✅ Loaded proof from file: {}", proof.display()); + + // Step 3: Create demo programs (needed for switchboard) + let swap_memory_program = demo::swap_memory(); + let square_program = demo::square_zeroth(); + + // Step 4: Create and prepare the switchboard for verification + let vswitchboard = + Switchboard::::new(vec![swap_memory_program, square_program]); + let vsetup = vsetup.into_ready(vswitchboard); + + // Step 5: Get the verifier key + let vk = vsetup.verifier_key()?; + println!("✅ Prepared verification key"); + + // Step 6: Verify the proof + println!("⚠️ Note: In a real verification scenario, z0 values would be provided separately"); + println!( + "⚠️ For this demo, we're using placeholder values which will cause verification to fail" + ); + + let z0_primary = [Scalar::from(input)]; + + match compressed_proof.verify(&vsetup.params, &vk, &z0_primary, Z0_SECONDARY) { + Ok(_) => { + println!("✅ Proof verification successful!"); + Ok(()) + }, + Err(e) => { + println!("❌ Proof verification failed: {e}"); + Err(e.into()) + }, + } + }, + } +} diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index 2088bbc..62e1084 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -26,3 +26,4 @@ bincode ={ workspace=true } [features] demo=[] +default=[] diff --git a/frontend/src/noir.rs b/frontend/src/noir.rs index af31db2..25049c2 100644 --- a/frontend/src/noir.rs +++ b/frontend/src/noir.rs @@ -13,9 +13,9 @@ use std::collections::{BTreeMap, HashMap}; +pub use acvm::acir::acir_field::GenericFieldElement; use acvm::{ acir::{ - acir_field::GenericFieldElement, circuit::{brillig::BrilligBytecode, Circuit, Opcode, Program}, native_types::{Witness, WitnessMap}, }, @@ -27,7 +27,8 @@ use ark_bn254::Fr; use bellpepper_core::{num::AllocatedNum, ConstraintSystem, LinearCombination, SynthesisError}; use edge_prover::supernova::StepCircuit; use halo2curves::ff::PrimeField; -use noirc_abi::{input_parser::InputValue, Abi, AbiType, InputMap}; +pub use noirc_abi::{input_parser::InputValue, InputMap}; +use noirc_abi::{Abi, AbiType}; use tracing::{error, trace}; use super::*; diff --git a/frontend/src/program.rs b/frontend/src/program.rs index 50bfb6c..b96b4cc 100644 --- a/frontend/src/program.rs +++ b/frontend/src/program.rs @@ -34,6 +34,8 @@ use crate::{ setup::{Ready, Setup}, }; +pub const Z0_SECONDARY: &[grumpkin::Fr] = &[grumpkin::Fr::ZERO]; + /// Trait for memory models used in the NIVC system /// /// This trait is sealed, meaning it can only be implemented by the types in this crate @@ -288,7 +290,6 @@ pub fn run_rom(setup: &Setup>) -> Result, Frontend info!("Starting SuperNova program with ROM memory model..."); let z0_primary = &setup.switchboard.public_input; - let z0_secondary = &[grumpkin::Fr::ZERO]; let time = std::time::Instant::now(); let mut recursive_snark: Option> = None; @@ -299,7 +300,7 @@ pub fn run_rom(setup: &Setup>) -> Result, Frontend // TODO: We should not clone the witness here recursive_snark = - prove_single_step(setup, recursive_snark, Some(witness.clone()), z0_primary, z0_secondary)?; + prove_single_step(setup, recursive_snark, Some(witness.clone()), z0_primary, Z0_SECONDARY)?; } trace!("Recursive loop of `program::run()` elapsed: {:?}", time.elapsed()); @@ -326,7 +327,6 @@ pub fn run_ram(setup: &Setup>) -> Result, Frontend info!("Starting SuperNova program with RAM memory model..."); let z0_primary = &setup.switchboard.public_input; - let z0_secondary = &[grumpkin::Fr::ZERO]; let time = std::time::Instant::now(); let mut recursive_snark: Option> = None; @@ -347,7 +347,7 @@ pub fn run_ram(setup: &Setup>) -> Result, Frontend recursive_snark, None, // RAM doesn't use predefined witness values z0_primary, - z0_secondary, + Z0_SECONDARY, )?; } diff --git a/frontend/tests/end_to_end/mod.rs b/frontend/tests/end_to_end/mod.rs index 6c8d4be..0488c0a 100644 --- a/frontend/tests/end_to_end/mod.rs +++ b/frontend/tests/end_to_end/mod.rs @@ -1,13 +1,12 @@ use std::fs; -use acvm::acir::acir_field::GenericFieldElement; use edge_frontend::{ demo, + noir::{GenericFieldElement, InputMap, InputValue}, program::{self, Configuration, Switchboard}, setup::Setup, CompressedSNARK, Scalar, }; -use noirc_abi::{input_parser::InputValue, InputMap}; use tempfile::tempdir; use super::*; diff --git a/frontend/tests/ivc/mod.rs b/frontend/tests/ivc/mod.rs index 6ac0ea7..939f1ed 100644 --- a/frontend/tests/ivc/mod.rs +++ b/frontend/tests/ivc/mod.rs @@ -1,12 +1,11 @@ -use acvm::acir::acir_field::GenericFieldElement; use edge_frontend::{ + noir::{GenericFieldElement, InputMap, InputValue}, program::{compress, run, Switchboard, RAM, ROM}, setup::Setup, Scalar, }; use edge_prover::supernova::snark::CompressedSNARK; use halo2curves::{ff::Field, grumpkin}; -use noirc_abi::{input_parser::InputValue, InputMap}; use super::*; diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 626663e..a653d72 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -40,11 +40,12 @@ rand ={ workspace=true } ref-cast ={ workspace=true } static_assertions={ workspace=true } rayon-scan ={ workspace=true } +grumpkin-msm ={ workspace=true } -[target.'cfg(any(target_arch = "x86_64", target_arch = "aarch64"))'.dependencies] +# [target.'cfg(any(target_arch = "x86_64", target_arch = "aarch64"))'.dependencies] # grumpkin-msm has been patched to support MSMs for the pasta curve cycle # see: https://github.com/argumentcomputer/grumpkin-msm/pull/3 -grumpkin-msm={ workspace=true } +# grumpkin-msm={ workspace=true } [target.'cfg(target_arch = "wasm32")'.dependencies] getrandom={ workspace=true } diff --git a/taplo.toml b/taplo.toml index f58bb47..39051ed 100644 --- a/taplo.toml +++ b/taplo.toml @@ -13,7 +13,7 @@ column_width=100 # remove whitespace around '=' compact_entries=true # alphabetically sort entries not separated by line breaks -reorder_keys=false +reorder_keys=true # align entries vertically (default: true) # align_comments =false # expand arrays into multiple lines (default: true) From 7306b9e3f26b40f7138e76ea5bd3636bb607182e Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 11 Mar 2025 11:36:41 -0600 Subject: [PATCH 3/9] feat: demo --- .gitignore | 3 +- Cargo.lock | 2 ++ Cargo.toml | 64 ++++++++++++++++++++--------------------- demo/Cargo.toml | 10 ++++--- demo/src/main.rs | 57 +++++++++++++++++++++++++++++------- frontend/Cargo.toml | 15 +++++----- frontend/src/lib.rs | 2 +- frontend/src/program.rs | 1 + prover/Cargo.toml | 64 ++++++++++++++++++++--------------------- 9 files changed, 129 insertions(+), 89 deletions(-) diff --git a/.gitignore b/.gitignore index 5d0c59e..05e103c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target/* -.DS_Store \ No newline at end of file +.DS_Store +*.bytes \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c7e2cf9..1041edd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -736,6 +736,8 @@ dependencies = [ "bincode", "clap", "edge-frontend", + "tracing", + "tracing-subscriber", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7548a34..8887894 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,40 +1,40 @@ [workspace] -members =["prover", "frontend","demo"] +members =["prover", "frontend", "demo"] resolver="2" [workspace.dependencies] -bellpepper-core ={ version="0.4.0", default-features=false } bellpepper ={ git="https://github.com/argumentcomputer/bellpepper", branch="dev", default-features=false } -ff ={ version="0.13.0", features=["derive"] } +bellpepper-core ={ version="0.4.0", default-features=false } +bincode ="1.3" +bitvec ="1.0" +byteorder ="1.4.3" +cfg-if ="1.0.0" digest ="0.10" +ff ={ version="0.13.0", features=["derive"] } +generic-array ="1.0.0" +group ="0.13.0" +grumpkin-msm ={ git="https://github.com/argumentcomputer/grumpkin-msm", branch="dev" } halo2curves ={ version="0.6.0", features=["bits", "derive_serde"] } -sha3 ="0.10" -rayon ="1.7" -rand_core ={ version="0.6", default-features=false } -rand_chacha ="0.3" -subtle ="2.5" +hex ="0.4.3" +itertools ="0.13.0" # zip_eq neptune ={ git="https://github.com/argumentcomputer/neptune", branch="dev", default-features=false } -generic-array ="1.0.0" num-bigint ={ version="0.4", features=["serde", "rand"] } -num-traits ="0.2" num-integer ="0.1" -serde ={ version="1.0", features=["derive", "rc"] } -bincode ="1.3" -bitvec ="1.0" -byteorder ="1.4.3" -thiserror ="1.0" -group ="0.13.0" -pairing ="0.23.0" -tracing ="0.1.37" -cfg-if ="1.0.0" +num-traits ="0.2" once_cell ="1.18.0" -itertools ="0.13.0" # zip_eq +pairing ="0.23.0" rand ="0.8.5" +rand_chacha ="0.3" +rand_core ={ version="0.6", default-features=false } +rayon ="1.7" +rayon-scan ="0.1.0" ref-cast ="1.0.20" # allocation-less conversion in multilinear polys # lightens impl macros for pasta +serde ={ version="1.0", features=["derive", "rc"] } +sha3 ="0.10" static_assertions="1.1.0" -rayon-scan ="0.1.0" -hex ="0.4.3" -grumpkin-msm={ git="https://github.com/argumentcomputer/grumpkin-msm", branch="dev" } +subtle ="2.5" +thiserror ="1.0" +tracing ="0.1.37" # wasm32 dependencies getrandom={ version="0.2.0", default-features=false, features=["js"] } @@ -46,26 +46,26 @@ proptest="1.2.0" criterion={ version="0.5", features=["html_reports"] } # dev dependencies +anyhow ="1.0.72" +expect-test ="1.4.1" flate2 ="1.0" +handlebars ="5.1.0" +serde_json ="1.0.1" sha2 ="0.10.7" -tracing-test ={ version="0.2.4", features=["no-env-filter"] } -expect-test ="1.4.1" -anyhow ="1.0.72" tap ="1.0.1" -tracing-texray ="0.2.0" tracing-subscriber={ version="0.3.17", features=["env-filter"] } -handlebars ="5.1.0" -serde_json ="1.0.1" +tracing-test ={ version="0.2.4", features=["no-env-filter"] } +tracing-texray ="0.2.0" [profile.release] -lto =true codegen-units=1 +lto =true panic ="abort" [profile.dev] -opt-level =1 +codegen-units=256 debug =true incremental =true -codegen-units=256 lto =false +opt-level =1 panic ="unwind" diff --git a/demo/Cargo.toml b/demo/Cargo.toml index 123fac8..8185ec8 100644 --- a/demo/Cargo.toml +++ b/demo/Cargo.toml @@ -1,9 +1,11 @@ [package] +edition="2021" name ="demo" version="0.1.0" -edition="2021" [dependencies] -edge-frontend={ path="../frontend", features=["demo"] } -clap = {version = "4.5", features = ["derive"]} -bincode = {workspace = true} +bincode ={ workspace=true } +clap ={ version="4.5", features=["derive"] } +edge-frontend ={ path="../frontend", features=["demo"] } +tracing ={ workspace=true } +tracing-subscriber={ workspace=true } diff --git a/demo/src/main.rs b/demo/src/main.rs index f291abd..1a9a795 100644 --- a/demo/src/main.rs +++ b/demo/src/main.rs @@ -2,13 +2,54 @@ use std::{fs, path::PathBuf}; use clap::{Parser, Subcommand}; use edge_frontend::{ - demo, - demo::{collatz_even, collatz_odd}, + noir::NoirProgram, program::{self, Configuration, Switchboard, RAM, Z0_SECONDARY}, setup::Setup, CompressedSNARK, Scalar, }; +/// Creates a Noir program that is the even case of the function in the Collatz conjecture. +pub fn collatz_even() -> NoirProgram { + let path = std::path::PathBuf::from("target/collatz_even.json"); + + // Get the current working directory + let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); + let absolute_path = current_dir.join(&path); + + match std::fs::read(&path) { + Ok(bytecode) => NoirProgram::new(&bytecode), + Err(e) => { + panic!( + "Failed to read Noir program file.\nRelative path: '{}'\nAbsolute path: '{}'\nError: {}", + path.display(), + absolute_path.display(), + e + ); + }, + } +} + +/// Creates a Noir program that is the odd case of the function in the Collatz conjecture. +pub fn collatz_odd() -> NoirProgram { + let path = std::path::PathBuf::from("target/collatz_odd.json"); + + // Get the current working directory + let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); + let absolute_path = current_dir.join(&path); + + match std::fs::read(&path) { + Ok(bytecode) => NoirProgram::new(&bytecode), + Err(e) => { + panic!( + "Failed to read Noir program file.\nRelative path: '{}'\nAbsolute path: '{}'\nError: {}", + path.display(), + absolute_path.display(), + e + ); + }, + } +} + #[derive(Parser)] #[command(author, version, about = "Demo application for edge-frontend", long_about = None)] struct Cli { @@ -129,12 +170,11 @@ fn main() -> Result<(), Box> { println!("✅ Loaded proof from file: {}", proof.display()); // Step 3: Create demo programs (needed for switchboard) - let swap_memory_program = demo::swap_memory(); - let square_program = demo::square_zeroth(); + let collatz_even = collatz_even(); + let collatz_odd = collatz_odd(); // Step 4: Create and prepare the switchboard for verification - let vswitchboard = - Switchboard::::new(vec![swap_memory_program, square_program]); + let vswitchboard = Switchboard::::new(vec![collatz_even, collatz_odd]); let vsetup = vsetup.into_ready(vswitchboard); // Step 5: Get the verifier key @@ -142,11 +182,6 @@ fn main() -> Result<(), Box> { println!("✅ Prepared verification key"); // Step 6: Verify the proof - println!("⚠️ Note: In a real verification scenario, z0 values would be provided separately"); - println!( - "⚠️ For this demo, we're using placeholder values which will cause verification to fail" - ); - let z0_primary = [Scalar::from(input)]; match compressed_proof.verify(&vsetup.params, &vk, &z0_primary, Z0_SECONDARY) { diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index 62e1084..7efd9b0 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -1,29 +1,28 @@ [package] +edition="2021" name ="edge-frontend" version="0.1.0" -edition="2021" [dependencies] +bellpepper-core={ workspace=true } edge-prover ={ path="../prover" } +halo2curves ={ workspace=true } serde ={ workspace=true } serde_json ={ workspace=true } thiserror ={ workspace=true } tracing ={ workspace=true } -bellpepper-core={ workspace=true } -halo2curves ={ workspace=true } # noir acvm ={ git="https://github.com/noir-lang/noir", rev="v1.0.0-beta.2" } -noirc_abi={ git="https://github.com/noir-lang/noir", rev="v1.0.0-beta.2" } ark-bn254="0.5" +noirc_abi={ git="https://github.com/noir-lang/noir", rev="v1.0.0-beta.2" } [dev-dependencies] -tracing-test ={ workspace=true } -tempdir ="0.3.7" +bincode ={ workspace=true } edge-frontend={ path=".", features=["demo"] } +tempdir ="0.3.7" tempfile ="3.17" -bincode ={ workspace=true } +tracing-test ={ workspace=true } [features] demo=[] -default=[] diff --git a/frontend/src/lib.rs b/frontend/src/lib.rs index 0d44a6d..7895e45 100644 --- a/frontend/src/lib.rs +++ b/frontend/src/lib.rs @@ -19,7 +19,7 @@ //! The crate uses several cryptographic backends: //! - Primary curve: bn254 (also known as BN256) //! - Secondary curve: Grumpkin -//! - Proof systems: SuperNova, Spartan R1CS SNARKs +//! - Proof systems: ``SuperNova``, Spartan R1CS SNARKs //! //! ## Memory Models //! diff --git a/frontend/src/program.rs b/frontend/src/program.rs index b96b4cc..35bb9a3 100644 --- a/frontend/src/program.rs +++ b/frontend/src/program.rs @@ -34,6 +34,7 @@ use crate::{ setup::{Ready, Setup}, }; +/// Input for the secondary circuit in the NIVC system for all proofs. pub const Z0_SECONDARY: &[grumpkin::Fr] = &[grumpkin::Fr::ZERO]; /// Trait for memory models used in the NIVC system diff --git a/prover/Cargo.toml b/prover/Cargo.toml index a653d72..2cf7dca 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -1,46 +1,46 @@ [package] -name ="edge-prover" -version ="0.1.0" authors =["Pluto Engineering"] -edition ="2021" description ="Client side proving" +edition ="2021" +keywords =["zkSNARKs", "cryptography", "proofs"] +license-file="LICENSE" +name ="edge-prover" readme ="README.md" repository ="https://github.com/pluto/edge" -license-file="LICENSE" -keywords =["zkSNARKs", "cryptography", "proofs"] +version ="0.1.0" [dependencies] -bellpepper-core ={ workspace=true } bellpepper ={ workspace=true } -ff ={ workspace=true } +bellpepper-core ={ workspace=true } +bincode ={ workspace=true } +bitvec ={ workspace=true } +byteorder ={ workspace=true } +cfg-if ={ workspace=true } digest ={ workspace=true } +ff ={ workspace=true } +generic-array ={ workspace=true } +group ={ workspace=true } +grumpkin-msm ={ workspace=true } halo2curves ={ workspace=true } -sha3 ={ workspace=true } -rayon ={ workspace=true } -rand_core ={ workspace=true } -rand_chacha ={ workspace=true } -subtle ={ workspace=true } +itertools ={ workspace=true } neptune ={ workspace=true } -generic-array ={ workspace=true } num-bigint ={ workspace=true } -num-traits ={ workspace=true } num-integer ={ workspace=true } -serde ={ workspace=true } -bincode ={ workspace=true } -bitvec ={ workspace=true } -byteorder ={ workspace=true } -thiserror ={ workspace=true } -group ={ workspace=true } -pairing ={ workspace=true } -tracing ={ workspace=true } -cfg-if ={ workspace=true } +num-traits ={ workspace=true } once_cell ={ workspace=true } -itertools ={ workspace=true } +pairing ={ workspace=true } rand ={ workspace=true } +rand_chacha ={ workspace=true } +rand_core ={ workspace=true } +rayon ={ workspace=true } +rayon-scan ={ workspace=true } ref-cast ={ workspace=true } +serde ={ workspace=true } +sha3 ={ workspace=true } static_assertions={ workspace=true } -rayon-scan ={ workspace=true } -grumpkin-msm ={ workspace=true } +subtle ={ workspace=true } +thiserror ={ workspace=true } +tracing ={ workspace=true } # [target.'cfg(any(target_arch = "x86_64", target_arch = "aarch64"))'.dependencies] # grumpkin-msm has been patched to support MSMs for the pasta curve cycle @@ -57,17 +57,17 @@ proptest={ workspace=true } criterion={ version="0.5", features=["html_reports"] } [dev-dependencies] +anyhow ={ workspace=true } +expect-test ={ workspace=true } flate2 ={ workspace=true } +handlebars ={ workspace=true } hex ={ workspace=true } +serde_json ={ workspace=true } sha2 ={ workspace=true } -tracing-test ={ workspace=true } -expect-test ={ workspace=true } -anyhow ={ workspace=true } tap ={ workspace=true } -tracing-texray ={ workspace=true } tracing-subscriber={ workspace=true } -handlebars ={ workspace=true } -serde_json ={ workspace=true } +tracing-test ={ workspace=true } +tracing-texray ={ workspace=true } # [build-dependencies] # vergen = { workspace = true } From 005ff44a4517e60cec321e348908ec1155c4e7de Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 11 Mar 2025 12:00:08 -0600 Subject: [PATCH 4/9] feat: logging --- demo/src/main.rs | 100 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 19 deletions(-) diff --git a/demo/src/main.rs b/demo/src/main.rs index 1a9a795..237a10a 100644 --- a/demo/src/main.rs +++ b/demo/src/main.rs @@ -7,6 +7,8 @@ use edge_frontend::{ setup::Setup, CompressedSNARK, Scalar, }; +use tracing::{debug, info, trace, Level}; +use tracing_subscriber::{fmt, prelude::*, EnvFilter}; /// Creates a Noir program that is the even case of the function in the Collatz conjecture. pub fn collatz_even() -> NoirProgram { @@ -53,6 +55,10 @@ pub fn collatz_odd() -> NoirProgram { #[derive(Parser)] #[command(author, version, about = "Demo application for edge-frontend", long_about = None)] struct Cli { + /// Verbosity level (-v = info, -vv = debug, -vvv = trace) + #[arg( short,long, action = clap::ArgAction::Count, global = true)] + verbose: u8, + #[command(subcommand)] command: Commands, } @@ -95,102 +101,158 @@ enum Commands { }, } +fn setup_logging(verbosity: u8) { + let level = match verbosity { + 0 => Level::WARN, + 1 => Level::INFO, + 2 => Level::DEBUG, + _ => Level::TRACE, + }; + + // Create a custom filter + let filter = EnvFilter::from_default_env() + .add_directive(format!("edge_frontend={}", level).parse().unwrap()) + .add_directive(format!("edge_prover={}", level).parse().unwrap()) + .add_directive(format!("demo={}", level).parse().unwrap()); + + // Set up the subscriber + tracing_subscriber::registry().with(fmt::layer().with_target(true)).with(filter).init(); + + debug!("Logging initialized at level: {:?}", level); +} + fn main() -> Result<(), Box> { let cli = Cli::parse(); + // Set up logging based on verbosity + setup_logging(cli.verbose); + match cli.command { Commands::Setup { output } => { - println!("🔧 Running offline setup phase..."); + info!("🔧 Running offline setup phase..."); // Step 1: Create demo programs let collatz_even = collatz_even(); let collatz_odd = collatz_odd(); - println!("✅ Created demo programs"); + info!("✅ Created demo programs"); + debug!("Program details - even: {:?}, odd: {:?}", collatz_even, collatz_odd); // Step 2: Create switchboard let switchboard = Switchboard::::new(vec![collatz_even, collatz_odd]); - println!("✅ Created switchboard"); + info!("✅ Created switchboard"); + trace!("Switchboard details: {:?}", switchboard); // Step 3: Initialize the setup let setup = Setup::new(switchboard)?; - println!("✅ Initialized setup"); + info!("✅ Initialized setup"); + trace!("Setup details: {:?}", setup); // Step 4: Save the setup to a file setup.store_file(&output)?; - println!("✅ Saved setup to file: {}", output.display()); + info!("✅ Saved setup to file: {}", output.display()); Ok(()) }, Commands::Prove { input, setup, output } => { - println!("🔍 Running proving phase..."); + info!("🔍 Running proving phase..."); + debug!("Input value: {}", input); // Step 1: Read the setup from the file let psetup = Setup::load_file(&setup)?; - println!("✅ Loaded setup from file: {}", setup.display()); + info!("✅ Loaded setup from file: {}", setup.display()); + trace!("Setup details: {:?}", psetup); // Step 2: Create demo programs let collatz_even = collatz_even(); let collatz_odd = collatz_odd(); - println!("✅ Created demo programs"); + info!("✅ Created demo programs"); + debug!("Program details - even: {:?}, odd: {:?}", collatz_even, collatz_odd); // Step 3: Create and prepare the switchboard for proving + let program_index = (input % 2) as usize; + debug!( + "Using program index: {} ({})", + program_index, + if program_index == 0 { "even" } else { "odd" } + ); + let switchboard = Switchboard::::new( vec![collatz_even, collatz_odd], vec![Scalar::from(input)], - (input % 2) as usize, + program_index, ); + trace!("Switchboard details: {:?}", switchboard); + let psetup = psetup.into_ready(switchboard); - println!("✅ Prepared setup for proving"); + info!("✅ Prepared setup for proving"); + trace!("Ready setup details: {:?}", psetup); // Step 4: Generate the proof + info!("Generating recursive SNARK (this may take a while)..."); let recursive_snark = program::run(&psetup)?; - println!("✅ Generated recursive SNARK"); + info!("✅ Generated recursive SNARK"); + trace!("Recursive SNARK details: {:?}", recursive_snark); // Step 5: Compress the proof + info!("Compressing proof (this may take a while)..."); let compressed_proof = program::compress(&psetup, &recursive_snark)?; - println!("✅ Compressed the proof"); + info!("✅ Compressed the proof"); + trace!("Compressed proof details: {:?}", compressed_proof); // Step 6: Serialize and store the proof let serialized_proof = bincode::serialize(&compressed_proof)?; fs::write(&output, &serialized_proof)?; - println!("✅ Saved proof to file: {}", output.display()); + info!("✅ Saved proof to file: {}", output.display()); + debug!("Proof size: {} bytes", serialized_proof.len()); Ok(()) }, Commands::Verify { input, setup, proof } => { - println!("🔐 Running verification phase..."); + info!("🔐 Running verification phase..."); + debug!("Input value: {}", input); // Step 1: Read the setup from the file let vsetup = Setup::load_file(&setup)?; - println!("✅ Loaded setup from file: {}", setup.display()); + info!("✅ Loaded setup from file: {}", setup.display()); + trace!("Setup details: {:?}", vsetup); // Step 2: Read and deserialize the proof let proof_bytes = fs::read(&proof)?; + debug!("Proof size: {} bytes", proof_bytes.len()); + let compressed_proof: CompressedSNARK = bincode::deserialize(&proof_bytes)?; - println!("✅ Loaded proof from file: {}", proof.display()); + info!("✅ Loaded proof from file: {}", proof.display()); + trace!("Compressed proof details: {:?}", compressed_proof); // Step 3: Create demo programs (needed for switchboard) let collatz_even = collatz_even(); let collatz_odd = collatz_odd(); + debug!("Program details - even: {:?}, odd: {:?}", collatz_even, collatz_odd); // Step 4: Create and prepare the switchboard for verification let vswitchboard = Switchboard::::new(vec![collatz_even, collatz_odd]); + trace!("Switchboard details: {:?}", vswitchboard); + let vsetup = vsetup.into_ready(vswitchboard); + trace!("Ready setup details: {:?}", vsetup); // Step 5: Get the verifier key let vk = vsetup.verifier_key()?; - println!("✅ Prepared verification key"); + info!("✅ Prepared verification key"); + trace!("Verifier key details: {:?}", vk); // Step 6: Verify the proof let z0_primary = [Scalar::from(input)]; + info!("Verifying proof..."); match compressed_proof.verify(&vsetup.params, &vk, &z0_primary, Z0_SECONDARY) { Ok(_) => { - println!("✅ Proof verification successful!"); + info!("✅ Proof verification successful!"); Ok(()) }, Err(e) => { - println!("❌ Proof verification failed: {e}"); + info!("❌ Proof verification failed: {e}"); + debug!("Verification error details: {:?}", e); Err(e.into()) }, } From b1d018d692ecc93134a92c8def89bd7a67c512fb Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 11 Mar 2025 12:13:05 -0600 Subject: [PATCH 5/9] cleanup --- .gitignore | 3 +- Cargo.lock | 1 + demo/Cargo.toml | 1 + demo/src/main.rs | 144 +++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 138 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 05e103c..32bc916 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target/* .DS_Store -*.bytes \ No newline at end of file +*.bytes +proof.meta.json \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 1041edd..4984982 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -736,6 +736,7 @@ dependencies = [ "bincode", "clap", "edge-frontend", + "serde_json", "tracing", "tracing-subscriber", ] diff --git a/demo/Cargo.toml b/demo/Cargo.toml index 8185ec8..7caa5de 100644 --- a/demo/Cargo.toml +++ b/demo/Cargo.toml @@ -9,3 +9,4 @@ clap ={ version="4.5", features=["derive"] } edge-frontend ={ path="../frontend", features=["demo"] } tracing ={ workspace=true } tracing-subscriber={ workspace=true } +serde_json ={ workspace=true } \ No newline at end of file diff --git a/demo/src/main.rs b/demo/src/main.rs index 237a10a..9d1c968 100644 --- a/demo/src/main.rs +++ b/demo/src/main.rs @@ -1,4 +1,11 @@ -use std::{fs, path::PathBuf}; +use std::{ + fs, + path::PathBuf, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, Mutex, + }, +}; use clap::{Parser, Subcommand}; use edge_frontend::{ @@ -7,8 +14,8 @@ use edge_frontend::{ setup::Setup, CompressedSNARK, Scalar, }; -use tracing::{debug, info, trace, Level}; -use tracing_subscriber::{fmt, prelude::*, EnvFilter}; +use tracing::{debug, info, trace, Level, Subscriber}; +use tracing_subscriber::{fmt, prelude::*, registry::LookupSpan, EnvFilter, Layer}; /// Creates a Noir program that is the even case of the function in the Collatz conjecture. pub fn collatz_even() -> NoirProgram { @@ -56,7 +63,7 @@ pub fn collatz_odd() -> NoirProgram { #[command(author, version, about = "Demo application for edge-frontend", long_about = None)] struct Cli { /// Verbosity level (-v = info, -vv = debug, -vvv = trace) - #[arg( short,long, action = clap::ArgAction::Count, global = true)] + #[arg(short, long, action = clap::ArgAction::Count, global = true)] verbose: u8, #[command(subcommand)] @@ -115,8 +122,19 @@ fn setup_logging(verbosity: u8) { .add_directive(format!("edge_prover={}", level).parse().unwrap()) .add_directive(format!("demo={}", level).parse().unwrap()); - // Set up the subscriber - tracing_subscriber::registry().with(fmt::layer().with_target(true)).with(filter).init(); + // Reset the step counter and sequence + STEP_COUNTER.store(0, Ordering::SeqCst); + reset_sequence(); + + // Set up the subscriber with our custom layer + let subscriber = tracing_subscriber::registry() + .with(fmt::layer().with_target(true)) + .with(filter) + .with(StepCounterLayer); + + // Set as the global default + let _guard = + tracing::subscriber::set_global_default(subscriber).expect("Failed to set tracing subscriber"); debug!("Logging initialized at level: {:?}", level); } @@ -185,19 +203,22 @@ fn main() -> Result<(), Box> { let psetup = psetup.into_ready(switchboard); info!("✅ Prepared setup for proving"); - trace!("Ready setup details: {:?}", psetup); // Step 4: Generate the proof info!("Generating recursive SNARK (this may take a while)..."); let recursive_snark = program::run(&psetup)?; - info!("✅ Generated recursive SNARK"); - trace!("Recursive SNARK details: {:?}", recursive_snark); + let step_count = STEP_COUNTER.load(Ordering::SeqCst); + let sequence = get_sequence(); + + // Format the sequence for display + let sequence_str = sequence.join(" → "); + info!("✅ Generated recursive SNARK in {} steps", step_count); + info!("📊 Collatz sequence: {}", sequence_str); // Step 5: Compress the proof info!("Compressing proof (this may take a while)..."); let compressed_proof = program::compress(&psetup, &recursive_snark)?; info!("✅ Compressed the proof"); - trace!("Compressed proof details: {:?}", compressed_proof); // Step 6: Serialize and store the proof let serialized_proof = bincode::serialize(&compressed_proof)?; @@ -205,6 +226,17 @@ fn main() -> Result<(), Box> { info!("✅ Saved proof to file: {}", output.display()); debug!("Proof size: {} bytes", serialized_proof.len()); + // Save step count and sequence to a metadata file + let metadata = serde_json::json!({ + "input": input, + "steps": step_count, + "sequence": sequence + }); + let metadata_path = output + .with_file_name(format!("{}.meta.json", output.file_stem().unwrap().to_string_lossy())); + fs::write(&metadata_path, serde_json::to_string_pretty(&metadata)?)?; + info!("✅ Saved metadata to file: {}", metadata_path.display()); + Ok(()) }, Commands::Verify { input, setup, proof } => { @@ -224,6 +256,24 @@ fn main() -> Result<(), Box> { info!("✅ Loaded proof from file: {}", proof.display()); trace!("Compressed proof details: {:?}", compressed_proof); + // Try to load metadata if available + let metadata_path = + proof.with_file_name(format!("{}.meta.json", proof.file_stem().unwrap().to_string_lossy())); + if metadata_path.exists() { + let metadata_str = fs::read_to_string(&metadata_path)?; + let metadata: serde_json::Value = serde_json::from_str(&metadata_str)?; + + if let Some(steps) = metadata["steps"].as_u64() { + info!("📊 Proof steps: {}", steps); + } + + if let Some(sequence) = metadata["sequence"].as_array() { + let sequence_str: Vec = + sequence.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect(); + info!("📊 Collatz sequence: {}", sequence_str.join(" → ")); + } + } + // Step 3: Create demo programs (needed for switchboard) let collatz_even = collatz_even(); let collatz_odd = collatz_odd(); @@ -243,6 +293,8 @@ fn main() -> Result<(), Box> { // Step 6: Verify the proof let z0_primary = [Scalar::from(input)]; + debug!("z0_primary: {:?}", z0_primary); + debug!("z0_secondary: {:?}", Z0_SECONDARY); info!("Verifying proof..."); match compressed_proof.verify(&vsetup.params, &vk, &z0_primary, Z0_SECONDARY) { @@ -259,3 +311,75 @@ fn main() -> Result<(), Box> { }, } } + +// Below is a custom layer to count steps just for some additional data. +// See this [site](https://www.dcode.fr/collatz-conjecture?__r=1.235e0b9644d4c82309944796365cca7b) for values of the Collatz stopping times and sequences. +// None of this is required for the IVC system, it is just for additional data for fun! + +// Create a static counter for steps +static STEP_COUNTER: AtomicUsize = AtomicUsize::new(0); + +// Create a thread-safe vector to store the sequence +type SequenceType = Arc>>; +thread_local! { + static SEQUENCE: SequenceType = Arc::new(Mutex::new(Vec::new())); +} + +/// A custom layer to count steps and track sequence +struct StepCounterLayer; + +impl Layer for StepCounterLayer +where S: Subscriber + for<'a> LookupSpan<'a> +{ + fn on_event(&self, event: &tracing::Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) { + // Extract the message from the event + let mut message = String::new(); + let mut visitor = MessageVisitor(&mut message); + event.record(&mut visitor); + + // Count steps based on the message + if message.contains("Proving single step") { + STEP_COUNTER.fetch_add(1, Ordering::SeqCst); + } + + // Track program counter for sequence + if message.contains("Program counter = 0") { + SEQUENCE.with(|seq| { + let mut seq = seq.lock().unwrap(); + seq.push("even".to_string()); + }); + } else if message.contains("Program counter = 1") { + SEQUENCE.with(|seq| { + let mut seq = seq.lock().unwrap(); + seq.push("odd".to_string()); + }); + } + } +} + +// Helper to extract message from event +struct MessageVisitor<'a>(&'a mut String); + +impl<'a> tracing::field::Visit for MessageVisitor<'a> { + fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { + if field.name() == "message" { + self.0.push_str(&format!("{:?}", value)); + } + } +} + +// Helper to get the current sequence +fn get_sequence() -> Vec { + SEQUENCE.with(|seq| { + let seq = seq.lock().unwrap(); + seq.clone() + }) +} + +// Helper to reset the sequence +fn reset_sequence() { + SEQUENCE.with(|seq| { + let mut seq = seq.lock().unwrap(); + seq.clear(); + }); +} From c3656bcc2fab6ae73dec82f0a0a0ed7bee1e2007 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 11 Mar 2025 16:35:23 -0600 Subject: [PATCH 6/9] feat: nice demo --- demo/src/counter.rs | 79 ++++++++++++++++++++++++++++++ demo/src/main.rs | 117 ++++++++++---------------------------------- 2 files changed, 104 insertions(+), 92 deletions(-) create mode 100644 demo/src/counter.rs diff --git a/demo/src/counter.rs b/demo/src/counter.rs new file mode 100644 index 0000000..f0897d7 --- /dev/null +++ b/demo/src/counter.rs @@ -0,0 +1,79 @@ +use std::{ + fs, + path::PathBuf, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, Mutex, + }, +}; + +use tracing::{debug, info, trace, Level, Subscriber}; +use tracing_subscriber::{fmt, prelude::*, registry::LookupSpan, EnvFilter, Layer}; + +// Create a static counter for steps +pub static STEP_COUNTER: AtomicUsize = AtomicUsize::new(0); + +// Create a thread-safe vector to store the sequence +type SequenceType = Arc>>; +thread_local! { + static SEQUENCE: SequenceType = Arc::new(Mutex::new(Vec::new())); +} + +/// A custom layer to count steps and track sequence +pub struct StepCounterLayer; + +impl Layer for StepCounterLayer +where S: Subscriber + for<'a> LookupSpan<'a> +{ + fn on_event(&self, event: &tracing::Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) { + // Extract the message from the event + let mut message = String::new(); + let mut visitor = MessageVisitor(&mut message); + event.record(&mut visitor); + + // Count steps based on the message + if message.contains("Proving single step") { + STEP_COUNTER.fetch_add(1, Ordering::SeqCst); + } + + // Track program counter for sequence + if message.contains("Program counter = 0") { + SEQUENCE.with(|seq| { + let mut seq = seq.lock().unwrap(); + seq.push("even".to_string()); + }); + } else if message.contains("Program counter = 1") { + SEQUENCE.with(|seq| { + let mut seq = seq.lock().unwrap(); + seq.push("odd".to_string()); + }); + } + } +} + +// Helper to extract message from event +pub struct MessageVisitor<'a>(&'a mut String); + +impl<'a> tracing::field::Visit for MessageVisitor<'a> { + fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { + if field.name() == "message" { + self.0.push_str(&format!("{:?}", value)); + } + } +} + +// Helper to get the current sequence +pub fn get_sequence() -> Vec { + SEQUENCE.with(|seq| { + let seq = seq.lock().unwrap(); + seq.clone() + }) +} + +// Helper to reset the sequence +pub fn reset_sequence() { + SEQUENCE.with(|seq| { + let mut seq = seq.lock().unwrap(); + seq.clear(); + }); +} diff --git a/demo/src/main.rs b/demo/src/main.rs index 9d1c968..6b77ecd 100644 --- a/demo/src/main.rs +++ b/demo/src/main.rs @@ -1,11 +1,4 @@ -use std::{ - fs, - path::PathBuf, - sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, Mutex, - }, -}; +use std::{fs, path::PathBuf, sync::atomic::Ordering}; use clap::{Parser, Subcommand}; use edge_frontend::{ @@ -14,8 +7,10 @@ use edge_frontend::{ setup::Setup, CompressedSNARK, Scalar, }; -use tracing::{debug, info, trace, Level, Subscriber}; -use tracing_subscriber::{fmt, prelude::*, registry::LookupSpan, EnvFilter, Layer}; +use tracing::{debug, info, trace, Level}; +use tracing_subscriber::{fmt, prelude::*, EnvFilter, Layer}; + +mod counter; /// Creates a Noir program that is the even case of the function in the Collatz conjecture. pub fn collatz_even() -> NoirProgram { @@ -116,21 +111,28 @@ fn setup_logging(verbosity: u8) { _ => Level::TRACE, }; - // Create a custom filter - let filter = EnvFilter::from_default_env() + // Create a display filter based on verbosity + let display_filter = EnvFilter::from_default_env() .add_directive(format!("edge_frontend={}", level).parse().unwrap()) .add_directive(format!("edge_prover={}", level).parse().unwrap()) .add_directive(format!("demo={}", level).parse().unwrap()); + // Create a separate filter for our counter layer that always captures DEBUG level + let counter_filter = EnvFilter::from_default_env() + .add_directive("edge_frontend=debug".parse().unwrap()) + .add_directive("edge_prover=debug".parse().unwrap()) + .add_directive("demo=debug".parse().unwrap()); + // Reset the step counter and sequence - STEP_COUNTER.store(0, Ordering::SeqCst); - reset_sequence(); + counter::STEP_COUNTER.store(0, Ordering::SeqCst); + counter::reset_sequence(); - // Set up the subscriber with our custom layer + // Set up the registry with two layers: + // 1. A display layer with the user-specified verbosity + // 2. A counter layer that always captures DEBUG level events let subscriber = tracing_subscriber::registry() - .with(fmt::layer().with_target(true)) - .with(filter) - .with(StepCounterLayer); + .with(fmt::layer().with_target(true).with_filter(display_filter)) + .with(counter::StepCounterLayer.with_filter(counter_filter)); // Set as the global default let _guard = @@ -203,22 +205,25 @@ fn main() -> Result<(), Box> { let psetup = psetup.into_ready(switchboard); info!("✅ Prepared setup for proving"); + trace!("Ready setup details: {:?}", psetup); // Step 4: Generate the proof info!("Generating recursive SNARK (this may take a while)..."); let recursive_snark = program::run(&psetup)?; - let step_count = STEP_COUNTER.load(Ordering::SeqCst); - let sequence = get_sequence(); + let step_count = counter::STEP_COUNTER.load(Ordering::SeqCst); + let sequence = counter::get_sequence(); // Format the sequence for display let sequence_str = sequence.join(" → "); info!("✅ Generated recursive SNARK in {} steps", step_count); info!("📊 Collatz sequence: {}", sequence_str); + trace!("Recursive SNARK details: {:?}", recursive_snark); // Step 5: Compress the proof info!("Compressing proof (this may take a while)..."); let compressed_proof = program::compress(&psetup, &recursive_snark)?; info!("✅ Compressed the proof"); + trace!("Compressed proof details: {:?}", compressed_proof); // Step 6: Serialize and store the proof let serialized_proof = bincode::serialize(&compressed_proof)?; @@ -311,75 +316,3 @@ fn main() -> Result<(), Box> { }, } } - -// Below is a custom layer to count steps just for some additional data. -// See this [site](https://www.dcode.fr/collatz-conjecture?__r=1.235e0b9644d4c82309944796365cca7b) for values of the Collatz stopping times and sequences. -// None of this is required for the IVC system, it is just for additional data for fun! - -// Create a static counter for steps -static STEP_COUNTER: AtomicUsize = AtomicUsize::new(0); - -// Create a thread-safe vector to store the sequence -type SequenceType = Arc>>; -thread_local! { - static SEQUENCE: SequenceType = Arc::new(Mutex::new(Vec::new())); -} - -/// A custom layer to count steps and track sequence -struct StepCounterLayer; - -impl Layer for StepCounterLayer -where S: Subscriber + for<'a> LookupSpan<'a> -{ - fn on_event(&self, event: &tracing::Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) { - // Extract the message from the event - let mut message = String::new(); - let mut visitor = MessageVisitor(&mut message); - event.record(&mut visitor); - - // Count steps based on the message - if message.contains("Proving single step") { - STEP_COUNTER.fetch_add(1, Ordering::SeqCst); - } - - // Track program counter for sequence - if message.contains("Program counter = 0") { - SEQUENCE.with(|seq| { - let mut seq = seq.lock().unwrap(); - seq.push("even".to_string()); - }); - } else if message.contains("Program counter = 1") { - SEQUENCE.with(|seq| { - let mut seq = seq.lock().unwrap(); - seq.push("odd".to_string()); - }); - } - } -} - -// Helper to extract message from event -struct MessageVisitor<'a>(&'a mut String); - -impl<'a> tracing::field::Visit for MessageVisitor<'a> { - fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { - if field.name() == "message" { - self.0.push_str(&format!("{:?}", value)); - } - } -} - -// Helper to get the current sequence -fn get_sequence() -> Vec { - SEQUENCE.with(|seq| { - let seq = seq.lock().unwrap(); - seq.clone() - }) -} - -// Helper to reset the sequence -fn reset_sequence() { - SEQUENCE.with(|seq| { - let mut seq = seq.lock().unwrap(); - seq.clear(); - }); -} From 729d0f4f6b25fcb0b988ef5d5c4cb7917b457308 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 11 Mar 2025 16:45:38 -0600 Subject: [PATCH 7/9] update readme --- README.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++-- demo/src/main.rs | 4 +-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 33db3b2..8e47170 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,67 @@ ## Project Structure The repository contains several key components: -- `edge-frontend`: Frontend adapters for both Noir and Circom -- `edge-prover`: Backend implementation of the client side prover +- `edge-prover`: Backend implementation of Supernova NIVC folding scheme +- `edge-frontend`: Frontend adapters for Noir to use `edge-prover` +- `demo`: A demo application for the `edge-frontend` and `edge-prover` + +### Prerequisites +Before running the demo, ensure you have: +1. Rust, Noir, and their associated tools installed +2. The Noir programs compiled to JSON (located in the `target/` directory). To do so, just run +``` +nargo compile --workspace +``` +from the root directory. + +### Running the Demo +The demo application has three main commands: setup, prove, and verify. The help command can be used to see the available options. +``` +cargo run -p demo -- --help +``` +The demo application proves sequences to a fun (and unproven) math called the Collatz conjecture. In short, for any positive integer `n`, the sequence is defined as: +``` +if n is 1, stop. +if n is even, repeat this process on n/2. +if n is odd, repeat this process on 3n + 1. +``` +So depending on the starting value, you will find a different sequence of circuits is used to prove the sequence. If you happen to find a case where this proof doesn't work, please let us know -- you may have found a counter example to the conjecture! 😁 + +#### 1. Setup Phase +First, run the offline setup phase: +``` +cargo run -p demo -- setup +``` +This will create a `setup.bytes` file in the current directory. You can specify an output file name as an argument (see help for more details). + +#### 2. Prove Phase +Generate a proof for a specific input value (e.g., 42): +``` +cargo run -p demo -- prove --input 42 +``` +This creates: +- `proof.bytes`: The compressed proof +- `proof.meta.json`: Metadata about the proof, including: + - The input value + - Number of steps in the Collatz sequence + - The complete sequence of even/odd operations +If you'd like, you can run with some logging to see the steps: +``` +cargo run -p demo -- prove --input 42 -v +``` +and to see more logs, you can use `-vv` or `-vvv`. + +#### 3. Verify Phase +To verify a proof, run: +``` +cargo run -p demo -- verify --input 42 +``` +This will verify the proof. Without verbosity, no output implies a valid proof. But if you provided an incorrect input, you will see an error message. For example, if you provide an input of 43, you will see: +``` +ERROR demo: ❌ Proof verification failed: NovaError +Error: NovaError(ProofVerifyError) +``` + ## Usage This repository and its crates are **not** production ready. Do not use them in production. No audits have been done and none are planned. diff --git a/demo/src/main.rs b/demo/src/main.rs index 6b77ecd..e002f30 100644 --- a/demo/src/main.rs +++ b/demo/src/main.rs @@ -7,7 +7,7 @@ use edge_frontend::{ setup::Setup, CompressedSNARK, Scalar, }; -use tracing::{debug, info, trace, Level}; +use tracing::{debug, error, info, trace, Level}; use tracing_subscriber::{fmt, prelude::*, EnvFilter, Layer}; mod counter; @@ -308,7 +308,7 @@ fn main() -> Result<(), Box> { Ok(()) }, Err(e) => { - info!("❌ Proof verification failed: {e}"); + error!("❌ Proof verification failed: {e}"); debug!("Verification error details: {:?}", e); Err(e.into()) }, From af610e2dc1b1361d2fa4b29c84a1f93ef57191f0 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 11 Mar 2025 16:45:52 -0600 Subject: [PATCH 8/9] Update Cargo.toml --- demo/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/Cargo.toml b/demo/Cargo.toml index 7caa5de..86c4353 100644 --- a/demo/Cargo.toml +++ b/demo/Cargo.toml @@ -7,6 +7,6 @@ version="0.1.0" bincode ={ workspace=true } clap ={ version="4.5", features=["derive"] } edge-frontend ={ path="../frontend", features=["demo"] } +serde_json ={ workspace=true } tracing ={ workspace=true } tracing-subscriber={ workspace=true } -serde_json ={ workspace=true } \ No newline at end of file From e52a695120a2be86b6b86a4ee432e2dcdf2ab14b Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 11 Mar 2025 16:48:00 -0600 Subject: [PATCH 9/9] Update counter.rs --- demo/src/counter.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/demo/src/counter.rs b/demo/src/counter.rs index f0897d7..0c51d6b 100644 --- a/demo/src/counter.rs +++ b/demo/src/counter.rs @@ -1,14 +1,10 @@ -use std::{ - fs, - path::PathBuf, - sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, Mutex, - }, +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, Mutex, }; -use tracing::{debug, info, trace, Level, Subscriber}; -use tracing_subscriber::{fmt, prelude::*, registry::LookupSpan, EnvFilter, Layer}; +use tracing::Subscriber; +use tracing_subscriber::{registry::LookupSpan, Layer}; // Create a static counter for steps pub static STEP_COUNTER: AtomicUsize = AtomicUsize::new(0);