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