From 87fea4b447596bdd11ab461f847e03d4f1cc45f2 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 21 Sep 2023 00:22:25 +0100 Subject: [PATCH] feat: short-circuit compilation and read build artifacts from file if program is unchanged (#2743) --- Cargo.lock | 1 + Cargo.toml | 1 + compiler/noirc_driver/Cargo.toml | 1 + compiler/noirc_driver/src/lib.rs | 17 +++-- compiler/noirc_driver/src/program.rs | 6 ++ compiler/noirc_evaluator/Cargo.toml | 2 +- compiler/noirc_frontend/src/ast/mod.rs | 4 +- compiler/noirc_frontend/src/hir_def/expr.rs | 2 +- compiler/noirc_frontend/src/hir_def/stmt.rs | 2 +- .../src/monomorphization/ast.rs | 34 ++++----- compiler/wasm/src/compile.rs | 2 +- tooling/nargo/src/artifacts/program.rs | 6 ++ tooling/nargo/src/ops/test.rs | 2 +- tooling/nargo/src/workspace.rs | 2 +- .../nargo_cli/src/cli/codegen_verifier_cmd.rs | 38 ++++------ tooling/nargo_cli/src/cli/compile_cmd.rs | 39 +++++++++-- tooling/nargo_cli/src/cli/execute_cmd.rs | 49 +++++++------ tooling/nargo_cli/src/cli/info_cmd.rs | 1 + tooling/nargo_cli/src/cli/prove_cmd.rs | 70 ++++++------------- tooling/nargo_cli/src/cli/verify_cmd.rs | 62 +++++----------- 20 files changed, 164 insertions(+), 177 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6050e15ad28..3464172b513 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2358,6 +2358,7 @@ dependencies = [ "base64", "clap", "fm", + "fxhash", "noirc_abi", "noirc_errors", "noirc_evaluator", diff --git a/Cargo.toml b/Cargo.toml index 343bad4918f..ebc3c34fcb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,3 +71,4 @@ url = "2.2.0" wasm-bindgen = { version = "=0.2.86", features = ["serde-serialize"] } wasm-bindgen-test = "0.3.33" base64 = "0.21.2" +fxhash = "0.2.1" diff --git a/compiler/noirc_driver/Cargo.toml b/compiler/noirc_driver/Cargo.toml index 4b67afb73ff..f1c21f74aab 100644 --- a/compiler/noirc_driver/Cargo.toml +++ b/compiler/noirc_driver/Cargo.toml @@ -17,3 +17,4 @@ acvm.workspace = true fm.workspace = true serde.workspace = true base64.workspace = true +fxhash.workspace = true diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index abd24b9f67c..4fa169992c4 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -58,7 +58,7 @@ pub type CompilationResult = Result<(T, Warnings), ErrorsAndWarnings>; // with the restricted version which only uses one file pub fn compile_file(context: &mut Context, root_file: &Path) -> CompilationResult { let crate_id = prepare_crate(context, root_file); - compile_main(context, crate_id, &CompileOptions::default()) + compile_main(context, crate_id, &CompileOptions::default(), None) } /// Adds the file from the file system at `Path` to the crate graph as a root file @@ -143,6 +143,7 @@ pub fn compile_main( context: &mut Context, crate_id: CrateId, options: &CompileOptions, + cached_program: Option, ) -> CompilationResult { let (_, warnings) = check_crate(context, crate_id, options.deny_warnings)?; @@ -158,7 +159,7 @@ pub fn compile_main( } }; - let compiled_program = compile_no_check(context, options, main)?; + let compiled_program = compile_no_check(context, options, main, cached_program)?; if options.print_acir { println!("Compiled ACIR for main (unoptimized):"); @@ -252,7 +253,7 @@ fn compile_contract_inner( continue; } - let function = match compile_no_check(context, options, function_id) { + let function = match compile_no_check(context, options, function_id, None) { Ok(function) => function, Err(new_error) => { errors.push(new_error); @@ -295,13 +296,21 @@ pub fn compile_no_check( context: &Context, options: &CompileOptions, main_function: FuncId, + cached_program: Option, ) -> Result { let program = monomorphize(main_function, &context.def_interner); + let hash = fxhash::hash64(&program); + if let Some(cached_program) = cached_program { + if hash == cached_program.hash { + return Ok(cached_program); + } + } + let (circuit, debug, abi) = create_circuit(context, program, options.show_ssa, options.show_brillig)?; let file_map = filter_relevant_files(&[debug.clone()], &context.file_manager); - Ok(CompiledProgram { circuit, debug, abi, file_map }) + Ok(CompiledProgram { hash, circuit, debug, abi, file_map }) } diff --git a/compiler/noirc_driver/src/program.rs b/compiler/noirc_driver/src/program.rs index 1ed2b0ddddc..3ebd4129312 100644 --- a/compiler/noirc_driver/src/program.rs +++ b/compiler/noirc_driver/src/program.rs @@ -11,6 +11,12 @@ use super::debug::DebugFile; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct CompiledProgram { + /// Hash of the [`Program`][noirc_frontend::monomorphization::ast::Program] from which this [`CompiledProgram`] + /// was compiled. + /// + /// Used to short-circuit compilation in the case of the source code not changing since the last compilation. + pub hash: u64, + #[serde(serialize_with = "serialize_circuit", deserialize_with = "deserialize_circuit")] pub circuit: Circuit, pub abi: noirc_abi::Abi, diff --git a/compiler/noirc_evaluator/Cargo.toml b/compiler/noirc_evaluator/Cargo.toml index 2b207e3d43c..d70c9d3179e 100644 --- a/compiler/noirc_evaluator/Cargo.toml +++ b/compiler/noirc_evaluator/Cargo.toml @@ -12,7 +12,7 @@ noirc_frontend.workspace = true noirc_errors.workspace = true noirc_abi.workspace = true acvm.workspace = true -fxhash = "0.2.1" +fxhash.workspace = true iter-extended.workspace = true thiserror.workspace = true num-bigint = "0.4" diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index aa79929fdd7..662c3b28bef 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -259,7 +259,7 @@ impl UnresolvedTypeExpression { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] /// Represents whether the parameter is public or known only to the prover. pub enum Visibility { Public, @@ -277,7 +277,7 @@ impl std::fmt::Display for Visibility { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] /// Represents whether the return value should compromise of unique witness indices such that no /// index occurs within the program's abi more than once. /// diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index fd980328f5f..3c49d3e4afc 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -41,7 +41,7 @@ impl HirExpression { } /// Corresponds to a variable in the source code -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct HirIdent { pub location: Location, pub id: DefinitionId, diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index d7f0d2e466f..76601d893ed 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -48,7 +48,7 @@ pub struct HirAssignStatement { #[derive(Debug, Clone)] pub struct HirConstrainStatement(pub ExprId, pub FileId, pub Option); -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub enum HirPattern { Identifier(HirIdent), Mutable(Box, Span), diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index bc59249b489..5124e6767f3 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -14,7 +14,7 @@ use crate::{hir_def::function::FunctionSignature, BinaryOpKind, Distinctness, Si /// e.g. `let (a, b) = (1, 2)` have been split up: `let tmp = (1, 2); let a = tmp.0; let b = tmp.1;`. /// This also affects function parameters: `fn foo((a, b): (i32, i32)` => `fn foo(a: i32, b: i32)`. /// - All structs are replaced with tuples -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub enum Expression { Ident(Ident), Literal(Literal), @@ -55,7 +55,7 @@ pub struct LocalId(pub u32); #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct FuncId(pub u32); -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct Ident { pub location: Option, pub definition: Definition, @@ -64,7 +64,7 @@ pub struct Ident { pub typ: Type, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct For { pub index_variable: LocalId, pub index_name: String, @@ -78,7 +78,7 @@ pub struct For { pub end_range_location: Location, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub enum Literal { Array(ArrayLiteral), Integer(FieldElement, Type), @@ -87,7 +87,7 @@ pub enum Literal { FmtStr(String, u64, Box), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct Unary { pub operator: crate::UnaryOp, pub rhs: Box, @@ -97,7 +97,7 @@ pub struct Unary { pub type BinaryOp = BinaryOpKind; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct Binary { pub lhs: Box, pub operator: BinaryOp, @@ -111,7 +111,7 @@ pub struct Lambda { pub env: Ident, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct If { pub condition: Box, pub consequence: Box, @@ -119,20 +119,20 @@ pub struct If { pub typ: Type, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct Cast { pub lhs: Box, pub r#type: Type, pub location: Location, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct ArrayLiteral { pub contents: Vec, pub typ: Type, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct Call { pub func: Box, pub arguments: Vec, @@ -140,7 +140,7 @@ pub struct Call { pub location: Location, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct Index { pub collection: Box, pub index: Box, @@ -160,7 +160,7 @@ pub struct Index { /// let field1 = tmp.0; // the struct has been translated to a tuple as well /// let field2 = tmp.1; /// ``` -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct Let { pub id: LocalId, pub mutable: bool, @@ -168,7 +168,7 @@ pub struct Let { pub expression: Box, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct Assign { pub lvalue: LValue, pub expression: Box, @@ -182,7 +182,7 @@ pub struct BinaryStatement { } /// Represents an Ast form that can be assigned to -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub enum LValue { Ident(Ident), Index { array: Box, index: Box, element_type: Type, location: Location }, @@ -192,7 +192,7 @@ pub enum LValue { pub type Parameters = Vec<(LocalId, /*mutable:*/ bool, /*name:*/ String, Type)>; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct Function { pub id: FuncId, pub name: String, @@ -209,7 +209,7 @@ pub struct Function { /// - Concrete lengths for each array and string /// - Several other variants removed (such as Type::Constant) /// - All structs replaced with tuples -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum Type { Field, Array(/*len:*/ u64, Box), // Array(4, Field) = [Field; 4] @@ -233,7 +233,7 @@ impl Type { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct Program { pub functions: Vec, pub main_function_signature: FunctionSignature, diff --git a/compiler/wasm/src/compile.rs b/compiler/wasm/src/compile.rs index c98e586ab45..99051a98407 100644 --- a/compiler/wasm/src/compile.rs +++ b/compiler/wasm/src/compile.rs @@ -125,7 +125,7 @@ pub fn compile(args: JsValue) -> JsValue { ::from_serde(&optimized_contract).unwrap() } else { - let compiled_program = compile_main(&mut context, crate_id, &options.compile_options) + let compiled_program = compile_main(&mut context, crate_id, &options.compile_options, None) .expect("Compilation failed") .0; diff --git a/tooling/nargo/src/artifacts/program.rs b/tooling/nargo/src/artifacts/program.rs index be01b7bdec1..190b4c76897 100644 --- a/tooling/nargo/src/artifacts/program.rs +++ b/tooling/nargo/src/artifacts/program.rs @@ -9,6 +9,12 @@ use serde::{Deserialize, Serialize}; /// - Proving and verification keys have been pregenerated based on this ACIR. #[derive(Serialize, Deserialize, Debug)] pub struct PreprocessedProgram { + /// Hash of the [`Program`][noirc_frontend::monomorphization::ast::Program] from which this [`PreprocessedProgram`] + /// was compiled. + /// + /// Used to short-circuit compilation in the case of the source code not changing since the last compilation. + pub hash: u64, + pub backend: String, pub abi: Abi, diff --git a/tooling/nargo/src/ops/test.rs b/tooling/nargo/src/ops/test.rs index 512780cc271..993ceb0f21e 100644 --- a/tooling/nargo/src/ops/test.rs +++ b/tooling/nargo/src/ops/test.rs @@ -20,7 +20,7 @@ pub fn run_test( show_output: bool, config: &CompileOptions, ) -> TestStatus { - let program = compile_no_check(context, config, test_function.get_id()); + let program = compile_no_check(context, config, test_function.get_id(), None); match program { Ok(program) => { // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, diff --git a/tooling/nargo/src/workspace.rs b/tooling/nargo/src/workspace.rs index 5df13350683..65f9ab7e0d9 100644 --- a/tooling/nargo/src/workspace.rs +++ b/tooling/nargo/src/workspace.rs @@ -25,7 +25,7 @@ pub struct Workspace { impl Workspace { pub fn package_build_path(&self, package: &Package) -> PathBuf { let name: String = package.name.clone().into(); - self.target_directory_path().join(name) + self.target_directory_path().join(name).with_extension("json") } pub fn contracts_directory_path(&self, package: &Package) -> PathBuf { diff --git a/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs b/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs index 30b865063d3..856970544b8 100644 --- a/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs +++ b/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs @@ -1,9 +1,7 @@ -use std::path::PathBuf; - use super::NargoConfig; use super::{ compile_cmd::compile_bin_package, - fs::{create_named_dir, program::read_program_from_file, write_to_file}, + fs::{create_named_dir, write_to_file}, }; use crate::backends::Backend; use crate::errors::CliError; @@ -12,15 +10,12 @@ use acvm::acir::circuit::Opcode; use acvm::Language; use bb_abstraction_leaks::ACVM_BACKEND_BARRETENBERG; use clap::Args; -use nargo::artifacts::program::PreprocessedProgram; use nargo::package::Package; +use nargo::workspace::Workspace; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::CompileOptions; use noirc_frontend::graph::CrateName; -// TODO(#1388): pull this from backend. -const BACKEND_IDENTIFIER: &str = "acvm-backend-barretenberg"; - /// Generates a Solidity verifier smart contract for the program #[derive(Debug, Clone, Args)] pub(crate) struct CodegenVerifierCommand { @@ -49,12 +44,10 @@ pub(crate) fn run( let (np_language, opcode_support) = backend.get_backend_info()?; for package in &workspace { - let circuit_build_path = workspace.package_build_path(package); - let smart_contract_string = smart_contract_for_package( + &workspace, backend, package, - circuit_build_path, &args.compile_options, np_language, &|opcode| opcode_support.is_opcode_supported(opcode), @@ -72,27 +65,22 @@ pub(crate) fn run( } fn smart_contract_for_package( + workspace: &Workspace, backend: &Backend, package: &Package, - circuit_build_path: PathBuf, compile_options: &CompileOptions, np_language: Language, is_opcode_supported: &impl Fn(&Opcode) -> bool, ) -> Result { - let preprocessed_program = if circuit_build_path.exists() { - read_program_from_file(circuit_build_path)? - } else { - let program = - compile_bin_package(package, compile_options, np_language, &is_opcode_supported)?; - - PreprocessedProgram { - backend: String::from(BACKEND_IDENTIFIER), - abi: program.abi, - bytecode: program.circuit, - } - }; - - let mut smart_contract_string = backend.eth_contract(&preprocessed_program.bytecode)?; + let program = compile_bin_package( + workspace, + package, + compile_options, + np_language, + &is_opcode_supported, + )?; + + let mut smart_contract_string = backend.eth_contract(&program.circuit)?; if backend.name() == ACVM_BACKEND_BARRETENBERG { smart_contract_string = diff --git a/tooling/nargo_cli/src/cli/compile_cmd.rs b/tooling/nargo_cli/src/cli/compile_cmd.rs index 3ccd99558a7..d7bcf475a40 100644 --- a/tooling/nargo_cli/src/cli/compile_cmd.rs +++ b/tooling/nargo_cli/src/cli/compile_cmd.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeMap; use std::path::Path; use acvm::acir::circuit::Opcode; @@ -11,8 +12,10 @@ use nargo::artifacts::debug::DebugArtifact; use nargo::artifacts::program::PreprocessedProgram; use nargo::package::Package; use nargo::prepare_package; +use nargo::workspace::Workspace; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{CompilationResult, CompileOptions, CompiledContract, CompiledProgram}; +use noirc_errors::debug_info::DebugInfo; use noirc_frontend::graph::CrateName; use clap::Args; @@ -20,6 +23,7 @@ use clap::Args; use crate::backends::Backend; use crate::errors::{CliError, CompileError}; +use super::fs::program::read_program_from_file; use super::fs::program::{ save_contract_to_file, save_debug_artifact_to_file, save_program_to_file, }; @@ -71,7 +75,8 @@ pub(crate) fn run( .partition(|package| package.is_binary()); let (np_language, opcode_support) = backend.get_backend_info()?; - let (compiled_programs, compiled_contracts) = compile_workspace( + let (_, compiled_contracts) = compile_workspace( + &workspace, &binary_packages, &contract_packages, np_language, @@ -80,9 +85,6 @@ pub(crate) fn run( )?; // Save build artifacts to disk. - for (package, program) in binary_packages.into_iter().zip(compiled_programs) { - save_program(program, &package, &circuit_dir, args.output_debug); - } for (package, contract) in contract_packages.into_iter().zip(compiled_contracts) { save_contract(contract, &package, &circuit_dir, args.output_debug); } @@ -91,6 +93,7 @@ pub(crate) fn run( } pub(super) fn compile_workspace( + workspace: &Workspace, binary_packages: &[Package], contract_packages: &[Package], np_language: Language, @@ -102,7 +105,9 @@ pub(super) fn compile_workspace( // Compile all of the packages in parallel. let program_results: Vec<(FileManager, CompilationResult)> = binary_packages .par_iter() - .map(|package| compile_program(package, compile_options, np_language, &is_opcode_supported)) + .map(|package| { + compile_program(workspace, package, compile_options, np_language, &is_opcode_supported) + }) .collect(); let contract_results: Vec<(FileManager, CompilationResult)> = contract_packages @@ -130,6 +135,7 @@ pub(super) fn compile_workspace( } pub(crate) fn compile_bin_package( + workspace: &Workspace, package: &Package, compile_options: &CompileOptions, np_language: Language, @@ -140,7 +146,7 @@ pub(crate) fn compile_bin_package( } let (file_manager, compilation_result) = - compile_program(package, compile_options, np_language, &is_opcode_supported); + compile_program(workspace, package, compile_options, np_language, &is_opcode_supported); let program = report_errors(compilation_result, &file_manager, compile_options.deny_warnings)?; @@ -148,6 +154,7 @@ pub(crate) fn compile_bin_package( } fn compile_program( + workspace: &Workspace, package: &Package, compile_options: &CompileOptions, np_language: Language, @@ -155,8 +162,23 @@ fn compile_program( ) -> (FileManager, CompilationResult) { let (mut context, crate_id) = prepare_package(package); + let cached_program = if let Ok(preprocessed_program) = + read_program_from_file(workspace.package_build_path(package)) + { + // TODO: Load debug information. + Some(CompiledProgram { + hash: preprocessed_program.hash, + circuit: preprocessed_program.bytecode, + abi: preprocessed_program.abi, + debug: DebugInfo::default(), + file_map: BTreeMap::new(), + }) + } else { + None + }; + let (program, warnings) = - match noirc_driver::compile_main(&mut context, crate_id, compile_options) { + match noirc_driver::compile_main(&mut context, crate_id, compile_options, cached_program) { Ok(program_and_warnings) => program_and_warnings, Err(errors) => { return (context.file_manager, Err(errors)); @@ -168,6 +190,8 @@ fn compile_program( nargo::ops::optimize_program(program, np_language, &is_opcode_supported) .expect("Backend does not support an opcode that is in the IR"); + save_program(optimized_program.clone(), package, &workspace.target_directory_path(), false); + (context.file_manager, Ok((optimized_program, warnings))) } @@ -200,6 +224,7 @@ fn save_program( output_debug: bool, ) { let preprocessed_program = PreprocessedProgram { + hash: program.hash, backend: String::from(BACKEND_IDENTIFIER), abi: program.abi, bytecode: program.circuit, diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index c3a30743849..7d8cf6de394 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -1,5 +1,5 @@ use acvm::acir::circuit::OpcodeLocation; -use acvm::acir::{circuit::Circuit, native_types::WitnessMap}; +use acvm::acir::native_types::WitnessMap; use acvm::pwg::{ErrorLocation, OpcodeResolutionError}; use clap::Args; @@ -9,7 +9,7 @@ use nargo::errors::{ExecutionError, NargoError}; use nargo::package::Package; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::input_parser::{Format, InputValue}; -use noirc_abi::{Abi, InputMap}; +use noirc_abi::InputMap; use noirc_driver::{CompileOptions, CompiledProgram}; use noirc_errors::CustomDiagnostic; use noirc_frontend::graph::CrateName; @@ -56,10 +56,13 @@ pub(crate) fn run( let (np_language, opcode_support) = backend.get_backend_info()?; for package in &workspace { - let compiled_program = - compile_bin_package(package, &args.compile_options, np_language, &|opcode| { - opcode_support.is_opcode_supported(opcode) - })?; + let compiled_program = compile_bin_package( + &workspace, + package, + &args.compile_options, + np_language, + &|opcode| opcode_support.is_opcode_supported(opcode), + )?; let (return_value, solved_witness) = execute_program_and_decode(compiled_program, package, &args.prover_name)?; @@ -82,14 +85,11 @@ fn execute_program_and_decode( package: &Package, prover_name: &str, ) -> Result<(Option, WitnessMap), CliError> { - let CompiledProgram { abi, circuit, debug, file_map } = program; - let debug_artifact = DebugArtifact { debug_symbols: vec![debug], file_map }; - // Parse the initial witness values from Prover.toml let (inputs_map, _) = - read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &abi)?; - let solved_witness = execute_program(circuit, &abi, &inputs_map, Some(debug_artifact))?; - let public_abi = abi.public_abi(); + read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &program.abi)?; + let solved_witness = execute_program(&program, &inputs_map)?; + let public_abi = program.abi.public_abi(); let (_, return_value) = public_abi.decode(&solved_witness)?; Ok((return_value, solved_witness)) @@ -186,25 +186,30 @@ fn report_error_with_opcode_locations( } pub(crate) fn execute_program( - circuit: Circuit, - abi: &Abi, + compiled_program: &CompiledProgram, inputs_map: &InputMap, - debug_data: Option, ) -> Result { #[allow(deprecated)] let blackbox_solver = barretenberg_blackbox_solver::BarretenbergSolver::new(); - let initial_witness = abi.encode(inputs_map, None)?; + let initial_witness = compiled_program.abi.encode(inputs_map, None)?; - let solved_witness_err = - nargo::ops::execute_circuit(&blackbox_solver, circuit, initial_witness, true); + let solved_witness_err = nargo::ops::execute_circuit( + &blackbox_solver, + compiled_program.circuit.clone(), + initial_witness, + true, + ); match solved_witness_err { Ok(solved_witness) => Ok(solved_witness), Err(err) => { - if let Some(debug_data) = debug_data { - let opcode_err_info = extract_opcode_error_from_nargo_error(&err); - report_error_with_opcode_locations(opcode_err_info, &debug_data); - } + let debug_artifact = DebugArtifact { + debug_symbols: vec![compiled_program.debug.clone()], + file_map: compiled_program.file_map.clone(), + }; + + let opcode_err_info = extract_opcode_error_from_nargo_error(&err); + report_error_with_opcode_locations(opcode_err_info, &debug_artifact); Err(crate::errors::CliError::NargoError(err)) } diff --git a/tooling/nargo_cli/src/cli/info_cmd.rs b/tooling/nargo_cli/src/cli/info_cmd.rs index 8018308ee54..50021e842c4 100644 --- a/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/tooling/nargo_cli/src/cli/info_cmd.rs @@ -57,6 +57,7 @@ pub(crate) fn run( let (np_language, opcode_support) = backend.get_backend_info()?; let (compiled_programs, compiled_contracts) = compile_workspace( + &workspace, &binary_packages, &contract_packages, np_language, diff --git a/tooling/nargo_cli/src/cli/prove_cmd.rs b/tooling/nargo_cli/src/cli/prove_cmd.rs index cbe6deca1c2..af300b7ebe0 100644 --- a/tooling/nargo_cli/src/cli/prove_cmd.rs +++ b/tooling/nargo_cli/src/cli/prove_cmd.rs @@ -1,29 +1,20 @@ -use std::path::{Path, PathBuf}; - -use acvm::acir::circuit::Opcode; -use acvm::Language; use clap::Args; -use nargo::artifacts::debug::DebugArtifact; -use nargo::artifacts::program::PreprocessedProgram; use nargo::constants::{PROVER_INPUT_FILE, VERIFIER_INPUT_FILE}; use nargo::package::Package; +use nargo::workspace::Workspace; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::input_parser::Format; -use noirc_driver::CompileOptions; +use noirc_driver::{CompileOptions, CompiledProgram}; use noirc_frontend::graph::CrateName; use super::compile_cmd::compile_bin_package; use super::fs::{ inputs::{read_inputs_from_file, write_inputs_to_file}, - program::read_program_from_file, proof::save_proof_to_dir, }; use super::NargoConfig; use crate::{backends::Backend, cli::execute_cmd::execute_program, errors::CliError}; -// TODO(#1388): pull this from backend. -const BACKEND_IDENTIFIER: &str = "acvm-backend-barretenberg"; - /// Create proof for this program. The proof is returned as a hex encoded string. #[derive(Debug, Clone, Args)] pub(crate) struct ProveCommand { @@ -61,70 +52,48 @@ pub(crate) fn run( if args.workspace { PackageSelection::All } else { PackageSelection::DefaultOrAll }; let selection = args.package.map_or(default_selection, PackageSelection::Selected); let workspace = resolve_workspace_from_toml(&toml_path, selection)?; - let proof_dir = workspace.proofs_directory_path(); let (np_language, opcode_support) = backend.get_backend_info()?; for package in &workspace { - let circuit_build_path = workspace.package_build_path(package); + let program = compile_bin_package( + &workspace, + package, + &args.compile_options, + np_language, + &|opcode| opcode_support.is_opcode_supported(opcode), + )?; prove_package( backend, + &workspace, package, + program, &args.prover_name, &args.verifier_name, - &proof_dir, - circuit_build_path, args.verify, - &args.compile_options, - np_language, - &|opcode| opcode_support.is_opcode_supported(opcode), )?; } Ok(()) } -#[allow(clippy::too_many_arguments)] pub(crate) fn prove_package( backend: &Backend, + workspace: &Workspace, package: &Package, + compiled_program: CompiledProgram, prover_name: &str, verifier_name: &str, - proof_dir: &Path, - circuit_build_path: PathBuf, check_proof: bool, - compile_options: &CompileOptions, - np_language: Language, - is_opcode_supported: &impl Fn(&Opcode) -> bool, ) -> Result<(), CliError> { - let (preprocessed_program, debug_data) = if circuit_build_path.exists() { - let program = read_program_from_file(circuit_build_path)?; - - (program, None) - } else { - let program = - compile_bin_package(package, compile_options, np_language, &is_opcode_supported)?; - let preprocessed_program = PreprocessedProgram { - backend: String::from(BACKEND_IDENTIFIER), - abi: program.abi, - bytecode: program.circuit, - }; - let debug_artifact = - DebugArtifact { debug_symbols: vec![program.debug], file_map: program.file_map }; - - (preprocessed_program, Some(debug_artifact)) - }; - - let PreprocessedProgram { abi, bytecode, .. } = preprocessed_program; - // Parse the initial witness values from Prover.toml let (inputs_map, _) = - read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &abi)?; + read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &compiled_program.abi)?; - let solved_witness = execute_program(bytecode.clone(), &abi, &inputs_map, debug_data)?; + let solved_witness = execute_program(&compiled_program, &inputs_map)?; // Write public inputs into Verifier.toml - let public_abi = abi.public_abi(); + let public_abi = compiled_program.abi.public_abi(); let (public_inputs, return_value) = public_abi.decode(&solved_witness)?; write_inputs_to_file( @@ -136,18 +105,19 @@ pub(crate) fn prove_package( Format::Toml, )?; - let proof = backend.prove(&bytecode, solved_witness, false)?; + let proof = backend.prove(&compiled_program.circuit, solved_witness, false)?; if check_proof { let public_inputs = public_abi.encode(&public_inputs, return_value)?; - let valid_proof = backend.verify(&proof, public_inputs, &bytecode, false)?; + let valid_proof = + backend.verify(&proof, public_inputs, &compiled_program.circuit, false)?; if !valid_proof { return Err(CliError::InvalidProof("".into())); } } - save_proof_to_dir(&proof, &String::from(&package.name), proof_dir)?; + save_proof_to_dir(&proof, &String::from(&package.name), workspace.proofs_directory_path())?; Ok(()) } diff --git a/tooling/nargo_cli/src/cli/verify_cmd.rs b/tooling/nargo_cli/src/cli/verify_cmd.rs index 8d10d70ddd0..6ae2b78fd0c 100644 --- a/tooling/nargo_cli/src/cli/verify_cmd.rs +++ b/tooling/nargo_cli/src/cli/verify_cmd.rs @@ -1,23 +1,18 @@ use super::NargoConfig; use super::{ compile_cmd::compile_bin_package, - fs::{inputs::read_inputs_from_file, load_hex_data, program::read_program_from_file}, + fs::{inputs::read_inputs_from_file, load_hex_data}, }; use crate::{backends::Backend, errors::CliError}; -use acvm::acir::circuit::Opcode; -use acvm::Language; use clap::Args; use nargo::constants::{PROOF_EXT, VERIFIER_INPUT_FILE}; -use nargo::{artifacts::program::PreprocessedProgram, package::Package}; +use nargo::package::Package; +use nargo::workspace::Workspace; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::input_parser::Format; -use noirc_driver::CompileOptions; +use noirc_driver::{CompileOptions, CompiledProgram}; use noirc_frontend::graph::CrateName; -use std::path::{Path, PathBuf}; - -// TODO(#1388): pull this from backend. -const BACKEND_IDENTIFIER: &str = "acvm-backend-barretenberg"; /// Given a proof and a program, verify whether the proof is valid #[derive(Debug, Clone, Args)] @@ -48,68 +43,47 @@ pub(crate) fn run( if args.workspace { PackageSelection::All } else { PackageSelection::DefaultOrAll }; let selection = args.package.map_or(default_selection, PackageSelection::Selected); let workspace = resolve_workspace_from_toml(&toml_path, selection)?; - let proofs_dir = workspace.proofs_directory_path(); let (np_language, opcode_support) = backend.get_backend_info()?; for package in &workspace { - let circuit_build_path = workspace.package_build_path(package); - - let proof_path = proofs_dir.join(String::from(&package.name)).with_extension(PROOF_EXT); - - verify_package( - backend, + let program = compile_bin_package( + &workspace, package, - &proof_path, - circuit_build_path, - &args.verifier_name, &args.compile_options, np_language, &|opcode| opcode_support.is_opcode_supported(opcode), )?; + + verify_package(backend, &workspace, package, program, &args.verifier_name)?; } Ok(()) } -#[allow(clippy::too_many_arguments)] fn verify_package( backend: &Backend, + workspace: &Workspace, package: &Package, - proof_path: &Path, - circuit_build_path: PathBuf, + compiled_program: CompiledProgram, verifier_name: &str, - compile_options: &CompileOptions, - np_language: Language, - is_opcode_supported: &impl Fn(&Opcode) -> bool, ) -> Result<(), CliError> { - let preprocessed_program = if circuit_build_path.exists() { - read_program_from_file(circuit_build_path)? - } else { - let program = - compile_bin_package(package, compile_options, np_language, &is_opcode_supported)?; - - PreprocessedProgram { - backend: String::from(BACKEND_IDENTIFIER), - abi: program.abi, - bytecode: program.circuit, - } - }; - - let PreprocessedProgram { abi, bytecode, .. } = preprocessed_program; - // Load public inputs (if any) from `verifier_name`. - let public_abi = abi.public_abi(); + let public_abi = compiled_program.abi.public_abi(); let (public_inputs_map, return_value) = read_inputs_from_file(&package.root_dir, verifier_name, Format::Toml, &public_abi)?; let public_inputs = public_abi.encode(&public_inputs_map, return_value)?; - let proof = load_hex_data(proof_path)?; - let valid_proof = backend.verify(&proof, public_inputs, &bytecode, false)?; + let proof_path = + workspace.proofs_directory_path().join(package.name.to_string()).with_extension(PROOF_EXT); + + let proof = load_hex_data(&proof_path)?; + + let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.circuit, false)?; if valid_proof { Ok(()) } else { - Err(CliError::InvalidProof(proof_path.to_path_buf())) + Err(CliError::InvalidProof(proof_path)) } }