diff --git a/.github/workflows/auto-pr-rebuild-script.yml b/.github/workflows/auto-pr-rebuild-script.yml index d81c9820c3e..4937de98e04 100644 --- a/.github/workflows/auto-pr-rebuild-script.yml +++ b/.github/workflows/auto-pr-rebuild-script.yml @@ -11,7 +11,7 @@ jobs: name: Check if artifacts should be published runs-on: ubuntu-22.04 outputs: - publish: ${{ steps.check.outputs.publish }} + publish: ${{ steps.check.outputs.result }} steps: - name: Check if artifacts should be published @@ -22,20 +22,24 @@ jobs: script: | const { REF_NAME } = process.env; if (REF_NAME == "master") { + console.log(`publish = true`) return true; } const labels = context.payload.pull_request.labels.map(label => label.name); - return labels.includes('publish-acir'); + const publish = labels.includes('publish-acir'); + + console.log(`publish = ${publish}`) + return publish; result-encoding: string env: REF_NAME: ${{ github.ref_name }} build-nargo: name: Build nargo binary + if: ${{ needs.check-artifacts-requested.outputs.publish == 'true' }} runs-on: ubuntu-22.04 needs: [check-artifacts-requested] - if: ${{ needs.check-artifacts-requested.outputs.publish == true }} strategy: matrix: target: [x86_64-unknown-linux-gnu] @@ -76,7 +80,7 @@ jobs: steps: - name: Check out code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Download nargo binary uses: actions/download-artifact@v3 @@ -112,7 +116,6 @@ jobs: if: ${{ github.ref_name }} == "master" run: | git diff --quiet tooling/nargo_cli/tests/acir_artifacts/ || echo "::set-output name=changes::true" - - name: Create or Update PR if: steps.check_changes.outputs.changes == 'true' diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index cdebb2b8bcc..227792d9617 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -2,6 +2,7 @@ name: Build docs on: pull_request: + workflow_dispatch: jobs: add_label: @@ -62,11 +63,37 @@ jobs: with: node-version: '18' + - name: Install wasm-bindgen-cli + uses: taiki-e/install-action@v2 + with: + tool: wasm-bindgen-cli@0.2.86 + + - name: Install wasm-opt + run: | + npm i wasm-opt -g + - name: Install dependencies run: yarn - + + - name: Build acvm_js + run: yarn workspace @noir-lang/acvm_js build + + - name: Build noirc_abi + run: yarn workspace @noir-lang/noirc_abi build + + - name: Build noir_js_types + run: yarn workspace @noir-lang/types build + + - name: Build barretenberg wrapper + run: yarn workspace @noir-lang/backend_barretenberg build + + - name: Run noir_js + run: | + yarn workspace @noir-lang/noir_js build + - name: Build docs - run: yarn workspace docs build + run: + yarn workspace docs build - name: Remove pre-releases working-directory: docs diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 4c20c9181db..9bd9e3b747a 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -13,12 +13,17 @@ jobs: if: ${{ inputs.tag != '' }} permissions: pull-requests: write + contents: write steps: - name: Checkout sources uses: actions/checkout@v4 with: ref: ${{ inputs.tag }} + - name: Create new branch + run: | + git checkout -b new-docs-version-${{ github.event.inputs.tag }} + - name: Setup Node.js uses: actions/setup-node@v2 with: @@ -33,12 +38,33 @@ jobs: - name: Cut a new version working-directory: ./docs run: yarn docusaurus docs:version ${{ inputs.tag }} - + - name: Remove pre-releases id: get_version run: | cd docs && yarn setStable + - name: Commit new documentation version + run: | + git config --local user.name 'signorecello' + git config --local user.email 'github@zepedro.me' + git add . + git commit -m "chore(docs): cut new docs version for tag ${{ github.event.inputs.tag }}" + + - name: Push changes to new branch + run: git push origin new-docs-version-${{ github.event.inputs.tag }} + + - name: Create Pull Request + run: | + gh pr create \ + --title "chore(docs): docs for ${{ github.event.inputs.tag }}" \ + --body "Updates documentation to new version for tag ${{ github.event.inputs.tag }}." \ + --base master \ + --head new-docs-version-${{ github.event.inputs.tag }} \ + --label documentation + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Build docs run: yarn workspace docs build @@ -55,3 +81,4 @@ jobs: NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} timeout-minutes: 1 + diff --git a/compiler/noirc_evaluator/src/errors.rs b/compiler/noirc_evaluator/src/errors.rs index 2429ca9e194..42672d4d0ad 100644 --- a/compiler/noirc_evaluator/src/errors.rs +++ b/compiler/noirc_evaluator/src/errors.rs @@ -63,16 +63,20 @@ impl From for FileDiagnostic { fn from(error: SsaReport) -> FileDiagnostic { match error { SsaReport::Warning(warning) => { - let InternalWarning::ReturnConstant { ref call_stack } = warning; - let call_stack = vecmap(call_stack, |location| *location); - let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); let message = warning.to_string(); + let (secondary_message, call_stack) = match warning { + InternalWarning::ReturnConstant { call_stack } => { + ("constant value".to_string(), call_stack) + }, + InternalWarning::VerifyProof { call_stack } => { + ("verify_proof(...) aggregates data for the verifier, the actual verification will be done when the full proof is verified using nargo verify. nargo prove may generate an invalid proof if bad data is used as input to verify_proof".to_string(), call_stack) + }, + }; + let call_stack = vecmap(call_stack, |location| location); + let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); let location = call_stack.last().expect("Expected RuntimeError to have a location"); - let diagnostic = Diagnostic::simple_warning( - message, - "constant value".to_string(), - location.span, - ); + let diagnostic = + Diagnostic::simple_warning(message, secondary_message, location.span); diagnostic.in_file(file_id).with_call_stack(call_stack) } } @@ -83,6 +87,8 @@ impl From for FileDiagnostic { pub enum InternalWarning { #[error("Returning a constant value is not allowed")] ReturnConstant { call_stack: CallStack }, + #[error("Calling std::verify_proof(...) does not verify a proof")] + VerifyProof { call_stack: CallStack }, } #[derive(Debug, PartialEq, Eq, Clone, Error)] diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index ff13878e129..24521b98bd1 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -7,9 +7,15 @@ //! This module heavily borrows from Cranelift #![allow(dead_code)] -use std::collections::BTreeSet; +use std::{ + collections::{BTreeMap, BTreeSet}, + ops::Range, +}; -use crate::errors::{RuntimeError, SsaReport}; +use crate::{ + brillig::Brillig, + errors::{RuntimeError, SsaReport}, +}; use acvm::acir::{ circuit::{Circuit, PublicInputs}, native_types::Witness, @@ -39,7 +45,8 @@ pub(crate) fn optimize_into_acir( print_brillig_trace: bool, ) -> Result { let abi_distinctness = program.return_distinctness; - let ssa = SsaBuilder::new(program, print_ssa_passes)? + + let ssa_builder = SsaBuilder::new(program, print_ssa_passes)? .run_pass(Ssa::defunctionalize, "After Defunctionalization:") .run_pass(Ssa::inline_functions, "After Inlining:") // Run mem2reg with the CFG separated into blocks @@ -56,10 +63,17 @@ pub(crate) fn optimize_into_acir( // Run mem2reg once more with the flattened CFG to catch any remaining loads/stores .run_pass(Ssa::mem2reg, "After Mem2Reg:") .run_pass(Ssa::fold_constants, "After Constant Folding:") - .run_pass(Ssa::dead_instruction_elimination, "After Dead Instruction Elimination:") + .run_pass(Ssa::dead_instruction_elimination, "After Dead Instruction Elimination:"); + + let brillig = ssa_builder.to_brillig(print_brillig_trace); + + // Split off any passes the are not necessary for Brillig generation but are necessary for ACIR generation. + // We only need to fill out nested slices as we need to have a known length when dealing with memory operations + // in ACIR gen while this is not necessary in the Brillig IR. + let ssa = ssa_builder + .run_pass(Ssa::fill_internal_slices, "After Fill Internal Slice Dummy Data:") .finish(); - let brillig = ssa.to_brillig(print_brillig_trace); let last_array_uses = ssa.find_last_array_uses(); ssa.into_acir(brillig, abi_distinctness, &last_array_uses) } @@ -87,14 +101,12 @@ pub fn create_circuit( .. } = generated_acir; - let abi = gen_abi(context, func_sig, &input_witnesses, return_witnesses.clone()); + let abi = gen_abi(context, func_sig, input_witnesses, return_witnesses.clone()); let public_abi = abi.clone().public_abi(); - let public_parameters = - PublicInputs(public_abi.param_witnesses.values().flatten().copied().collect()); + let public_parameters = PublicInputs(tree_to_set(&public_abi.param_witnesses)); - let all_parameters: BTreeSet = - abi.param_witnesses.values().flatten().copied().collect(); + let all_parameters: BTreeSet = tree_to_set(&abi.param_witnesses); let private_parameters = all_parameters.difference(&public_parameters.0).copied().collect(); let return_values = PublicInputs(return_witnesses.into_iter().collect()); @@ -155,6 +167,10 @@ impl SsaBuilder { Ok(self.print(msg)) } + fn to_brillig(&self, print_brillig_trace: bool) -> Brillig { + self.ssa.to_brillig(print_brillig_trace) + } + fn print(self, msg: &str) -> Self { if self.print_ssa_passes { println!("{msg}\n{}", self.ssa); @@ -162,3 +178,15 @@ impl SsaBuilder { self } } + +// Flatten the witnesses in the map into a BTreeSet +fn tree_to_set(input: &BTreeMap>>) -> BTreeSet { + let mut result = BTreeSet::new(); + for range in input.values().flatten() { + for i in range.start.witness_index()..range.end.witness_index() { + result.insert(Witness(i)); + } + } + + result +} diff --git a/compiler/noirc_evaluator/src/ssa/abi_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/abi_gen/mod.rs index e4b4026bf21..948fb7d5263 100644 --- a/compiler/noirc_evaluator/src/ssa/abi_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/abi_gen/mod.rs @@ -11,6 +11,7 @@ use noirc_frontend::{ }, node_interner::NodeInterner, }; +use std::ops::Range; /// Attempts to retrieve the name of this parameter. Returns None /// if this parameter is a tuple or struct pattern. @@ -38,7 +39,7 @@ pub fn into_abi_params(context: &Context, params: Vec) -> Vec>, return_witnesses: Vec, ) -> Abi { let (parameters, return_type) = func_sig; @@ -52,16 +53,33 @@ pub(crate) fn gen_abi( // parameter's constituent values live. fn param_witnesses_from_abi_param( abi_params: &Vec, - input_witnesses: &[Witness], -) -> BTreeMap> { + input_witnesses: Vec>, +) -> BTreeMap>> { let mut idx = 0_usize; + if input_witnesses.is_empty() { + return BTreeMap::new(); + } + let mut processed_range = input_witnesses[idx].start.witness_index(); btree_map(abi_params, |param| { let num_field_elements_needed = param.typ.field_count(); let mut wit = Vec::new(); - for _ in 0..num_field_elements_needed { - wit.push(input_witnesses[idx]); - idx += 1; + let mut processed_fields = 0; + while processed_fields < num_field_elements_needed { + let end = input_witnesses[idx].end.witness_index(); + if num_field_elements_needed <= end - processed_range { + wit.push( + Witness(processed_range)..Witness(processed_range + num_field_elements_needed), + ); + processed_range += num_field_elements_needed; + processed_fields += num_field_elements_needed; + } else { + // consume the current range + wit.push(Witness(processed_range)..input_witnesses[idx].end); + processed_fields += end - processed_range; + idx += 1; + processed_range = input_witnesses[idx].start.witness_index(); + } } (param.name.clone(), wit) }) diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 908189ad785..4ee70ed9b22 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -23,6 +23,7 @@ use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError}; use fxhash::FxHashMap as HashMap; use iter_extended::{try_vecmap, vecmap}; use num_bigint::BigUint; +use std::ops::RangeInclusive; use std::{borrow::Cow, hash::Hash}; #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -893,7 +894,9 @@ impl AcirContext { return Ok(variable); } } - let witness = self.var_to_witness(variable)?; + + let witness_var = self.get_or_create_witness_var(variable)?; + let witness = self.var_to_witness(witness_var)?; self.acir_ir.range_constraint(witness, *bit_size)?; if let Some(message) = message { self.acir_ir @@ -1082,7 +1085,8 @@ impl AcirContext { // Intrinsics only accept Witnesses. This is not a limitation of the // intrinsics, its just how we have defined things. Ideally, we allow // constants too. - let witness = self.var_to_witness(input)?; + let witness_var = self.get_or_create_witness_var(input)?; + let witness = self.var_to_witness(witness_var)?; let num_bits = typ.bit_size(); single_val_witnesses.push(FunctionInput { witness, num_bits }); } @@ -1176,8 +1180,29 @@ impl AcirContext { } /// Terminates the context and takes the resulting `GeneratedAcir` - pub(crate) fn finish(mut self, inputs: Vec, warnings: Vec) -> GeneratedAcir { - self.acir_ir.input_witnesses = vecmap(inputs, Witness); + pub(crate) fn finish( + mut self, + inputs: Vec>, + warnings: Vec, + ) -> GeneratedAcir { + let mut current_range = 0..0; + for range in inputs { + if current_range.end == *range.start() { + current_range.end = range.end() + 1; + } else { + if current_range.end != 0 { + self.acir_ir + .input_witnesses + .push(Witness(current_range.start)..Witness(current_range.end)); + } + current_range = *range.start()..range.end() + 1; + } + } + if current_range.end != 0 { + self.acir_ir + .input_witnesses + .push(Witness(current_range.start)..Witness(current_range.end)); + } self.acir_ir.warnings = warnings; self.acir_ir } diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index f29d3c9ec05..9bd83096adf 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -23,6 +23,7 @@ use acvm::{ }; use iter_extended::vecmap; use num_bigint::BigUint; +use std::ops::Range; #[derive(Debug, Default)] /// The output of the Acir-gen pass @@ -42,7 +43,7 @@ pub(crate) struct GeneratedAcir { pub(crate) return_witnesses: Vec, /// All witness indices which are inputs to the main function - pub(crate) input_witnesses: Vec, + pub(crate) input_witnesses: Vec>, /// Correspondence between an opcode index (in opcodes) and the source code call stack which generated it pub(crate) locations: BTreeMap, diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 35b970f1e06..a849ae59e50 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -25,6 +25,8 @@ use crate::brillig::brillig_ir::BrilligContext; use crate::brillig::{brillig_gen::brillig_fn::FunctionContext as BrilligFunctionContext, Brillig}; use crate::errors::{InternalError, InternalWarning, RuntimeError, SsaReport}; pub(crate) use acir_ir::generated_acir::GeneratedAcir; + +use acvm::acir::BlackBoxFunc; use acvm::{ acir::{circuit::opcodes::BlockId, native_types::Expression}, FieldElement, @@ -71,9 +73,23 @@ struct Context { /// which utilizes this internal memory for ACIR generation. internal_memory_blocks: HashMap, BlockId>, + /// Maps an internal memory block to its length + /// + /// This is necessary to keep track of an internal memory block's size. + /// We do not need a separate map to keep track of `memory_blocks` as + /// the length is set when we construct a `AcirValue::DynamicArray` and is tracked + /// as part of the `AcirValue` in the `ssa_values` map. + /// The length of an internal memory block is determined before an array operation + /// takes place thus we track it separate here in this map. + internal_mem_block_lengths: HashMap, + /// Number of the next BlockId, it is used to construct /// a new BlockId max_block_id: u32, + + /// 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>, } #[derive(Clone)] @@ -168,7 +184,9 @@ impl Context { initialized_arrays: HashSet::new(), memory_blocks: HashMap::default(), internal_memory_blocks: HashMap::default(), + internal_mem_block_lengths: HashMap::default(), max_block_id: 0, + slice_sizes: HashMap::default(), } } @@ -196,14 +214,19 @@ 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)?; - + let mut warnings = Vec::new(); for instruction_id in entry_block.instructions() { - self.convert_ssa_instruction(*instruction_id, dfg, ssa, &brillig, last_array_uses)?; + warnings.extend(self.convert_ssa_instruction( + *instruction_id, + dfg, + ssa, + &brillig, + last_array_uses, + )?); } - let warnings = self.convert_ssa_return(entry_block.unwrap_terminator(), dfg)?; - - Ok(self.acir_context.finish(input_witness.collect(), warnings)) + warnings.extend(self.convert_ssa_return(entry_block.unwrap_terminator(), dfg)?); + Ok(self.acir_context.finish(vec![input_witness], warnings)) } fn convert_brillig_main( @@ -239,8 +262,8 @@ impl Context { for acir_var in output_vars { self.acir_context.return_var(acir_var)?; } - - Ok(self.acir_context.finish(witness_inputs, Vec::new())) + let witnesses = vecmap(witness_inputs, |input| RangeInclusive::new(input, input)); + Ok(self.acir_context.finish(witnesses, Vec::new())) } /// Adds and binds `AcirVar`s for each numeric block parameter or block parameter array element. @@ -360,9 +383,10 @@ impl Context { ssa: &Ssa, brillig: &Brillig, last_array_uses: &HashMap, - ) -> Result<(), RuntimeError> { + ) -> Result, RuntimeError> { let instruction = &dfg[instruction_id]; self.acir_context.set_call_stack(dfg.get_call_stack(instruction_id)); + let mut warnings = Vec::new(); match instruction { Instruction::Binary(binary) => { let result_acir_var = self.convert_ssa_binary(binary, dfg)?; @@ -407,7 +431,9 @@ impl Context { let rhs_var = read_from_index(rhs_block_id, i)?; Ok((lhs_var, rhs_var)) }), - _ => unreachable!("ICE: lhs and rhs should be of the same type"), + _ => { + unreachable!("ICE: lhs and rhs should be of the same type") + } } } @@ -463,6 +489,14 @@ impl Context { } } Value::Intrinsic(intrinsic) => { + if matches!( + intrinsic, + Intrinsic::BlackBox(BlackBoxFunc::RecursiveAggregation) + ) { + warnings.push(SsaReport::Warning(InternalWarning::VerifyProof { + call_stack: self.acir_context.get_call_stack(), + })); + } let outputs = self .convert_ssa_intrinsic_call(*intrinsic, arguments, dfg, result_ids)?; @@ -485,7 +519,7 @@ impl Context { self.initialize_array(block_id, len, Some(output.clone()))?; } AcirValue::DynamicArray(_) => { - unreachable!("The output from an intrinsic call is expected to be a single value or an array but got {output:?}"); + // Do nothing as a dynamic array returned from a slice intrinsic should already be initialized } AcirValue::Var(_, _) => { // Do nothing @@ -539,7 +573,7 @@ impl Context { } } self.acir_context.set_call_stack(CallStack::new()); - Ok(()) + Ok(warnings) } fn gen_brillig_for( @@ -684,6 +718,11 @@ impl Context { } } Type::Slice(_) => { + // TODO(#3188): Need to be able to handle constant index for slices to seriously reduce + // constraint sizes of nested slices + // This can only be done if we accurately flatten nested slices as other we will reach + // index out of bounds errors. + // Do nothing we only want dynamic checks for slices } _ => unreachable!("ICE: expected array or slice type"), @@ -724,8 +763,24 @@ impl Context { let mut dummy_predicate_index = predicate_index; // We must setup the dummy value to match the type of the value we wish to store - let dummy = - self.array_get_value(&store_type, block_id, &mut dummy_predicate_index)?; + let slice_sizes = if store_type.contains_slice_element() { + self.compute_slice_sizes(store, None, dfg); + self.slice_sizes.get(&store).cloned().ok_or_else(|| { + InternalError::UnExpected { + expected: "Store value should have slice sizes computed".to_owned(), + found: "Missing key in slice sizes map".to_owned(), + call_stack: self.acir_context.get_call_stack(), + } + })? + } else { + vec![] + }; + let dummy = self.array_get_value( + &store_type, + block_id, + &mut dummy_predicate_index, + &slice_sizes, + )?; Some(self.convert_array_set_store_value(&store_value, &dummy)?) } @@ -821,11 +876,31 @@ impl Context { mut var_index: AcirVar, dfg: &DataFlowGraph, ) -> Result { - let (_, _, block_id) = self.check_array_is_initialized(array, dfg)?; + 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]); - let value = self.array_get_value(&res_typ, block_id, &mut var_index)?; + + let value = if !res_typ.contains_slice_element() { + self.array_get_value(&res_typ, block_id, &mut var_index, &[])? + } else { + let slice_sizes = self + .slice_sizes + .get(&array_id) + .expect("ICE: Array with slices should have associated slice sizes"); + + // The first max size is going to be the length of the parent slice + // As we are fetching from the parent slice we just want its internal + // slize sizes. + let slice_sizes = slice_sizes[1..].to_vec(); + + let value = self.array_get_value(&res_typ, block_id, &mut var_index, &slice_sizes)?; + + // Insert the resulting slice sizes + self.slice_sizes.insert(results[0], slice_sizes); + + value + }; self.define_result(dfg, instruction, value.clone()); @@ -837,6 +912,7 @@ impl Context { ssa_type: &Type, block_id: BlockId, var_index: &mut AcirVar, + slice_sizes: &[usize], ) -> Result { let one = self.acir_context.add_constant(FieldElement::one()); match ssa_type.clone() { @@ -854,20 +930,31 @@ impl Context { let mut values = Vector::new(); for _ in 0..len { for typ in element_types.as_ref() { - values.push_back(self.array_get_value(typ, block_id, var_index)?); + values.push_back(self.array_get_value( + typ, + block_id, + var_index, + slice_sizes, + )?); } } Ok(AcirValue::Array(values)) } - Type::Slice(_) => { - // TODO(#2752): need SSA values here to fetch the len like we do for a Type::Array - // Update this to enable fetching slices from nested arrays - Err(InternalError::UnExpected { - expected: "array".to_owned(), - found: ssa_type.to_string(), - call_stack: self.acir_context.get_call_stack(), + Type::Slice(element_types) => { + // It is not enough to execute this loop and simply pass the size from the parent definition. + // We need the internal sizes of each type in case of a nested slice. + let mut values = Vector::new(); + + let (current_size, new_sizes) = + slice_sizes.split_first().expect("should be able to split"); + + for _ in 0..*current_size { + for typ in element_types.as_ref() { + values + .push_back(self.array_get_value(typ, block_id, var_index, new_sizes)?); + } } - .into()) + Ok(AcirValue::Array(values)) } _ => unreachable!("ICE - expected an array or slice"), } @@ -906,13 +993,10 @@ impl Context { // However, this size is simply the capacity of a slice. The capacity is dependent upon the witness // and may contain data for which we want to restrict access. The true slice length is tracked in a // a separate SSA value and restrictions on slice indices should be generated elsewhere in the SSA. - let array_len = match &array_typ { - Type::Array(_, _) => { - // Flatten the array length to handle arrays of complex types - array_typ.flattened_size() - } - Type::Slice(_) => self.flattened_slice_size(array_id, dfg), - _ => unreachable!("ICE - expected an array"), + let array_len = if !array_typ.contains_slice_element() { + array_typ.flattened_size() + } else { + self.flattened_slice_size(array_id, dfg) }; // Since array_set creates a new array, we create a new block ID for this @@ -934,11 +1018,29 @@ impl Context { self.array_set_value(store_value, result_block_id, &mut var_index)?; - let arr_element_type_sizes = self.internal_block_id(&array_id); + // Set new resulting array to have the same slice sizes as the instruction input + if let Type::Slice(element_types) = &array_typ { + let has_internal_slices = + element_types.as_ref().iter().any(|typ| typ.contains_slice_element()); + if has_internal_slices { + let slice_sizes = self + .slice_sizes + .get(&array_id) + .expect( + "ICE: Expected array with internal slices to have associated slice sizes", + ) + .clone(); + let results = dfg.instruction_results(instruction); + self.slice_sizes.insert(results[0], slice_sizes); + } + } + + let element_type_sizes = + self.init_element_type_sizes_array(&array_typ, array_id, None, dfg)?; let result_value = AcirValue::DynamicArray(AcirDynamicArray { block_id: result_block_id, len: array_len, - element_type_sizes: arr_element_type_sizes, + element_type_sizes, }); self.define_result(dfg, instruction, result_value); Ok(()) @@ -997,7 +1099,7 @@ impl Context { match value { Value::Array { .. } | Value::Instruction { .. } => { let value = self.convert_value(array_id, dfg); - let len = if matches!(array_typ, Type::Array(_, _)) { + let len = if !array_typ.contains_slice_element() { array_typ.flattened_size() } else { self.flattened_slice_size(array_id, dfg) @@ -1021,10 +1123,13 @@ impl Context { &mut self, array_typ: &Type, array_id: ValueId, + array_acir_value: Option, dfg: &DataFlowGraph, ) -> Result { let element_type_sizes = self.internal_block_id(&array_id); // Check whether an internal type sizes array has already been initialized + // Need to look into how to optimize for slices as this could lead to different element type sizes + // for different slices that do not have consistent sizes if self.initialized_arrays.contains(&element_type_sizes) { return Ok(element_type_sizes); } @@ -1032,35 +1137,44 @@ impl Context { let mut flat_elem_type_sizes = Vec::new(); flat_elem_type_sizes.push(0); match array_typ { - Type::Array(element_types, _) => { - for (i, typ) in element_types.as_ref().iter().enumerate() { - flat_elem_type_sizes.push(typ.flattened_size() + flat_elem_type_sizes[i]); - } - } - Type::Slice(element_types) => { + Type::Array(_, _) | Type::Slice(_) => { match &dfg[array_id] { Value::Array { array, .. } => { - for i in 0..element_types.len() { + self.compute_slice_sizes(array_id, None, dfg); + + for (i, value) in array.iter().enumerate() { flat_elem_type_sizes.push( - self.flattened_slice_size(array[i], dfg) + flat_elem_type_sizes[i], + self.flattened_slice_size(*value, dfg) + flat_elem_type_sizes[i], ); } } - Value::Instruction { .. } => { + Value::Instruction { .. } | Value::Param { .. } => { // An instruction representing the slice means it has been processed previously during ACIR gen. // Use the previously defined result of an array operation to fetch the internal type information. - let array_acir_value = self.convert_value(array_id, dfg); + let array_acir_value = if let Some(array_acir_value) = array_acir_value { + array_acir_value + } else { + self.convert_value(array_id, dfg) + }; match array_acir_value { AcirValue::DynamicArray(AcirDynamicArray { element_type_sizes: inner_elem_type_sizes, .. }) => { if self.initialized_arrays.contains(&inner_elem_type_sizes) { + let type_sizes_array_len = self.internal_mem_block_lengths.get(&inner_elem_type_sizes).copied().ok_or_else(|| + InternalError::General { + message: format!("Array {array_id}'s inner element type sizes array does not have a tracked length"), + call_stack: self.acir_context.get_call_stack(), + } + )?; self.copy_dynamic_array( inner_elem_type_sizes, element_type_sizes, - element_types.len() + 1, + type_sizes_array_len, )?; + self.internal_mem_block_lengths + .insert(element_type_sizes, type_sizes_array_len); return Ok(element_type_sizes); } else { return Err(InternalError::General { @@ -1071,10 +1185,9 @@ impl Context { } } AcirValue::Array(values) => { - for i in 0..element_types.len() { + for (i, value) in values.iter().enumerate() { flat_elem_type_sizes.push( - Self::flattened_value_size(&values[i]) - + flat_elem_type_sizes[i], + Self::flattened_value_size(value) + flat_elem_type_sizes[i], ); } } @@ -1108,20 +1221,59 @@ impl Context { .into()); } } + // The final array should will the flattened index at each outer array index let init_values = vecmap(flat_elem_type_sizes, |type_size| { let var = self.acir_context.add_constant(FieldElement::from(type_size as u128)); AcirValue::Var(var, AcirType::field()) }); + let element_type_sizes_len = init_values.len(); self.initialize_array( element_type_sizes, - init_values.len(), + element_type_sizes_len, Some(AcirValue::Array(init_values.into())), )?; + self.internal_mem_block_lengths.insert(element_type_sizes, element_type_sizes_len); + Ok(element_type_sizes) } + fn compute_slice_sizes( + &mut self, + current_array_id: ValueId, + parent_array: Option, + dfg: &DataFlowGraph, + ) { + let (array, typ) = match &dfg[current_array_id] { + Value::Array { array, typ } => (array, typ.clone()), + _ => return, + }; + + if !matches!(typ, Type::Slice(_)) { + return; + } + + let element_size = typ.element_size(); + let true_len = array.len() / element_size; + if let Some(parent_array) = parent_array { + let sizes_list = + self.slice_sizes.get_mut(&parent_array).expect("ICE: expected size list"); + sizes_list.push(true_len); + for value in array { + self.compute_slice_sizes(*value, Some(parent_array), dfg); + } + } else { + // This means the current_array_id is the parent array + // The slice sizes should follow the parent array's type structure + // thus we start our sizes list with the parent array size. + self.slice_sizes.insert(current_array_id, vec![true_len]); + for value in array { + self.compute_slice_sizes(*value, Some(current_array_id), dfg); + } + } + } + fn copy_dynamic_array( &mut self, source: BlockId, @@ -1145,31 +1297,15 @@ impl Context { var_index: AcirVar, dfg: &DataFlowGraph, ) -> Result { - let element_type_sizes = self.init_element_type_sizes_array(array_typ, array_id, dfg)?; - - let element_size = array_typ.element_size(); - - let element_size_var = - self.acir_context.add_constant(FieldElement::from(element_size as u128)); - let outer_offset = self.acir_context.div_var( - var_index, - element_size_var, - AcirType::unsigned(32), - self.current_side_effects_enabled_var, - )?; - let inner_offset_index = self.acir_context.modulo_var( - var_index, - element_size_var, - 32, - self.current_side_effects_enabled_var, - )?; - let inner_offset = - self.acir_context.read_from_memory(element_type_sizes, &inner_offset_index)?; + let element_type_sizes = + self.init_element_type_sizes_array(array_typ, array_id, None, dfg)?; + let predicate_index = + self.acir_context.mul_var(var_index, self.current_side_effects_enabled_var)?; let flat_element_size_var = - self.acir_context.read_from_memory(element_type_sizes, &element_size_var)?; - let var_index = self.acir_context.mul_var(outer_offset, flat_element_size_var)?; - self.acir_context.add_var(var_index, inner_offset) + self.acir_context.read_from_memory(element_type_sizes, &predicate_index)?; + + Ok(flat_element_size_var) } fn flattened_slice_size(&mut self, array_id: ValueId, dfg: &DataFlowGraph) -> usize { @@ -1189,6 +1325,10 @@ impl Context { let array_acir_value = self.convert_value(array_id, dfg); size += Self::flattened_value_size(&array_acir_value); } + Value::Param { .. } => { + let array_acir_value = self.convert_value(array_id, dfg); + size += Self::flattened_value_size(&array_acir_value); + } _ => { unreachable!("ICE: Unexpected SSA value when computing the slice size"); } @@ -1612,26 +1752,50 @@ impl Context { } Intrinsic::SlicePushBack => { let slice_length = self.convert_value(arguments[0], dfg).into_var()?; + let (array_id, array_typ, _) = + self.check_array_is_initialized(arguments[1], dfg)?; let slice = self.convert_value(arguments[1], dfg); - // TODO(#2461): make sure that we have handled nested struct inputs - let element = self.convert_value(arguments[2], dfg); + // TODO(#3364): make sure that we have handled nested struct inputs + let element = self.convert_value(arguments[2], dfg); let one = self.acir_context.add_constant(FieldElement::one()); let new_slice_length = self.acir_context.add_var(slice_length, one)?; + // We attach the element no matter what in case len == capacity, as otherwise we + // may get an out of bounds error. let mut new_slice = Vector::new(); - self.slice_intrinsic_input(&mut new_slice, slice)?; - new_slice.push_back(element); - - Ok(vec![ - AcirValue::Var(new_slice_length, AcirType::field()), - AcirValue::Array(new_slice), - ]) + self.slice_intrinsic_input(&mut new_slice, slice.clone())?; + new_slice.push_back(element.clone()); + + // TODO(#3364): This works for non-nested outputs + let len = Self::flattened_value_size(&slice); + let new_elem_size = Self::flattened_value_size(&element); + let new_slice_val = AcirValue::Array(new_slice); + let result_block_id = self.block_id(&result_ids[1]); + self.initialize_array( + result_block_id, + len + new_elem_size, + Some(new_slice_val.clone()), + )?; + let mut var_index = slice_length; + self.array_set_value(element, result_block_id, &mut var_index)?; + + let result = AcirValue::DynamicArray(AcirDynamicArray { + block_id: result_block_id, + len: len + new_elem_size, + element_type_sizes: self.init_element_type_sizes_array( + &array_typ, + array_id, + Some(new_slice_val), + dfg, + )?, + }); + Ok(vec![AcirValue::Var(new_slice_length, AcirType::field()), result]) } Intrinsic::SlicePushFront => { let slice_length = self.convert_value(arguments[0], dfg).into_var()?; - let slice = self.convert_value(arguments[1], dfg); - // TODO(#2461): make sure that we have handled nested struct inputs + let slice: AcirValue = self.convert_value(arguments[1], dfg); + // TODO(#3364): make sure that we have handled nested struct inputs let element = self.convert_value(arguments[2], dfg); let one = self.acir_context.add_constant(FieldElement::one()); @@ -1653,12 +1817,18 @@ impl Context { let one = self.acir_context.add_constant(FieldElement::one()); let new_slice_length = self.acir_context.sub_var(slice_length, one)?; + let (_, _, block_id) = self.check_array_is_initialized(arguments[1], dfg)?; + let mut var_index = new_slice_length; + let elem = self.array_get_value( + &dfg.type_of_value(result_ids[2]), + block_id, + &mut var_index, + &[], + )?; + + // TODO(#3364): make sure that we have handled nested struct inputs let mut new_slice = Vector::new(); self.slice_intrinsic_input(&mut new_slice, slice)?; - // TODO(#2461): make sure that we have handled nested struct inputs - let elem = new_slice - .pop_back() - .expect("There are no elements in this slice to be removed"); Ok(vec![ AcirValue::Var(new_slice_length, AcirType::field()), @@ -1675,7 +1845,7 @@ impl Context { let mut new_slice = Vector::new(); self.slice_intrinsic_input(&mut new_slice, slice)?; - // TODO(#2461): make sure that we have handled nested struct inputs + // TODO(#3364): make sure that we have handled nested struct inputs let elem = new_slice .pop_front() .expect("There are no elements in this slice to be removed"); @@ -1715,7 +1885,7 @@ impl Context { // they are attempting to insert at too large of an index. // This check prevents a panic inside of the im::Vector insert method. if index <= new_slice.len() { - // TODO(#2461): make sure that we have handled nested struct inputs + // TODO(#3364): make sure that we have handled nested struct inputs new_slice.insert(index, element); } @@ -1753,7 +1923,7 @@ impl Context { // they are attempting to remove at too large of an index. // This check prevents a panic inside of the im::Vector remove method. let removed_elem = if index < new_slice.len() { - // TODO(#2461): make sure that we have handled nested struct inputs + // TODO(#3364): make sure that we have handled nested struct inputs new_slice.remove(index) } else { // This is a dummy value which should never be used if the appropriate diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 7eaf557c175..71e773e3f70 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -847,9 +847,12 @@ fn eval_constant_binary_op( if matches!(operator, BinaryOp::Div | BinaryOp::Mod) && rhs == 0 { return None; } - - let result = function(lhs, rhs); - truncate(result, *bit_size).into() + let result = function(lhs, rhs)?; + // Check for overflow + if result >= 2u128.pow(*bit_size) { + return None; + } + result.into() } Type::Numeric(NumericType::Signed { bit_size }) => { let function = operator.get_i128_function(); @@ -869,10 +872,14 @@ fn eval_constant_binary_op( return None; } - let result = function(lhs, rhs); + let result = function(lhs, rhs)?; + // Check for overflow + if result >= 2i128.pow(*bit_size - 1) || result < -(2i128.pow(*bit_size - 1)) { + return None; + } let result = if result >= 0 { result as u128 } else { (2i128.pow(*bit_size) + result) as u128 }; - truncate(result, *bit_size).into() + result.into() } _ => return None, }; @@ -906,33 +913,33 @@ impl BinaryOp { } } - fn get_u128_function(self) -> fn(u128, u128) -> u128 { + fn get_u128_function(self) -> fn(u128, u128) -> Option { match self { - BinaryOp::Add => u128::wrapping_add, - BinaryOp::Sub => u128::wrapping_sub, - BinaryOp::Mul => u128::wrapping_mul, - BinaryOp::Div => u128::wrapping_div, - BinaryOp::Mod => u128::wrapping_rem, - BinaryOp::And => |x, y| x & y, - BinaryOp::Or => |x, y| x | y, - BinaryOp::Xor => |x, y| x ^ y, - BinaryOp::Eq => |x, y| (x == y) as u128, - BinaryOp::Lt => |x, y| (x < y) as u128, + BinaryOp::Add => u128::checked_add, + BinaryOp::Sub => u128::checked_sub, + BinaryOp::Mul => u128::checked_mul, + BinaryOp::Div => u128::checked_div, + BinaryOp::Mod => u128::checked_rem, + BinaryOp::And => |x, y| Some(x & y), + BinaryOp::Or => |x, y| Some(x | y), + BinaryOp::Xor => |x, y| Some(x ^ y), + BinaryOp::Eq => |x, y| Some((x == y) as u128), + BinaryOp::Lt => |x, y| Some((x < y) as u128), } } - fn get_i128_function(self) -> fn(i128, i128) -> i128 { + fn get_i128_function(self) -> fn(i128, i128) -> Option { match self { - BinaryOp::Add => i128::wrapping_add, - BinaryOp::Sub => i128::wrapping_sub, - BinaryOp::Mul => i128::wrapping_mul, - BinaryOp::Div => i128::wrapping_div, - BinaryOp::Mod => i128::wrapping_rem, - BinaryOp::And => |x, y| x & y, - BinaryOp::Or => |x, y| x | y, - BinaryOp::Xor => |x, y| x ^ y, - BinaryOp::Eq => |x, y| (x == y) as i128, - BinaryOp::Lt => |x, y| (x < y) as i128, + BinaryOp::Add => i128::checked_add, + BinaryOp::Sub => i128::checked_sub, + BinaryOp::Mul => i128::checked_mul, + BinaryOp::Div => i128::checked_div, + BinaryOp::Mod => i128::checked_rem, + BinaryOp::And => |x, y| Some(x & y), + BinaryOp::Or => |x, y| Some(x | y), + BinaryOp::Xor => |x, y| Some(x ^ y), + BinaryOp::Eq => |x, y| Some((x == y) as i128), + BinaryOp::Lt => |x, y| Some((x < y) as i128), } } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/types.rs b/compiler/noirc_evaluator/src/ssa/ir/types.rs index e69e936372d..7fe0713e748 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -79,6 +79,18 @@ impl Type { } } + pub(crate) fn contains_slice_element(&self) -> bool { + match self { + Type::Array(elements, _) => { + elements.iter().any(|element| element.contains_slice_element()) + } + Type::Slice(_) => true, + Type::Numeric(_) => false, + Type::Reference => false, + Type::Function => false, + } + } + /// Returns the flattened size of a Type pub(crate) fn flattened_size(&self) -> usize { let mut size = 0; diff --git a/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs b/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs new file mode 100644 index 00000000000..d12b01b9196 --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs @@ -0,0 +1,693 @@ +//! This module defines the internal slices data fill pass. +//! The purpose of this pass is to fill out nested slice values represented by SSA array values. +//! "Filling out" a nested slice specifically refers to making a nested slice's internal slice types +//! match up in their size. This pass is necessary for dynamic array operations to work in ACIR gen +//! as we need to have a known size for any memory operations. As slice types do not carry a size we +//! need to make sure all nested internal slices have the same size in order to accurately +//! read from or write to a nested slice. This pass ultimately attaches dummy data to any smaller internal slice types. +//! +//! A simple example: +//! If we have a slice of the type [[Field]] which is of length 2. The internal slices themselves +//! could be of different sizes, such as 3 and 4. An array operation on this nested slice would look +//! something like below: +//! array_get [Field 3, [Field 1, Field 1, Field 1], Field 4, [Field 2, Field 2, Field 2, Field 2]], index Field v0 +//! Will get translated into a new instruction like such: +//! array_get [Field 3, [Field 1, Field 1, Field 1, Field 0], Field 4, [Field 2, Field 2, Field 2, Field 2]], index Field v0 +//! +//! +//! TODO(#3188): Currently the pass only works on a single flattened block. This should be updated in followup work. +//! The steps of the pass are as follows: +//! - Process each instruction of the block to collect relevant slice size information. We want to find the maximum size that a nested slice +//! potentially could be. Slices can potentially be set to larger array values or used in intrinsics that increase or shorten their size. +//! - Track all array constants and compute an initial map of their nested slice sizes. The slice sizes map is simply a map of an SSA array value +//! to its array size and then any child slice values that may exist. +//! - We also track a map to resolve a starting array constant to its final possible array value. This map is updated on the appropriate instructions +//! such as ArraySet or any slice intrinsics. +//! - On an ArrayGet operation add the resulting value as a possible child of the original slice. In SSA we will reuse the same memory block +//! for the nested slice and must account for an internal slice being fetched and set to a larger value, otherwise we may have an out of bounds error. +//! Also set the resulting fetched value to have the same internal slice size map as the children of the original array used in the operation. +//! - On an ArraySet operation we set the resulting value to have the same slice sizes map as the original array used in the operation. Like the result of +//! an ArrayGet we need to also add the `value` for an ArraySet as a possible child slice of the original array. +//! - For slice intrinsics we set the resulting value to have the same slice sizes map as the original array the same way as we do in an ArraySet. +//! However, with a slice intrinsic we also increase the size for the respective slice intrinsics. +//! We do not decrement the size on intrinsics that could remove values from a slice. This is because we could potentially go back to the smaller slice size, +//! not fill in the appropriate dummies and then get an out of bounds error later when executing the ACIR. We always want to compute +//! what a slice maximum size could be. +//! - Now we need to add each instruction back except with the updated original array values. +//! - Resolve the original slice value to what its final value would be using the previously computed map. +//! - Find the max size as each layer of the recursive nested slice type. +//! For instance in the example above we have a slice of depth 2 with the max sizes of [2, 4]. +//! - Follow the slice type to check whether the SSA value is under the specified max size. If a slice value +//! is under the max size we then attach dummy data. +//! - Construct a final nested slice with the now attached dummy data and replace the original array in the previously +//! saved ArrayGet and ArraySet instructions. + +use crate::ssa::{ + ir::{ + basic_block::BasicBlockId, + dfg::CallStack, + function::Function, + function_inserter::FunctionInserter, + instruction::{Instruction, InstructionId, Intrinsic}, + post_order::PostOrder, + types::Type, + value::{Value, ValueId}, + }, + ssa_gen::Ssa, +}; + +use acvm::FieldElement; +use fxhash::FxHashMap as HashMap; + +impl Ssa { + pub(crate) fn fill_internal_slices(mut self) -> Ssa { + for function in self.functions.values_mut() { + let mut context = Context::new(function); + context.process_blocks(); + } + self + } +} + +struct Context<'f> { + post_order: PostOrder, + inserter: FunctionInserter<'f>, + + /// Maps SSA array values representing a slice's contents to its updated array value + /// after an array set or a slice intrinsic operation. + /// Maps original value -> result + mapped_slice_values: HashMap, + + /// Maps an updated array value following an array operation to its previous value. + /// When used in conjunction with `mapped_slice_values` we form a two way map of all array + /// values being used in array operations. + /// Maps result -> original value + slice_parents: HashMap, +} + +impl<'f> Context<'f> { + fn new(function: &'f mut Function) -> Self { + let post_order = PostOrder::with_function(function); + let inserter = FunctionInserter::new(function); + + Context { + post_order, + inserter, + mapped_slice_values: HashMap::default(), + slice_parents: HashMap::default(), + } + } + + fn process_blocks(&mut self) { + let mut block_order = PostOrder::with_function(self.inserter.function).into_vec(); + block_order.reverse(); + for block in block_order { + self.process_block(block); + } + } + + fn process_block(&mut self, block: BasicBlockId) { + // Fetch SSA values potentially with internal slices + let instructions = self.inserter.function.dfg[block].take_instructions(); + + // Values containing nested slices to be replaced + let mut slice_values = Vec::new(); + // Maps SSA array ID representing slice contents to its length and a list of its potential internal slices + // This map is constructed once for an array constant and is then updated + // according to the rules in `collect_slice_information`. + let mut slice_sizes: HashMap)> = HashMap::default(); + + // Update the slice sizes map to help find the potential max size of each nested slice. + for instruction in instructions.iter() { + self.collect_slice_information(*instruction, &mut slice_values, &mut slice_sizes); + } + + // Add back every instruction with the updated nested slices. + for instruction in instructions { + self.push_updated_instruction(instruction, &slice_values, &slice_sizes, block); + } + + self.inserter.map_terminator_in_place(block); + } + + /// Determine how the slice sizes map needs to be updated according to the provided instruction. + fn collect_slice_information( + &mut self, + instruction: InstructionId, + slice_values: &mut Vec, + slice_sizes: &mut HashMap)>, + ) { + let results = self.inserter.function.dfg.instruction_results(instruction); + match &self.inserter.function.dfg[instruction] { + Instruction::ArrayGet { array, .. } => { + let array_typ = self.inserter.function.dfg.type_of_value(*array); + let array_value = &self.inserter.function.dfg[*array]; + // If we have an SSA value containing nested slices we should mark it + // as a slice that potentially requires to be filled with dummy data. + if matches!(array_value, Value::Array { .. }) && array_typ.contains_slice_element() + { + slice_values.push(*array); + // Initial insertion into the slice sizes map + // Any other insertions should only occur if the value is already + // a part of the map. + self.compute_slice_sizes(*array, slice_sizes); + } + + let res_typ = self.inserter.function.dfg.type_of_value(results[0]); + if res_typ.contains_slice_element() { + if let Some(inner_sizes) = slice_sizes.get_mut(array) { + // Include the result in the parent array potential children + // If the result has internal slices and is called in an array set + // we could potentially have a new larger slice which we need to account for + inner_sizes.1.push(results[0]); + self.slice_parents.insert(results[0], *array); + + let inner_sizes_iter = inner_sizes.1.clone(); + for slice_value in inner_sizes_iter { + let inner_slice = slice_sizes.get(&slice_value).unwrap_or_else(|| { + panic!("ICE: should have inner slice set for {slice_value}") + }); + slice_sizes.insert(results[0], inner_slice.clone()); + } + } + } + } + Instruction::ArraySet { array, value, .. } => { + let array_typ = self.inserter.function.dfg.type_of_value(*array); + let array_value = &self.inserter.function.dfg[*array]; + // If we have an SSA value containing nested slices we should mark it + // as a slice that potentially requires to be filled with dummy data. + if matches!(array_value, Value::Array { .. }) && array_typ.contains_slice_element() + { + slice_values.push(*array); + // Initial insertion into the slice sizes map + // Any other insertions should only occur if the value is already + // a part of the map. + self.compute_slice_sizes(*array, slice_sizes); + } + + let value_typ = self.inserter.function.dfg.type_of_value(*value); + if value_typ.contains_slice_element() { + self.compute_slice_sizes(*value, slice_sizes); + + let inner_sizes = slice_sizes.get_mut(array).expect("ICE expected slice sizes"); + inner_sizes.1.push(*value); + + let value_parent = self.resolve_slice_parent(*value); + if slice_values.contains(&value_parent) { + // Map the value parent to the current array in case nested slices + // from the current array are set to larger values later in the program + self.mapped_slice_values.insert(value_parent, *array); + } + } + + if let Some(inner_sizes) = slice_sizes.get_mut(array) { + let inner_sizes = inner_sizes.clone(); + slice_sizes.insert(results[0], inner_sizes); + + self.mapped_slice_values.insert(*array, results[0]); + self.slice_parents.insert(results[0], *array); + } + } + Instruction::Call { func, arguments } => { + let func = &self.inserter.function.dfg[*func]; + if let Value::Intrinsic(intrinsic) = func { + let (argument_index, result_index) = match intrinsic { + Intrinsic::SlicePushBack + | Intrinsic::SlicePushFront + | Intrinsic::SlicePopBack + | Intrinsic::SliceInsert + | Intrinsic::SliceRemove => (1, 1), + Intrinsic::SlicePopFront => (1, 2), + _ => return, + }; + match intrinsic { + Intrinsic::SlicePushBack + | Intrinsic::SlicePushFront + | Intrinsic::SliceInsert => { + let slice_contents = arguments[argument_index]; + if let Some(inner_sizes) = slice_sizes.get_mut(&slice_contents) { + inner_sizes.0 += 1; + + let inner_sizes = inner_sizes.clone(); + slice_sizes.insert(results[result_index], inner_sizes); + + self.mapped_slice_values + .insert(slice_contents, results[result_index]); + } + } + Intrinsic::SlicePopBack + | Intrinsic::SlicePopFront + | Intrinsic::SliceRemove => { + let slice_contents = arguments[argument_index]; + // We do not decrement the size on intrinsics that could remove values from a slice. + // This is because we could potentially go back to the smaller slice and not fill in dummies. + // This pass should be tracking the potential max that a slice ***could be*** + if let Some(inner_sizes) = slice_sizes.get(&slice_contents) { + let inner_sizes = inner_sizes.clone(); + slice_sizes.insert(results[result_index], inner_sizes); + + self.mapped_slice_values + .insert(slice_contents, results[result_index]); + } + } + _ => {} + } + } + } + _ => {} + } + } + + fn push_updated_instruction( + &mut self, + instruction: InstructionId, + slice_values: &[ValueId], + slice_sizes: &HashMap)>, + block: BasicBlockId, + ) { + match &self.inserter.function.dfg[instruction] { + Instruction::ArrayGet { array, .. } | Instruction::ArraySet { array, .. } => { + if slice_values.contains(array) { + let (new_array_op_instr, call_stack) = + self.get_updated_array_op_instr(*array, slice_sizes, instruction); + + self.inserter.push_instruction_value( + new_array_op_instr, + instruction, + block, + call_stack, + ); + } else { + self.inserter.push_instruction(instruction, block); + } + } + _ => { + self.inserter.push_instruction(instruction, block); + } + } + } + + /// Construct an updated ArrayGet or ArraySet instruction where the array value + /// has been replaced by a newly filled in array according to the max internal + /// slice sizes. + fn get_updated_array_op_instr( + &mut self, + array_id: ValueId, + slice_sizes: &HashMap)>, + instruction: InstructionId, + ) -> (Instruction, CallStack) { + let mapped_slice_value = self.resolve_slice_value(array_id); + + let (current_size, _) = slice_sizes + .get(&mapped_slice_value) + .unwrap_or_else(|| panic!("should have slice sizes: {mapped_slice_value}")); + + let mut max_sizes = Vec::new(); + + let typ = self.inserter.function.dfg.type_of_value(array_id); + let depth = Self::compute_nested_slice_depth(&typ); + max_sizes.resize(depth, 0); + max_sizes[0] = *current_size; + self.compute_slice_max_sizes(array_id, slice_sizes, &mut max_sizes, 1); + + let new_array = self.attach_slice_dummies(&typ, Some(array_id), true, &max_sizes); + + let instruction_id = instruction; + let (instruction, call_stack) = self.inserter.map_instruction(instruction_id); + let new_array_op_instr = match instruction { + Instruction::ArrayGet { index, .. } => { + Instruction::ArrayGet { array: new_array, index } + } + Instruction::ArraySet { index, value, .. } => { + Instruction::ArraySet { array: new_array, index, value } + } + _ => panic!("Expected array set"), + }; + + (new_array_op_instr, call_stack) + } + + fn attach_slice_dummies( + &mut self, + typ: &Type, + value: Option, + is_parent_slice: bool, + max_sizes: &[usize], + ) -> ValueId { + match typ { + Type::Numeric(_) => { + if let Some(value) = value { + self.inserter.resolve(value) + } else { + let zero = FieldElement::zero(); + self.inserter.function.dfg.make_constant(zero, Type::field()) + } + } + Type::Array(element_types, len) => { + if let Some(value) = value { + self.inserter.resolve(value) + } else { + let mut array = im::Vector::new(); + for _ in 0..*len { + for typ in element_types.iter() { + array.push_back(self.attach_slice_dummies(typ, None, false, max_sizes)); + } + } + self.inserter.function.dfg.make_array(array, typ.clone()) + } + } + Type::Slice(element_types) => { + let (current_size, max_sizes) = + max_sizes.split_first().expect("ICE: Missing internal slice max size"); + let mut max_size = *current_size; + if let Some(value) = value { + let mut slice = im::Vector::new(); + + let array = match self.inserter.function.dfg[value].clone() { + Value::Array { array, .. } => array, + _ => panic!("Expected an array value"), + }; + + if is_parent_slice { + max_size = array.len() / element_types.len(); + } + for i in 0..max_size { + for (element_index, element_type) in element_types.iter().enumerate() { + let index_usize = i * element_types.len() + element_index; + let valid_index = index_usize < array.len(); + let maybe_value = + if valid_index { Some(array[index_usize]) } else { None }; + slice.push_back(self.attach_slice_dummies( + element_type, + maybe_value, + false, + max_sizes, + )); + } + } + + self.inserter.function.dfg.make_array(slice, typ.clone()) + } else { + let mut slice = im::Vector::new(); + for _ in 0..max_size { + for typ in element_types.iter() { + slice.push_back(self.attach_slice_dummies(typ, None, false, max_sizes)); + } + } + self.inserter.function.dfg.make_array(slice, typ.clone()) + } + } + Type::Reference => { + unreachable!("ICE: Generating dummy data for references is unsupported") + } + Type::Function => { + unreachable!("ICE: Generating dummy data for functions is unsupported") + } + } + } + + // This methods computes a map representing a nested slice. + // The method also automatically computes the given max slice size + // at each depth of the recursive type. + // For example if we had a next slice + fn compute_slice_sizes( + &self, + array_id: ValueId, + slice_sizes: &mut HashMap)>, + ) { + if let Value::Array { array, typ } = &self.inserter.function.dfg[array_id].clone() { + if let Type::Slice(_) = typ { + let element_size = typ.element_size(); + let len = array.len() / element_size; + let mut slice_value = (len, vec![]); + for value in array { + let typ = self.inserter.function.dfg.type_of_value(*value); + if let Type::Slice(_) = typ { + slice_value.1.push(*value); + self.compute_slice_sizes(*value, slice_sizes); + } + } + // Mark the correct max size based upon an array values internal structure + let mut max_size = 0; + for inner_value in slice_value.1.iter() { + let inner_slice = + slice_sizes.get(inner_value).expect("ICE: should have inner slice set"); + if inner_slice.0 > max_size { + max_size = inner_slice.0; + } + } + for inner_value in slice_value.1.iter() { + let inner_slice = + slice_sizes.get_mut(inner_value).expect("ICE: should have inner slice set"); + if inner_slice.0 < max_size { + inner_slice.0 = max_size; + } + } + slice_sizes.insert(array_id, slice_value); + } + } + } + + /// Determine the maximum possible size of an internal slice at each + /// layer of a nested slice. + /// + /// If the slice map is incorrectly formed the function will exceed + /// the type's nested slice depth and panic. + fn compute_slice_max_sizes( + &self, + array_id: ValueId, + slice_sizes: &HashMap)>, + max_sizes: &mut Vec, + depth: usize, + ) { + let array_id = self.resolve_slice_value(array_id); + let (current_size, inner_slices) = slice_sizes + .get(&array_id) + .unwrap_or_else(|| panic!("should have slice sizes: {array_id}")); + + if inner_slices.is_empty() { + return; + } + + let mut max = *current_size; + for inner_slice in inner_slices.iter() { + let inner_slice = &self.resolve_slice_value(*inner_slice); + + let (inner_size, _) = slice_sizes[inner_slice]; + if inner_size > max { + max = inner_size; + } + self.compute_slice_max_sizes(*inner_slice, slice_sizes, max_sizes, depth + 1); + } + + max_sizes[depth] = max; + if max > max_sizes[depth] { + max_sizes[depth] = max; + } + } + + /// Compute the depth of nested slices in a given Type. + /// The depth follows the recursive type structure of a slice. + fn compute_nested_slice_depth(typ: &Type) -> usize { + let mut depth = 0; + if let Type::Slice(element_types) = typ { + depth += 1; + for typ in element_types.as_ref() { + depth += Self::compute_nested_slice_depth(typ); + } + } + depth + } + + /// Resolves a ValueId representing a slice's contents to its updated value. + /// If there is no resolved value for the supplied value, the value which + /// was passed to the method is returned. + fn resolve_slice_value(&self, array_id: ValueId) -> ValueId { + match self.mapped_slice_values.get(&array_id) { + Some(value) => self.resolve_slice_value(*value), + None => array_id, + } + } + + /// Resolves a ValueId representing a slice's contents to its previous value. + /// If there is no resolved parent value it means we have the original slice value + /// and the value which was passed to the method is returned. + fn resolve_slice_parent(&self, array_id: ValueId) -> ValueId { + match self.slice_parents.get(&array_id) { + Some(value) => self.resolve_slice_parent(*value), + None => array_id, + } + } +} + +#[cfg(test)] +mod tests { + + use std::rc::Rc; + + use acvm::FieldElement; + use im::vector; + + use crate::ssa::{ + function_builder::FunctionBuilder, + ir::{ + dfg::DataFlowGraph, + function::RuntimeType, + instruction::{BinaryOp, Instruction}, + map::Id, + types::Type, + value::ValueId, + }, + }; + + #[test] + fn test_simple_nested_slice() { + // We want to test that a nested slice with two internal slices of primitive types + // fills the smaller internal slice with dummy data to match the length of the + // larger internal slice. + + // Note that slices are a represented by a tuple of (length, contents). + // The type of the nested slice in this test is [[Field]]. + // + // This is the original SSA: + // acir fn main f0 { + // b0(v0: Field): + // v2 = lt v0, Field 2 + // constrain v2 == Field 1 'Index out of bounds' + // v11 = array_get [[Field 3, [Field 1, Field 1, Field 1]], [Field 4, [Field 2, Field 2, Field 2, Field 2]]], index Field v0 + // constrain v11 == Field 4 + // return + // } + + let main_id = Id::test_new(0); + let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + + let main_v0 = builder.add_parameter(Type::field()); + + let two = builder.field_constant(2_u128); + // Every slice access checks against the dynamic slice length + let slice_access_check = builder.insert_binary(main_v0, BinaryOp::Lt, two); + let one = builder.field_constant(1_u128); + builder.insert_constrain(slice_access_check, one, Some("Index out of bounds".to_owned())); + + let field_element_type = Rc::new(vec![Type::field()]); + let inner_slice_contents_type = Type::Slice(field_element_type); + + let inner_slice_small_len = builder.field_constant(3_u128); + let inner_slice_small_contents = + builder.array_constant(vector![one, one, one], inner_slice_contents_type.clone()); + + let inner_slice_big_len = builder.field_constant(4_u128); + let inner_slice_big_contents = + builder.array_constant(vector![two, two, two, two], inner_slice_contents_type.clone()); + + let outer_slice_element_type = Rc::new(vec![Type::field(), inner_slice_contents_type]); + let outer_slice_type = Type::Slice(outer_slice_element_type); + + let outer_slice_contents = builder.array_constant( + vector![ + inner_slice_small_len, + inner_slice_small_contents, + inner_slice_big_len, + inner_slice_big_contents + ], + outer_slice_type, + ); + // Fetching the length of the second nested slice + // We must use a parameter to main as we do not want the array operation to be simplified out during SSA gen. The filling of internal slices + // is necessary for dynamic nested slices and thus we want to generate the SSA that ACIR gen would be converting. + let array_get_res = builder.insert_array_get(outer_slice_contents, main_v0, Type::field()); + + let four = builder.field_constant(4_u128); + builder.insert_constrain(array_get_res, four, None); + builder.terminate_with_return(vec![]); + + // Note that now the smaller internal slice should have extra dummy data that matches the larger internal slice's size. + // + // Expected SSA: + // acir fn main f0 { + // b0(v0: Field): + // v10 = lt v0, Field 2 + // constrain v10 == Field 1 'Index out of bounds' + // v18 = array_get [Field 3, [Field 1, Field 1, Field 1, Field 0], Field 4, [Field 2, Field 2, Field 2, Field 2]], index v0 + // constrain v18 == Field 4 + // return + // } + + let ssa = builder.finish().fill_internal_slices(); + + let func = ssa.main(); + let block_id = func.entry_block(); + + // Check the array get expression has replaced its nested slice with a new slice + // where the internal slice has dummy data attached to it. + let instructions = func.dfg[block_id].instructions(); + let array_id = instructions + .iter() + .find_map(|instruction| { + if let Instruction::ArrayGet { array, .. } = func.dfg[*instruction] { + Some(array) + } else { + None + } + }) + .expect("Should find array_get instruction"); + + let (array_constant, _) = + func.dfg.get_array_constant(array_id).expect("should have an array constant"); + + let inner_slice_small_len = func + .dfg + .get_numeric_constant(array_constant[0]) + .expect("should have a numeric constant"); + assert_eq!( + inner_slice_small_len, + FieldElement::from(3u128), + "The length of the smaller internal slice should be unchanged" + ); + + let (inner_slice_small_contents, _) = + func.dfg.get_array_constant(array_constant[1]).expect("should have an array constant"); + let small_capacity = inner_slice_small_contents.len(); + assert_eq!(small_capacity, 4, "The inner slice contents should contain dummy element"); + + compare_array_constants(&inner_slice_small_contents, &[1, 1, 1, 0], &func.dfg); + + let inner_slice_big_len = func + .dfg + .get_numeric_constant(array_constant[2]) + .expect("should have a numeric constant"); + assert_eq!( + inner_slice_big_len, + FieldElement::from(4u128), + "The length of the larger internal slice should be unchanged" + ); + + let (inner_slice_big_contents, _) = + func.dfg.get_array_constant(array_constant[3]).expect("should have an array constant"); + let big_capacity = inner_slice_big_contents.len(); + assert_eq!( + small_capacity, big_capacity, + "The length of both internal slice contents should be the same" + ); + + compare_array_constants(&inner_slice_big_contents, &[2u128; 4], &func.dfg); + } + + fn compare_array_constants( + got_list: &im::Vector, + expected_list: &[u128], + dfg: &DataFlowGraph, + ) { + for i in 0..got_list.len() { + let got_value = + dfg.get_numeric_constant(got_list[i]).expect("should have a numeric constant"); + assert_eq!( + got_value, + FieldElement::from(expected_list[i]), + "Value is different than expected" + ); + } + } +} diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs index 3041dddd573..32979f78632 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs @@ -194,7 +194,8 @@ impl<'a> ValueMerger<'a> { for i in 0..len { for (element_index, element_type) in element_types.iter().enumerate() { - let index_value = ((i * element_types.len() + element_index) as u128).into(); + let index_usize = i * element_types.len() + element_index; + let index_value = (index_usize as u128).into(); let index = self.dfg.make_constant(index_value, Type::field()); let typevars = Some(vec![element_type.clone()]); @@ -202,7 +203,7 @@ impl<'a> ValueMerger<'a> { let mut get_element = |array, typevars, len| { // The smaller slice is filled with placeholder data. Codegen for slice accesses must // include checks against the dynamic slice length so that this placeholder data is not incorrectly accessed. - if len <= index_value.to_u128() as usize { + if len <= index_usize { self.make_slice_dummy_data(element_type) } else { let get = Instruction::ArrayGet { array, index }; @@ -239,6 +240,11 @@ impl<'a> ValueMerger<'a> { Value::Instruction { instruction: instruction_id, .. } => { let instruction = &self.dfg[*instruction_id]; match instruction { + // TODO(#3188): A slice can be the result of an ArrayGet when it is the + // fetched from a slice of slices or as a struct field. + // However, we need to incorporate nested slice support in flattening + // in order for this to be valid + // Instruction::ArrayGet { array, .. } => {} Instruction::ArraySet { array, .. } => { let array = *array; let len = self.get_slice_length(array); @@ -323,7 +329,9 @@ impl<'a> ValueMerger<'a> { self.dfg.make_array(array, typ.clone()) } Type::Slice(_) => { - unreachable!("ICE: Slices of slice is unsupported") + // TODO(#3188): Need to update flattening to use true user facing length of slices + // to accurately construct dummy data + unreachable!("ICE: Cannot return a slice of slices from an if expression") } Type::Reference => { unreachable!("ICE: Merging references is unsupported") diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index 4d003c0594b..95784194d28 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -8,6 +8,7 @@ mod assert_constant; mod constant_folding; mod defunctionalize; mod die; +mod fill_internal_slices; pub(crate) mod flatten_cfg; mod inlining; mod mem2reg; diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index 4cf97acef9a..4879facd780 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -289,6 +289,17 @@ impl<'a> FunctionContext<'a> { self.builder.insert_binary(positive_predicate, BinaryOp::Add, negative_predicate) } + /// Insert constraints ensuring that the operation does not overflow the bit size of the result + /// + /// If the result is unsigned, we simply range check against the bit size + /// + /// If the result is signed, we just prepare it for check_signed_overflow() by casting it to + /// an unsigned value representing the signed integer. + /// We need to use a bigger bit size depending on the operation, in case the operation does overflow, + /// Then, we delegate the overflow checks to check_signed_overflow() and cast the result back to its type. + /// Note that we do NOT want to check for overflows here, only check_signed_overflow() is allowed to do so. + /// This is because an overflow might be valid. For instance if 'a' is a signed integer, then 'a - a', as an unsigned result will always + /// overflow the bit size, however the operation is still valid (i.e it is not a signed overflow) fn check_overflow( &mut self, result: ValueId, @@ -375,6 +386,18 @@ impl<'a> FunctionContext<'a> { } } + /// Insert constraints ensuring that the operation does not overflow the bit size of the result + /// We assume that: + /// lhs and rhs are signed integers of bit size bit_size + /// result is the result of the operation, casted into an unsigned integer and not reduced + /// + /// overflow check for signed integer is less straightforward than for unsigned integers. + /// We first compute the sign of the operands, and then we use the following rules: + /// addition: positive operands => result must be positive (i.e less than half the bit size) + /// negative operands => result must be negative (i.e not positive) + /// different sign => no overflow + /// multiplication: we check that the product of the operands' absolute values does not overflow the bit size + /// then we check that the result has the proper sign, using the rule of signs fn check_signed_overflow( &mut self, result: ValueId, @@ -450,6 +473,7 @@ impl<'a> FunctionContext<'a> { _ => unreachable!("operator {} should not overflow", operator), } } + /// Insert a binary instruction at the end of the current block. /// Converts the form of the binary instruction as necessary /// (e.g. swapping arguments, inserting a not) to represent it in the IR. diff --git a/compiler/noirc_frontend/src/hir/mod.rs b/compiler/noirc_frontend/src/hir/mod.rs index aabb0e82daa..5a28d7b779a 100644 --- a/compiler/noirc_frontend/src/hir/mod.rs +++ b/compiler/noirc_frontend/src/hir/mod.rs @@ -7,7 +7,7 @@ pub mod type_check; #[cfg(feature = "aztec")] pub(crate) mod aztec_library; -use crate::graph::{CrateGraph, CrateId, Dependency}; +use crate::graph::{CrateGraph, CrateId}; use crate::hir_def::function::FuncMeta; use crate::node_interner::{FuncId, NodeInterner, StructId}; use def_map::{Contract, CrateDefMap}; @@ -119,18 +119,35 @@ impl Context { if &module_id.krate == crate_id { module_path } else { - let crate_name = &self.crate_graph[crate_id] - .dependencies - .iter() - .find_map(|dep| match dep { - Dependency { name, crate_id } if crate_id == &module_id.krate => Some(name), - _ => None, - }) + let crates = self + .find_dependencies(crate_id, &module_id.krate) .expect("The Struct was supposed to be defined in a dependency"); - format!("{crate_name}::{module_path}") + crates.join("::") + "::" + &module_path } } + /// Recursively walks down the crate dependency graph from crate_id until we reach requested crate + /// This is needed in case a library (lib1) re-export a structure defined in another library (lib2) + /// In that case, we will get [lib1,lib2] when looking for a struct defined in lib2, + /// re-exported by lib1 and used by the main crate. + /// Returns the path from crate_id to target_crate_id + fn find_dependencies( + &self, + crate_id: &CrateId, + target_crate_id: &CrateId, + ) -> Option> { + for dep in &self.crate_graph[crate_id].dependencies { + if &dep.crate_id == target_crate_id { + return Some(vec![dep.name.to_string()]); + } + if let Some(mut path) = self.find_dependencies(&dep.crate_id, target_crate_id) { + path.insert(0, dep.name.to_string()); + return Some(path); + } + } + None + } + pub fn function_meta(&self, func_id: &FuncId) -> FuncMeta { self.def_interner.function_meta(func_id) } diff --git a/docs/.gitignore b/docs/.gitignore index c034d49ebde..e76d8caf6bc 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -7,6 +7,7 @@ # Generated files .docusaurus .cache-loader +/docs/noir_js/reference/ # Misc .DS_Store diff --git a/docs/docs/noir_js/noir_js.md b/docs/docs/noir_js/noir_js.md index 23ea550e156..f895b22eaf8 100644 --- a/docs/docs/noir_js/noir_js.md +++ b/docs/docs/noir_js/noir_js.md @@ -33,4 +33,4 @@ To install its JavaScript library, run this in your project: npm i @noir-lang/backend_barretenberg ``` -For more details on how to instantiate and use the libraries, refer to the [Full Noir App Guide](./getting_started/01_tiny_noir_app.md) and [Reference](./reference/01_noirjs.md) sections. +For more details on how to instantiate and use the libraries, refer to the [Full Noir App Guide](./getting_started/01_tiny_noir_app.md) and [Reference](./reference/noir_js/classes/Noir.md) sections. diff --git a/docs/docs/noir_js/reference/01_noirjs.md b/docs/docs/noir_js/reference/01_noirjs.md deleted file mode 100644 index 0d6d5abbbff..00000000000 --- a/docs/docs/noir_js/reference/01_noirjs.md +++ /dev/null @@ -1,147 +0,0 @@ ---- -title: Noir -description: Reference to noir_js library and the Noir class -keywords: [Noir project, javascript, typescript, node.js, browser, react, class, reference] ---- - -## Table of Contents - -- [constructor](#constructor) -- [init](#init) -- [generateFinalProof](#generatefinalproof) -- [verifyFinalProof](#verifyfinalproof) - -## `constructor` - -The `constructor` is a method used to create and initialize objects created within the `Noir` class. In the `Noir` class constructor, you need to pass two parameters: `circuit` and `backend`. - -### Syntax - -```js -constructor(circuit, backend); -``` - -### Parameters - -| Parameter | Type | Description | -| --------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `circuit` | Object | A circuit represented in a `json` format, containing the ABI and bytecode. Typically obtained by running [`nargo compile`](../../nargo/01_commands.md) | -| `backend` | Object | A backend instance, before initialization. | - -### Usage - -```js -const noir = new Noir(circuit, backend); -``` - -## `init` - -This async method should be called after class instantiation. It will run processes on the ACVM, instantiate your backend, etc. - -### Syntax - -```js -async init() -``` - -### Parameters - -This method takes no parameters - -### Usage - -```js -await noirInstance.init(); -``` - -## `execute` - -This async method allows to execute a circuit to get its witness and return value. [`generateFinalProof`](#generatefinalproof) calls it for you, but you can call it directly (i.e. to feed directly to a backend, or to get the return value). - -You can optionally provide a foreignCallHandler, to handle functions that should run outside of the prover (e.g. `std::println`) - -### Syntax - -```js -async execute(inputs, foreignCallHandler) -``` - -### Parameters - -| Parameter | Type | Description | -| --------- | ------ | ------------------------------------------------ | -| `inputs` | Object | An object containing the inputs to your circuit. | -| `foreignCallHandler` (optional) | Function | A function handling the foreign call from your circuit | - -### Returns - -| Return value | Type | Description | -| ------------ | --------------------- | --------------------------------------------------- | -| `witness` | Promise | The witness | -| `returnValue` | Promise | The return value | - -### Usage - -```js -const { witness, returnValue } = await noir.execute(inputs) -const { witness, returnValue } = await noir.execute(inputs, (name, args) => console.log(`Received foreign call ${name} with arguments ${args}`)) -``` - -## `generateFinalProof` - -This async method generates a witness and a proof given an object as input. - -### Syntax - -```js -async generateFinalproof(input) -``` - -### Parameters - -| Parameter | Type | Description | -| --------- | ------ | ------------------------------------------------ | -| `input` | Object | An object containing the inputs to your circuit. | - -### Returns - -| Return value | Type | Description | -| ------------ | --------------------- | --------------------------------------------------- | -| `proof` | Promise | An array with the byte representation of the proof. | - -### Usage - -```js -// consider the Standard Noir Example given with nargo init -const input = { x: 1, y: 2 }; -noirInstance.generateProof(input); -``` - -## `verifyFinalProof` - -This async method instantiates the verification key and verifies your proof. - -### Syntax - -```js -async verifyFinalProof(proof) -``` - -### Parameters - -| Parameter | Type | Description | -| --------- | ---------- | --------------------------------------------------------------------------------------------- | -| `proof` | Uint8Array | The Uint8Array representation of your proof, usually obtained by calling `generateFinalProof` | - -### Returns - -| Return value | Type | Description | -| ------------ | ------------------ | -------------------------------------------- | -| `verified` | Promise | A boolean for whether the proof was verified | - -### Usage - -```js -const proof = noirInstance.generateProof(input); -noirInstance.verifyFinalProof(proof); -``` diff --git a/docs/docs/noir_js/reference/02_bb_backend.md b/docs/docs/noir_js/reference/02_bb_backend.md deleted file mode 100644 index 21c2ff32b57..00000000000 --- a/docs/docs/noir_js/reference/02_bb_backend.md +++ /dev/null @@ -1,272 +0,0 @@ ---- -title: BarretenbergBackend -description: Reference documentation for the barretenberg_backend library and the BarretenbergBackend class -keywords: - [ - BarretenbergBackend, - Barretenberg, - javascript, - typescript, - node.js, - browser, - class, - reference, - noir_js, - ] ---- - -## Table of Contents - -- [constructor](#constructor) -- [generateFinalProof](#generatefinalproof) -- [generateIntermediateProof](#generateintermediateproof) -- [generateProof](#generateproof) -- [generateIntermediateProofArtifacts](#generateintermediateproofartifacts) -- [verifyFinalProof](#verifyfinalproof) -- [verifyIntermediateProof](#verifyintermediateproof) -- [verifyProof](#verifyproof) -- [destroy](#destroy) - -## `constructor` - -The `constructor` is a method used to create and initialize objects created within the `BarretenbergBackend` class. In this class, you should pass at least one argument for the `circuit`. - -### Syntax - -```js -constructor(acirCircuit, (numberOfThreads = 1)); -``` - -### Parameters - -| Parameter | Type | Description | -| ----------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `acirCircuit` | Object | A circuit represented in a `json` format, containing the ABI and bytecode Tipically obtained by running [`nargo compile`](../../nargo/01_commands.md). This is the same circuit expected to be passed to [the Noir class](01_noirjs.md) | -| `numberOfThreads` | Number (optional) | The number of threads to be used by the backend. Defaults to 1. | - -### Usage - -```js -const backend = new BarretenbergBackend(acirCircuit); -``` - -## `generateFinalProof` - -An async wrapper around the [generateProof](#generateproof) method that passes a `false` flag. Usually called by the Noir class. - -### Syntax - -```js -async generateFinalProof(decompressedWitness) -``` - -### Parameters - -| Parameter | Type | Description | -| --------------------- | ------ | -------------------------------------------------------- | -| `decompressedWitness` | Object | The decompressed witness for generating the final proof. | - -### Returns - -| Return value | Type | Description | -| ------------ | -------------------- | --------------------------------------------------------- | -| `proof` | Promise | An array with the byte representation of the final proof. | - -### Usage - -```js -const finalProof = await backend.generateFinalProof(decompressedWitness); -``` - -## `generateIntermediateProof` - -An async wrapper around the [generateProof](#generateproof) method that passes a `true` flag. It's not currently being used by the Noir class, but developers can call this method directly to use Noir's recursive features. - -### Syntax - -```js -async generateIntermediateProof(witness) -``` - -### Parameters - -| Parameter | Type | Description | -| --------- | ------ | -------------------------------------------------- | -| `witness` | Object | The witness for generating the intermediate proof. | - -### Returns - -| Return value | Type | Description | -| ------------ | -------------------- | --------------------------------------------------------------- | -| `proof` | Promise | An array with the byte representation of the intermediate proof | - -### Usage - -```js -const intermediateProof = await backend.generateIntermediateProof(witness); -``` - -## `generateProof` - -This async method generates a proof. Takes the witness generated by ACVM, and a boolean that evaluates to `true` when the proof _is_ meant to be verified in another circuit. Not currently used by the Noir class. - -### Syntax - -```js -async generateProof(decompressedWitness, makeEasyToVerifyInCircuit) -``` - -### Parameters - -| Parameter | Type | Description | -| --------------------------- | ------- | ---------------------------------------------------------------------------------------------- | -| `decompressedWitness` | Object | The decompressed witness for generating the proof. | -| `makeEasyToVerifyInCircuit` | Boolean | A flag indicating whether to generate proof components for easy verification within a circuit. | - -### Returns - -| Return value | Type | Description | -| ------------ | -------------------- | -------------------------------------------------- | -| `proof` | Promise | An array with the byte representation of the proof | - -### Usage - -```js -const proof = await backend.generateProof(decompressedWitness, makeEasyToVerifyInCircuit); -``` - -## `generateIntermediateProofArtifacts` - -This async method returns the artifacts needed to verify the intermediate proof in another circuit. It's not currently being used by the Noir class, but developers can call this method directly to use Noir's recursive features. - -### Syntax - -```js -async generateIntermediateProofArtifacts(proof, numOfPublicInputs = 0) -``` - -### Parameters - -| Parameter | Type | Description | -| ------------------- | ----------------- | ---------------------------------------------------------------- | -| `proof` | Object | The proof object. | -| `numOfPublicInputs` | Number (optional) | The number of public inputs in the inner proof, defaulting to 0. | - -### Returns - -| Return value | Type | Description | -| --------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `proofAsFields` | string[] | An array of strings with the hexadecimal representation of the [Fields](../../language_concepts/data_types/00_fields.md) that make up a proof | -| `vkAsFields` | string[] | An array of strings with the hexadecimal representation of the [Fields](../../language_concepts/data_types/00_fields.md) that make up the verification key | -| `vkHash` | string | A pedersen hash of the verification key | - -### Usage - -```js -const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); -``` - -## `verifyFinalProof` - -An async wrapper around [verifyProof](#verifyproof) that sets the `false` flag. Usually called by the Noir class. - -### Syntax - -```js -async verifyFinalProof(proof) -``` - -### Parameters - -| Parameter | Type | Description | -| --------- | ------ | --------------------------- | -| `proof` | Object | The proof object to verify. | - -### Returns - -| Return value | Type | Description | -| ------------ | ------------------ | -------------------------------------------- | -| `verified` | Promise | A boolean for whether the proof was verified | - -### Usage - -```js -const isValidFinal = await backend.verifyFinalProof(proof); -``` - -## `verifyIntermediateProof` - -An async wrapper around [verifyProof](#verifyproof) that sets the `true` flag. It's not currently being used by the Noir class, but developers can call this method directly to use Noir's recursive features. - -### Syntax - -```js -async verifyIntermediateProof(proof) -``` - -### Parameters - -| Parameter | Type | Description | -| --------- | ------ | ---------------------------------------- | -| `proof` | Object | The intermediate proof object to verify. | - -### Returns - -| Return value | Type | Description | -| ------------ | ------------------ | -------------------------------------------- | -| `verified` | Promise | A boolean for whether the proof was verified | - -### Usage - -```js -const isValidIntermediate = await backend.verifyIntermediateProof(proof); -``` - -## `verifyProof` - -This async method verifies a proof. Takes the proof, and a boolean that evaluates to `true` when the proof is intermediate. - -### Syntax - -```js -async verifyProof(proof, makeEasyToVerifyInCircuit) -``` - -### Parameters - -| Parameter | Type | Description | -| --------------------------- | ------- | ------------------------------------------------------------ | -| `proof` | Object | The proof object to verify | -| `makeEasyToVerifyInCircuit` | Boolean | A flag indicating whether the proof is intermediate or final | - -### Returns - -| Parameter | Type | Description | -| ---------- | ------------------ | -------------------------------------------- | -| `verified` | Promise\ | A boolean for whether the proof was verified | - -### Usage - -```js -const isValid = await backend.verifyProof(proof, makeEasyToVerifyInCircuit); -``` - -## `destroy` - -This method destroys the resources allocated in the [instantiate](#instantiate) method. Noir doesn't currently call this method, but it's highly recommended that developers do so in order to save resources. - -### Syntax - -```js -async destroy() -``` - -### Parameters - -This method takes no parameters. - -### Usage - -```js -await backend.destroy(); -``` diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 5128a99b000..8f62df3d0e9 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -146,6 +146,70 @@ const config = { indexName: 'noir-lang', }, }, + plugins: [ + [ + 'docusaurus-plugin-typedoc', + { + id: 'noir_js', + entryPoints: ['../tooling/noir_js/src/index.ts'], + tsconfig: '../tooling/noir_js/tsconfig.json', + entryPointStrategy: 'resolve', + out: 'docs/noir_js/reference/noir_js', + plugin: ['typedoc-plugin-markdown'], + name: 'Noir JS', + disableSources: true, + excludePrivate: true, + + sidebar: { + filteredIds: ['noir_js/reference/noir_js/index'], + }, + readme: 'none', + hidePageHeader: true, + hideBreadcrumbs: true, + hideInPageTOC: true, + useCodeBlocks: true, + typeDeclarationFormat: 'table', + propertiesFormat: 'table', + parametersFormat: 'table', + enumMembersFormat: 'table', + indexFormat: 'table', + outputFileStrategy: 'members', + memberPageTitle: '{name}', + membersWithOwnFile: ['Interface', 'Class', 'TypeAlias', 'Function'], + }, + ], + [ + 'docusaurus-plugin-typedoc', + { + id: 'noir_js_backend_barretenberg', + entryPoints: ['../tooling/noir_js_backend_barretenberg/src/index.ts'], + tsconfig: '../tooling/noir_js_backend_barretenberg/tsconfig.json', + entryPointStrategy: 'resolve', + out: 'docs/noir_js/reference/backend_barretenberg', + plugin: ['typedoc-plugin-markdown'], + name: 'Backend Barretenberg', + disableSources: true, + excludePrivate: true, + + sidebar: { + filteredIds: ['noir_js/reference/backend_barretenberg/index'], + }, + readme: 'none', + hidePageHeader: true, + hideBreadcrumbs: true, + hideInPageTOC: true, + useCodeBlocks: true, + typeDeclarationFormat: 'table', + propertiesFormat: 'table', + parametersFormat: 'table', + enumMembersFormat: 'table', + indexFormat: 'table', + outputFileStrategy: 'members', + memberPageTitle: '{name}', + membersWithOwnFile: ['Interface', 'Class', 'TypeAlias'], + }, + ], + ], }; module.exports = config; diff --git a/docs/package.json b/docs/package.json index 3d71493c0ff..09f8d718b56 100644 --- a/docs/package.json +++ b/docs/package.json @@ -15,12 +15,18 @@ "@mdx-js/react": "^1.6.22", "axios": "^1.4.0", "clsx": "^1.2.1", + "docusaurus-plugin-typedoc": "1.0.0-next.18", "hast-util-is-element": "^1.1.0", "prism-react-renderer": "^1.3.5", "react": "^17.0.2", "react-dom": "^17.0.2", "rehype-katex": "^5.0.0", - "remark-math": "^3.0.1" + "remark-math": "^3.0.1", + "typedoc": "^0.25.0", + "typedoc-plugin-frontmatter": "^0.0.2", + "typedoc-plugin-markdown": "4.0.0-next.25", + "typedoc-plugin-merge-modules": "^5.1.0", + "typescript": "^5.2.2" }, "devDependencies": { "@docusaurus/module-type-aliases": "^2.4.0" diff --git a/docs/sidebars.js b/docs/sidebars.js index 8fddb677a58..3fd391cf09c 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -119,8 +119,22 @@ const sidebars = { label: 'Reference', items: [ { - type: 'autogenerated', - dirName: 'noir_js/reference', + type: 'category', + label: 'Noir JS', + link: { + type: 'doc', + id: 'noir_js/reference/noir_js/index', + }, + items: require('./docs/noir_js/reference/noir_js/typedoc-sidebar.cjs'), + }, + { + type: 'category', + label: 'Backend Barretenberg', + link: { + type: 'doc', + id: 'noir_js/reference/backend_barretenberg/index', + }, + items: require('./docs/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs'), }, ], }, diff --git a/noir_stdlib/src/hash/poseidon.nr b/noir_stdlib/src/hash/poseidon.nr index c0a31f2d0ea..bf45c3ecd23 100644 --- a/noir_stdlib/src/hash/poseidon.nr +++ b/noir_stdlib/src/hash/poseidon.nr @@ -20,7 +20,8 @@ pub fn config( mds: [Field; N]) -> PoseidonConfig { // Input checks - assert(t as u8 * (rf + rp) == ark.len() as u8); + let mul = crate::wrapping_mul(t as u8, (rf + rp)); + assert(mul == ark.len() as u8); assert(t * t == mds.len()); assert(alpha != 0); diff --git a/tooling/nargo_cli/tests/acir_artifacts/1_mul/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/1_mul/target/acir.gz index 1c28b75f74e..7572c9ac2cf 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/1_mul/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/1_mul/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/1_mul/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/1_mul/target/witness.gz index f01447653fa..76f5c8a2fe2 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/1_mul/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/1_mul/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/2_div/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/2_div/target/acir.gz index 37012dece4e..46405fc2029 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/2_div/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/2_div/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/2_div/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/2_div/target/witness.gz index d8733d8606e..3145b77d957 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/2_div/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/2_div/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/3_add/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/3_add/target/acir.gz index 490b5f41330..42e66d90f73 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/3_add/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/3_add/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/3_add/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/3_add/target/witness.gz index f44e09476bc..0cfc48c525e 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/3_add/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/3_add/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/6_array/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/6_array/target/acir.gz index f25a320b01e..787db190b49 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/6_array/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/6_array/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/6_array/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/6_array/target/witness.gz index 6aba7ca03c0..cc96fd18e00 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/6_array/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/6_array/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/7_function/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/7_function/target/acir.gz index 8b417c250a3..cc557da84d3 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/7_function/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/7_function/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/7_function/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/7_function/target/witness.gz index 9dcb6d30140..7ddf50cf14a 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/7_function/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/7_function/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/acir.gz index 57701adcf3f..376ae46ff4f 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/witness.gz index de5a1d07227..706a5784953 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/array_dynamic/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/acir.gz index 647dc586b04..4756ea6b632 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/witness.gz index 13d2ec1b7b5..890e987dd74 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_comptime/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/acir.gz index 7a59062156f..45c1cc44c8e 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/witness.gz index 28357d49cd1..bdc4e70ba09 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/bit_shifts_runtime/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/acir.gz index b7a2d88a36e..69cbde31d9d 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/witness.gz index 96855675d25..844178f0430 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_acir_as_brillig/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/acir.gz index b91295d3cec..b69e231774b 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/witness.gz index 96855675d25..844178f0430 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_calls/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls_array/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_calls_array/target/acir.gz index 1d348538bff..59b89c22bc3 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_calls_array/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_calls_array/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/acir.gz index e8bede3cbed..d1819212993 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/witness.gz index 0b563e5b0be..f4a9c9f6dda 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_fns_as_values/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_hash_to_field/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_hash_to_field/target/acir.gz index 06569c728ad..73c742a2dd1 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_hash_to_field/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_hash_to_field/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_hash_to_field/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_hash_to_field/target/witness.gz index 4c41f9a8bb1..1529254d597 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_hash_to_field/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_hash_to_field/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_oracle/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_oracle/target/acir.gz index 3ebdee29a46..db158f61882 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_oracle/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_oracle/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/acir.gz index b8eaf5a13a0..27f6f353d25 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_pedersen/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_recursion/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_recursion/target/acir.gz index 4853adb029b..c0c91d81546 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_recursion/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_recursion/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/acir.gz index 7bc3304fdaa..ac18684a07e 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/witness.gz index 2cbd61f9c4a..ceaf7ad008e 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/brillig_unitialised_arrays/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/acir.gz index 7380f30d354..dff5ebe0a87 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/witness.gz index 2ff44d73044..acc00ece890 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/conditional_1/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/acir.gz index 01de08c56d2..7c336747f92 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/witness.gz index ad901ffb80d..1efbfebfaad 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/conditional_regression_661/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/acir.gz index 275afa4c6e0..cec0caab082 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/witness.gz index f87a868de3d..477da9a8739 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/eddsa/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/acir.gz index c2c8c9f9469..b8d272e1271 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/witness.gz index 6802e590420..cbead933c40 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/hash_to_field/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/acir.gz index 889352e685f..09c3efbdf85 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/witness.gz index c4ded4eea08..f1625f0c2cf 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/merkle_insert/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/acir.gz index c7559671f2a..9f518522755 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/witness.gz index 7efd9b48ca4..62a1378dece 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/nested_array_dynamic/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/acir.gz index fcfd2ab066d..3db0a495a9d 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/witness.gz index ba1f5565170..9c9e80efe8f 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/nested_slice_dynamic/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/regression/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/regression/target/acir.gz index ed80135203d..66b4abc42dc 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/regression/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/regression/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/regression/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/regression/target/witness.gz index 76afd0975f6..ea06e69e18e 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/regression/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/regression/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/acir.gz index 20e8fa769a5..5c0339446c5 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/witness.gz index 000b4483daf..095aef252ee 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/regression_mem_op_predicate/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/acir.gz index 4005c95b18d..d1d827365c3 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/witness.gz index 91324bd7284..64537ff7375 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/schnorr/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/acir.gz deleted file mode 100644 index 1e9c237d083..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/acir.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/witness.gz deleted file mode 100644 index 6bedf3922b0..00000000000 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha2_blocks/target/witness.gz and /dev/null differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/acir.gz index 881bf967590..7f6715a12af 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/witness.gz index 746c6fc56a2..c7be868682e 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/sha2_byte/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/acir.gz index 505e8f56bb9..82747c17417 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/witness.gz index c180c733428..6627fd7d53f 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/signed_arithmetic/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/acir.gz index 906e4f2010b..39a17a5a529 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/witness.gz index c56799e636a..a35e3011ee6 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/signed_division/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/acir.gz index 25a54000fdb..84fc5cc5de2 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/witness.gz index 2cb925c0149..2afa317a120 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_bitwise/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/acir.gz index 80a70ba7ee3..e99ba23f4b4 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/witness.gz index e730d65c475..20064d105da 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_radix/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/acir.gz index 0600c1dfc78..f0edce7ee23 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/witness.gz index 0bc6d3e8fbc..f8b24dec230 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_shield/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_shift_left_right/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_shift_left_right/target/acir.gz index 19d240987a8..6bbdaef4a9b 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_shift_left_right/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_shift_left_right/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/simple_shift_left_right/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/simple_shift_left_right/target/witness.gz index 9c05da63d6b..6f15adc6525 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/simple_shift_left_right/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/simple_shift_left_right/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/acir.gz index 08d3fa2de40..3bb3470c51f 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/witness.gz index a077aaf9e56..797776a1886 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/slice_dynamic_index/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/slice_struct_field/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/slice_struct_field/target/acir.gz new file mode 100644 index 00000000000..07c003c93df Binary files /dev/null and b/tooling/nargo_cli/tests/acir_artifacts/slice_struct_field/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/slice_struct_field/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/slice_struct_field/target/witness.gz new file mode 100644 index 00000000000..e46b519ede3 Binary files /dev/null and b/tooling/nargo_cli/tests/acir_artifacts/slice_struct_field/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/slices/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/slices/target/acir.gz index 70a3700cbaf..7a053fcb196 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/slices/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/slices/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/slices/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/slices/target/witness.gz index 07ac75fe038..359b2f75601 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/slices/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/slices/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/strings/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/strings/target/acir.gz index 26528ac0100..424f4bd2d0f 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/strings/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/strings/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/acir.gz index aec6f367127..d7de6222904 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/witness.gz index 88ed370f861..9027dae5645 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_be_bytes/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/acir.gz index c505c205faa..d893d6f55c9 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/witness.gz index 5f82918d57a..9313438a5ff 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_consistent/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/acir.gz index 247509bd50a..b47dd38d6b3 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/witness.gz index a672966388a..617301d310b 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_bytes_integration/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/acir.gz b/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/acir.gz index 14286c04ef9..1b7d71c53ff 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/acir.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/acir.gz differ diff --git a/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/witness.gz b/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/witness.gz index 44c71164ae4..b27845dc122 100644 Binary files a/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/witness.gz and b/tooling/nargo_cli/tests/acir_artifacts/to_le_bytes/target/witness.gz differ diff --git a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/Nargo.toml b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/Nargo.toml new file mode 100644 index 00000000000..ea2ffc0d2eb --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/Nargo.toml @@ -0,0 +1,7 @@ +[workspace] +members = [ + "library", + "library2", + "binary" +] +default-member = "binary" \ No newline at end of file diff --git a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/binary/Nargo.toml b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/binary/Nargo.toml new file mode 100644 index 00000000000..90d8321589b --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/binary/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "binary" +type = "bin" +authors = [""] +[dependencies] +library = { path = "../library" } \ No newline at end of file diff --git a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/binary/src/main.nr b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/binary/src/main.nr new file mode 100644 index 00000000000..742ec4187af --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/binary/src/main.nr @@ -0,0 +1,2 @@ +use dep::library::ReExportMeFromAnotherLib; +fn main(_x : ReExportMeFromAnotherLib) {} diff --git a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library/Nargo.toml b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library/Nargo.toml new file mode 100644 index 00000000000..88831bada4e --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "library" +type = "lib" +authors = [""] + +[dependencies] +library2 = { path = "../library2"} \ No newline at end of file diff --git a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library/src/lib.nr b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library/src/lib.nr new file mode 100644 index 00000000000..41f7b2aeffd --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library/src/lib.nr @@ -0,0 +1,2 @@ +// Re-export +use dep::library2::ReExportMeFromAnotherLib; \ No newline at end of file diff --git a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library2/Nargo.toml b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library2/Nargo.toml new file mode 100644 index 00000000000..f2c20c0bf4a --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library2/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "library" +type = "lib" +authors = [""] +[dependencies] diff --git a/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library2/src/lib.nr b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library2/src/lib.nr new file mode 100644 index 00000000000..354558183e7 --- /dev/null +++ b/tooling/nargo_cli/tests/compile_success_empty/workspace_reexport_bug/library2/src/lib.nr @@ -0,0 +1,5 @@ +// When we re-export this type from another library and then use it in +// main, we get a panic +struct ReExportMeFromAnotherLib { + x : Field, +} \ No newline at end of file diff --git a/tooling/nargo_cli/tests/execution_success/4_sub/src/main.nr b/tooling/nargo_cli/tests/execution_success/4_sub/src/main.nr index 60bcde9c0b3..6aef8e7b208 100644 --- a/tooling/nargo_cli/tests/execution_success/4_sub/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/4_sub/src/main.nr @@ -3,8 +3,4 @@ use dep::std; fn main(mut x: u32, y: u32, z: u32) { x = std::wrapping_sub(x,y); assert(x == z); - - // Test constant underflow (regression for #2045) - let x = -1 as u4; - assert(x == 15); } diff --git a/tooling/nargo_cli/tests/execution_success/6_array/src/main.nr b/tooling/nargo_cli/tests/execution_success/6_array/src/main.nr index 44a5363f8bc..f9873e78cc4 100644 --- a/tooling/nargo_cli/tests/execution_success/6_array/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/6_array/src/main.nr @@ -25,7 +25,7 @@ fn main(x: [u32; 5], y: [u32; 5], mut z: u32, t: u32) { for i in 0..5 { z = z + x[i]*y[i]; for _i in 0..3 { - c = i as u32 - 2 as u32; + c = std::wrapping_sub(i as u32,2 as u32); z = std::wrapping_mul(z,c); } } diff --git a/tooling/nargo_cli/tests/execution_success/nested_array_dynamic/src/main.nr b/tooling/nargo_cli/tests/execution_success/nested_array_dynamic/src/main.nr index cd10b5e7347..0be7d688608 100644 --- a/tooling/nargo_cli/tests/execution_success/nested_array_dynamic/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/nested_array_dynamic/src/main.nr @@ -30,7 +30,7 @@ fn main(mut x : [Foo; 4], y : pub Field) { } else { x[y].a = 100; } - assert(x[y].a == 50); + assert(x[3].a == 50); if y == 2 { x[y - 1].b = [50, 51, 52]; diff --git a/tooling/nargo_cli/tests/execution_success/regression_mem_op_predicate/src/main.nr b/tooling/nargo_cli/tests/execution_success/regression_mem_op_predicate/src/main.nr index 2592d02ec2e..4b5ca67f6de 100644 --- a/tooling/nargo_cli/tests/execution_success/regression_mem_op_predicate/src/main.nr +++ b/tooling/nargo_cli/tests/execution_success/regression_mem_op_predicate/src/main.nr @@ -1,8 +1,8 @@ fn main(mut x: [u32; 5], idx: Field) { - // We should not hit out of bounds here as we have a predicate - // that should not be hit + // We should not hit out of bounds here as we have a predicate + // that should not be hit if idx as u32 < 3 { - x[idx] = 10; + x[idx] = 10; } assert(x[4] == 111); } diff --git a/tooling/nargo_cli/tests/execution_success/slice_struct_field/Nargo.toml b/tooling/nargo_cli/tests/execution_success/slice_struct_field/Nargo.toml new file mode 100644 index 00000000000..9530ebf9271 --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/slice_struct_field/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "slice_struct_field" +type = "bin" +authors = [""] + +[dependencies] diff --git a/tooling/nargo_cli/tests/execution_success/slice_struct_field/Prover.toml b/tooling/nargo_cli/tests/execution_success/slice_struct_field/Prover.toml new file mode 100644 index 00000000000..7127baac5bf --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/slice_struct_field/Prover.toml @@ -0,0 +1 @@ +y = "3" diff --git a/tooling/nargo_cli/tests/execution_success/slice_struct_field/src/main.nr b/tooling/nargo_cli/tests/execution_success/slice_struct_field/src/main.nr new file mode 100644 index 00000000000..a8ef5db063a --- /dev/null +++ b/tooling/nargo_cli/tests/execution_success/slice_struct_field/src/main.nr @@ -0,0 +1,184 @@ +struct FooParent { + parent_arr: [Field; 3], + foos: [Foo], +} + +struct Bar { + inner: [Field; 3], +} + +struct Foo { + a: Field, + b: [Field], + bar: Bar, +} + +fn main(y : pub Field) { + let mut b_one = [2, 3, 20]; + b_one = b_one.push_back(20); + let foo_one = Foo { a: 1, b: b_one, bar: Bar { inner: [100, 101, 102] } }; + + let mut b_two = [5, 6, 21]; + b_two = b_two.push_back(21); + let foo_two = Foo { a: 4, b: b_two, bar: Bar { inner: [103, 104, 105] } }; + + let foo_three = Foo { a: 7, b: [8, 9, 22], bar: Bar { inner: [106, 107, 108] } }; + let foo_four = Foo { a: 10, b: [11, 12, 23], bar: Bar { inner: [109, 110, 111] } }; + + let mut x = [foo_one, foo_two]; + x = x.push_back(foo_three); + x = x.push_back(foo_four); + + assert(x[y - 3].a == 1); + let struct_slice = x[y - 3].b; + for i in 0..4 { + assert(struct_slice[i] == b_one[i]); + } + + assert(x[y - 2].a == 4); + let struct_slice = x[y - 2].b; + for i in 0..4 { + assert(struct_slice[i] == b_two[i]); + } + + assert(x[y - 1].a == 7); + let struct_slice = x[y - 1].b; + assert(struct_slice[0] == 8); + assert(struct_slice[1] == 9); + assert(struct_slice[2] == 22); + + assert(x[y].a == 10); + let struct_slice = x[y].b; + assert(struct_slice[0] == 11); + assert(struct_slice[1] == 12); + assert(struct_slice[2] == 23); + assert(x[y].bar.inner == [109, 110, 111]); + + assert(x[y - 3].bar.inner == [100, 101, 102]); + assert(x[y - 2].bar.inner == [103, 104, 105]); + assert(x[y - 1].bar.inner == [106, 107, 108]); + assert(x[y].bar.inner == [109, 110, 111]); + // Check that switching the lhs and rhs is still valid + assert([109, 110, 111] == x[y].bar.inner); + + // TODO: Enable merging nested slices + // if y != 2 { + // x[y].a = 50; + // } else { + // x[y].a = 100; + // } + // assert(x[3].a == 50); + // if y == 2 { + // x[y - 1].b = [50, 51, 52]; + // } else { + // x[y - 1].b = [100, 101, 102]; + // } + // assert(x[2].b[0] == 100); + // assert(x[2].b[1] == 101); + // assert(x[2].b[2] == 102); + + let q = x.push_back(foo_four); + let foo_parent_one = FooParent { parent_arr: [0, 1, 2], foos: x }; + let foo_parent_two = FooParent { parent_arr: [3, 4, 5], foos: q }; + let mut foo_parents = [foo_parent_one]; + foo_parents = foo_parents.push_back(foo_parent_two); + + // TODO: make a separate test for compile time + // foo_parents[1].foos.push_back(foo_four); + + // TODO: Merging nested slices is broken + // if y == 3 { + // foo_parents[y - 2].foos[y - 1].b[y - 1] = 5000; + // } else { + // foo_parents[y - 2].foos[y - 1].b[y - 1] = 1000; + // } + + assert(foo_parents[y - 2].foos[y - 2].b[y - 1] == 21); + foo_parents[y - 2].foos[y - 2].b[y - 1] = 5000; + assert(foo_parents[y - 2].foos[y - 2].b[y - 1] == 5000); + + let b_array = foo_parents[y - 2].foos[y - 3].b; + assert(foo_parents[y - 2].foos[y - 3].a == 1); + assert(b_array[0] == 2); + assert(b_array[1] == 3); + assert(b_array[2] == 20); + assert(b_array[3] == 20); + + let b_array = foo_parents[y - 2].foos[y - 2].b; + assert(foo_parents[y - 2].foos[y - 2].a == 4); + assert(b_array[0] == 5); + assert(b_array[1] == 6); + assert(b_array[2] == 5000); + assert(b_array[3] == 21); + + assert(foo_parents[y - 2].foos[y - 1].a == 7); + foo_parents[y - 2].foos[y - 1].a = 50; + + let b_array = foo_parents[y - 2].foos[y - 1].b; + assert(b_array[2] == 22); + assert(b_array.len() == 3); + + // Test setting a nested array with non-dynamic + let x = [5, 6, 5000, 21, 100, 101].as_slice(); + foo_parents[y - 2].foos[y - 1].b = x; + + assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); + assert(foo_parents[y - 2].foos[y - 1].b[4] == 100); + assert(foo_parents[y - 2].foos[y - 1].b[5] == 101); + + test_basic_intrinsics_nested_slices(foo_parents, y); + // TODO(#3364): still have to enable slice intrinsics on dynamic nested slices + // assert(foo_parents[y - 2].foos.len() == 5); + // foo_parents[y - 2].foos = foo_parents[y - 2].foos.push_back(foo_four); + // assert(foo_parents[y - 2].foos.len() == 6); + + let b_array = foo_parents[y - 2].foos[y - 1].b; + assert(b_array[0] == 5); + assert(b_array[1] == 6); + assert(b_array[2] == 5000); + assert(b_array[3] == 21); + + let b_array = foo_parents[y - 2].foos[y].b; + assert(foo_parents[y - 2].foos[y].a == 10); + assert(b_array[0] == 11); + assert(b_array[1] == 12); + assert(b_array[2] == 23); + + assert(foo_parents[y - 2].foos[y - 3].bar.inner == [100, 101, 102]); + assert(foo_parents[y - 2].foos[y - 2].bar.inner == [103, 104, 105]); + assert(foo_parents[y - 2].foos[y - 1].bar.inner == [106, 107, 108]); + assert(foo_parents[y - 2].foos[y].bar.inner == [109, 110, 111]); +} + +fn test_basic_intrinsics_nested_slices(mut foo_parents: [FooParent], y: Field) { + foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.push_back(500); + assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); + assert(foo_parents[y - 2].foos[y - 1].b[6] == 500); + + let (popped_slice, last_elem) = foo_parents[y - 2].foos[y - 1].b.pop_back(); + foo_parents[y - 2].foos[y - 1].b = popped_slice; + assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); + assert(last_elem == 500); + + foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.push_front(11); + assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); + assert(foo_parents[y - 2].foos[y - 1].b[0] == 11); + + let (first_elem, rest_of_slice) = foo_parents[y - 2].foos[y - 1].b.pop_front(); + foo_parents[y - 2].foos[y - 1].b = rest_of_slice; + assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); + assert(first_elem == 11); + + foo_parents[y - 2].foos[y - 1].b = foo_parents[y - 2].foos[y - 1].b.insert(2, 20); + assert(foo_parents[y - 2].foos[y - 1].b.len() == 7); + assert(foo_parents[y - 2].foos[y - 1].b[y - 1] == 20); + assert(foo_parents[y - 2].foos[y - 1].b[y] == 5000); + assert(foo_parents[y - 2].foos[y - 1].b[6] == 101); + + let (rest_of_slice, removed_elem) = foo_parents[y - 2].foos[y - 1].b.remove(3); + foo_parents[y - 2].foos[y - 1].b = rest_of_slice; + assert(removed_elem == 5000); + assert(foo_parents[y - 2].foos[y - 1].b.len() == 6); + assert(foo_parents[y - 2].foos[y - 1].b[2] == 20); + assert(foo_parents[y - 2].foos[y - 1].b[3] == 21); +} \ No newline at end of file diff --git a/tooling/noir_js/src/index.ts b/tooling/noir_js/src/index.ts index 35a3e716b6b..5e700a5b583 100644 --- a/tooling/noir_js/src/index.ts +++ b/tooling/noir_js/src/index.ts @@ -1,6 +1,6 @@ import * as acvm from '@noir-lang/acvm_js'; import * as abi from '@noir-lang/noirc_abi'; -export { acvm, abi }; +import { CompiledCircuit, ProofData } from '@noir-lang/types'; export { ecdsa_secp256r1_verify, @@ -11,6 +11,13 @@ export { xor, and, } from '@noir-lang/acvm_js'; + export { WitnessMap, ForeignCallHandler, ForeignCallInput, ForeignCallOutput } from '@noir-lang/acvm_js'; export { Noir } from './program.js'; + +/** @ignore */ +export { acvm, abi }; + +// type exports for typedoc +export { CompiledCircuit, ProofData }; diff --git a/tooling/noir_js/src/program.ts b/tooling/noir_js/src/program.ts index cfd7a715dfe..711413bbc84 100644 --- a/tooling/noir_js/src/program.ts +++ b/tooling/noir_js/src/program.ts @@ -10,6 +10,7 @@ export class Noir { private backend?: Backend, ) {} + /** @ignore */ async init(): Promise { // If these are available, then we are in the // web environment. For the node environment, this @@ -19,6 +20,17 @@ export class Noir { } } + /** + * + * @description + * Destroys the underlying backend instance. + * + * @example + * ```typescript + * await noir.destroy(); + * ``` + * + */ async destroy(): Promise { await this.backend?.destroy(); } @@ -29,6 +41,15 @@ export class Noir { } // Initial inputs to your program + /** + * @description + * Allows to execute a circuit to get its witness and return value. + * + * @example + * ```typescript + * async execute(inputs) + * ``` + */ async execute( inputs: InputMap, foreignCallHandler?: ForeignCallHandler, @@ -39,12 +60,34 @@ export class Noir { return { witness: compressWitness(witness), returnValue }; } - // Initial inputs to your program + /** + * + * @description + * Generates a witness and a proof given an object as input. + * + * @example + * ```typescript + * async generateFinalproof(input) + * ``` + * + */ async generateFinalProof(inputs: InputMap): Promise { const { witness } = await this.execute(inputs); return this.getBackend().generateFinalProof(witness); } + /** + * + * @description + * Instantiates the verification key and verifies a proof. + * + * + * @example + * ```typescript + * async verifyFinalProof(proof) + * ``` + * + */ async verifyFinalProof(proofData: ProofData): Promise { return this.getBackend().verifyFinalProof(proofData); } 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 3b2b1b2c5a1..c01b2c5d3f5 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":[1],"y":[2]},"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 +{"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 diff --git a/tooling/noir_js/tsconfig.json b/tooling/noir_js/tsconfig.json index 1e0fdea09c7..0dbc5204556 100644 --- a/tooling/noir_js/tsconfig.json +++ b/tooling/noir_js/tsconfig.json @@ -2,7 +2,6 @@ "compilerOptions": { "target": "esnext", "declaration": true, - "emitDeclarationOnly": false, "module": "ESNext", "moduleResolution": "node", "outDir": "./lib", @@ -12,5 +11,10 @@ "noImplicitAny": false, }, "include": ["src/**/*.ts"], - "exclude": ["node_modules"] -} \ No newline at end of file + "exclude": ["node_modules"], + "references": [ + { + "path": "../noir_js_types" + }, + ] +} diff --git a/tooling/noir_js_backend_barretenberg/src/index.ts b/tooling/noir_js_backend_barretenberg/src/index.ts index 11c56a7384d..2e5a44ae8d2 100644 --- a/tooling/noir_js_backend_barretenberg/src/index.ts +++ b/tooling/noir_js_backend_barretenberg/src/index.ts @@ -25,6 +25,7 @@ export class BarretenbergBackend implements Backend { this.acirUncompressedBytecode = acirToUint8Array(acirBytecodeBase64); } + /** @ignore */ async instantiate(): Promise { if (!this.api) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -63,11 +64,20 @@ export class BarretenbergBackend implements Backend { // We set `makeEasyToVerifyInCircuit` to true, which will tell the backend to // generate the proof using components that will make the proof // easier to verify in a circuit. + + /** + * + * @example + * ```typescript + * const intermediateProof = await backend.generateIntermediateProof(witness); + * ``` + */ async generateIntermediateProof(witness: Uint8Array): Promise { const makeEasyToVerifyInCircuit = true; return this.generateProof(witness, makeEasyToVerifyInCircuit); } + /** @ignore */ async generateProof(compressedWitness: Uint8Array, makeEasyToVerifyInCircuit: boolean): Promise { await this.instantiate(); const proofWithPublicInputs = await this.api.acirCreateProof( @@ -103,6 +113,14 @@ export class BarretenbergBackend implements Backend { // method. // // The number of public inputs denotes how many public inputs are in the inner proof. + + /** + * + * @example + * ```typescript + * const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); + * ``` + */ async generateIntermediateProofArtifacts( proofData: ProofData, numOfPublicInputs = 0, @@ -136,12 +154,20 @@ export class BarretenbergBackend implements Backend { return verified; } + /** + * + * @example + * ```typescript + * const isValidIntermediate = await backend.verifyIntermediateProof(proof); + * ``` + */ async verifyIntermediateProof(proofData: ProofData): Promise { const proof = reconstructProofWithPublicInputs(proofData); const makeEasyToVerifyInCircuit = true; return this.verifyProof(proof, makeEasyToVerifyInCircuit); } + /** @ignore */ async verifyProof(proof: Uint8Array, makeEasyToVerifyInCircuit: boolean): Promise { await this.instantiate(); await this.api.acirInitVerificationKey(this.acirComposer); @@ -178,3 +204,6 @@ function flattenUint8Arrays(arrays: Uint8Array[]): Uint8Array { return result; } + +// typedoc exports +export { Backend, BackendOptions, CompiledCircuit, ProofData }; diff --git a/tooling/noir_js_backend_barretenberg/src/types.ts b/tooling/noir_js_backend_barretenberg/src/types.ts index b88a942d986..041e36fdf91 100644 --- a/tooling/noir_js_backend_barretenberg/src/types.ts +++ b/tooling/noir_js_backend_barretenberg/src/types.ts @@ -1,3 +1,8 @@ +/** + * @description + * An options object, currently only used to specify the number of threads to use. + */ export type BackendOptions = { + /** @description Number of threads */ threads: number; }; diff --git a/tooling/noir_js_backend_barretenberg/tsconfig.json b/tooling/noir_js_backend_barretenberg/tsconfig.json index 393fa38f583..1e28c044bba 100644 --- a/tooling/noir_js_backend_barretenberg/tsconfig.json +++ b/tooling/noir_js_backend_barretenberg/tsconfig.json @@ -12,5 +12,10 @@ "noImplicitAny": false, }, "include": ["src/**/*.ts"], - "exclude": ["node_modules"] + "exclude": ["node_modules"], + "references": [ + { + "path": "../noir_js_types" + } + ] } diff --git a/tooling/noir_js_types/.gitignore b/tooling/noir_js_types/.gitignore index 7951405f85a..e92523b6247 100644 --- a/tooling/noir_js_types/.gitignore +++ b/tooling/noir_js_types/.gitignore @@ -1 +1,3 @@ -lib \ No newline at end of file +lib + +*.tsbuildinfo diff --git a/tooling/noir_js_types/src/types.ts b/tooling/noir_js_types/src/types.ts index f534ec9a920..5ed6b1721e9 100644 --- a/tooling/noir_js_types/src/types.ts +++ b/tooling/noir_js_types/src/types.ts @@ -1,25 +1,60 @@ import { Abi } from '@noir-lang/noirc_abi'; export interface Backend { - // Generate an outer proof. This is the proof for the circuit which will verify - // inner proofs and or can be seen as the proof created for regular circuits. + /** + * @description Generates a final proof (not meant to be verified in another circuit) */ generateFinalProof(decompressedWitness: Uint8Array): Promise; - // Generates an inner proof. This is the proof that will be verified - // in another circuit. + /** + * @description Generates an intermediate proof (meant to be verified in another circuit) */ generateIntermediateProof(decompressedWitness: Uint8Array): Promise; + /** + * + * @description Retrieves the artifacts from a proof in the Field format + */ + generateIntermediateProofArtifacts( + proofData: ProofData, + numOfPublicInputs: number, + ): Promise<{ + /** @description An array of Fields containing the proof */ + proofAsFields: string[]; + /** @description An array of Fields containing the verification key */ + vkAsFields: string[]; + /** @description A Field containing the verification key hash */ + vkHash: string; + }>; + + /** + * @description Verifies a final proof */ verifyFinalProof(proofData: ProofData): Promise; + + /** @description Verifies an intermediate proof */ verifyIntermediateProof(proofData: ProofData): Promise; + + /** + * @description Destroys the backend */ destroy(): Promise; } +/** + * @description + * The representation of a proof + * */ export type ProofData = { + /** @description Public inputs of a proof */ publicInputs: Uint8Array[]; + /** @description An byte array representing the proof */ proof: Uint8Array; }; +/** + * @description + * The representation of a compiled circuit + * */ export type CompiledCircuit = { + /** @description The bytecode of the circuit */ bytecode: string; + /** @description ABI representation of the circuit */ abi: Abi; }; diff --git a/tooling/noir_js_types/tsconfig.json b/tooling/noir_js_types/tsconfig.json index 1fe2a46abaa..0d5441cc5a2 100644 --- a/tooling/noir_js_types/tsconfig.json +++ b/tooling/noir_js_types/tsconfig.json @@ -1,12 +1,13 @@ { "compilerOptions": { + "composite": true, "declaration": true, "module": "ESNext", "moduleResolution": "node", "outDir": "lib/esm", "target": "ES2020", - "rootDir": "./src" + "rootDir": "./src", }, "include": ["src/**/*.ts"], "exclude": ["node_modules"] -} \ No newline at end of file +} diff --git a/tooling/noirc_abi/src/lib.rs b/tooling/noirc_abi/src/lib.rs index 9753dc21f14..7092f05c26e 100644 --- a/tooling/noirc_abi/src/lib.rs +++ b/tooling/noirc_abi/src/lib.rs @@ -3,8 +3,6 @@ #![warn(unreachable_pub)] #![warn(clippy::semicolon_if_nothing_returned)] -use std::{collections::BTreeMap, str}; - use acvm::{ acir::native_types::{Witness, WitnessMap}, FieldElement, @@ -16,6 +14,8 @@ use noirc_frontend::{ hir::Context, Signedness, StructType, Type, TypeBinding, TypeVariableKind, Visibility, }; use serde::{Deserialize, Serialize}; +use std::ops::Range; +use std::{collections::BTreeMap, str}; // This is the ABI used to bridge the different TOML formats for the initial // witness, the partial witness generator and the interpreter. // @@ -218,7 +218,7 @@ pub struct Abi { 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>, + pub param_witnesses: BTreeMap>>, pub return_type: Option, pub return_witnesses: Vec, } @@ -315,13 +315,14 @@ impl Abi { let mut witness_map: BTreeMap = encoded_input_map .iter() .flat_map(|(param_name, encoded_param_fields)| { - let param_witness_indices = &self.param_witnesses[param_name]; + let param_witness_indices = range_to_vec(&self.param_witnesses[param_name]); param_witness_indices .iter() .zip(encoded_param_fields.iter()) .map(|(&witness, &field_element)| (witness, field_element)) + .collect::>() }) - .collect(); + .collect::>(); // 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. @@ -398,7 +399,7 @@ impl Abi { 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| { + try_vecmap(range_to_vec(&self.param_witnesses[&name]), |witness_index| { witness_map .get(&witness_index) .ok_or_else(|| AbiError::MissingParamWitnessValue { @@ -529,6 +530,16 @@ impl ContractEvent { } } +fn range_to_vec(ranges: &[Range]) -> Vec { + let mut result = Vec::new(); + for range in ranges { + for witness in range.start.witness_index()..range.end.witness_index() { + result.push(witness.into()); + } + } + result +} + #[cfg(test)] mod test { use std::collections::BTreeMap; @@ -554,8 +565,8 @@ mod test { ], // 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)]), + ("thing1".to_string(), vec![(Witness(1)..Witness(3))]), + ("thing2".to_string(), vec![(Witness(3)..Witness(4))]), ]), return_type: Some(AbiType::Field), return_witnesses: vec![Witness(3)], diff --git a/tooling/noirc_abi_wasm/src/lib.rs b/tooling/noirc_abi_wasm/src/lib.rs index ea03aa8abe7..734ca1ff40e 100644 --- a/tooling/noirc_abi_wasm/src/lib.rs +++ b/tooling/noirc_abi_wasm/src/lib.rs @@ -57,7 +57,7 @@ export type AbiType = { kind: "array", length: number, type: AbiType } | { kind: "tuple", fields: AbiType[] } | { kind: "struct", path: string, fields: [string, AbiType][] }; - + export type AbiParameter = { name: string, type: AbiType, @@ -66,7 +66,7 @@ export type AbiParameter = { export type Abi = { parameters: AbiParameter[], - param_witnesses: Record, + param_witnesses: Record, return_type: AbiType | null, return_witnesses: number[], } diff --git a/tooling/noirc_abi_wasm/test/shared/abi_encode.ts b/tooling/noirc_abi_wasm/test/shared/abi_encode.ts index 28379745dec..cb80c6710ba 100644 --- a/tooling/noirc_abi_wasm/test/shared/abi_encode.ts +++ b/tooling/noirc_abi_wasm/test/shared/abi_encode.ts @@ -9,7 +9,7 @@ export const abi: Abi = { visibility: 'private', }, ], - param_witnesses: { foo: [1], bar: [2, 3] }, + param_witnesses: { foo: [{ start: 1, end: 2 }], bar: [{ start: 2, end: 4 }] }, return_type: null, return_witnesses: [], }; diff --git a/tooling/noirc_abi_wasm/test/shared/array_as_field.ts b/tooling/noirc_abi_wasm/test/shared/array_as_field.ts index ba58f075702..0cc0035fa68 100644 --- a/tooling/noirc_abi_wasm/test/shared/array_as_field.ts +++ b/tooling/noirc_abi_wasm/test/shared/array_as_field.ts @@ -8,7 +8,7 @@ export const abi: Abi = { visibility: 'private', }, ], - param_witnesses: { foo: [1, 2] }, + param_witnesses: { foo: [{ start: 1, end: 3 }] }, return_type: null, return_witnesses: [], }; diff --git a/tooling/noirc_abi_wasm/test/shared/field_as_array.ts b/tooling/noirc_abi_wasm/test/shared/field_as_array.ts index 931720d5e1b..6ae709459de 100644 --- a/tooling/noirc_abi_wasm/test/shared/field_as_array.ts +++ b/tooling/noirc_abi_wasm/test/shared/field_as_array.ts @@ -8,7 +8,7 @@ export const abi: Abi = { visibility: 'private', }, ], - param_witnesses: { foo: [1, 2] }, + param_witnesses: { foo: [{ start: 1, end: 3 }] }, return_type: null, return_witnesses: [], }; diff --git a/tooling/noirc_abi_wasm/test/shared/uint_overflow.ts b/tooling/noirc_abi_wasm/test/shared/uint_overflow.ts index ee87e050b23..c6e066e2bcd 100644 --- a/tooling/noirc_abi_wasm/test/shared/uint_overflow.ts +++ b/tooling/noirc_abi_wasm/test/shared/uint_overflow.ts @@ -8,7 +8,7 @@ export const abi: Abi = { visibility: 'private', }, ], - param_witnesses: { foo: [1] }, + param_witnesses: { foo: [{ start: 1, end: 2 }] }, return_type: null, return_witnesses: [], }; diff --git a/yarn.lock b/yarn.lock index f423ba8b384..87713e1f915 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2281,7 +2281,7 @@ __metadata: languageName: node linkType: hard -"@docusaurus/types@npm:2.4.3": +"@docusaurus/types@npm:2.4.3, @docusaurus/types@npm:^2.4.1": version: 2.4.3 resolution: "@docusaurus/types@npm:2.4.3" dependencies: @@ -5915,6 +5915,13 @@ __metadata: languageName: node linkType: hard +"ansi-sequence-parser@npm:^1.1.0": + version: 1.1.1 + resolution: "ansi-sequence-parser@npm:1.1.1" + checksum: ead5b15c596e8e85ca02951a844366c6776769dcc9fd1bd3a0db11bb21364554822c6a439877fb599e7e1ffa0b5f039f1e5501423950457f3dcb2f480c30b188 + languageName: node + linkType: hard + "ansi-styles@npm:^3.2.1": version: 3.2.1 resolution: "ansi-styles@npm:3.2.1" @@ -8285,12 +8292,18 @@ __metadata: "@mdx-js/react": ^1.6.22 axios: ^1.4.0 clsx: ^1.2.1 + docusaurus-plugin-typedoc: 1.0.0-next.18 hast-util-is-element: ^1.1.0 prism-react-renderer: ^1.3.5 react: ^17.0.2 react-dom: ^17.0.2 rehype-katex: ^5.0.0 remark-math: ^3.0.1 + typedoc: ^0.25.0 + typedoc-plugin-frontmatter: ^0.0.2 + typedoc-plugin-markdown: 4.0.0-next.25 + typedoc-plugin-merge-modules: ^5.1.0 + typescript: ^5.2.2 languageName: unknown linkType: soft @@ -8303,6 +8316,17 @@ __metadata: languageName: node linkType: hard +"docusaurus-plugin-typedoc@npm:1.0.0-next.18": + version: 1.0.0-next.18 + resolution: "docusaurus-plugin-typedoc@npm:1.0.0-next.18" + dependencies: + "@docusaurus/types": ^2.4.1 + peerDependencies: + typedoc-plugin-markdown: ">=4.0.0-next.24" + checksum: a501e3bd1cc5b33d215a1b71b018a34d4aa5bd98f39580ab114a127d3f555078443d6b690119c0adb17fb8867ba4131382ad5d14dbaa7919715020cdfaf6b9c4 + languageName: node + linkType: hard + "dom-converter@npm:^0.2.0": version: 0.2.0 resolution: "dom-converter@npm:0.2.0" @@ -11459,6 +11483,13 @@ __metadata: languageName: node linkType: hard +"jsonc-parser@npm:^3.2.0": + version: 3.2.0 + resolution: "jsonc-parser@npm:3.2.0" + checksum: 946dd9a5f326b745aa326d48a7257e3f4a4b62c5e98ec8e49fa2bdd8d96cef7e6febf1399f5c7016114fd1f68a1c62c6138826d5d90bc650448e3cf0951c53c7 + languageName: node + linkType: hard + "jsonfile@npm:^2.1.0": version: 2.4.0 resolution: "jsonfile@npm:2.4.0" @@ -12140,6 +12171,15 @@ __metadata: languageName: node linkType: hard +"marked@npm:^4.3.0": + version: 4.3.0 + resolution: "marked@npm:4.3.0" + bin: + marked: bin/marked.js + checksum: 0db6817893952c3ec710eb9ceafb8468bf5ae38cb0f92b7b083baa13d70b19774674be04db5b817681fa7c5c6a088f61300815e4dd75a59696f4716ad69f6260 + languageName: node + linkType: hard + "marky@npm:^1.2.2": version: 1.2.5 resolution: "marky@npm:1.2.5" @@ -12425,7 +12465,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.1": +"minimatch@npm:^9.0.1, minimatch@npm:^9.0.3": version: 9.0.3 resolution: "minimatch@npm:9.0.3" dependencies: @@ -15471,6 +15511,18 @@ __metadata: languageName: node linkType: hard +"shiki@npm:^0.14.1": + version: 0.14.5 + resolution: "shiki@npm:0.14.5" + dependencies: + ansi-sequence-parser: ^1.1.0 + jsonc-parser: ^3.2.0 + vscode-oniguruma: ^1.7.0 + vscode-textmate: ^8.0.0 + checksum: 41d847817cfc9bb6d8bf190316896698d250303656546446659cc02caed8dcc171b10cd113bb5da82425b51d0032e87aafcdc36c3dd61dadc123170b438da736 + languageName: node + linkType: hard + "side-channel@npm:^1.0.4": version: 1.0.4 resolution: "side-channel@npm:1.0.4" @@ -16511,6 +16563,49 @@ __metadata: languageName: node linkType: hard +"typedoc-plugin-frontmatter@npm:^0.0.2": + version: 0.0.2 + resolution: "typedoc-plugin-frontmatter@npm:0.0.2" + dependencies: + yaml: ^2.2.2 + checksum: 44cbdb82e3fd8f4eb89cdf54783b5b07b03a57edc7bda85a48280edba73f401a2f5439cbba97426dd79e9584c410244af5dd20d5d7281c27d67d61675fa7aaef + languageName: node + linkType: hard + +"typedoc-plugin-markdown@npm:4.0.0-next.25": + version: 4.0.0-next.25 + resolution: "typedoc-plugin-markdown@npm:4.0.0-next.25" + peerDependencies: + typedoc: ">=0.25.0" + checksum: 284e2ce44446faf8db5fc54dfa84e7cd819cfc60bbed12ffb781cb24a166ba6b3a220c542990a3eb14aefff33f1d7c8322378297551f55476155cf532fbbb807 + languageName: node + linkType: hard + +"typedoc-plugin-merge-modules@npm:^5.1.0": + version: 5.1.0 + resolution: "typedoc-plugin-merge-modules@npm:5.1.0" + peerDependencies: + typedoc: 0.24.x || 0.25.x + checksum: f01d825a1c6b73c29faaf515e76076931b16bcc5762c8e9b56d18a7eca6d450bd3691012e96bc3a09ce05f29aef90744e93e187171c561ef0a3c2c1fe116803f + languageName: node + linkType: hard + +"typedoc@npm:^0.25.0": + version: 0.25.2 + resolution: "typedoc@npm:0.25.2" + dependencies: + lunr: ^2.3.9 + marked: ^4.3.0 + minimatch: ^9.0.3 + shiki: ^0.14.1 + peerDependencies: + typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x + bin: + typedoc: bin/typedoc + checksum: 5b6e24bae7498bb542aaba495378ed5a3e13c76eb04a1ae95b506f76bda4d517847101fb05a7eab3f6b79357d1e2ac6f4747d39792395329b72e463f7effda65 + languageName: node + linkType: hard + "typescript@npm:4.9.4": version: 4.9.4 resolution: "typescript@npm:4.9.4" @@ -17054,6 +17149,20 @@ __metadata: languageName: node linkType: hard +"vscode-oniguruma@npm:^1.7.0": + version: 1.7.0 + resolution: "vscode-oniguruma@npm:1.7.0" + checksum: 53519d91d90593e6fb080260892e87d447e9b200c4964d766772b5053f5699066539d92100f77f1302c91e8fc5d9c772fbe40fe4c90f3d411a96d5a9b1e63f42 + languageName: node + linkType: hard + +"vscode-textmate@npm:^8.0.0": + version: 8.0.0 + resolution: "vscode-textmate@npm:8.0.0" + checksum: 127780dfea89559d70b8326df6ec344cfd701312dd7f3f591a718693812b7852c30b6715e3cfc8b3200a4e2515b4c96f0843c0eacc0a3020969b5de262c2a4bb + languageName: node + linkType: hard + "wait-on@npm:^6.0.1": version: 6.0.1 resolution: "wait-on@npm:6.0.1" @@ -17595,6 +17704,13 @@ __metadata: languageName: node linkType: hard +"yaml@npm:^2.2.2": + version: 2.3.3 + resolution: "yaml@npm:2.3.3" + checksum: cdfd132e7e0259f948929efe8835923df05c013c273c02bb7a2de9b46ac3af53c2778a35b32c7c0f877cc355dc9340ed564018c0242bfbb1278c2a3e53a0e99e + languageName: node + linkType: hard + "yargs-parser@npm:20.2.4": version: 20.2.4 resolution: "yargs-parser@npm:20.2.4"