diff --git a/.github/actions/download-noir-execute/action.yml b/.github/actions/download-noir-execute/action.yml new file mode 100644 index 00000000000..470edc04538 --- /dev/null +++ b/.github/actions/download-noir-execute/action.yml @@ -0,0 +1,18 @@ +name: Download noir-execute +description: Downloads the noir-execute binary from an artifact and adds it to the path + +runs: + using: composite + steps: + - name: Download noir-execute binary + uses: actions/download-artifact@v4 + with: + name: noir-execute + path: ./noir-execute + + - name: Set noir-execute on PATH + shell: bash + run: | + noir_binary="${{ github.workspace }}/noir-execute/noir-execute" + chmod +x $noir_binary + echo "$(dirname $noir_binary)" >> $GITHUB_PATH diff --git a/.github/workflows/test-js-packages.yml b/.github/workflows/test-js-packages.yml index f88e64fa1a5..981d97e1bdf 100644 --- a/.github/workflows/test-js-packages.yml +++ b/.github/workflows/test-js-packages.yml @@ -78,6 +78,39 @@ jobs: path: ./dist/* retention-days: 3 + build-noir-execute: + runs-on: ubuntu-22.04 + timeout-minutes: 30 + + steps: + - name: Checkout Noir repo + uses: actions/checkout@v4 + + - name: Setup toolchain + uses: dtolnay/rust-toolchain@1.75.0 + + - uses: Swatinem/rust-cache@v2 + with: + key: x86_64-unknown-linux-gnu + cache-on-failure: true + save-if: ${{ github.event_name != 'merge_group' }} + + - name: Build noir-execute + run: cargo build --package noir_artifact_cli --release + + - name: Package artifacts + run: | + mkdir dist + cp ./target/release/noir-execute ./dist/noir-execute + 7z a -ttar -so -an ./dist/* | 7z a -si ./noir-execute-x86_64-unknown-linux-gnu.tar.gz + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: noir-execute + path: ./dist/* + retention-days: 3 + build-noirc-abi: runs-on: ubuntu-22.04 timeout-minutes: 30 @@ -457,7 +490,7 @@ jobs: test-examples: name: Example scripts runs-on: ubuntu-22.04 - needs: [build-nargo] + needs: [build-nargo, build-noir-execute] timeout-minutes: 30 steps: @@ -477,6 +510,9 @@ jobs: - name: Download nargo binary uses: ./.github/actions/download-nargo + - name: Download noir-execute binary + uses: ./.github/actions/download-noir-execute + - name: Run `prove_and_verify` working-directory: ./examples/prove_and_verify run: ./test.sh @@ -485,6 +521,10 @@ jobs: working-directory: ./examples/codegen_verifier run: ./test.sh + - name: Run `oracle_transcript` + working-directory: ./examples/oracle_transcript + run: ./test.sh + external-repo-checks: needs: [build-nargo, critical-library-list] runs-on: ubuntu-22.04 diff --git a/examples/oracle_transcript/Nargo.toml b/examples/oracle_transcript/Nargo.toml new file mode 100644 index 00000000000..3f333c912b0 --- /dev/null +++ b/examples/oracle_transcript/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "oracle_transcript" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] diff --git a/examples/oracle_transcript/Oracle.jsonl b/examples/oracle_transcript/Oracle.jsonl new file mode 100644 index 00000000000..570e9590761 --- /dev/null +++ b/examples/oracle_transcript/Oracle.jsonl @@ -0,0 +1,5 @@ +{"call":{"function":"void_field","inputs":[]},"result":{"values":["000000000000000000000000000000000000000000000000000000000000000a"]}} +{"call":{"function":"void_field","inputs":[]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000014"]}} +{"call":{"function":"field_field","inputs":["0000000000000000000000000000000000000000000000000000000000000002"]},"result":{"values":["000000000000000000000000000000000000000000000000000000000000001e"]}} +{"call":{"function":"field_field","inputs":["0000000000000000000000000000000000000000000000000000000000000003"]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000028"]}} +{"call":{"function":"struct_field","inputs":["000000000000000000000000000000000000000000000000000000000000012c","0000000000000000000000000000000000000000000000000000000000000320",["000000000000000000000000000000000000000000000000000000000000000a","0000000000000000000000000000000000000000000000000000000000000014","000000000000000000000000000000000000000000000000000000000000001e","0000000000000000000000000000000000000000000000000000000000000028"]]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000064"]}} diff --git a/examples/oracle_transcript/Oracle.test.jsonl b/examples/oracle_transcript/Oracle.test.jsonl new file mode 100644 index 00000000000..cebe10d307f --- /dev/null +++ b/examples/oracle_transcript/Oracle.test.jsonl @@ -0,0 +1,20 @@ +{"call":{"function":"create_mock","inputs":[["0000000000000000000000000000000000000000000000000000000000000076","000000000000000000000000000000000000000000000000000000000000006f","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000064","000000000000000000000000000000000000000000000000000000000000005f","0000000000000000000000000000000000000000000000000000000000000066","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000065","000000000000000000000000000000000000000000000000000000000000006c","0000000000000000000000000000000000000000000000000000000000000064"]]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000000"]}} +{"call":{"function":"set_mock_returns","inputs":["0000000000000000000000000000000000000000000000000000000000000000","000000000000000000000000000000000000000000000000000000000000000a"]},"result":{"values":[]}} +{"call":{"function":"set_mock_times","inputs":["0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000001"]},"result":{"values":[]}} +{"call":{"function":"create_mock","inputs":[["0000000000000000000000000000000000000000000000000000000000000076","000000000000000000000000000000000000000000000000000000000000006f","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000064","000000000000000000000000000000000000000000000000000000000000005f","0000000000000000000000000000000000000000000000000000000000000066","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000065","000000000000000000000000000000000000000000000000000000000000006c","0000000000000000000000000000000000000000000000000000000000000064"]]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000001"]}} +{"call":{"function":"set_mock_returns","inputs":["0000000000000000000000000000000000000000000000000000000000000001","0000000000000000000000000000000000000000000000000000000000000014"]},"result":{"values":[]}} +{"call":{"function":"set_mock_times","inputs":["0000000000000000000000000000000000000000000000000000000000000001","0000000000000000000000000000000000000000000000000000000000000001"]},"result":{"values":[]}} +{"call":{"function":"create_mock","inputs":[["0000000000000000000000000000000000000000000000000000000000000066","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000065","000000000000000000000000000000000000000000000000000000000000006c","0000000000000000000000000000000000000000000000000000000000000064","000000000000000000000000000000000000000000000000000000000000005f","0000000000000000000000000000000000000000000000000000000000000066","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000065","000000000000000000000000000000000000000000000000000000000000006c","0000000000000000000000000000000000000000000000000000000000000064"]]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000002"]}} +{"call":{"function":"set_mock_params","inputs":["0000000000000000000000000000000000000000000000000000000000000002","0000000000000000000000000000000000000000000000000000000000000002"]},"result":{"values":[]}} +{"call":{"function":"set_mock_returns","inputs":["0000000000000000000000000000000000000000000000000000000000000002","000000000000000000000000000000000000000000000000000000000000001e"]},"result":{"values":[]}} +{"call":{"function":"create_mock","inputs":[["0000000000000000000000000000000000000000000000000000000000000066","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000065","000000000000000000000000000000000000000000000000000000000000006c","0000000000000000000000000000000000000000000000000000000000000064","000000000000000000000000000000000000000000000000000000000000005f","0000000000000000000000000000000000000000000000000000000000000066","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000065","000000000000000000000000000000000000000000000000000000000000006c","0000000000000000000000000000000000000000000000000000000000000064"]]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000003"]}} +{"call":{"function":"set_mock_params","inputs":["0000000000000000000000000000000000000000000000000000000000000003","0000000000000000000000000000000000000000000000000000000000000003"]},"result":{"values":[]}} +{"call":{"function":"set_mock_returns","inputs":["0000000000000000000000000000000000000000000000000000000000000003","0000000000000000000000000000000000000000000000000000000000000028"]},"result":{"values":[]}} +{"call":{"function":"create_mock","inputs":[["0000000000000000000000000000000000000000000000000000000000000073","0000000000000000000000000000000000000000000000000000000000000074","0000000000000000000000000000000000000000000000000000000000000072","0000000000000000000000000000000000000000000000000000000000000075","0000000000000000000000000000000000000000000000000000000000000063","0000000000000000000000000000000000000000000000000000000000000074","000000000000000000000000000000000000000000000000000000000000005f","0000000000000000000000000000000000000000000000000000000000000066","0000000000000000000000000000000000000000000000000000000000000069","0000000000000000000000000000000000000000000000000000000000000065","000000000000000000000000000000000000000000000000000000000000006c","0000000000000000000000000000000000000000000000000000000000000064"]]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000004"]}} +{"call":{"function":"set_mock_params","inputs":["0000000000000000000000000000000000000000000000000000000000000004","000000000000000000000000000000000000000000000000000000000000012c","0000000000000000000000000000000000000000000000000000000000000320",["000000000000000000000000000000000000000000000000000000000000000a","0000000000000000000000000000000000000000000000000000000000000014","000000000000000000000000000000000000000000000000000000000000001e","0000000000000000000000000000000000000000000000000000000000000028"]]},"result":{"values":[]}} +{"call":{"function":"set_mock_returns","inputs":["0000000000000000000000000000000000000000000000000000000000000004","0000000000000000000000000000000000000000000000000000000000000064"]},"result":{"values":[]}} +{"call":{"function":"void_field","inputs":[]},"result":{"values":["000000000000000000000000000000000000000000000000000000000000000a"]}} +{"call":{"function":"void_field","inputs":[]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000014"]}} +{"call":{"function":"field_field","inputs":["0000000000000000000000000000000000000000000000000000000000000002"]},"result":{"values":["000000000000000000000000000000000000000000000000000000000000001e"]}} +{"call":{"function":"field_field","inputs":["0000000000000000000000000000000000000000000000000000000000000003"]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000028"]}} +{"call":{"function":"struct_field","inputs":["000000000000000000000000000000000000000000000000000000000000012c","0000000000000000000000000000000000000000000000000000000000000320",["000000000000000000000000000000000000000000000000000000000000000a","0000000000000000000000000000000000000000000000000000000000000014","000000000000000000000000000000000000000000000000000000000000001e","0000000000000000000000000000000000000000000000000000000000000028"]]},"result":{"values":["0000000000000000000000000000000000000000000000000000000000000064"]}} diff --git a/examples/oracle_transcript/Prover.toml b/examples/oracle_transcript/Prover.toml new file mode 100644 index 00000000000..eb8504c2b0c --- /dev/null +++ b/examples/oracle_transcript/Prover.toml @@ -0,0 +1,6 @@ + +[input] +x = 2 +y = 3 + +return = 100 diff --git a/examples/oracle_transcript/log_and_exec_transcript.sh b/examples/oracle_transcript/log_and_exec_transcript.sh new file mode 100755 index 00000000000..c6e5066f158 --- /dev/null +++ b/examples/oracle_transcript/log_and_exec_transcript.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -eu + +cd $(dirname $0) + +# Execute the test to capture oracle calls. +NARGO_TEST_FOREIGN_CALL_LOG=Oracle.test.jsonl nargo test + +# Get rid of the mock setup calls +cat Oracle.test.jsonl \ + | jq --slurp -r -c '.[] | select(.call.function | contains("mock") | not)' \ + > Oracle.jsonl + +# Execute `main` with the Prover.toml and Oracle.jsonl files. +nargo execute --skip-underconstrained-check --oracle-file Oracle.jsonl + +# Also execute through `noir-execute` +noir-execute \ + --artifact-path target/oracle_transcript.json \ + --oracle-file Oracle.jsonl \ + --prover-file Prover.toml \ + --output-dir target diff --git a/examples/oracle_transcript/src/main.nr b/examples/oracle_transcript/src/main.nr new file mode 100644 index 00000000000..585ff2af2b2 --- /dev/null +++ b/examples/oracle_transcript/src/main.nr @@ -0,0 +1,63 @@ +use std::test::OracleMock; + +struct Point { + x: Field, + y: Field, +} + +impl Eq for Point { + fn eq(self, other: Point) -> bool { + (self.x == other.x) & (self.y == other.y) + } +} + +#[oracle(void_field)] +unconstrained fn void_field_oracle() -> Field {} + +unconstrained fn void_field() -> Field { + void_field_oracle() +} + +#[oracle(field_field)] +unconstrained fn field_field_oracle(_x: Field) -> Field {} + +unconstrained fn field_field(x: Field) -> Field { + field_field_oracle(x) +} + +#[oracle(struct_field)] +unconstrained fn struct_field_oracle(_point: Point, _array: [Field; 4]) -> Field {} + +unconstrained fn struct_field(point: Point, array: [Field; 4]) -> Field { + struct_field_oracle(point, array) +} + +fn main(input: Point) -> pub Field { + // Safety: testing context + unsafe { + let a = void_field(); + let b = void_field(); + let c = field_field(input.x); + let d = field_field(input.y); + let p = Point { x: a * c, y: b * d }; + struct_field(p, [a, b, c, d]) + } +} + +/// This test is used to capture an oracle transcript, which can then be replayed +/// during execution. +#[test] +fn test_main() { + // Safety: testing context + unsafe { + let _ = OracleMock::mock("void_field").returns(10).times(1); + let _ = OracleMock::mock("void_field").returns(20).times(1); + let _ = OracleMock::mock("field_field").with_params((2,)).returns(30); + let _ = OracleMock::mock("field_field").with_params((3,)).returns(40); + let _ = OracleMock::mock("struct_field") + .with_params((Point { x: 300, y: 800 }, [10, 20, 30, 40])) + .returns(100); + } + let output = main(Point { x: 2, y: 3 }); + assert_eq(output, 100) +} diff --git a/examples/oracle_transcript/test.sh b/examples/oracle_transcript/test.sh new file mode 100755 index 00000000000..8f43c3b8bb9 --- /dev/null +++ b/examples/oracle_transcript/test.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -eu + +cd $(dirname $0) + +# This file is used for Noir CI and is not required. + +rm -f ./Oracle.* + +./log_and_exec_transcript.sh diff --git a/tooling/artifact_cli/src/commands/execute_cmd.rs b/tooling/artifact_cli/src/commands/execute_cmd.rs index df5d469e75b..6ce96628269 100644 --- a/tooling/artifact_cli/src/commands/execute_cmd.rs +++ b/tooling/artifact_cli/src/commands/execute_cmd.rs @@ -8,7 +8,10 @@ use crate::{ errors::CliError, execution::{self, ExecutionResults}, }; -use nargo::{PrintOutput, foreign_calls::DefaultForeignCallBuilder}; +use nargo::{ + PrintOutput, + foreign_calls::{DefaultForeignCallBuilder, layers, transcript::ReplayForeignCallExecutor}, +}; use noirc_driver::CompiledProgram; use super::parse_and_normalize_path; @@ -40,10 +43,27 @@ pub struct ExecuteCommand { #[clap(long)] pub contract_fn: Option, + /// Path to the oracle transcript that is to be replayed during the + /// execution in response to foreign calls. The format is expected + /// to be JSON Lines, with each request/response on a separate line. + /// + /// Note that a transcript might be invalid if the inputs change and + /// the circuit takes a different path during execution. + #[clap(long, conflicts_with = "oracle_resolver")] + pub oracle_file: Option, + /// JSON RPC url to solve oracle calls. - #[clap(long)] + #[clap(long, conflicts_with = "oracle_file")] pub oracle_resolver: Option, + /// Root directory for the RPC oracle resolver. + #[clap(long, value_parser = parse_and_normalize_path)] + pub oracle_root_dir: Option, + + /// Package name for the RPC oracle resolver + #[clap(long)] + pub oracle_package_name: Option, + /// Use pedantic ACVM solving, i.e. double-check some black-box function assumptions when solving. #[clap(long, default_value_t = false)] pub pedantic_solving: bool, @@ -92,10 +112,15 @@ pub fn run(args: ExecuteCommand) -> Result<(), CliError> { /// Execute a circuit and return the output witnesses. fn execute(circuit: &CompiledProgram, args: &ExecuteCommand) -> Result { - // TODO: Build a custom foreign call executor that reads from the Oracle transcript, + // Build a custom foreign call executor that replays the Oracle transcript, // 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 transcript_executor = match args.oracle_file { + Some(ref path) => layers::Either::Left(ReplayForeignCallExecutor::from_file(path)?), + None => layers::Either::Right(layers::Empty), + }; + let mut foreign_call_executor = DefaultForeignCallBuilder { output: PrintOutput::Stdout, enable_mocks: false, @@ -103,7 +128,7 @@ fn execute(circuit: &CompiledProgram, args: &ExecuteCommand) -> Result { String(&'a mut String), } +/// Handle `println` calls. #[derive(Debug, Default)] pub struct PrintForeignCallExecutor<'a> { output: PrintOutput<'a>, diff --git a/tooling/nargo/src/foreign_calls/transcript.rs b/tooling/nargo/src/foreign_calls/transcript.rs new file mode 100644 index 00000000000..155835c3fc1 --- /dev/null +++ b/tooling/nargo/src/foreign_calls/transcript.rs @@ -0,0 +1,156 @@ +use std::{ + collections::VecDeque, + path::{Path, PathBuf}, +}; + +use acvm::{AcirField, acir::brillig::ForeignCallResult, pwg::ForeignCallWaitInfo}; +use serde::{Deserialize, Serialize}; +use serde_json::json; + +use crate::PrintOutput; + +use super::{ForeignCallError, ForeignCallExecutor}; + +#[derive(Debug, thiserror::Error)] +pub enum TranscriptError { + #[error(transparent)] + IoError(#[from] std::io::Error), + + #[error(transparent)] + DeserializationError(#[from] serde_json::Error), +} + +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +struct LogItem { + call: ForeignCallWaitInfo, + result: ForeignCallResult, +} + +/// Log foreign calls during the execution, for testing purposes. +pub struct LoggingForeignCallExecutor<'a, E> { + pub executor: E, + pub output: PrintOutput<'a>, +} + +impl<'a, E> LoggingForeignCallExecutor<'a, E> { + pub fn new(executor: E, output: PrintOutput<'a>) -> Self { + Self { executor, output } + } +} + +impl ForeignCallExecutor for LoggingForeignCallExecutor<'_, E> +where + F: AcirField + Serialize, + E: ForeignCallExecutor, +{ + fn execute( + &mut self, + foreign_call: &ForeignCallWaitInfo, + ) -> Result, ForeignCallError> { + let result = self.executor.execute(foreign_call); + if let Ok(ref result) = result { + let log_item = || { + // Match the JSON structure of `LogItem` without having to clone. + let json = json!({"call": foreign_call, "result": result}); + serde_json::to_string(&json).expect("failed to serialize foreign call") + }; + match &mut self.output { + PrintOutput::None => (), + PrintOutput::Stdout => println!("{}", log_item()), + PrintOutput::String(s) => { + s.push_str(&log_item()); + s.push('\n'); + } + } + } + result + } +} + +/// Log foreign calls to stdout as soon as they are made, or buffer them and write to a file at the end. +pub enum ForeignCallLog { + None, + Stdout, + File(PathBuf, String), +} + +impl ForeignCallLog { + /// Instantiate based on an env var. + pub fn from_env(key: &str) -> Self { + match std::env::var(key) { + Err(_) => Self::None, + Ok(s) if s == "stdout" => Self::Stdout, + Ok(s) => Self::File(PathBuf::from(s), String::new()), + } + } + + /// Create a [PrintOutput] based on the log setting. + pub fn print_output(&mut self) -> PrintOutput { + match self { + ForeignCallLog::None => PrintOutput::None, + ForeignCallLog::Stdout => PrintOutput::Stdout, + ForeignCallLog::File(_, s) => PrintOutput::String(s), + } + } + + /// Any final logging. + pub fn write_log(self) -> std::io::Result<()> { + if let ForeignCallLog::File(path, contents) = self { + std::fs::write(path, contents)?; + } + Ok(()) + } +} + +/// Replay an oracle transcript which was logged with [LoggingForeignCallExecutor]. +/// +/// This is expected to be the last executor in the stack, e.g. prints can be handled above it. +pub struct ReplayForeignCallExecutor { + transcript: VecDeque>, +} + +impl Deserialize<'a>> ReplayForeignCallExecutor { + pub fn from_file(path: &Path) -> Result { + let contents = std::fs::read_to_string(path)?; + + let transcript = + contents.lines().map(serde_json::from_str).collect::, _>>()?; + + Ok(Self { transcript }) + } +} + +impl ForeignCallExecutor for ReplayForeignCallExecutor +where + F: AcirField, +{ + fn execute( + &mut self, + foreign_call: &ForeignCallWaitInfo, + ) -> Result, ForeignCallError> { + let error = |msg| Err(ForeignCallError::TranscriptError(msg)); + // Verify without popping. + if let Some(next) = self.transcript.front() { + if next.call.function != foreign_call.function { + let msg = format!( + "unexpected foreign call; expected '{}', got '{}'", + next.call.function, foreign_call.function + ); + return error(msg); + } + if next.call.inputs != foreign_call.inputs { + let msg = format!( + "unexpected foreign call inputs to '{}'; expected {:?}, got {:?}", + next.call.function, next.call.inputs, foreign_call.inputs + ); + return error(msg); + } + } + // Consume the next call. + if let Some(next) = self.transcript.pop_front() { + Ok(next.result) + } else { + error("unexpected foreign call; no more calls in transcript".to_string()) + } + } +} diff --git a/tooling/nargo/src/ops/test.rs b/tooling/nargo/src/ops/test.rs index 4f0e63564e2..c4adaa5cfaa 100644 --- a/tooling/nargo/src/ops/test.rs +++ b/tooling/nargo/src/ops/test.rs @@ -14,7 +14,11 @@ use noirc_frontend::hir::{Context, def_map::TestFunction}; use crate::{ NargoError, errors::try_to_diagnose_runtime_error, - foreign_calls::{ForeignCallError, ForeignCallExecutor, layers, print::PrintOutput}, + foreign_calls::{ + ForeignCallError, ForeignCallExecutor, layers, + print::PrintOutput, + transcript::{ForeignCallLog, LoggingForeignCallExecutor}, + }, }; use super::execute_program; @@ -60,11 +64,21 @@ where let compiled_program = crate::ops::transform_program(compiled_program, target_width); if test_function_has_no_arguments { + let ignore_foreign_call_failures = + std::env::var("NARGO_IGNORE_TEST_FAILURES_FROM_FOREIGN_CALLS") + .is_ok_and(|var| &var == "true"); + + let mut foreign_call_log = ForeignCallLog::from_env("NARGO_TEST_FOREIGN_CALL_LOG"); + // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, // otherwise constraints involving these expressions will not error. // Use a base layer that doesn't handle anything, which we handle in the `execute` below. - let inner_executor = build_foreign_call_executor(output, layers::Unhandled); - let mut foreign_call_executor = TestForeignCallExecutor::new(inner_executor); + let foreign_call_executor = build_foreign_call_executor(output, layers::Unhandled); + let foreign_call_executor = TestForeignCallExecutor::new(foreign_call_executor); + let mut foreign_call_executor = LoggingForeignCallExecutor::new( + foreign_call_executor, + foreign_call_log.print_output(), + ); let circuit_execution = execute_program( &compiled_program.program, @@ -80,9 +94,8 @@ where &circuit_execution, ); - let ignore_foreign_call_failures = - std::env::var("NARGO_IGNORE_TEST_FAILURES_FROM_FOREIGN_CALLS") - .is_ok_and(|var| &var == "true"); + let foreign_call_executor = foreign_call_executor.executor; + foreign_call_log.write_log().expect("failed to write foreign call log"); if let TestStatus::Fail { .. } = status { if ignore_foreign_call_failures diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index 07a7c1195af..7ba95a16eeb 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use clap::Args; use nargo::constants::PROVER_INPUT_FILE; @@ -29,8 +31,12 @@ pub(crate) struct ExecuteCommand { compile_options: CompileOptions, /// JSON RPC url to solve oracle calls - #[clap(long)] + #[clap(long, conflicts_with = "oracle_file")] oracle_resolver: Option, + + /// Path to the oracle transcript. + #[clap(long, conflicts_with = "oracle_resolver")] + oracle_file: Option, } impl WorkspaceCommand for ExecuteCommand { @@ -61,7 +67,10 @@ pub(crate) fn run(args: ExecuteCommand, workspace: Workspace) -> Result<(), CliE args.witness_name.clone().unwrap_or_else(|| package.name.to_string()), ), contract_fn: None, + oracle_file: args.oracle_file.clone(), oracle_resolver: args.oracle_resolver.clone(), + oracle_root_dir: Some(workspace.root_dir.clone()), + oracle_package_name: Some(package.name.to_string()), pedantic_solving: args.compile_options.pedantic_solving, }; diff --git a/tooling/nargo_cli/src/main.rs b/tooling/nargo_cli/src/main.rs index f4cc74447bc..63b0a43cbe6 100644 --- a/tooling/nargo_cli/src/main.rs +++ b/tooling/nargo_cli/src/main.rs @@ -17,6 +17,9 @@ use color_eyre::config::HookBuilder; use tracing_appender::rolling; use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan}; +// TODO: Currently only used by benches. +use noir_artifact_cli as _; + const PANIC_MESSAGE: &str = "This is a bug. We may have already fixed this in newer versions of Nargo so try searching for similar issues at https://github.com/noir-lang/noir/issues/.\nIf there isn't an open issue for this bug, consider opening one at https://github.com/noir-lang/noir/issues/new?labels=bug&template=bug_report.yml"; fn main() { diff --git a/tooling/profiler/src/cli/mod.rs b/tooling/profiler/src/cli/mod.rs index b91dd6990aa..0b4e0a92b27 100644 --- a/tooling/profiler/src/cli/mod.rs +++ b/tooling/profiler/src/cli/mod.rs @@ -1,5 +1,5 @@ use clap::{Parser, Subcommand}; -use color_eyre::eyre; +use color_eyre::eyre::{self}; use const_format::formatcp; mod execution_flamegraph_cmd;