From 6e8b665d722664a07b919b42e73eee333701b657 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Thu, 11 Jan 2024 17:35:03 +0000 Subject: [PATCH 01/10] add `apply_range_constraint` --- noir_stdlib/src/field.nr | 8 ++++++++ .../execution_success/unsafe_range_constraint/Nargo.toml | 7 +++++++ .../execution_success/unsafe_range_constraint/Prover.toml | 1 + .../execution_success/unsafe_range_constraint/src/main.nr | 5 +++++ 4 files changed, 21 insertions(+) create mode 100644 test_programs/execution_success/unsafe_range_constraint/Nargo.toml create mode 100644 test_programs/execution_success/unsafe_range_constraint/Prover.toml create mode 100644 test_programs/execution_success/unsafe_range_constraint/src/main.nr diff --git a/noir_stdlib/src/field.nr b/noir_stdlib/src/field.nr index b4cb9b64e3c..7f36045839f 100644 --- a/noir_stdlib/src/field.nr +++ b/noir_stdlib/src/field.nr @@ -15,6 +15,14 @@ impl Field { #[builtin(to_be_bits)] fn __to_be_bits(_self: Self, _bit_size: u32) -> [u1] {} + #[builtin(apply_range_constraint)] + fn __apply_range_constraint(_self: Self, _bit_size: u32) {} + + pub fn apply_range_constraint(self: Self, bit_size: u32) { + crate::assert_constant(bit_size); + self.__apply_range_constraint(bit_size); + } + pub fn to_le_bytes(self: Self, byte_size: u32) -> [u8] { self.to_le_radix(256, byte_size) } diff --git a/test_programs/execution_success/unsafe_range_constraint/Nargo.toml b/test_programs/execution_success/unsafe_range_constraint/Nargo.toml new file mode 100644 index 00000000000..8714d95ed54 --- /dev/null +++ b/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/test_programs/execution_success/unsafe_range_constraint/Prover.toml b/test_programs/execution_success/unsafe_range_constraint/Prover.toml new file mode 100644 index 00000000000..07890234a19 --- /dev/null +++ b/test_programs/execution_success/unsafe_range_constraint/Prover.toml @@ -0,0 +1 @@ +x = "3" diff --git a/test_programs/execution_success/unsafe_range_constraint/src/main.nr b/test_programs/execution_success/unsafe_range_constraint/src/main.nr new file mode 100644 index 00000000000..50be8ca2441 --- /dev/null +++ b/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.apply_range_constraint(48); +} From b465aca7e874547ea771c64092ff452df4c2a675 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Thu, 11 Jan 2024 17:35:48 +0000 Subject: [PATCH 02/10] add intrinsic::apply_range_constraint and set side-effect to true --- compiler/noirc_evaluator/src/ssa/ir/instruction.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index f7875a73f6a..457fe41de93 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/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), From 4ba41628fe5a4660a7ac03a6a8d931c4b48518cd Mon Sep 17 00:00:00 2001 From: kevaundray Date: Thu, 11 Jan 2024 17:36:45 +0000 Subject: [PATCH 03/10] apply a range constraint whenever `Intrinsic::ApplyRangeConstraint` is called --- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 7 +++++++ compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs | 14 ++++++++++++++ .../noirc_evaluator/src/ssa/ir/instruction/call.rs | 1 + 3 files changed, 22 insertions(+) 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 ddafc0bb570..e7c55237259 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 @@ -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/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index a1c96a3cd23..30cd4dfb0d1 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -1712,6 +1712,20 @@ impl Context { Ok(Self::convert_vars_to_values(vars, dfg, result_ids)) } + Intrinsic::ApplyRangeConstraint => { + let field = self.convert_value(arguments[0], dfg).into_var()?; + + let bit_size = self.convert_value(arguments[1], dfg).into_var()?; + let bit_size = self.acir_context.constant(bit_size).to_u128() as u32; + + self.acir_context.range_constrain_var( + field, + &NumericType::Unsigned { bit_size }, + Some("call to apply_range_constraint".to_owned()), + )?; + + Ok(Vec::new()) + } Intrinsic::ToRadix(endian) => { let field = self.convert_value(arguments[0], dfg).into_var()?; let radix = self.convert_value(arguments[1], dfg).into_var()?; diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 146a4a8f124..11832f54343 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -233,6 +233,7 @@ pub(super) fn simplify_call( SimplifyResult::None } } + Intrinsic::ApplyRangeConstraint => SimplifyResult::None, Intrinsic::BlackBox(bb_func) => simplify_black_box_func(bb_func, arguments, dfg), Intrinsic::Sort => simplify_sort(dfg, arguments), Intrinsic::AsField => { From cd3b49b6073a8f0e63fd2290ef64aff8c82cf71c Mon Sep 17 00:00:00 2001 From: kevaundray Date: Thu, 11 Jan 2024 17:48:58 +0000 Subject: [PATCH 04/10] change stdlib name to assert_maximum_bit_size --- noir_stdlib/src/field.nr | 6 +++--- .../execution_success/unsafe_range_constraint/src/main.nr | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/noir_stdlib/src/field.nr b/noir_stdlib/src/field.nr index 7f36045839f..e1506473a03 100644 --- a/noir_stdlib/src/field.nr +++ b/noir_stdlib/src/field.nr @@ -16,11 +16,11 @@ impl Field { fn __to_be_bits(_self: Self, _bit_size: u32) -> [u1] {} #[builtin(apply_range_constraint)] - fn __apply_range_constraint(_self: Self, _bit_size: u32) {} + fn __assert_maximum_bit_size(_self: Self, _bit_size: u32) {} - pub fn apply_range_constraint(self: Self, bit_size: u32) { + pub fn assert_maximum_bit_size(self: Self, bit_size: u32) { crate::assert_constant(bit_size); - self.__apply_range_constraint(bit_size); + self.__assert_maximum_bit_size(bit_size); } pub fn to_le_bytes(self: Self, byte_size: u32) -> [u8] { diff --git a/test_programs/execution_success/unsafe_range_constraint/src/main.nr b/test_programs/execution_success/unsafe_range_constraint/src/main.nr index 50be8ca2441..cd8321ee2e0 100644 --- a/test_programs/execution_success/unsafe_range_constraint/src/main.nr +++ b/test_programs/execution_success/unsafe_range_constraint/src/main.nr @@ -1,5 +1,5 @@ // Test that we can apply a range constraint to a field using // a builtin. fn main(x: Field) { - x.apply_range_constraint(48); + x.assert_maximum_bit_size(48); } From 947b428a6785c08092dacc1bb7150211bff0a543 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Thu, 11 Jan 2024 18:35:25 +0000 Subject: [PATCH 05/10] Update noir_stdlib/src/field.nr Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- noir_stdlib/src/field.nr | 1 + 1 file changed, 1 insertion(+) diff --git a/noir_stdlib/src/field.nr b/noir_stdlib/src/field.nr index e1506473a03..3950c2486ae 100644 --- a/noir_stdlib/src/field.nr +++ b/noir_stdlib/src/field.nr @@ -21,6 +21,7 @@ impl Field { pub fn assert_maximum_bit_size(self: Self, bit_size: u32) { crate::assert_constant(bit_size); self.__assert_maximum_bit_size(bit_size); + assert(bit_size < modulus_num_bits()) } pub fn to_le_bytes(self: Self, byte_size: u32) -> [u8] { From 192765887fa347eeb6182fdf99e532c1627815ba Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 11 Jan 2024 18:40:24 +0000 Subject: [PATCH 06/10] Update noir_stdlib/src/field.nr --- noir_stdlib/src/field.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir_stdlib/src/field.nr b/noir_stdlib/src/field.nr index 3950c2486ae..05e0675744d 100644 --- a/noir_stdlib/src/field.nr +++ b/noir_stdlib/src/field.nr @@ -20,8 +20,8 @@ impl Field { pub fn assert_maximum_bit_size(self: Self, bit_size: u32) { crate::assert_constant(bit_size); + assert(bit_size < modulus_num_bits()); self.__assert_maximum_bit_size(bit_size); - assert(bit_size < modulus_num_bits()) } pub fn to_le_bytes(self: Self, byte_size: u32) -> [u8] { From e016972d783fbc1f1af7ea055d88db5e5dc0f939 Mon Sep 17 00:00:00 2001 From: Tom French Date: Thu, 11 Jan 2024 19:22:17 +0000 Subject: [PATCH 07/10] chore: convert `Intrinsic::ApplyRangeConstraint` into `Instruction::RangeCheck` --- compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs | 13 +------------ .../src/ssa/ir/instruction/call.rs | 15 ++++++++++++++- noir_stdlib/src/field.nr | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 30cd4dfb0d1..03fb4db73c5 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -1713,18 +1713,7 @@ impl Context { Ok(Self::convert_vars_to_values(vars, dfg, result_ids)) } Intrinsic::ApplyRangeConstraint => { - let field = self.convert_value(arguments[0], dfg).into_var()?; - - let bit_size = self.convert_value(arguments[1], dfg).into_var()?; - let bit_size = self.acir_context.constant(bit_size).to_u128() as u32; - - self.acir_context.range_constrain_var( - field, - &NumericType::Unsigned { bit_size }, - Some("call to apply_range_constraint".to_owned()), - )?; - - Ok(Vec::new()) + 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()?; diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 11832f54343..cfbde1b247d 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -233,7 +233,20 @@ pub(super) fn simplify_call( SimplifyResult::None } } - Intrinsic::ApplyRangeConstraint => 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 apply_range_constraint".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_stdlib/src/field.nr b/noir_stdlib/src/field.nr index 05e0675744d..2531cc10233 100644 --- a/noir_stdlib/src/field.nr +++ b/noir_stdlib/src/field.nr @@ -20,7 +20,7 @@ impl Field { pub fn assert_maximum_bit_size(self: Self, bit_size: u32) { crate::assert_constant(bit_size); - assert(bit_size < modulus_num_bits()); + assert(bit_size < modulus_num_bits() as u32); self.__assert_maximum_bit_size(bit_size); } From e14c038b62baf436f69fdf23e35b4c4218bf9afb Mon Sep 17 00:00:00 2001 From: Tom French Date: Fri, 12 Jan 2024 11:20:39 +0000 Subject: [PATCH 08/10] chore: `maximum` -> `max` --- noir_stdlib/src/field.nr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/noir_stdlib/src/field.nr b/noir_stdlib/src/field.nr index 2531cc10233..df00b3eb653 100644 --- a/noir_stdlib/src/field.nr +++ b/noir_stdlib/src/field.nr @@ -16,12 +16,12 @@ impl Field { fn __to_be_bits(_self: Self, _bit_size: u32) -> [u1] {} #[builtin(apply_range_constraint)] - fn __assert_maximum_bit_size(_self: Self, _bit_size: u32) {} + fn __assert_max_bit_size(_self: Self, _bit_size: u32) {} - pub fn assert_maximum_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_maximum_bit_size(bit_size); + self.__assert_max_bit_size(bit_size); } pub fn to_le_bytes(self: Self, byte_size: u32) -> [u8] { From 52446d5932ddf6ed0cce2cefa44d0ff45a10d2dd Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Fri, 12 Jan 2024 11:21:33 +0000 Subject: [PATCH 09/10] Update compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs --- compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index cfbde1b247d..68c7177f066 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -241,7 +241,7 @@ pub(super) fn simplify_call( SimplifyResult::SimplifiedToInstruction(Instruction::RangeCheck { value, max_bit_size, - assert_message: Some("call to apply_range_constraint".to_owned()), + assert_message: Some("call to assert_max_bit_size".to_owned()), }) } else { SimplifyResult::None From b0359a726be2dc4952f5a612a42ecc4d1a74a6f8 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Fri, 12 Jan 2024 11:22:03 +0000 Subject: [PATCH 10/10] Update test_programs/execution_success/unsafe_range_constraint/src/main.nr --- .../execution_success/unsafe_range_constraint/src/main.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_programs/execution_success/unsafe_range_constraint/src/main.nr b/test_programs/execution_success/unsafe_range_constraint/src/main.nr index cd8321ee2e0..ead5613bcce 100644 --- a/test_programs/execution_success/unsafe_range_constraint/src/main.nr +++ b/test_programs/execution_success/unsafe_range_constraint/src/main.nr @@ -1,5 +1,5 @@ // Test that we can apply a range constraint to a field using // a builtin. fn main(x: Field) { - x.assert_maximum_bit_size(48); + x.assert_max_bit_size(48); }