diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 37c3b9d89d9..33862ed641f 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -8,7 +8,7 @@ use acvm::{BlackBoxFunctionSolver, FieldElement}; use nargo::artifacts::debug::DebugArtifact; use nargo::errors::{ExecutionError, Location}; -use nargo::ops::ForeignCallExecutor; +use nargo::ops::{DefaultForeignCallExecutor, ForeignCallExecutor}; use nargo::NargoError; use std::collections::{hash_set::Iter, HashSet}; @@ -24,9 +24,8 @@ pub(super) enum DebugCommandResult { pub(super) struct DebugContext<'a, B: BlackBoxFunctionSolver> { acvm: ACVM<'a, B>, brillig_solver: Option>, - foreign_call_executor: ForeignCallExecutor, + foreign_call_executor: DefaultForeignCallExecutor, debug_artifact: &'a DebugArtifact, - show_output: bool, breakpoints: HashSet, } @@ -40,9 +39,8 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { Self { acvm: ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness), brillig_solver: None, - foreign_call_executor: ForeignCallExecutor::default(), + foreign_call_executor: DefaultForeignCallExecutor::new(true), debug_artifact, - show_output: true, breakpoints: HashSet::new(), } } @@ -119,15 +117,14 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } fn handle_foreign_call(&mut self, foreign_call: ForeignCallWaitInfo) -> DebugCommandResult { - let foreign_call_result = - self.foreign_call_executor.execute(&foreign_call, self.show_output); + let foreign_call_result = self.foreign_call_executor.execute(&foreign_call); match foreign_call_result { Ok(foreign_call_result) => { self.acvm.resolve_pending_foreign_call(foreign_call_result); // TODO: should we retry executing the opcode somehow in this case? DebugCommandResult::Ok } - Err(error) => DebugCommandResult::Error(error), + Err(error) => DebugCommandResult::Error(error.into()), } } diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index cf14934d61e..d7cb44188c4 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -7,16 +7,14 @@ use crate::NargoError; use super::foreign_calls::ForeignCallExecutor; -pub fn execute_circuit( - blackbox_solver: &B, +pub fn execute_circuit( circuit: &Circuit, initial_witness: WitnessMap, - show_output: bool, + blackbox_solver: &B, + foreign_call_executor: &mut F, ) -> Result { let mut acvm = ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness); - let mut foreign_call_executor = ForeignCallExecutor::default(); - loop { let solver_status = acvm.solve(); @@ -50,8 +48,7 @@ pub fn execute_circuit( })); } ACVMStatus::RequiresForeignCall(foreign_call) => { - let foreign_call_result = - foreign_call_executor.execute(&foreign_call, show_output)?; + let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; acvm.resolve_pending_foreign_call(foreign_call_result); } } diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index 4d20a0bd4f0..6cc78febab3 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -5,7 +5,12 @@ use acvm::{ use iter_extended::vecmap; use noirc_printable_type::{decode_string_value, ForeignCallError, PrintableValueDisplay}; -use crate::NargoError; +pub trait ForeignCallExecutor { + fn execute( + &mut self, + foreign_call: &ForeignCallWaitInfo, + ) -> Result; +} /// This enumeration represents the Brillig foreign calls that are natively supported by nargo. /// After resolution of a foreign call, nargo will restart execution of the ACVM @@ -89,23 +94,55 @@ impl MockedCall { } #[derive(Debug, Default)] -pub struct ForeignCallExecutor { +pub struct DefaultForeignCallExecutor { /// Mocks have unique ids used to identify them in Noir, allowing to update or remove them. last_mock_id: usize, /// The registered mocks mocked_responses: Vec, + /// Whether to print [`ForeignCall::Println`] output. + show_output: bool, } -impl ForeignCallExecutor { - pub fn execute( +impl DefaultForeignCallExecutor { + pub fn new(show_output: bool) -> Self { + DefaultForeignCallExecutor { show_output, ..DefaultForeignCallExecutor::default() } + } +} + +impl DefaultForeignCallExecutor { + fn extract_mock_id( + foreign_call_inputs: &[ForeignCallParam], + ) -> Result<(usize, &[ForeignCallParam]), ForeignCallError> { + let (id, params) = + foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?; + Ok((id.unwrap_value().to_usize(), params)) + } + + fn find_mock_by_id(&mut self, id: usize) -> Option<&mut MockedCall> { + self.mocked_responses.iter_mut().find(|response| response.id == id) + } + + fn parse_string(param: &ForeignCallParam) -> String { + let fields: Vec<_> = param.values().into_iter().map(|value| value.to_field()).collect(); + decode_string_value(&fields) + } + + fn execute_println(foreign_call_inputs: &[ForeignCallParam]) -> Result<(), ForeignCallError> { + let display_values: PrintableValueDisplay = foreign_call_inputs.try_into()?; + println!("{display_values}"); + Ok(()) + } +} + +impl ForeignCallExecutor for DefaultForeignCallExecutor { + fn execute( &mut self, foreign_call: &ForeignCallWaitInfo, - show_output: bool, - ) -> Result { + ) -> Result { let foreign_call_name = foreign_call.function.as_str(); match ForeignCall::lookup(foreign_call_name) { Some(ForeignCall::Println) => { - if show_output { + if self.show_output { Self::execute_println(&foreign_call.inputs)?; } Ok(ForeignCallResult { values: vec![] }) @@ -202,27 +239,4 @@ impl ForeignCallExecutor { } } } - - fn extract_mock_id( - foreign_call_inputs: &[ForeignCallParam], - ) -> Result<(usize, &[ForeignCallParam]), ForeignCallError> { - let (id, params) = - foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?; - Ok((id.unwrap_value().to_usize(), params)) - } - - fn find_mock_by_id(&mut self, id: usize) -> Option<&mut MockedCall> { - self.mocked_responses.iter_mut().find(|response| response.id == id) - } - - fn parse_string(param: &ForeignCallParam) -> String { - let fields: Vec<_> = param.values().into_iter().map(|value| value.to_field()).collect(); - decode_string_value(&fields) - } - - fn execute_println(foreign_call_inputs: &[ForeignCallParam]) -> Result<(), NargoError> { - let display_values: PrintableValueDisplay = foreign_call_inputs.try_into()?; - println!("{display_values}"); - Ok(()) - } } diff --git a/tooling/nargo/src/ops/mod.rs b/tooling/nargo/src/ops/mod.rs index 9a523c59841..34487ed9770 100644 --- a/tooling/nargo/src/ops/mod.rs +++ b/tooling/nargo/src/ops/mod.rs @@ -1,6 +1,6 @@ pub use self::compile::{compile_program, compile_workspace}; pub use self::execute::execute_circuit; -pub use self::foreign_calls::ForeignCallExecutor; +pub use self::foreign_calls::{DefaultForeignCallExecutor, ForeignCallExecutor}; pub use self::optimize::{optimize_contract, optimize_program}; pub use self::test::{run_test, TestStatus}; diff --git a/tooling/nargo/src/ops/test.rs b/tooling/nargo/src/ops/test.rs index d2ef2659e4d..5bfdd6d15d0 100644 --- a/tooling/nargo/src/ops/test.rs +++ b/tooling/nargo/src/ops/test.rs @@ -6,7 +6,7 @@ use noirc_frontend::hir::{def_map::TestFunction, Context}; use crate::{errors::try_to_diagnose_runtime_error, NargoError}; -use super::execute_circuit; +use super::{execute_circuit, DefaultForeignCallExecutor}; pub enum TestStatus { Pass, @@ -26,8 +26,12 @@ pub fn run_test( Ok(program) => { // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, // otherwise constraints involving these expressions will not error. - let circuit_execution = - execute_circuit(blackbox_solver, &program.circuit, WitnessMap::new(), show_output); + let circuit_execution = execute_circuit( + &program.circuit, + WitnessMap::new(), + blackbox_solver, + &mut DefaultForeignCallExecutor::new(show_output), + ); test_status_program_compile_pass(test_function, program.debug, circuit_execution) } Err(err) => test_status_program_compile_fail(err, test_function), diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index 3a0da2b1134..91e4b800453 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -4,6 +4,7 @@ use clap::Args; use nargo::artifacts::debug::DebugArtifact; use nargo::constants::PROVER_INPUT_FILE; use nargo::errors::try_to_diagnose_runtime_error; +use nargo::ops::DefaultForeignCallExecutor; use nargo::package::Package; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::input_parser::{Format, InputValue}; @@ -106,10 +107,10 @@ pub(crate) fn execute_program( let initial_witness = compiled_program.abi.encode(inputs_map, None)?; let solved_witness_err = nargo::ops::execute_circuit( - &blackbox_solver, &compiled_program.circuit, initial_witness, - true, + &blackbox_solver, + &mut DefaultForeignCallExecutor::new(true), ); match solved_witness_err { Ok(solved_witness) => Ok(solved_witness),