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: Complete, auditors: [Federico], commit: 2094fd1467dd9a94803b2c5007cf60ac357aa7d2 }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
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
31
32bb::fr from_buffer_with_bound_checks(const std::vector<uint8_t>& buffer)
33{
34 BB_ASSERT_EQ(buffer.size(), 32U, "acir_format::from_buffer_with_bound_checks: buffer size must be 32 bytes.");
35 return fr::serialize_from_buffer(buffer.data());
36}
37
39{
40 WitnessOrConstant<bb::fr> result = std::visit(
41 [&](auto&& e) {
42 using T = std::decay_t<decltype(e)>;
45 .index = e.value.value,
46 .value = bb::fr::zero(),
47 .is_constant = false,
48 };
51 .index = bb::stdlib::IS_CONSTANT,
52 .value = from_buffer_with_bound_checks(e.value),
53 .is_constant = true,
54 };
55 } else {
56 bb::assert_failure("acir_format::parse_input: unrecognized Acir::FunctionInput variant. An error here "
57 "means there was a serialization error.");
58 }
59 },
60 input.value);
61 return result;
62}
63
65{
67 "acir_format::get_witness_from_function_input: input must be a Witness variant. An error here means "
68 "there was a serialization error.");
69
70 return std::get<Acir::FunctionInput::Witness>(input.value).value.value;
71}
72
73void update_max_witness_index(const uint32_t witness_idx, AcirFormat& af)
74{
75 if (witness_idx != stdlib::IS_CONSTANT) {
76 af.max_witness_index = std::max(af.max_witness_index, witness_idx);
77 }
78}
79
81{
82 // Process multiplication terms: each term has two witness indices
83 for (const auto& mul_term : expr.mul_terms) {
86 }
87
88 // Process linear combinations: each term has one witness index
89 for (const auto& linear_term : expr.linear_combinations) {
91 }
92}
93
95{
96 auto update_max_witness_index_from_function_input = [&](const Acir::FunctionInput& input) {
99 }
100 };
101
102 auto update_max_witness_index_from_witness = [&](const Acir::Witness& witness) {
103 update_max_witness_index(witness.value, af);
104 };
105
106 std::visit(
107 [&](auto&& arg) {
108 using T = std::decay_t<decltype(arg)>;
112 std::visit(
113 [&](auto&& bb_arg) {
114 using BBT = std::decay_t<decltype(bb_arg)>;
117 update_max_witness_index_from_function_input(bb_arg.lhs);
118 update_max_witness_index_from_function_input(bb_arg.rhs);
119 update_max_witness_index_from_witness(bb_arg.output);
121 update_max_witness_index_from_function_input(bb_arg.input);
123 for (const auto& input : bb_arg.inputs) {
124 update_max_witness_index_from_function_input(input);
125 }
126 for (const auto& input : *bb_arg.iv) {
127 update_max_witness_index_from_function_input(input);
128 }
129 for (const auto& input : *bb_arg.key) {
130 update_max_witness_index_from_function_input(input);
131 }
132 for (const auto& output : bb_arg.outputs) {
133 update_max_witness_index_from_witness(output);
134 }
136 for (const auto& input : *bb_arg.inputs) {
137 update_max_witness_index_from_function_input(input);
138 }
139 for (const auto& input : *bb_arg.hash_values) {
140 update_max_witness_index_from_function_input(input);
141 }
142 for (const auto& output : *bb_arg.outputs) {
143 update_max_witness_index_from_witness(output);
144 }
147 for (const auto& input : bb_arg.inputs) {
148 update_max_witness_index_from_function_input(input);
149 }
150 for (const auto& output : *bb_arg.outputs) {
151 update_max_witness_index_from_witness(output);
152 }
155 for (const auto& input : *bb_arg.public_key_x) {
156 update_max_witness_index_from_function_input(input);
157 }
158 for (const auto& input : *bb_arg.public_key_y) {
159 update_max_witness_index_from_function_input(input);
160 }
161 for (const auto& input : *bb_arg.signature) {
162 update_max_witness_index_from_function_input(input);
163 }
164 for (const auto& input : *bb_arg.hashed_message) {
165 update_max_witness_index_from_function_input(input);
166 }
167 update_max_witness_index_from_function_input(bb_arg.predicate);
168 update_max_witness_index_from_witness(bb_arg.output);
170 for (const auto& input : bb_arg.points) {
171 update_max_witness_index_from_function_input(input);
172 }
173 for (const auto& input : bb_arg.scalars) {
174 update_max_witness_index_from_function_input(input);
175 }
176 update_max_witness_index_from_function_input(bb_arg.predicate);
177 for (const auto& output : *bb_arg.outputs) {
178 update_max_witness_index_from_witness(output);
179 }
181 for (const auto& input : *bb_arg.input1) {
182 update_max_witness_index_from_function_input(input);
183 }
184 for (const auto& input : *bb_arg.input2) {
185 update_max_witness_index_from_function_input(input);
186 }
187 update_max_witness_index_from_function_input(bb_arg.predicate);
188 for (const auto& output : *bb_arg.outputs) {
189 update_max_witness_index_from_witness(output);
190 }
192 for (const auto& input : *bb_arg.inputs) {
193 update_max_witness_index_from_function_input(input);
194 }
195 for (const auto& output : *bb_arg.outputs) {
196 update_max_witness_index_from_witness(output);
197 }
199 for (const auto& input : bb_arg.verification_key) {
200 update_max_witness_index_from_function_input(input);
201 }
202 for (const auto& input : bb_arg.proof) {
203 update_max_witness_index_from_function_input(input);
204 }
205 for (const auto& input : bb_arg.public_inputs) {
206 update_max_witness_index_from_function_input(input);
207 }
208 update_max_witness_index_from_function_input(bb_arg.key_hash);
209 update_max_witness_index_from_function_input(bb_arg.predicate);
211 for (const auto& input : bb_arg.inputs) {
212 update_max_witness_index_from_function_input(input);
213 }
214 for (const auto& output : bb_arg.outputs) {
215 update_max_witness_index_from_witness(output);
216 }
217 }
218 },
219 arg.value.value);
220 } else if constexpr (std::is_same_v<T, Acir::Opcode::MemoryInit>) {
221 for (const auto& init : arg.init) {
222 update_max_witness_index_from_witness(init);
223 }
224 } else if constexpr (std::is_same_v<T, Acir::Opcode::MemoryOp>) {
227 update_max_witness_index_from_expression(arg.op.operation, af);
229 // Process inputs
230 for (const auto& input : arg.inputs) {
231 std::visit(
232 [&](auto&& e) {
233 using IT = std::decay_t<decltype(e)>;
237 for (const auto& expr : e.value) {
239 }
240 }
241 // MemoryArray contains a BlockId, no direct witnesses to track
242 },
243 input.value);
244 }
245 // Process outputs
246 for (const auto& output : arg.outputs) {
247 std::visit(
248 [&](auto&& e) {
249 using OT = std::decay_t<decltype(e)>;
251 update_max_witness_index_from_witness(e.value);
253 for (const auto& witness : e.value) {
254 update_max_witness_index_from_witness(witness);
255 }
256 }
257 },
258 output.value);
259 }
260 // Process optional predicate
261 if (arg.predicate.has_value()) {
262 update_max_witness_index_from_expression(arg.predicate.value(), af);
263 }
264 } else {
265 bb::assert_failure("acir_format::update_max_witness_index_from_opcode: Unrecognized opcode.");
266 }
267 },
268 opcode.value);
269}
270
272
273template <typename T>
274T deserialize_msgpack_compact(std::vector<uint8_t>&& buf, std::function<T(msgpack::object const&)> decode_msgpack)
275{
276 BB_ASSERT(!buf.empty(), "deserialize_msgpack_compact: buffer is empty");
277
278 // Expect format marker for msgpack or msgpack-compact
279 const uint8_t FORMAT_MSGPACK = 2;
280 const uint8_t FORMAT_MSGPACK_COMPACT = 3;
281 uint8_t format_u8 = buf[0];
282 BB_ASSERT(format_u8 == FORMAT_MSGPACK || format_u8 == FORMAT_MSGPACK_COMPACT,
283 "deserialize_msgpack_compact: expected msgpack format marker (2 or 3), got " + std::to_string(format_u8));
284
285 // Skip the format marker to get the data.
286 const char* buffer = &reinterpret_cast<const char*>(buf.data())[1];
287 size_t size = buf.size() - 1;
288
289 auto oh = msgpack::unpack(buffer, size);
290 auto o = oh.get();
291
292 // Expect ARRAY type for msgpack-compact format
293 BB_ASSERT(o.type == msgpack::type::ARRAY,
294 "deserialize_msgpack_compact: expected ARRAY type, got " + std::to_string(o.type));
295
296 return decode_msgpack(o);
297}
298
300{
302 circuit.opcodes.size(), UINT32_MAX, "acir_format::circuit_serde_to_acir_format: too many opcodes in circuit.");
303
304 AcirFormat af;
305 af.num_acir_opcodes = static_cast<uint32_t>(circuit.opcodes.size());
306 af.public_inputs = join({
308 [&](const Acir::Witness& e) {
309 update_max_witness_index(e.value, af);
310 return e.value;
311 }),
313 [&](const Acir::Witness& e) {
314 update_max_witness_index(e.value, af);
315 return e.value;
316 }),
317 });
318 // Map to a pair of: BlockConstraint, and list of opcodes associated with that BlockConstraint
319 // Block constraints are built as we process the opcodes, so we store them in this map and we add them to the
320 // AcirFormat struct at the end
321 // NOTE: We want to deterministically visit this map, so unordered_map should not be used.
323
324 for (size_t i = 0; i < circuit.opcodes.size(); ++i) {
325 const auto& gate = circuit.opcodes[i];
327 std::visit(
328 [&](auto&& arg) {
329 using T = std::decay_t<decltype(arg)>;
334 } else if constexpr (std::is_same_v<T, Acir::Opcode::MemoryInit>) {
335 auto block = memory_init_to_block_constraint(arg);
336 uint32_t block_id = arg.block_id.value;
337 block_id_to_block_constraint[block_id] = { block, /*opcode_indices=*/{ i } };
338 } else if constexpr (std::is_same_v<T, Acir::Opcode::MemoryOp>) {
339 auto block = block_id_to_block_constraint.find(arg.block_id.value);
340 if (block == block_id_to_block_constraint.end()) {
341 bb::assert_failure("acir_format::circuit_serder_to_acir_format: unitialized MemoryOp.");
342 }
343 add_memory_op_to_block_constraint(arg, block->second.first);
344 block->second.second.push_back(i);
346 // This is a no-op in barretenberg
347 } else {
348 bb::assert_failure("acir_format::circuit_serde_to_acir_format: Unrecognized Acir Opcode. An error "
349 "here means there was a serialization error.");
350 }
351 },
352 gate.value);
353 }
354 // Add the block constraints to the AcirFormat struct
355 for (const auto& [_, block] : block_id_to_block_constraint) {
356 af.block_constraints.push_back(block.first);
357 af.original_opcode_indices.block_constraints.push_back(block.second);
358 }
359
360 return af;
361}
362
364{
365 // We need to deserialize into Acir::Program first because the buffer returned by Noir has this structure
366 auto program = deserialize_msgpack_compact<Acir::Program>(std::move(buf), [](auto o) -> Acir::Program {
367 Acir::Program program;
368 try {
369 // Deserialize into a partial structure that ignores the Brillig parts,
370 // so that new opcodes can be added without breaking Barretenberg.
371 Acir::ProgramWithoutBrillig program_wob;
372 o.convert(program_wob);
373 program.functions = program_wob.functions;
374 } catch (const msgpack::type_error&) {
375 std::cerr << o << std::endl;
376 bb::assert_failure("acir_format::circuit_buf_to_acir_format: failed to convert msgpack data to Program");
377 }
378 return program;
379 });
380 BB_ASSERT_EQ(program.functions.size(), 1U, "circuit_buf_to_acir_format: expected single function in ACIR program");
381
382 return circuit_serde_to_acir_format(program.functions[0]);
383}
384
386{
387 // We need to deserialize into WitnessStack first because the buffer returned by Noir has this structure
388 auto witness_stack = deserialize_msgpack_compact<Witnesses::WitnessStack>(std::move(buf), [](auto o) {
389 Witnesses::WitnessStack witness_stack;
390 try {
391 o.convert(witness_stack);
392 } catch (const msgpack::type_error&) {
393 std::cerr << o << std::endl;
395 "acir_format::witness_buf_to_witness_vector: failed to convert msgpack data to WitnessStack");
396 }
397 return witness_stack;
398 });
399 BB_ASSERT_EQ(witness_stack.stack.size(),
400 1U,
401 "acir_format::witness_buf_to_witness_vector: expected single WitnessMap in WitnessStack");
402
403 return witness_map_to_witness_vector(witness_stack.stack[0].witness);
404}
405
407{
408 // Note that the WitnessMap is in increasing order of witness indices because the comparator for the Acir::Witness
409 // is defined in terms of the witness index.
410
411 WitnessVector witness_vector;
412 for (size_t index = 0; const auto& e : witness_map.value) {
413 // ACIR uses a sparse format for WitnessMap where unused witness indices may be left unassigned.
414 // To ensure that witnesses sit at the correct indices in the `WitnessVector`, we fill any indices
415 // which do not exist within the `WitnessMap` with the random values. We use random values instead of zero
416 // because unassigned witnesses indices are not supposed to be used in any constraint, so filling them with a
417 // random value helps catching bugs.
418 while (index < e.first.value) {
419 witness_vector.emplace_back(fr::random_element());
420 index++;
421 }
422 witness_vector.emplace_back(from_buffer_with_bound_checks(e.second));
423 index++;
424 }
425
426 return witness_vector;
427}
428
430
432 std::map<uint32_t, bb::fr>& linear_terms)
433{
434 // Lambda to add next linear term from linear_terms to the mul_quad_ gate and erase it from linear_terms
435 auto add_linear_term_and_erase = [](uint32_t& idx, fr& scaling, std::map<uint32_t, fr>& linear_terms) {
437 idx, bb::stdlib::IS_CONSTANT, "Attempting to override a non-constant witness index in mul_quad_ gate");
438 idx = linear_terms.begin()->first;
439 scaling += linear_terms.begin()->second;
440 linear_terms.erase(idx);
441 };
442
444 // We cannot precompute the exact number of gates that will result from the expression. Therefore, we reserve the
445 // maximum number of gates that could ever be needed: one per multiplication term plus one per linear term. The real
446 // number of gates will in general be lower than this.
447 BB_ASSERT_LTE(arg.mul_terms.size(),
448 SIZE_MAX - linear_terms.size(),
449 "split_into_mul_quad_gates: overflow when reserving space for mul_quad_ gates.");
450 result.reserve(arg.mul_terms.size() + linear_terms.size());
451
452 // Step 1. Add multiplication terms and linear terms with the same witness index
453 for (const auto& mul_term : arg.mul_terms) {
454 result.emplace_back(mul_quad_<fr>{
455 .a = std::get<1>(mul_term).value,
456 .b = std::get<2>(mul_term).value,
457 .c = bb::stdlib::IS_CONSTANT,
458 .d = bb::stdlib::IS_CONSTANT,
459 .mul_scaling = from_buffer_with_bound_checks(std::get<0>(mul_term)),
460 .a_scaling = fr::zero(),
461 .b_scaling = fr::zero(),
462 .c_scaling = fr::zero(),
463 .d_scaling = fr::zero(),
464 .const_scaling = fr::zero(),
465 });
466
467 // Add linear terms corresponding to the witnesses involved in the multiplication term
468 auto& mul_quad = result.back();
469 if (linear_terms.contains(mul_quad.a)) {
470 mul_quad.a_scaling += linear_terms.at(mul_quad.a);
471 linear_terms.erase(mul_quad.a); // Remove it as the linear term for a has been processed
472 }
473 if (linear_terms.contains(mul_quad.b)) {
474 // Note that we enter here only if b is different from a
475 mul_quad.b_scaling += linear_terms.at(mul_quad.b);
476 linear_terms.erase(mul_quad.b); // Remove it as the linear term for b has been processed
477 }
478 }
479
480 // Step 2. Add linear terms to existing gates
481 bool is_first_gate = true;
482 for (auto& mul_quad : result) {
483 if (!linear_terms.empty()) {
484 add_linear_term_and_erase(mul_quad.c, mul_quad.c_scaling, linear_terms);
485 }
486
487 if (is_first_gate) {
488 // First gate contains the constant term and uses all four wires
489 mul_quad.const_scaling = from_buffer_with_bound_checks(arg.q_c);
490 if (!linear_terms.empty()) {
491 add_linear_term_and_erase(mul_quad.d, mul_quad.d_scaling, linear_terms);
492 }
493 is_first_gate = false;
494 }
495 }
496
497 // Step 3. Add remaining linear terms
498 while (!linear_terms.empty()) {
499 // We need to create new mul_quad_ gates to accomodate the remaining linear terms
500 mul_quad_<fr> mul_quad = {
501 .a = bb::stdlib::IS_CONSTANT,
502 .b = bb::stdlib::IS_CONSTANT,
503 .c = bb::stdlib::IS_CONSTANT,
504 .d = bb::stdlib::IS_CONSTANT,
505 .mul_scaling = fr::zero(),
506 .a_scaling = fr::zero(),
507 .b_scaling = fr::zero(),
508 .c_scaling = fr::zero(),
509 .d_scaling = fr::zero(),
510 .const_scaling = fr::zero(),
511 };
512 if (!linear_terms.empty()) {
513 add_linear_term_and_erase(mul_quad.a, mul_quad.a_scaling, linear_terms);
514 }
515 if (!linear_terms.empty()) {
516 add_linear_term_and_erase(mul_quad.b, mul_quad.b_scaling, linear_terms);
517 }
518 if (!linear_terms.empty()) {
519 add_linear_term_and_erase(mul_quad.c, mul_quad.c_scaling, linear_terms);
520 }
521 if (is_first_gate) {
522 // First gate contains the constant term and uses all four wires
524 if (!linear_terms.empty()) {
525 add_linear_term_and_erase(mul_quad.d, mul_quad.d_scaling, linear_terms);
526 }
527 is_first_gate = false;
528 }
529
530 result.emplace_back(mul_quad);
531 }
532
533 BB_ASSERT(!result.empty(),
534 "split_into_mul_quad_gates: resulted in zero gates. This means that there is an expression with no "
535 "multiplication terms and no linear terms.");
536 result.shrink_to_fit();
537
538 return result;
539}
540
542{
543 // Lambda to detect zero gates
544 auto is_zero_gate = [](const mul_quad_<fr>& gate) {
545 return ((gate.mul_scaling == fr(0)) && (gate.a_scaling == fr(0)) && (gate.b_scaling == fr(0)) &&
546 (gate.c_scaling == fr(0)) && (gate.d_scaling == fr(0)) && (gate.const_scaling == fr(0)));
547 };
548
549 auto linear_terms = process_linear_terms(arg.value);
550 bool is_single_gate = is_single_arithmetic_gate(arg.value, linear_terms);
551 std::vector<mul_quad_<fr>> mul_quads = split_into_mul_quad_gates(arg.value, linear_terms);
552
553 if (is_single_gate) {
554 BB_ASSERT_EQ(mul_quads.size(), 1U, "acir_format::assert_zero_to_quad_constraints: expected a single gate.");
555 auto mul_quad = mul_quads[0];
556
557 af.quad_constraints.push_back(mul_quad);
558 af.original_opcode_indices.quad_constraints.push_back(opcode_index);
559 } else {
560 BB_ASSERT_GT(mul_quads.size(),
561 1U,
562 "acir_format::assert_zero_to_quad_constraints: expected multiple gates but found one.");
563 af.big_quad_constraints.push_back(BigQuadConstraint(mul_quads));
564 af.original_opcode_indices.big_quad_constraints.push_back(opcode_index);
565 }
566
567 for (auto const& mul_quad : mul_quads) {
568 BB_ASSERT(!is_zero_gate(mul_quad),
569 "acir_format::assert_zero_to_quad_constraints: produced an arithmetic zero gate.");
570 }
571}
572
574 AcirFormat& af,
575 size_t opcode_index)
576{
577 auto to_witness_or_constant = [&](const Acir::FunctionInput& e) { return parse_input(e); };
578 auto to_witness = [&](const Acir::Witness& e) { return e.value; };
579 auto to_witness_from_input = [&](const Acir::FunctionInput& e) { return get_witness_from_function_input(e); };
580
581 std::visit(
582 [&](auto&& arg) {
583 using T = std::decay_t<decltype(arg)>;
586 .a = parse_input(arg.lhs),
587 .b = parse_input(arg.rhs),
588 .result = to_witness(arg.output),
589 .num_bits = arg.num_bits,
590 .is_xor_gate = false,
591 });
592 af.original_opcode_indices.logic_constraints.push_back(opcode_index);
595 .a = parse_input(arg.lhs),
596 .b = parse_input(arg.rhs),
597 .result = to_witness(arg.output),
598 .num_bits = arg.num_bits,
599 .is_xor_gate = true,
600 });
601 af.original_opcode_indices.logic_constraints.push_back(opcode_index);
605 .num_bits = arg.num_bits,
606 });
607 af.original_opcode_indices.range_constraints.push_back(opcode_index);
610 .inputs = transform::map(arg.inputs, to_witness_or_constant),
611 .iv = transform::map(*arg.iv, to_witness_or_constant),
612 .key = transform::map(*arg.key, to_witness_or_constant),
613 .outputs = transform::map(arg.outputs, to_witness),
614 });
615 af.original_opcode_indices.aes128_constraints.push_back(opcode_index);
618 .inputs = transform::map(*arg.inputs, to_witness_or_constant),
619 .hash_values = transform::map(*arg.hash_values, to_witness_or_constant),
620 .result = transform::map(*arg.outputs, to_witness),
621 });
622 af.original_opcode_indices.sha256_compression.push_back(opcode_index);
625 .inputs = transform::map(arg.inputs, to_witness_or_constant),
626 .result = transform::map(*arg.outputs, to_witness),
627 });
628 af.original_opcode_indices.blake2s_constraints.push_back(opcode_index);
631 .inputs = transform::map(arg.inputs, to_witness_or_constant),
632 .result = transform::map(*arg.outputs, to_witness),
633 });
634 af.original_opcode_indices.blake3_constraints.push_back(opcode_index);
638 .hashed_message = transform::map(*arg.hashed_message, to_witness_from_input),
639 .signature = transform::map(*arg.signature, to_witness_from_input),
640 .pub_x_indices = transform::map(*arg.public_key_x, to_witness_from_input),
641 .pub_y_indices = transform::map(*arg.public_key_y, to_witness_from_input),
642 .predicate = parse_input(arg.predicate),
643 .result = to_witness(arg.output),
644 });
645 af.original_opcode_indices.ecdsa_k1_constraints.push_back(opcode_index);
649 .hashed_message = transform::map(*arg.hashed_message, to_witness_from_input),
650 .signature = transform::map(*arg.signature, to_witness_from_input),
651 .pub_x_indices = transform::map(*arg.public_key_x, to_witness_from_input),
652 .pub_y_indices = transform::map(*arg.public_key_y, to_witness_from_input),
653 .predicate = parse_input(arg.predicate),
654 .result = to_witness(arg.output),
655 });
656 af.original_opcode_indices.ecdsa_r1_constraints.push_back(opcode_index);
659 .points = transform::map(arg.points, to_witness_or_constant),
660 .scalars = transform::map(arg.scalars, to_witness_or_constant),
661 .predicate = parse_input(arg.predicate),
662 .out_point_x = to_witness((*arg.outputs)[0]),
663 .out_point_y = to_witness((*arg.outputs)[1]),
664 .out_point_is_infinite = to_witness((*arg.outputs)[2]),
665 });
666 af.original_opcode_indices.multi_scalar_mul_constraints.push_back(opcode_index);
668 af.ec_add_constraints.push_back(EcAdd{
669 .input1_x = parse_input((*arg.input1)[0]),
670 .input1_y = parse_input((*arg.input1)[1]),
671 .input1_infinite = parse_input((*arg.input1)[2]),
672 .input2_x = parse_input((*arg.input2)[0]),
673 .input2_y = parse_input((*arg.input2)[1]),
674 .input2_infinite = parse_input((*arg.input2)[2]),
675 .predicate = parse_input(arg.predicate),
676 .result_x = to_witness((*arg.outputs)[0]),
677 .result_y = to_witness((*arg.outputs)[1]),
678 .result_infinite = to_witness((*arg.outputs)[2]),
679 });
680 af.original_opcode_indices.ec_add_constraints.push_back(opcode_index);
682 af.keccak_permutations.push_back(Keccakf1600{
683 .state = transform::map(*arg.inputs, to_witness_or_constant),
684 .result = transform::map(*arg.outputs, to_witness),
685 });
686 af.original_opcode_indices.keccak_permutations.push_back(opcode_index);
688 auto predicate = parse_input(arg.predicate);
689 if (predicate.is_constant && predicate.value.is_zero()) {
690 // No constraint if the recursion is disabled
691 return;
692 }
693 auto c = RecursionConstraint{
694 .key = transform::map(arg.verification_key, to_witness_from_input),
695 .proof = transform::map(arg.proof, to_witness_from_input),
696 .public_inputs = transform::map(arg.public_inputs, to_witness_from_input),
697 .key_hash = get_witness_from_function_input(arg.key_hash),
698 .proof_type = arg.proof_type,
699 .predicate = predicate,
700 };
701
702 // Add the recursion constraint to the appropriate container based on proof type
703 switch (c.proof_type) {
704 case HONK_ZK:
705 case HONK:
706 case ROLLUP_HONK:
707 case ROOT_ROLLUP_HONK:
708 af.honk_recursion_constraints.push_back(c);
709 af.original_opcode_indices.honk_recursion_constraints.push_back(opcode_index);
710 break;
711 case OINK:
712 case HN:
713 case HN_TAIL:
714 case HN_FINAL:
715 af.hn_recursion_constraints.push_back(c);
716 af.original_opcode_indices.hn_recursion_constraints.push_back(opcode_index);
717 break;
718 case AVM:
719 af.avm_recursion_constraints.push_back(c);
720 af.original_opcode_indices.avm_recursion_constraints.push_back(opcode_index);
721 break;
722 case CHONK:
723 af.chonk_recursion_constraints.push_back(c);
724 af.original_opcode_indices.chonk_recursion_constraints.push_back(opcode_index);
725 break;
726 default:
728 "acir_format::handle_black_box_fun_call: Invalid PROOF_TYPE in RecursionConstraint.");
729 }
732 .state = transform::map(arg.inputs, to_witness_or_constant),
733 .result = transform::map(arg.outputs, to_witness),
734 });
735 af.original_opcode_indices.poseidon2_constraints.push_back(opcode_index);
736 } else {
737 bb::assert_failure("acir_format::handle_blackbox_func_call: Unrecognized BlackBoxFuncCall variant. An "
738 "error here means there was a serialization error.");
739 }
740 },
741 arg.value.value);
742}
743
745{
746 // Noir doesn't distinguish between ROM and RAM table. Therefore, we initialize every table as a ROM table, and
747 // then we make it a RAM table if there is at least one write operation
748 BlockConstraint block{
749 .init = {},
750 .trace = {},
751 .type = BlockType::ROM,
752 .calldata_id = CallDataType::None,
753 };
754
755 for (const auto& init : mem_init.init) {
756 block.init.push_back(init.value);
757 }
758
759 // Databus is only supported for Goblin, non Goblin builders will treat call_data and return_data as normal
760 // array.
762 uint32_t calldata_id = std::get<Acir::BlockType::CallData>(mem_init.block_type.value).value;
763 BB_ASSERT(calldata_id == 0 || calldata_id == 1, "acir_format::handle_memory_init: Unsupported calldata id");
764
765 block.type = BlockType::CallData;
766 block.calldata_id = calldata_id == 0 ? CallDataType::Primary : CallDataType::Secondary;
768 block.type = BlockType::ReturnData;
769 }
770
771 return block;
772}
773
775{
776 // Lambda to convert an Acir::Expression to a witness index
777 auto acir_expression_to_witness_or_constant = [&](const Acir::Expression& expr) {
778 // Noir gives us witnesses or constants for read/write operations. We use the following assertions to ensure
779 // that the data coming from Noir is in the correct form.
780 BB_ASSERT(expr.mul_terms.empty(), "MemoryOp should not have multiplication terms");
781 BB_ASSERT_LTE(expr.linear_combinations.size(), 1U, "MemoryOp should have at most one linear term");
782
783 const fr a_scaling = expr.linear_combinations.size() == 1
784 ? from_buffer_with_bound_checks(std::get<0>(expr.linear_combinations[0]))
785 : fr::zero();
786 const fr constant_term = from_buffer_with_bound_checks(expr.q_c);
787
788 bool is_witness = a_scaling == fr::one() && constant_term == fr::zero();
789 bool is_constant = a_scaling == fr::zero();
790 BB_ASSERT(is_witness || is_constant, "MemoryOp expression must be a witness or a constant");
791
793 .index = is_witness ? std::get<1>(expr.linear_combinations[0]).value : bb::stdlib::IS_CONSTANT,
794 .value = is_constant ? constant_term : fr::zero(),
795 .is_constant = is_constant,
796 };
797 };
798
799 // Lambda to determine whether a memory operation is a read or write operation
800 auto is_read_operation = [&](const Acir::Expression& expr) {
801 BB_ASSERT(expr.mul_terms.empty(), "MemoryOp expression should not have multiplication terms");
802 BB_ASSERT(expr.linear_combinations.empty(), "MemoryOp expression should not have linear terms");
803
804 const fr const_term = from_buffer_with_bound_checks(expr.q_c);
805
806 BB_ASSERT((const_term == fr::one()) || (const_term == fr::zero()),
807 "MemoryOp expression should be either zero or one");
808
809 // A read operation is given by a zero Expression
810 return const_term == fr::zero();
811 };
812
813 AccessType access_type = is_read_operation(mem_op.op.operation) ? AccessType::Read : AccessType::Write;
814 if (access_type == AccessType::Write) {
815 // We are not allowed to write on the databus
817 // Mark the table as a RAM table
818 block.type = BlockType::RAM;
819 }
820
821 // Update the ranges of the index using the array length
822 WitnessOrConstant<bb::fr> index = acir_expression_to_witness_or_constant(mem_op.op.index);
823 WitnessOrConstant<bb::fr> value = acir_expression_to_witness_or_constant(mem_op.op.value);
824
825 MemOp acir_mem_op = MemOp{ .access_type = access_type, .index = index, .value = value };
826 block.trace.push_back(acir_mem_op);
827}
828
830{
831 static constexpr size_t NUM_WIRES = 4; // Equal to the number of wires in the arithmetization
832
833 // If there are more than 4 distinct witnesses in the linear terms, then we need multiple arithmetic gates
834 if (linear_terms.size() > NUM_WIRES) {
835 return false;
836 }
837
838 if (arg.mul_terms.size() > 1) {
839 // If there is more than one multiplication gate, then we need multiple arithmetic gates
840 return false;
841 }
842
843 if (arg.mul_terms.size() == 1) {
844 // In this case we have two witnesses coming from the multiplication term plus the linear terms.
845 // We proceed as follows:
846 // 0. Start from the assumption that all witnesses (from linear terms and multiplication) are distinct
847 // 1. Check if the lhs and rhs witness in the multiplication are already contained in the linear terms
848 // 2. Check if the lhs witness and the rhs witness are equal
849 // 2.a If they are distinct, update the total number of witnesses to be added to wires according to result
850 // of the check at step 1: each distinct witness already in the linear terms subtracts one from the
851 // total
852 // 2.b If they are equal, update the total number of witnesses to be added to wires according to result of
853 // the check at step 1: if the witness is already in the linear terms, it removes one from the total
854
855 // Number of witnesses to be put in wires if the witnesses from the linear terms and the multiplication term are
856 // all different
857 size_t num_witnesses_to_be_put_in_wires = 2 + linear_terms.size();
858
859 uint32_t witness_idx_lhs = std::get<1>(arg.mul_terms[0]).value;
860 uint32_t witness_idx_rhs = std::get<2>(arg.mul_terms[0]).value;
861
862 bool lhs_is_distinct_from_linear_terms = !linear_terms.contains(witness_idx_lhs);
863 bool rhs_is_distinct_from_linear_terms = !linear_terms.contains(witness_idx_rhs);
864
865 if (witness_idx_lhs != witness_idx_rhs) {
866 num_witnesses_to_be_put_in_wires -= lhs_is_distinct_from_linear_terms ? 0U : 1U;
867 num_witnesses_to_be_put_in_wires -= rhs_is_distinct_from_linear_terms ? 0U : 1U;
868 } else {
869 num_witnesses_to_be_put_in_wires -= lhs_is_distinct_from_linear_terms ? 0U : 1U;
870 }
871
872 return num_witnesses_to_be_put_in_wires <= NUM_WIRES;
873 }
874
875 return linear_terms.size() <= NUM_WIRES;
876}
877
879{
880 std::map<uint32_t, bb::fr> linear_terms;
881 for (const auto& linear_term : expr.linear_combinations) {
882 fr selector_value = from_buffer_with_bound_checks(std::get<0>(linear_term));
883 uint32_t witness_idx = std::get<1>(linear_term).value;
884 if (linear_terms.contains(witness_idx)) {
885 linear_terms[witness_idx] += selector_value; // Accumulate coefficients for duplicate witnesses
886 } else {
887 linear_terms[witness_idx] = selector_value;
888 }
889 }
890 return linear_terms;
891}
892
893} // namespace acir_format
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:113
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:158
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:143
Constraint representing a polynomial of degree 1 or 2 that does not fit into a standard UltraHonk ari...
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:135
void add_memory_op_to_block_constraint(Acir::Opcode::MemoryOp const &mem_op, BlockConstraint &block)
Process memory operation, either read or write, and update the BlockConstraint type accordingly.
AcirFormat circuit_serde_to_acir_format(Acir::Circuit const &circuit)
Convert an Acir::Circuit into an AcirFormat by processing all the opcodes.
WitnessOrConstant< bb::fr > parse_input(const Acir::FunctionInput &input)
Parse an Acir::FunctionInput (which can either be a witness or a constant) into a WitnessOrConstant.
void update_max_witness_index_from_opcode(Acir::Opcode const &opcode, AcirFormat &af)
Update the max witness index by processing all the witness indices contained in the Acir::Opcode.
uint32_t get_witness_from_function_input(const Acir::FunctionInput &input)
Extract the witness index from an Acir::FunctionInput representing a witness.
void update_max_witness_index_from_expression(Acir::Expression const &expr, AcirFormat &af)
Update max_witness_index by processing all witnesses in an Acir::Expression.
WitnessVector witness_buf_to_witness_vector(std::vector< uint8_t > &&buf)
Convert a buffer representing a witness vector into Barretenberg's internal WitnessVector format.
std::vector< mul_quad_< fr > > split_into_mul_quad_gates(Acir::Expression const &arg, std::map< uint32_t, bb::fr > &linear_terms)
========= ACIR OPCODE HANDLERS ========= ///
void update_max_witness_index(const uint32_t witness_idx, AcirFormat &af)
Update the max_witness_index.
WitnessVector witness_map_to_witness_vector(Witnesses::WitnessMap const &witness_map)
Convert from the ACIR-native WitnessMap format to Barretenberg's internal WitnessVector format.
T deserialize_msgpack_compact(std::vector< uint8_t > &&buf, std::function< T(msgpack::object const &)> decode_msgpack)
========= BYTES TO BARRETENBERG'S REPRESENTATION ========= ///
std::vector< bb::fr > WitnessVector
bool is_single_arithmetic_gate(Acir::Expression const &arg, const std::map< uint32_t, bb::fr > &linear_terms)
Given an Acir::Expression and its processed linear terms, determine whether it can be represented by ...
BlockConstraint memory_init_to_block_constraint(Acir::Opcode::MemoryInit const &mem_init)
========= MEMORY OPERATIONS ========== ///
AcirFormat circuit_buf_to_acir_format(std::vector< uint8_t > &&buf)
Convert a buffer representing a circuit into Barretenberg's internal AcirFormat representation.
bb::fr from_buffer_with_bound_checks(const std::vector< uint8_t > &buffer)
========= HELPERS ========= ///
void assert_zero_to_quad_constraints(Acir::Opcode::AssertZero const &arg, AcirFormat &af, size_t opcode_index)
Single entrypoint for processing arithmetic (AssertZero) opcodes.
void add_blackbox_func_call_to_acir_format(Acir::Opcode::BlackBoxFuncCall const &arg, AcirFormat &af, size_t opcode_index)
std::map< uint32_t, bb::fr > process_linear_terms(Acir::Expression const &expr)
========= ACIR OPCODE HANDLERS ========= ///
Cont< OutElem > map(Cont< InElem, Args... > const &in, F &&op)
Definition map.hpp:15
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FrParams > fr
Definition fr.hpp:174
void assert_failure(std::string const &err)
Definition assert.cpp:11
C join(std::initializer_list< C > to_join)
Definition container.hpp:26
@ SECP256K1
Definition types.hpp:10
@ SECP256R1
Definition types.hpp:10
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
std::variant< AES128Encrypt, AND, XOR, RANGE, Blake2s, Blake3, EcdsaSecp256k1, EcdsaSecp256r1, MultiScalarMul, EmbeddedCurveAdd, Keccakf1600, RecursiveAggregation, Poseidon2Permutation, Sha256Compression > value
Definition acir.hpp:2528
std::variant< Memory, CallData, ReturnData > value
Definition acir.hpp:2746
Acir::PublicInputs return_values
Definition acir.hpp:3501
std::vector< Acir::Opcode > opcodes
Definition acir.hpp:3498
Acir::PublicInputs public_parameters
Definition acir.hpp:3500
std::vector< std::tuple< std::vector< uint8_t >, Acir::Witness > > linear_combinations
Definition acir.hpp:2796
std::vector< uint8_t > q_c
Definition acir.hpp:2797
std::vector< std::tuple< std::vector< uint8_t >, Acir::Witness, Acir::Witness > > mul_terms
Definition acir.hpp:2795
std::variant< Constant, Witness > value
Definition acir.hpp:2069
Acir::Expression value
Definition acir.hpp:3021
Acir::Expression operation
Definition acir.hpp:3019
Acir::Expression index
Definition acir.hpp:3020
Acir::Expression value
Definition acir.hpp:3047
Acir::BlackBoxFuncCall value
Definition acir.hpp:3063
std::vector< Acir::Witness > init
Definition acir.hpp:3103
Acir::BlockType block_type
Definition acir.hpp:3104
std::variant< AssertZero, BlackBoxFuncCall, MemoryOp, MemoryInit, BrilligCall, Call > value
Definition acir.hpp:3185
std::vector< Acir::Circuit > functions
Definition acir.hpp:3557
std::vector< Acir::Circuit > functions
Definition acir.hpp:3580
std::vector< Acir::Witness > value
Definition acir.hpp:3480
std::map< Witnesses::Witness, std::vector< uint8_t > > value
std::vector< WitnessOrConstant< bb::fr > > inputs
Barretenberg's representation of ACIR constraints.
std::vector< MultiScalarMul > multi_scalar_mul_constraints
std::vector< Blake2sConstraint > blake2s_constraints
std::vector< Sha256Compression > sha256_compression
std::vector< Poseidon2Constraint > poseidon2_constraints
std::vector< LogicConstraint > logic_constraints
std::vector< EcAdd > ec_add_constraints
std::vector< QuadConstraint > quad_constraints
std::vector< Keccakf1600 > keccak_permutations
std::vector< RecursionConstraint > honk_recursion_constraints
std::vector< Blake3Constraint > blake3_constraints
std::vector< EcdsaConstraint > ecdsa_r1_constraints
std::vector< RangeConstraint > range_constraints
std::vector< BigQuadConstraint > big_quad_constraints
std::vector< AES128Constraint > aes128_constraints
AcirFormatOriginalOpcodeIndices original_opcode_indices
std::vector< BlockConstraint > block_constraints
std::vector< EcdsaConstraint > ecdsa_k1_constraints
std::vector< RecursionConstraint > hn_recursion_constraints
std::vector< uint32_t > public_inputs
std::vector< RecursionConstraint > avm_recursion_constraints
std::vector< RecursionConstraint > chonk_recursion_constraints
std::vector< std::vector< size_t > > block_constraints
std::vector< WitnessOrConstant< bb::fr > > inputs
std::vector< WitnessOrConstant< bb::fr > > inputs
Struct holding the data required to add memory constraints to a circuit.
std::vector< uint32_t > init
Constraints for addition of two points on the Grumpkin curve.
WitnessOrConstant< bb::fr > input1_x
std::array< WitnessOrConstant< bb::fr >, 25 > state
Logic constraint representation in ACIR format.
WitnessOrConstant< fr > a
Memory operation. Index and value store the index of the memory location, and value is the value to b...
std::vector< WitnessOrConstant< bb::fr > > points
std::vector< WitnessOrConstant< bb::fr > > state
RecursionConstraint struct contains information required to recursively verify a proof.
std::array< WitnessOrConstant< bb::fr >, 16 > inputs
static constexpr field one()
static field random_element(numeric::RNG *engine=nullptr) noexcept
static field serialize_from_buffer(const uint8_t *buffer)
static constexpr field zero()