diff --git a/test_programs/noir_test_success/bounded_vec/Prover.toml b/test_programs/noir_test_success/bounded_vec/Prover.toml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test_programs/noir_test_success/brillig_overflow_checks/Prover.toml b/test_programs/noir_test_success/brillig_overflow_checks/Prover.toml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test_programs/noir_test_success/field_comparisons/Prover.toml b/test_programs/noir_test_success/field_comparisons/Prover.toml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test_programs/noir_test_success/ignored_oracle/Nargo.toml b/test_programs/noir_test_success/ignored_oracle/Nargo.toml new file mode 100644 index 0000000000..0d9b77c01d --- /dev/null +++ b/test_programs/noir_test_success/ignored_oracle/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "ignored_oracle" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] diff --git a/test_programs/noir_test_success/ignored_oracle/src/main.nr b/test_programs/noir_test_success/ignored_oracle/src/main.nr new file mode 100644 index 0000000000..9e0bc18993 --- /dev/null +++ b/test_programs/noir_test_success/ignored_oracle/src/main.nr @@ -0,0 +1,23 @@ +// In `nargo test` we want to avoid the need for an external oracle resolver service to be required in the situation +// where its existence doesn't affect whether the tests will pass or fail. We then want to be able to handle any +// oracles which return zero field elements. + +// Note that this custom oracle doesn't return any new values into the program. +// We can then safely continue execution even in the case where there is no oracle resolver to handle it. +#[oracle(custom_debug)] +unconstrained fn custom_debug() {} + +// However this oracle call should return a field element. We expect the ACVM to raise an error when it +// doesn't receive this value. +#[oracle(custom_getter)] +unconstrained fn custom_getter() -> Field {} + +#[test] +unconstrained fn unit_return_oracle_ignored() { + custom_debug(); +} + +#[test(should_fail_with = "0 output values were provided as a foreign call result for 1 destination slots")] +unconstrained fn field_return_oracle_fails() { + let _ = custom_getter(); +} diff --git a/test_programs/noir_test_success/mock_oracle/Prover.toml b/test_programs/noir_test_success/mock_oracle/Prover.toml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test_programs/noir_test_success/out_of_bounds_alignment/Prover.toml b/test_programs/noir_test_success/out_of_bounds_alignment/Prover.toml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test_programs/noir_test_success/should_fail_with_matches/Prover.toml b/test_programs/noir_test_success/should_fail_with_matches/Prover.toml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index 33767314a3..9031303241 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -315,43 +315,49 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { .iter() .position(|response| response.matches(foreign_call_name, &foreign_call.inputs)); - match (mock_response_position, &self.external_resolver) { - (Some(response_position), _) => { - let mock = self - .mocked_responses - .get_mut(response_position) - .expect("Invalid position of mocked response"); - - mock.last_called_params = Some(foreign_call.inputs.clone()); - - let result = mock.result.values.clone(); - - if let Some(times_left) = &mut mock.times_left { - *times_left -= 1; - if *times_left == 0 { - self.mocked_responses.remove(response_position); - } - } + if let Some(response_position) = mock_response_position { + // If the program has registered a mocked response to this oracle call then we prefer responding + // with that. + + let mock = self + .mocked_responses + .get_mut(response_position) + .expect("Invalid position of mocked response"); + + mock.last_called_params = Some(foreign_call.inputs.clone()); + + let result = mock.result.values.clone(); - Ok(result.into()) + if let Some(times_left) = &mut mock.times_left { + *times_left -= 1; + if *times_left == 0 { + self.mocked_responses.remove(response_position); + } } - (None, Some(external_resolver)) => { - let encoded_params: Vec<_> = - foreign_call.inputs.iter().map(build_json_rpc_arg).collect(); - let req = - external_resolver.build_request(foreign_call_name, &encoded_params); + Ok(result.into()) + } else if let Some(external_resolver) = &self.external_resolver { + // If the user has registered an external resolver then we forward any remaining oracle calls there. - let response = external_resolver.send_request(req)?; + let encoded_params: Vec<_> = + foreign_call.inputs.iter().map(build_json_rpc_arg).collect(); - let parsed_response: ForeignCallResult = response.result()?; + let req = external_resolver.build_request(foreign_call_name, &encoded_params); - Ok(parsed_response.into()) - } - (None, None) => panic!( - "No mock for foreign call {}({:?})", - foreign_call_name, &foreign_call.inputs - ), + let response = external_resolver.send_request(req)?; + + let parsed_response: ForeignCallResult = response.result()?; + + Ok(parsed_response.into()) + } else { + // If there's no registered mock oracle response and no registered resolver then we cannot + // return a correct response to the ACVM. The best we can do is to return an empty response, + // this allows us to ignore any foreign calls which exist solely to pass information from inside + // the circuit to the environment (e.g. custom logging) as the execution will still be able to progress. + // + // We optimistically return an empty response for all oracle calls as the ACVM will error + // should a response have been required. + Ok(ForeignCallResult::default().into()) } } }