diff --git a/crates/nargo/src/cli/prove_cmd.rs b/crates/nargo/src/cli/prove_cmd.rs index ade17917122..605cc97ba5f 100644 --- a/crates/nargo/src/cli/prove_cmd.rs +++ b/crates/nargo/src/cli/prove_cmd.rs @@ -5,12 +5,15 @@ use acvm::FieldElement; use acvm::ProofSystemCompiler; use acvm::{GateResolution, PartialWitnessGenerator}; use clap::ArgMatches; +use noirc_abi::AbiType; use noirc_abi::{input_parser::InputValue, Abi}; use std::path::Path; use crate::{errors::CliError, resolver::Resolver}; -use super::{create_named_dir, write_to_file, PROOFS_DIR, PROOF_EXT, PROVER_INPUT_FILE}; +use super::{ + create_named_dir, write_to_file, PROOFS_DIR, PROOF_EXT, PROVER_INPUT_FILE, VERIFIER_INPUT_FILE, +}; pub(crate) fn run(args: ArgMatches) -> Result<(), CliError> { let args = args.subcommand_matches("prove").unwrap(); @@ -38,12 +41,22 @@ fn prove(proof_name: &str, show_ssa: bool) -> Result<(), CliError> { /// We then need the witness map to get the elements field values. fn process_abi_with_input( abi: Abi, - witness_map: BTreeMap, -) -> Result, CliError> { + witness_map: &BTreeMap, +) -> Result<(BTreeMap, Option), CliError> { let mut solved_witness = BTreeMap::new(); let mut index = 0; - + let mut return_witness = None; + let return_witness_len = if let Some(return_param) = + abi.parameters.iter().find(|x| x.0 == noirc_frontend::hir_def::function::MAIN_RETURN_NAME) + { + match &return_param.1 { + AbiType::Array { length, .. } => *length as u32, + AbiType::Integer { .. } | AbiType::Field(_) => 1, + } + } else { + 0 + }; for (param_name, param_type) in abi.parameters.into_iter() { let value = witness_map .get(¶m_name) @@ -71,9 +84,28 @@ fn process_abi_with_input( index += 1; } } + InputValue::Undefined => { + assert_eq!( + param_name, + noirc_frontend::hir_def::function::MAIN_RETURN_NAME, + "input value {} is not defined", + param_name + ); + return_witness = Some(Witness::new(index + WITNESS_OFFSET)); + + //We do not support undefined arrays for now - TODO + if return_witness_len != 1 { + return Err(CliError::Generic( + "Values of array returned from main must be specified in prover toml file" + .to_string(), + )); + } + index += return_witness_len; + //XXX We do not support (yet) array of arrays + } } } - Ok(solved_witness) + Ok((solved_witness, return_witness)) } pub fn compile_circuit_and_witness>( @@ -86,7 +118,7 @@ pub fn compile_circuit_and_witness>( // Parse the initial witness values let witness_map = noirc_abi::input_parser::Format::Toml - .parse(program_dir, PROVER_INPUT_FILE) + .parse(&program_dir, PROVER_INPUT_FILE) .map_err(CliError::from)?; // Check that enough witness values were supplied @@ -98,11 +130,14 @@ pub fn compile_circuit_and_witness>( witness_map.len() ) } - + // Map initial witnesses with their values let abi = compiled_program.abi.as_ref().unwrap(); - let mut solved_witness = process_abi_with_input(abi.clone(), witness_map)?; - + // Solve the remaining witnesses + let (mut solved_witness, rv) = process_abi_with_input(abi.clone(), &witness_map)?; let solver_res = backend.solve(&mut solved_witness, compiled_program.circuit.gates.clone()); + // (over)writes verifier.toml + export_public_inputs(rv, &solved_witness, &witness_map, abi, &program_dir) + .map_err(CliError::from)?; match solver_res { GateResolution::UnsupportedOpcode(opcode) => return Err(CliError::Generic(format!( @@ -119,6 +154,42 @@ pub fn compile_circuit_and_witness>( Ok((compiled_program, solved_witness)) } +fn export_public_inputs>( + w_ret: Option, + solved_witness: &BTreeMap, + witness_map: &BTreeMap, + abi: &Abi, + path: P, +) -> Result<(), noirc_abi::errors::InputParserError> { + // generate a name->value map for the public inputs, using the ABI and witness_map: + let mut public_inputs = BTreeMap::new(); + public_inputs + .insert(super::verify_cmd::RESERVED_PUBLIC_ARR.into(), InputValue::Vec(Vec::new())); //the dummy setpub array + for i in &abi.parameters { + if i.1.is_public() { + let v = &witness_map[&i.0]; + + let iv = if matches!(*v, InputValue::Undefined) { + let w_ret = w_ret.unwrap(); + match &i.1 { + AbiType::Array { length, .. } => { + let return_values = noirc_frontend::util::vecmap(0..*length, |i| { + *solved_witness.get(&Witness::new(w_ret.0 + i as u32)).unwrap() + }); + InputValue::Vec(return_values) + } + _ => InputValue::Field(*solved_witness.get(&w_ret).unwrap()), + } + } else { + v.clone() + }; + public_inputs.insert(i.0.clone(), iv); + } + } + //serialise public inputs into verifier.toml + noirc_abi::input_parser::Format::Toml.serialise(&path, VERIFIER_INPUT_FILE, &public_inputs) +} + pub fn prove_with_path>( proof_name: &str, program_dir: P, diff --git a/crates/nargo/src/cli/verify_cmd.rs b/crates/nargo/src/cli/verify_cmd.rs index b4bef4797f2..d6966745056 100644 --- a/crates/nargo/src/cli/verify_cmd.rs +++ b/crates/nargo/src/cli/verify_cmd.rs @@ -55,6 +55,12 @@ fn process_abi_with_verifier_input( match value { InputValue::Field(elem) => public_inputs.push(elem), InputValue::Vec(vec_elem) => public_inputs.extend(vec_elem), + InputValue::Undefined => { + return Err(CliError::Generic(format!( + "The parameter {} is not defined in the {}.toml file.", + param_name, VERIFIER_INPUT_FILE + ))) + } } } diff --git a/crates/nargo/tests/test_data/main_return/Prover.toml b/crates/nargo/tests/test_data/main_return/Prover.toml index d3fe594b452..ef1ca7bb7e8 100644 --- a/crates/nargo/tests/test_data/main_return/Prover.toml +++ b/crates/nargo/tests/test_data/main_return/Prover.toml @@ -1,2 +1,2 @@ -return = "8" +return = "" x = "8" diff --git a/crates/noirc_abi/src/errors.rs b/crates/noirc_abi/src/errors.rs index 8148f02bd3e..49a517c0052 100644 --- a/crates/noirc_abi/src/errors.rs +++ b/crates/noirc_abi/src/errors.rs @@ -4,6 +4,7 @@ use std::path::PathBuf; pub enum InputParserError { MissingTomlFile(PathBuf), ParseTomlMap(String), + SaveTomlFile(std::io::Error), ParseStr(String), ParseHexStr(String), DuplicateVariableName(String), @@ -14,6 +15,7 @@ impl std::fmt::Display for InputParserError { match self { InputParserError::MissingTomlFile(path) => write!(f, "cannot find input file located at {:?}, run nargo build to generate the missing Prover and/or Verifier toml files", path), InputParserError::ParseTomlMap(err_msg) => write!(f, "input.toml file is badly formed, could not parse, {}", err_msg), + InputParserError::SaveTomlFile(err) => write!(f, "could not save file to disk, {}", err), InputParserError::ParseStr(err_msg) => write!(f, "Expected witness values to be integers, provided value causes `{}` error", err_msg), InputParserError::ParseHexStr(err_msg) => write!(f, "Could not parse hex value {}", err_msg), InputParserError::DuplicateVariableName(err_msg) => write!(f, "duplicate variable name {}", err_msg) diff --git a/crates/noirc_abi/src/input_parser/mod.rs b/crates/noirc_abi/src/input_parser/mod.rs index 1217214d9c9..928c71051df 100644 --- a/crates/noirc_abi/src/input_parser/mod.rs +++ b/crates/noirc_abi/src/input_parser/mod.rs @@ -3,16 +3,18 @@ mod toml; use std::{collections::BTreeMap, path::Path}; use acvm::FieldElement; +use serde::Serialize; use crate::errors::InputParserError; use crate::AbiType; /// This is what all formats eventually transform into /// For example, a toml file will parse into TomlTypes /// and those TomlTypes will be mapped to Value -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] pub enum InputValue { Field(FieldElement), Vec(Vec), + Undefined, } impl InputValue { @@ -26,6 +28,7 @@ impl InputValue { (InputValue::Vec(_), AbiType::Field(_)) => false, (InputValue::Vec(x), AbiType::Array { length, .. }) => x.len() == length as usize, (InputValue::Vec(_), AbiType::Integer { .. }) => false, + (InputValue::Undefined, _) => true, } } } @@ -65,4 +68,21 @@ impl Format { } } } + + pub fn serialise>( + &self, + path: P, + file_name: &str, + w_map: &BTreeMap, + ) -> Result<(), InputParserError> { + match self { + Format::Toml => { + let mut dir_path = path.as_ref().to_path_buf(); + dir_path.push(file_name); + dir_path.set_extension(self.ext()); + toml::serialise(dir_path, w_map)?; + } + } + Ok(()) + } } diff --git a/crates/noirc_abi/src/input_parser/toml.rs b/crates/noirc_abi/src/input_parser/toml.rs index f2c8a4391bf..530d0d6920c 100644 --- a/crates/noirc_abi/src/input_parser/toml.rs +++ b/crates/noirc_abi/src/input_parser/toml.rs @@ -1,6 +1,7 @@ use super::InputValue; use crate::errors::InputParserError; use acvm::FieldElement; +use serde::Serialize; use serde_derive::Deserialize; use std::{collections::BTreeMap, path::Path}; @@ -20,39 +21,70 @@ pub(crate) fn parse>( toml_map_to_field(data) } +pub fn serialise>( + path_to_toml: P, + w_map: &BTreeMap, +) -> Result<(), InputParserError> { + let path_to_toml = path_to_toml.as_ref(); + if !path_to_toml.exists() { + return Err(InputParserError::MissingTomlFile(path_to_toml.to_path_buf())); + } + let to_map = toml_remap(w_map); + let toml_string = toml::to_string(&to_map) + .map_err(|err_msg| InputParserError::ParseTomlMap(err_msg.to_string()))?; + std::fs::write(path_to_toml, toml_string).map_err(InputParserError::SaveTomlFile)?; + + Ok(()) +} + /// Converts the Toml mapping to the native representation that the compiler /// understands for Inputs fn toml_map_to_field( toml_map: BTreeMap, ) -> Result, InputParserError> { let mut field_map = BTreeMap::new(); - for (parameter, value) in toml_map { match value { TomlTypes::String(string) => { let new_value = parse_str(&string)?; - check_toml_map_duplicates(&mut field_map, parameter, InputValue::Field(new_value))? + if new_value.is_none() { + check_toml_map_duplicates(&mut field_map, parameter, InputValue::Undefined)? + } else { + check_toml_map_duplicates( + &mut field_map, + parameter, + InputValue::Field(new_value.unwrap()), + )? + } } TomlTypes::Integer(integer) => { let new_value = parse_str(&integer.to_string())?; - check_toml_map_duplicates(&mut field_map, parameter, InputValue::Field(new_value))? + check_toml_map_duplicates( + &mut field_map, + parameter, + InputValue::Field(new_value.unwrap()), + )? } TomlTypes::ArrayNum(arr_num) => { - let array_elements: Result, _> = - arr_num.into_iter().map(|elem_num| parse_str(&elem_num.to_string())).collect(); + let array_elements: Vec<_> = arr_num + .into_iter() + .map(|elem_num| parse_str(&elem_num.to_string()).unwrap().unwrap()) + .collect(); check_toml_map_duplicates( &mut field_map, parameter, - InputValue::Vec(array_elements?), + InputValue::Vec(array_elements), )? } TomlTypes::ArrayString(arr_str) => { - let array_elements: Result, _> = - arr_str.into_iter().map(|elem_str| parse_str(&elem_str)).collect(); + let array_elements: Vec<_> = arr_str + .into_iter() + .map(|elem_str| parse_str(&elem_str).unwrap().unwrap()) + .collect(); check_toml_map_duplicates( &mut field_map, parameter, - InputValue::Vec(array_elements?), + InputValue::Vec(array_elements), )? } } @@ -61,6 +93,24 @@ fn toml_map_to_field( Ok(field_map) } +fn toml_remap(map: &BTreeMap) -> BTreeMap { + let mut toml_map = BTreeMap::new(); + for (parameter, value) in map { + match value { + InputValue::Field(f) => { + let f_str = format!("0x{}", f.to_hex()); + toml_map.insert(parameter.clone(), TomlTypes::String(f_str)); + } + InputValue::Vec(v) => { + let array = v.iter().map(|i| format!("0x{}", i.to_hex())).collect(); + toml_map.insert(parameter.clone(), TomlTypes::ArrayString(array)); + } + InputValue::Undefined => unreachable!(), + } + } + toml_map +} + fn check_toml_map_duplicates( field_map: &mut BTreeMap, parameter: String, @@ -72,7 +122,7 @@ fn check_toml_map_duplicates( } } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize)] #[serde(untagged)] enum TomlTypes { // This is most likely going to be a hex string @@ -86,13 +136,20 @@ enum TomlTypes { ArrayString(Vec), } -fn parse_str(value: &str) -> Result { - if value.starts_with("0x") { - FieldElement::from_hex(value).ok_or_else(|| InputParserError::ParseHexStr(value.to_owned())) +fn parse_str(value: &str) -> Result, InputParserError> { + if value.is_empty() { + Ok(None) + } else if value.starts_with("0x") { + let result = FieldElement::from_hex(value); + if result.is_some() { + Ok(result) + } else { + Err(InputParserError::ParseHexStr(value.to_owned())) + } } else { let val: i128 = value .parse::() .map_err(|err_msg| InputParserError::ParseStr(err_msg.to_string()))?; - Ok(FieldElement::from(val)) + Ok(Some(FieldElement::from(val))) } } diff --git a/crates/noirc_frontend/src/hir_def/function.rs b/crates/noirc_frontend/src/hir_def/function.rs index 3fb1d9ff6d2..31a3d91d5c6 100644 --- a/crates/noirc_frontend/src/hir_def/function.rs +++ b/crates/noirc_frontend/src/hir_def/function.rs @@ -13,7 +13,7 @@ use crate::{token::Attribute, FunctionKind}; #[derive(Debug, Clone)] pub struct HirFunction(ExprId); -const MAIN_RETURN_NAME: &str = "return"; +pub const MAIN_RETURN_NAME: &str = "return"; impl HirFunction { pub fn empty() -> HirFunction {