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
31
33{
34 WitnessOrConstant<bb::fr> result = std::visit(
35 [&](auto&& e) {
36 using T = std::decay_t<decltype(e)>;
39 .index = e.value.value,
40 .value = bb::fr::zero(),
41 .is_constant = false,
42 };
45 .index = bb::stdlib::IS_CONSTANT,
46 .value = fr::serialize_from_buffer(&e.value[0]),
47 .is_constant = true,
48 };
49 } else {
50 bb::assert_failure("acir_format::parse_input: unrecognized Acir::FunctionInput variant. An error here "
51 "means there was a serialization error.");
52 }
53 },
54 input.value);
55 return result;
56}
57
59{
61 "acir_format::get_witness_from_function_input: input must be a Witness variant. An error here means "
62 "there was a serialization error.");
63
64 return std::get<Acir::FunctionInput::Witness>(input.value).value.value;
65}
66
67void update_max_witness_index(uint32_t witness_idx, AcirFormat& af)
68{
69 if (witness_idx != stdlib::IS_CONSTANT) {
70 af.max_witness_index = std::max(af.max_witness_index, witness_idx);
71 }
72}
73
75{
76 // Process multiplication terms: each term has two witness indices
77 for (const auto& mul_term : expr.mul_terms) {
80 }
81
82 // Process linear combinations: each term has one witness index
83 for (const auto& linear_term : expr.linear_combinations) {
85 }
86}
87
89{
90 auto update_max_witness_index_from_function_input = [&](const Acir::FunctionInput& input) {
93 }
94 };
95
96 auto update_max_witness_index_from_witness = [&](const Acir::Witness& witness) {
97 update_max_witness_index(witness.value, af);
98 };
99
100 std::visit(
101 [&](auto&& arg) {
102 using T = std::decay_t<decltype(arg)>;
106 std::visit(
107 [&](auto&& bb_arg) {
108 using BBT = std::decay_t<decltype(bb_arg)>;
111 update_max_witness_index_from_function_input(bb_arg.lhs);
112 update_max_witness_index_from_function_input(bb_arg.rhs);
113 update_max_witness_index_from_witness(bb_arg.output);
115 update_max_witness_index_from_function_input(bb_arg.input);
117 for (const auto& input : bb_arg.inputs) {
118 update_max_witness_index_from_function_input(input);
119 }
120 for (const auto& input : *bb_arg.iv) {
121 update_max_witness_index_from_function_input(input);
122 }
123 for (const auto& input : *bb_arg.key) {
124 update_max_witness_index_from_function_input(input);
125 }
126 for (const auto& output : bb_arg.outputs) {
127 update_max_witness_index_from_witness(output);
128 }
130 for (const auto& input : *bb_arg.inputs) {
131 update_max_witness_index_from_function_input(input);
132 }
133 for (const auto& input : *bb_arg.hash_values) {
134 update_max_witness_index_from_function_input(input);
135 }
136 for (const auto& output : *bb_arg.outputs) {
137 update_max_witness_index_from_witness(output);
138 }
141 for (const auto& input : bb_arg.inputs) {
142 update_max_witness_index_from_function_input(input);
143 }
144 for (const auto& output : *bb_arg.outputs) {
145 update_max_witness_index_from_witness(output);
146 }
149 for (const auto& input : *bb_arg.public_key_x) {
150 update_max_witness_index_from_function_input(input);
151 }
152 for (const auto& input : *bb_arg.public_key_y) {
153 update_max_witness_index_from_function_input(input);
154 }
155 for (const auto& input : *bb_arg.signature) {
156 update_max_witness_index_from_function_input(input);
157 }
158 for (const auto& input : *bb_arg.hashed_message) {
159 update_max_witness_index_from_function_input(input);
160 }
161 update_max_witness_index_from_function_input(bb_arg.predicate);
162 update_max_witness_index_from_witness(bb_arg.output);
164 for (const auto& input : bb_arg.points) {
165 update_max_witness_index_from_function_input(input);
166 }
167 for (const auto& input : bb_arg.scalars) {
168 update_max_witness_index_from_function_input(input);
169 }
170 update_max_witness_index_from_function_input(bb_arg.predicate);
171 for (const auto& output : *bb_arg.outputs) {
172 update_max_witness_index_from_witness(output);
173 }
175 for (const auto& input : *bb_arg.input1) {
176 update_max_witness_index_from_function_input(input);
177 }
178 for (const auto& input : *bb_arg.input2) {
179 update_max_witness_index_from_function_input(input);
180 }
181 update_max_witness_index_from_function_input(bb_arg.predicate);
182 for (const auto& output : *bb_arg.outputs) {
183 update_max_witness_index_from_witness(output);
184 }
186 for (const auto& input : *bb_arg.inputs) {
187 update_max_witness_index_from_function_input(input);
188 }
189 for (const auto& output : *bb_arg.outputs) {
190 update_max_witness_index_from_witness(output);
191 }
193 for (const auto& input : bb_arg.verification_key) {
194 update_max_witness_index_from_function_input(input);
195 }
196 for (const auto& input : bb_arg.proof) {
197 update_max_witness_index_from_function_input(input);
198 }
199 for (const auto& input : bb_arg.public_inputs) {
200 update_max_witness_index_from_function_input(input);
201 }
202 update_max_witness_index_from_function_input(bb_arg.key_hash);
203 update_max_witness_index_from_function_input(bb_arg.predicate);
205 for (const auto& input : bb_arg.inputs) {
206 update_max_witness_index_from_function_input(input);
207 }
208 for (const auto& output : bb_arg.outputs) {
209 update_max_witness_index_from_witness(output);
210 }
211 }
212 },
213 arg.value.value);
214 } else if constexpr (std::is_same_v<T, Acir::Opcode::MemoryInit>) {
215 for (const auto& init : arg.init) {
216 update_max_witness_index_from_witness(init);
217 }
218 } else if constexpr (std::is_same_v<T, Acir::Opcode::MemoryOp>) {
221 update_max_witness_index_from_expression(arg.op.operation, af);
223 // Process inputs
224 for (const auto& input : arg.inputs) {
225 std::visit(
226 [&](auto&& e) {
227 using IT = std::decay_t<decltype(e)>;
231 for (const auto& expr : e.value) {
233 }
234 }
235 // MemoryArray contains a BlockId, no direct witnesses to track
236 },
237 input.value);
238 }
239 // Process outputs
240 for (const auto& output : arg.outputs) {
241 std::visit(
242 [&](auto&& e) {
243 using OT = std::decay_t<decltype(e)>;
245 update_max_witness_index_from_witness(e.value);
247 for (const auto& witness : e.value) {
248 update_max_witness_index_from_witness(witness);
249 }
250 }
251 },
252 output.value);
253 }
254 // Process optional predicate
255 if (arg.predicate.has_value()) {
256 update_max_witness_index_from_expression(arg.predicate.value(), af);
257 }
258 } else {
259 bb::assert_failure("acir_format::update_max_witness_index_from_opcode: Unrecognized opcode.");
260 }
261 },
262 opcode.value);
263}
264
266
267template <typename T>
268T deserialize_any_format(std::vector<uint8_t>&& buf,
269 std::function<T(msgpack::object const&)> decode_msgpack,
270 std::function<T(std::vector<uint8_t>)> decode_bincode)
271{
272 // We can't rely on exceptions to try to deserialize binpack, falling back to
273 // msgpack if it fails, because exceptions are (or were) not supported in Wasm
274 // and they are turned off in arch.cmake.
275 //
276 // For now our other option is to check if the data is valid msgpack,
277 // which slows things down, but we can't tell if the first byte of
278 // the data accidentally matches one of our format values.
279 //
280 // Unfortunately this doesn't seem to work either: `msgpack::parse`
281 // returns true for a `bincode` encoded program, and we have to check
282 // whether the value parsed is plausible.
283
284 if (!buf.empty()) {
285 // Once we remove support for legacy bincode format, we should expect to always
286 // have a format marker corresponding to acir::serialization::Format::Msgpack,
287 // but until then a match could be pure coincidence.
288 const uint8_t FORMAT_MSGPACK = 2;
289 const uint8_t FORMAT_MSGPACK_COMPACT = 3;
290 uint8_t format = buf[0];
291 if (format == FORMAT_MSGPACK || format == FORMAT_MSGPACK_COMPACT) {
292 // Skip the format marker to get the data.
293 const char* buffer = &reinterpret_cast<const char*>(buf.data())[1];
294 size_t size = buf.size() - 1;
295 msgpack::null_visitor probe;
296 if (msgpack::parse(buffer, size, probe)) {
297 auto oh = msgpack::unpack(buffer, size);
298 // This has to be on a separate line, see
299 // https://github.com/msgpack/msgpack-c/issues/695#issuecomment-393035172
300 auto o = oh.get();
301 // In experiments bincode data was parsed as 0, so a successful parse is not a guarnatee that it's
302 // msgpack. All the top level formats we look for are MAP or ARRAY types (depending on the format).
303 if (o.type == msgpack::type::MAP || o.type == msgpack::type::ARRAY) {
304 return decode_msgpack(o);
305 }
306 }
307 }
308 // `buf[0] == 1` would indicate bincode starting with a format byte,
309 // but if it's a coincidence and it fails to parse then we can't recover
310 // from it, so let's just acknowledge that for now we don't want to
311 // exercise this code path and treat the whole data as bincode.
312 }
313 return decode_bincode(std::move(buf));
314}
315
317{
318 AcirFormat af;
319 af.num_acir_opcodes = static_cast<uint32_t>(circuit.opcodes.size());
320 af.public_inputs = join({
322 [&](auto e) {
323 update_max_witness_index(e.value, af);
324 return e.value;
325 }),
327 [&](auto e) {
328 update_max_witness_index(e.value, af);
329 return e.value;
330 }),
331 });
332 // Map to a pair of: BlockConstraint, and list of opcodes associated with that BlockConstraint
333 // Block constraints are built as we process the opcodes, so we store them in this map and we add them to the
334 // AcirFormat struct at the end
335 // NOTE: We want to deterministically visit this map, so unordered_map should not be used.
337
338 for (size_t i = 0; i < circuit.opcodes.size(); ++i) {
339 const auto& gate = circuit.opcodes[i];
341 std::visit(
342 [&](auto&& arg) {
343 using T = std::decay_t<decltype(arg)>;
348 } else if constexpr (std::is_same_v<T, Acir::Opcode::MemoryInit>) {
349 auto block = memory_init_to_block_constraint(arg);
350 uint32_t block_id = arg.block_id.value;
351 block_id_to_block_constraint[block_id] = { block, /*opcode_indices=*/{ i } };
352 } else if constexpr (std::is_same_v<T, Acir::Opcode::MemoryOp>) {
353 auto block = block_id_to_block_constraint.find(arg.block_id.value);
354 if (block == block_id_to_block_constraint.end()) {
355 bb::assert_failure("acir_format::circuit_serder_to_acir_format: unitialized MemoryOp.");
356 }
357 add_memory_op_to_block_constraint(arg, block->second.first);
358 block->second.second.push_back(i);
360 // This is a no-op in barretenberg
361 } else {
362 bb::assert_failure("acir_format::circuit_serde_to_acir_format: Unrecognized Acir Opcode. An error "
363 "here means there was a serialization error.");
364 }
365 },
366 gate.value);
367 }
368 // Add the block constraints to the AcirFormat struct
369 for (const auto& [_, block] : block_id_to_block_constraint) {
370 af.block_constraints.push_back(block.first);
371 af.original_opcode_indices.block_constraints.push_back(block.second);
372 }
373
374 return af;
375}
376
378{
379 // We need to deserialize into Acir::Program first because the buffer returned by Noir has this structure
380 auto program = deserialize_any_format<Acir::Program>(
381 std::move(buf),
382 [](auto o) -> Acir::Program {
383 Acir::Program program;
384 try {
385 // Deserialize into a partial structure that ignores the Brillig parts,
386 // so that new opcodes can be added without breaking Barretenberg.
387 Acir::ProgramWithoutBrillig program_wob;
388 o.convert(program_wob);
389 program.functions = program_wob.functions;
390 } catch (const msgpack::type_error&) {
391 std::cerr << o << std::endl;
393 "acir_format::circuit_buf_to_acir_format: failed to convert msgpack data to Program");
394 }
395 return program;
396 },
398 BB_ASSERT_EQ(program.functions.size(), 1U, "circuit_buf_to_acir_format: expected single function in ACIR program");
399
400 return circuit_serde_to_acir_format(program.functions[0]);
401}
402
404{
405 // We need to deserialize into WitnessStack first because the buffer returned by Noir has this structure
406 auto witness_stack = deserialize_any_format<Witnesses::WitnessStack>(
407 std::move(buf),
408 [](auto o) {
409 Witnesses::WitnessStack witness_stack;
410 try {
411 o.convert(witness_stack);
412 } catch (const msgpack::type_error&) {
413 std::cerr << o << std::endl;
415 "acir_format::witness_buf_to_witness_vector: failed to convert msgpack data to WitnessStack");
416 }
417 return witness_stack;
418 },
420 BB_ASSERT_EQ(witness_stack.stack.size(),
421 1U,
422 "acir_format::witness_buf_to_witness_vector: expected single WitnessMap in WitnessStack");
423
424 return witness_map_to_witness_vector(witness_stack.stack[0].witness);
425}
426
428{
429 // Note that the WitnessMap is in increasing order of witness indices because the comparator for the Acir::Witness
430 // is defined in terms of the witness index.
431
432 WitnessVector witness_vector;
433 for (size_t index = 0; const auto& e : witness_map.value) {
434 // ACIR uses a sparse format for WitnessMap where unused witness indices may be left unassigned.
435 // To ensure that witnesses sit at the correct indices in the `WitnessVector`, we fill any indices
436 // which do not exist within the `WitnessMap` with the dummy value of zero.
437 while (index < e.first.value) {
438 witness_vector.emplace_back(0);
439 index++;
440 }
441 witness_vector.emplace_back(fr::serialize_from_buffer(&e.second[0]));
442 index++;
443 }
444
445 return witness_vector;
446}
447
449
451 std::map<uint32_t, bb::fr>& linear_terms)
452{
453 // Lambda to add next linear term from linear_terms to the mul_quad_ gate and erase it from linear_terms
454 auto add_linear_term_and_erase = [](uint32_t& idx, fr& scaling, std::map<uint32_t, fr>& linear_terms) {
456 idx, bb::stdlib::IS_CONSTANT, "Attempting to override a non-constant witness index in mul_quad_ gate");
457 idx = linear_terms.begin()->first;
458 scaling += linear_terms.begin()->second;
459 linear_terms.erase(idx);
460 };
461
463 // We cannot precompute the exact number of gates that will result from the expression. Therefore, we reserve the
464 // maximum number of gates that could ever be needed: one per multiplication term plus one per linear term. The real
465 // number of gates will in general be lower than this.
466 result.reserve(arg.mul_terms.size() + linear_terms.size());
467
468 // Step 1. Add multiplication terms and linear terms with the same witness index
469 for (const auto& mul_term : arg.mul_terms) {
470 result.emplace_back(mul_quad_<fr>{
471 .a = std::get<1>(mul_term).value,
472 .b = std::get<2>(mul_term).value,
473 .c = bb::stdlib::IS_CONSTANT,
474 .d = bb::stdlib::IS_CONSTANT,
475 .mul_scaling = fr::serialize_from_buffer(&(std::get<0>(mul_term)[0])),
476 .a_scaling = fr::zero(),
477 .b_scaling = fr::zero(),
478 .c_scaling = fr::zero(),
479 .d_scaling = fr::zero(),
480 .const_scaling = fr::zero(),
481 });
482
483 // Add linear terms corresponding to the witnesses involved in the multiplication term
484 auto& mul_quad = result.back();
485 if (linear_terms.contains(mul_quad.a)) {
486 mul_quad.a_scaling += linear_terms.at(mul_quad.a);
487 linear_terms.erase(mul_quad.a); // Remove it as the linear term for a has been processed
488 }
489 if (linear_terms.contains(mul_quad.b)) {
490 // Note that we enter here only if b is different from a
491 mul_quad.b_scaling += linear_terms.at(mul_quad.b);
492 linear_terms.erase(mul_quad.b); // Remove it as the linear term for b has been processed
493 }
494 }
495
496 // Step 2. Add linear terms to existing gates
497 bool is_first_gate = true;
498 for (auto& mul_quad : result) {
499 if (!linear_terms.empty()) {
500 add_linear_term_and_erase(mul_quad.c, mul_quad.c_scaling, linear_terms);
501 }
502
503 if (is_first_gate) {
504 // First gate contains the constant term and uses all four wires
505 mul_quad.const_scaling = fr::serialize_from_buffer(&arg.q_c[0]);
506 if (!linear_terms.empty()) {
507 add_linear_term_and_erase(mul_quad.d, mul_quad.d_scaling, linear_terms);
508 }
509 is_first_gate = false;
510 }
511 }
512
513 // Step 3. Add remaining linear terms
514 while (!linear_terms.empty()) {
515 // We need to create new mul_quad_ gates to accomodate the remaining linear terms
516 mul_quad_<fr> mul_quad = {
517 .a = bb::stdlib::IS_CONSTANT,
518 .b = bb::stdlib::IS_CONSTANT,
519 .c = bb::stdlib::IS_CONSTANT,
520 .d = bb::stdlib::IS_CONSTANT,
521 .mul_scaling = fr::zero(),
522 .a_scaling = fr::zero(),
523 .b_scaling = fr::zero(),
524 .c_scaling = fr::zero(),
525 .d_scaling = fr::zero(),
526 .const_scaling = fr::zero(),
527 };
528 if (!linear_terms.empty()) {
529 add_linear_term_and_erase(mul_quad.a, mul_quad.a_scaling, linear_terms);
530 }
531 if (!linear_terms.empty()) {
532 add_linear_term_and_erase(mul_quad.b, mul_quad.b_scaling, linear_terms);
533 }
534 if (!linear_terms.empty()) {
535 add_linear_term_and_erase(mul_quad.c, mul_quad.c_scaling, linear_terms);
536 }
537 if (is_first_gate) {
538 // First gate contains the constant term and uses all four wires
539 mul_quad.const_scaling = fr::serialize_from_buffer(&arg.q_c[0]);
540 if (!linear_terms.empty()) {
541 add_linear_term_and_erase(mul_quad.d, mul_quad.d_scaling, linear_terms);
542 }
543 is_first_gate = false;
544 }
545
546 result.emplace_back(mul_quad);
547 }
548
549 BB_ASSERT(!result.empty(), "split_into_mul_quad_gates: resulted in zero gates.");
550 result.shrink_to_fit();
551
552 return result;
553}
554
556{
557 // Lambda to detect zero gates
558 auto is_zero_gate = [](const mul_quad_<fr>& gate) {
559 return ((gate.mul_scaling == fr(0)) && (gate.a_scaling == fr(0)) && (gate.b_scaling == fr(0)) &&
560 (gate.c_scaling == fr(0)) && (gate.d_scaling == fr(0)) && (gate.const_scaling == fr(0)));
561 };
562
563 auto linear_terms = process_linear_terms(arg.value);
564 bool is_single_gate = is_single_arithmetic_gate(arg.value, linear_terms);
565 std::vector<mul_quad_<fr>> mul_quads = split_into_mul_quad_gates(arg.value, linear_terms);
566
567 if (is_single_gate) {
568 BB_ASSERT_EQ(mul_quads.size(), 1U, "acir_format::handle_arithmetic: expected a single gate.");
569 auto mul_quad = mul_quads[0];
570
571 af.quad_constraints.push_back(mul_quad);
572 af.original_opcode_indices.quad_constraints.push_back(opcode_index);
573 } else {
574 BB_ASSERT_GT(mul_quads.size(), 1U, "acir_format::handle_arithmetic: expected multiple gates but found one.");
575 af.big_quad_constraints.push_back(mul_quads);
576 af.original_opcode_indices.big_quad_constraints.push_back(opcode_index);
577 }
578
579 for (auto const& mul_quad : mul_quads) {
580 BB_ASSERT(!is_zero_gate(mul_quad), "acir_format::handle_arithmetic: produced an arithmetic zero gate.");
581 }
582}
583
585 AcirFormat& af,
586 size_t opcode_index)
587{
588 auto to_witness_or_constant = [&](auto& e) { return parse_input(e); };
589 auto to_witness = [&](auto& e) { return e.value; };
590 auto to_witness_from_input = [&](auto& e) { return get_witness_from_function_input(e); };
591
592 std::visit(
593 [&](auto&& arg) {
594 using T = std::decay_t<decltype(arg)>;
597 .a = parse_input(arg.lhs),
598 .b = parse_input(arg.rhs),
599 .result = to_witness(arg.output),
600 .num_bits = arg.num_bits,
601 .is_xor_gate = false,
602 });
603 af.original_opcode_indices.logic_constraints.push_back(opcode_index);
606 .a = parse_input(arg.lhs),
607 .b = parse_input(arg.rhs),
608 .result = to_witness(arg.output),
609 .num_bits = arg.num_bits,
610 .is_xor_gate = true,
611 });
612 af.original_opcode_indices.logic_constraints.push_back(opcode_index);
616 .num_bits = arg.num_bits,
617 });
618 af.original_opcode_indices.range_constraints.push_back(opcode_index);
621 .inputs = transform::map(arg.inputs, to_witness_or_constant),
622 .iv = transform::map(*arg.iv, to_witness_or_constant),
623 .key = transform::map(*arg.key, to_witness_or_constant),
624 .outputs = transform::map(arg.outputs, to_witness),
625 });
626 af.original_opcode_indices.aes128_constraints.push_back(opcode_index);
629 .inputs = transform::map(*arg.inputs, to_witness_or_constant),
630 .hash_values = transform::map(*arg.hash_values, to_witness_or_constant),
631 .result = transform::map(*arg.outputs, to_witness),
632 });
633 af.original_opcode_indices.sha256_compression.push_back(opcode_index);
636 .inputs = transform::map(arg.inputs,
637 [&](auto& e) {
638 return Blake2sInput{
639 .blackbox_input = parse_input(e),
640 .num_bits = 8,
641 };
642 }),
643 .result = transform::map(*arg.outputs, to_witness),
644 });
645 af.original_opcode_indices.blake2s_constraints.push_back(opcode_index);
649 arg.inputs,
650 [&](auto& e) { return Blake3Input{ .blackbox_input = parse_input(e), .num_bits = 8 }; }),
651 .result = transform::map(*arg.outputs, to_witness),
652 });
653 af.original_opcode_indices.blake3_constraints.push_back(opcode_index);
655 af.ecdsa_k1_constraints.push_back(EcdsaConstraint{
657 .hashed_message = transform::map(*arg.hashed_message, to_witness_from_input),
658 .signature = transform::map(*arg.signature, to_witness_from_input),
659 .pub_x_indices = transform::map(*arg.public_key_x, to_witness_from_input),
660 .pub_y_indices = transform::map(*arg.public_key_y, to_witness_from_input),
661 .predicate = parse_input(arg.predicate),
662 .result = to_witness(arg.output),
663 });
664 af.original_opcode_indices.ecdsa_k1_constraints.push_back(opcode_index);
666 af.ecdsa_r1_constraints.push_back(EcdsaConstraint{
668 .hashed_message = transform::map(*arg.hashed_message, to_witness_from_input),
669 .signature = transform::map(*arg.signature, to_witness_from_input),
670 .pub_x_indices = transform::map(*arg.public_key_x, to_witness_from_input),
671 .pub_y_indices = transform::map(*arg.public_key_y, to_witness_from_input),
672 .predicate = parse_input(arg.predicate),
673 .result = to_witness(arg.output),
674 });
675 af.original_opcode_indices.ecdsa_r1_constraints.push_back(opcode_index);
677 af.multi_scalar_mul_constraints.push_back(MultiScalarMul{
678 .points = transform::map(arg.points, to_witness_or_constant),
679 .scalars = transform::map(arg.scalars, to_witness_or_constant),
680 .predicate = parse_input(arg.predicate),
681 .out_point_x = to_witness((*arg.outputs)[0]),
682 .out_point_y = to_witness((*arg.outputs)[1]),
683 .out_point_is_infinite = to_witness((*arg.outputs)[2]),
684 });
685 af.original_opcode_indices.multi_scalar_mul_constraints.push_back(opcode_index);
687 af.ec_add_constraints.push_back(EcAdd{
688 .input1_x = parse_input((*arg.input1)[0]),
689 .input1_y = parse_input((*arg.input1)[1]),
690 .input1_infinite = parse_input((*arg.input1)[2]),
691 .input2_x = parse_input((*arg.input2)[0]),
692 .input2_y = parse_input((*arg.input2)[1]),
693 .input2_infinite = parse_input((*arg.input2)[2]),
694 .predicate = parse_input(arg.predicate),
695 .result_x = to_witness((*arg.outputs)[0]),
696 .result_y = to_witness((*arg.outputs)[1]),
697 .result_infinite = to_witness((*arg.outputs)[2]),
698 });
699 af.original_opcode_indices.ec_add_constraints.push_back(opcode_index);
701 af.keccak_permutations.push_back(Keccakf1600{
702 .state = transform::map(*arg.inputs, to_witness_or_constant),
703 .result = transform::map(*arg.outputs, to_witness),
704 });
705 af.original_opcode_indices.keccak_permutations.push_back(opcode_index);
707 auto predicate = parse_input(arg.predicate);
708 if (predicate.is_constant && predicate.value.is_zero()) {
709 // No constraint if the recursion is disabled
710 return;
711 }
712 auto c = RecursionConstraint{
713 .key = transform::map(arg.verification_key, to_witness_from_input),
714 .proof = transform::map(arg.proof, to_witness_from_input),
715 .public_inputs = transform::map(arg.public_inputs, to_witness_from_input),
716 .key_hash = get_witness_from_function_input(arg.key_hash),
717 .proof_type = arg.proof_type,
718 .predicate = predicate,
719 };
720
721 // Add the recursion constraint to the appropriate container based on proof type
722 switch (c.proof_type) {
723 case HONK_ZK:
724 case HONK:
725 case ROLLUP_HONK:
726 case ROOT_ROLLUP_HONK:
727 af.honk_recursion_constraints.push_back(c);
728 af.original_opcode_indices.honk_recursion_constraints.push_back(opcode_index);
729 break;
730 case OINK:
731 case HN:
732 case HN_TAIL:
733 case HN_FINAL:
734 af.hn_recursion_constraints.push_back(c);
735 af.original_opcode_indices.hn_recursion_constraints.push_back(opcode_index);
736 break;
737 case AVM:
738 af.avm_recursion_constraints.push_back(c);
739 af.original_opcode_indices.avm_recursion_constraints.push_back(opcode_index);
740 break;
741 case CHONK:
742 af.chonk_recursion_constraints.push_back(c);
743 af.original_opcode_indices.chonk_recursion_constraints.push_back(opcode_index);
744 break;
745 default:
747 "acir_format::handle_black_box_fun_call: Invalid PROOF_TYPE in RecursionConstraint.");
748 }
750 af.poseidon2_constraints.push_back(Poseidon2Constraint{
751 .state = transform::map(arg.inputs, to_witness_or_constant),
752 .result = transform::map(arg.outputs, to_witness),
753 });
754 af.original_opcode_indices.poseidon2_constraints.push_back(opcode_index);
755 } else {
756 bb::assert_failure("acir_format::handle_blackbox_func_call: Unrecognized BlackBoxFuncCall variant. An "
757 "error here means there was a serialization error.");
758 }
759 },
760 arg.value.value);
761}
762
764{
765 // Noir doesn't distinguish between ROM and RAM table. Therefore, we initialize every table as a ROM table, and
766 // then we make it a RAM table if there is at least one write operation
767 BlockConstraint block{
768 .init = {},
769 .trace = {},
770 .type = BlockType::ROM,
771 .calldata_id = CallDataType::None,
772 };
773
774 for (const auto& init : mem_init.init) {
775 block.init.push_back(init.value);
776 }
777
778 // Databus is only supported for Goblin, non Goblin builders will treat call_data and return_data as normal
779 // array.
781 uint32_t calldata_id = std::get<Acir::BlockType::CallData>(mem_init.block_type.value).value;
782 BB_ASSERT(calldata_id == 0 || calldata_id == 1, "acir_format::handle_memory_init: Unsupported calldata id");
783
784 block.type = BlockType::CallData;
785 block.calldata_id = calldata_id == 0 ? CallDataType::Primary : CallDataType::Secondary;
787 block.type = BlockType::ReturnData;
788 }
789
790 return block;
791}
792
794{
795 // Lambda to convert an Acir::Expression to a witness index
796 auto acir_expression_to_witness_or_constant = [&](const Acir::Expression& expr) {
797 // Noir gives us witnesses or constants for read/write operations. We use the following assertions to ensure
798 // that the data coming from Noir is in the correct form.
799 BB_ASSERT(expr.mul_terms.empty(), "MemoryOp should not have multiplication terms");
800 BB_ASSERT_LTE(expr.linear_combinations.size(), 1U, "MemoryOp should have at most one linear term");
801
802 const fr a_scaling = expr.linear_combinations.size() == 1
803 ? fr::serialize_from_buffer(std::get<0>(expr.linear_combinations[0]).data())
804 : fr::zero();
805 const fr constant_term = fr::serialize_from_buffer(expr.q_c.data());
806
807 bool is_witness = a_scaling == fr::one() && constant_term == fr::zero();
808 bool is_constant = a_scaling == fr::zero();
809 BB_ASSERT(is_witness || is_constant, "MemoryOp expression must be a witness or a constant");
810
812 .index = is_witness ? std::get<1>(expr.linear_combinations[0]).value : bb::stdlib::IS_CONSTANT,
813 .value = is_constant ? constant_term : fr::zero(),
814 .is_constant = is_constant,
815 };
816 };
817
818 // Lambda to determine whether a memory operation is a read or write operation
819 auto is_read_operation = [&](const Acir::Expression& expr) {
820 BB_ASSERT(expr.mul_terms.empty(), "MemoryOp expression should not have multiplication terms");
821 BB_ASSERT(expr.linear_combinations.empty(), "MemoryOp expression should not have linear terms");
822
823 const fr const_term = fr::serialize_from_buffer(&expr.q_c[0]);
824
825 BB_ASSERT((const_term == fr::one()) || (const_term == fr::zero()),
826 "MemoryOp expression should be either zero or one");
827
828 // A read operation is given by a zero Expression
829 return const_term == fr::zero();
830 };
831
832 AccessType access_type = is_read_operation(mem_op.op.operation) ? AccessType::Read : AccessType::Write;
833 if (access_type == AccessType::Write) {
834 // We are not allowed to write on the databus
835 BB_ASSERT((block.type != BlockType::CallData) && (block.type != BlockType::ReturnData));
836 // Mark the table as a RAM table
837 block.type = BlockType::RAM;
838 }
839
840 // Update the ranges of the index using the array length
841 WitnessOrConstant<bb::fr> index = acir_expression_to_witness_or_constant(mem_op.op.index);
842 WitnessOrConstant<bb::fr> value = acir_expression_to_witness_or_constant(mem_op.op.value);
843
844 MemOp acir_mem_op = MemOp{ .access_type = access_type, .index = index, .value = value };
845 block.trace.push_back(acir_mem_op);
846}
847
849{
850 static constexpr size_t NUM_WIRES = 4; // Equal to the number of wires in the arithmetization
851
852 // If there are more than 4 distinct witnesses in the linear terms, then we need multiple arithmetic gates
853 if (linear_terms.size() > NUM_WIRES) {
854 return false;
855 }
856
857 if (arg.mul_terms.size() > 1) {
858 // If there is more than one multiplication gate, then we need multiple arithmetic gates
859 return false;
860 }
861
862 if (arg.mul_terms.size() == 1) {
863 // In this case we have two witnesses coming from the multiplication term plus the linear terms.
864 // We proceed as follows:
865 // 0. Start from the assumption that all witnesses (from linear terms and multiplication) are distinct
866 // 1. Check if the lhs and rhs witness in the multiplication are already contained in the linear terms
867 // 2. Check if the lhs witness and the rhs witness are equal
868 // 2.a If they are distinct, update the total number of witnesses to be added to wires according to result
869 // of the check at step 1: each distinct witness already in the linear terms subtracts one from the
870 // total
871 // 2.b If they are equal, update the total number of witnesses to be added to wires according to result of
872 // the check at step 1: if the witness is already in the linear terms, it removes one from the total
873
874 // Number of witnesses to be put in wires if the witnesses from the linear terms and the multiplication term are
875 // all different
876 size_t num_witnesses_to_be_put_in_wires = 2 + linear_terms.size();
877
878 uint32_t witness_idx_lhs = std::get<1>(arg.mul_terms[0]).value;
879 uint32_t witness_idx_rhs = std::get<2>(arg.mul_terms[0]).value;
880
881 bool lhs_is_distinct_from_linear_terms = !linear_terms.contains(witness_idx_lhs);
882 bool rhs_is_distinct_from_linear_terms = !linear_terms.contains(witness_idx_rhs);
883
884 if (witness_idx_lhs != witness_idx_rhs) {
885 num_witnesses_to_be_put_in_wires -= lhs_is_distinct_from_linear_terms ? 0U : 1U;
886 num_witnesses_to_be_put_in_wires -= rhs_is_distinct_from_linear_terms ? 0U : 1U;
887 } else {
888 num_witnesses_to_be_put_in_wires -= lhs_is_distinct_from_linear_terms ? 0U : 1U;
889 }
890
891 return num_witnesses_to_be_put_in_wires <= NUM_WIRES;
892 }
893
894 return linear_terms.size() <= NUM_WIRES;
895}
896
898{
899 std::map<uint32_t, bb::fr> linear_terms;
900 for (const auto& linear_term : expr.linear_combinations) {
901 fr selector_value = fr::serialize_from_buffer(&(std::get<0>(linear_term)[0]));
902 uint32_t witness_idx = std::get<1>(linear_term).value;
903 if (linear_terms.contains(witness_idx)) {
904 linear_terms[witness_idx] += selector_value; // Accumulate coefficients for duplicate witnesses
905 } else {
906 linear_terms[witness_idx] = selector_value;
907 }
908 }
909 return linear_terms;
910}
911
912} // namespace acir_format
#define BB_ASSERT(expression,...)
Definition assert.hpp:67
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:107
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:77
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:152
std::string format(Args... args)
Definition log.hpp:22
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
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.
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)
========= BYTES TO BARRETENBERG'S REPRESENTATION ========= ///
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.
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 ========= ///
WitnessVector witness_map_to_witness_vector(Witnesses::WitnessMap const &witness_map)
Convert from the ACIR-native WitnessMap format to Barretenberg's internal WitnessVector format.
uint32_t get_witness_from_function_input(Acir::FunctionInput input)
Extract the witness index from an Acir::FunctionInput representing a witness.
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 ...
WitnessOrConstant< bb::fr > parse_input(Acir::FunctionInput input)
========= HELPERS ========= ///
void update_max_witness_index(uint32_t witness_idx, AcirFormat &af)
Update the max_witness_index.
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.
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::variant< Memory, CallData, ReturnData > value
Definition acir.hpp:3920
Acir::PublicInputs return_values
Definition acir.hpp:5014
std::vector< Acir::Opcode > opcodes
Definition acir.hpp:5011
Acir::PublicInputs public_parameters
Definition acir.hpp:5013
std::vector< std::tuple< std::vector< uint8_t >, Acir::Witness > > linear_combinations
Definition acir.hpp:4006
std::vector< uint8_t > q_c
Definition acir.hpp:4007
std::vector< std::tuple< std::vector< uint8_t >, Acir::Witness, Acir::Witness > > mul_terms
Definition acir.hpp:4005
std::variant< Constant, Witness > value
Definition acir.hpp:2968
Acir::Expression value
Definition acir.hpp:4329
Acir::Expression operation
Definition acir.hpp:4327
Acir::Expression index
Definition acir.hpp:4328
Acir::Expression value
Definition acir.hpp:4365
std::vector< Acir::Witness > init
Definition acir.hpp:4438
Acir::BlockType block_type
Definition acir.hpp:4439
std::variant< AssertZero, BlackBoxFuncCall, MemoryOp, MemoryInit, BrilligCall, Call > value
Definition acir.hpp:4552
std::vector< Acir::Circuit > functions
Definition acir.hpp:5093
static Program bincodeDeserialize(std::vector< uint8_t >)
Definition acir.hpp:11515
std::vector< Acir::Circuit > functions
Definition acir.hpp:5125
std::vector< Acir::Witness > value
Definition acir.hpp:4989
std::map< Witnesses::Witness, std::vector< uint8_t > > value
static WitnessStack bincodeDeserialize(std::vector< uint8_t >)
std::vector< WitnessOrConstant< bb::fr > > inputs
std::vector< Blake2sConstraint > blake2s_constraints
std::vector< Sha256Compression > sha256_compression
std::vector< LogicConstraint > logic_constraints
std::vector< QuadConstraints > quad_constraints
std::vector< Blake3Constraint > blake3_constraints
std::vector< RangeConstraint > range_constraints
std::vector< AES128Constraint > aes128_constraints
AcirFormatOriginalOpcodeIndices original_opcode_indices
std::vector< BlockConstraint > block_constraints
std::vector< uint32_t > public_inputs
std::vector< std::vector< QuadConstraints > > big_quad_constraints
std::vector< std::vector< size_t > > block_constraints
std::vector< Blake2sInput > inputs
std::vector< Blake3Input > inputs
Struct holding the data required to add memory constraints to a circuit.
std::vector< uint32_t > init
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::array< WitnessOrConstant< bb::fr >, 16 > inputs
static constexpr field one()
static field serialize_from_buffer(const uint8_t *buffer)
static constexpr field zero()