Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(cli): Forward nargo execute to noir_artifact_cli #7406

Merged
merged 70 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
fd4ab05
Add artifact_cli
aakoshh Feb 12, 2025
1fb81c6
Turn artifact_cli into a library/binary combo
aakoshh Feb 13, 2025
9558618
Port fs from acvm_cli
aakoshh Feb 13, 2025
aac1b0f
Remove fs from acvm_cli
aakoshh Feb 13, 2025
10fbcb0
Parse inputs
aakoshh Feb 14, 2025
a568cce
Execute the circuit
aakoshh Feb 14, 2025
88ce449
Show diagnostic
aakoshh Feb 14, 2025
65ed320
Save the witness
aakoshh Feb 14, 2025
aea18c3
Add command docs
aakoshh Feb 14, 2025
7878c25
Merge branch 'master' into 7132-artifact-cli
aakoshh Feb 14, 2025
236e5f1
Reset indent size in Cargo.toml
aakoshh Feb 14, 2025
d36f2b9
Merge branch '7132-artifact-cli' of github.com:noir-lang/noir into 71…
aakoshh Feb 14, 2025
8bf7e61
Normalize paths
aakoshh Feb 14, 2025
ef090a4
Use Artifact in inspector print-acir
aakoshh Feb 14, 2025
7ceee2a
Filter by name in print-acir
aakoshh Feb 14, 2025
5f7c87d
Make inspector info work on Artifact
aakoshh Feb 14, 2025
98850a4
Use PathBuf in profiler
aakoshh Feb 14, 2025
2066359
Update profiler to use noir_artifact_cli::fs
aakoshh Feb 14, 2025
7d820ca
Update profiler to use noir_artifact_cli::fs
aakoshh Feb 14, 2025
1ae7d45
Use artifact_cli::fs in criterion benchmarks
aakoshh Feb 14, 2025
adc71a3
Use artifact_cli::fs in nargo
aakoshh Feb 14, 2025
ce6b763
Remove fs from nargo
aakoshh Feb 14, 2025
642b9b3
Merge branch '7381-use-artifact-fs' of github.com:noir-lang/noir into…
aakoshh Feb 14, 2025
cedb107
Return error if bincode serialization fails
aakoshh Feb 17, 2025
7317a63
Fully qualify call to exec
aakoshh Feb 17, 2025
8c707c2
Use CompiledProgram instead of Circuit
aakoshh Feb 17, 2025
ac3b5ea
Move execution methods into their own module
aakoshh Feb 17, 2025
e19c4b7
Call artifact_cli::execution from nargo
aakoshh Feb 17, 2025
c1ad32b
Use as_deref
aakoshh Feb 17, 2025
66bcf73
Create and run the ExecuteCommand as-is
aakoshh Feb 17, 2025
4ec10a9
Merge branch 'master' into 7132-artifact-cli
aakoshh Feb 17, 2025
6c00259
Merge branch '7132-artifact-cli' into 7381-use-artifact-fs
aakoshh Feb 17, 2025
0615070
Merge branch '7381-use-artifact-fs' into 7380-nargo-exec-fwd
aakoshh Feb 17, 2025
47c7ade
Move ACVM CLI witness functions back
aakoshh Feb 17, 2025
51b1bd7
Remove --oracle-file for now
aakoshh Feb 17, 2025
cff69fa
Add Format::from_ext
aakoshh Feb 17, 2025
98cfd77
Try to make read_inputs_from_file easier to read
aakoshh Feb 17, 2025
c18bb19
Rename to artifact_path
aakoshh Feb 17, 2025
9c2c3d4
Rename binary to noir-executor with no subcommand
aakoshh Feb 17, 2025
5f89660
.
TomAFrench Feb 18, 2025
85bdd85
.
TomAFrench Feb 18, 2025
817df4a
.
TomAFrench Feb 18, 2025
51f2e2d
Switch to bin/execute.rs
aakoshh Feb 18, 2025
780cde3
Merge branch '7132-artifact-cli' of github.com:noir-lang/noir into 71…
aakoshh Feb 18, 2025
8a30ce5
Merge branch 'master' into 7132-artifact-cli
aakoshh Feb 18, 2025
3429365
Merge branch '7132-artifact-cli' into 7381-use-artifact-fs
aakoshh Feb 18, 2025
4502655
Merge branch '7381-use-artifact-fs' into 7380-nargo-exec-fwd
aakoshh Feb 18, 2025
c5d993a
Comment about ordering
aakoshh Feb 18, 2025
48556ac
Merge branch 'master' into 7132-artifact-cli
aakoshh Feb 18, 2025
17bed50
Merge remote-tracking branch 'origin/7132-artifact-cli' into 7381-use…
aakoshh Feb 18, 2025
416b563
Merge remote-tracking branch 'origin/7381-use-artifact-fs' into 7380-…
aakoshh Feb 18, 2025
ba3d6d6
Add `hash` to contract function artifacts
aakoshh Feb 19, 2025
0196e1a
Just use default empty names, the info command won't work on such con…
aakoshh Feb 19, 2025
40dc505
Merge branch '7381-use-artifact-fs' into 7380-nargo-exec-fwd
aakoshh Feb 19, 2025
14fa101
Add ContractFunctionArtifact::into_compiled_program
aakoshh Feb 19, 2025
6c519d0
Add hash? to NoirFunctionEntry
aakoshh Feb 19, 2025
f5592bc
Use string to serialize hash
aakoshh Feb 19, 2025
537b1d9
Fix the Wasm compiler to use deterministic file IDs
aakoshh Feb 19, 2025
c6ed3e0
Merge branch '7381-use-artifact-fs' into 7380-nargo-exec-fwd
aakoshh Feb 19, 2025
2b2e99c
Delete hash for contracts before comparison
aakoshh Feb 19, 2025
49ed538
Backwards compatible parsing of hash in noirc_artifacts
aakoshh Feb 19, 2025
4d61e75
Comments about why the hash doesn't match when there are dependencies
aakoshh Feb 19, 2025
9f5e8d6
Merge remote-tracking branch 'origin/master' into 7381-use-artifact-fs
aakoshh Feb 19, 2025
7f44036
Merge branch '7381-use-artifact-fs' into 7380-nargo-exec-fwd
aakoshh Feb 19, 2025
330a07e
Merge remote-tracking branch 'origin/master' into 7380-nargo-exec-fwd
aakoshh Feb 26, 2025
7a8cb30
Merge branch 'master' into 7380-nargo-exec-fwd
TomAFrench Feb 27, 2025
cefcd25
chore: remove unused args
TomAFrench Feb 28, 2025
3d0a6d7
.
TomAFrench Feb 28, 2025
5fe2c77
Remove TODO
aakoshh Feb 28, 2025
7c7f931
Merge branch 'master' into 7380-nargo-exec-fwd
aakoshh Feb 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions acvm-repo/acir/src/native_types/witness_stack.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::io::Read;

use flate2::Compression;
use flate2::bufread::GzDecoder;

Check warning on line 4 in acvm-repo/acir/src/native_types/witness_stack.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (bufread)
use flate2::bufread::GzEncoder;

Check warning on line 5 in acvm-repo/acir/src/native_types/witness_stack.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (bufread)
use serde::{Deserialize, Serialize};
use thiserror::Error;

Expand All @@ -12,6 +12,9 @@
enum SerializationError {
#[error(transparent)]
Deflate(#[from] std::io::Error),

#[error(transparent)]
BincodeError(#[from] bincode::Error),
}

#[derive(Debug, Error)]
Expand Down Expand Up @@ -57,26 +60,35 @@
}
}

impl<F: Serialize> TryFrom<WitnessStack<F>> for Vec<u8> {
impl<F: Serialize> TryFrom<&WitnessStack<F>> for Vec<u8> {
type Error = WitnessStackError;

fn try_from(val: WitnessStack<F>) -> Result<Self, Self::Error> {
let buf = bincode::serialize(&val).unwrap();
fn try_from(val: &WitnessStack<F>) -> Result<Self, Self::Error> {
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()))?;
Ok(buf_c)
}
}

impl<F: Serialize> TryFrom<WitnessStack<F>> for Vec<u8> {
type Error = WitnessStackError;

fn try_from(val: WitnessStack<F>) -> Result<Self, Self::Error> {
Self::try_from(&val)
}
}

impl<F: for<'a> Deserialize<'a>> TryFrom<&[u8]> for WitnessStack<F> {
type Error = WitnessStackError;

fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
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)
}
}
2 changes: 1 addition & 1 deletion compiler/wasm/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ pub(crate) struct DependencyGraph {
pub(crate) root_dependencies: Vec<CrateName>,
pub(crate) library_dependencies: BTreeMap<CrateName, Vec<CrateName>>,
}
/// 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
Expand Down
7 changes: 4 additions & 3 deletions tooling/acvm_cli/src/cli/execute_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -56,7 +56,7 @@ fn run_command(args: ExecuteCommand) -> Result<String, CliError> {
)?;
if args.output_witness.is_some() {
save_witness_to_dir(
output_witness,
&output_witness,
&args.output_witness.unwrap(),
&args.working_directory,
)?;
Expand All @@ -80,7 +80,8 @@ pub(crate) fn execute_program_from_witness(
) -> Result<WitnessStack<FieldElement>, CliError> {
let program: Program<FieldElement> =
Program::deserialize_program(bytecode).map_err(CliError::CircuitDeserializationError)?;
execute_program(

nargo::ops::execute_program(
&program,
inputs_map,
&Bn254BlackBoxSolver(pedantic_solving),
Expand Down
176 changes: 39 additions & 137 deletions tooling/artifact_cli/src/commands/execute_cmd.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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<PathBuf>,
pub output_dir: Option<PathBuf>,

/// Write the execution witness to named file
///
/// Defaults to the name of the circuit being executed.
#[clap(long, short)]
witness_name: Option<String>,
pub witness_name: Option<String>,

/// Name of the function to execute, if the artifact is a contract.
#[clap(long)]
contract_fn: Option<String>,
pub contract_fn: Option<String>,

/// JSON RPC url to solve oracle calls.
#[clap(long)]
oracle_resolver: Option<String>,
pub oracle_resolver: Option<String>,

/// 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::<Vec<_>>().join(",");
let names = || contract.functions.iter().map(|f| f.name.clone()).collect::<Vec<_>>();

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<String>,
abi: Abi,
bytecode: Program<FieldElement>,
debug_symbols: noirc_errors::debug_info::ProgramDebugInfo,
file_map: BTreeMap<fm::FileId, noirc_driver::DebugFile>,
}

struct SolvedWitnesses {
expected_return: Option<InputValue>,
actual_return: Option<InputValue>,
witness_stack: WitnessStack<FieldElement>,
}

/// Execute a circuit and return the output witnesses.
fn execute(circuit: &Circuit, args: &ExecuteCommand) -> Result<SolvedWitnesses, CliError> {
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<ExecutionResults, CliError> {
// 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,
Expand All @@ -130,80 +105,7 @@ fn execute(circuit: &Circuit, args: &ExecuteCommand) -> Result<SolvedWitnesses,
}
.build();

let witness_stack = nargo::ops::execute_program(
&circuit.bytecode,
initial_witness,
&Bn254BlackBoxSolver(args.pedantic_solving),
&mut 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)?;
let blackbox_solver = Bn254BlackBoxSolver(args.pedantic_solving);

Ok(SolvedWitnesses { expected_return, actual_return, witness_stack })
}

/// Print an error stack trace, if possible.
fn show_diagnostic(circuit: Circuit, err: NargoError<FieldElement>) {
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)
}
1 change: 1 addition & 0 deletions tooling/artifact_cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
18 changes: 17 additions & 1 deletion tooling/artifact_cli/src/errors.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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<InputValue> },

#[error("Missing return witnesses; expected {expected:?}")]
MissingReturn { expected: AbiReturnType },

#[error("Missing contract function name; options: {names:?}")]
MissingContractFn { names: Vec<String> },

#[error("Unknown contract function '{name}'; options: {names:?}")]
UnknownContractFn { name: String, names: Vec<String> },
}
Loading
Loading