Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
acir_to_constraint_buf.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
8
9#include <cstddef>
10#include <cstdint>
11#include <map>
12#include <tuple>
13#include <utility>
14
25
26namespace acir_format {
27
28using namespace bb;
29
39template <typename T>
40T deserialize_any_format(std::vector<uint8_t>&& buf,
41 std::function<T(msgpack::object const&)> decode_msgpack,
42 std::function<T(std::vector<uint8_t>)> decode_bincode)
43{
44 // We can't rely on exceptions to try to deserialize binpack, falling back to
45 // msgpack if it fails, because exceptions are (or were) not supported in Wasm
46 // and they are turned off in arch.cmake.
47 //
48 // For now our other option is to check if the data is valid msgpack,
49 // which slows things down, but we can't tell if the first byte of
50 // the data accidentally matches one of our format values.
51 //
52 // Unfortunately this doesn't seem to work either: `msgpack::parse`
53 // returns true for a `bincode` encoded program, and we have to check
54 // whether the value parsed is plausible.
55
56 if (!buf.empty()) {
57 // Once we remove support for legacy bincode format, we should expect to always
58 // have a format marker corresponding to acir::serialization::Format::Msgpack,
59 // but until then a match could be pure coincidence.
60 if (buf[0] == 2) {
61 // Skip the format marker to get the data.
62 const char* buffer = &reinterpret_cast<const char*>(buf.data())[1];
63 size_t size = buf.size() - 1;
64 msgpack::null_visitor probe;
65 if (msgpack::parse(buffer, size, probe)) {
66 auto oh = msgpack::unpack(buffer, size);
67 // This has to be on a separate line, see
68 // https://github.com/msgpack/msgpack-c/issues/695#issuecomment-393035172
69 auto o = oh.get();
70 // In experiments bincode data was parsed as 0.
71 // All the top level formats we look for are MAP types.
72 if (o.type == msgpack::type::MAP) {
73 return decode_msgpack(o);
74 }
75 }
76 }
77 // `buf[0] == 1` would indicate bincode starting with a format byte,
78 // but if it's a coincidence and it fails to parse then we can't recover
79 // from it, so let's just acknowledge that for now we don't want to
80 // exercise this code path and treat the whole data as bincode.
81 }
82 return decode_bincode(std::move(buf));
83}
84
89Acir::Program deserialize_program(std::vector<uint8_t>&& buf)
90{
91 return deserialize_any_format<Acir::Program>(
93 [](auto o) -> Acir::Program {
94 Acir::Program program;
95 try {
96 // Deserialize into a partial structure that ignores the Brillig parts,
97 // so that new opcodes can be added without breaking Barretenberg.
99 o.convert(program_wob);
100 program.functions = program_wob.functions;
101 } catch (const msgpack::type_error&) {
102 std::cerr << o << std::endl;
103 throw_or_abort("failed to convert msgpack data to Program");
104 }
105 return program;
106 },
108}
109
114{
115 return deserialize_any_format<Witnesses::WitnessStack>(
116 std::move(buf),
117 [](auto o) {
118 Witnesses::WitnessStack witness_stack;
119 try {
120 o.convert(witness_stack);
121 } catch (const msgpack::type_error&) {
122 std::cerr << o << std::endl;
123 throw_or_abort("failed to convert msgpack data to WitnessStack");
124 }
125 return witness_stack;
126 },
128}
129
139{
140 poly_triple pt{
141 .a = 0,
142 .b = 0,
143 .c = 0,
144 .q_m = 0,
145 .q_l = 0,
146 .q_r = 0,
147 .q_o = 0,
148 .q_c = 0,
149 };
150
151 // Flags indicating whether each witness index for the present poly_tuple has been set
152 bool a_set = false;
153 bool b_set = false;
154 bool c_set = false;
155
156 // If necessary, set values for quadratic term (q_m * w_l * w_r)
157 BB_ASSERT_LTE(arg.mul_terms.size(), 1U, "We can only accommodate 1 quadratic term");
158 // Note: mul_terms are tuples of the form {selector_value, witness_idx_1, witness_idx_2}
159 if (!arg.mul_terms.empty()) {
160 const auto& mul_term = arg.mul_terms[0];
161 pt.q_m = uint256_t(std::get<0>(mul_term));
162 pt.a = std::get<1>(mul_term).value;
163 pt.b = std::get<2>(mul_term).value;
164 a_set = true;
165 b_set = true;
166 }
167
168 // If necessary, set values for linears terms q_l * w_l, q_r * w_r and q_o * w_o
169 BB_ASSERT_LTE(arg.linear_combinations.size(), 3U, "We can only accommodate 3 linear terms");
170 for (const auto& linear_term : arg.linear_combinations) {
171 fr selector_value(uint256_t(std::get<0>(linear_term)));
172 uint32_t witness_idx = std::get<1>(linear_term).value;
173
174 // If the witness index has not yet been set or if the corresponding linear term is active, set the witness
175 // index and the corresponding selector value.
176 if (!a_set || pt.a == witness_idx) { // q_l * w_l
177 pt.a = witness_idx;
178 pt.q_l = selector_value;
179 a_set = true;
180 } else if (!b_set || pt.b == witness_idx) { // q_r * w_r
181 pt.b = witness_idx;
182 pt.q_r = selector_value;
183 b_set = true;
184 } else if (!c_set || pt.c == witness_idx) { // q_o * w_o
185 pt.c = witness_idx;
186 pt.q_o = selector_value;
187 c_set = true;
188 } else {
189 return poly_triple{
190 .a = 0,
191 .b = 0,
192 .c = 0,
193 .q_m = 0,
194 .q_l = 0,
195 .q_r = 0,
196 .q_o = 0,
197 .q_c = 0,
198 };
199 }
200 }
201
202 // Set constant value q_c
203 pt.q_c = uint256_t(arg.q_c);
204 return pt;
205}
206
208
211
219void assign_linear_term(mul_quad_<fr>& gate, int index, uint32_t witness_index, fr const& scaling)
220{
221 switch (index) {
222 case 0:
223 gate.a = witness_index;
224 gate.a_scaling = scaling;
225 break;
226 case 1:
227 gate.b = witness_index;
228 gate.b_scaling = scaling;
229 break;
230 case 2:
231 gate.c = witness_index;
232 gate.c_scaling = scaling;
233 break;
234 case 3:
235 gate.d = witness_index;
236 gate.d_scaling = scaling;
237 break;
238 default:
239 throw_or_abort("Unexpected index");
240 }
241}
242
245{
247 auto current_mul_term = arg.mul_terms.begin();
248 auto current_linear_term = arg.linear_combinations.begin();
249
250 // number of wires to use in the intermediate gate
251 int max_size = 4;
252 bool done = false;
253 // the intermediate 'big add' gates. The first one contains the constant term.
254 mul_quad_<fr> mul_gate = { .a = 0,
255 .b = 0,
256 .c = 0,
257 .d = 0,
258 .mul_scaling = fr::zero(),
259 .a_scaling = fr::zero(),
260 .b_scaling = fr::zero(),
261 .c_scaling = fr::zero(),
262 .d_scaling = fr::zero(),
263 .const_scaling = fr(uint256_t(arg.q_c)) };
264
265 // list of witnesses that are part of mul terms
266 std::set<uint32_t> all_mul_terms;
267 for (auto const& term : arg.mul_terms) {
268 all_mul_terms.insert(std::get<1>(term).value);
269 all_mul_terms.insert(std::get<2>(term).value);
270 }
271 // The 'mul term' witnesses that have been processed
272 std::set<uint32_t> processed_mul_terms;
273
274 while (!done) {
275 int i = 0; // index of the current free wire in the new intermediate gate
276
277 // we add a mul term (if there are some) to every intermediate gate
278 if (current_mul_term != arg.mul_terms.end()) {
279 mul_gate.mul_scaling = fr(uint256_t(std::get<0>(*current_mul_term)));
280 mul_gate.a = std::get<1>(*current_mul_term).value;
281 mul_gate.b = std::get<2>(*current_mul_term).value;
282 mul_gate.a_scaling = fr::zero();
283 mul_gate.b_scaling = fr::zero();
284 // Try to add corresponding linear terms, only if they were not already added
285 if (!processed_mul_terms.contains(mul_gate.a) || !processed_mul_terms.contains(mul_gate.b)) {
286 for (auto lin_term : arg.linear_combinations) {
287 auto w = std::get<1>(lin_term).value;
288 if (w == mul_gate.a) {
289 if (!processed_mul_terms.contains(mul_gate.a)) {
290 mul_gate.a_scaling = fr(uint256_t(std::get<0>(lin_term)));
291 processed_mul_terms.insert(w);
292 }
293 if (mul_gate.a == mul_gate.b) {
294 break;
295 }
296 } else if (w == mul_gate.b) {
297 if (!processed_mul_terms.contains(mul_gate.b)) {
298 mul_gate.b_scaling = fr(uint256_t(std::get<0>(lin_term)));
299 processed_mul_terms.insert(w);
300 }
301 break;
302 }
303 }
304 }
305 i = 2; // a and b are used because of the mul term
306 current_mul_term = std::next(current_mul_term);
307 }
308 // We need to process all the mul terms before being done.
309 done = current_mul_term == arg.mul_terms.end();
310
311 // Assign available wires with the remaining linear terms which are not also a 'mul term'
312 while (current_linear_term != arg.linear_combinations.end()) {
313 auto w = std::get<1>(*current_linear_term).value;
314 if (!all_mul_terms.contains(w)) {
315 if (i < max_size) {
316 assign_linear_term(mul_gate, i, w, fr(uint256_t(std::get<0>(*current_linear_term)))); // * fr(-1)));
317 ++i;
318 } else {
319 // No more available wire, but there is still some linear terms; we need another mul_gate
320 done = false;
321 break;
322 }
323 }
324 current_linear_term = std::next(current_linear_term);
325 }
326
327 // Index 4 of the next gate will be used
328 max_size = 3;
329 result.push_back(mul_gate);
330 mul_gate = { .a = 0,
331 .b = 0,
332 .c = 0,
333 .d = 0,
334 .mul_scaling = fr::zero(),
335 .a_scaling = fr::zero(),
336 .b_scaling = fr::zero(),
337 .c_scaling = fr::zero(),
338 .d_scaling = fr::zero(),
339 .const_scaling = fr::zero() };
340 }
341
342 return result;
343}
344
346{
347 mul_quad_<fr> quad{ .a = 0,
348 .b = 0,
349 .c = 0,
350 .d = 0,
351 .mul_scaling = 0,
352 .a_scaling = 0,
353 .b_scaling = 0,
354 .c_scaling = 0,
355 .d_scaling = 0,
356 .const_scaling = 0 };
357
358 // Flags indicating whether each witness index for the present mul_quad has been set
359 bool a_set = false;
360 bool b_set = false;
361 bool c_set = false;
362 bool d_set = false;
363 BB_ASSERT_LTE(arg.mul_terms.size(), 1U, "We can only accommodate 1 quadratic term");
364 // Note: mul_terms are tuples of the form {selector_value, witness_idx_1, witness_idx_2}
365 if (!arg.mul_terms.empty()) {
366 const auto& mul_term = arg.mul_terms[0];
367 quad.mul_scaling = uint256_t(std::get<0>(mul_term));
368 quad.a = std::get<1>(mul_term).value;
369 quad.b = std::get<2>(mul_term).value;
370 a_set = true;
371 b_set = true;
372 }
373 // If necessary, set values for linears terms q_l * w_l, q_r * w_r and q_o * w_o
374 for (const auto& linear_term : arg.linear_combinations) {
375 fr selector_value(uint256_t(std::get<0>(linear_term)));
376 uint32_t witness_idx = std::get<1>(linear_term).value;
377
378 // If the witness index has not yet been set or if the corresponding linear term is active, set the witness
379 // index and the corresponding selector value.
380 if (!a_set || quad.a == witness_idx) {
381 quad.a = witness_idx;
382 quad.a_scaling = selector_value;
383 a_set = true;
384 } else if (!b_set || quad.b == witness_idx) {
385 quad.b = witness_idx;
386 quad.b_scaling = selector_value;
387 b_set = true;
388 } else if (!c_set || quad.c == witness_idx) {
389 quad.c = witness_idx;
390 quad.c_scaling = selector_value;
391 c_set = true;
392 } else if (!d_set || quad.d == witness_idx) {
393 quad.d = witness_idx;
394 quad.d_scaling = selector_value;
395 d_set = true;
396 } else {
397 // We cannot assign linear term to a constraint of width 4
398 return { .a = 0,
399 .b = 0,
400 .c = 0,
401 .d = 0,
402 .mul_scaling = 0,
403 .a_scaling = 0,
404 .b_scaling = 0,
405 .c_scaling = 0,
406 .d_scaling = 0,
407 .const_scaling = 0 };
408 }
409 }
410
411 // Set constant value q_c
412 quad.const_scaling = uint256_t(arg.q_c);
413 return quad;
414}
415
417{
418 for (const auto& linear_term : arg.value.linear_combinations) {
419 uint32_t witness_idx = std::get<1>(linear_term).value;
420 af.constrained_witness.insert(witness_idx);
421 }
422 for (const auto& linear_term : arg.value.mul_terms) {
423 uint32_t witness_idx = std::get<1>(linear_term).value;
424 af.constrained_witness.insert(witness_idx);
425 witness_idx = std::get<2>(linear_term).value;
426 af.constrained_witness.insert(witness_idx);
427 }
428}
429
431 poly_triple const& pt,
432 AcirFormat const& af)
433{
434 if (!arg.value.mul_terms.empty() || arg.value.linear_combinations.size() != 2) {
435 return { 0, 0 };
436 }
437 if (pt.q_l == -pt.q_r && pt.q_l != bb::fr::zero() && pt.q_c == bb::fr::zero()) {
438 // we require that one of the 2 witnesses to be constrained in an arithmetic gate
439 if (af.constrained_witness.contains(pt.a) || af.constrained_witness.contains(pt.b)) {
440 return { pt.a, pt.b };
441 }
442 }
443 return { 0, 0 };
444}
445
446void handle_arithmetic(Acir::Opcode::AssertZero const& arg, AcirFormat& af, size_t opcode_index)
447{
448 // If the expression fits in a polytriple, we use it.
449 if (arg.value.linear_combinations.size() <= 3 && arg.value.mul_terms.size() <= 1) {
451
452 auto assert_equal = is_assert_equal(arg, pt, af);
453 uint32_t w1 = std::get<0>(assert_equal);
454 uint32_t w2 = std::get<1>(assert_equal);
455 if (w1 != 0) {
456 if (w1 != w2) {
457 if (!af.constrained_witness.contains(pt.a)) {
458 // we mark it as constrained because it is going to be asserted to be equal to a constrained one.
459 af.constrained_witness.insert(pt.a);
460 // swap the witnesses so that the first one is always properly constrained.
461 auto tmp = pt.a;
462 pt.a = pt.b;
463 pt.b = tmp;
464 }
465 if (!af.constrained_witness.contains(pt.b)) {
466 // we mark it as constrained because it is going to be asserted to be equal to a constrained one.
467 af.constrained_witness.insert(pt.b);
468 }
469 // minimal_range of a witness is the smallest range of the witness and the witness that are
470 // 'assert_equal' to it
471 if (af.minimal_range.contains(pt.b) && af.minimal_range.contains(pt.a)) {
472 if (af.minimal_range[pt.a] < af.minimal_range[pt.b]) {
473 af.minimal_range[pt.a] = af.minimal_range[pt.b];
474 } else {
475 af.minimal_range[pt.b] = af.minimal_range[pt.a];
476 }
477 } else if (af.minimal_range.contains(pt.b)) {
478 af.minimal_range[pt.a] = af.minimal_range[pt.b];
479 } else if (af.minimal_range.contains(pt.a)) {
480 af.minimal_range[pt.b] = af.minimal_range[pt.a];
481 }
482
483 af.assert_equalities.push_back(pt);
484 af.original_opcode_indices.assert_equalities.push_back(opcode_index);
485 }
486 return;
487 }
488 // Even if the number of linear terms is less than 3, we might not be able to fit it into a width-3 arithmetic
489 // gate. This is the case if the linear terms are all distinct witness from the multiplication term. In that
490 // case, the serialize_arithmetic_gate() function will return a poly_triple with all 0's, and we use a width-4
491 // gate instead. We could probably always use a width-4 gate in fact.
492 if (pt == poly_triple{ 0, 0, 0, 0, 0, 0, 0, 0 }) {
494 af.original_opcode_indices.quad_constraints.push_back(opcode_index);
495
496 } else {
497 af.poly_triple_constraints.push_back(pt);
498 af.original_opcode_indices.poly_triple_constraints.push_back(opcode_index);
499 }
500 } else {
501 std::vector<mul_quad_<fr>> mul_quads;
502 // We try to use a single mul_quad gate to represent the expression.
503 if (arg.value.mul_terms.size() <= 1) {
504 auto quad = serialize_mul_quad_gate(arg.value);
505 // add it to the result vector if it worked
506 if (quad.a != 0 || !(quad.mul_scaling == fr(0)) || !(quad.a_scaling == fr(0))) {
507 mul_quads.push_back(quad);
508 }
509 }
510 if (mul_quads.empty()) {
511 // If not, we need to split the expression into multiple gates
512 mul_quads = split_into_mul_quad_gates(arg.value);
513 }
514 if (mul_quads.size() == 1) {
515 af.quad_constraints.push_back(mul_quads[0]);
516 af.original_opcode_indices.quad_constraints.push_back(opcode_index);
517 }
518 if (mul_quads.size() > 1) {
519 af.big_quad_constraints.push_back(mul_quads);
520 }
521 }
522 constrain_witnesses(arg, af);
523}
525{
527 return input_witness.value.value;
528}
529
531{
532 WitnessOrConstant result = std::visit(
533 [&](auto&& e) {
534 using T = std::decay_t<decltype(e)>;
537 .index = e.value.value,
538 .value = bb::fr::zero(),
539 .is_constant = false,
540 };
543 .index = 0,
544 .value = uint256_t(e.value),
545 .is_constant = true,
546 };
547 } else {
548 throw_or_abort("Unrecognized Acir::ConstantOrWitnessEnum variant.");
549 }
551 .index = 0,
552 .value = bb::fr::zero(),
553 .is_constant = true,
554 };
555 },
556 input.input.value);
557 return result;
558}
559
561{
562 std::visit(
563 [&](auto&& arg) {
564 using T = std::decay_t<decltype(arg)>;
566 auto lhs_input = parse_input(arg.lhs);
567 auto rhs_input = parse_input(arg.rhs);
569 .a = lhs_input,
570 .b = rhs_input,
571 .result = arg.output.value,
572 .num_bits = arg.lhs.num_bits,
573 .is_xor_gate = false,
574 });
575 af.constrained_witness.insert(af.logic_constraints.back().result);
576 af.original_opcode_indices.logic_constraints.push_back(opcode_index);
578 auto lhs_input = parse_input(arg.lhs);
579 auto rhs_input = parse_input(arg.rhs);
581 .a = lhs_input,
582 .b = rhs_input,
583 .result = arg.output.value,
584 .num_bits = arg.lhs.num_bits,
585 .is_xor_gate = true,
586 });
587 af.constrained_witness.insert(af.logic_constraints.back().result);
588 af.original_opcode_indices.logic_constraints.push_back(opcode_index);
590 auto witness_input = get_witness_from_function_input(arg.input);
592 .witness = witness_input,
593 .num_bits = arg.input.num_bits,
594 });
595 af.original_opcode_indices.range_constraints.push_back(opcode_index);
596 if (af.minimal_range.contains(witness_input)) {
597 if (af.minimal_range[witness_input] > arg.input.num_bits) {
598 af.minimal_range[witness_input] = arg.input.num_bits;
599 }
600 } else {
601 af.minimal_range[witness_input] = arg.input.num_bits;
602 }
605 .inputs = transform::map(arg.inputs, [](auto& e) { return parse_input(e); }),
606 .iv = transform::map(*arg.iv, [](auto& e) { return parse_input(e); }),
607 .key = transform::map(*arg.key, [](auto& e) { return parse_input(e); }),
608 .outputs = transform::map(arg.outputs, [](auto& e) { return e.value; }),
609 });
610 for (auto& output : af.aes128_constraints.back().outputs) {
611 af.constrained_witness.insert(output);
612 }
613 af.original_opcode_indices.aes128_constraints.push_back(opcode_index);
616 .inputs = transform::map(*arg.inputs, [](auto& e) { return parse_input(e); }),
617 .hash_values = transform::map(*arg.hash_values, [](auto& e) { return parse_input(e); }),
618 .result = transform::map(*arg.outputs, [](auto& e) { return e.value; }),
619 });
620 for (auto& output : af.sha256_compression.back().result) {
621 af.constrained_witness.insert(output);
622 }
623 af.original_opcode_indices.sha256_compression.push_back(opcode_index);
626 .inputs = transform::map(arg.inputs,
627 [](auto& e) {
628 return Blake2sInput{
629 .blackbox_input = parse_input(e),
630 .num_bits = e.num_bits,
631 };
632 }),
633 .result = transform::map(*arg.outputs, [](auto& e) { return e.value; }),
634 });
635 for (auto& output : af.blake2s_constraints.back().result) {
636 af.constrained_witness.insert(output);
637 }
638 af.original_opcode_indices.blake2s_constraints.push_back(opcode_index);
641 .inputs = transform::map(arg.inputs,
642 [](auto& e) {
643 return Blake3Input{
644 .blackbox_input = parse_input(e),
645 .num_bits = e.num_bits,
646 };
647 }),
648 .result = transform::map(*arg.outputs, [](auto& e) { return e.value; }),
649 });
650 for (auto& output : af.blake3_constraints.back().result) {
651 af.constrained_witness.insert(output);
652 }
653 af.original_opcode_indices.blake3_constraints.push_back(opcode_index);
655 af.ecdsa_k1_constraints.push_back(EcdsaConstraint{
656 .hashed_message =
657 transform::map(*arg.hashed_message, [](auto& e) { return get_witness_from_function_input(e); }),
658 .signature =
659 transform::map(*arg.signature, [](auto& e) { return get_witness_from_function_input(e); }),
660 .pub_x_indices =
661 transform::map(*arg.public_key_x, [](auto& e) { return get_witness_from_function_input(e); }),
662 .pub_y_indices =
663 transform::map(*arg.public_key_y, [](auto& e) { return get_witness_from_function_input(e); }),
664 .result = arg.output.value,
665 });
666 af.constrained_witness.insert(af.ecdsa_k1_constraints.back().result);
667 af.original_opcode_indices.ecdsa_k1_constraints.push_back(opcode_index);
669 af.ecdsa_r1_constraints.push_back(EcdsaConstraint{
670 .hashed_message =
671 transform::map(*arg.hashed_message, [](auto& e) { return get_witness_from_function_input(e); }),
672 .signature =
673 transform::map(*arg.signature, [](auto& e) { return get_witness_from_function_input(e); }),
674 .pub_x_indices =
675 transform::map(*arg.public_key_x, [](auto& e) { return get_witness_from_function_input(e); }),
676 .pub_y_indices =
677 transform::map(*arg.public_key_y, [](auto& e) { return get_witness_from_function_input(e); }),
678 .result = arg.output.value,
679 });
680 af.constrained_witness.insert(af.ecdsa_r1_constraints.back().result);
681 af.original_opcode_indices.ecdsa_r1_constraints.push_back(opcode_index);
683 af.multi_scalar_mul_constraints.push_back(MultiScalarMul{
684 .points = transform::map(arg.points, [](auto& e) { return parse_input(e); }),
685 .scalars = transform::map(arg.scalars, [](auto& e) { return parse_input(e); }),
686 .out_point_x = (*arg.outputs)[0].value,
687 .out_point_y = (*arg.outputs)[1].value,
688 .out_point_is_infinite = (*arg.outputs)[2].value,
689 });
690 af.constrained_witness.insert(af.multi_scalar_mul_constraints.back().out_point_x);
691 af.constrained_witness.insert(af.multi_scalar_mul_constraints.back().out_point_y);
692 af.constrained_witness.insert(af.multi_scalar_mul_constraints.back().out_point_is_infinite);
693 af.original_opcode_indices.multi_scalar_mul_constraints.push_back(opcode_index);
695 auto input_1_x = parse_input((*arg.input1)[0]);
696 auto input_1_y = parse_input((*arg.input1)[1]);
697 auto input_1_infinite = parse_input((*arg.input1)[2]);
698 auto input_2_x = parse_input((*arg.input2)[0]);
699 auto input_2_y = parse_input((*arg.input2)[1]);
700 auto input_2_infinite = parse_input((*arg.input2)[2]);
701
702 af.ec_add_constraints.push_back(EcAdd{
703 .input1_x = input_1_x,
704 .input1_y = input_1_y,
705 .input1_infinite = input_1_infinite,
706 .input2_x = input_2_x,
707 .input2_y = input_2_y,
708 .input2_infinite = input_2_infinite,
709 .result_x = (*arg.outputs)[0].value,
710 .result_y = (*arg.outputs)[1].value,
711 .result_infinite = (*arg.outputs)[2].value,
712 });
713 af.constrained_witness.insert(af.ec_add_constraints.back().result_x);
714 af.constrained_witness.insert(af.ec_add_constraints.back().result_y);
715 af.constrained_witness.insert(af.ec_add_constraints.back().result_infinite);
716 af.original_opcode_indices.ec_add_constraints.push_back(opcode_index);
718 af.keccak_permutations.push_back(Keccakf1600{
719 .state = transform::map(*arg.inputs, [](auto& e) { return parse_input(e); }),
720 .result = transform::map(*arg.outputs, [](auto& e) { return e.value; }),
721 });
722 for (auto& output : af.keccak_permutations.back().result) {
723 af.constrained_witness.insert(output);
724 }
725 af.original_opcode_indices.keccak_permutations.push_back(opcode_index);
727
728 auto input_key = get_witness_from_function_input(arg.key_hash);
729
730 auto proof_type_in = arg.proof_type;
731
732 auto c = RecursionConstraint{
733 .key = transform::map(arg.verification_key,
734 [](auto& e) { return get_witness_from_function_input(e); }),
735 .proof = transform::map(arg.proof, [](auto& e) { return get_witness_from_function_input(e); }),
736 .public_inputs =
737 transform::map(arg.public_inputs, [](auto& e) { return get_witness_from_function_input(e); }),
738 .key_hash = input_key,
739 .proof_type = proof_type_in,
740 };
741
742 // Add the recursion constraint to the appropriate container based on proof type
743 switch (c.proof_type) {
744 case HONK_ZK:
745 case HONK:
746 case ROLLUP_HONK:
747 case ROOT_ROLLUP_HONK:
748 af.honk_recursion_constraints.push_back(c);
749 af.original_opcode_indices.honk_recursion_constraints.push_back(opcode_index);
750 break;
751 case OINK:
752 case PG:
753 case PG_TAIL:
754 case PG_FINAL:
755 af.pg_recursion_constraints.push_back(c);
756 af.original_opcode_indices.pg_recursion_constraints.push_back(opcode_index);
757 break;
758 case AVM:
759 af.avm_recursion_constraints.push_back(c);
760 af.original_opcode_indices.avm_recursion_constraints.push_back(opcode_index);
761 break;
762 case CIVC:
763 af.civc_recursion_constraints.push_back(c);
764 af.original_opcode_indices.civc_recursion_constraints.push_back(opcode_index);
765 break;
766 default:
767 throw_or_abort("Invalid PROOF_TYPE in RecursionConstraint!");
768 }
770 af.bigint_from_le_bytes_constraints.push_back(BigIntFromLeBytes{
771 .inputs = transform::map(arg.inputs, [](auto& e) { return get_witness_from_function_input(e); }),
772 .modulus = transform::map(arg.modulus, [](auto& e) -> uint32_t { return e; }),
773 .result = arg.output,
774 });
775 af.original_opcode_indices.bigint_from_le_bytes_constraints.push_back(opcode_index);
777 af.bigint_to_le_bytes_constraints.push_back(BigIntToLeBytes{
778 .input = arg.input,
779 .result = transform::map(arg.outputs, [](auto& e) { return e.value; }),
780 });
781 for (auto& output : af.bigint_to_le_bytes_constraints.back().result) {
782 af.constrained_witness.insert(output);
783 }
784 af.original_opcode_indices.bigint_to_le_bytes_constraints.push_back(opcode_index);
786 af.bigint_operations.push_back(BigIntOperation{
787 .lhs = arg.lhs,
788 .rhs = arg.rhs,
789 .result = arg.output,
790 .opcode = BigIntOperationType::Add,
791 });
792 af.original_opcode_indices.bigint_operations.push_back(opcode_index);
794 af.bigint_operations.push_back(BigIntOperation{
795 .lhs = arg.lhs,
796 .rhs = arg.rhs,
797 .result = arg.output,
798 .opcode = BigIntOperationType::Sub,
799 });
800 af.original_opcode_indices.bigint_operations.push_back(opcode_index);
802 af.bigint_operations.push_back(BigIntOperation{
803 .lhs = arg.lhs,
804 .rhs = arg.rhs,
805 .result = arg.output,
806 .opcode = BigIntOperationType::Mul,
807 });
808 af.original_opcode_indices.bigint_operations.push_back(opcode_index);
810 af.bigint_operations.push_back(BigIntOperation{
811 .lhs = arg.lhs,
812 .rhs = arg.rhs,
813 .result = arg.output,
814 .opcode = BigIntOperationType::Div,
815 });
816 af.original_opcode_indices.bigint_operations.push_back(opcode_index);
818 af.poseidon2_constraints.push_back(Poseidon2Constraint{
819 .state = transform::map(arg.inputs, [](auto& e) { return parse_input(e); }),
820 .result = transform::map(arg.outputs, [](auto& e) { return e.value; }),
821 .len = arg.len,
822 });
823 for (auto& output : af.poseidon2_constraints.back().result) {
824 af.constrained_witness.insert(output);
825 }
826 af.original_opcode_indices.poseidon2_constraints.push_back(opcode_index);
827 }
828 },
829 arg.value.value);
830}
831
833{
834 BlockConstraint block{ .init = {}, .trace = {}, .type = BlockType::ROM };
837
838 auto len = mem_init.init.size();
839 for (size_t i = 0; i < len; ++i) {
840 block.init.push_back(poly_triple{
841 .a = mem_init.init[i].value,
842 .b = 0,
843 .c = 0,
844 .q_m = 0,
845 .q_l = 1,
846 .q_r = 0,
847 .q_o = 0,
848 .q_c = 0,
849 });
850 }
851
852 // Databus is only supported for Goblin, non Goblin builders will treat call_data and return_data as normal
853 // array.
855 block.type = BlockType::CallData;
856 block.calldata_id = std::get<Acir::BlockType::CallData>(mem_init.block_type.value).value;
858 block.type = BlockType::ReturnData;
859 }
860
861 return block;
862}
863
864bool is_rom(Acir::MemOp const& mem_op)
865{
866 return mem_op.operation.mul_terms.empty() && mem_op.operation.linear_combinations.empty() &&
867 uint256_t(mem_op.operation.q_c) == 0;
868}
869
870uint32_t poly_to_witness(const poly_triple poly)
871{
872 if (poly.q_m == 0 && poly.q_r == 0 && poly.q_o == 0 && poly.q_l == 1 && poly.q_c == 0) {
873 return poly.a;
874 }
875 return 0;
876}
877
879{
880 uint8_t access_type = 1;
881 if (is_rom(mem_op.op)) {
882 access_type = 0;
883 }
884 if (access_type == 1) {
885 // We are not allowed to write on the databus
886 ASSERT((block.type != BlockType::CallData) && (block.type != BlockType::ReturnData));
887 block.type = BlockType::RAM;
888 }
889
890 // Update the ranges of the index using the array length
892 int bit_range = std::bit_width(block.init.size());
893 uint32_t index_witness = poly_to_witness(index);
894 if (index_witness != 0 && bit_range > 0) {
895 unsigned int u_bit_range = static_cast<unsigned int>(bit_range);
896 // Updates both af.minimal_range and af.index_range with u_bit_range when it is lower.
897 // By doing so, we keep these invariants:
898 // - minimal_range contains the smallest possible range for a witness
899 // - index_range constains the smallest range for a witness implied by any array operation
900 if (af.minimal_range.contains(index_witness)) {
901 if (af.minimal_range[index_witness] > u_bit_range) {
902 af.minimal_range[index_witness] = u_bit_range;
903 }
904 } else {
905 af.minimal_range[index_witness] = u_bit_range;
906 }
907 if (af.index_range.contains(index_witness)) {
908 if (af.index_range[index_witness] > u_bit_range) {
909 af.index_range[index_witness] = u_bit_range;
910 }
911 } else {
912 af.index_range[index_witness] = u_bit_range;
913 }
914 }
915
916 MemOp acir_mem_op =
917 MemOp{ .access_type = access_type, .index = index, .value = serialize_arithmetic_gate(mem_op.op.value) };
918 block.trace.push_back(acir_mem_op);
919}
920
922{
923 AcirFormat af;
924 // `varnum` is the true number of variables, thus we add one to the index which starts at zero
925 af.varnum = circuit.current_witness_index + 1;
926 af.num_acir_opcodes = static_cast<uint32_t>(circuit.opcodes.size());
927 af.public_inputs = join({ transform::map(circuit.public_parameters.value, [](auto e) { return e.value; }),
928 transform::map(circuit.return_values.value, [](auto e) { return e.value; }) });
929 // Map to a pair of: BlockConstraint, and list of opcodes associated with that BlockConstraint
930 // NOTE: We want to deterministically visit this map, so unordered_map should not be used.
932 for (size_t i = 0; i < circuit.opcodes.size(); ++i) {
933 const auto& gate = circuit.opcodes[i];
934 std::visit(
935 [&](auto&& arg) {
936 using T = std::decay_t<decltype(arg)>;
938 handle_arithmetic(arg, af, i);
940 handle_blackbox_func_call(arg, af, i);
941 } else if constexpr (std::is_same_v<T, Acir::Opcode::MemoryInit>) {
942 auto block = handle_memory_init(arg);
943 uint32_t block_id = arg.block_id.value;
944 block_id_to_block_constraint[block_id] = { block, /*opcode_indices=*/{ i } };
945 } else if constexpr (std::is_same_v<T, Acir::Opcode::MemoryOp>) {
946 auto block = block_id_to_block_constraint.find(arg.block_id.value);
947 if (block == block_id_to_block_constraint.end()) {
948 throw_or_abort("unitialized MemoryOp");
949 }
950 handle_memory_op(arg, af, block->second.first);
951 block->second.second.push_back(i);
952 }
953 },
954 gate.value);
955 }
956 for (const auto& [block_id, block] : block_id_to_block_constraint) {
957 // Note: the trace will always be empty for ReturnData since it cannot be explicitly read from in noir
958 if (!block.first.trace.empty() || block.first.type == BlockType::ReturnData ||
959 block.first.type == BlockType::CallData) {
960 af.block_constraints.push_back(block.first);
961 af.original_opcode_indices.block_constraints.push_back(block.second);
962 }
963 }
964 return af;
965}
966
968{
969 // TODO(https://github.com/AztecProtocol/barretenberg/issues/927): Move to using just
970 // `program_buf_to_acir_format` once Honk fully supports all ACIR test flows For now the backend still expects
971 // to work with a single ACIR function
972 auto program = deserialize_program(std::move(buf));
973 auto circuit = program.functions[0];
974
975 return circuit_serde_to_acir_format(circuit);
976}
977
987{
988 WitnessVector wv;
989 size_t index = 0;
990 for (const auto& e : witness_map.value) {
991 // ACIR uses a sparse format for WitnessMap where unused witness indices may be left unassigned.
992 // To ensure that witnesses sit at the correct indices in the `WitnessVector`, we fill any indices
993 // which do not exist within the `WitnessMap` with the dummy value of zero.
994 while (index < e.first.value) {
995 wv.emplace_back(0);
996 index++;
997 }
998 wv.emplace_back(uint256_t(e.second));
999 index++;
1000 }
1001 return wv;
1002}
1003
1005{
1006 // TODO(https://github.com/AztecProtocol/barretenberg/issues/927): Move to using just
1007 // `witness_buf_to_witness_stack` once Honk fully supports all ACIR test flows. For now the backend still
1008 // expects to work with the stop of the `WitnessStack`.
1009 auto witness_stack = deserialize_witness_stack(std::move(buf));
1010 auto w = witness_stack.stack[witness_stack.stack.size() - 1].witness;
1011
1013}
1014
1016{
1017 auto program = deserialize_program(std::move(buf));
1018
1019 std::vector<AcirFormat> constraint_systems;
1020 constraint_systems.reserve(program.functions.size());
1021 for (auto const& function : program.functions) {
1022 constraint_systems.emplace_back(circuit_serde_to_acir_format(function));
1023 }
1024
1025 return constraint_systems;
1026}
1027
1029{
1030 auto witness_stack = deserialize_witness_stack(std::move(buf));
1031 WitnessVectorStack witness_vector_stack;
1032 witness_vector_stack.reserve(witness_stack.stack.size());
1033 for (auto const& stack_item : witness_stack.stack) {
1034 witness_vector_stack.emplace_back(stack_item.index, witness_map_to_witness_vector(stack_item.witness));
1035 }
1036 return witness_vector_stack;
1037}
1038
1039AcirProgramStack get_acir_program_stack(std::string const& bytecode_path, std::string const& witness_path)
1040{
1041 vinfo("in get_acir_program_stack; witness path is ", witness_path);
1042 std::vector<uint8_t> bytecode = get_bytecode(bytecode_path);
1043 std::vector<AcirFormat> constraint_systems = program_buf_to_acir_format(std::move(bytecode));
1044 WitnessVectorStack witness_stack = [&]() {
1045 if (witness_path.empty()) {
1046 info("producing a stack of empties");
1047 WitnessVectorStack stack_of_empties{ constraint_systems.size(),
1048 std::make_pair(uint32_t(), WitnessVector()) };
1049 return stack_of_empties;
1050 }
1051 std::vector<uint8_t> witness_data = get_bytecode(witness_path);
1052 return witness_buf_to_witness_stack(std::move(witness_data));
1053 }();
1054
1055 return { std::move(constraint_systems), std::move(witness_stack) };
1056}
1057
1058} // namespace acir_format
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:129
#define ASSERT(expression,...)
Definition assert.hpp:49
void vinfo(Args... args)
Definition log.hpp:76
void info(Args... args)
Definition log.hpp:70
TestTraceContainer trace
uint8_t const * buf
Definition data_store.hpp:9
uint8_t buffer[RANDOM_BUFFER_SIZE]
Definition engine.cpp:34
const auto init
Definition fr.bench.cpp:141
std::vector< uint8_t > get_bytecode(const std::string &bytecodePath)
uint32_t poly_to_witness(const poly_triple poly)
Acir::Program deserialize_program(std::vector< uint8_t > &&buf)
Deserializes a Program from bytes, trying msgpack or bincode formats.
T deserialize_any_format(std::vector< uint8_t > &&buf, std::function< T(msgpack::object const &)> decode_msgpack, std::function< T(std::vector< uint8_t >)> decode_bincode)
Deserialize buf either based on the first byte interpreted as a Noir serialization format byte,...
std::pair< uint32_t, uint32_t > is_assert_equal(Acir::Opcode::AssertZero const &arg, poly_triple const &pt, AcirFormat const &af)
void assign_linear_term(mul_quad_< fr > &gate, int index, uint32_t witness_index, fr const &scaling)
Assigns a linear term to a specific index in a mul_quad_ gate.
WitnessVector witness_buf_to_witness_data(std::vector< uint8_t > &&buf)
Converts from the ACIR-native WitnessStack format to Barretenberg's internal WitnessVector format.
void constrain_witnesses(Acir::Opcode::AssertZero const &arg, AcirFormat &af)
std::vector< mul_quad_< fr > > split_into_mul_quad_gates(Acir::Expression const &arg)
Accumulate the input expression into a serie of quad gates.
Witnesses::WitnessStack deserialize_witness_stack(std::vector< uint8_t > &&buf)
Deserializes a WitnessStack from bytes, trying msgpack or bincode formats.
AcirFormat circuit_serde_to_acir_format(Acir::Circuit const &circuit)
poly_triple serialize_arithmetic_gate(Acir::Expression const &arg)
Construct a poly_tuple for a standard width-3 arithmetic gate from its acir representation.
BlockConstraint handle_memory_init(Acir::Opcode::MemoryInit const &mem_init)
uint32_t get_witness_from_function_input(Acir::FunctionInput input)
WitnessVector witness_map_to_witness_vector(Witnesses::WitnessMap const &witness_map)
Converts from the ACIR-native WitnessMap format to Barretenberg's internal WitnessVector format.
AcirProgramStack get_acir_program_stack(std::string const &bytecode_path, std::string const &witness_path)
std::vector< std::pair< uint32_t, WitnessVector > > WitnessVectorStack
void handle_arithmetic(Acir::Opcode::AssertZero const &arg, AcirFormat &af, size_t opcode_index)
mul_quad_< fr > serialize_mul_quad_gate(Acir::Expression const &arg)
WitnessOrConstant< bb::fr > parse_input(Acir::FunctionInput input)
WitnessVectorStack witness_buf_to_witness_stack(std::vector< uint8_t > &&buf)
bool is_rom(Acir::MemOp const &mem_op)
void handle_memory_op(Acir::Opcode::MemoryOp const &mem_op, AcirFormat &af, BlockConstraint &block)
std::vector< AcirFormat > program_buf_to_acir_format(std::vector< uint8_t > &&buf)
AcirFormat circuit_buf_to_acir_format(std::vector< uint8_t > &&buf)
bb::SlabVector< bb::fr > WitnessVector
void handle_blackbox_func_call(Acir::Opcode::BlackBoxFuncCall const &arg, AcirFormat &af, size_t opcode_index)
Cont< OutElem > map(Cont< InElem, Args... > const &in, F &&op)
Definition map.hpp:15
Entry point for Barretenberg command-line interface.
field< Bn254FrParams > fr
Definition fr.hpp:174
C join(std::initializer_list< C > to_join)
Definition container.hpp:26
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
uint8_t len
std::variant< Memory, CallData, ReturnData > value
Definition acir.hpp:4024
Acir::PublicInputs return_values
Definition acir.hpp:5159
std::vector< Acir::Opcode > opcodes
Definition acir.hpp:5155
uint32_t current_witness_index
Definition acir.hpp:5154
Acir::PublicInputs public_parameters
Definition acir.hpp:5158
std::variant< Constant, Witness > value
Definition acir.hpp:2955
std::vector< std::tuple< std::string, Acir::Witness, Acir::Witness > > mul_terms
Definition acir.hpp:4109
std::string q_c
Definition acir.hpp:4111
std::vector< std::tuple< std::string, Acir::Witness > > linear_combinations
Definition acir.hpp:4110
Acir::ConstantOrWitnessEnum input
Definition acir.hpp:3040
Acir::Expression value
Definition acir.hpp:4424
Acir::Expression operation
Definition acir.hpp:4422
Acir::Expression index
Definition acir.hpp:4423
Acir::Expression value
Definition acir.hpp:4451
Acir::BlackBoxFuncCall value
Definition acir.hpp:4471
std::vector< Acir::Witness > init
Definition acir.hpp:4519
Acir::BlockType block_type
Definition acir.hpp:4520
std::vector< Acir::Circuit > functions
Definition acir.hpp:5214
static Program bincodeDeserialize(std::vector< uint8_t >)
Definition acir.hpp:12254
std::vector< Acir::Circuit > functions
Definition acir.hpp:5238
std::vector< Acir::Witness > value
Definition acir.hpp:5134
std::map< Witnesses::Witness, std::string > value
static WitnessStack bincodeDeserialize(std::vector< uint8_t >)
std::vector< WitnessOrConstant< bb::fr > > inputs
std::vector< Blake2sConstraint > blake2s_constraints
std::vector< Sha256Compression > sha256_compression
bb::SlabVector< PolyTripleConstraint > poly_triple_constraints
bb::SlabVector< std::vector< bb::mul_quad_< bb::curve::BN254::ScalarField > > > big_quad_constraints
std::vector< LogicConstraint > logic_constraints
std::map< uint32_t, uint32_t > index_range
std::vector< Blake3Constraint > blake3_constraints
std::vector< RangeConstraint > range_constraints
std::vector< bb::poly_triple_< bb::curve::BN254::ScalarField > > assert_equalities
std::vector< AES128Constraint > aes128_constraints
std::map< uint32_t, uint32_t > minimal_range
AcirFormatOriginalOpcodeIndices original_opcode_indices
std::set< uint32_t > constrained_witness
bb::SlabVector< bb::mul_quad_< bb::curve::BN254::ScalarField > > quad_constraints
std::vector< BlockConstraint > block_constraints
std::vector< uint32_t > public_inputs
std::vector< std::vector< size_t > > block_constraints
Storage for constaint_systems/witnesses for a stack of acir programs.
std::vector< Blake2sInput > inputs
std::vector< Blake3Input > inputs
std::vector< bb::poly_triple > init
WitnessOrConstant< bb::fr > a
std::array< WitnessOrConstant< bb::fr >, 16 > inputs
static constexpr field zero()
void throw_or_abort(std::string const &err)