diff --git a/README.md b/README.md index 0fddf680b..34612a927 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,12 @@ Verify the Noir Proof: cargo run --release --bin noir-r1cs verify ./noir-proof-scheme.nps ./noir-proof.np ``` +Generate inputs for Gnark circuit: + +```sh +cargo run --release --bin noir-r1cs generate-gnark-inputs ./noir-proof-scheme.nps ./noir-proof.np +``` + Recursively verify in a Gnark proof (reads the proof from `../ProveKit/prover/proof`): ```sh diff --git a/noir-r1cs/src/cli/cmd/generate_gnark_inputs.rs b/noir-r1cs/src/cli/cmd/generate_gnark_inputs.rs new file mode 100644 index 000000000..8094865a8 --- /dev/null +++ b/noir-r1cs/src/cli/cmd/generate_gnark_inputs.rs @@ -0,0 +1,63 @@ +use { + crate::Command, + anyhow::{Context, Result}, + argh::FromArgs, + ark_serialize::CanonicalSerialize, + noir_r1cs::{ + create_io_pattern, read, write_gnark_parameters_to_file, NoirProof, NoirProofScheme, + }, + std::{fs::File, io::Write, path::PathBuf}, + tracing::{info, instrument}, +}; + +/// Generate input compatible with gnark. +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand, name = "generate-gnark-inputs")] +pub struct Args { + /// path to the compiled Noir program + #[argh(positional)] + scheme_path: PathBuf, + + /// path to the proof file + #[argh(positional)] + proof_path: PathBuf, +} + +impl Command for Args { + #[instrument(skip_all)] + fn run(&self) -> Result<()> { + // Read the scheme + let scheme: NoirProofScheme = + read(&self.scheme_path).context("while reading Noir proof scheme")?; + let (constraints, witnesses) = scheme.size(); + info!(constraints, witnesses, "Read Noir proof scheme"); + + // Read the proof + let proof: NoirProof = read(&self.proof_path).context("while reading proof")?; + + write_gnark_parameters_to_file( + &scheme.whir.whir_config, + &proof.whir_r1cs_proof.transcript, + &create_io_pattern(scheme.whir.m_0, &scheme.whir.whir_config), + proof.whir_r1cs_proof.whir_query_answer_sums, + scheme.whir.m_0, + scheme.whir.m, + ); + + let mut file = File::create("./prover/proof").unwrap(); + let mut proof_bytes = vec![]; + proof + .whir_r1cs_proof + .whir_proof + .serialize_compressed(&mut proof_bytes) + .unwrap(); + file.write_all(&proof_bytes) + .expect("Writing proof bytes to a file failed"); + + let json = serde_json::to_string_pretty(&scheme.r1cs).unwrap(); // Or `to_string` for compact + let mut file = File::create("r1cs.json")?; + file.write_all(json.as_bytes())?; + + Ok(()) + } +} diff --git a/noir-r1cs/src/cli/cmd/mod.rs b/noir-r1cs/src/cli/cmd/mod.rs index f7c83696d..ce6839da7 100644 --- a/noir-r1cs/src/cli/cmd/mod.rs +++ b/noir-r1cs/src/cli/cmd/mod.rs @@ -1,4 +1,5 @@ mod circuit_stats; +mod generate_gnark_inputs; mod prepare; mod prove; mod verify; @@ -23,6 +24,7 @@ enum Commands { Prove(prove::Args), CircuitStats(circuit_stats::Args), Verify(verify::Args), + GenerateGnarkInputs(generate_gnark_inputs::Args), } impl Command for Args { @@ -38,6 +40,7 @@ impl Command for Commands { Commands::Prove(args) => args.run(), Commands::CircuitStats(args) => args.run(), Commands::Verify(args) => args.run(), + Commands::GenerateGnarkInputs(args) => args.run(), } } } diff --git a/noir-r1cs/src/gnark_config.rs b/noir-r1cs/src/gnark_config.rs index 4061c7469..7f1f053b2 100644 --- a/noir-r1cs/src/gnark_config.rs +++ b/noir-r1cs/src/gnark_config.rs @@ -52,7 +52,7 @@ pub struct GnarkConfig { #[instrument(skip_all)] pub fn gnark_parameters( whir_params: &WhirConfig, - merlin: &ProverState, + transcript: &[u8], io: &IOPattern, sums: [FieldElement; 3], m_0: usize, @@ -89,8 +89,8 @@ pub fn gnark_parameters( whir_params.starting_domain.backing_domain.group_gen() ), io_pattern: String::from_utf8(io.as_bytes().to_vec()).unwrap(), - transcript: merlin.narg_string().to_vec(), - transcript_len: merlin.narg_string().to_vec().len(), + transcript: transcript.to_vec(), + transcript_len: transcript.to_vec().len(), statement_evaluations: vec![ sums[0].to_string(), sums[1].to_string(), @@ -103,13 +103,13 @@ pub fn gnark_parameters( #[instrument(skip_all)] pub fn write_gnark_parameters_to_file( whir_params: &WhirConfig, - merlin: &ProverState, + transcript: &[u8], io: &IOPattern, sums: [FieldElement; 3], m_0: usize, m: usize, ) { - let gnark_config = gnark_parameters(whir_params, merlin, io, sums, m_0, m); + let gnark_config = gnark_parameters(whir_params, transcript, io, sums, m_0, m); println!("round config {:?}", whir_params.round_parameters); let mut file_params = File::create("./prover/params").unwrap(); file_params diff --git a/noir-r1cs/src/lib.rs b/noir-r1cs/src/lib.rs index 702bc7fae..c88ec38d0 100644 --- a/noir-r1cs/src/lib.rs +++ b/noir-r1cs/src/lib.rs @@ -15,13 +15,15 @@ mod whir_r1cs; pub use { crate::{ file::{read, write, FileFormat}, - noir_proof_scheme::NoirProofScheme, + noir_proof_scheme::{NoirProof, NoirProofScheme}, noir_to_r1cs::noir_to_r1cs, r1cs::R1CS, utils::human, }, acir::FieldElement as NoirElement, + gnark_config::write_gnark_parameters_to_file, whir::crypto::fields::Field256 as FieldElement, + whir_r1cs::create_io_pattern, }; use { crate::{ diff --git a/noir-r1cs/src/noir_proof_scheme.rs b/noir-r1cs/src/noir_proof_scheme.rs index 6b440492a..087540e7f 100644 --- a/noir-r1cs/src/noir_proof_scheme.rs +++ b/noir-r1cs/src/noir_proof_scheme.rs @@ -17,14 +17,14 @@ use { /// A scheme for proving a Noir program. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct NoirProofScheme { - r1cs: R1CS, - witness_generator: NoirWitnessGenerator, - whir: WhirR1CSScheme, + pub r1cs: R1CS, + pub witness_generator: NoirWitnessGenerator, + pub whir: WhirR1CSScheme, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct NoirProof { - whir_r1cs_proof: WhirR1CSProof, + pub whir_r1cs_proof: WhirR1CSProof, } impl NoirProofScheme { diff --git a/noir-r1cs/src/whir_r1cs.rs b/noir-r1cs/src/whir_r1cs.rs index 76dcc553f..e905d702a 100644 --- a/noir-r1cs/src/whir_r1cs.rs +++ b/noir-r1cs/src/whir_r1cs.rs @@ -33,7 +33,9 @@ use { domainsep::WhirDomainSeparator, parameters::WhirConfig as GenericWhirConfig, prover::Prover, - statement::{Statement, StatementVerifier as GenericStatementVerifier, VerifierWeights, Weights}, + statement::{ + Statement, StatementVerifier as GenericStatementVerifier, VerifierWeights, Weights, + }, verifier::Verifier, WhirProof as GenericWhirProof, }, @@ -49,26 +51,26 @@ pub type StatementVerifier = GenericStatementVerifier; #[derive(Clone, PartialEq, Serialize, Deserialize)] pub struct WhirR1CSScheme { - m: usize, - m_0: usize, - whir_config: WhirConfig, + pub m: usize, + pub m_0: usize, + pub whir_config: WhirConfig, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct WhirR1CSProof { #[serde(with = "serde_hex")] - transcript: Vec, + pub transcript: Vec, - whir_proof: WhirProof, + pub whir_proof: WhirProof, // TODO: Derive from transcript #[serde(with = "serde_ark")] - whir_query_answer_sums: [FieldElement; 3], + pub whir_query_answer_sums: [FieldElement; 3], } struct DataFromSumcheckVerifier { - r: Vec, - alpha: Vec, + r: Vec, + alpha: Vec, last_sumcheck_val: FieldElement, } @@ -131,8 +133,7 @@ impl WhirR1CSScheme { // First round of sumcheck to reduce R1CS to a batch weighted evaluation of the // witness - let (merlin, alpha) = - run_sumcheck_prover(r1cs, &witness, merlin, self.m_0); + let (merlin, alpha) = run_sumcheck_prover(r1cs, &witness, merlin, self.m_0); // Compute weights from R1CS instance let alphas = calculate_external_row_of_r1cs_matrices(&alpha, r1cs); @@ -157,17 +158,16 @@ impl WhirR1CSScheme { let io = create_io_pattern(self.m_0, &self.whir_config); let mut arthur = io.to_verifier_state(&proof.transcript); - // Compute statement verifier - let mut statement_verifier = StatementVerifier::from_statement(&Statement::::new(self.m)); + let mut statement_verifier = + StatementVerifier::from_statement(&Statement::::new(self.m)); for claimed_sum in &proof.whir_query_answer_sums { - statement_verifier.add_constraint( - VerifierWeights::linear(self.m, None), - claimed_sum.clone(), - ); - } + statement_verifier + .add_constraint(VerifierWeights::linear(self.m, None), claimed_sum.clone()); + } - let data_from_sumcheck_verifier = run_sumcheck_verifier(&mut arthur, self.m_0).context("while verifying sumcheck")?; + let data_from_sumcheck_verifier = + run_sumcheck_verifier(&mut arthur, self.m_0).context("while verifying sumcheck")?; run_whir_pcs_verifier( &mut arthur, &self.whir_config, @@ -181,7 +181,10 @@ impl WhirR1CSScheme { data_from_sumcheck_verifier.last_sumcheck_val == (proof.whir_query_answer_sums[0] * proof.whir_query_answer_sums[1] - proof.whir_query_answer_sums[2]) - * calculate_eq(&data_from_sumcheck_verifier.r, &data_from_sumcheck_verifier.alpha), + * calculate_eq( + &data_from_sumcheck_verifier.r, + &data_from_sumcheck_verifier.alpha + ), "last sumcheck value does not match" ); @@ -359,10 +362,13 @@ pub fn run_sumcheck_verifier( saved_val_for_sumcheck_equality_assertion = eval_qubic_poly(&hhat_i, &alpha_i[0]); } - Ok(DataFromSumcheckVerifier{r, alpha, last_sumcheck_val: saved_val_for_sumcheck_equality_assertion}) + Ok(DataFromSumcheckVerifier { + r, + alpha, + last_sumcheck_val: saved_val_for_sumcheck_equality_assertion, + }) } - #[instrument(skip_all)] pub fn run_whir_pcs_verifier( arthur: &mut VerifierState,