Skip to content

Commit

Permalink
feat: create separate methods for encoding to a witness vs verifier i…
Browse files Browse the repository at this point in the history
…nputs
  • Loading branch information
TomAFrench committed Feb 15, 2023
1 parent 123f09d commit 0f829ed
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 102 deletions.
71 changes: 10 additions & 61 deletions crates/nargo/src/cli/execute_cmd.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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))
Expand All @@ -80,68 +78,19 @@ pub(crate) fn solve_witness(
) -> Result<WitnessMap, CliError> {
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())?;

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<WitnessMap, AbiError> {
// First encode each input separately
let encoded_input_map: BTreeMap<String, Vec<FieldElement>> =
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<InputMap, AbiError> {
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<P: AsRef<Path>>(
witness: WitnessMap,
witness_name: &str,
Expand Down
18 changes: 3 additions & 15 deletions crates/nargo/src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand All @@ -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<String, InputValue>;

/// A map from the witnesses in a constraint system to the field element values
pub type WitnessMap = BTreeMap<Witness, FieldElement>;

pub fn start_cli() {
let allow_warnings = Arg::with_name("allow-warnings")
.long("allow-warnings")
Expand Down
7 changes: 2 additions & 5 deletions crates/nargo/src/cli/prove_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -58,7 +55,7 @@ pub fn prove_with_path<P: AsRef<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.
Expand Down
2 changes: 1 addition & 1 deletion crates/nargo/src/cli/verify_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub(crate) fn verify_proof(
) -> Result<bool, CliError> {
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."))
}
Expand Down
90 changes: 70 additions & 20 deletions crates/noirc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<String, InputValue>;

/// A map from the witnesses in a constraint system to the field element values
pub type WitnessMap = BTreeMap<Witness, FieldElement>;

pub const MAIN_RETURN_NAME: &str = "return";

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -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<String, InputValue>,
) -> Result<BTreeMap<Witness, FieldElement>, 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<String, Vec<FieldElement>> =
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<String, InputValue>,
skip_output: bool,
) -> Result<Vec<FieldElement>, 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(&&param.name));
let param_names: Vec<&String> = parameters.clone().map(|param| &param.name).collect();
let parameters = self.parameters.clone();
let param_names: Vec<&String> = parameters.iter().map(|param| &param.name).collect();
let mut encoded_inputs = Vec::new();

for param in parameters {
for param in self.parameters {
let value = inputs
.get(&param.name)
.ok_or_else(|| AbiError::MissingParam(param.name.to_owned()))?
.clone();

if !value.matches_abi(&param.typ) {
return Err(AbiError::TypeMismatch { param: param.to_owned(), value });
return Err(AbiError::TypeMismatch { param, value });
}

encoded_inputs.extend(encode_value(value, &param.name)?);
Expand All @@ -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<Witness, FieldElement>,
) -> Result<BTreeMap<String, InputValue>, 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<BTreeMap<String, InputValue>, AbiError> {
Expand All @@ -227,7 +277,7 @@ impl Abi {
}
}

pub fn encode_value(value: InputValue, param_name: &String) -> Result<Vec<FieldElement>, AbiError> {
fn encode_value(value: InputValue, param_name: &String) -> Result<Vec<FieldElement>, AbiError> {
let mut encoded_value = Vec::new();
match value {
InputValue::Field(elem) => encoded_value.push(elem),
Expand All @@ -248,7 +298,7 @@ pub fn encode_value(value: InputValue, param_name: &String) -> Result<Vec<FieldE
Ok(encoded_value)
}

pub fn decode_value(
fn decode_value(
field_iterator: &mut impl Iterator<Item = FieldElement>,
value_type: &AbiType,
) -> Result<InputValue, AbiError> {
Expand Down

0 comments on commit 0f829ed

Please sign in to comment.