diff --git a/acvm-repo/acir/src/native_types/witness_stack.rs b/acvm-repo/acir/src/native_types/witness_stack.rs index d048e050995..6338ad630d6 100644 --- a/acvm-repo/acir/src/native_types/witness_stack.rs +++ b/acvm-repo/acir/src/native_types/witness_stack.rs @@ -12,6 +12,9 @@ use super::WitnessMap; enum SerializationError { #[error(transparent)] Deflate(#[from] std::io::Error), + + #[error(transparent)] + BincodeError(#[from] bincode::Error), } #[derive(Debug, Error)] @@ -57,11 +60,11 @@ impl From> for WitnessStack { } } -impl TryFrom> for Vec { +impl TryFrom<&WitnessStack> for Vec { type Error = WitnessStackError; - fn try_from(val: WitnessStack) -> Result { - let buf = bincode::serialize(&val).unwrap(); + fn try_from(val: &WitnessStack) -> Result { + let buf = bincode::serialize(val).map_err(|e| WitnessStackError(e.into()))?; let mut deflater = GzEncoder::new(buf.as_slice(), Compression::best()); let mut buf_c = Vec::new(); deflater.read_to_end(&mut buf_c).map_err(|err| WitnessStackError(err.into()))?; @@ -69,6 +72,14 @@ impl TryFrom> for Vec { } } +impl TryFrom> for Vec { + type Error = WitnessStackError; + + fn try_from(val: WitnessStack) -> Result { + Self::try_from(&val) + } +} + impl Deserialize<'a>> TryFrom<&[u8]> for WitnessStack { type Error = WitnessStackError; @@ -76,7 +87,8 @@ impl Deserialize<'a>> TryFrom<&[u8]> for WitnessStack { let mut deflater = GzDecoder::new(bytes); let mut buf_d = Vec::new(); deflater.read_to_end(&mut buf_d).map_err(|err| WitnessStackError(err.into()))?; - let witness_stack = bincode::deserialize(&buf_d).unwrap(); + let witness_stack = + bincode::deserialize(&buf_d).map_err(|e| WitnessStackError(e.into()))?; Ok(witness_stack) } } diff --git a/compiler/wasm/src/compile.rs b/compiler/wasm/src/compile.rs index d31b7ec47c7..8c0359bbced 100644 --- a/compiler/wasm/src/compile.rs +++ b/compiler/wasm/src/compile.rs @@ -130,7 +130,7 @@ pub(crate) struct DependencyGraph { pub(crate) root_dependencies: Vec, pub(crate) library_dependencies: BTreeMap>, } -/// This is map contains the paths of all of the files in the entry-point crate and +/// This map contains the paths of all of the files in the entry-point crate and /// the transitive dependencies of the entry-point crate. /// /// This is for all intents and purposes the file system that the compiler will use to resolve/compile diff --git a/tooling/acvm_cli/src/cli/execute_cmd.rs b/tooling/acvm_cli/src/cli/execute_cmd.rs index 703d272310b..c1d4533230e 100644 --- a/tooling/acvm_cli/src/cli/execute_cmd.rs +++ b/tooling/acvm_cli/src/cli/execute_cmd.rs @@ -8,7 +8,7 @@ use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use nargo::PrintOutput; -use nargo::{foreign_calls::DefaultForeignCallBuilder, ops::execute_program}; +use nargo::foreign_calls::DefaultForeignCallBuilder; use noir_artifact_cli::errors::CliError; use noir_artifact_cli::fs::artifact::read_bytecode_from_file; use noir_artifact_cli::fs::witness::save_witness_to_dir; @@ -56,7 +56,7 @@ fn run_command(args: ExecuteCommand) -> Result { )?; if args.output_witness.is_some() { save_witness_to_dir( - output_witness, + &output_witness, &args.output_witness.unwrap(), &args.working_directory, )?; @@ -80,7 +80,8 @@ pub(crate) fn execute_program_from_witness( ) -> Result, CliError> { let program: Program = Program::deserialize_program(bytecode).map_err(CliError::CircuitDeserializationError)?; - execute_program( + + nargo::ops::execute_program( &program, inputs_map, &Bn254BlackBoxSolver(pedantic_solving), diff --git a/tooling/artifact_cli/src/commands/execute_cmd.rs b/tooling/artifact_cli/src/commands/execute_cmd.rs index f863428149b..df5d469e75b 100644 --- a/tooling/artifact_cli/src/commands/execute_cmd.rs +++ b/tooling/artifact_cli/src/commands/execute_cmd.rs @@ -1,18 +1,15 @@ -use std::{collections::BTreeMap, path::PathBuf}; +use std::path::PathBuf; -use acir::{FieldElement, circuit::Program, native_types::WitnessStack}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; -use color_eyre::eyre::{self, bail}; use crate::{ Artifact, errors::CliError, - fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}, + execution::{self, ExecutionResults}, }; -use nargo::{NargoError, PrintOutput, foreign_calls::DefaultForeignCallBuilder}; -use noirc_abi::{Abi, input_parser::InputValue}; -use noirc_artifacts::debug::DebugArtifact; +use nargo::{PrintOutput, foreign_calls::DefaultForeignCallBuilder}; +use noirc_driver::CompiledProgram; use super::parse_and_normalize_path; @@ -21,106 +18,84 @@ use super::parse_and_normalize_path; pub struct ExecuteCommand { /// Path to the JSON build artifact (either a program or a contract). #[clap(long, short, value_parser = parse_and_normalize_path)] - artifact_path: PathBuf, + pub artifact_path: PathBuf, /// Path to the Prover.toml file which contains the inputs and the /// optional return value in ABI format. #[clap(long, short, value_parser = parse_and_normalize_path)] - prover_file: PathBuf, + pub prover_file: PathBuf, /// Path to the directory where the output witness should be saved. /// If empty then the results are discarded. #[clap(long, short, value_parser = parse_and_normalize_path)] - output_dir: Option, + pub output_dir: Option, /// Write the execution witness to named file /// /// Defaults to the name of the circuit being executed. #[clap(long, short)] - witness_name: Option, + pub witness_name: Option, /// Name of the function to execute, if the artifact is a contract. #[clap(long)] - contract_fn: Option, + pub contract_fn: Option, /// JSON RPC url to solve oracle calls. #[clap(long)] - oracle_resolver: Option, + pub oracle_resolver: Option, /// Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. #[clap(long, default_value_t = false)] - pedantic_solving: bool, + pub pedantic_solving: bool, } -pub fn run(args: ExecuteCommand) -> eyre::Result<()> { +pub fn run(args: ExecuteCommand) -> Result<(), CliError> { let artifact = Artifact::read_from_file(&args.artifact_path)?; + let artifact_name = args.artifact_path.file_stem().and_then(|s| s.to_str()).unwrap_or_default(); - let circuit = match artifact { - Artifact::Program(program) => Circuit { - name: None, - abi: program.abi, - bytecode: program.bytecode, - debug_symbols: program.debug_symbols, - file_map: program.file_map, - }, + let (circuit, circuit_name): (CompiledProgram, String) = match artifact { + Artifact::Program(program) => (program.into(), artifact_name.to_string()), Artifact::Contract(contract) => { - let names = - contract.functions.iter().map(|f| f.name.clone()).collect::>().join(","); + let names = || contract.functions.iter().map(|f| f.name.clone()).collect::>(); let Some(ref name) = args.contract_fn else { - bail!("--contract-fn missing; options: [{names}]"); + return Err(CliError::MissingContractFn { names: names() }); }; - let Some(function) = contract.functions.into_iter().find(|f| f.name == *name) else { - bail!("unknown --contract-fn '{name}'; options: [{names}]"); + let Some(program) = contract.function_as_compiled_program(name) else { + return Err(CliError::UnknownContractFn { name: name.clone(), names: names() }); }; - Circuit { - name: Some(name.clone()), - abi: function.abi, - bytecode: function.bytecode, - debug_symbols: function.debug_symbols, - file_map: contract.file_map, - } + (program, format!("{artifact_name}::{name}")) } }; match execute(&circuit, &args) { - Ok(solved) => { - save_witness(circuit, args, solved)?; - } - Err(CliError::CircuitExecutionError(err)) => { - show_diagnostic(circuit, err); + Ok(results) => { + execution::save_and_check_witness( + &circuit, + results, + &circuit_name, + args.output_dir.as_deref(), + args.witness_name.as_deref(), + )?; } Err(e) => { - bail!("failed to execute the circuit: {e}"); + if let CliError::CircuitExecutionError(ref err) = e { + execution::show_diagnostic(&circuit, err); + } + // Still returning the error to facilitate command forwarding, to indicate that the command failed. + return Err(e); } } Ok(()) } -/// Parameters necessary to execute a circuit, display execution failures, etc. -struct Circuit { - name: Option, - abi: Abi, - bytecode: Program, - debug_symbols: noirc_errors::debug_info::ProgramDebugInfo, - file_map: BTreeMap, -} - -struct SolvedWitnesses { - expected_return: Option, - actual_return: Option, - witness_stack: WitnessStack, -} - /// Execute a circuit and return the output witnesses. -fn execute(circuit: &Circuit, args: &ExecuteCommand) -> Result { - let (input_map, expected_return) = read_inputs_from_file(&args.prover_file, &circuit.abi)?; - - let initial_witness = circuit.abi.encode(&input_map, None)?; - +fn execute(circuit: &CompiledProgram, args: &ExecuteCommand) -> Result { // TODO: Build a custom foreign call executor that reads from the Oracle transcript, - // and use it as a base for the default executor; see `DefaultForeignCallBuilder::build_with_base` + // and use it as a base for the default executor. Using it as the innermost rather + // than top layer so that any extra `print` added for debugging is handled by the + // default, rather than trying to match it to the transcript. let mut foreign_call_executor = DefaultForeignCallBuilder { output: PrintOutput::Stdout, enable_mocks: false, @@ -130,80 +105,7 @@ fn execute(circuit: &Circuit, args: &ExecuteCommand) -> Result) { - if let Some(diagnostic) = nargo::errors::try_to_diagnose_runtime_error( - &err, - &circuit.abi, - &circuit.debug_symbols.debug_infos, - ) { - let debug_artifact = DebugArtifact { - debug_symbols: circuit.debug_symbols.debug_infos, - file_map: circuit.file_map, - }; - diagnostic.report(&debug_artifact, false); - } -} - -/// Print information about the witness and compare to expectations, -/// returning errors if something isn't right. -fn save_witness( - circuit: Circuit, - args: ExecuteCommand, - solved: SolvedWitnesses, -) -> eyre::Result<()> { - let artifact = args.artifact_path.file_stem().and_then(|s| s.to_str()).unwrap_or_default(); - let name = circuit - .name - .as_ref() - .map(|name| format!("{artifact}.{name}")) - .unwrap_or_else(|| artifact.to_string()); - - println!("[{}] Circuit witness successfully solved", name); - - if let Some(ref witness_dir) = args.output_dir { - let witness_path = save_witness_to_dir( - solved.witness_stack, - &args.witness_name.unwrap_or_else(|| name.clone()), - witness_dir, - )?; - println!("[{}] Witness saved to {}", name, witness_path.display()); - } - - // Check that the circuit returned a non-empty result if the ABI expects a return value. - if let Some(ref expected) = circuit.abi.return_type { - if solved.actual_return.is_none() { - bail!("Missing return witness; expected a value of type {expected:?}"); - } - } - - // Check that if the prover file contained a `return` entry then that's what we got. - if let Some(expected) = solved.expected_return { - match solved.actual_return { - None => { - bail!("Missing return witness;\nexpected:\n{expected:?}"); - } - Some(actual) if actual != expected => { - bail!("Unexpected return witness;\nexpected:\n{expected:?}\ngot:\n{actual:?}"); - } - _ => {} - } - } - - Ok(()) + execution::execute(circuit, &blackbox_solver, &mut foreign_call_executor, &args.prover_file) } diff --git a/tooling/artifact_cli/src/commands/mod.rs b/tooling/artifact_cli/src/commands/mod.rs index 78f3b19292f..9049b3695b7 100644 --- a/tooling/artifact_cli/src/commands/mod.rs +++ b/tooling/artifact_cli/src/commands/mod.rs @@ -1,3 +1,4 @@ +//! This module is for commands that we might want to invoke from `nargo` as-is. use std::path::PathBuf; use color_eyre::eyre; diff --git a/tooling/artifact_cli/src/errors.rs b/tooling/artifact_cli/src/errors.rs index 5f302f78695..6c87273cb37 100644 --- a/tooling/artifact_cli/src/errors.rs +++ b/tooling/artifact_cli/src/errors.rs @@ -1,6 +1,10 @@ use acir::FieldElement; use nargo::NargoError; -use noirc_abi::errors::{AbiError, InputParserError}; +use noirc_abi::{ + AbiReturnType, + errors::{AbiError, InputParserError}, + input_parser::InputValue, +}; use std::path::PathBuf; use thiserror::Error; @@ -61,4 +65,16 @@ pub enum CliError { #[error("Failed to serialize output witness: {0}")] OutputWitnessSerializationFailed(#[from] toml::ser::Error), + + #[error("Unexpected return value: expected {expected:?}; got {actual:?}")] + UnexpectedReturn { expected: InputValue, actual: Option }, + + #[error("Missing return witnesses; expected {expected:?}")] + MissingReturn { expected: AbiReturnType }, + + #[error("Missing contract function name; options: {names:?}")] + MissingContractFn { names: Vec }, + + #[error("Unknown contract function '{name}'; options: {names:?}")] + UnknownContractFn { name: String, names: Vec }, } diff --git a/tooling/artifact_cli/src/execution.rs b/tooling/artifact_cli/src/execution.rs new file mode 100644 index 00000000000..2e19ab55161 --- /dev/null +++ b/tooling/artifact_cli/src/execution.rs @@ -0,0 +1,135 @@ +use std::path::Path; + +use acir::{FieldElement, native_types::WitnessStack}; +use acvm::BlackBoxFunctionSolver; +use nargo::{NargoError, foreign_calls::ForeignCallExecutor}; +use noirc_abi::input_parser::InputValue; +use noirc_artifacts::debug::DebugArtifact; +use noirc_driver::CompiledProgram; + +use crate::{ + errors::CliError, + fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}, +}; + +/// Results of a circuit execution. +#[derive(Clone, Debug)] +pub struct ExecutionResults { + pub witness_stack: WitnessStack, + pub return_values: ReturnValues, +} + +/// The decoded `return` witnesses. +#[derive(Clone, Debug)] +pub struct ReturnValues { + /// The `return` value from the `Prover.toml` file, if present. + pub expected_return: Option, + /// The `return` value from the circuit execution. + pub actual_return: Option, +} + +/// Execute a circuit and return the output witnesses. +pub fn execute( + circuit: &CompiledProgram, + blackbox_solver: &B, + foreign_call_executor: &mut E, + prover_file: &Path, +) -> Result +where + B: BlackBoxFunctionSolver, + E: ForeignCallExecutor, +{ + let (input_map, expected_return) = read_inputs_from_file(prover_file, &circuit.abi)?; + + let initial_witness = circuit.abi.encode(&input_map, None)?; + + let witness_stack = nargo::ops::execute_program( + &circuit.program, + initial_witness, + blackbox_solver, + foreign_call_executor, + )?; + + let main_witness = + &witness_stack.peek().expect("Should have at least one witness on the stack").witness; + + let (_, actual_return) = circuit.abi.decode(main_witness)?; + + Ok(ExecutionResults { + witness_stack, + return_values: ReturnValues { actual_return, expected_return }, + }) +} + +/// Print an error stack trace, if possible. +pub fn show_diagnostic(circuit: &CompiledProgram, err: &NargoError) { + if let Some(diagnostic) = + nargo::errors::try_to_diagnose_runtime_error(err, &circuit.abi, &circuit.debug) + { + let debug_artifact = DebugArtifact { + debug_symbols: circuit.debug.clone(), + file_map: circuit.file_map.clone(), + }; + + diagnostic.report(&debug_artifact, false); + } +} + +/// Print some information and save the witness if an output directory is specified, +/// then checks if the expected return values were the ones we expected. +pub fn save_and_check_witness( + circuit: &CompiledProgram, + results: ExecutionResults, + circuit_name: &str, + witness_dir: Option<&Path>, + witness_name: Option<&str>, +) -> Result<(), CliError> { + println!("[{}] Circuit witness successfully solved", circuit_name); + // Save first, so that we can potentially look at the output if the expectations fail. + if let Some(witness_dir) = witness_dir { + save_witness(&results.witness_stack, circuit_name, witness_dir, witness_name)?; + } + check_witness(circuit, results.return_values) +} + +/// Save the witness stack to a file. +pub fn save_witness( + witness_stack: &WitnessStack, + circuit_name: &str, + witness_dir: &Path, + witness_name: Option<&str>, +) -> Result<(), CliError> { + let witness_name = witness_name.unwrap_or(circuit_name); + let witness_path = save_witness_to_dir(witness_stack, witness_name, witness_dir)?; + println!("[{}] Witness saved to {}", circuit_name, witness_path.display()); + Ok(()) +} + +/// Compare return values to expectations, returning errors if something unexpected was returned. +pub fn check_witness( + circuit: &CompiledProgram, + return_values: ReturnValues, +) -> Result<(), CliError> { + // Check that the circuit returned a non-empty result if the ABI expects a return value. + if let Some(ref expected) = circuit.abi.return_type { + if return_values.actual_return.is_none() { + return Err(CliError::MissingReturn { expected: expected.clone() }); + } + } + + // Check that if the prover file contained a `return` entry then that's what we got. + if let Some(expected) = return_values.expected_return { + match return_values.actual_return { + None => { + return Err(CliError::UnexpectedReturn { expected, actual: None }); + } + Some(actual) => { + if actual != expected { + return Err(CliError::UnexpectedReturn { expected, actual: Some(actual) }); + } + } + } + } + + Ok(()) +} diff --git a/tooling/artifact_cli/src/fs/witness.rs b/tooling/artifact_cli/src/fs/witness.rs index cf1fe75aad7..fee31ec1f22 100644 --- a/tooling/artifact_cli/src/fs/witness.rs +++ b/tooling/artifact_cli/src/fs/witness.rs @@ -7,7 +7,7 @@ use crate::errors::{CliError, FilesystemError}; /// Write `witness.gz` to the output directory. pub fn save_witness_to_dir( - witnesses: WitnessStack, + witnesses: &WitnessStack, witness_name: &str, witness_dir: &Path, ) -> Result { diff --git a/tooling/artifact_cli/src/lib.rs b/tooling/artifact_cli/src/lib.rs index 2cd2341b7b7..ac4316f5801 100644 --- a/tooling/artifact_cli/src/lib.rs +++ b/tooling/artifact_cli/src/lib.rs @@ -2,6 +2,7 @@ use noirc_artifacts::{contract::ContractArtifact, program::ProgramArtifact}; pub mod commands; pub mod errors; +pub mod execution; pub mod fs; /// A parsed JSON build artifact. diff --git a/tooling/inspector/src/cli/info_cmd.rs b/tooling/inspector/src/cli/info_cmd.rs index b221042c79f..34107ebea3a 100644 --- a/tooling/inspector/src/cli/info_cmd.rs +++ b/tooling/inspector/src/cli/info_cmd.rs @@ -3,7 +3,6 @@ use std::path::PathBuf; use clap::Args; use color_eyre::eyre; use noir_artifact_cli::Artifact; -use noirc_artifacts::program::ProgramArtifact; use noirc_artifacts_info::{InfoReport, count_opcodes_and_gates_in_program, show_info_report}; #[derive(Debug, Clone, Args)] @@ -39,19 +38,12 @@ pub(crate) fn run(args: InfoCommand) -> eyre::Result<()> { .into_iter() .filter(|f| args.contract_fn.as_ref().map(|n| *n == f.name).unwrap_or(true)) .map(|f| { - // We have to cheat to be able to call `count_opcodes_and_gates_in_program`. let package_name = format!("{}::{}", contract.name, f.name); - let program = ProgramArtifact { - noir_version: contract.noir_version.clone(), - hash: f.hash, - abi: f.abi, - bytecode: f.bytecode, - debug_symbols: f.debug_symbols, - file_map: contract.file_map.clone(), - names: f.names, - brillig_names: f.brillig_names, - }; - count_opcodes_and_gates_in_program(program, package_name, None) + let program = f.into_compiled_program( + contract.noir_version.clone(), + contract.file_map.clone(), + ); + count_opcodes_and_gates_in_program(program.into(), package_name, None) }) .collect::>(), }; diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index f12e83297e6..f9303180fc0 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -215,7 +215,7 @@ fn run_async( if let Some(witness_name) = witness_name { let witness_path = - save_witness_to_dir(solved_witness_stack, witness_name, target_dir)?; + save_witness_to_dir(&solved_witness_stack, witness_name, target_dir)?; println!("[{}] Witness saved to {}", package.name, witness_path.display()); } diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index c7c3227795d..07a7c1195af 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -1,24 +1,9 @@ -use std::path::PathBuf; - -use acvm::FieldElement; -use acvm::acir::native_types::WitnessStack; -use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; -use nargo::PrintOutput; use nargo::constants::PROVER_INPUT_FILE; -use nargo::errors::try_to_diagnose_runtime_error; -use nargo::foreign_calls::DefaultForeignCallBuilder; -use nargo::package::Package; use nargo::workspace::Workspace; use nargo_toml::PackageSelection; -use noir_artifact_cli::fs::artifact::read_program_from_file; -use noir_artifact_cli::fs::inputs::read_inputs_from_file; -use noir_artifact_cli::fs::witness::save_witness_to_dir; -use noirc_abi::InputMap; -use noirc_abi::input_parser::InputValue; -use noirc_artifacts::debug::DebugArtifact; -use noirc_driver::{CompileOptions, CompiledProgram}; +use noirc_driver::CompileOptions; use super::compile_cmd::compile_workspace_full; use super::{LockType, PackageOptions, WorkspaceCommand}; @@ -60,127 +45,27 @@ impl WorkspaceCommand for ExecuteCommand { } pub(crate) fn run(args: ExecuteCommand, workspace: Workspace) -> Result<(), CliError> { - let target_dir = &workspace.target_directory_path(); - // Compile the full workspace in order to generate any build artifacts. compile_workspace_full(&workspace, &args.compile_options)?; 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 abi = program.abi.clone(); - - let results = execute_program_and_decode( - program, - package, - &args.prover_name, - args.oracle_resolver.as_deref(), - Some(workspace.root_dir.clone()), - Some(package.name.to_string()), - args.compile_options.pedantic_solving, - )?; - - println!("[{}] Circuit witness successfully solved", package.name); - if let Some(ref return_value) = results.actual_return { - println!("[{}] Circuit output: {return_value:?}", package.name); - } - - let package_name = package.name.clone().into(); - let witness_name = args.witness_name.as_ref().unwrap_or(&package_name); - let witness_path = save_witness_to_dir(results.witness_stack, witness_name, target_dir)?; - println!("[{}] Witness saved to {}", package.name, witness_path.display()); - - // Sanity checks on the return value after the witness has been saved, so it can be inspected if necessary. - if let Some(expected) = results.expected_return { - if results.actual_return.as_ref() != Some(&expected) { - return Err(CliError::UnexpectedReturn { expected, actual: results.actual_return }); - } - } - // We can expect that if the circuit returns something, it should be non-empty after execution. - if let Some(ref expected) = abi.return_type { - if results.actual_return.is_none() { - return Err(CliError::MissingReturn { expected: expected.clone() }); - } - } + let prover_file = package.root_dir.join(&args.prover_name).with_extension("toml"); + + let cmd = noir_artifact_cli::commands::execute_cmd::ExecuteCommand { + artifact_path: program_artifact_path, + prover_file, + output_dir: Some(workspace.target_directory_path()), + witness_name: Some( + args.witness_name.clone().unwrap_or_else(|| package.name.to_string()), + ), + contract_fn: None, + oracle_resolver: args.oracle_resolver.clone(), + pedantic_solving: args.compile_options.pedantic_solving, + }; + + noir_artifact_cli::commands::execute_cmd::run(cmd)?; } Ok(()) } - -fn execute_program_and_decode( - program: CompiledProgram, - package: &Package, - prover_name: &str, - foreign_call_resolver_url: Option<&str>, - root_path: Option, - package_name: Option, - pedantic_solving: bool, -) -> Result { - // Parse the initial witness values from Prover.toml - let (inputs_map, expected_return) = read_inputs_from_file( - &package.root_dir.join(prover_name).with_extension("toml"), - &program.abi, - )?; - let witness_stack = execute_program( - &program, - &inputs_map, - foreign_call_resolver_url, - root_path, - package_name, - pedantic_solving, - )?; - // Get the entry point witness for the ABI - let main_witness = - &witness_stack.peek().expect("Should have at least one witness on the stack").witness; - let (_, actual_return) = program.abi.decode(main_witness)?; - - Ok(ExecutionResults { expected_return, actual_return, witness_stack }) -} - -struct ExecutionResults { - expected_return: Option, - actual_return: Option, - witness_stack: WitnessStack, -} - -pub(crate) fn execute_program( - compiled_program: &CompiledProgram, - inputs_map: &InputMap, - foreign_call_resolver_url: Option<&str>, - root_path: Option, - package_name: Option, - pedantic_solving: bool, -) -> Result, CliError> { - let initial_witness = compiled_program.abi.encode(inputs_map, None)?; - - let solved_witness_stack_err = nargo::ops::execute_program( - &compiled_program.program, - initial_witness, - &Bn254BlackBoxSolver(pedantic_solving), - &mut DefaultForeignCallBuilder { - output: PrintOutput::Stdout, - enable_mocks: false, - resolver_url: foreign_call_resolver_url.map(|s| s.to_string()), - root_path, - package_name, - } - .build(), - ); - match solved_witness_stack_err { - Ok(solved_witness_stack) => Ok(solved_witness_stack), - Err(err) => { - let debug_artifact = DebugArtifact { - debug_symbols: compiled_program.debug.clone(), - file_map: compiled_program.file_map.clone(), - }; - - if let Some(diagnostic) = - try_to_diagnose_runtime_error(&err, &compiled_program.abi, &compiled_program.debug) - { - diagnostic.report(&debug_artifact, false); - } - - Err(CliError::NargoError(err)) - } - } -} diff --git a/tooling/nargo_cli/src/errors.rs b/tooling/nargo_cli/src/errors.rs index 74ede00f0d0..fb241bcbd29 100644 --- a/tooling/nargo_cli/src/errors.rs +++ b/tooling/nargo_cli/src/errors.rs @@ -2,7 +2,7 @@ use acvm::FieldElement; use nargo::{NargoError, errors::CompileError}; use nargo_toml::ManifestError; use noir_debugger::errors::DapError; -use noirc_abi::{AbiReturnType, errors::AbiError, input_parser::InputValue}; +use noirc_abi::errors::AbiError; use std::path::PathBuf; use thiserror::Error; @@ -42,10 +42,4 @@ pub(crate) enum CliError { /// Error from the compilation pipeline #[error(transparent)] CompileError(#[from] CompileError), - - #[error("Unexpected return value: expected {expected:?}; got {actual:?}")] - UnexpectedReturn { expected: InputValue, actual: Option }, - - #[error("Missing return witnesses; expected {expected:?}")] - MissingReturn { expected: AbiReturnType }, } diff --git a/tooling/noirc_artifacts/src/contract.rs b/tooling/noirc_artifacts/src/contract.rs index c2e44e54266..9f8f7019ff1 100644 --- a/tooling/noirc_artifacts/src/contract.rs +++ b/tooling/noirc_artifacts/src/contract.rs @@ -1,6 +1,6 @@ use acvm::{FieldElement, acir::circuit::Program}; use noirc_abi::{Abi, AbiType, AbiValue}; -use noirc_driver::{CompiledContract, CompiledContractOutputs, ContractFunction}; +use noirc_driver::{CompiledContract, CompiledContractOutputs, CompiledProgram, ContractFunction}; use serde::{Deserialize, Serialize}; use noirc_driver::DebugFile; @@ -49,6 +49,14 @@ impl From for ContractArtifact { } } +impl ContractArtifact { + pub fn function_as_compiled_program(&self, function_name: &str) -> Option { + self.functions.iter().find(|f| f.name == function_name).map(|f| { + f.clone().into_compiled_program(self.noir_version.clone(), self.file_map.clone()) + }) + } +} + /// Each function in the contract will be compiled as a separate noir program. /// /// A contract function unlike a regular Noir program however can have additional properties. @@ -85,6 +93,26 @@ pub struct ContractFunctionArtifact { pub brillig_names: Vec, } +impl ContractFunctionArtifact { + pub fn into_compiled_program( + self, + noir_version: String, + file_map: BTreeMap, + ) -> CompiledProgram { + CompiledProgram { + noir_version, + hash: self.hash, + program: self.bytecode, + abi: self.abi, + debug: self.debug_symbols.debug_infos, + file_map, + warnings: Vec::new(), + names: self.names, + brillig_names: self.brillig_names, + } + } +} + impl From for ContractFunctionArtifact { fn from(func: ContractFunction) -> Self { ContractFunctionArtifact { diff --git a/tooling/profiler/src/cli/gates_flamegraph_cmd.rs b/tooling/profiler/src/cli/gates_flamegraph_cmd.rs index a82e5ea3e2a..736cddf7cf4 100644 --- a/tooling/profiler/src/cli/gates_flamegraph_cmd.rs +++ b/tooling/profiler/src/cli/gates_flamegraph_cmd.rs @@ -125,7 +125,7 @@ fn run_with_provider( #[cfg(test)] mod tests { use acir::circuit::{Circuit, Program}; - use color_eyre::eyre::{self}; + use color_eyre::eyre; use fm::codespan_files::Files; use noirc_artifacts::program::ProgramArtifact; use noirc_errors::debug_info::{DebugInfo, ProgramDebugInfo}; diff --git a/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs b/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs index 649331891a2..8ce9ba1de39 100644 --- a/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs +++ b/tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs @@ -160,7 +160,7 @@ mod tests { brillig::{BrilligBytecode, BrilligFunctionId}, }, }; - use color_eyre::eyre::{self}; + use color_eyre::eyre; use fm::codespan_files::Files; use noirc_artifacts::program::ProgramArtifact; use noirc_errors::debug_info::{DebugInfo, ProgramDebugInfo}; diff --git a/tooling/profiler/src/flamegraph.rs b/tooling/profiler/src/flamegraph.rs index b56e8a43312..16857eb2454 100644 --- a/tooling/profiler/src/flamegraph.rs +++ b/tooling/profiler/src/flamegraph.rs @@ -3,7 +3,7 @@ use std::{collections::BTreeMap, io::BufWriter}; use acir::circuit::OpcodeLocation; use acir::circuit::brillig::BrilligFunctionId; -use color_eyre::eyre::{self}; +use color_eyre::eyre; use fm::codespan_files::Files; use fxhash::FxHashMap as HashMap; use inferno::flamegraph::{Options, TextTruncateDirection, from_lines}; diff --git a/tooling/profiler/src/gates_provider.rs b/tooling/profiler/src/gates_provider.rs index 3f07f3e4be6..044e2a3642c 100644 --- a/tooling/profiler/src/gates_provider.rs +++ b/tooling/profiler/src/gates_provider.rs @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; -use color_eyre::eyre::{self}; +use color_eyre::eyre; use serde::{Deserialize, Serialize}; pub(crate) trait GatesProvider {