diff --git a/noir/.github/workflows/test-js-packages.yml b/noir/.github/workflows/test-js-packages.yml index 31bd1219654..addc9ce3d83 100644 --- a/noir/.github/workflows/test-js-packages.yml +++ b/noir/.github/workflows/test-js-packages.yml @@ -80,7 +80,9 @@ jobs: uses: actions/upload-artifact@v3 with: name: noir_wasm - path: ./compiler/wasm + path: | + ./compiler/wasm/dist + ./compiler/wasm/build retention-days: 3 build-acvm-js: diff --git a/noir/.gitrepo b/noir/.gitrepo index 38e6027e885..f33a81ba0a4 100644 --- a/noir/.gitrepo +++ b/noir/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/noir-lang/noir branch = aztec-packages - commit = 5162b33c7cf2e942e2f25a90938c8b9b014b448a + commit = d7c4c669c6f2f2738b4764addcd7859bdf37004e parent = a907c3b9d21c653162161091e8c97e924a0e8542 method = merge cmdver = 0.4.6 diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index f8b8579eb66..ceca49cd749 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -135,6 +135,13 @@ impl AcirContext { self.add_data(constant_data) } + /// Returns the constant represented by the given variable. + /// + /// Panics: if the variable does not represent a constant. + pub(crate) fn constant(&self, var: AcirVar) -> FieldElement { + self.vars[&var].as_constant().expect("ICE - expected the variable to be a constant value") + } + /// Adds a Variable to the context, whose exact value is resolved at /// runtime. pub(crate) fn add_variable(&mut self) -> AcirVar { diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index a1c96a3cd23..e65e71b045d 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -1712,6 +1712,9 @@ impl Context { Ok(Self::convert_vars_to_values(vars, dfg, result_ids)) } + Intrinsic::ApplyRangeConstraint => { + unreachable!("ICE: `Intrinsic::ApplyRangeConstraint` calls should be transformed into an `Instruction::RangeCheck`"); + } Intrinsic::ToRadix(endian) => { let field = self.convert_value(arguments[0], dfg).into_var()?; let radix = self.convert_value(arguments[1], dfg).into_var()?; @@ -2428,8 +2431,7 @@ impl Context { } } -// We can omit the element size array for arrays which have elements of size 1 and do not contain slices. -// TODO: remove restriction on size 1 elements. +// We can omit the element size array for arrays which don't contain arrays or slices. fn can_omit_element_sizes_array(array_typ: &Type) -> bool { if array_typ.contains_slice_element() { return false; @@ -2438,5 +2440,5 @@ fn can_omit_element_sizes_array(array_typ: &Type) -> bool { panic!("ICE: expected array type"); }; - types.len() == 1 && types[0].flattened_size() == 1 + !types.iter().any(|typ| typ.contains_an_array()) } diff --git a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index f7875a73f6a..457fe41de93 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -40,6 +40,7 @@ pub(crate) enum Intrinsic { SlicePopFront, SliceInsert, SliceRemove, + ApplyRangeConstraint, StrAsBytes, ToBits(Endian), ToRadix(Endian), @@ -61,6 +62,7 @@ impl std::fmt::Display for Intrinsic { Intrinsic::SliceInsert => write!(f, "slice_insert"), Intrinsic::SliceRemove => write!(f, "slice_remove"), Intrinsic::StrAsBytes => write!(f, "str_as_bytes"), + Intrinsic::ApplyRangeConstraint => write!(f, "apply_range_constraint"), Intrinsic::ToBits(Endian::Big) => write!(f, "to_be_bits"), Intrinsic::ToBits(Endian::Little) => write!(f, "to_le_bits"), Intrinsic::ToRadix(Endian::Big) => write!(f, "to_be_radix"), @@ -78,7 +80,7 @@ impl Intrinsic { /// If there are no side effects then the `Intrinsic` can be removed if the result is unused. pub(crate) fn has_side_effects(&self) -> bool { match self { - Intrinsic::AssertConstant => true, + Intrinsic::AssertConstant | Intrinsic::ApplyRangeConstraint => true, Intrinsic::Sort | Intrinsic::ArrayLen @@ -106,6 +108,7 @@ impl Intrinsic { "arraysort" => Some(Intrinsic::Sort), "array_len" => Some(Intrinsic::ArrayLen), "assert_constant" => Some(Intrinsic::AssertConstant), + "apply_range_constraint" => Some(Intrinsic::ApplyRangeConstraint), "slice_push_back" => Some(Intrinsic::SlicePushBack), "slice_push_front" => Some(Intrinsic::SlicePushFront), "slice_pop_back" => Some(Intrinsic::SlicePopBack), diff --git a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index ac8239f97ac..b0ee8468bd8 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -233,6 +233,20 @@ pub(super) fn simplify_call( SimplifyResult::None } } + Intrinsic::ApplyRangeConstraint => { + let value = arguments[0]; + let max_bit_size = dfg.get_numeric_constant(arguments[1]); + if let Some(max_bit_size) = max_bit_size { + let max_bit_size = max_bit_size.to_u128() as u32; + SimplifyResult::SimplifiedToInstruction(Instruction::RangeCheck { + value, + max_bit_size, + assert_message: Some("call to assert_max_bit_size".to_owned()), + }) + } else { + SimplifyResult::None + } + } Intrinsic::BlackBox(bb_func) => simplify_black_box_func(bb_func, arguments, dfg), Intrinsic::Sort => simplify_sort(dfg, arguments), Intrinsic::AsField => { diff --git a/noir/compiler/wasm/.gitignore b/noir/compiler/wasm/.gitignore index 2c992f6c0b2..3ae8fb4b218 100644 --- a/noir/compiler/wasm/.gitignore +++ b/noir/compiler/wasm/.gitignore @@ -1,4 +1,3 @@ -noir-script/target dist build diff --git a/noir/noir_stdlib/src/field.nr b/noir/noir_stdlib/src/field.nr index b4cb9b64e3c..df00b3eb653 100644 --- a/noir/noir_stdlib/src/field.nr +++ b/noir/noir_stdlib/src/field.nr @@ -15,6 +15,15 @@ impl Field { #[builtin(to_be_bits)] fn __to_be_bits(_self: Self, _bit_size: u32) -> [u1] {} + #[builtin(apply_range_constraint)] + fn __assert_max_bit_size(_self: Self, _bit_size: u32) {} + + pub fn assert_max_bit_size(self: Self, bit_size: u32) { + crate::assert_constant(bit_size); + assert(bit_size < modulus_num_bits() as u32); + self.__assert_max_bit_size(bit_size); + } + pub fn to_le_bytes(self: Self, byte_size: u32) -> [u8] { self.to_le_radix(256, byte_size) } diff --git a/noir/test_programs/execution_success/unsafe_range_constraint/Nargo.toml b/noir/test_programs/execution_success/unsafe_range_constraint/Nargo.toml new file mode 100644 index 00000000000..8714d95ed54 --- /dev/null +++ b/noir/test_programs/execution_success/unsafe_range_constraint/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "unsafe_range_constraint" +version = "0.1.0" +type = "bin" +authors = [""] + +[dependencies] diff --git a/noir/test_programs/execution_success/unsafe_range_constraint/Prover.toml b/noir/test_programs/execution_success/unsafe_range_constraint/Prover.toml new file mode 100644 index 00000000000..07890234a19 --- /dev/null +++ b/noir/test_programs/execution_success/unsafe_range_constraint/Prover.toml @@ -0,0 +1 @@ +x = "3" diff --git a/noir/test_programs/execution_success/unsafe_range_constraint/src/main.nr b/noir/test_programs/execution_success/unsafe_range_constraint/src/main.nr new file mode 100644 index 00000000000..ead5613bcce --- /dev/null +++ b/noir/test_programs/execution_success/unsafe_range_constraint/src/main.nr @@ -0,0 +1,5 @@ +// Test that we can apply a range constraint to a field using +// a builtin. +fn main(x: Field) { + x.assert_max_bit_size(48); +} diff --git a/noir/tooling/nargo_fmt/tests/expected/contract.nr b/noir/tooling/nargo_fmt/tests/expected/contract.nr index 8e6000a8d78..2e3f4d7c8c4 100644 --- a/noir/tooling/nargo_fmt/tests/expected/contract.nr +++ b/noir/tooling/nargo_fmt/tests/expected/contract.nr @@ -20,8 +20,8 @@ contract Benchmarking { }; struct Storage { - notes: Map>, - balances: Map>, + notes: Map>, + balances: Map>, } impl Storage {