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

Auto return #360

Merged
merged 5 commits into from
Oct 14, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
89 changes: 80 additions & 9 deletions crates/nargo/src/cli/prove_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ 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 noirc_frontend::node_interner::NodeInterner;
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();
Expand Down Expand Up @@ -38,12 +42,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<String, InputValue>,
) -> Result<BTreeMap<Witness, FieldElement>, CliError> {
witness_map: &BTreeMap<String, InputValue>,
) -> Result<(BTreeMap<Witness, FieldElement>, Option<Witness>), CliError> {
let mut solved_witness = BTreeMap::new();

let mut index = 0;

let mut return_witness = None;
let return_witness_len = if let Some(pos) =
abi.parameters.iter().position(|x| x.0 == NodeInterner::main_return_name())
guipublic marked this conversation as resolved.
Show resolved Hide resolved
{
match &abi.parameters[pos].1 {
AbiType::Array { length, .. } => *length,
AbiType::Integer { .. } | AbiType::Field(_) => 1,
}
} else {
0
};
for (param_name, param_type) in abi.parameters.into_iter() {
let value = witness_map
.get(&param_name)
Expand Down Expand Up @@ -71,9 +85,24 @@ fn process_abi_with_input(
index += 1;
}
}
InputValue::Undefined => {
assert_eq!(
param_name,
NodeInterner::main_return_name(),
"input value {} is not defined",
param_name
);
return_witness = Some(Witness::new(index + WITNESS_OFFSET));
index += 1;
//XXX We do not support (yet) array of arrays
for _i in 1..return_witness_len {
Witness::new(index + WITNESS_OFFSET);
index += 1
guipublic marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
Ok(solved_witness)
Ok((solved_witness, return_witness))
}

pub fn compile_circuit_and_witness<P: AsRef<Path>>(
Expand All @@ -86,7 +115,7 @@ pub fn compile_circuit_and_witness<P: AsRef<Path>>(

// 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
Expand All @@ -98,11 +127,14 @@ pub fn compile_circuit_and_witness<P: AsRef<Path>>(
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!(
Expand All @@ -119,6 +151,45 @@ pub fn compile_circuit_and_witness<P: AsRef<Path>>(
Ok((compiled_program, solved_witness))
}

fn export_public_inputs<P: AsRef<Path>>(
w_ret: Option<Witness>,
solved_witness: &BTreeMap<Witness, FieldElement>,
witness_map: &BTreeMap<String, InputValue>,
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 mut return_values = Vec::new();
for i in 0..*length {
return_values.push(
*solved_witness.get(&Witness::new(w_ret.0 + i as u32)).unwrap(),
guipublic marked this conversation as resolved.
Show resolved Hide resolved
);
}
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<P: AsRef<Path>>(
proof_name: &str,
program_dir: P,
Expand Down
6 changes: 6 additions & 0 deletions crates/nargo/src/cli/verify_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
)))
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/nargo/tests/test_data/main_return/Prover.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
return = "8"
return = ""
x = "8"
2 changes: 2 additions & 0 deletions crates/noirc_abi/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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)
Expand Down
22 changes: 21 additions & 1 deletion crates/noirc_abi/src/input_parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FieldElement>),
Undefined,
}

impl InputValue {
Expand All @@ -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,
}
}
}
Expand Down Expand Up @@ -65,4 +68,21 @@ impl Format {
}
}
}

pub fn serialise<P: AsRef<Path>>(
&self,
path: P,
file_name: &str,
w_map: &BTreeMap<String, InputValue>,
) -> 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(())
}
}
85 changes: 71 additions & 14 deletions crates/noirc_abi/src/input_parser/toml.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand All @@ -20,39 +21,70 @@ pub(crate) fn parse<P: AsRef<Path>>(
toml_map_to_field(data)
}

pub fn serialise<P: AsRef<Path>>(
path_to_toml: P,
w_map: &BTreeMap<String, InputValue>,
) -> 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<String, TomlTypes>,
) -> Result<BTreeMap<String, InputValue>, 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<Vec<_>, _> =
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<Vec<_>, _> =
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),
)?
}
}
Expand All @@ -61,6 +93,24 @@ fn toml_map_to_field(
Ok(field_map)
}

fn toml_remap(map: &BTreeMap<String, InputValue>) -> BTreeMap<String, TomlTypes> {
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<String, InputValue>,
parameter: String,
Expand All @@ -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
Expand All @@ -86,13 +136,20 @@ enum TomlTypes {
ArrayString(Vec<String>),
}

fn parse_str(value: &str) -> Result<FieldElement, InputParserError> {
if value.starts_with("0x") {
FieldElement::from_hex(value).ok_or_else(|| InputParserError::ParseHexStr(value.to_owned()))
fn parse_str(value: &str) -> Result<Option<FieldElement>, 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::<i128>()
.map_err(|err_msg| InputParserError::ParseStr(err_msg.to_string()))?;
Ok(FieldElement::from(val))
Ok(Some(FieldElement::from(val)))
}
}