Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bigint_constraint.test.cpp
Go to the documentation of this file.
2#include "acir_format.hpp"
7
8#include <cstdint>
9#include <gtest/gtest.h>
10#include <vector>
11
13
14class BigIntTests : public ::testing::Test {
15 protected:
17};
19
22 BigIntOperationType op, fr lhs, fr rhs, WitnessVector& witness_values, const std::vector<uint32_t>& modulus)
23{
24 // CAUTION We assume here the operands and the result fit into one byte!
25 // So trying to divide 7/2 won't work, but 8/2 will do.
26 auto lhs_id = static_cast<uint32_t>(witness_values.size());
27 witness_values.push_back(lhs);
28 auto rhs_id = static_cast<uint32_t>(witness_values.size());
29 witness_values.push_back(rhs);
30 BigIntFromLeBytes from_le_bytes_constraint_bigint_lhs{
31 .inputs = { lhs_id },
32 .modulus = modulus,
33 .result = lhs_id,
34 };
35 BigIntFromLeBytes from_le_bytes_constraint_bigint_rhs{
36 .inputs = { rhs_id },
37 .modulus = modulus,
38 .result = rhs_id,
39 };
40
41 auto result = static_cast<uint32_t>(witness_values.size());
42 BigIntOperation constraint{
43 .lhs = lhs_id,
44 .rhs = rhs_id,
45 .result = result,
46 .opcode = op,
47 };
48 // Expecting the result to be just one byte long
49 BigIntToLeBytes to_bytes{
50 .input = result,
51 .result = { static_cast<uint32_t>(witness_values.size()) },
52 };
53 // overflow is NOT supported, you have to make sure there is no overflow/underflow.
54 fr value = 0;
55 switch (op) {
56 case Add:
57 value = witness_values[lhs_id] + witness_values[rhs_id];
58 break;
59 case Sub:
60 value = witness_values[lhs_id] - witness_values[rhs_id];
61 break;
62 case Mul:
63 value = witness_values[lhs_id] * witness_values[rhs_id];
64 break;
65 case Div:
66 value = witness_values[lhs_id] / witness_values[rhs_id];
67 break;
68 default:
69 throw_or_abort("Unexpected BigIntOperationType.");
70 break;
71 }
72
73 witness_values.push_back(value);
74 return { from_le_bytes_constraint_bigint_lhs, from_le_bytes_constraint_bigint_rhs, constraint, to_bytes };
75}
76
78 BigIntOperationType op, fr lhs, fr rhs, WitnessVector& witness_values)
79{
80 // modulus is bn254/fq
82 op,
83 lhs,
84 rhs,
85 witness_values,
86 {
87 0x47, 0xFD, 0x7C, 0xD8, 0x16, 0x8C, 0x20, 0x3C, 0x8d, 0xca, 0x71, 0x68, 0x91, 0x6a, 0x81, 0x97,
88 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30,
89 });
90}
91
94{
96 op, lhs, rhs, witness_values, { 0x41, 0x41, 0x36, 0xD0, 0x8C, 0x5E, 0xD2, 0xBF, 0x3B, 0xA0, 0x48,
97 0xAF, 0xE6, 0xDC, 0xAE, 0xBA, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
98 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF });
99}
100
103{
105 op, lhs, rhs, witness_values, { 0x2F, 0xFC, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
106 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
107 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF });
108}
109void apply_constraints(AcirFormat& constraint_system,
111{
112 constraint_system.bigint_from_le_bytes_constraints.push_back(get<0>(constraints));
113 constraint_system.bigint_from_le_bytes_constraints.push_back(get<1>(constraints));
114 constraint_system.bigint_to_le_bytes_constraints.push_back(get<3>(constraints));
115 constraint_system.bigint_operations.push_back(get<2>(constraints));
116}
117
119 uint32_t lhs_id,
120 uint32_t rhs_id,
121 WitnessVector& witness_values)
122{
123 // lhs_id, rhs_id are big int it, so we can generate the operation directly
124 auto result = static_cast<uint32_t>(witness_values.size());
125 BigIntOperation constraint{
126 .lhs = lhs_id,
127 .rhs = rhs_id,
128 .result = result,
129 .opcode = op,
130 };
131 // Expecting the result to be just one byte long
132 BigIntToLeBytes to_bytes{
133 .input = result,
134 .result = { static_cast<uint32_t>(witness_values.size()) },
135 };
136 // overflow is NOT supported, you have to make sure there is no overflow/underflow.
137 fr value = 0;
138 switch (op) {
139 case Add:
140 value = witness_values[lhs_id] + witness_values[rhs_id];
141 break;
142 case Sub:
143 value = witness_values[lhs_id] - witness_values[rhs_id];
144 break;
145 case Mul:
146 value = witness_values[lhs_id] * witness_values[rhs_id];
147 break;
148 case Div:
149 value = witness_values[lhs_id] / witness_values[rhs_id];
150 break;
151 default:
152 throw_or_abort("Unexpected BigIntOperationType.");
153 break;
154 }
155
156 witness_values.push_back(value);
157 return { constraint, to_bytes };
158}
159
160// Based on TestBigIntConstraintSimple, we generate constraints for multiple operations at the same time.
161TEST_F(BigIntTests, TestBigIntConstraintMultiple)
162{
163 WitnessVector witness;
164 auto contraints = generate_big_int_op_constraint(BigIntOperationType::Add, fr(3), fr(1), witness);
165 auto contraints2 = generate_big_int_op_constraint(BigIntOperationType::Add, fr(3), fr(1), witness);
166 auto contraints3 = generate_big_int_op_constraint(BigIntOperationType::Sub, fr(5), fr(2), witness);
167 auto contraints4 = generate_big_int_op_constraint(BigIntOperationType::Mul, fr(5), fr(3), witness);
168 auto contraints5 = generate_big_int_op_constraint(BigIntOperationType::Div, fr(8), fr(2), witness);
169 AcirFormat constraint_system{
170 .varnum = static_cast<uint32_t>(witness.size() + 1),
171 .num_acir_opcodes = 5,
172 .public_inputs = {},
173 .original_opcode_indices = create_empty_original_opcode_indices(),
174 };
175 apply_constraints(constraint_system, contraints);
176 apply_constraints(constraint_system, contraints2);
177 apply_constraints(constraint_system, contraints3);
178 apply_constraints(constraint_system, contraints4);
179 apply_constraints(constraint_system, contraints5);
180 mock_opcode_indices(constraint_system);
181 constraint_system.varnum = static_cast<uint32_t>(witness.size() + 1);
182
183 AcirProgram program{ constraint_system, witness };
184 auto builder = create_circuit(program);
185
186 EXPECT_TRUE(CircuitChecker::check(builder));
187}
188
189TEST_F(BigIntTests, TestBigIntConstraintSimple)
190{
191 // 3 + 3 = 6
192 // 3 = bigint(1) = from_bytes(w(1))
193 // 6 = bigint(2) = to_bytes(w(2))
194 BigIntOperation add_constraint{
195 .lhs = 1,
196 .rhs = 1,
197 .result = 2,
198 .opcode = BigIntOperationType::Add,
199 };
200
201 BigIntFromLeBytes from_le_bytes_constraint_bigint1{
202 .inputs = { 1 },
203 .modulus = { 0x47, 0xFD, 0x7C, 0xD8, 0x16, 0x8C, 0x20, 0x3C, 0x8d, 0xca, 0x71, 0x68, 0x91, 0x6a, 0x81, 0x97,
204 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30, },
205 .result = 1,
206 };
207
208 BigIntToLeBytes result2_to_le_bytes{
209 .input = 2, .result = { 2 }, // 3+3=6
210 };
211
212 AcirFormat constraint_system{
213 .varnum = 5,
214 .num_acir_opcodes = 3,
215 .public_inputs = {},
216 .bigint_from_le_bytes_constraints = { from_le_bytes_constraint_bigint1 },
217 .bigint_to_le_bytes_constraints = { result2_to_le_bytes },
218 .bigint_operations = { add_constraint },
219 .original_opcode_indices = create_empty_original_opcode_indices(),
220 };
221 mock_opcode_indices(constraint_system);
222
223 WitnessVector witness{
224 0, 3, 6, 3, 0,
225 };
226 AcirProgram program{ constraint_system, witness };
227 auto builder = create_circuit(program);
228
229 EXPECT_TRUE(CircuitChecker::check(builder));
230}
231
232// Based on TestBigIntConstraintMultiple, we generate constraints re-using the bigfields created by the first two
233// operations
234TEST_F(BigIntTests, TestBigIntConstraintReuse)
235{
236 WitnessVector witness;
238 auto contraints2 = generate_big_int_op_constraint_secpk1_fr(BigIntOperationType::Sub, fr(5), fr(2), witness);
239 auto contraints3 = generate_big_int_op_constraint_with_id(BigIntOperationType::Mul, 0, 5, witness);
240 auto contraints4 = generate_big_int_op_constraint_with_id(BigIntOperationType::Div, 0, 1, witness);
241 auto contraints5 = generate_big_int_op_constraint_with_id(BigIntOperationType::Sub, 7, 1, witness);
242
243 AcirFormat constraint_system{
244 .varnum = static_cast<uint32_t>(witness.size() + 1),
245 .num_acir_opcodes = 5,
246 .public_inputs = {},
247 .original_opcode_indices = create_empty_original_opcode_indices(),
248 };
249 apply_constraints(constraint_system, contraints);
250 apply_constraints(constraint_system, contraints2);
251 constraint_system.bigint_to_le_bytes_constraints.push_back(get<1>(contraints3));
252 constraint_system.bigint_operations.push_back(get<0>(contraints3));
253 constraint_system.bigint_to_le_bytes_constraints.push_back(get<1>(contraints4));
254 constraint_system.bigint_operations.push_back(get<0>(contraints4));
255 constraint_system.bigint_to_le_bytes_constraints.push_back(get<1>(contraints5));
256 constraint_system.bigint_operations.push_back(get<0>(contraints5));
257 constraint_system.varnum = static_cast<uint32_t>(witness.size() + 1);
258 mock_opcode_indices(constraint_system);
259
260 AcirProgram program{ constraint_system, witness };
261 auto builder = create_circuit(program);
262
263 EXPECT_TRUE(CircuitChecker::check(builder));
264}
265
266TEST_F(BigIntTests, TestBigIntConstraintReuse2)
267{
268 WitnessVector witness;
270 auto contraints2 = generate_big_int_op_constraint_secpk1_fq(BigIntOperationType::Sub, fr(5), fr(2), witness);
271 auto contraints3 = generate_big_int_op_constraint_with_id(BigIntOperationType::Add, 0, 5, witness);
272 auto contraints4 = generate_big_int_op_constraint_with_id(BigIntOperationType::Sub, 0, 1, witness);
273 auto contraints5 = generate_big_int_op_constraint_with_id(BigIntOperationType::Sub, 7, 1, witness);
274
275 AcirFormat constraint_system{
276 .varnum = static_cast<uint32_t>(witness.size() + 1),
277 .num_acir_opcodes = 5,
278 .public_inputs = {},
279 .original_opcode_indices = create_empty_original_opcode_indices(),
280 };
281 apply_constraints(constraint_system, contraints);
282 apply_constraints(constraint_system, contraints2);
283 constraint_system.bigint_to_le_bytes_constraints.push_back(get<1>(contraints3));
284 constraint_system.bigint_operations.push_back(get<0>(contraints3));
285 constraint_system.bigint_to_le_bytes_constraints.push_back(get<1>(contraints4));
286 constraint_system.bigint_operations.push_back(get<0>(contraints4));
287 constraint_system.bigint_to_le_bytes_constraints.push_back(get<1>(contraints5));
288 constraint_system.bigint_operations.push_back(get<0>(contraints5));
289 constraint_system.varnum = static_cast<uint32_t>(witness.size() + 1);
290 mock_opcode_indices(constraint_system);
291
292 AcirProgram program{ constraint_system, witness };
293 auto builder = create_circuit(program);
294
295 EXPECT_TRUE(CircuitChecker::check(builder));
296}
297
298TEST_F(BigIntTests, TestBigIntDIV)
299{
300 // 6 / 3 = 2
301 // 6 = bigint(1) = from_bytes(w(1))
302 // 3 = bigint(2) = from_bytes(w(2))
303 // 2 = bigint(3) = to_bytes(w(3))
304 BigIntOperation div_constraint{
305 .lhs = 1,
306 .rhs = 2,
307 .result = 3,
308 .opcode = BigIntOperationType::Div,
309 };
310
311 BigIntFromLeBytes from_le_bytes_constraint_bigint1{
312 .inputs = { 1 },
313 .modulus = { 0x41, 0x41, 0x36, 0xD0, 0x8C, 0x5E, 0xD2, 0xBF, 0x3B, 0xA0, 0x48, 0xAF, 0xE6, 0xDC, 0xAE, 0xBA,
314 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
315 .result = 1,
316 };
317 BigIntFromLeBytes from_le_bytes_constraint_bigint2{
318 .inputs = { 2 },
319 .modulus = { 0x41, 0x41, 0x36, 0xD0, 0x8C, 0x5E, 0xD2, 0xBF, 0x3B, 0xA0, 0x48, 0xAF, 0xE6, 0xDC, 0xAE, 0xBA,
320 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
321 .result = 2,
322 };
323
324 BigIntToLeBytes result3_to_le_bytes{
325 .input = 3, .result = { 3 }, //
326 };
327
328 AcirFormat constraint_system{
329 .varnum = 5,
330 .num_acir_opcodes = 4,
331 .public_inputs = {},
332 .bigint_from_le_bytes_constraints = { from_le_bytes_constraint_bigint1, from_le_bytes_constraint_bigint2 },
333 .bigint_to_le_bytes_constraints = { result3_to_le_bytes },
334 .bigint_operations = { div_constraint },
335 .original_opcode_indices = create_empty_original_opcode_indices(),
336 };
337 mock_opcode_indices(constraint_system);
338
339 WitnessVector witness{
340 0, 6, 3, 2, 0,
341 };
342 AcirProgram program{ constraint_system, witness };
343 auto builder = create_circuit(program);
344
345 EXPECT_TRUE(CircuitChecker::check(builder));
346}
347} // namespace acir_format::tests
acir_format::AcirFormatOriginalOpcodeIndices create_empty_original_opcode_indices()
void mock_opcode_indices(acir_format::AcirFormat &constraint_system)
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
AluTraceBuilder builder
Definition alu.test.cpp:123
std::tuple< BigIntOperation, BigIntToLeBytes > generate_big_int_op_constraint_with_id(BigIntOperationType op, uint32_t lhs_id, uint32_t rhs_id, WitnessVector &witness_values)
std::tuple< BigIntFromLeBytes, BigIntFromLeBytes, BigIntOperation, BigIntToLeBytes > generate_big_int_op_constraint(BigIntOperationType op, fr lhs, fr rhs, WitnessVector &witness_values)
std::tuple< BigIntFromLeBytes, BigIntFromLeBytes, BigIntOperation, BigIntToLeBytes > generate_big_int_op_constraint_secpk1_fq(BigIntOperationType op, fr lhs, fr rhs, WitnessVector &witness_values)
field< Bn254FrParams > fr
std::tuple< BigIntFromLeBytes, BigIntFromLeBytes, BigIntOperation, BigIntToLeBytes > generate_big_int_op_constraint_secpk1_fr(BigIntOperationType op, fr lhs, fr rhs, WitnessVector &witness_values)
std::tuple< BigIntFromLeBytes, BigIntFromLeBytes, BigIntOperation, BigIntToLeBytes > generate_big_int_op_constraint_with_modulus(BigIntOperationType op, fr lhs, fr rhs, WitnessVector &witness_values, const std::vector< uint32_t > &modulus)
void apply_constraints(AcirFormat &constraint_system, std::tuple< BigIntFromLeBytes, BigIntFromLeBytes, BigIntOperation, BigIntToLeBytes > constraints)
UltraCircuitBuilder create_circuit(AcirProgram &program, const ProgramMetadata &metadata)
Specialization for creating an Ultra circuit from an acir program.
bb::SlabVector< bb::fr > WitnessVector
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:123
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::vector< BigIntToLeBytes > bigint_to_le_bytes_constraints
std::vector< BigIntFromLeBytes > bigint_from_le_bytes_constraints
std::vector< BigIntOperation > bigint_operations
void throw_or_abort(std::string const &err)