Skip to content

Commit

Permalink
feat(nargo): Update nargo to use preprocessing interface (#765)
Browse files Browse the repository at this point in the history
* initial preprocess cmd to test backend changes

* cargo lock merge conflicts

* initial work using preprocess for nargo proving and verification, still have non-deterministic error on backend with C++

* use fixed up aztec_backend for preprocess

* moving preprocess into compile command

* use nargo compile to preprocess no more preprocess cmd

* remove preprocess cmd file

* remove TODO from nargo cargo.toml

* add checking of acir file in nargo prove

* switch to using circuit_path for pk and vk paths, compare compiled circuit to one written to file

* add logic to look at checksum of acir rather than the file byte by bte

* remove debug

* cargo update

* remove unnecessary comments

* switch backend reference to updated composer

* when build artifacts are not provided to nargo prove, preprocess as a fallback, still needs to be done for verification

* move fetch_pk_and_vk into main nargo module, also use hash cs method from acvm

* move path specifications to separate method in nargo commands

* comment in compile cmd

* nit comments and cspell

* update aztec_backend commit rev

* cargo fmt after merge
  • Loading branch information
vezenovm authored Feb 16, 2023
1 parent 98acb5a commit b3f1556
Show file tree
Hide file tree
Showing 10 changed files with 227 additions and 43 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@ pkg/
# Nargo output
*.proof
*.acir
*.acir.sha256
*.tr
*.pk
*.vk
**/Verifier.toml
6 changes: 3 additions & 3 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 crates/nargo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ termcolor = "1.1.2"
tempdir = "0.3.7"

# Backends
aztec_backend = { optional = true, package = "barretenberg_static_lib", git = "https://github.com/noir-lang/aztec_backend", rev = "d0e1257c22618f98f53781faba3c372ef91a0172" }
aztec_wasm_backend = { optional = true, package = "barretenberg_wasm", git = "https://github.com/noir-lang/aztec_backend", rev = "d0e1257c22618f98f53781faba3c372ef91a0172" }
aztec_backend = { optional = true, package = "barretenberg_static_lib", git = "https://github.com/noir-lang/aztec_backend", rev = "cff757dca7971161e4bd25e7a744d910c37c22be" }
aztec_wasm_backend = { optional = true, package = "barretenberg_wasm", git = "https://github.com/noir-lang/aztec_backend", rev = "cff757dca7971161e4bd25e7a744d910c37c22be" }
marlin_arkworks_backend = { optional = true, git = "https://github.com/noir-lang/marlin_arkworks_backend", rev = "144378edad821bfaa52bf2cacca8ecc87514a4fc" }
color-eyre = "0.6.2"

Expand Down
75 changes: 64 additions & 11 deletions crates/nargo/src/cli/compile_cmd.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use clap::ArgMatches;
use std::path::{Path, PathBuf};

use acvm::ProofSystemCompiler;
use acvm::{acir::circuit::Circuit, hash_constraint_system};
use clap::ArgMatches;
use noirc_abi::input_parser::Format;
use std::path::{Path, PathBuf};

use super::{add_std_lib, create_named_dir, read_inputs_from_file, write_to_file};
use crate::{
cli::execute_cmd::save_witness_to_dir,
constants::{ACIR_EXT, PROVER_INPUT_FILE, TARGET_DIR},
constants::{ACIR_EXT, PK_EXT, PROVER_INPUT_FILE, TARGET_DIR, VK_EXT},
errors::CliError,
resolver::Resolver,
};
Expand All @@ -33,7 +33,6 @@ pub(crate) fn run(args: ArgMatches) -> Result<(), CliError> {
.map(|_| ())
}

#[allow(deprecated)]
pub fn generate_circuit_and_witness_to_disk<P: AsRef<Path>>(
circuit_name: &str,
program_dir: P,
Expand All @@ -42,13 +41,12 @@ pub fn generate_circuit_and_witness_to_disk<P: AsRef<Path>>(
allow_warnings: bool,
) -> Result<PathBuf, CliError> {
let compiled_program = compile_circuit(program_dir.as_ref(), false, allow_warnings)?;
let serialized = compiled_program.circuit.to_bytes();

let mut circuit_path = create_named_dir(circuit_dir.as_ref(), "build");
circuit_path.push(circuit_name);
circuit_path.set_extension(ACIR_EXT);
let path = write_to_file(serialized.as_slice(), &circuit_path);
println!("Generated ACIR code into {path}");
preprocess_with_path(circuit_name, circuit_dir.as_ref(), compiled_program.circuit.clone())?;

let mut circuit_path =
save_acir_to_dir(compiled_program.circuit.clone(), circuit_name, circuit_dir.as_ref());
println!("Generated ACIR code into {}", circuit_path.display());

if generate_witness {
// Parse the initial witness values from Prover.toml
Expand Down Expand Up @@ -82,3 +80,58 @@ pub fn compile_circuit<P: AsRef<Path>>(
.into_compiled_program(backend.np_language(), show_ssa, allow_warnings)
.map_err(|_| std::process::exit(1))
}

pub fn preprocess_with_path<P: AsRef<Path>>(
key_name: &str,
preprocess_dir: P,
circuit: Circuit,
) -> Result<(PathBuf, PathBuf), CliError> {
let backend = crate::backends::ConcreteBackend;

let (proving_key, verification_key) = backend.preprocess(circuit);

let pk_path = save_key_to_dir(proving_key, key_name, &preprocess_dir, true)?;
println!("Proving key saved to {}", pk_path.display());
let vk_path = save_key_to_dir(verification_key, key_name, preprocess_dir, false)?;
println!("Verification key saved to {}", vk_path.display());

Ok((pk_path, vk_path))
}

fn save_acir_to_dir<P: AsRef<Path>>(
circuit: Circuit,
circuit_name: &str,
circuit_dir: P,
) -> PathBuf {
let mut circuit_path = create_named_dir(circuit_dir.as_ref(), "target");
circuit_path.push(circuit_name);

// Save a checksum of the circuit to compare against during proving and verification
let acir_hash = hash_constraint_system(&circuit);
circuit_path.set_extension(ACIR_EXT.to_owned() + ".sha256");
write_to_file(hex::encode(acir_hash).as_bytes(), &circuit_path);

let mut serialized = Vec::new();
circuit.write(&mut serialized).expect("could not serialize circuit");

circuit_path.set_extension(ACIR_EXT);
write_to_file(serialized.as_slice(), &circuit_path);

circuit_path
}

fn save_key_to_dir<P: AsRef<Path>>(
key: Vec<u8>,
key_name: &str,
key_dir: P,
is_proving_key: bool,
) -> Result<PathBuf, CliError> {
let mut key_path = create_named_dir(key_dir.as_ref(), key_name);
key_path.push(key_name);
let extension = if is_proving_key { PK_EXT } else { VK_EXT };
key_path.set_extension(extension);

write_to_file(hex::encode(key).as_bytes(), &key_path);

Ok(key_path)
}
79 changes: 75 additions & 4 deletions crates/nargo/src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use acvm::{
acir::{circuit::PublicInputs, native_types::Witness},
FieldElement,
acir::{
circuit::{Circuit, PublicInputs},
native_types::Witness,
},
hash_constraint_system, FieldElement, ProofSystemCompiler,
};
pub use check_cmd::check_from_path;
use clap::{App, AppSettings, Arg};
Expand All @@ -20,7 +23,10 @@ use std::{
extern crate tempdir;
use tempdir::TempDir;

use crate::errors::CliError;
use crate::{
constants::{ACIR_EXT, PK_EXT, VK_EXT},
errors::CliError,
};

mod check_cmd;
mod compile_cmd;
Expand Down Expand Up @@ -79,6 +85,9 @@ pub fn start_cli() {
App::new("verify")
.about("Given a proof and a program, verify whether the proof is valid")
.arg(Arg::with_name("proof").help("The proof to verify").required(true))
.arg(Arg::with_name("circuit_name").help(
"The name of the circuit build files (ACIR, proving and verification keys)",
))
.arg(allow_warnings.clone()),
)
.subcommand(
Expand All @@ -87,6 +96,9 @@ pub fn start_cli() {
"Create proof for this program. The proof is returned as a hex encoded string.",
)
.arg(Arg::with_name("proof_name").help("The name of the proof"))
.arg(Arg::with_name("circuit_name").help(
"The name of the circuit build files (ACIR, proving and verification keys)",
))
.arg(Arg::with_name("verify").long("verify").help("Verify proof after proving"))
.arg(show_ssa.clone())
.arg(allow_warnings.clone()),
Expand Down Expand Up @@ -134,7 +146,7 @@ pub fn start_cli() {
.takes_value(true),
)
.arg(show_ssa)
.arg(allow_warnings),
.arg(allow_warnings.clone()),
)
.setting(AppSettings::SubcommandRequiredElseHelp)
.get_matches();
Expand Down Expand Up @@ -228,6 +240,7 @@ pub fn prove_and_verify(proof_name: &str, prg_dir: &Path, show_ssa: bool) -> boo
Some(proof_name),
prg_dir,
&tmp_dir.into_path(),
None,
true,
show_ssa,
false,
Expand Down Expand Up @@ -289,6 +302,64 @@ pub(crate) fn dedup_public_input_indices_values(
)
}

pub fn load_hex_data<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, CliError> {
let hex_data: Vec<_> =
std::fs::read(&path).map_err(|_| CliError::PathNotValid(path.as_ref().to_path_buf()))?;

let raw_bytes = hex::decode(hex_data).map_err(CliError::HexArtifactNotValid)?;

Ok(raw_bytes)
}

fn fetch_pk_and_vk<P: AsRef<Path>>(
circuit: Circuit,
circuit_build_path: Option<P>,
prove_circuit: bool,
check_proof: bool,
) -> Result<(Vec<u8>, Vec<u8>), CliError> {
let backend = crate::backends::ConcreteBackend;
if let Some(circuit_build_path) = circuit_build_path {
let mut acir_hash_path = PathBuf::new();
acir_hash_path.push(circuit_build_path.as_ref());
acir_hash_path.set_extension(ACIR_EXT.to_owned() + ".sha256");
let expected_acir_hash = load_hex_data(acir_hash_path.clone())?;

let new_acir_hash = hash_constraint_system(&circuit);

if new_acir_hash[..] != expected_acir_hash {
return Err(CliError::MismatchedAcir(acir_hash_path));
}

// This flag exists to avoid an unnecessary read of the proving key during verification
// as this method is used by both `nargo prove` and `nargo verify`
let proving_key = if prove_circuit {
let mut proving_key_path = PathBuf::new();
proving_key_path.push(circuit_build_path.as_ref());
proving_key_path.set_extension(PK_EXT);
load_hex_data(proving_key_path)?
} else {
// We can return an empty Vec here as `prove_circuit` should only be false when running `nargo verify`
vec![]
};

let verification_key = if check_proof {
let mut verification_key_path = PathBuf::new();
verification_key_path.push(circuit_build_path);
verification_key_path.set_extension(VK_EXT);
load_hex_data(verification_key_path)?
} else {
// We can return an empty Vec here as the verification key is used only is `check_proof` is true
vec![]
};

Ok((proving_key, verification_key))
} else {
// If a path to the circuit's build dir has not been provided, run preprocess and generate the proving and verification keys
let (proving_key, verification_key) = backend.preprocess(circuit);
Ok((proving_key, verification_key))
}
}

// FIXME: I not sure that this is the right place for this tests.
#[cfg(test)]
mod tests {
Expand Down
37 changes: 31 additions & 6 deletions crates/nargo/src/cli/prove_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,22 @@ use clap::ArgMatches;
use noirc_abi::input_parser::Format;

use super::{
create_named_dir, dedup_public_input_indices, read_inputs_from_file, write_inputs_to_file,
write_to_file,
create_named_dir, dedup_public_input_indices, fetch_pk_and_vk, read_inputs_from_file,
write_inputs_to_file, write_to_file,
};
use crate::{
cli::{
execute_cmd::{execute_program, extract_public_inputs},
verify_cmd::verify_proof,
},
constants::{PROOFS_DIR, PROOF_EXT, PROVER_INPUT_FILE, VERIFIER_INPUT_FILE},
constants::{PROOFS_DIR, PROOF_EXT, PROVER_INPUT_FILE, TARGET_DIR, VERIFIER_INPUT_FILE},
errors::CliError,
};

pub(crate) fn run(args: ArgMatches) -> Result<(), CliError> {
let args = args.subcommand_matches("prove").unwrap();
let proof_name = args.value_of("proof_name");
let circuit_name = args.value_of("circuit_name");
let check_proof = args.is_present("verify");
let show_ssa = args.is_present("show-ssa");
let allow_warnings = args.is_present("allow-warnings");
Expand All @@ -30,7 +31,24 @@ pub(crate) fn run(args: ArgMatches) -> Result<(), CliError> {
let mut proof_dir = program_dir.clone();
proof_dir.push(PROOFS_DIR);

prove_with_path(proof_name, program_dir, proof_dir, check_proof, show_ssa, allow_warnings)?;
let circuit_build_path = if let Some(circuit_name) = circuit_name {
let mut circuit_build_path = program_dir.clone();
circuit_build_path.push(TARGET_DIR);
circuit_build_path.push(circuit_name);
Some(circuit_build_path)
} else {
None
};

prove_with_path(
proof_name,
program_dir,
proof_dir,
circuit_build_path,
check_proof,
show_ssa,
allow_warnings,
)?;

Ok(())
}
Expand All @@ -39,12 +57,19 @@ pub fn prove_with_path<P: AsRef<Path>>(
proof_name: Option<&str>,
program_dir: P,
proof_dir: P,
circuit_build_path: Option<P>,
check_proof: bool,
show_ssa: bool,
allow_warnings: bool,
) -> Result<Option<PathBuf>, CliError> {
let compiled_program =
super::compile_cmd::compile_circuit(program_dir.as_ref(), show_ssa, allow_warnings)?;
let (proving_key, verification_key) = fetch_pk_and_vk(
compiled_program.circuit.clone(),
circuit_build_path.as_ref(),
true,
check_proof,
)?;

// Parse the initial witness values from Prover.toml
let inputs_map = read_inputs_from_file(
Expand All @@ -67,11 +92,11 @@ pub fn prove_with_path<P: AsRef<Path>>(
prover_circuit.public_inputs = dedup_public_input_indices(prover_circuit.public_inputs);

let backend = crate::backends::ConcreteBackend;
let proof = backend.prove_with_meta(prover_circuit, solved_witness);
let proof = backend.prove_with_pk(prover_circuit, solved_witness, proving_key);

println!("Proof successfully created");
if check_proof {
let valid_proof = verify_proof(compiled_program, public_inputs, &proof)?;
let valid_proof = verify_proof(compiled_program, public_inputs, &proof, verification_key)?;
println!("Proof verified : {valid_proof}");
if !valid_proof {
return Err(CliError::Generic("Could not verify generated proof".to_owned()));
Expand Down
Loading

0 comments on commit b3f1556

Please sign in to comment.