diff --git a/crates/nargo/src/cli/execute_cmd.rs b/crates/nargo/src/cli/execute_cmd.rs index cd38e98ace6..a9b6600b60e 100644 --- a/crates/nargo/src/cli/execute_cmd.rs +++ b/crates/nargo/src/cli/execute_cmd.rs @@ -1,16 +1,14 @@ -use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use acvm::acir::native_types::Witness; -use acvm::{FieldElement, PartialWitnessGenerator}; +use acvm::PartialWitnessGenerator; use clap::ArgMatches; -use iter_extended::{try_btree_map, vecmap}; use noirc_abi::errors::AbiError; use noirc_abi::input_parser::{Format, InputValue}; -use noirc_abi::{decode_value, encode_value, Abi, AbiParameter, MAIN_RETURN_NAME}; +use noirc_abi::{InputMap, WitnessMap, MAIN_RETURN_NAME}; use noirc_driver::CompiledProgram; -use super::{create_named_dir, read_inputs_from_file, write_to_file, InputMap, WitnessMap}; +use super::{create_named_dir, read_inputs_from_file, write_to_file}; use crate::{ cli::compile_cmd::compile_circuit, constants::{PROVER_INPUT_FILE, TARGET_DIR, WITNESS_EXT}, @@ -68,7 +66,7 @@ pub(crate) fn execute_program( let solved_witness = solve_witness(compiled_program, inputs_map)?; let public_abi = compiled_program.abi.as_ref().unwrap().clone().public_abi(); - let public_inputs = extract_public_inputs(&public_abi, &solved_witness)?; + let public_inputs = public_abi.decode_from_witness(&solved_witness)?; let return_value = public_inputs.get(MAIN_RETURN_NAME).cloned(); Ok((return_value, solved_witness)) @@ -80,13 +78,12 @@ pub(crate) fn solve_witness( ) -> Result { let abi = compiled_program.abi.as_ref().unwrap(); - let mut solved_witness = - input_map_to_witness_map(abi, input_map).map_err(|error| match error { - AbiError::UndefinedInput(_) => { - CliError::Generic(format!("{error} in the {PROVER_INPUT_FILE}.toml file.")) - } - _ => CliError::from(error), - })?; + let mut solved_witness = abi.encode_to_witness(input_map).map_err(|error| match error { + AbiError::UndefinedInput(_) => { + CliError::Generic(format!("{error} in the {PROVER_INPUT_FILE}.toml file.")) + } + _ => CliError::from(error), + })?; let backend = crate::backends::ConcreteBackend; backend.solve(&mut solved_witness, compiled_program.circuit.opcodes.clone())?; @@ -94,54 +91,6 @@ pub(crate) fn solve_witness( Ok(solved_witness) } -/// Given an InputMap and an Abi, produce a WitnessMap -/// -/// In particular, this method shows one how to associate values in a Toml/JSON -/// file with witness indices -fn input_map_to_witness_map(abi: &Abi, input_map: &InputMap) -> Result { - // First encode each input separately - let encoded_input_map: BTreeMap> = - try_btree_map(input_map, |(key, value)| { - encode_value(value.clone(), key).map(|v| (key.clone(), v)) - })?; - - // Write input field elements into witness indices specified in `abi_witness_map`. - let witness_map = encoded_input_map - .iter() - .flat_map(|(param_name, encoded_param_fields)| { - let param_witness_indices = &abi.param_witnesses[param_name]; - param_witness_indices - .iter() - .zip(encoded_param_fields.iter()) - .map(|(&witness, &field_element)| (witness, field_element)) - }) - .collect(); - - Ok(witness_map) -} - -pub(crate) fn extract_public_inputs( - public_abi: &Abi, - solved_witness: &WitnessMap, -) -> Result { - let public_inputs_map = public_abi - .parameters - .iter() - .map(|AbiParameter { name, typ, .. }| { - let param_witness_values = - vecmap(public_abi.param_witnesses[name].clone(), |witness_index| { - solved_witness[&witness_index] - }); - - decode_value(&mut param_witness_values.into_iter(), typ) - .map(|input_value| (name.clone(), input_value)) - .unwrap() - }) - .collect(); - - Ok(public_inputs_map) -} - pub(crate) fn save_witness_to_dir>( witness: WitnessMap, witness_name: &str, diff --git a/crates/nargo/src/cli/mod.rs b/crates/nargo/src/cli/mod.rs index 92cc68b41d9..f9d8bbc42d3 100644 --- a/crates/nargo/src/cli/mod.rs +++ b/crates/nargo/src/cli/mod.rs @@ -1,19 +1,13 @@ -use acvm::{ - acir::{circuit::PublicInputs, native_types::Witness}, - FieldElement, -}; +use acvm::{acir::circuit::PublicInputs, FieldElement}; pub use check_cmd::check_from_path; use clap::{App, AppSettings, Arg}; use const_format::formatcp; use git_version::git_version; -use noirc_abi::{ - input_parser::{Format, InputValue}, - Abi, -}; +use noirc_abi::{input_parser::Format, Abi, InputMap}; use noirc_driver::Driver; use noirc_frontend::graph::{CrateName, CrateType}; use std::{ - collections::{BTreeMap, HashMap, HashSet}, + collections::{HashMap, HashSet}, fs::File, io::Write, path::{Path, PathBuf}, @@ -36,12 +30,6 @@ mod verify_cmd; const SHORT_GIT_HASH: &str = git_version!(prefix = "git:"); const VERSION_STRING: &str = formatcp!("{} ({})", env!("CARGO_PKG_VERSION"), SHORT_GIT_HASH); -/// A map from the fields in an TOML/JSON file which correspond to some ABI to their values -pub type InputMap = BTreeMap; - -/// A map from the witnesses in a constraint system to the field element values -pub type WitnessMap = BTreeMap; - pub fn start_cli() { let allow_warnings = Arg::with_name("allow-warnings") .long("allow-warnings") diff --git a/crates/nargo/src/cli/prove_cmd.rs b/crates/nargo/src/cli/prove_cmd.rs index e0009b6ea9b..9d2e78f7387 100644 --- a/crates/nargo/src/cli/prove_cmd.rs +++ b/crates/nargo/src/cli/prove_cmd.rs @@ -9,10 +9,7 @@ use super::{ write_to_file, }; use crate::{ - cli::{ - execute_cmd::{execute_program, extract_public_inputs}, - verify_cmd::verify_proof, - }, + cli::{execute_cmd::execute_program, verify_cmd::verify_proof}, constants::{PROOFS_DIR, PROOF_EXT, PROVER_INPUT_FILE, VERIFIER_INPUT_FILE}, errors::CliError, }; @@ -58,7 +55,7 @@ pub fn prove_with_path>( // Write public inputs into Verifier.toml let public_abi = compiled_program.abi.as_ref().unwrap().clone().public_abi(); - let public_inputs = extract_public_inputs(&public_abi, &solved_witness)?; + let public_inputs = public_abi.decode_from_witness(&solved_witness)?; write_inputs_to_file(&public_inputs, &program_dir, VERIFIER_INPUT_FILE, Format::Toml)?; // Since the public outputs are added onto the public inputs list, there can be duplicates. diff --git a/crates/nargo/src/cli/verify_cmd.rs b/crates/nargo/src/cli/verify_cmd.rs index 60efc882ec7..6f541c33482 100644 --- a/crates/nargo/src/cli/verify_cmd.rs +++ b/crates/nargo/src/cli/verify_cmd.rs @@ -64,7 +64,7 @@ pub(crate) fn verify_proof( ) -> Result { let public_abi = compiled_program.abi.unwrap().public_abi(); let public_inputs = - public_abi.encode(&public_inputs_map, false).map_err(|error| match error { + public_abi.encode_to_array(&public_inputs_map).map_err(|error| match error { AbiError::UndefinedInput(_) => { CliError::Generic(format!("{error} in the {VERIFIER_INPUT_FILE}.toml file.")) } diff --git a/crates/noirc_abi/src/lib.rs b/crates/noirc_abi/src/lib.rs index c5e80b6c1f6..197c40088e4 100644 --- a/crates/noirc_abi/src/lib.rs +++ b/crates/noirc_abi/src/lib.rs @@ -4,6 +4,7 @@ use std::{collections::BTreeMap, convert::TryInto, str}; use acvm::{acir::native_types::Witness, FieldElement}; use errors::AbiError; use input_parser::InputValue; +use iter_extended::{try_btree_map, vecmap}; use serde::{Deserialize, Serialize}; // This is the ABI used to bridge the different TOML formats for the initial @@ -15,6 +16,12 @@ pub mod errors; pub mod input_parser; mod serialization; +/// A map from the fields in an TOML/JSON file which correspond to some ABI to their values +pub type InputMap = BTreeMap; + +/// A map from the witnesses in a constraint system to the field element values +pub type WitnessMap = BTreeMap; + pub const MAIN_RETURN_NAME: &str = "return"; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -158,35 +165,55 @@ impl Abi { Abi { parameters, param_witnesses } } + /// Encode a set of inputs as described in the ABI into a `WitnessMap`. + pub fn encode_to_witness( + &self, + input_map: &BTreeMap, + ) -> Result, AbiError> { + // TODO: Avoid this clone + let mut input_map = input_map.clone(); + + // Remove the return value parameter + input_map.remove(MAIN_RETURN_NAME); + + // First encode each input separately + let encoded_input_map: BTreeMap> = + try_btree_map(input_map, |(key, value)| { + encode_value(value, &key).map(|v| (key.clone(), v)) + })?; + + // Write input field elements into witness indices specified in `abi_witness_map`. + let witness_map = encoded_input_map + .iter() + .flat_map(|(param_name, encoded_param_fields)| { + let param_witness_indices = &self.param_witnesses[param_name]; + param_witness_indices + .iter() + .zip(encoded_param_fields.iter()) + .map(|(&witness, &field_element)| (witness, field_element)) + }) + .collect(); + + Ok(witness_map) + } + /// Encode a set of inputs as described in the ABI into a vector of `FieldElement`s. - pub fn encode( + pub fn encode_to_array( self, inputs: &BTreeMap, - skip_output: bool, ) -> Result, AbiError> { - // Condition that specifies whether we should filter the "return" - // parameter. We do this in the case that it is not in the `inputs` - // map specified. - // - // See Issue #645 : Adding a `public outputs` field into acir and - // the ABI will clean up this logic - // For prosperity; the prover does not know about a `return` value - // so we skip this when encoding the ABI - let return_condition = - |param_name: &&String| !skip_output || (param_name != &MAIN_RETURN_NAME); - - let parameters = self.parameters.iter().filter(|param| return_condition(&¶m.name)); - let param_names: Vec<&String> = parameters.clone().map(|param| ¶m.name).collect(); + let parameters = self.parameters.clone(); + let param_names: Vec<&String> = parameters.iter().map(|param| ¶m.name).collect(); let mut encoded_inputs = Vec::new(); - for param in parameters { + for param in self.parameters { let value = inputs .get(¶m.name) .ok_or_else(|| AbiError::MissingParam(param.name.to_owned()))? .clone(); if !value.matches_abi(¶m.typ) { - return Err(AbiError::TypeMismatch { param: param.to_owned(), value }); + return Err(AbiError::TypeMismatch { param, value }); } encoded_inputs.extend(encode_value(value, ¶m.name)?); @@ -203,8 +230,31 @@ impl Abi { Ok(encoded_inputs) } + /// Decode a `WitnessMap` into the types specified in the ABI. + pub fn decode_from_witness( + &self, + witness_map: &BTreeMap, + ) -> Result, AbiError> { + let public_inputs_map = self + .parameters + .iter() + .map(|AbiParameter { name, typ, .. }| { + let param_witness_values = + vecmap(self.param_witnesses[name].clone(), |witness_index| { + witness_map[&witness_index] + }); + + decode_value(&mut param_witness_values.into_iter(), typ) + .map(|input_value| (name.clone(), input_value)) + .unwrap() + }) + .collect(); + + Ok(public_inputs_map) + } + /// Decode a vector of `FieldElements` into the types specified in the ABI. - pub fn decode( + pub fn decode_from_array( &self, encoded_inputs: &[FieldElement], ) -> Result, AbiError> { @@ -227,7 +277,7 @@ impl Abi { } } -pub fn encode_value(value: InputValue, param_name: &String) -> Result, AbiError> { +fn encode_value(value: InputValue, param_name: &String) -> Result, AbiError> { let mut encoded_value = Vec::new(); match value { InputValue::Field(elem) => encoded_value.push(elem), @@ -248,7 +298,7 @@ pub fn encode_value(value: InputValue, param_name: &String) -> Result, value_type: &AbiType, ) -> Result {