Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
range_constraint.test.cpp
Go to the documentation of this file.
1
#include "
barretenberg/circuit_checker/circuit_checker.hpp
"
2
#include "
barretenberg/numeric/uint256/uint256.hpp
"
3
#include "
failure_test_utils.hpp
"
4
#include "
ultra_honk.test.hpp
"
5
6
using namespace
bb
;
7
8
#ifdef STARKNET_GARAGA_FLAVORS
9
using
FlavorTypes
= testing::Types<
UltraFlavor
,
10
UltraZKFlavor
,
11
UltraKeccakFlavor
,
12
UltraKeccakZKFlavor
,
13
UltraStarknetFlavor,
14
UltraStarknetZKFlavor>;
15
#else
16
using
FlavorTypes
= testing::Types<UltraFlavor, UltraZKFlavor, UltraKeccakFlavor, UltraKeccakZKFlavor>;
17
#endif
18
template
<
typename
T>
using
RangeTests
=
UltraHonkTests<T>
;
19
TYPED_TEST_SUITE
(
RangeTests
,
FlavorTypes
);
20
21
/***************************************************************************************************
22
* enforce_small_deltas tests
23
* These test the low-level delta constraint: consecutive values must differ by at most 3.
24
***************************************************************************************************/
25
26
// Basic test: sorted sequence [1,2,3,4] has deltas of 1, which is valid
27
TYPED_TEST
(
RangeTests
, EnforceSmallDeltasBasic)
28
{
29
auto
builder
=
UltraCircuitBuilder
();
30
auto
idx = TestFixture::add_variables(
builder
, { 1, 2, 3, 4 });
31
builder
.enforce_small_deltas(idx);
32
33
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
34
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
35
}
36
37
// Sequence with max delta of 3 and duplicates: all deltas in {0,1,2,3}
38
TYPED_TEST
(
RangeTests
, EnforceSmallDeltasWithDuplicatesAndMaxDelta)
39
{
40
auto
builder
=
UltraCircuitBuilder
();
41
// Deltas: 2, 1, 3, 0, 1, 3, 3, 1, 0, 3, 1, 2, 0, 3, 1, 1, 1, 3, 2
42
auto
idx = TestFixture::add_variables(
builder
,
43
{ 1, 3, 4, 7, 7, 8, 11, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 });
44
builder
.enforce_small_deltas(idx);
45
46
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
47
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
48
}
49
50
// FAILURE: delta of 5 (from 3 to 8) exceeds maximum of 3
51
TYPED_TEST
(
RangeTests
, EnforceSmallDeltasFailsDeltaTooLarge)
52
{
53
auto
builder
=
UltraCircuitBuilder
();
54
auto
idx = TestFixture::add_variables(
builder
, { 1, 2, 3, 8 });
// delta from 3 to 8 is 5
55
builder
.enforce_small_deltas(idx);
56
57
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
58
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
false
);
59
}
60
61
// FAILURE: sequence not sorted (16 comes before 14)
62
TYPED_TEST
(
RangeTests
, EnforceSmallDeltasFailsNotSorted)
63
{
64
auto
builder
=
UltraCircuitBuilder
();
65
// 16 appears before 14, causing a negative delta (wraps to large positive in field)
66
auto
idx = TestFixture::add_variables(
builder
,
67
{ 1, 3, 4, 7, 7, 8, 16, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 });
68
builder
.enforce_small_deltas(idx);
69
70
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
71
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
false
);
72
}
73
74
/***************************************************************************************************
75
* create_sort_constraint_with_edges tests
76
* These test delta constraints with explicit start and end boundary checks.
77
***************************************************************************************************/
78
79
// Basic test: sequence [1..8] with start=1, end=8
80
TYPED_TEST
(
RangeTests
, SortConstraintWithEdgesBasic)
81
{
82
auto
builder
=
UltraCircuitBuilder
();
83
auto
idx = TestFixture::add_variables(
builder
, { 1, 2, 3, 4, 5, 6, 7, 8 });
84
builder
.create_sort_constraint_with_edges(idx,
/*start=*/
1,
/*end=*/
8);
85
86
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
87
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
88
}
89
90
// Complex sequence with duplicates and varying deltas, all within bounds
91
TYPED_TEST
(
RangeTests
, SortConstraintWithEdgesComplex)
92
{
93
auto
builder
=
UltraCircuitBuilder
();
94
auto
idx = TestFixture::add_variables(
builder
, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25,
95
26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 });
96
builder
.create_sort_constraint_with_edges(idx,
/*start=*/
1,
/*end=*/
45);
97
98
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
99
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
100
}
101
102
// FAILURE: end constraint not satisfied (actual end is 8, but we claim end=7)
103
TYPED_TEST
(
RangeTests
, SortConstraintWithEdgesFailsWrongEnd)
104
{
105
auto
builder
=
UltraCircuitBuilder
();
106
auto
idx = TestFixture::add_variables(
builder
, { 1, 2, 3, 4, 5, 6, 7, 8 });
107
builder
.create_sort_constraint_with_edges(idx,
/*start=*/
1,
/*end=*/
7);
// actual end is 8
108
109
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
110
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
false
);
111
}
112
113
// FAILURE: start constraint not satisfied (actual start is 1, but we claim start=2)
114
TYPED_TEST
(
RangeTests
, SortConstraintWithEdgesFailsWrongStart)
115
{
116
auto
builder
=
UltraCircuitBuilder
();
117
auto
idx = TestFixture::add_variables(
builder
, { 1, 2, 3, 4, 5, 6, 7, 8 });
118
builder
.create_sort_constraint_with_edges(idx,
/*start=*/
2,
/*end=*/
8);
// actual start is 1
119
120
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
121
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
false
);
122
}
123
124
// FAILURE: delta too large (15 appears where small delta expected)
125
TYPED_TEST
(
RangeTests
, SortConstraintWithEdgesFailsDeltaTooLarge)
126
{
127
auto
builder
=
UltraCircuitBuilder
();
128
auto
idx = TestFixture::add_variables(
builder
, { 1, 15, 3, 4, 5, 6, 7, 8 });
// 1 to 15 is delta of 14
129
builder
.create_sort_constraint_with_edges(idx,
/*start=*/
2,
/*end=*/
8);
130
131
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
132
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
false
);
133
}
134
135
/***************************************************************************************************
136
* create_small_range_constraint tests
137
* Range is [0, target_range] (inclusive).
138
***************************************************************************************************/
139
140
// Basic test: values [1..8] all in range [0, 8]
141
TYPED_TEST
(
RangeTests
, SmallRangeConstraintBasic)
142
{
143
auto
builder
=
UltraCircuitBuilder
();
144
auto
idx = TestFixture::add_variables(
builder
, { 1, 2, 3, 4, 5, 6, 7, 8 });
145
for
(
auto
i : idx) {
146
builder
.create_small_range_constraint(i,
/*target_range=*/
8);
147
}
148
builder
.create_unconstrained_gates(idx);
149
150
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
151
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
152
}
153
154
// Single value at exact boundary: 3 in range [0, 3]
155
TYPED_TEST
(
RangeTests
, SmallRangeConstraintAtBoundary)
156
{
157
auto
builder
=
UltraCircuitBuilder
();
158
auto
idx = TestFixture::add_variables(
builder
, { 3 });
159
builder
.create_small_range_constraint(idx[0],
/*target_range=*/
3);
160
builder
.create_unconstrained_gates(idx);
161
162
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
163
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
164
}
165
166
// Multiple values with various ranges, all valid
167
TYPED_TEST
(
RangeTests
, SmallRangeConstraintMultipleValues)
168
{
169
auto
builder
=
UltraCircuitBuilder
();
170
auto
idx =
171
TestFixture::add_variables(
builder
, { 1, 2, 3, 4, 5, 6, 10, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 19, 51 });
172
for
(
auto
i : idx) {
173
builder
.create_small_range_constraint(i,
/*target_range=*/
128);
174
}
175
builder
.create_unconstrained_gates(idx);
176
177
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
178
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
179
}
180
181
// FAILURE: value 25 exceeds range [0, 8]
182
TYPED_TEST
(
RangeTests
, SmallRangeConstraintFailsValueTooLarge)
183
{
184
auto
builder
=
UltraCircuitBuilder
();
185
auto
idx = TestFixture::add_variables(
builder
, { 1, 2, 3, 4, 5, 6, 8, 25 });
// 25 > 8
186
for
(
auto
i : idx) {
187
builder
.create_small_range_constraint(i,
/*target_range=*/
8);
188
}
189
builder
.enforce_small_deltas(idx);
190
191
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
192
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
false
);
193
}
194
195
// FAILURE: value 80 exceeds range [0, 79]
196
TYPED_TEST
(
RangeTests
, SmallRangeConstraintFailsValueJustOverBoundary)
197
{
198
auto
builder
=
UltraCircuitBuilder
();
199
auto
idx = TestFixture::add_variables(
200
builder
, { 1, 2, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 });
// 80 > 79
201
for
(
auto
i : idx) {
202
builder
.create_small_range_constraint(i,
/*target_range=*/
79);
203
}
204
builder
.create_unconstrained_gates(idx);
205
206
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
207
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
false
);
208
}
209
210
// FAILURE: orphan variable (not in any gate) causes GPA failure. This is a quirk of `create_small_range_constraint`.
211
TYPED_TEST
(
RangeTests
, SmallRangeConstraintFailsOrphanVariable)
212
{
213
auto
builder
=
UltraCircuitBuilder
();
214
auto
idx = TestFixture::add_variables(
builder
, { 1, 2, 3, 4, 5, 6, 7, 8 });
215
for
(
auto
i : idx) {
216
builder
.create_small_range_constraint(i,
/*target_range=*/
8);
217
}
218
// NOT calling create_unconstrained_gates - variables are orphans
219
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
220
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
false
);
221
}
222
223
/***************************************************************************************************
224
* Range constraints combined with arithmetic gates
225
***************************************************************************************************/
226
227
// Range constraints work alongside arithmetic gates
228
TYPED_TEST
(
RangeTests
, RangeConstraintWithArithmeticGates)
229
{
230
auto
builder
=
UltraCircuitBuilder
();
231
auto
idx = TestFixture::add_variables(
builder
, { 1, 2, 3, 4, 5, 6, 7, 8 });
232
for
(
auto
i : idx) {
233
builder
.create_small_range_constraint(i,
/*target_range=*/
8);
234
}
235
236
// Add arithmetic constraints: 1+2=3, 3+4=7, 5+6=11, 7+8=15
237
builder
.create_add_gate({ idx[0], idx[1],
builder
.zero_idx(),
fr::one
(),
fr::one
(),
fr::zero
(), -3 });
238
builder
.create_add_gate({ idx[2], idx[3],
builder
.zero_idx(),
fr::one
(),
fr::one
(),
fr::zero
(), -7 });
239
builder
.create_add_gate({ idx[4], idx[5],
builder
.zero_idx(),
fr::one
(),
fr::one
(),
fr::zero
(), -11 });
240
builder
.create_add_gate({ idx[6], idx[7],
builder
.zero_idx(),
fr::one
(),
fr::one
(),
fr::zero
(), -15 });
241
242
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
243
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
244
}
245
246
// Non-power-of-two range works correctly
247
TYPED_TEST
(
RangeTests
, RangeConstraintNonPowerOfTwo)
248
{
249
auto
builder
=
UltraCircuitBuilder
();
250
auto
idx = TestFixture::add_variables(
builder
, { 1, 2, 3, 4, 5, 6, 7, 8 });
251
for
(
auto
i : idx) {
252
builder
.create_small_range_constraint(i,
/*target_range=*/
12);
// not a power of 2
253
}
254
255
builder
.create_add_gate({ idx[0], idx[1],
builder
.zero_idx(),
fr::one
(),
fr::one
(),
fr::zero
(), -3 });
256
builder
.create_add_gate({ idx[2], idx[3],
builder
.zero_idx(),
fr::one
(),
fr::one
(),
fr::zero
(), -7 });
257
builder
.create_add_gate({ idx[4], idx[5],
builder
.zero_idx(),
fr::one
(),
fr::one
(),
fr::zero
(), -11 });
258
builder
.create_add_gate({ idx[6], idx[7],
builder
.zero_idx(),
fr::one
(),
fr::one
(),
fr::zero
(), -15 });
259
260
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
261
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
262
}
263
267
TYPED_TEST
(
RangeTests
, MultipleRangeConstraintsOnSmallWitness)
268
{
269
auto
builder
=
UltraCircuitBuilder
();
270
271
uint32_t witness_idx =
builder
.add_variable(
fr
(5));
272
273
// Apply multiple range constraints with different target ranges
274
builder
.create_small_range_constraint(witness_idx,
/*target_range=*/
8);
275
builder
.create_small_range_constraint(witness_idx,
/*target_range=*/
6);
276
builder
.create_small_range_constraint(witness_idx,
/*target_range=*/
10);
277
builder
.create_small_range_constraint(witness_idx,
/*target_range=*/
100);
278
279
// Add unconstrained gate to prevent orphan variable failure
280
builder
.create_unconstrained_gates({ witness_idx });
281
282
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
283
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
284
}
285
/***************************************************************************************************
286
* create_limbed_range_constraint tests
287
* For large ranges (> 14 bits), decompose into smaller limbs.
288
***************************************************************************************************/
289
290
// Boundary case: exactly 14 bits (single limb, at DEFAULT_PLOOKUP_RANGE_BITNUM)
291
TYPED_TEST
(
RangeTests
, LimbedRangeConstraint14Bits)
292
{
293
auto
builder
=
UltraCircuitBuilder
();
294
295
// Create a value that fits in exactly 14 bits (max value = 16383 = 2^14 - 1)
296
auto
value
=
fr
(16383);
297
298
auto
idx =
builder
.add_variable(
value
);
299
// NOTE: we do not need to create an auxiliary arithmetic gate; the `create_limbed_range_constraint` functionality
300
// prevents `idx` from being orphaned.
301
builder
.create_limbed_range_constraint(idx,
/*num_bits=*/
14);
302
303
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
304
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
305
}
306
307
// Large range constraint using limb decomposition (133 bits)
308
TYPED_TEST
(
RangeTests
, LimbedRangeConstraint133Bits)
309
{
310
auto
builder
=
UltraCircuitBuilder
();
311
312
// Create a random value that fits in 133 bits
313
auto
random_field =
fr::random_element
();
314
auto
truncated =
uint256_t
(random_field).
slice
(0, 133);
315
auto
value
=
fr
(truncated);
316
317
auto
idx =
builder
.add_variable(
value
);
318
builder
.create_add_gate({ idx,
builder
.zero_idx(),
builder
.zero_idx(), 1, 0, 0, -
value
});
319
builder
.create_limbed_range_constraint(idx,
/*num_bits=*/
133);
320
321
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
322
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
323
}
324
325
// Edge-case range constraint using limb decomposition. Here, `253 == MAX_NUM_BITS_RANGE_CONSTRAINT`.
326
TYPED_TEST
(
RangeTests
, LimbedRangeConstraint253Bits)
327
{
328
auto
builder
=
UltraCircuitBuilder
();
329
330
// Create a random value that fits in 253 bits
331
auto
random_field =
fr::random_element
();
332
auto
truncated =
uint256_t
(random_field).
slice
(0, 253);
333
auto
value
=
fr
(truncated);
334
335
auto
idx =
builder
.add_variable(
value
);
336
builder
.create_limbed_range_constraint(idx,
/*num_bits=*/
253);
337
338
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
339
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
340
}
341
342
/***************************************************************************************************
343
* create_dyadic_range_constraint tests
344
* Main entry point that handles orphan variables by adding dummy arithmetic gates.
345
***************************************************************************************************/
346
352
TYPED_TEST
(
RangeTests
, DyadicRangeConstraintOnOrphanVariable)
353
{
354
auto
builder
=
UltraCircuitBuilder
();
355
356
// Create a variable that will ONLY be range-constrained, not used in any other gate
357
auto
orphan_idx =
builder
.add_variable(
fr
(100));
358
builder
.create_dyadic_range_constraint(orphan_idx,
/*num_bits=*/
8,
"orphan range constraint"
);
359
360
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
361
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
362
}
363
364
/***************************************************************************************************
365
* Copy constraint interaction tests
366
* Tests that (multiple) range constraints work correctly copy constraints.
367
***************************************************************************************************/
368
374
TYPED_TEST
(
RangeTests
, RangeConstraintsOnDuplicateVariables)
375
{
376
auto
builder
=
UltraCircuitBuilder
();
377
378
uint32_t
a
=
builder
.add_variable(
fr
(100));
379
uint32_t
b
=
builder
.add_variable(
fr
(100));
380
uint32_t c =
builder
.add_variable(
fr
(100));
381
uint32_t d =
builder
.add_variable(
fr
(100));
382
383
// Link all variables together via copy constraints
384
builder
.assert_equal(
a
,
b
);
385
builder
.assert_equal(
a
, c);
386
builder
.assert_equal(
a
, d);
387
388
// Apply different range constraints to each (tightest is 999)
389
builder
.create_small_range_constraint(
a
,
/*target_range=*/
1000);
390
builder
.create_small_range_constraint(
b
,
/*target_range=*/
1001);
391
builder
.create_small_range_constraint(c,
/*target_range=*/
999);
392
builder
.create_small_range_constraint(d,
/*target_range=*/
1000);
393
394
builder
.create_unconstrained_gates({
a
,
b
, c, d });
395
396
TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(
builder
);
397
TestFixture::prove_and_verify(
builder
,
/*expected_result=*/
true
);
398
}
circuit_checker.hpp
bb::UltraFlavor
Definition
ultra_flavor.hpp:32
bb::UltraHonkTests
Definition
ultra_honk.test.hpp:27
bb::UltraKeccakFlavor
Definition
ultra_keccak_flavor.hpp:12
bb::UltraKeccakZKFlavor
Definition
ultra_keccak_zk_flavor.hpp:14
bb::UltraZKFlavor
Child class of UltraFlavor that runs with ZK Sumcheck.
Definition
ultra_zk_flavor.hpp:23
bb::numeric::uint256_t
Definition
uint256.hpp:32
bb::numeric::uint256_t::slice
constexpr uint256_t slice(uint64_t start, uint64_t end) const
Definition
uint256_impl.hpp:330
builder
AluTraceBuilder builder
Definition
alu.test.cpp:124
a
FF a
Definition
field_gt.test.cpp:52
b
FF b
Definition
field_gt.test.cpp:53
value
FF value
Definition
indexed_tree_check.test.cpp:67
failure_test_utils.hpp
FlavorTypes
testing::Types< UltraFlavor, UltraKeccakFlavor, MegaFlavor > FlavorTypes
Definition
flavor_serialization.test.cpp:34
bb
Entry point for Barretenberg command-line interface.
Definition
api.hpp:5
bb::TYPED_TEST_SUITE
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
bb::fr
field< Bn254FrParams > fr
Definition
fr.hpp:155
bb::TYPED_TEST
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
Definition
commitment_key.test.cpp:133
bb::UltraCircuitBuilder
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
Definition
circuit_builders_fwd.hpp:18
bb::field< Bn254FrParams >::one
static constexpr field one()
Definition
field_declarations.hpp:279
bb::field< Bn254FrParams >::random_element
static field random_element(numeric::RNG *engine=nullptr) noexcept
Definition
field_impl.hpp:783
bb::field< Bn254FrParams >::zero
static constexpr field zero()
Definition
field_declarations.hpp:277
uint256.hpp
ultra_honk.test.hpp
src
barretenberg
ultra_honk
range_constraint.test.cpp
Generated by
1.9.8