From f66c254a03d9cf084e4d367d65de8b27ec073c66 Mon Sep 17 00:00:00 2001 From: Tom French Date: Tue, 4 Apr 2023 18:24:51 +0100 Subject: [PATCH] feat: import core logic in cli from `nargo` crate --- Cargo.lock | 2 ++ crates/nargo/Cargo.toml | 2 ++ crates/nargo/src/errors.rs | 13 ++++++++ crates/nargo/src/lib.rs | 4 +++ crates/nargo/src/ops/codegen_verifier.rs | 10 ++++++ crates/nargo/src/ops/execute.rs | 20 ++++++++++++ crates/nargo/src/ops/mod.rs | 11 +++++++ crates/nargo/src/ops/preprocess.rs | 31 +++++++++++++++++++ crates/nargo/src/ops/prove.rs | 16 ++++++++++ crates/nargo/src/ops/verify.rs | 17 ++++++++++ .../nargo_cli/src/cli/codegen_verifier_cmd.rs | 1 + crates/nargo_cli/src/cli/compile_cmd.rs | 9 ++++-- crates/nargo_cli/src/cli/execute_cmd.rs | 15 ++------- crates/nargo_cli/src/cli/fs/keys.rs | 9 +++--- crates/nargo_cli/src/cli/fs/program.rs | 4 --- crates/nargo_cli/src/cli/preprocess_cmd.rs | 31 +++---------------- crates/nargo_cli/src/cli/prove_cmd.rs | 8 +++-- crates/nargo_cli/src/cli/test_cmd.rs | 15 +++------ crates/nargo_cli/src/cli/verify_cmd.rs | 14 ++++++--- crates/nargo_cli/src/errors.rs | 6 ++-- 20 files changed, 167 insertions(+), 71 deletions(-) create mode 100644 crates/nargo/src/errors.rs create mode 100644 crates/nargo/src/ops/codegen_verifier.rs create mode 100644 crates/nargo/src/ops/execute.rs create mode 100644 crates/nargo/src/ops/mod.rs create mode 100644 crates/nargo/src/ops/preprocess.rs create mode 100644 crates/nargo/src/ops/prove.rs create mode 100644 crates/nargo/src/ops/verify.rs diff --git a/Cargo.lock b/Cargo.lock index b311bc608c..c4d231f377 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2244,6 +2244,8 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" name = "nargo" version = "0.3.2" dependencies = [ + "acvm 0.8.0", + "noirc_abi", "rustc_version 0.4.0", "serde", "thiserror", diff --git a/crates/nargo/Cargo.toml b/crates/nargo/Cargo.toml index d24d238ea3..17827c6b60 100644 --- a/crates/nargo/Cargo.toml +++ b/crates/nargo/Cargo.toml @@ -11,6 +11,8 @@ edition.workspace = true rustc_version = "0.4.0" [dependencies] +acvm.workspace = true +noirc_abi.workspace = true toml.workspace = true serde.workspace = true thiserror.workspace = true diff --git a/crates/nargo/src/errors.rs b/crates/nargo/src/errors.rs new file mode 100644 index 0000000000..59cec4552e --- /dev/null +++ b/crates/nargo/src/errors.rs @@ -0,0 +1,13 @@ +use acvm::OpcodeResolutionError; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum NargoError { + /// Error while compiling Noir into ACIR. + #[error("Failed to compile circuit")] + CompilationError, + + /// ACIR circuit solving error + #[error(transparent)] + SolvingError(#[from] OpcodeResolutionError), +} diff --git a/crates/nargo/src/lib.rs b/crates/nargo/src/lib.rs index 9e98f9e358..a3be3cdab7 100644 --- a/crates/nargo/src/lib.rs +++ b/crates/nargo/src/lib.rs @@ -6,4 +6,8 @@ //! This name was used because it sounds like `cargo` and //! Noir Package Manager abbreviated is npm, which is already taken. +mod errors; pub mod manifest; +pub mod ops; + +pub use self::errors::NargoError; diff --git a/crates/nargo/src/ops/codegen_verifier.rs b/crates/nargo/src/ops/codegen_verifier.rs new file mode 100644 index 0000000000..ead125699b --- /dev/null +++ b/crates/nargo/src/ops/codegen_verifier.rs @@ -0,0 +1,10 @@ +use acvm::SmartContract; + +use crate::NargoError; + +pub fn codegen_verifier( + backend: &impl SmartContract, + verification_key: &[u8], +) -> Result { + Ok(backend.eth_contract_from_vk(verification_key)) +} diff --git a/crates/nargo/src/ops/execute.rs b/crates/nargo/src/ops/execute.rs new file mode 100644 index 0000000000..eb82df60d4 --- /dev/null +++ b/crates/nargo/src/ops/execute.rs @@ -0,0 +1,20 @@ +use acvm::PartialWitnessGenerator; +use acvm::{acir::circuit::Circuit, pwg::block::Blocks}; +use noirc_abi::WitnessMap; + +use crate::NargoError; + +pub fn execute_circuit( + backend: &impl PartialWitnessGenerator, + circuit: Circuit, + mut initial_witness: WitnessMap, +) -> Result { + let mut blocks = Blocks::default(); + let (unresolved_opcodes, oracles) = + backend.solve(&mut initial_witness, &mut blocks, circuit.opcodes)?; + if !unresolved_opcodes.is_empty() || !oracles.is_empty() { + todo!("Add oracle support to nargo execute") + } + + Ok(initial_witness) +} diff --git a/crates/nargo/src/ops/mod.rs b/crates/nargo/src/ops/mod.rs new file mode 100644 index 0000000000..578fc1ebbe --- /dev/null +++ b/crates/nargo/src/ops/mod.rs @@ -0,0 +1,11 @@ +pub use self::codegen_verifier::codegen_verifier; +pub use self::execute::execute_circuit; +pub use self::preprocess::{checksum_acir, preprocess_circuit, PreprocessedData}; +pub use self::prove::prove; +pub use self::verify::verify_proof; + +mod codegen_verifier; +mod execute; +mod preprocess; +mod prove; +mod verify; diff --git a/crates/nargo/src/ops/preprocess.rs b/crates/nargo/src/ops/preprocess.rs new file mode 100644 index 0000000000..3046f1e7dd --- /dev/null +++ b/crates/nargo/src/ops/preprocess.rs @@ -0,0 +1,31 @@ +use acvm::acir::circuit::Circuit; +use acvm::{checksum_constraint_system, ProofSystemCompiler}; + +use crate::NargoError; + +pub fn checksum_acir(circuit: &Circuit) -> [u8; 4] { + checksum_constraint_system(circuit).to_be_bytes() +} + +/// The result of preprocessing the ACIR bytecode. +/// The proving, verification key and circuit are backend specific. +/// +/// The circuit is backend specific because at the end of compilation +/// an optimization pass is applied which will transform the bytecode into +/// a format that the backend will accept; removing unsupported gates +/// is one example of this. +pub struct PreprocessedData { + pub proving_key: Vec, + pub verification_key: Vec, + pub program_checksum: [u8; 4], +} + +pub fn preprocess_circuit( + backend: &impl ProofSystemCompiler, + circuit: &Circuit, +) -> Result { + let (proving_key, verification_key) = backend.preprocess(circuit); + let program_checksum = checksum_acir(circuit); + + Ok(PreprocessedData { proving_key, verification_key, program_checksum }) +} diff --git a/crates/nargo/src/ops/prove.rs b/crates/nargo/src/ops/prove.rs new file mode 100644 index 0000000000..fc7ddcd4cb --- /dev/null +++ b/crates/nargo/src/ops/prove.rs @@ -0,0 +1,16 @@ +use acvm::acir::circuit::Circuit; +use acvm::ProofSystemCompiler; +use noirc_abi::WitnessMap; + +use crate::NargoError; + +pub fn prove( + backend: &impl ProofSystemCompiler, + circuit: &Circuit, + solved_witness: WitnessMap, + proving_key: &[u8], +) -> Result, NargoError> { + let proof = backend.prove_with_pk(circuit, solved_witness, proving_key); + + Ok(proof) +} diff --git a/crates/nargo/src/ops/verify.rs b/crates/nargo/src/ops/verify.rs new file mode 100644 index 0000000000..5109d2291d --- /dev/null +++ b/crates/nargo/src/ops/verify.rs @@ -0,0 +1,17 @@ +use acvm::acir::circuit::Circuit; +use acvm::ProofSystemCompiler; +use noirc_abi::WitnessMap; + +use crate::NargoError; + +pub fn verify_proof( + backend: &impl ProofSystemCompiler, + circuit: &Circuit, + proof: &[u8], + public_inputs: WitnessMap, + verification_key: &[u8], +) -> Result { + let valid_proof = backend.verify_with_vk(proof, public_inputs, circuit, verification_key); + + Ok(valid_proof) +} diff --git a/crates/nargo_cli/src/cli/codegen_verifier_cmd.rs b/crates/nargo_cli/src/cli/codegen_verifier_cmd.rs index b05626e439..a9e233d3c1 100644 --- a/crates/nargo_cli/src/cli/codegen_verifier_cmd.rs +++ b/crates/nargo_cli/src/cli/codegen_verifier_cmd.rs @@ -15,6 +15,7 @@ pub(crate) struct CodegenVerifierCommand { pub(crate) fn run(args: CodegenVerifierCommand, config: NargoConfig) -> Result<(), CliError> { let compiled_program = compile_circuit(&config.program_dir, &args.compile_options)?; + // TODO: replace with `nargo::ops::codegen_verifier` let backend = crate::backends::ConcreteBackend; #[allow(deprecated)] let smart_contract_string = backend.eth_contract_from_cs(compiled_program.circuit); diff --git a/crates/nargo_cli/src/cli/compile_cmd.rs b/crates/nargo_cli/src/cli/compile_cmd.rs index d259006c90..7691c3ebc6 100644 --- a/crates/nargo_cli/src/cli/compile_cmd.rs +++ b/crates/nargo_cli/src/cli/compile_cmd.rs @@ -1,4 +1,5 @@ use acvm::ProofSystemCompiler; +use nargo::ops::preprocess_circuit; use noirc_driver::{CompileOptions, CompiledContract, CompiledProgram, Driver}; use std::path::Path; @@ -8,7 +9,7 @@ use crate::resolver::DependencyResolutionError; use crate::{constants::TARGET_DIR, errors::CliError, resolver::Resolver}; use super::fs::program::{save_contract_to_file, save_program_to_file}; -use super::preprocess_cmd::{save_preprocess_data, PreprocessedData}; +use super::preprocess_cmd::save_preprocess_data; use super::NargoConfig; /// Compile the program and its secret execution trace into ACIR format @@ -54,7 +55,8 @@ fn save_and_preprocess_program( ) -> Result<(), CliError> { save_program_to_file(compiled_program, circuit_name, circuit_dir); - let preprocessed_data = PreprocessedData::from(&compiled_program.circuit); + let backend = crate::backends::ConcreteBackend; + let preprocessed_data = preprocess_circuit(&backend, &compiled_program.circuit)?; save_preprocess_data(&preprocessed_data, circuit_name, circuit_dir)?; Ok(()) } @@ -74,9 +76,10 @@ fn save_and_preprocess_contract( // Preprocess all contract data // We are patching the verification key in our contract functions // so when we save it to disk, the ABI will have the verification key. + let backend = crate::backends::ConcreteBackend; let mut contract_preprocess_data = Vec::new(); for contract_function in &mut compiled_contract.functions { - let preprocessed_data = PreprocessedData::from(&contract_function.bytecode); + let preprocessed_data = preprocess_circuit(&backend, &contract_function.bytecode)?; contract_function.verification_key = Some(preprocessed_data.verification_key.clone()); contract_preprocess_data.push(preprocessed_data); } diff --git a/crates/nargo_cli/src/cli/execute_cmd.rs b/crates/nargo_cli/src/cli/execute_cmd.rs index 4a3d89585b..e7ecdc543e 100644 --- a/crates/nargo_cli/src/cli/execute_cmd.rs +++ b/crates/nargo_cli/src/cli/execute_cmd.rs @@ -1,7 +1,5 @@ use std::path::Path; -use acvm::pwg::block::Blocks; -use acvm::PartialWitnessGenerator; use clap::Args; use noirc_abi::input_parser::{Format, InputValue}; use noirc_abi::{InputMap, WitnessMap}; @@ -65,18 +63,11 @@ pub(crate) fn execute_program( compiled_program: &CompiledProgram, inputs_map: &InputMap, ) -> Result { - let mut solved_witness = compiled_program.abi.encode(inputs_map, None)?; + let initial_witness = compiled_program.abi.encode(inputs_map, None)?; let backend = crate::backends::ConcreteBackend; - let mut blocks = Blocks::default(); - let (unresolved_opcodes, oracles) = backend.solve( - &mut solved_witness, - &mut blocks, - compiled_program.circuit.opcodes.clone(), - )?; - if !unresolved_opcodes.is_empty() || !oracles.is_empty() { - todo!("Add oracle support to nargo execute") - } + let solved_witness = + nargo::ops::execute_circuit(&backend, compiled_program.circuit.clone(), initial_witness)?; Ok(solved_witness) } diff --git a/crates/nargo_cli/src/cli/fs/keys.rs b/crates/nargo_cli/src/cli/fs/keys.rs index bbb8487691..32920e5140 100644 --- a/crates/nargo_cli/src/cli/fs/keys.rs +++ b/crates/nargo_cli/src/cli/fs/keys.rs @@ -1,9 +1,10 @@ -use super::{create_named_dir, load_hex_data, program::checksum_acir, write_to_file}; +use super::{create_named_dir, load_hex_data, write_to_file}; use crate::{ constants::{ACIR_CHECKSUM, PK_EXT, VK_EXT}, errors::CliError, }; use acvm::acir::circuit::Circuit; +use nargo::ops::checksum_acir; use std::path::{Path, PathBuf}; pub(crate) fn save_key_to_dir>( @@ -61,11 +62,9 @@ pub(crate) fn fetch_pk_and_vk>( #[cfg(test)] mod tests { use super::fetch_pk_and_vk; - use crate::cli::fs::{ - keys::save_key_to_dir, - program::{checksum_acir, save_acir_checksum_to_dir}, - }; + use crate::cli::fs::{keys::save_key_to_dir, program::save_acir_checksum_to_dir}; use acvm::acir::circuit::Circuit; + use nargo::ops::checksum_acir; use tempdir::TempDir; #[test] diff --git a/crates/nargo_cli/src/cli/fs/program.rs b/crates/nargo_cli/src/cli/fs/program.rs index f327c81f60..b01455e283 100644 --- a/crates/nargo_cli/src/cli/fs/program.rs +++ b/crates/nargo_cli/src/cli/fs/program.rs @@ -1,6 +1,5 @@ use std::path::{Path, PathBuf}; -use acvm::{acir::circuit::Circuit, checksum_constraint_system}; use noirc_driver::{CompiledContract, CompiledProgram}; use crate::{constants::ACIR_CHECKSUM, errors::CliError}; @@ -34,9 +33,6 @@ fn save_build_artifact_to_file, T: ?Sized + serde::Serialize>( circuit_path } -pub(crate) fn checksum_acir(circuit: &Circuit) -> [u8; 4] { - checksum_constraint_system(circuit).to_be_bytes() -} pub(crate) fn save_acir_checksum_to_dir>( acir_checksum: [u8; 4], hash_name: &str, diff --git a/crates/nargo_cli/src/cli/preprocess_cmd.rs b/crates/nargo_cli/src/cli/preprocess_cmd.rs index 05f6edd60a..95a2ab849e 100644 --- a/crates/nargo_cli/src/cli/preprocess_cmd.rs +++ b/crates/nargo_cli/src/cli/preprocess_cmd.rs @@ -1,5 +1,4 @@ -use acvm::acir::circuit::Circuit; -use acvm::ProofSystemCompiler; +use nargo::ops::{preprocess_circuit, PreprocessedData}; use std::path::{Path, PathBuf}; use clap::Args; @@ -8,7 +7,7 @@ use crate::{constants::TARGET_DIR, errors::CliError}; use super::fs::{ keys::save_key_to_dir, - program::{checksum_acir, read_program_from_file, save_acir_checksum_to_dir}, + program::{read_program_from_file, save_acir_checksum_to_dir}, }; use super::NargoConfig; @@ -23,33 +22,13 @@ pub(crate) fn run(args: PreprocessCommand, config: NargoConfig) -> Result<(), Cl let circuit_dir = config.program_dir.join(TARGET_DIR); let program = read_program_from_file(circuit_dir.join(&args.artifact_name))?; - let preprocess_data = PreprocessedData::from(&program.circuit); + + let backend = crate::backends::ConcreteBackend; + let preprocess_data = preprocess_circuit(&backend, &program.circuit)?; save_preprocess_data(&preprocess_data, &args.artifact_name, circuit_dir)?; Ok(()) } -/// The result of preprocessing the ACIR bytecode. -/// The proving, verification key and circuit are backend specific. -/// -/// The circuit is backend specific because at the end of compilation -/// an optimization pass is applied which will transform the bytecode into -/// a format that the backend will accept; removing unsupported gates -/// is one example of this. -pub(crate) struct PreprocessedData { - pub(crate) proving_key: Vec, - pub(crate) verification_key: Vec, - pub(crate) program_checksum: [u8; 4], -} - -impl From<&Circuit> for PreprocessedData { - fn from(circuit: &Circuit) -> Self { - let backend = crate::backends::ConcreteBackend; - let (proving_key, verification_key) = backend.preprocess(circuit); - let program_checksum = checksum_acir(circuit); - - PreprocessedData { proving_key, verification_key, program_checksum } - } -} pub(crate) fn save_preprocess_data>( data: &PreprocessedData, diff --git a/crates/nargo_cli/src/cli/prove_cmd.rs b/crates/nargo_cli/src/cli/prove_cmd.rs index 5f0f9d831c..720110a675 100644 --- a/crates/nargo_cli/src/cli/prove_cmd.rs +++ b/crates/nargo_cli/src/cli/prove_cmd.rs @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; -use acvm::ProofSystemCompiler; use clap::Args; +use nargo::ops::{preprocess_circuit, PreprocessedData}; use noirc_abi::input_parser::Format; use noirc_driver::CompileOptions; @@ -75,7 +75,8 @@ pub(crate) fn prove_with_path>( super::compile_cmd::compile_circuit(program_dir.as_ref(), compile_options)?; let backend = crate::backends::ConcreteBackend; - let (proving_key, verification_key) = backend.preprocess(&compiled_program.circuit); + let PreprocessedData { proving_key, verification_key, .. } = + preprocess_circuit(&backend, &compiled_program.circuit)?; (compiled_program, proving_key, verification_key) } }; @@ -103,7 +104,8 @@ pub(crate) fn prove_with_path>( )?; let backend = crate::backends::ConcreteBackend; - let proof = backend.prove_with_pk(&compiled_program.circuit, solved_witness, &proving_key); + let proof = + nargo::ops::prove(&backend, &compiled_program.circuit, solved_witness, &proving_key)?; if check_proof { let no_proof_name = "".into(); diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index 665591f188..d168e6c39c 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -1,7 +1,8 @@ use std::{collections::BTreeMap, io::Write, path::Path}; -use acvm::{pwg::block::Blocks, PartialWitnessGenerator, ProofSystemCompiler}; +use acvm::ProofSystemCompiler; use clap::Args; +use nargo::ops::execute_circuit; use noirc_driver::{CompileOptions, Driver}; use noirc_frontend::node_interner::FuncId; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; @@ -79,23 +80,15 @@ fn run_test( config: &CompileOptions, ) -> Result<(), CliError> { let backend = crate::backends::ConcreteBackend; - let mut blocks = Blocks::default(); let program = driver .compile_no_check(config, main) .map_err(|_| CliError::Generic(format!("Test '{test_name}' failed to compile")))?; - let mut solved_witness = BTreeMap::new(); - // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, // otherwise constraints involving these expressions will not error. - match backend.solve(&mut solved_witness, &mut blocks, program.circuit.opcodes) { - Ok((unresolved_opcodes, oracles)) => { - if !unresolved_opcodes.is_empty() || !oracles.is_empty() { - todo!("Add oracle support to nargo test") - } - Ok(()) - } + match execute_circuit(&backend, program.circuit, BTreeMap::new()) { + Ok(_) => Ok(()), Err(error) => { let writer = StandardStream::stderr(ColorChoice::Always); let mut writer = writer.lock(); diff --git a/crates/nargo_cli/src/cli/verify_cmd.rs b/crates/nargo_cli/src/cli/verify_cmd.rs index 021b10c940..03de9a7e33 100644 --- a/crates/nargo_cli/src/cli/verify_cmd.rs +++ b/crates/nargo_cli/src/cli/verify_cmd.rs @@ -7,8 +7,8 @@ use crate::{ constants::{PROOFS_DIR, PROOF_EXT, TARGET_DIR, VERIFIER_INPUT_FILE}, errors::CliError, }; -use acvm::ProofSystemCompiler; use clap::Args; +use nargo::ops::{preprocess_circuit, PreprocessedData}; use noirc_abi::input_parser::{Format, InputValue}; use noirc_driver::{CompileOptions, CompiledProgram}; use std::path::{Path, PathBuf}; @@ -55,7 +55,8 @@ fn verify_with_path>( let compiled_program = compile_circuit(program_dir.as_ref(), &compile_options)?; let backend = crate::backends::ConcreteBackend; - let (_, verification_key) = backend.preprocess(&compiled_program.circuit); + let PreprocessedData { verification_key, .. } = + preprocess_circuit(&backend, &compiled_program.circuit)?; (compiled_program, verification_key) } }; @@ -87,8 +88,13 @@ pub(crate) fn verify_proof( let public_inputs = public_abi.encode(&public_inputs_map, return_value)?; let backend = crate::backends::ConcreteBackend; - let valid_proof = - backend.verify_with_vk(proof, public_inputs, &compiled_program.circuit, verification_key); + let valid_proof = nargo::ops::verify_proof( + &backend, + &compiled_program.circuit, + proof, + public_inputs, + verification_key, + )?; if valid_proof { Ok(()) diff --git a/crates/nargo_cli/src/errors.rs b/crates/nargo_cli/src/errors.rs index 1fd8681885..1561a5033f 100644 --- a/crates/nargo_cli/src/errors.rs +++ b/crates/nargo_cli/src/errors.rs @@ -1,5 +1,5 @@ -use acvm::OpcodeResolutionError; use hex::FromHexError; +use nargo::NargoError; use noirc_abi::errors::{AbiError, InputParserError}; use std::path::PathBuf; use thiserror::Error; @@ -40,7 +40,7 @@ pub(crate) enum CliError { #[error(transparent)] AbiError(#[from] AbiError), - /// ACIR circuit solving error + /// Error from Nargo #[error(transparent)] - SolvingError(#[from] OpcodeResolutionError), + NargoError(#[from] NargoError), }