Skip to content

Commit

Permalink
feat(cli): Verify return against ABI and Prover.toml (noir-lang#6765
Browse files Browse the repository at this point in the history
)
  • Loading branch information
aakoshh authored Dec 12, 2024
1 parent bd46bdd commit 5795a09
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 9 deletions.
2 changes: 1 addition & 1 deletion test_programs/execution_success/diamond_deps_0/Prover.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
x = 1
y = 1
return = 5
return = 7
7 changes: 7 additions & 0 deletions test_programs/execution_success/return_twice/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "return_twice"
version = "0.1.0"
type = "bin"
authors = [""]

[dependencies]
2 changes: 2 additions & 0 deletions test_programs/execution_success/return_twice/Prover.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
return = [100, 100]
in0 = 10
5 changes: 5 additions & 0 deletions test_programs/execution_success/return_twice/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub fn main(in0: Field) -> pub (Field, Field) {
let out0 = (in0 * in0);
let out1 = (in0 * in0);
(out0, out1)
}
34 changes: 27 additions & 7 deletions tooling/nargo_cli/src/cli/execute_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ pub(crate) fn run(args: ExecuteCommand, config: NargoConfig) -> Result<(), CliEr
let program_artifact_path = workspace.package_build_path(package);
let program: CompiledProgram =
read_program_from_file(program_artifact_path.clone())?.into();
let abi = program.abi.clone();

let (return_value, witness_stack) = execute_program_and_decode(
let results = execute_program_and_decode(
program,
package,
&args.prover_name,
Expand All @@ -75,14 +76,27 @@ pub(crate) fn run(args: ExecuteCommand, config: NargoConfig) -> Result<(), CliEr
)?;

println!("[{}] Circuit witness successfully solved", package.name);
if let Some(return_value) = return_value {
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(witness_stack, witness_name, target_dir)?;
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() });
}
}
}
Ok(())
}
Expand All @@ -94,18 +108,24 @@ fn execute_program_and_decode(
foreign_call_resolver_url: Option<&str>,
root_path: Option<PathBuf>,
package_name: Option<String>,
) -> Result<(Option<InputValue>, WitnessStack<FieldElement>), CliError> {
) -> Result<ExecutionResults, CliError> {
// Parse the initial witness values from Prover.toml
let (inputs_map, _) =
let (inputs_map, expected_return) =
read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &program.abi)?;
let witness_stack =
execute_program(&program, &inputs_map, foreign_call_resolver_url, root_path, package_name)?;
// 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 (_, return_value) = program.abi.decode(main_witness)?;
let (_, actual_return) = program.abi.decode(main_witness)?;

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

Ok((return_value, witness_stack))
struct ExecutionResults {
expected_return: Option<InputValue>,
actual_return: Option<InputValue>,
witness_stack: WitnessStack<FieldElement>,
}

pub(crate) fn execute_program(
Expand Down
13 changes: 12 additions & 1 deletion tooling/nargo_cli/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ use acvm::{acir::native_types::WitnessStackError, FieldElement};
use nargo::{errors::CompileError, NargoError};
use nargo_toml::ManifestError;
use noir_debugger::errors::DapError;
use noirc_abi::errors::{AbiError, InputParserError};
use noirc_abi::{
errors::{AbiError, InputParserError},
input_parser::InputValue,
AbiReturnType,
};
use std::path::PathBuf;
use thiserror::Error;

Expand Down Expand Up @@ -32,6 +36,7 @@ pub(crate) enum FilesystemError {
pub(crate) enum CliError {
#[error("{0}")]
Generic(String),

#[error("Error: destination {} already exists", .0.display())]
DestinationAlreadyExists(PathBuf),

Expand Down Expand Up @@ -63,4 +68,10 @@ 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<InputValue> },

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

0 comments on commit 5795a09

Please sign in to comment.