diff --git a/crates/nargo/src/cli/execute_cmd.rs b/crates/nargo/src/cli/execute_cmd.rs index 796183585a8..25c27d75bdb 100644 --- a/crates/nargo/src/cli/execute_cmd.rs +++ b/crates/nargo/src/cli/execute_cmd.rs @@ -1,14 +1,14 @@ -use clap::ArgMatches; use std::path::{Path, PathBuf}; use acvm::acir::native_types::Witness; -use acvm::{FieldElement, PartialWitnessGenerator}; +use acvm::PartialWitnessGenerator; +use clap::ArgMatches; use noirc_abi::errors::AbiError; use noirc_abi::input_parser::{Format, InputValue}; -use noirc_abi::{Abi, 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}, @@ -40,10 +40,6 @@ pub(crate) fn run(args: ArgMatches) -> Result<(), CliError> { Ok(()) } -/// In Barretenberg, the proof system adds a zero witness in the first index, -/// So when we add witness values, their index start from 1. -const WITNESS_OFFSET: u32 = 1; - fn execute_with_path>( program_dir: P, show_ssa: bool, @@ -69,36 +65,19 @@ pub(crate) fn execute_program( // Solve the remaining witnesses let solved_witness = solve_witness(compiled_program, inputs_map)?; - let public_inputs = extract_public_inputs(compiled_program, &solved_witness)?; + let public_abi = compiled_program.abi.clone().public_abi(); + 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)) } -pub(crate) fn extract_public_inputs( - compiled_program: &CompiledProgram, - solved_witness: &WitnessMap, -) -> Result { - let encoded_public_inputs: Vec = compiled_program - .circuit - .public_inputs - .0 - .iter() - .map(|index| solved_witness[index]) - .collect(); - - let public_abi = compiled_program.abi.clone().public_abi(); - - public_abi.decode(&encoded_public_inputs) -} - pub(crate) fn solve_witness( compiled_program: &CompiledProgram, input_map: &InputMap, ) -> Result { - let abi = compiled_program.abi.clone(); let mut solved_witness = - input_map_to_witness_map(abi, input_map).map_err(|error| match error { + compiled_program.abi.encode_to_witness(input_map).map_err(|error| match error { AbiError::UndefinedInput(_) => { CliError::Generic(format!("{error} in the {PROVER_INPUT_FILE}.toml file.")) } @@ -111,24 +90,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 { - // The ABI map is first encoded as a vector of field elements - let encoded_inputs = abi.encode(input_map, true)?; - - Ok(encoded_inputs - .into_iter() - .enumerate() - .map(|(index, witness_value)| { - let witness = Witness::new(WITNESS_OFFSET + (index as u32)); - (witness, witness_value) - }) - .collect()) -} - 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 e92cb3d8d02..b7158e70bbe 100644 --- a/crates/nargo/src/cli/mod.rs +++ b/crates/nargo/src/cli/mod.rs @@ -1,21 +1,15 @@ use acvm::{ - acir::{ - circuit::{Circuit, PublicInputs}, - native_types::Witness, - }, + acir::circuit::{Circuit, PublicInputs}, hash_constraint_system, FieldElement, ProofSystemCompiler, }; pub use check_cmd::check_from_path; use clap::{App, AppSettings, Arg}; use const_format::formatcp; -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}, @@ -45,12 +39,6 @@ const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); static VERSION_STRING: &str = formatcp!("{} (git version hash: {}, is dirty: {})", CARGO_PKG_VERSION, GIT_HASH, IS_DIRTY); -/// 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 01f22a43883..05f5affb580 100644 --- a/crates/nargo/src/cli/prove_cmd.rs +++ b/crates/nargo/src/cli/prove_cmd.rs @@ -9,10 +9,7 @@ use super::{ write_inputs_to_file, 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, TARGET_DIR, VERIFIER_INPUT_FILE}, errors::CliError, }; @@ -82,7 +79,8 @@ pub fn prove_with_path>( let (_, solved_witness) = execute_program(&compiled_program, &inputs_map)?; // Write public inputs into Verifier.toml - let public_inputs = extract_public_inputs(&compiled_program, &solved_witness)?; + let public_abi = compiled_program.abi.clone().public_abi(); + 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 89ff148f056..536b2b4f7b4 100644 --- a/crates/nargo/src/cli/verify_cmd.rs +++ b/crates/nargo/src/cli/verify_cmd.rs @@ -88,7 +88,7 @@ pub(crate) fn verify_proof( ) -> Result { let public_abi = compiled_program.abi.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/errors.rs b/crates/noirc_abi/src/errors.rs index fa35b4a9dba..f6f195694fe 100644 --- a/crates/noirc_abi/src/errors.rs +++ b/crates/noirc_abi/src/errors.rs @@ -1,4 +1,5 @@ use crate::{input_parser::InputValue, AbiParameter, AbiType}; +use acvm::acir::native_types::Witness; use thiserror::Error; #[derive(Debug, Error)] @@ -41,4 +42,8 @@ pub enum AbiError { UndefinedInput(String), #[error("ABI specifies an input of length {expected} but received input of length {actual}")] UnexpectedInputLength { expected: u32, actual: u32 }, + #[error( + "Could not read witness value at index {witness_index:?} (required for parameter \"{name}\")" + )] + MissingParamWitnessValue { name: String, witness_index: Witness }, } diff --git a/crates/noirc_abi/src/input_parser/mod.rs b/crates/noirc_abi/src/input_parser/mod.rs index 64fe6ab1b8a..914b5261e49 100644 --- a/crates/noirc_abi/src/input_parser/mod.rs +++ b/crates/noirc_abi/src/input_parser/mod.rs @@ -10,6 +10,7 @@ use crate::{Abi, 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 +#[cfg_attr(test, derive(PartialEq))] #[derive(Debug, Clone, Serialize)] pub enum InputValue { Field(FieldElement), diff --git a/crates/noirc_abi/src/lib.rs b/crates/noirc_abi/src/lib.rs index 685f1bf5e16..22695ec6459 100644 --- a/crates/noirc_abi/src/lib.rs +++ b/crates/noirc_abi/src/lib.rs @@ -1,10 +1,10 @@ #![forbid(unsafe_code)] use std::{collections::BTreeMap, convert::TryInto, str}; -use acvm::FieldElement; +use acvm::{acir::native_types::Witness, FieldElement}; use errors::AbiError; use input_parser::InputValue; -use iter_extended::vecmap; +use iter_extended::{try_btree_map, try_vecmap, vecmap}; use serde::{Deserialize, Serialize}; // This is the ABI used to bridge the different TOML formats for the initial // witness, the partial witness generator and the interpreter. @@ -15,6 +15,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)] @@ -119,7 +125,11 @@ impl AbiParameter { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Abi { + /// An ordered list of the arguments to the program's `main` function, specifying their types and visibility. pub parameters: Vec, + /// A map from the ABI's parameters to the indices they are written to in the [`WitnessMap`]. + /// This defines how to convert between the [`InputMap`] and [`WitnessMap`]. + pub param_witnesses: BTreeMap>, } impl Abi { @@ -149,52 +159,89 @@ impl Abi { pub fn public_abi(self) -> Abi { let parameters: Vec<_> = self.parameters.into_iter().filter(|param| param.is_public()).collect(); - Abi { parameters } + let param_witnesses = self + .param_witnesses + .into_iter() + .filter(|(param_name, _)| parameters.iter().any(|param| ¶m.name == param_name)) + .collect(); + 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: &InputMap) -> Result { + self.check_for_unexpected_inputs(input_map)?; + + // First encode each input separately, performing any input validation. + let encoded_input_map: BTreeMap> = self + .to_btree_map() + .into_iter() + .filter(|(param_name, _)| param_name != MAIN_RETURN_NAME) + .map(|(param_name, expected_type)| { + let value = input_map + .get(¶m_name) + .ok_or_else(|| AbiError::MissingParam(param_name.clone()))? + .clone(); + + if !value.matches_abi(&expected_type) { + let missing_param = self + .parameters + .iter() + .find(|param| param.name == param_name) + .unwrap() + .clone(); + return Err(AbiError::TypeMismatch { param: missing_param, value }); + } + + Self::encode_value(value, ¶m_name).map(|v| (param_name, v)) + }) + .collect::>()?; + + // Write input field elements into witness indices specified in `self.param_witnesses`. + 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( - 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 mut encoded_inputs = Vec::new(); + pub fn encode_to_array(self, inputs: &InputMap) -> Result, AbiError> { + self.check_for_unexpected_inputs(inputs)?; - for param in parameters { + let mut encoded_inputs = Vec::new(); + 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: param.clone(), value }); } encoded_inputs.extend(Self::encode_value(value, ¶m.name)?); } - // Check that no extra witness values have been provided. - // Any missing values should be caught by the above for-loop so this only catches extra values. - if param_names.len() != inputs.len() { + Ok(encoded_inputs) + } + + /// Checks that no extra witness values have been provided. + fn check_for_unexpected_inputs(&self, inputs: &InputMap) -> Result<(), AbiError> { + let param_names = self.parameter_names(); + if param_names.len() < inputs.len() { let unexpected_params: Vec = inputs.keys().filter(|param| !param_names.contains(param)).cloned().collect(); return Err(AbiError::UnexpectedParams(unexpected_params)); } - Ok(encoded_inputs) + Ok(()) } fn encode_value(value: InputValue, param_name: &String) -> Result, AbiError> { @@ -218,11 +265,30 @@ impl Abi { Ok(encoded_value) } + /// Decode a `WitnessMap` into the types specified in the ABI. + pub fn decode_from_witness(&self, witness_map: &WitnessMap) -> Result { + let public_inputs_map = + try_btree_map(self.parameters.clone(), |AbiParameter { name, typ, .. }| { + let param_witness_values = + try_vecmap(self.param_witnesses[&name].clone(), |witness_index| { + witness_map + .get(&witness_index) + .ok_or_else(|| AbiError::MissingParamWitnessValue { + name: name.clone(), + witness_index, + }) + .copied() + })?; + + Self::decode_value(&mut param_witness_values.into_iter(), &typ) + .map(|input_value| (name.clone(), input_value)) + })?; + + Ok(public_inputs_map) + } + /// Decode a vector of `FieldElements` into the types specified in the ABI. - pub fn decode( - &self, - encoded_inputs: &[FieldElement], - ) -> Result, AbiError> { + pub fn decode_from_array(&self, encoded_inputs: &[FieldElement]) -> Result { let input_length: u32 = encoded_inputs.len().try_into().unwrap(); if input_length != self.field_count() { return Err(AbiError::UnexpectedInputLength { @@ -293,3 +359,60 @@ pub fn decode_string_value(field_elements: &[FieldElement]) -> String { let final_string = str::from_utf8(&string_as_slice).unwrap(); final_string.to_owned() } + +#[cfg(test)] +mod test { + use std::collections::BTreeMap; + + use acvm::{acir::native_types::Witness, FieldElement}; + + use crate::{ + input_parser::InputValue, Abi, AbiParameter, AbiType, AbiVisibility, InputMap, + MAIN_RETURN_NAME, + }; + + #[test] + fn witness_encoding_roundtrip() { + let abi = Abi { + parameters: vec![ + AbiParameter { + name: "thing1".to_string(), + typ: AbiType::Array { length: 2, typ: Box::new(AbiType::Field) }, + visibility: AbiVisibility::Public, + }, + AbiParameter { + name: "thing2".to_string(), + typ: AbiType::Field, + visibility: AbiVisibility::Public, + }, + AbiParameter { + name: MAIN_RETURN_NAME.to_string(), + typ: AbiType::Field, + visibility: AbiVisibility::Public, + }, + ], + // Note that the return value shares a witness with `thing2` + param_witnesses: BTreeMap::from([ + ("thing1".to_string(), vec![Witness(1), Witness(2)]), + ("thing2".to_string(), vec![Witness(3)]), + (MAIN_RETURN_NAME.to_string(), vec![Witness(3)]), + ]), + }; + + // Note we omit return value from inputs + let inputs: InputMap = BTreeMap::from([ + ("thing1".to_string(), InputValue::Vec(vec![FieldElement::one(), FieldElement::one()])), + ("thing2".to_string(), InputValue::Field(FieldElement::zero())), + ]); + + let witness_map = abi.encode_to_witness(&inputs).unwrap(); + let reconstructed_inputs = abi.decode_from_witness(&witness_map).unwrap(); + + for (key, expected_value) in inputs { + assert_eq!(reconstructed_inputs[&key], expected_value); + } + + // We also decode the return value (we can do this immediately as we know it shares a witness with an input). + assert_eq!(reconstructed_inputs[MAIN_RETURN_NAME], reconstructed_inputs["thing2"]) + } +} diff --git a/crates/noirc_driver/src/lib.rs b/crates/noirc_driver/src/lib.rs index 537cf2ddaf2..a7e393705c9 100644 --- a/crates/noirc_driver/src/lib.rs +++ b/crates/noirc_driver/src/lib.rs @@ -181,15 +181,12 @@ impl Driver { local_crate.main_function().expect("cannot compile a program with no main function") }); - // Create ABI for main function - let func_meta = self.context.def_interner.function_meta(&main_function); - let abi = func_meta.into_abi(&self.context.def_interner); - let program = monomorphize(main_function, &self.context.def_interner); let blackbox_supported = acvm::default_is_black_box_supported(np_language.clone()); + match create_circuit(program, np_language, blackbox_supported, show_ssa, show_output) { - Ok(circuit) => Ok(CompiledProgram { circuit, abi }), + Ok((circuit, abi)) => Ok(CompiledProgram { circuit, abi }), Err(err) => { // The FileId here will be the file id of the file with the main file // Errors will be shown at the call site without a stacktrace diff --git a/crates/noirc_evaluator/src/lib.rs b/crates/noirc_evaluator/src/lib.rs index 2172e4bf683..9feaee396df 100644 --- a/crates/noirc_evaluator/src/lib.rs +++ b/crates/noirc_evaluator/src/lib.rs @@ -10,7 +10,7 @@ use acvm::{ }; use errors::{RuntimeError, RuntimeErrorKind}; use iter_extended::btree_map; -use noirc_abi::{AbiType, AbiVisibility}; +use noirc_abi::{Abi, AbiType, AbiVisibility}; use noirc_frontend::monomorphization::ast::*; use ssa::{node, ssa_gen::IrGenerator}; use std::collections::BTreeMap; @@ -25,6 +25,10 @@ pub struct Evaluator { // This is the number of witnesses indices used when // creating the private/public inputs of the ABI. num_witnesses_abi_len: usize, + param_witnesses: BTreeMap>, + // This is the list of witness indices which are linked to public inputs. + // Witnesses below `num_witnesses_abi_len` and not included in this set + // correspond to private inputs and must not be made public. public_inputs: Vec, opcodes: Vec, } @@ -40,14 +44,17 @@ pub fn create_circuit( is_blackbox_supported: IsBlackBoxSupported, enable_logging: bool, show_output: bool, -) -> Result { +) -> Result<(Circuit, Abi), RuntimeError> { let mut evaluator = Evaluator::new(); // First evaluate the main function - evaluator.evaluate_main_alt(program, enable_logging, show_output)?; + evaluator.evaluate_main_alt(program.clone(), enable_logging, show_output)?; let witness_index = evaluator.current_witness_index(); + let mut abi = program.abi; + abi.param_witnesses = evaluator.param_witnesses; + let optimized_circuit = acvm::compiler::compile( Circuit { current_witness_index: witness_index, @@ -59,14 +66,15 @@ pub fn create_circuit( ) .map_err(|_| RuntimeErrorKind::Spanless(String::from("produced an acvm compile error")))?; - Ok(optimized_circuit) + Ok((optimized_circuit, abi)) } impl Evaluator { fn new() -> Self { Evaluator { - public_inputs: Vec::new(), num_witnesses_abi_len: 0, + public_inputs: Vec::new(), + param_witnesses: BTreeMap::new(), // XXX: Barretenberg, reserves the first index to have value 0. // When we increment, we do not use this index at all. // This means that every constraint system at the moment, will either need @@ -142,46 +150,41 @@ impl Evaluator { name: &str, def: Definition, param_type: &AbiType, - visibility: &AbiVisibility, + param_visibility: &AbiVisibility, ir_gen: &mut IrGenerator, ) -> Result<(), RuntimeErrorKind> { - match param_type { + let witnesses = match param_type { AbiType::Field => { let witness = self.add_witness_to_cs(); - if *visibility == AbiVisibility::Public { - self.public_inputs.push(witness); - } ir_gen.create_new_variable( name.to_owned(), Some(def), node::ObjectType::NativeField, Some(witness), ); + vec![witness] } AbiType::Array { length, typ } => { let witnesses = self.generate_array_witnesses(length, typ)?; - if *visibility == AbiVisibility::Public { - self.public_inputs.extend(witnesses.clone()); - } - ir_gen.abi_array(name, Some(def), typ.as_ref(), *length, witnesses); + + ir_gen.abi_array(name, Some(def), typ.as_ref(), *length, witnesses.clone()); + witnesses } AbiType::Integer { sign: _, width } => { let witness = self.add_witness_to_cs(); ssa::acir_gen::range_constraint(witness, *width, self)?; - if *visibility == AbiVisibility::Public { - self.public_inputs.push(witness); - } let obj_type = ir_gen.get_object_type_from_abi(param_type); // Fetch signedness of the integer ir_gen.create_new_variable(name.to_owned(), Some(def), obj_type, Some(witness)); + + vec![witness] } AbiType::Boolean => { let witness = self.add_witness_to_cs(); ssa::acir_gen::range_constraint(witness, 1, self)?; - if *visibility == AbiVisibility::Public { - self.public_inputs.push(witness); - } let obj_type = node::ObjectType::Boolean; ir_gen.create_new_variable(name.to_owned(), Some(def), obj_type, Some(witness)); + + vec![witness] } AbiType::Struct { fields } => { let new_fields = btree_map(fields, |(inner_name, value)| { @@ -191,22 +194,23 @@ impl Evaluator { let mut struct_witnesses: BTreeMap> = BTreeMap::new(); self.generate_struct_witnesses(&mut struct_witnesses, &new_fields)?; - if *visibility == AbiVisibility::Public { - let witnesses: Vec = - struct_witnesses.values().flatten().cloned().collect(); - self.public_inputs.extend(witnesses); - } - ir_gen.abi_struct(name, Some(def), fields, struct_witnesses); + + ir_gen.abi_struct(name, Some(def), fields, struct_witnesses.clone()); + struct_witnesses.values().flatten().cloned().collect() } AbiType::String { length } => { let typ = AbiType::Integer { sign: noirc_abi::Sign::Unsigned, width: 8 }; let witnesses = self.generate_array_witnesses(length, &typ)?; - if *visibility == AbiVisibility::Public { - self.public_inputs.extend(witnesses.clone()); - } - ir_gen.abi_array(name, Some(def), &typ, *length, witnesses); + ir_gen.abi_array(name, Some(def), &typ, *length, witnesses.clone()); + witnesses } + }; + + if param_visibility == &AbiVisibility::Public { + self.public_inputs.extend(witnesses.clone()); } + self.param_witnesses.insert(name.to_owned(), witnesses); + Ok(()) } diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/operations/return.rs b/crates/noirc_evaluator/src/ssa/acir_gen/operations/return.rs index 96b637d99c2..36efeb0de63 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/operations/return.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/operations/return.rs @@ -1,3 +1,6 @@ +use acvm::acir::native_types::Witness; +use noirc_abi::MAIN_RETURN_NAME; + use crate::{ errors::RuntimeErrorKind, ssa::{ @@ -37,6 +40,7 @@ pub(crate) fn evaluate( } }; + let mut witnesses: Vec = Vec::new(); for mut object in objects { let witness = object.get_or_compute_witness(evaluator, true).expect( "infallible: `None` can only be returned when we disallow constant Expressions.", @@ -48,8 +52,10 @@ pub(crate) fn evaluate( "we do not allow private ABI inputs to be returned as public outputs", ))); } - evaluator.public_inputs.push(witness); + witnesses.push(witness); } + evaluator.public_inputs.extend(witnesses.clone()); + evaluator.param_witnesses.insert(MAIN_RETURN_NAME.to_owned(), witnesses); } Ok(None) diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/operations/sort.rs b/crates/noirc_evaluator/src/ssa/acir_gen/operations/sort.rs index ff42c1307e8..70d32c7438b 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/operations/sort.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/operations/sort.rs @@ -122,7 +122,7 @@ mod test { let mut eval = Evaluator { current_witness_index: 0, num_witnesses_abi_len: 0, - public_inputs: Vec::new(), + param_witnesses: BTreeMap::new(), opcodes: Vec::new(), }; diff --git a/crates/noirc_frontend/src/hir_def/function.rs b/crates/noirc_frontend/src/hir_def/function.rs index 2fa9d5edf1b..4f295fee90a 100644 --- a/crates/noirc_frontend/src/hir_def/function.rs +++ b/crates/noirc_frontend/src/hir_def/function.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeMap; + use iter_extended::vecmap; use noirc_abi::{Abi, AbiParameter, AbiVisibility, MAIN_RETURN_NAME}; use noirc_errors::{Location, Span}; @@ -61,7 +63,7 @@ impl Parameters { let as_abi = param.1.as_abi_type(); AbiParameter { name: param_name, typ: as_abi, visibility: param.2 } }); - noirc_abi::Abi { parameters } + noirc_abi::Abi { parameters, param_witnesses: BTreeMap::new() } } pub fn span(&self) -> Span {