From 6b0bdbced1bfe2c92e90dd7b70ca8f9e5ccb7c0d Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Fri, 1 Dec 2023 19:03:08 +0100 Subject: [PATCH] feat: data bus (#3508) # Description ## Problem\* aztec private kernel is doing a lot of proof recursion along the call stack of contract calls, resulting in a lot of public inputs required for the aztec ABI. These amount of public inputs have an impact on the performance, which can be mitigated by having instead the public inputs as private and a public commitment of these inputs. We can then leverage the proving system capability for proving/verifying efficiently commitments, outside of the noir program. ## Summary\* In order to interface with the proving system, the user can declare some inputs as call_data (instead of pub) and the return values as return_data (instead of pub). The compiler will abstract these into specific immutable arrays that the proving system will use. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [X] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French --- compiler/noirc_driver/src/abi_gen.rs | 6 +- compiler/noirc_driver/src/lib.rs | 5 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 24 ++- .../src/ssa/function_builder/data_bus.rs | 144 ++++++++++++++++++ .../src/ssa/function_builder/mod.rs | 2 + compiler/noirc_evaluator/src/ssa/ir/dfg.rs | 4 +- compiler/noirc_evaluator/src/ssa/opt/die.rs | 4 + .../src/ssa/opt/fill_internal_slices.rs | 3 + .../noirc_evaluator/src/ssa/opt/inlining.rs | 6 +- .../noirc_evaluator/src/ssa/opt/mem2reg.rs | 6 + .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 67 ++++++-- compiler/noirc_frontend/src/ast/mod.rs | 7 +- .../src/hir/resolution/resolver.rs | 2 +- compiler/noirc_frontend/src/lexer/token.rs | 6 + .../src/monomorphization/ast.rs | 14 +- .../src/monomorphization/mod.rs | 10 +- compiler/noirc_frontend/src/parser/parser.rs | 16 +- docs/docs/language_concepts/12_data_bus.md | 23 +++ tooling/nargo_cli/src/cli/fs/inputs.rs | 7 +- .../execution_success/databus/Nargo.toml | 6 + .../execution_success/databus/Prover.toml | 3 + .../execution_success/databus/src/main.nr | 10 ++ tooling/nargo_fmt/src/utils.rs | 1 + tooling/noir_codegen/src/noir_types.ts | 4 +- .../test/assert_lt/target/assert_lt.json | 2 +- .../assert_lt/target/assert_lt.json | 2 +- .../test/public_input_deflattening.test.ts | 27 ++-- tooling/noirc_abi/src/input_parser/json.rs | 9 +- tooling/noirc_abi/src/input_parser/mod.rs | 8 +- tooling/noirc_abi/src/input_parser/toml.rs | 9 +- tooling/noirc_abi/src/lib.rs | 24 ++- tooling/noirc_abi_wasm/src/lib.rs | 8 +- 32 files changed, 402 insertions(+), 67 deletions(-) create mode 100644 compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs create mode 100644 docs/docs/language_concepts/12_data_bus.md create mode 100644 tooling/nargo_cli/tests/execution_success/databus/Nargo.toml create mode 100644 tooling/nargo_cli/tests/execution_success/databus/Prover.toml create mode 100644 tooling/nargo_cli/tests/execution_success/databus/src/main.nr diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs index 1cad67ba5b7..9d0d64b6300 100644 --- a/compiler/noirc_driver/src/abi_gen.rs +++ b/compiler/noirc_driver/src/abi_gen.rs @@ -2,11 +2,12 @@ use std::collections::BTreeMap; use acvm::acir::native_types::Witness; use iter_extended::{btree_map, vecmap}; -use noirc_abi::{Abi, AbiParameter, AbiType}; +use noirc_abi::{Abi, AbiParameter, AbiReturnType, AbiType}; use noirc_frontend::{ hir::Context, hir_def::{function::Param, stmt::HirPattern}, node_interner::{FuncId, NodeInterner}, + Visibility, }; use std::ops::Range; @@ -17,9 +18,12 @@ pub(super) fn gen_abi( func_id: &FuncId, input_witnesses: Vec, return_witnesses: Vec, + return_visibility: Visibility, ) -> Abi { let (parameters, return_type) = compute_function_abi(context, func_id); let param_witnesses = param_witnesses_from_abi_param(¶meters, input_witnesses); + let return_type = return_type + .map(|typ| AbiReturnType { abi_type: typ, visibility: return_visibility.into() }); Abi { parameters, return_type, param_witnesses, return_witnesses } } diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 93ed26fb91a..23869fc2a61 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -346,11 +346,12 @@ pub fn compile_no_check( if !force_compile && hashes_match { return Ok(cached_program.expect("cache must exist for hashes to match")); } - + let visibility = program.return_visibility; let (circuit, debug, input_witnesses, return_witnesses, warnings) = create_circuit(program, options.show_ssa, options.show_brillig)?; - let abi = abi_gen::gen_abi(context, &main_function, input_witnesses, return_witnesses); + let abi = + abi_gen::gen_abi(context, &main_function, input_witnesses, return_witnesses, visibility); let file_map = filter_relevant_files(&[debug.clone()], &context.file_manager); Ok(CompiledProgram { diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 186e260a52a..d73bb514e02 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -5,6 +5,7 @@ use std::collections::HashSet; use std::fmt::Debug; use self::acir_ir::acir_variable::{AcirContext, AcirType, AcirVar}; +use super::function_builder::data_bus::DataBus; use super::ir::dfg::CallStack; use super::{ ir::{ @@ -90,6 +91,8 @@ struct Context { /// Maps SSA array values to their slice size and any nested slices internal to the parent slice. /// This enables us to maintain the slice structure of a slice when performing an array get. slice_sizes: HashMap, Vec>, + + data_bus: DataBus, } #[derive(Clone)] @@ -199,6 +202,7 @@ impl Context { internal_mem_block_lengths: HashMap::default(), max_block_id: 0, slice_sizes: HashMap::default(), + data_bus: DataBus::default(), } } @@ -226,6 +230,8 @@ impl Context { let dfg = &main_func.dfg; let entry_block = &dfg[main_func.entry_block()]; let input_witness = self.convert_ssa_block_params(entry_block.parameters(), dfg)?; + + self.data_bus = dfg.data_bus.to_owned(); let mut warnings = Vec::new(); for instruction_id in entry_block.instructions() { warnings.extend(self.convert_ssa_instruction( @@ -892,10 +898,26 @@ impl Context { dfg: &DataFlowGraph, ) -> Result { let (array_id, _, block_id) = self.check_array_is_initialized(array, dfg)?; - let results = dfg.instruction_results(instruction); let res_typ = dfg.type_of_value(results[0]); + // Get operations to call-data parameters are replaced by a get to the call-data-bus array + if let Some(call_data) = self.data_bus.call_data { + if self.data_bus.call_data_map.contains_key(&array_id) { + // TODO: the block_id of call-data must be notified to the backend + // TODO: should we do the same for return-data? + let type_size = res_typ.flattened_size(); + let type_size = + self.acir_context.add_constant(FieldElement::from(type_size as i128)); + let offset = self.acir_context.mul_var(var_index, type_size)?; + let bus_index = self.acir_context.add_constant(FieldElement::from( + self.data_bus.call_data_map[&array_id] as i128, + )); + let new_index = self.acir_context.add_var(offset, bus_index)?; + return self.array_get(instruction, call_data, new_index, dfg); + } + } + let value = if !res_typ.contains_slice_element() { self.array_get_value(&res_typ, block_id, &mut var_index, &[])? } else { diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs new file mode 100644 index 00000000000..7f337089321 --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs @@ -0,0 +1,144 @@ +use std::rc::Rc; + +use crate::ssa::ir::{types::Type, value::ValueId}; +use acvm::FieldElement; +use fxhash::FxHashMap as HashMap; +use noirc_frontend::hir_def::function::FunctionSignature; + +use super::FunctionBuilder; + +/// Used to create a data bus, which is an array of private inputs +/// replacing public inputs +pub(crate) struct DataBusBuilder { + pub(crate) values: im::Vector, + index: usize, + pub(crate) map: HashMap, + pub(crate) databus: Option, +} + +impl DataBusBuilder { + pub(crate) fn new() -> DataBusBuilder { + DataBusBuilder { + index: 0, + map: HashMap::default(), + databus: None, + values: im::Vector::new(), + } + } + + /// Generates a boolean vector telling which (ssa) parameter from the given function signature + /// are tagged with databus visibility + pub(crate) fn is_databus(main_signature: &FunctionSignature) -> Vec { + let mut params_is_databus = Vec::new(); + + for param in &main_signature.0 { + let is_databus = match param.2 { + noirc_frontend::Visibility::Public | noirc_frontend::Visibility::Private => false, + noirc_frontend::Visibility::DataBus => true, + }; + let len = param.1.field_count() as usize; + params_is_databus.extend(vec![is_databus; len]); + } + params_is_databus + } +} + +#[derive(Clone, Default, Debug)] +pub(crate) struct DataBus { + pub(crate) call_data: Option, + pub(crate) call_data_map: HashMap, + pub(crate) return_data: Option, +} + +impl DataBus { + /// Updates the databus values with the provided function + pub(crate) fn map_values(&self, mut f: impl FnMut(ValueId) -> ValueId) -> DataBus { + let mut call_data_map = HashMap::default(); + for (k, v) in self.call_data_map.iter() { + call_data_map.insert(f(*k), *v); + } + DataBus { + call_data: self.call_data.map(&mut f), + call_data_map, + return_data: self.return_data.map(&mut f), + } + } + + /// Construct a databus from call_data and return_data data bus builders + pub(crate) fn get_data_bus(call_data: DataBusBuilder, return_data: DataBusBuilder) -> DataBus { + DataBus { + call_data: call_data.databus, + call_data_map: call_data.map, + return_data: return_data.databus, + } + } +} + +impl FunctionBuilder { + /// Insert a value into a data bus builder + fn add_to_data_bus(&mut self, value: ValueId, databus: &mut DataBusBuilder) { + assert!(databus.databus.is_none(), "initialising finalized call data"); + let typ = self.current_function.dfg[value].get_type().clone(); + match typ { + Type::Numeric(_) => { + databus.values.push_back(value); + databus.index += 1; + } + Type::Reference(_) => unreachable!(), + Type::Array(typ, len) => { + assert!(typ.len() == 1, "unsupported composite type"); + databus.map.insert(value, databus.index); + for i in 0..len { + // load each element of the array + let index = self + .current_function + .dfg + .make_constant(FieldElement::from(i as i128), Type::field()); + let element = self.insert_array_get(value, index, typ[0].clone()); + self.add_to_data_bus(element, databus); + } + } + Type::Slice(_) => unreachable!(), + Type::Function => unreachable!(), + } + } + + /// Create a data bus builder from a list of values + pub(crate) fn initialize_data_bus( + &mut self, + values: &[ValueId], + mut databus: DataBusBuilder, + ) -> DataBusBuilder { + for value in values { + self.add_to_data_bus(*value, &mut databus); + } + let len = databus.values.len(); + + let array = if len > 0 { + let array = + self.array_constant(databus.values, Type::Array(Rc::new(vec![Type::field()]), len)); + Some(array) + } else { + None + }; + + DataBusBuilder { index: 0, map: databus.map, databus: array, values: im::Vector::new() } + } + + /// Generate the data bus for call-data, based on the parameters of the entry block + /// and a boolean vector telling which ones are call-data + pub(crate) fn call_data_bus(&mut self, is_params_databus: Vec) -> DataBusBuilder { + //filter parameters of the first block that have call-data visilibity + let first_block = self.current_function.entry_block(); + let params = self.current_function.dfg[first_block].parameters(); + let mut databus_param = Vec::new(); + for (param, is_databus) in params.iter().zip(is_params_databus) { + if is_databus { + databus_param.push(param.to_owned()); + } + } + // create the call-data-bus from the filtered list + let call_data = DataBusBuilder::new(); + self.initialize_data_bus(&databus_param, call_data) + } +} diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index e01e1fe1a1d..f143ca7ee86 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -1,3 +1,5 @@ +pub(crate) mod data_bus; + use std::{borrow::Cow, rc::Rc}; use acvm::FieldElement; diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 75b2cf962f7..abddbfb74c7 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use crate::ssa::ir::instruction::SimplifyResult; +use crate::ssa::{function_builder::data_bus::DataBus, ir::instruction::SimplifyResult}; use super::{ basic_block::{BasicBlock, BasicBlockId}, @@ -80,6 +80,8 @@ pub(crate) struct DataFlowGraph { /// Instructions inserted by internal SSA passes that don't correspond to user code /// may not have a corresponding location. locations: HashMap, + + pub(crate) data_bus: DataBus, } pub(crate) type CallStack = im::Vector; diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 53cdf72bbbf..492e96dc08c 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -33,6 +33,10 @@ impl Ssa { /// of its instructions are needed elsewhere. fn dead_instruction_elimination(function: &mut Function) { let mut context = Context::default(); + if let Some(call_data) = function.dfg.data_bus.call_data { + context.mark_used_instruction_results(&function.dfg, call_data); + } + let blocks = PostOrder::with_function(function); for block in blocks.as_slice() { diff --git a/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs b/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs index fede0e540e2..f5e9598114c 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs @@ -67,8 +67,11 @@ impl Ssa { // The pass is also currently only setup to handle a function with a single flattened block. // For complex Brillig functions we can expect this pass to panic. if function.runtime() == RuntimeType::Acir { + let databus = function.dfg.data_bus.clone(); let mut context = Context::new(function); context.process_blocks(); + // update the databus with the new array instructions + function.dfg.data_bus = databus.map_values(|t| context.inserter.resolve(t)); } } self diff --git a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index ed2484febac..b4f12b2f897 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -139,11 +139,15 @@ impl InlineContext { context.blocks.insert(context.source_function.entry_block(), entry_block); context.inline_blocks(ssa); + // translate databus values + let databus = entry_point.dfg.data_bus.map_values(|t| context.translate_value(t)); // Finally, we should have 1 function left representing the inlined version of the target function. let mut new_ssa = self.builder.finish(); assert_eq!(new_ssa.functions.len(), 1); - new_ssa.functions.pop_first().unwrap().1 + let mut new_func = new_ssa.functions.pop_first().unwrap().1; + new_func.dfg.data_bus = databus; + new_func } /// Inlines a function into the current function and returns the translated return values diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index fba6e6ab989..ce205c8d883 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -91,6 +91,7 @@ impl Ssa { let mut context = PerFunctionContext::new(function); context.mem2reg(); context.remove_instructions(); + context.update_data_bus(); } self } @@ -362,6 +363,11 @@ impl<'f> PerFunctionContext<'f> { } } + fn update_data_bus(&mut self) { + let databus = self.inserter.function.dfg.data_bus.clone(); + self.inserter.function.dfg.data_bus = databus.map_values(|t| self.inserter.resolve(t)); + } + fn handle_terminator(&mut self, block: BasicBlockId, references: &mut Block) { self.inserter.map_terminator_in_place(block); diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 53f1bc863be..acf60cd6799 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -1,4 +1,4 @@ -mod context; +pub(crate) mod context; mod program; mod value; @@ -9,12 +9,15 @@ use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; use noirc_frontend::{ monomorphization::ast::{self, Binary, Expression, Program}, - BinaryOpKind, + BinaryOpKind, Visibility, }; use crate::{ errors::RuntimeError, - ssa::ir::{instruction::Intrinsic, types::NumericType}, + ssa::{ + function_builder::data_bus::DataBusBuilder, + ir::{instruction::Intrinsic, types::NumericType}, + }, }; use self::{ @@ -22,17 +25,25 @@ use self::{ value::{Tree, Values}, }; -use super::ir::{ - function::RuntimeType, - instruction::{BinaryOp, TerminatorInstruction}, - types::Type, - value::ValueId, +use super::{ + function_builder::data_bus::DataBus, + ir::{ + function::RuntimeType, + instruction::{BinaryOp, TerminatorInstruction}, + types::Type, + value::ValueId, + }, }; /// Generates SSA for the given monomorphized program. /// /// This function will generate the SSA but does not perform any optimizations on it. pub(crate) fn generate_ssa(program: Program) -> Result { + // see which parameter has call_data/return_data attribute + let is_databus = DataBusBuilder::is_databus(&program.main_function_signature); + + let is_return_data = matches!(program.return_visibility, Visibility::DataBus); + let return_location = program.return_location; let context = SharedContext::new(program); @@ -48,20 +59,41 @@ pub(crate) fn generate_ssa(program: Program) -> Result { if main.unconstrained { RuntimeType::Brillig } else { RuntimeType::Acir }, &context, ); + + // Generate the call_data bus from the relevant parameters. We create it *before* processing the function body + let call_data = function_context.builder.call_data_bus(is_databus); + function_context.codegen_function_body(&main.body)?; + let mut return_data = DataBusBuilder::new(); if let Some(return_location) = return_location { let block = function_context.builder.current_block(); - if function_context.builder.current_function.dfg[block].terminator().is_some() { - let return_instruction = - function_context.builder.current_function.dfg[block].unwrap_terminator_mut(); - match return_instruction { - TerminatorInstruction::Return { call_stack, .. } => { - call_stack.clear(); - call_stack.push_back(return_location); + if function_context.builder.current_function.dfg[block].terminator().is_some() + && is_return_data + { + // initialize the return_data bus from the return values + let return_data_values = + match function_context.builder.current_function.dfg[block].unwrap_terminator() { + TerminatorInstruction::Return { return_values, .. } => return_values.to_owned(), + _ => unreachable!("ICE - expect return on the last block"), + }; + + return_data = + function_context.builder.initialize_data_bus(&return_data_values, return_data); + } + let return_instruction = + function_context.builder.current_function.dfg[block].unwrap_terminator_mut(); + match return_instruction { + TerminatorInstruction::Return { return_values, call_stack } => { + call_stack.clear(); + call_stack.push_back(return_location); + // replace the returned values with the return data array + if let Some(return_data_bus) = return_data.databus { + return_values.clear(); + return_values.push(return_data_bus); } - _ => unreachable!("ICE - expect return on the last block"), } + _ => unreachable!("ICE - expect return on the last block"), } } @@ -74,6 +106,9 @@ pub(crate) fn generate_ssa(program: Program) -> Result { function_context.new_function(dest_id, function); function_context.codegen_function_body(&function.body)?; } + // we save the data bus inside the dfg + function_context.builder.current_function.dfg.data_bus = + DataBus::get_data_bus(call_data, return_data); Ok(function_context.builder.finish()) } diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 5cbed19620d..2fbe73dafef 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -15,6 +15,7 @@ pub use expression::*; pub use function::*; use noirc_errors::Span; +use serde::{Deserialize, Serialize}; pub use statement::*; pub use structure::*; pub use traits::*; @@ -281,13 +282,16 @@ pub enum FunctionVisibility { PublicCrate, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] /// Represents whether the parameter is public or known only to the prover. pub enum Visibility { Public, // Constants are not allowed in the ABI for main at the moment. // Constant, Private, + /// DataBus is public input handled as private input. We use the fact that return values are properly computed by the program to avoid having them as public inputs + /// it is useful for recursion and is handled by the proving system. + DataBus, } impl std::fmt::Display for Visibility { @@ -295,6 +299,7 @@ impl std::fmt::Display for Visibility { match self { Self::Public => write!(f, "pub"), Self::Private => write!(f, "priv"), + Self::DataBus => write!(f, "databus"), } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index a788137a545..4e7ed7e2ea9 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -794,7 +794,7 @@ impl<'a> Resolver<'a> { // 'pub_allowed' also implies 'pub' is required on return types if self.pub_allowed(func) && return_type.as_ref() != &Type::Unit - && func.def.return_visibility != Visibility::Public + && func.def.return_visibility == Visibility::Private { self.push_err(ResolverError::NecessaryPub { ident: func.name_ident().clone() }); } diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index b16de42c0ba..ba93e52e1a8 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -644,6 +644,7 @@ pub enum Keyword { Assert, AssertEq, Bool, + CallData, Char, CompTime, Constrain, @@ -667,6 +668,7 @@ pub enum Keyword { Open, Pub, Return, + ReturnData, String, Struct, Trait, @@ -685,6 +687,7 @@ impl fmt::Display for Keyword { Keyword::AssertEq => write!(f, "assert_eq"), Keyword::Bool => write!(f, "bool"), Keyword::Char => write!(f, "char"), + Keyword::CallData => write!(f, "call_data"), Keyword::CompTime => write!(f, "comptime"), Keyword::Constrain => write!(f, "constrain"), Keyword::Contract => write!(f, "contract"), @@ -707,6 +710,7 @@ impl fmt::Display for Keyword { Keyword::Open => write!(f, "open"), Keyword::Pub => write!(f, "pub"), Keyword::Return => write!(f, "return"), + Keyword::ReturnData => write!(f, "return_data"), Keyword::String => write!(f, "str"), Keyword::Struct => write!(f, "struct"), Keyword::Trait => write!(f, "trait"), @@ -727,6 +731,7 @@ impl Keyword { "assert" => Keyword::Assert, "assert_eq" => Keyword::AssertEq, "bool" => Keyword::Bool, + "call_data" => Keyword::CallData, "char" => Keyword::Char, "comptime" => Keyword::CompTime, "constrain" => Keyword::Constrain, @@ -750,6 +755,7 @@ impl Keyword { "open" => Keyword::Open, "pub" => Keyword::Pub, "return" => Keyword::Return, + "return_data" => Keyword::ReturnData, "str" => Keyword::String, "struct" => Keyword::Struct, "trait" => Keyword::Trait, diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index 0a005d766fe..5a5f07b0a38 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -2,7 +2,9 @@ use acvm::FieldElement; use iter_extended::vecmap; use noirc_errors::Location; -use crate::{hir_def::function::FunctionSignature, BinaryOpKind, Distinctness, Signedness}; +use crate::{ + hir_def::function::FunctionSignature, BinaryOpKind, Distinctness, Signedness, Visibility, +}; /// The monomorphized AST is expression-based, all statements are also /// folded into this expression enum. Compared to the HIR, the monomorphized @@ -243,6 +245,7 @@ pub struct Program { /// forwarding to the next phase. pub return_distinctness: Distinctness, pub return_location: Option, + pub return_visibility: Visibility, } impl Program { @@ -251,8 +254,15 @@ impl Program { main_function_signature: FunctionSignature, return_distinctness: Distinctness, return_location: Option, + return_visibility: Visibility, ) -> Program { - Program { functions, main_function_signature, return_distinctness, return_location } + Program { + functions, + main_function_signature, + return_distinctness, + return_location, + return_visibility, + } } pub fn main(&self) -> &Function { diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 57e4e6cdeb0..4f035e0fc2e 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -105,8 +105,14 @@ pub fn monomorphize(main: node_interner::FuncId, interner: &NodeInterner) -> Pro } let functions = vecmap(monomorphizer.finished_functions, |(_, f)| f); - let FuncMeta { return_distinctness, .. } = interner.function_meta(&main); - Program::new(functions, function_sig, return_distinctness, monomorphizer.return_location) + let FuncMeta { return_distinctness, return_visibility, .. } = interner.function_meta(&main); + Program::new( + functions, + function_sig, + return_distinctness, + monomorphizer.return_location, + return_visibility, + ) } impl<'interner> Monomorphizer<'interner> { diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 4c0500a0599..cc85fe88205 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -1034,10 +1034,18 @@ fn parenthesized_type( } fn optional_visibility() -> impl NoirParser { - keyword(Keyword::Pub).or_not().map(|opt| match opt { - Some(_) => Visibility::Public, - None => Visibility::Private, - }) + keyword(Keyword::Pub) + .or(keyword(Keyword::CallData)) + .or(keyword(Keyword::ReturnData)) + .or_not() + .map(|opt| match opt { + Some(Token::Keyword(Keyword::Pub)) => Visibility::Public, + Some(Token::Keyword(Keyword::CallData)) | Some(Token::Keyword(Keyword::ReturnData)) => { + Visibility::DataBus + } + None => Visibility::Private, + _ => unreachable!("unexpected token found"), + }) } fn optional_distinctness() -> impl NoirParser { diff --git a/docs/docs/language_concepts/12_data_bus.md b/docs/docs/language_concepts/12_data_bus.md new file mode 100644 index 00000000000..9ef5c33fcf6 --- /dev/null +++ b/docs/docs/language_concepts/12_data_bus.md @@ -0,0 +1,23 @@ +--- +title: Data Bus +--- +**Disclaimer** this feature is experimental, do not use it! + +The data bus is an optimization that the backend can use to make recursion more efficient. +In order to use it, you must define some inputs of the program entry points (usually the `main()` +function) with the `call_data` modifier, and the return values with the `return_data` modifier. +These modifiers are incompatible with `pub` and `mut` modifiers. + + +## Example + + +```rust +fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { + let a = z[x]; + a+y +} +``` + +As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. + diff --git a/tooling/nargo_cli/src/cli/fs/inputs.rs b/tooling/nargo_cli/src/cli/fs/inputs.rs index f3f0baf10f4..023195010ac 100644 --- a/tooling/nargo_cli/src/cli/fs/inputs.rs +++ b/tooling/nargo_cli/src/cli/fs/inputs.rs @@ -73,7 +73,7 @@ mod tests { use nargo::constants::VERIFIER_INPUT_FILE; use noirc_abi::{ input_parser::{Format, InputValue}, - Abi, AbiParameter, AbiType, AbiVisibility, + Abi, AbiParameter, AbiReturnType, AbiType, AbiVisibility, }; use tempfile::TempDir; @@ -98,7 +98,10 @@ mod tests { visibility: AbiVisibility::Private, }, ], - return_type: Some(AbiType::Field), + return_type: Some(AbiReturnType { + abi_type: AbiType::Field, + visibility: AbiVisibility::Public, + }), // Input serialization is only dependent on types, not position in witness map. // Neither of these should be relevant so we leave them empty. diff --git a/tooling/nargo_cli/tests/execution_success/databus/Nargo.toml b/tooling/nargo_cli/tests/execution_success/databus/Nargo.toml new file mode 100644 index 00000000000..72360f7aefe --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/databus/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "databus" +type = "bin" +authors = [""] + +[dependencies] diff --git a/tooling/nargo_cli/tests/execution_success/databus/Prover.toml b/tooling/nargo_cli/tests/execution_success/databus/Prover.toml new file mode 100644 index 00000000000..2d034508dd8 --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/databus/Prover.toml @@ -0,0 +1,3 @@ +x = "3" +y = "4" +z = [1,2,3,4] diff --git a/tooling/nargo_cli/tests/execution_success/databus/src/main.nr b/tooling/nargo_cli/tests/execution_success/databus/src/main.nr new file mode 100644 index 00000000000..631331ef2d7 --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/databus/src/main.nr @@ -0,0 +1,10 @@ +// Test unsafe integer multiplication with overflow: 12^8 = 429 981 696 +// The circuit should handle properly the growth of the bit size +use dep::std; + +fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { + +let a = z[x]; + a+y + +} diff --git a/tooling/nargo_fmt/src/utils.rs b/tooling/nargo_fmt/src/utils.rs index 4fbc1e8f85b..1160f01972f 100644 --- a/tooling/nargo_fmt/src/utils.rs +++ b/tooling/nargo_fmt/src/utils.rs @@ -254,6 +254,7 @@ impl Item for Param { let visibility = match self.visibility { Visibility::Public => "pub ", Visibility::Private => "", + Visibility::DataBus => "call_data", }; let pattern = visitor.slice(self.pattern.span()); let ty = rewrite::typ(visitor, shape, self.typ); diff --git a/tooling/noir_codegen/src/noir_types.ts b/tooling/noir_codegen/src/noir_types.ts index d1a22a3e2da..ba4f8650b3b 100644 --- a/tooling/noir_codegen/src/noir_types.ts +++ b/tooling/noir_codegen/src/noir_types.ts @@ -170,7 +170,7 @@ export function generateTsInterface( // Generating Return type, if it exists if (abiObj.return_type != null) { - result += generateStructInterfaces(abiObj.return_type, outputStructs, primitiveTypeMap); + result += generateStructInterfaces(abiObj.return_type.abi_type, outputStructs, primitiveTypeMap); } return [result, getTsFunctionSignature(abiObj, primitiveTypeMap)]; @@ -184,6 +184,6 @@ function getTsFunctionSignature( param.name, abiTypeToTs(param.type, primitiveTypeMap), ]); - const returnValue = abi.return_type ? abiTypeToTs(abi.return_type, primitiveTypeMap) : null; + const returnValue = abi.return_type ? abiTypeToTs(abi.return_type.abi_type, primitiveTypeMap) : null; return { inputs, returnValue }; } diff --git a/tooling/noir_codegen/test/assert_lt/target/assert_lt.json b/tooling/noir_codegen/test/assert_lt/target/assert_lt.json index 6d928a26d43..a1ab87a99fe 100644 --- a/tooling/noir_codegen/test/assert_lt/target/assert_lt.json +++ b/tooling/noir_codegen/test/assert_lt/target/assert_lt.json @@ -1 +1 @@ -{"noir_version":"0.19.3+e9322d14070fa444d77ee5c43c905dd86a67c6e3","hash":9449934793688855780,"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"},{"name":"array","type":{"kind":"array","length":5,"type":{"kind":"integer","sign":"unsigned","width":8}},"visibility":"private"},{"name":"my_struct","type":{"kind":"struct","path":"MyStruct","fields":[{"name":"foo","type":{"kind":"boolean"}},{"name":"bar","type":{"kind":"array","length":3,"type":{"kind":"string","length":5}}}]},"visibility":"private"},{"name":"string","type":{"kind":"string","length":5},"visibility":"private"}],"param_witnesses":{"array":[{"start":3,"end":8}],"my_struct":[{"start":8,"end":24}],"string":[{"start":24,"end":29}],"x":[{"start":1,"end":2}],"y":[{"start":2,"end":3}]},"return_type":{"kind":"tuple","fields":[{"kind":"integer","sign":"unsigned","width":64},{"kind":"integer","sign":"unsigned","width":64},{"kind":"struct","path":"MyStruct","fields":[{"name":"foo","type":{"kind":"boolean"}},{"name":"bar","type":{"kind":"array","length":3,"type":{"kind":"string","length":5}}}]}]},"return_witnesses":[31,32,33,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]},"bytecode":"H4sIAAAAAAAA/81XbU/CMBDu5hv4gopvvGw49JOJH1q2wfaN+E+AddFEgzGL/H250Go5dInumnhJ0z2jXJ9er7s+t4yxe7YyZ9lc1Y8N7CK8tWw1A28jvIPwLsJ7Cus5mfIPxquZqBlzmX5DPowiORpIEYoJH6TTJOZRPB0mIhFxEmeDJAxlEiWjdJqOeCqiUIo8TsNcOa7RceQ6DnUUl32EDxA+RPgI4QbCxwifIHyKcBPhM4TPEb5A+BLhK4RbCLcR7iDcRdhjX3mjzUb+jIlyxibPFgFPmYNlVnm2yXjOcps8O3Q8pU2eXTqemU2eHh3PGdQbl22aS8zZYXRn3/07L4FffLN0Mt9mXH3V99iqhuu80GOgzj+wzZxxjGdXjXFLxjg/+Kkb7/T/G8bvVRe/EQxzciqfvgok9QXEp+P4eQHpGT61bRHHw9ahqurrhjCeZfH7JU+OeAqfcM09wn2tEL/SD9x/Pjdl+8yr2do54dVMUJ6Ta0b/3TF92tr3gI53aJNnn3DfuwZHyE8o2FDIQYBr0Q1FFoQmiEsQlCAiociCWASBCKIQhCCIPxB8IPJA2IGYA9EBF3q4LMNcHlsv/E31XGUOyI1g2fpsvfDfqd5T/aQo5MtrERTzYJJlweKpeAzm7/Itf54vPgBYg2KL1RAAAA=="} \ No newline at end of file +{"noir_version":"0.19.4+55670ff82c270534a4bdb999ab0de5cea7017093","hash":11505576107297330043,"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"},{"name":"array","type":{"kind":"array","length":5,"type":{"kind":"integer","sign":"unsigned","width":8}},"visibility":"private"},{"name":"my_struct","type":{"kind":"struct","path":"MyStruct","fields":[{"name":"foo","type":{"kind":"boolean"}},{"name":"bar","type":{"kind":"array","length":3,"type":{"kind":"string","length":5}}}]},"visibility":"private"},{"name":"string","type":{"kind":"string","length":5},"visibility":"private"}],"param_witnesses":{"array":[{"start":3,"end":8}],"my_struct":[{"start":8,"end":24}],"string":[{"start":24,"end":29}],"x":[{"start":1,"end":2}],"y":[{"start":2,"end":3}]},"return_type":{"abi_type":{"kind":"tuple","fields":[{"kind":"integer","sign":"unsigned","width":64},{"kind":"integer","sign":"unsigned","width":64},{"kind":"struct","path":"MyStruct","fields":[{"name":"foo","type":{"kind":"boolean"}},{"name":"bar","type":{"kind":"array","length":3,"type":{"kind":"string","length":5}}}]}]},"visibility":"public"},"return_witnesses":[31,32,33,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]},"bytecode":"H4sIAAAAAAAA/81XbU/CMBDu5hv4gopvvGw49JOJH1q2wfaN+E+AddFEgzGL/H250Go5dInumnhJ0z2jXJ9er7s+t4yxe7YyZ9lc1Y8N7CK8tWw1A28jvIPwLsJ7Cus5mfIPxquZqBlzmX5DPowiORpIEYoJH6TTJOZRPB0mIhFxEmeDJAxlEiWjdJqOeCqiUIo8TsNcOa7RceQ6DnUUl32EDxA+RPgI4QbCxwifIHyKcBPhM4TPEb5A+BLhK4RbCLcR7iDcRdhjX3mjzUb+jIlyxibPFgFPmYNlVnm2yXjOcps8O3Q8pU2eXTqemU2eHh3PGdQbl22aS8zZYXRn3/07L4FffLN0Mt9mXH3V99iqhuu80GOgzj+wzZxxjGdXjXFLxjg/+Kkb7/T/G8bvVRe/EQxzciqfvgok9QXEp+P4eQHpGT61bRHHw9ahqurrhjCeZfH7JU+OeAqfcM09wn2tEL/SD9x/Pjdl+8yr2do54dVMUJ6Ta0b/3TF92tr3gI53aJNnn3DfuwZHyE8o2FDIQYBr0Q1FFoQmiEsQlCAiociCWASBCKIQhCCIPxB8IPJA2IGYA9EBF3q4LMNcHlsv/E31XGUOyI1g2fpsvfDfqd5T/aQo5MtrERTzYJJlweKpeAzm7/Itf54vPgBYg2KL1RAAAA=="} \ No newline at end of file diff --git a/tooling/noir_js/test/noir_compiled_examples/assert_lt/target/assert_lt.json b/tooling/noir_js/test/noir_compiled_examples/assert_lt/target/assert_lt.json index c01b2c5d3f5..7c9b30daa9e 100644 --- a/tooling/noir_js/test/noir_compiled_examples/assert_lt/target/assert_lt.json +++ b/tooling/noir_js/test/noir_compiled_examples/assert_lt/target/assert_lt.json @@ -1 +1 @@ -{"hash":13834844072603749544,"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"}],"param_witnesses":{"x":[{ "start": 1, "end": 2 }],"y":[{ "start": 2, "end": 3 }]},"return_type":{"kind":"integer","sign":"unsigned","width":64},"return_witnesses":[12]},"bytecode":"H4sIAAAAAAAA/+1WUW6DMAx1QksZoGr72jUcAiX8VbvJ0Oj9j7ChJpKbtXw0NpvUWkImUXixn53w3gDgHc6mfh7t/ZGMtR9TU96HeYuHtp36ZjLWfGIzjK7DthsPzjjTue6rcdZOrnX9MA49Dqa1kzl1gz3h2bL7sTDCMhmJbylmTDOT8WEhjXfjH/DcB8u8zwVygWifmL/9lTnWzSWKsxHA3QJf00vlveWvERJIUU4x0eb86aEJppljVox9oO+Py8QTV1Jnw6a85t7vSL8pwvN89j7gd88o8q79Gr2wRt3AeSFz4XvRSyokl5MAtSfgGO2ZCewdsDibLRVrDzIXTMxfqiLIGXPeMdY1gb/Fg8+tznJY50eSGmfB2DNrqciCD+tCRc4X5FNFJmIWnkhu3BL+t4qc8y75aySqIkvGOP9CRWKaGQ0ydUrsgUUVWXlfw4OpyAouVWQN66pITDPDqSJfQaZxuVVkxZhzzVgLTv5uHbDwXhN+vwGywklHPBQAAA=="} \ No newline at end of file +{"noir_version":"0.19.4+55670ff82c270534a4bdb999ab0de5cea7017093","hash":4729344722355302200,"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"x","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"private"},{"name":"y","type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"}],"param_witnesses":{"x":[{"start":1,"end":2}],"y":[{"start":2,"end":3}]},"return_type":{"abi_type":{"kind":"integer","sign":"unsigned","width":64},"visibility":"public"},"return_witnesses":[5]},"bytecode":"H4sIAAAAAAAA/81V4WrDIBDWmGxsP/YE+5FH8KI2+q/0TRJi2GAjY8j6+itM4eraQOsVeiCnF/1y9513PjLGntmf8MOoot6idRXXIs5zSbZt1LJMgCMsJTda+77zoGCQnRutkdqMGwsWjDVTZ5XyVtveja6XDrTyMBun5ghWXe8X5IYToZNhY15F1PVhNFlukn2H9nOkOcrJDp05tYefwXlCtnT+BX0vDf4fGfjnVJgiEpnjll4oQeejTEmtEWYSQczHrYqqFOuBkM81/i70U2Z+giCMuSbMawF/qw3unutmLc+yTI7qRJYJUNZJw+j7TsI894CkecOOm396IF6jHkLwn1+hDUs7TFO7fw9v7fLjv+ePZf8LJenp6WEIAAA="} \ No newline at end of file diff --git a/tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts b/tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts index 98189eaed5f..dab1c56436a 100644 --- a/tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts +++ b/tooling/noir_js_backend_barretenberg/test/public_input_deflattening.test.ts @@ -38,18 +38,21 @@ const abi: Abi = { ], }, return_type: { - kind: 'tuple', - fields: [ - { - kind: 'field', - }, - { - kind: 'field', - }, - { - kind: 'field', - }, - ], + abi_type: { + kind: 'tuple', + fields: [ + { + kind: 'field', + }, + { + kind: 'field', + }, + { + kind: 'field', + }, + ], + }, + visibility: 'public', }, return_witnesses: [2, 13, 13], }; diff --git a/tooling/noirc_abi/src/input_parser/json.rs b/tooling/noirc_abi/src/input_parser/json.rs index e2b1a83ee6b..7618cd6c15a 100644 --- a/tooling/noirc_abi/src/input_parser/json.rs +++ b/tooling/noirc_abi/src/input_parser/json.rs @@ -28,8 +28,11 @@ pub(crate) fn parse_json( if let (Some(return_type), Some(json_return_value)) = (&abi.return_type, data.get(MAIN_RETURN_NAME)) { - let return_value = - InputValue::try_from_json(json_return_value.clone(), return_type, MAIN_RETURN_NAME)?; + let return_value = InputValue::try_from_json( + json_return_value.clone(), + &return_type.abi_type, + MAIN_RETURN_NAME, + )?; parsed_inputs.insert(MAIN_RETURN_NAME.to_owned(), return_value); } @@ -48,7 +51,7 @@ pub(crate) fn serialize_to_json( if let (Some(return_type), Some(return_value)) = (&abi.return_type, input_map.get(MAIN_RETURN_NAME)) { - let return_value = JsonTypes::try_from_input_value(return_value, return_type)?; + let return_value = JsonTypes::try_from_input_value(return_value, &return_type.abi_type)?; json_map.insert(MAIN_RETURN_NAME.to_owned(), return_value); } diff --git a/tooling/noirc_abi/src/input_parser/mod.rs b/tooling/noirc_abi/src/input_parser/mod.rs index 7bbcb42296c..e1265da1c85 100644 --- a/tooling/noirc_abi/src/input_parser/mod.rs +++ b/tooling/noirc_abi/src/input_parser/mod.rs @@ -130,7 +130,8 @@ mod serialization_tests { use strum::IntoEnumIterator; use crate::{ - input_parser::InputValue, Abi, AbiParameter, AbiType, AbiVisibility, Sign, MAIN_RETURN_NAME, + input_parser::InputValue, Abi, AbiParameter, AbiReturnType, AbiType, AbiVisibility, Sign, + MAIN_RETURN_NAME, }; use super::Format; @@ -159,7 +160,10 @@ mod serialization_tests { visibility: AbiVisibility::Private, }, ], - return_type: Some(AbiType::String { length: 5 }), + return_type: Some(AbiReturnType { + abi_type: AbiType::String { length: 5 }, + visibility: AbiVisibility::Public, + }), // These two fields are unused when serializing/deserializing to file. param_witnesses: BTreeMap::new(), return_witnesses: Vec::new(), diff --git a/tooling/noirc_abi/src/input_parser/toml.rs b/tooling/noirc_abi/src/input_parser/toml.rs index 645b59b00cd..b216fe58794 100644 --- a/tooling/noirc_abi/src/input_parser/toml.rs +++ b/tooling/noirc_abi/src/input_parser/toml.rs @@ -28,8 +28,11 @@ pub(crate) fn parse_toml( if let (Some(return_type), Some(toml_return_value)) = (&abi.return_type, data.get(MAIN_RETURN_NAME)) { - let return_value = - InputValue::try_from_toml(toml_return_value.clone(), return_type, MAIN_RETURN_NAME)?; + let return_value = InputValue::try_from_toml( + toml_return_value.clone(), + &return_type.abi_type, + MAIN_RETURN_NAME, + )?; parsed_inputs.insert(MAIN_RETURN_NAME.to_owned(), return_value); } @@ -48,7 +51,7 @@ pub(crate) fn serialize_to_toml( if let (Some(return_type), Some(return_value)) = (&abi.return_type, input_map.get(MAIN_RETURN_NAME)) { - let return_value = TomlTypes::try_from_input_value(return_value, return_type)?; + let return_value = TomlTypes::try_from_input_value(return_value, &return_type.abi_type)?; toml_map.insert(MAIN_RETURN_NAME.to_owned(), return_value); } diff --git a/tooling/noirc_abi/src/lib.rs b/tooling/noirc_abi/src/lib.rs index 7092f05c26e..41ef7cde628 100644 --- a/tooling/noirc_abi/src/lib.rs +++ b/tooling/noirc_abi/src/lib.rs @@ -78,6 +78,7 @@ pub enum AbiVisibility { // Constants are not allowed in the ABI for main at the moment. // Constant, Private, + DataBus, } impl From for AbiVisibility { @@ -85,6 +86,7 @@ impl From for AbiVisibility { match value { Visibility::Public => AbiVisibility::Public, Visibility::Private => AbiVisibility::Private, + Visibility::DataBus => AbiVisibility::DataBus, } } } @@ -94,6 +96,7 @@ impl From<&Visibility> for AbiVisibility { match value { Visibility::Public => AbiVisibility::Public, Visibility::Private => AbiVisibility::Private, + Visibility::DataBus => AbiVisibility::DataBus, } } } @@ -212,6 +215,11 @@ impl AbiParameter { } } +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct AbiReturnType { + pub abi_type: AbiType, + pub visibility: AbiVisibility, +} #[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. @@ -219,7 +227,7 @@ pub struct Abi { /// 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>>, - pub return_type: Option, + pub return_type: Option, pub return_witnesses: Vec, } @@ -327,7 +335,7 @@ impl Abi { // When encoding public inputs to be passed to the verifier, the user can must provide a return value // to be inserted into the witness map. This is not needed when generating a witness when proving the circuit. match (&self.return_type, return_value) { - (Some(return_type), Some(return_value)) => { + (Some(AbiReturnType { abi_type: return_type, .. }), Some(return_value)) => { if !return_value.matches_abi(return_type) { return Err(AbiError::ReturnTypeMismatch { return_type: return_type.clone(), @@ -426,7 +434,7 @@ impl Abi { .copied() }) { - Some(decode_value(&mut return_witness_values.into_iter(), return_type)?) + Some(decode_value(&mut return_witness_values.into_iter(), &return_type.abi_type)?) } else { // Unlike for the circuit inputs, we tolerate not being able to find the witness values for the return value. // This is because the user may be decoding a partial witness map for which is hasn't been calculated yet. @@ -546,7 +554,10 @@ mod test { use acvm::{acir::native_types::Witness, FieldElement}; - use crate::{input_parser::InputValue, Abi, AbiParameter, AbiType, AbiVisibility, InputMap}; + use crate::{ + input_parser::InputValue, Abi, AbiParameter, AbiReturnType, AbiType, AbiVisibility, + InputMap, + }; #[test] fn witness_encoding_roundtrip() { @@ -568,7 +579,10 @@ mod test { ("thing1".to_string(), vec![(Witness(1)..Witness(3))]), ("thing2".to_string(), vec![(Witness(3)..Witness(4))]), ]), - return_type: Some(AbiType::Field), + return_type: Some(AbiReturnType { + abi_type: AbiType::Field, + visibility: AbiVisibility::Public, + }), return_witnesses: vec![Witness(3)], }; diff --git a/tooling/noirc_abi_wasm/src/lib.rs b/tooling/noirc_abi_wasm/src/lib.rs index a3d829dd40f..fb4c295b8c8 100644 --- a/tooling/noirc_abi_wasm/src/lib.rs +++ b/tooling/noirc_abi_wasm/src/lib.rs @@ -47,7 +47,7 @@ extern "C" { #[wasm_bindgen(typescript_custom_section)] const ABI: &'static str = r#" -export type Visibility = "public" | "private"; +export type Visibility = "public" | "private" | "databus"; export type Sign = "unsigned" | "signed"; export type AbiType = { kind: "field" } | @@ -67,7 +67,7 @@ export type AbiParameter = { export type Abi = { parameters: AbiParameter[], param_witnesses: Record, - return_type: AbiType | null, + return_type: {abi_type: AbiType, visibility: Visibility} | null, return_witnesses: number[], } "#; @@ -96,7 +96,7 @@ pub fn abi_encode( .expect("could not decode return value"); InputValue::try_from_json( toml_return_value, - abi.return_type.as_ref().unwrap(), + &abi.return_type.as_ref().unwrap().abi_type, MAIN_RETURN_NAME, ) }) @@ -134,7 +134,7 @@ pub fn abi_decode(abi: JsAbi, witness_map: JsWitnessMap) -> Result