From 67aafd73f096c4bb7928a1b19ce79881ce344fc8 Mon Sep 17 00:00:00 2001 From: Tom French Date: Wed, 8 May 2024 16:54:46 +0100 Subject: [PATCH] feat: switch `bb` over to read ACIR from nargo artifacts --- .../cpp/src/barretenberg/bb/get_bytecode.hpp | 4 +- .../backend_interface/src/cli/contract.rs | 6 +- .../backend_interface/src/cli/gates.rs | 10 +- .../backend_interface/src/cli/prove.rs | 10 +- .../backend_interface/src/cli/verify.rs | 8 +- .../backend_interface/src/cli/write_vk.rs | 10 +- .../backend_interface/src/proof_system.rs | 49 +- .../backend_interface/src/smart_contract.rs | 43 +- .../nargo_cli/src/cli/codegen_verifier_cmd.rs | 4 +- .../tooling/nargo_cli/src/cli/info_cmd.rs | 660 +++++++++--------- .../tooling/nargo_cli/src/cli/mod.rs | 6 +- .../tooling/nargo_cli/src/cli/prove_cmd.rs | 15 +- .../tooling/nargo_cli/src/cli/verify_cmd.rs | 3 +- 13 files changed, 399 insertions(+), 429 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/bb/get_bytecode.hpp b/barretenberg/cpp/src/barretenberg/bb/get_bytecode.hpp index 2c7af46cfca5..f5ba724f812a 100644 --- a/barretenberg/cpp/src/barretenberg/bb/get_bytecode.hpp +++ b/barretenberg/cpp/src/barretenberg/bb/get_bytecode.hpp @@ -6,6 +6,6 @@ */ inline std::vector get_bytecode(const std::string& bytecodePath) { - std::string command = "gunzip -c \"" + bytecodePath + "\""; + std::string command = "jq -r '.bytecode' \"" + bytecodePath + "\" | base64 -d | gunzip -c"; return exec_pipe(command); -} \ No newline at end of file +} diff --git a/noir/noir-repo/tooling/backend_interface/src/cli/contract.rs b/noir/noir-repo/tooling/backend_interface/src/cli/contract.rs index e83fc1909b67..935b96b3ac40 100644 --- a/noir/noir-repo/tooling/backend_interface/src/cli/contract.rs +++ b/noir/noir-repo/tooling/backend_interface/src/cli/contract.rs @@ -48,15 +48,15 @@ fn contract_command() -> Result<(), BackendError> { let temp_directory = tempdir().expect("could not create a temporary directory"); let temp_directory_path = temp_directory.path(); - let bytecode_path = temp_directory_path.join("acir.gz"); + let artifact_path = temp_directory_path.join("program.json"); let vk_path = temp_directory_path.join("vk"); let crs_path = backend.backend_directory(); - std::fs::File::create(&bytecode_path).expect("file should be created"); + std::fs::File::create(&artifact_path).expect("file should be created"); let write_vk_command = super::WriteVkCommand { - bytecode_path, + artifact_path, vk_path_output: vk_path.clone(), crs_path: crs_path.clone(), }; diff --git a/noir/noir-repo/tooling/backend_interface/src/cli/gates.rs b/noir/noir-repo/tooling/backend_interface/src/cli/gates.rs index aca05f0232a7..24228a9065a3 100644 --- a/noir/noir-repo/tooling/backend_interface/src/cli/gates.rs +++ b/noir/noir-repo/tooling/backend_interface/src/cli/gates.rs @@ -9,7 +9,7 @@ use super::string_from_stderr; /// for the given bytecode. pub(crate) struct GatesCommand { pub(crate) crs_path: PathBuf, - pub(crate) bytecode_path: PathBuf, + pub(crate) artifact_path: PathBuf, } impl GatesCommand { @@ -19,7 +19,7 @@ impl GatesCommand { .arg("-c") .arg(self.crs_path) .arg("-b") - .arg(self.bytecode_path) + .arg(self.artifact_path) .output()?; if !output.status.success() { @@ -49,12 +49,12 @@ fn gate_command() -> Result<(), BackendError> { let temp_directory = tempdir().expect("could not create a temporary directory"); let temp_directory_path = temp_directory.path(); - let bytecode_path = temp_directory_path.join("acir.gz"); + let artifact_path = temp_directory_path.join("program.json"); let crs_path = backend.backend_directory(); - std::fs::File::create(&bytecode_path).expect("file should be created"); + std::fs::File::create(&artifact_path).expect("file should be created"); - let gate_command = GatesCommand { crs_path, bytecode_path }; + let gate_command = GatesCommand { crs_path, artifact_path }; let output = gate_command.run(backend.binary_path())?; // Mock backend always returns zero gates. diff --git a/noir/noir-repo/tooling/backend_interface/src/cli/prove.rs b/noir/noir-repo/tooling/backend_interface/src/cli/prove.rs index c63d8afab542..30a27048b480 100644 --- a/noir/noir-repo/tooling/backend_interface/src/cli/prove.rs +++ b/noir/noir-repo/tooling/backend_interface/src/cli/prove.rs @@ -13,7 +13,7 @@ use super::string_from_stderr; /// The proof will be written to the specified output file. pub(crate) struct ProveCommand { pub(crate) crs_path: PathBuf, - pub(crate) bytecode_path: PathBuf, + pub(crate) artifact_path: PathBuf, pub(crate) witness_path: PathBuf, } @@ -26,7 +26,7 @@ impl ProveCommand { .arg("-c") .arg(self.crs_path) .arg("-b") - .arg(self.bytecode_path) + .arg(self.artifact_path) .arg("-w") .arg(self.witness_path) .arg("-o") @@ -49,14 +49,14 @@ fn prove_command() -> Result<(), BackendError> { let temp_directory = tempdir().expect("could not create a temporary directory"); let temp_directory_path = temp_directory.path(); - let bytecode_path = temp_directory_path.join("acir.gz"); + let artifact_path = temp_directory_path.join("acir.gz"); let witness_path = temp_directory_path.join("witness.tr"); - std::fs::File::create(&bytecode_path).expect("file should be created"); + std::fs::File::create(&artifact_path).expect("file should be created"); std::fs::File::create(&witness_path).expect("file should be created"); let crs_path = backend.backend_directory(); - let prove_command = ProveCommand { crs_path, bytecode_path, witness_path }; + let prove_command = ProveCommand { crs_path, artifact_path, witness_path }; let proof = prove_command.run(backend.binary_path())?; assert_eq!(proof, "proof".as_bytes()); diff --git a/noir/noir-repo/tooling/backend_interface/src/cli/verify.rs b/noir/noir-repo/tooling/backend_interface/src/cli/verify.rs index 1a4ba50b7dea..beea4bbec7d0 100644 --- a/noir/noir-repo/tooling/backend_interface/src/cli/verify.rs +++ b/noir/noir-repo/tooling/backend_interface/src/cli/verify.rs @@ -41,25 +41,25 @@ fn verify_command() -> Result<(), BackendError> { let temp_directory = tempdir().expect("could not create a temporary directory"); let temp_directory_path = temp_directory.path(); - let bytecode_path = temp_directory_path.join("acir.gz"); + let artifact_path = temp_directory_path.join("acir.json"); let witness_path = temp_directory_path.join("witness.tr"); let proof_path = temp_directory_path.join("1_mul.proof"); let vk_path_output = temp_directory_path.join("vk"); let crs_path = backend.backend_directory(); - std::fs::File::create(&bytecode_path).expect("file should be created"); + std::fs::File::create(&artifact_path).expect("file should be created"); std::fs::File::create(&witness_path).expect("file should be created"); let write_vk_command = WriteVkCommand { - bytecode_path: bytecode_path.clone(), + artifact_path: artifact_path.clone(), crs_path: crs_path.clone(), vk_path_output: vk_path_output.clone(), }; write_vk_command.run(backend.binary_path())?; - let prove_command = ProveCommand { crs_path: crs_path.clone(), bytecode_path, witness_path }; + let prove_command = ProveCommand { crs_path: crs_path.clone(), artifact_path, witness_path }; let proof = prove_command.run(backend.binary_path())?; write_to_file(&proof, &proof_path); diff --git a/noir/noir-repo/tooling/backend_interface/src/cli/write_vk.rs b/noir/noir-repo/tooling/backend_interface/src/cli/write_vk.rs index da9fc04cbefe..3d51b5a4a8c1 100644 --- a/noir/noir-repo/tooling/backend_interface/src/cli/write_vk.rs +++ b/noir/noir-repo/tooling/backend_interface/src/cli/write_vk.rs @@ -7,7 +7,7 @@ use crate::BackendError; /// to write a verification key to a file pub(crate) struct WriteVkCommand { pub(crate) crs_path: PathBuf, - pub(crate) bytecode_path: PathBuf, + pub(crate) artifact_path: PathBuf, pub(crate) vk_path_output: PathBuf, } @@ -21,7 +21,7 @@ impl WriteVkCommand { .arg("-c") .arg(self.crs_path) .arg("-b") - .arg(self.bytecode_path) + .arg(self.artifact_path) .arg("-o") .arg(self.vk_path_output); @@ -42,14 +42,14 @@ fn write_vk_command() -> Result<(), BackendError> { let temp_directory = tempdir().expect("could not create a temporary directory"); let temp_directory_path = temp_directory.path(); - let bytecode_path = temp_directory_path.join("acir.gz"); + let artifact_path = temp_directory_path.join("program.json"); let vk_path_output = temp_directory.path().join("vk"); let crs_path = backend.backend_directory(); - std::fs::File::create(&bytecode_path).expect("file should be created"); + std::fs::File::create(&artifact_path).expect("file should be created"); - let write_vk_command = WriteVkCommand { bytecode_path, crs_path, vk_path_output }; + let write_vk_command = WriteVkCommand { artifact_path, crs_path, vk_path_output }; write_vk_command.run(backend.binary_path())?; drop(temp_directory); diff --git a/noir/noir-repo/tooling/backend_interface/src/proof_system.rs b/noir/noir-repo/tooling/backend_interface/src/proof_system.rs index fa1f82a5722d..de89675920e0 100644 --- a/noir/noir-repo/tooling/backend_interface/src/proof_system.rs +++ b/noir/noir-repo/tooling/backend_interface/src/proof_system.rs @@ -1,9 +1,9 @@ -use std::fs::File; use std::io::Write; use std::path::Path; +use std::{fs::File, path::PathBuf}; use acvm::acir::{ - circuit::{ExpressionWidth, Program}, + circuit::ExpressionWidth, native_types::{WitnessMap, WitnessStack}, }; use acvm::FieldElement; @@ -17,20 +17,11 @@ use crate::cli::{ use crate::{Backend, BackendError}; impl Backend { - pub fn get_exact_circuit_size(&self, program: &Program) -> Result { + pub fn get_exact_circuit_size(&self, artifact_path: PathBuf) -> Result { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; - let temp_directory = tempdir().expect("could not create a temporary directory"); - let temp_directory = temp_directory.path().to_path_buf(); - - // Create a temporary file for the circuit - let circuit_path = temp_directory.join("circuit").with_extension("bytecode"); - let serialized_program = Program::serialize_program(program); - write_to_file(&serialized_program, &circuit_path); - - GatesCommand { crs_path: self.crs_directory(), bytecode_path: circuit_path } - .run(binary_path) + GatesCommand { crs_path: self.crs_directory(), artifact_path }.run(binary_path) } pub fn get_backend_info(&self) -> Result { @@ -55,8 +46,9 @@ impl Backend { #[tracing::instrument(level = "trace", skip_all)] pub fn prove( &self, - program: &Program, + artifact_path: PathBuf, witness_stack: WitnessStack, + num_public_inputs: u32, ) -> Result, BackendError> { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; @@ -70,20 +62,14 @@ impl Backend { let witness_path = temp_directory.join("witness").with_extension("tr"); write_to_file(&serialized_witnesses, &witness_path); - // Create a temporary file for the circuit - // - let bytecode_path = temp_directory.join("program").with_extension("bytecode"); - let serialized_program = Program::serialize_program(program); - write_to_file(&serialized_program, &bytecode_path); - // Create proof and store it in the specified path let proof_with_public_inputs = - ProveCommand { crs_path: self.crs_directory(), bytecode_path, witness_path } + ProveCommand { crs_path: self.crs_directory(), artifact_path, witness_path } .run(binary_path)?; let proof = bb_abstraction_leaks::remove_public_inputs( // TODO(https://github.com/noir-lang/noir/issues/4428) - program.functions[0].public_inputs().0.len(), + num_public_inputs as usize, &proof_with_public_inputs, ); Ok(proof) @@ -94,7 +80,7 @@ impl Backend { &self, proof: &[u8], public_inputs: WitnessMap, - program: &Program, + artifact_path: PathBuf, ) -> Result { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; @@ -108,17 +94,12 @@ impl Backend { let proof_path = temp_directory.join("proof").with_extension("proof"); write_to_file(&proof_with_public_inputs, &proof_path); - // Create a temporary file for the circuit - let bytecode_path = temp_directory.join("program").with_extension("bytecode"); - let serialized_program = Program::serialize_program(program); - write_to_file(&serialized_program, &bytecode_path); - // Create the verification key and write it to the specified path let vk_path = temp_directory.join("vk"); WriteVkCommand { crs_path: self.crs_directory(), - bytecode_path, + artifact_path, vk_path_output: vk_path.clone(), } .run(binary_path)?; @@ -129,7 +110,7 @@ impl Backend { pub fn get_intermediate_proof_artifacts( &self, - program: &Program, + artifact_path: PathBuf, proof: &[u8], public_inputs: WitnessMap, ) -> Result<(Vec, FieldElement, Vec), BackendError> { @@ -139,18 +120,12 @@ impl Backend { let temp_directory = tempdir().expect("could not create a temporary directory"); let temp_directory = temp_directory.path().to_path_buf(); - // Create a temporary file for the circuit - // - let bytecode_path = temp_directory.join("program").with_extension("bytecode"); - let serialized_program = Program::serialize_program(program); - write_to_file(&serialized_program, &bytecode_path); - // Create the verification key and write it to the specified path let vk_path = temp_directory.join("vk"); WriteVkCommand { crs_path: self.crs_directory(), - bytecode_path, + artifact_path, vk_path_output: vk_path.clone(), } .run(binary_path)?; diff --git a/noir/noir-repo/tooling/backend_interface/src/smart_contract.rs b/noir/noir-repo/tooling/backend_interface/src/smart_contract.rs index 153ab52c83f8..8b26ea07a2fb 100644 --- a/noir/noir-repo/tooling/backend_interface/src/smart_contract.rs +++ b/noir/noir-repo/tooling/backend_interface/src/smart_contract.rs @@ -1,30 +1,25 @@ -use super::proof_system::write_to_file; +use std::path::PathBuf; + use crate::{ cli::{ContractCommand, WriteVkCommand}, Backend, BackendError, }; -use acvm::acir::circuit::Program; use tempfile::tempdir; impl Backend { - pub fn eth_contract(&self, program: &Program) -> Result { + pub fn eth_contract(&self, artifact_path: PathBuf) -> Result { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; let temp_directory = tempdir().expect("could not create a temporary directory"); let temp_directory_path = temp_directory.path().to_path_buf(); - // Create a temporary file for the circuit - let bytecode_path = temp_directory_path.join("program").with_extension("bytecode"); - let serialized_program = Program::serialize_program(program); - write_to_file(&serialized_program, &bytecode_path); - // Create the verification key and write it to the specified path let vk_path = temp_directory_path.join("vk"); WriteVkCommand { crs_path: self.crs_directory(), - bytecode_path, + artifact_path, vk_path_output: vk_path.clone(), } .run(binary_path)?; @@ -35,33 +30,23 @@ impl Backend { #[cfg(test)] mod tests { - use std::collections::BTreeSet; - use acvm::acir::{ - circuit::{Circuit, ExpressionWidth, Opcode, Program, PublicInputs}, - native_types::{Expression, Witness}, - }; + use serde_json::json; + use tempfile::tempdir; - use crate::{get_mock_backend, BackendError}; + use crate::{get_mock_backend, proof_system::write_to_file, BackendError}; #[test] fn test_smart_contract() -> Result<(), BackendError> { - let expression = &(Witness(1) + Witness(2)) - &Expression::from(Witness(3)); - let constraint = Opcode::AssertZero(expression); + let dummy_artifact = json!({"bytecode": ""}); + let artifact_bytes = serde_json::to_vec(&dummy_artifact).unwrap(); - let circuit = Circuit { - current_witness_index: 4, - expression_width: ExpressionWidth::Bounded { width: 4 }, - opcodes: vec![constraint], - private_parameters: BTreeSet::from([Witness(1), Witness(2)]), - public_parameters: PublicInputs::default(), - return_values: PublicInputs::default(), - assert_messages: Default::default(), - recursive: false, - }; - let program = Program { functions: vec![circuit], unconstrained_functions: Vec::new() }; + let temp_directory = tempdir().expect("could not create a temporary directory"); + let temp_directory_path = temp_directory.path(); + let artifact_path = temp_directory_path.join("program.json"); + write_to_file(&artifact_bytes, &artifact_path); - let contract = get_mock_backend()?.eth_contract(&program)?; + let contract = get_mock_backend()?.eth_contract(artifact_path)?; assert!(contract.contains("contract VerifierContract")); diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs index 04ed5c2b6b87..62f955322472 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs @@ -46,7 +46,7 @@ pub(crate) fn run( let binary_packages = workspace.into_iter().filter(|package| package.is_binary()); for package in binary_packages { let program_artifact_path = workspace.package_build_path(package); - let program: CompiledProgram = read_program_from_file(program_artifact_path)?.into(); + let program: CompiledProgram = read_program_from_file(&program_artifact_path)?.into(); // TODO(https://github.com/noir-lang/noir/issues/4428): // We do not expect to have a smart contract verifier for a foldable program with multiple circuits. @@ -54,7 +54,7 @@ pub(crate) fn run( // that will be inlined at a later step such as by the ACVM compiler or by the backend. // Add appropriate handling here once the compiler enables multiple ACIR functions. assert_eq!(program.program.functions.len(), 1); - let smart_contract_string = backend.eth_contract(&program.program)?; + let smart_contract_string = backend.eth_contract(program_artifact_path)?; let contract_dir = workspace.contracts_directory_path(package); create_named_dir(&contract_dir, "contract"); diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs index c0aa7169af4e..4e85d16d5c0b 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/info_cmd.rs @@ -1,330 +1,330 @@ -use std::collections::HashMap; - -use acvm::acir::circuit::{ExpressionWidth, Program}; -use backend_interface::BackendError; -use clap::Args; -use iter_extended::vecmap; -use nargo::{ - artifacts::{contract::ContractArtifact, debug::DebugArtifact, program::ProgramArtifact}, - package::Package, -}; -use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::{CompileOptions, NOIR_ARTIFACT_VERSION_STRING}; -use noirc_errors::{debug_info::OpCodesCount, Location}; -use noirc_frontend::graph::CrateName; -use prettytable::{row, table, Row}; -use rayon::prelude::*; -use serde::Serialize; - -use crate::backends::Backend; -use crate::errors::CliError; - -use super::{ - compile_cmd::compile_workspace_full, - fs::program::{read_contract_from_file, read_program_from_file}, - NargoConfig, -}; - -/// Provides detailed information on each of a program's function (represented by a single circuit) -/// -/// Current information provided per circuit: -/// 1. The number of ACIR opcodes -/// 2. Counts the final number gates in the circuit used by a backend -#[derive(Debug, Clone, Args)] -#[clap(visible_alias = "i")] -pub(crate) struct InfoCommand { - /// The name of the package to detail - #[clap(long, conflicts_with = "workspace")] - package: Option, - - /// Detail all packages in the workspace - #[clap(long, conflicts_with = "package")] - workspace: bool, - - /// Output a JSON formatted report. Changes to this format are not currently considered breaking. - #[clap(long, hide = true)] - json: bool, - - #[clap(long, hide = true)] - profile_info: bool, - - #[clap(flatten)] - compile_options: CompileOptions, -} - -pub(crate) fn run( - backend: &Backend, - args: InfoCommand, - config: NargoConfig, -) -> Result<(), CliError> { - let toml_path = get_package_manifest(&config.program_dir)?; - let default_selection = - 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, - Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), - )?; - - // Compile the full workspace in order to generate any build artifacts. - compile_workspace_full(&workspace, &args.compile_options)?; - - let binary_packages: Vec<(Package, ProgramArtifact)> = workspace - .into_iter() - .filter(|package| package.is_binary()) - .map(|package| -> Result<(Package, ProgramArtifact), CliError> { - let program_artifact_path = workspace.package_build_path(package); - let program = read_program_from_file(program_artifact_path)?; - Ok((package.clone(), program)) - }) - .collect::>()?; - - let compiled_contracts: Vec = workspace - .into_iter() - .filter(|package| package.is_contract()) - .map(|package| { - let contract_artifact_path = workspace.package_build_path(package); - read_contract_from_file(contract_artifact_path) - }) - .collect::>()?; - - if args.profile_info { - for (_, compiled_program) in &binary_packages { - let debug_artifact = DebugArtifact::from(compiled_program.clone()); - for function_debug in compiled_program.debug_symbols.debug_infos.iter() { - let span_opcodes = function_debug.count_span_opcodes(); - print_span_opcodes(span_opcodes, &debug_artifact); - } - } - - for compiled_contract in &compiled_contracts { - let debug_artifact = DebugArtifact::from(compiled_contract.clone()); - let functions = &compiled_contract.functions; - for contract_function in functions { - for function_debug in contract_function.debug_symbols.debug_infos.iter() { - let span_opcodes = function_debug.count_span_opcodes(); - print_span_opcodes(span_opcodes, &debug_artifact); - } - } - } - } - - let program_info = binary_packages - .into_iter() - .par_bridge() - .map(|(package, program)| { - count_opcodes_and_gates_in_program( - backend, - program, - &package, - args.compile_options.expression_width.unwrap_or_default(), - ) - }) - .collect::>()?; - - let contract_info = compiled_contracts - .into_par_iter() - .map(|contract| { - count_opcodes_and_gates_in_contract( - backend, - contract, - args.compile_options.expression_width.unwrap_or_default(), - ) - }) - .collect::>()?; - - let info_report = InfoReport { programs: program_info, contracts: contract_info }; - - if args.json { - // Expose machine-readable JSON data. - println!("{}", serde_json::to_string(&info_report).unwrap()); - } else { - // Otherwise print human-readable table. - if !info_report.programs.is_empty() { - let mut program_table = table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes", Fm->"Backend Circuit Size"]); - - for program_info in info_report.programs { - let program_rows: Vec = program_info.into(); - for row in program_rows { - program_table.add_row(row); - } - } - program_table.printstd(); - } - if !info_report.contracts.is_empty() { - let mut contract_table = table!([ - Fm->"Contract", - Fm->"Function", - Fm->"Expression Width", - Fm->"ACIR Opcodes", - Fm->"Backend Circuit Size" - ]); - for contract_info in info_report.contracts { - let contract_rows: Vec = contract_info.into(); - for row in contract_rows { - contract_table.add_row(row); - } - } - - contract_table.printstd(); - } - } - - Ok(()) -} - -/// Provides profiling information on -/// -/// Number of OpCodes in relation to Noir source file -/// and line number information -fn print_span_opcodes( - span_opcodes_map: HashMap, - debug_artifact: &DebugArtifact, -) { - let mut pairs: Vec<(&Location, &OpCodesCount)> = span_opcodes_map.iter().collect(); - - pairs.sort_by(|a, b| { - a.1.acir_size.cmp(&b.1.acir_size).then_with(|| a.1.brillig_size.cmp(&b.1.brillig_size)) - }); - - for (location, opcodes_count) in pairs { - let debug_file = debug_artifact.file_map.get(&location.file).unwrap(); - - let start_byte = byte_index(&debug_file.source, location.span.start() + 1); - let end_byte = byte_index(&debug_file.source, location.span.end() + 1); - let range = start_byte..end_byte; - let span_content = &debug_file.source[range]; - let line = debug_artifact.location_line_index(*location).unwrap() + 1; - println!( - "Ln. {}: {} (ACIR:{}, Brillig:{} opcode|s) in file: {}", - line, - span_content, - opcodes_count.acir_size, - opcodes_count.brillig_size, - debug_file.path.to_str().unwrap() - ); - } -} -fn byte_index(string: &str, index: u32) -> usize { - let mut byte_index = 0; - let mut char_index = 0; - - #[allow(clippy::explicit_counter_loop)] - for (byte_offset, _) in string.char_indices() { - if char_index == index { - return byte_index; - } - - byte_index = byte_offset; - char_index += 1; - } - - byte_index -} - -#[derive(Debug, Default, Serialize)] -struct InfoReport { - programs: Vec, - contracts: Vec, -} - -#[derive(Debug, Serialize)] -struct ProgramInfo { - package_name: String, - #[serde(skip)] - expression_width: ExpressionWidth, - functions: Vec, -} - -impl From for Vec { - fn from(program_info: ProgramInfo) -> Self { - vecmap(program_info.functions, |function| { - row![ - Fm->format!("{}", program_info.package_name), - Fc->format!("{}", function.name), - format!("{:?}", program_info.expression_width), - Fc->format!("{}", function.acir_opcodes), - Fc->format!("{}", function.circuit_size), - ] - }) - } -} - -#[derive(Debug, Serialize)] -struct ContractInfo { - name: String, - #[serde(skip)] - expression_width: ExpressionWidth, - // TODO(https://github.com/noir-lang/noir/issues/4720): Settle on how to display contract functions with non-inlined Acir calls - functions: Vec, -} - -#[derive(Debug, Serialize)] -struct FunctionInfo { - name: String, - acir_opcodes: usize, - circuit_size: u32, -} - -impl From for Vec { - fn from(contract_info: ContractInfo) -> Self { - vecmap(contract_info.functions, |function| { - row![ - Fm->format!("{}", contract_info.name), - Fc->format!("{}", function.name), - format!("{:?}", contract_info.expression_width), - Fc->format!("{}", function.acir_opcodes), - Fc->format!("{}", function.circuit_size), - ] - }) - } -} - -fn count_opcodes_and_gates_in_program( - backend: &Backend, - compiled_program: ProgramArtifact, - package: &Package, - expression_width: ExpressionWidth, -) -> Result { - let functions = compiled_program - .bytecode - .functions - .into_par_iter() - .enumerate() - .map(|(i, function)| -> Result<_, BackendError> { - Ok(FunctionInfo { - name: compiled_program.names[i].clone(), - acir_opcodes: function.opcodes.len(), - // Unconstrained functions do not matter to a backend circuit count so we pass nothing here - circuit_size: backend.get_exact_circuit_size(&Program { - functions: vec![function], - unconstrained_functions: Vec::new(), - })?, - }) - }) - .collect::>()?; - - Ok(ProgramInfo { package_name: package.name.to_string(), expression_width, functions }) -} - -fn count_opcodes_and_gates_in_contract( - backend: &Backend, - contract: ContractArtifact, - expression_width: ExpressionWidth, -) -> Result { - let functions = contract - .functions - .into_par_iter() - .map(|function| -> Result<_, BackendError> { - Ok(FunctionInfo { - name: function.name, - // TODO(https://github.com/noir-lang/noir/issues/4720) - acir_opcodes: function.bytecode.functions[0].opcodes.len(), - circuit_size: backend.get_exact_circuit_size(&function.bytecode)?, - }) - }) - .collect::>()?; - - Ok(ContractInfo { name: contract.name, expression_width, functions }) -} +// use std::collections::HashMap; + +// use acvm::acir::circuit::{ExpressionWidth, Program}; +// use backend_interface::BackendError; +// use clap::Args; +// use iter_extended::vecmap; +// use nargo::{ +// artifacts::{contract::ContractArtifact, debug::DebugArtifact, program::ProgramArtifact}, +// package::Package, +// }; +// use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; +// use noirc_driver::{CompileOptions, NOIR_ARTIFACT_VERSION_STRING}; +// use noirc_errors::{debug_info::OpCodesCount, Location}; +// use noirc_frontend::graph::CrateName; +// use prettytable::{row, table, Row}; +// use rayon::prelude::*; +// use serde::Serialize; + +// use crate::backends::Backend; +// use crate::errors::CliError; + +// use super::{ +// compile_cmd::compile_workspace_full, +// fs::program::{read_contract_from_file, read_program_from_file}, +// NargoConfig, +// }; + +// /// Provides detailed information on each of a program's function (represented by a single circuit) +// /// +// /// Current information provided per circuit: +// /// 1. The number of ACIR opcodes +// /// 2. Counts the final number gates in the circuit used by a backend +// #[derive(Debug, Clone, Args)] +// #[clap(visible_alias = "i")] +// pub(crate) struct InfoCommand { +// /// The name of the package to detail +// #[clap(long, conflicts_with = "workspace")] +// package: Option, + +// /// Detail all packages in the workspace +// #[clap(long, conflicts_with = "package")] +// workspace: bool, + +// /// Output a JSON formatted report. Changes to this format are not currently considered breaking. +// #[clap(long, hide = true)] +// json: bool, + +// #[clap(long, hide = true)] +// profile_info: bool, + +// #[clap(flatten)] +// compile_options: CompileOptions, +// } + +// pub(crate) fn run( +// backend: &Backend, +// args: InfoCommand, +// config: NargoConfig, +// ) -> Result<(), CliError> { +// let toml_path = get_package_manifest(&config.program_dir)?; +// let default_selection = +// 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, +// Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), +// )?; + +// // Compile the full workspace in order to generate any build artifacts. +// compile_workspace_full(&workspace, &args.compile_options)?; + +// let binary_packages: Vec<(Package, ProgramArtifact)> = workspace +// .into_iter() +// .filter(|package| package.is_binary()) +// .map(|package| -> Result<(Package, ProgramArtifact), CliError> { +// let program_artifact_path = workspace.package_build_path(package); +// let program = read_program_from_file(program_artifact_path)?; +// Ok((package.clone(), program)) +// }) +// .collect::>()?; + +// let compiled_contracts: Vec = workspace +// .into_iter() +// .filter(|package| package.is_contract()) +// .map(|package| { +// let contract_artifact_path = workspace.package_build_path(package); +// read_contract_from_file(contract_artifact_path) +// }) +// .collect::>()?; + +// if args.profile_info { +// for (_, compiled_program) in &binary_packages { +// let debug_artifact = DebugArtifact::from(compiled_program.clone()); +// for function_debug in compiled_program.debug_symbols.debug_infos.iter() { +// let span_opcodes = function_debug.count_span_opcodes(); +// print_span_opcodes(span_opcodes, &debug_artifact); +// } +// } + +// for compiled_contract in &compiled_contracts { +// let debug_artifact = DebugArtifact::from(compiled_contract.clone()); +// let functions = &compiled_contract.functions; +// for contract_function in functions { +// for function_debug in contract_function.debug_symbols.debug_infos.iter() { +// let span_opcodes = function_debug.count_span_opcodes(); +// print_span_opcodes(span_opcodes, &debug_artifact); +// } +// } +// } +// } + +// let program_info = binary_packages +// .into_iter() +// .par_bridge() +// .map(|(package, program)| { +// count_opcodes_and_gates_in_program( +// backend, +// program, +// &package, +// args.compile_options.expression_width.unwrap_or_default(), +// ) +// }) +// .collect::>()?; + +// let contract_info = compiled_contracts +// .into_par_iter() +// .map(|contract| { +// count_opcodes_and_gates_in_contract( +// backend, +// contract, +// args.compile_options.expression_width.unwrap_or_default(), +// ) +// }) +// .collect::>()?; + +// let info_report = InfoReport { programs: program_info, contracts: contract_info }; + +// if args.json { +// // Expose machine-readable JSON data. +// println!("{}", serde_json::to_string(&info_report).unwrap()); +// } else { +// // Otherwise print human-readable table. +// if !info_report.programs.is_empty() { +// let mut program_table = table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes", Fm->"Backend Circuit Size"]); + +// for program_info in info_report.programs { +// let program_rows: Vec = program_info.into(); +// for row in program_rows { +// program_table.add_row(row); +// } +// } +// program_table.printstd(); +// } +// if !info_report.contracts.is_empty() { +// let mut contract_table = table!([ +// Fm->"Contract", +// Fm->"Function", +// Fm->"Expression Width", +// Fm->"ACIR Opcodes", +// Fm->"Backend Circuit Size" +// ]); +// for contract_info in info_report.contracts { +// let contract_rows: Vec = contract_info.into(); +// for row in contract_rows { +// contract_table.add_row(row); +// } +// } + +// contract_table.printstd(); +// } +// } + +// Ok(()) +// } + +// /// Provides profiling information on +// /// +// /// Number of OpCodes in relation to Noir source file +// /// and line number information +// fn print_span_opcodes( +// span_opcodes_map: HashMap, +// debug_artifact: &DebugArtifact, +// ) { +// let mut pairs: Vec<(&Location, &OpCodesCount)> = span_opcodes_map.iter().collect(); + +// pairs.sort_by(|a, b| { +// a.1.acir_size.cmp(&b.1.acir_size).then_with(|| a.1.brillig_size.cmp(&b.1.brillig_size)) +// }); + +// for (location, opcodes_count) in pairs { +// let debug_file = debug_artifact.file_map.get(&location.file).unwrap(); + +// let start_byte = byte_index(&debug_file.source, location.span.start() + 1); +// let end_byte = byte_index(&debug_file.source, location.span.end() + 1); +// let range = start_byte..end_byte; +// let span_content = &debug_file.source[range]; +// let line = debug_artifact.location_line_index(*location).unwrap() + 1; +// println!( +// "Ln. {}: {} (ACIR:{}, Brillig:{} opcode|s) in file: {}", +// line, +// span_content, +// opcodes_count.acir_size, +// opcodes_count.brillig_size, +// debug_file.path.to_str().unwrap() +// ); +// } +// } +// fn byte_index(string: &str, index: u32) -> usize { +// let mut byte_index = 0; +// let mut char_index = 0; + +// #[allow(clippy::explicit_counter_loop)] +// for (byte_offset, _) in string.char_indices() { +// if char_index == index { +// return byte_index; +// } + +// byte_index = byte_offset; +// char_index += 1; +// } + +// byte_index +// } + +// #[derive(Debug, Default, Serialize)] +// struct InfoReport { +// programs: Vec, +// contracts: Vec, +// } + +// #[derive(Debug, Serialize)] +// struct ProgramInfo { +// package_name: String, +// #[serde(skip)] +// expression_width: ExpressionWidth, +// functions: Vec, +// } + +// impl From for Vec { +// fn from(program_info: ProgramInfo) -> Self { +// vecmap(program_info.functions, |function| { +// row![ +// Fm->format!("{}", program_info.package_name), +// Fc->format!("{}", function.name), +// format!("{:?}", program_info.expression_width), +// Fc->format!("{}", function.acir_opcodes), +// Fc->format!("{}", function.circuit_size), +// ] +// }) +// } +// } + +// #[derive(Debug, Serialize)] +// struct ContractInfo { +// name: String, +// #[serde(skip)] +// expression_width: ExpressionWidth, +// // TODO(https://github.com/noir-lang/noir/issues/4720): Settle on how to display contract functions with non-inlined Acir calls +// functions: Vec, +// } + +// #[derive(Debug, Serialize)] +// struct FunctionInfo { +// name: String, +// acir_opcodes: usize, +// circuit_size: u32, +// } + +// impl From for Vec { +// fn from(contract_info: ContractInfo) -> Self { +// vecmap(contract_info.functions, |function| { +// row![ +// Fm->format!("{}", contract_info.name), +// Fc->format!("{}", function.name), +// format!("{:?}", contract_info.expression_width), +// Fc->format!("{}", function.acir_opcodes), +// Fc->format!("{}", function.circuit_size), +// ] +// }) +// } +// } + +// // fn count_opcodes_and_gates_in_program( +// // backend: &Backend, +// // compiled_program: ProgramArtifact, +// // package: &Package, +// // expression_width: ExpressionWidth, +// // ) -> Result { +// // let functions = compiled_program +// // .bytecode +// // .functions +// // .into_par_iter() +// // .enumerate() +// // .map(|(i, function)| -> Result<_, BackendError> { +// // Ok(FunctionInfo { +// // name: compiled_program.names[i].clone(), +// // acir_opcodes: function.opcodes.len(), +// // // Unconstrained functions do not matter to a backend circuit count so we pass nothing here +// // circuit_size: backend.get_exact_circuit_size(&Program { +// // functions: vec![function], +// // unconstrained_functions: Vec::new(), +// // })?, +// // }) +// // }) +// // .collect::>()?; + +// // Ok(ProgramInfo { package_name: package.name.to_string(), expression_width, functions }) +// // } + +// // fn count_opcodes_and_gates_in_contract( +// // backend: &Backend, +// // contract: ContractArtifact, +// // expression_width: ExpressionWidth, +// // ) -> Result { +// // let functions = contract +// // .functions +// // .into_par_iter() +// // .map(|function| -> Result<_, BackendError> { +// // Ok(FunctionInfo { +// // name: function.name, +// // // TODO(https://github.com/noir-lang/noir/issues/4720) +// // acir_opcodes: function.bytecode.functions[0].opcodes.len(), +// // circuit_size: backend.get_exact_circuit_size(&function.bytecode)?, +// // }) +// // }) +// // .collect::>()?; + +// // Ok(ContractInfo { name: contract.name, expression_width, functions }) +// // } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs index e8e178938157..d4a62f173256 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/mod.rs @@ -19,7 +19,7 @@ mod debug_cmd; mod execute_cmd; mod export_cmd; mod fmt_cmd; -mod info_cmd; +// mod info_cmd; mod init_cmd; mod lsp_cmd; mod new_cmd; @@ -76,7 +76,7 @@ enum NargoCommand { Prove(prove_cmd::ProveCommand), Verify(verify_cmd::VerifyCommand), Test(test_cmd::TestCommand), - Info(info_cmd::InfoCommand), + // Info(info_cmd::InfoCommand), Lsp(lsp_cmd::LspCommand), #[command(hide = true)] Dap(dap_cmd::DapCommand), @@ -117,7 +117,7 @@ pub(crate) fn start_cli() -> eyre::Result<()> { NargoCommand::Prove(args) => prove_cmd::run(&backend, args, config), NargoCommand::Verify(args) => verify_cmd::run(&backend, args, config), NargoCommand::Test(args) => test_cmd::run(&backend, args, config), - NargoCommand::Info(args) => info_cmd::run(&backend, args, config), + // NargoCommand::Info(args) => info_cmd::run(&backend, args, config), NargoCommand::CodegenVerifier(args) => codegen_verifier_cmd::run(&backend, args, config), NargoCommand::Backend(args) => backend_cmd::run(args), NargoCommand::Lsp(args) => lsp_cmd::run(&backend, args, config), diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/prove_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/prove_cmd.rs index 6fb6e7269f7f..15a0aa38367e 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/prove_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/prove_cmd.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use clap::Args; use nargo::constants::{PROVER_INPUT_FILE, VERIFIER_INPUT_FILE}; use nargo::package::Package; @@ -68,12 +70,13 @@ pub(crate) fn run( let binary_packages = workspace.into_iter().filter(|package| package.is_binary()); for package in binary_packages { let program_artifact_path = workspace.package_build_path(package); - let program: CompiledProgram = read_program_from_file(program_artifact_path)?.into(); + let program: CompiledProgram = read_program_from_file(&program_artifact_path)?.into(); let proof = prove_package( backend, package, program, + program_artifact_path, &args.prover_name, &args.verifier_name, args.verify, @@ -86,10 +89,12 @@ pub(crate) fn run( Ok(()) } +#[warn(clippy::too_many_arguments)] fn prove_package( backend: &Backend, package: &Package, compiled_program: CompiledProgram, + program_artifact_path: PathBuf, prover_name: &str, verifier_name: &str, check_proof: bool, @@ -117,11 +122,15 @@ fn prove_package( Format::Toml, )?; - let proof = backend.prove(&compiled_program.program, witness_stack)?; + let proof = backend.prove( + program_artifact_path.clone(), + witness_stack, + compiled_program.program.functions[0].public_inputs().0.len() as u32, + )?; if check_proof { let public_inputs = public_abi.encode(&public_inputs, return_value)?; - let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.program)?; + let valid_proof = backend.verify(&proof, public_inputs, program_artifact_path)?; if !valid_proof { return Err(CliError::InvalidProof("".into())); diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/verify_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/verify_cmd.rs index a7f2772330a0..ad1978cabe00 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/verify_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/verify_cmd.rs @@ -81,7 +81,8 @@ fn verify_package( let proof = load_hex_data(&proof_path)?; - let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.program)?; + let valid_proof = + backend.verify(&proof, public_inputs, workspace.package_build_path(package))?; if valid_proof { Ok(())