Skip to content

Commit 5e2a759

Browse files
committed
remove underflow
1 parent ad6e726 commit 5e2a759

File tree

2 files changed

+33
-31
lines changed

2 files changed

+33
-31
lines changed

src/fns/constrained_ops.nr

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use crate::fns::{
44
expressions::{evaluate_quadratic_expression, validate_udiv_mod_expression},
55
unconstrained_helpers::{
66
__add_with_flags, __from_field, __neg_with_flags, __sub_with_flags,
7-
__validate_gte_with_flags, __validate_in_field_compute_borrow_flags,
7+
__validate_gt_with_flags, __validate_gte_with_flags,
8+
__validate_in_field_compute_borrow_flags,
89
},
910
unconstrained_ops::{__add, __div, __mul, __neg, __sqr, __sub, __udiv_mod},
1011
};
@@ -481,16 +482,10 @@ pub(crate) fn validate_quotient_in_range<let N: u32, let MOD_BITS: u32>(limbs: [
481482
/// `assert_is_not_zero_integer(result)` is crucial. Without it, we could always provide
482483
/// two identical inputs `x`, `x` and set `borrow_flags = [false; N]`,
483484
/// which would satisfy the limb constraints.
484-
///
485-
/// Also note that `underflow` is not properly constrained, so it just hangs there for
486-
/// completeness
487485
pub(crate) fn validate_gt<let N: u32, let MOD_BITS: u32>(lhs: [u128; N], rhs: [u128; N]) {
488-
// Safety: compute borrow flags out-of-circuit
489-
let (underflow, result, borrow_flags): (bool, [u128; N], [bool; N - 1]) =
490-
unsafe { __validate_gte_with_flags(lhs, rhs) };
491-
492-
// Completeness: require that no underflow occurred
493-
assert(!underflow, "validate_gt fail");
486+
// Safety: compute witness values out-of-circuit
487+
let (result, borrow_flags): ([u128; N], [bool; N - 1]) =
488+
unsafe { __validate_gt_with_flags(lhs, rhs) };
494489

495490
// Constrain the `result` to be a valid `BigNum` value
496491
validate_in_range::<u128, N, MOD_BITS>(result);

src/fns/unconstrained_helpers.nr

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -189,34 +189,19 @@ pub(crate) unconstrained fn __validate_in_field_compute_borrow_flags<let N: u32,
189189
borrow_flags
190190
}
191191

192-
/// Validate that lhs - rhs does not underflow (unconstrained)
193-
///
194-
/// Compute underflow flag (lhs < rhs)
195-
/// then perform subtraction with borrow flags
196-
///
197-
/// ## Note
198-
/// The `borrow` must be equal to 0 at the end of the loop.
199-
/// And it can't be nonzero, since we swap the terms at the beginning
200-
pub(crate) unconstrained fn __validate_gte_with_flags<let N: u32>(
192+
/// Compute `lhs - rhs` with borrow flags (unconstrained)
193+
/// Return the result of the subtraction and the borrow flags
194+
pub(crate) unconstrained fn __validate_gt_with_flags<let N: u32>(
201195
lhs: [u128; N],
202196
rhs: [u128; N],
203-
) -> (bool, [u128; N], [bool; N - 1]) {
204-
let mut a: [u128; N] = lhs;
205-
let mut b: [u128; N] = rhs;
206-
207-
let underflow: bool = !__gte(lhs, rhs);
208-
// swap a and b if there's an underflow
209-
let (a, b): ([u128; N], [u128; N]) = if underflow { (b, a) } else { (a, b) };
210-
197+
) -> ([u128; N], [bool; N - 1]) {
211198
let mut result: [u128; N] = [0; N];
212-
213199
let mut borrow_flags: [bool; N - 1] = [false; N - 1];
214200

215201
let mut borrow: u128 = 0;
216202
for i in 0..N {
217-
let mut add_term: u128 = a[i];
218-
219-
let sub_term: u128 = b[i] + borrow;
203+
let add_term: u128 = lhs[i];
204+
let sub_term: u128 = rhs[i] + borrow;
220205
borrow = (sub_term > add_term) as u128;
221206

222207
result[i] = borrow * TWO_POW_120 + add_term - sub_term;
@@ -225,6 +210,28 @@ pub(crate) unconstrained fn __validate_gte_with_flags<let N: u32>(
225210
borrow_flags[i] = borrow != 0;
226211
}
227212
}
213+
(result, borrow_flags)
214+
}
215+
216+
/// Validate that lhs - rhs does not underflow (unconstrained)
217+
///
218+
/// Compute underflow flag (lhs < rhs), then perform subtraction with borrow flags.
219+
/// Always computes max(lhs, rhs) - min(lhs, rhs) by swapping if needed.
220+
///
221+
/// ## Note
222+
/// The `borrow` must be equal to 0 at the end of the loop
223+
/// And it can't be nonzero, since we swap the terms at the beginning
224+
pub(crate) unconstrained fn __validate_gte_with_flags<let N: u32>(
225+
lhs: [u128; N],
226+
rhs: [u128; N],
227+
) -> (bool, [u128; N], [bool; N - 1]) {
228+
let underflow: bool = !__gte(lhs, rhs);
229+
// swap a and b if there's an underflow
230+
let (a, b): ([u128; N], [u128; N]) = if underflow { (rhs, lhs) } else { (lhs, rhs) };
231+
232+
// Compute max - min using the basic subtraction helper
233+
let (result, borrow_flags) = __validate_gt_with_flags(a, b);
234+
228235
(underflow, result, borrow_flags)
229236
}
230237

0 commit comments

Comments
 (0)