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

feat(cli): Verify return against ABI and Prover.toml #6765

Merged
merged 11 commits into from
Dec 12, 2024
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
@@ -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,
@@ -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(())
}
@@ -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(
13 changes: 12 additions & 1 deletion tooling/nargo_cli/src/errors.rs
Original file line number Diff line number Diff line change
@@ -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;

@@ -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),

@@ -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 },
}
Loading