Expected Behavior
boundary_check(limit) is supposed to return the unique suffix mask where r[i] == 1 iff i >= limit.
Bug
Instead, the all-zeros mask passes for every limit, so the returned mask is not bound to limit.
The bug
r comes from the unconstrained hint __boundary_check (src/boundary_check.nr), so the prover fully controls it. After the loop counts the single 0 -> 1 transition, a correction term handles the legitimate "boundary is past the end" case (limit >= Range, honest mask all zeros):
transition_index = transition_index + (1 - r[Range - 1]) * limit as Field; // boundary_check.nr:45
assert(transition_index == limit as Field); // boundary_check.nr:46
That correction fires for any all-zeros array, not just the degenerate one. With no transition the loop sum is 0, r[Range - 1] == 0 adds (1 - 0) * limit == limit, and the assert passes unconditionally for every limit, including limit < Range.
Workaround
No response
Project Impact
No response
Nargo Version
No response
NoirJS Version
No response
Proving Backend
No response
Would you like to submit a PR for this Issue?
None
Expected Behavior
boundary_check(limit)is supposed to return the unique suffix mask wherer[i] == 1iffi >= limit.Bug
Instead, the all-zeros mask passes for every
limit, so the returned mask is not bound tolimit.The bug
rcomes from the unconstrained hint__boundary_check(src/boundary_check.nr), so the prover fully controls it. After the loop counts the single0 -> 1transition, a correction term handles the legitimate "boundary is past the end" case (limit >= Range, honest mask all zeros):That correction fires for any all-zeros array, not just the degenerate one. With no transition the loop sum is
0,r[Range - 1] == 0adds(1 - 0) * limit == limit, and the assert passes unconditionally for everylimit, includinglimit < Range.Workaround
No response
Project Impact
No response
Nargo Version
No response
NoirJS Version
No response
Proving Backend
No response
Would you like to submit a PR for this Issue?
None