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
32template <class... Ts> struct overloaded : Ts... {
33 using Ts::operator()...;
34};
35
36bb::fr from_buffer_with_bound_checks(const std::vector<uint8_t>& buffer)
37{
38 BB_ASSERT_EQ(buffer.size(), 32U, "acir_format::from_buffer_with_bound_checks: buffer size must be 32 bytes.");
39 return fr::serialize_from_buffer(buffer.data());
40}
41
43{
44 WitnessOrConstant<bb::fr> result = std::visit(overloaded{ [](const Acir::FunctionInput::Witness& e) {
46 .index = e.value.value,
47 .value = bb::fr::zero(),
48 .is_constant = false,
49 };
50 },
53 .index = bb::stdlib::IS_CONSTANT,
54 .value = from_buffer_with_bound_checks(e.value),
55 .is_constant = true,
56 };
57 } },
58 input.value);
59 return result;
60}
61
63{
65 "acir_format::get_witness_from_function_input: input must be a Witness variant. An error here means "
66 "there was a serialization error.");
67
68 return std::get<Acir::FunctionInput::Witness>(input.value).value.value;
69}
70
71void update_max_witness_index(const uint32_t witness_idx, AcirFormat& af)
72{
73 if (witness_idx != stdlib::IS_CONSTANT) {
74 af.max_witness_index = std::max(af.max_witness_index, witness_idx);
75 }
76}
77
79{
80 // Process multiplication terms: each term has two witness indices
81 for (const auto& mul_term : expr.mul_terms) {
84 }
85
86 // Process linear combinations: each term has one witness index
87 for (const auto& linear_term : expr.linear_combinations) {
89 }
90}
91
93{
94 auto update_max_witness_index_from_function_input = [&](const Acir::FunctionInput& input) {
97 }
98 };
99
100 auto update_max_witness_index_from_witness = [&](const Acir::Witness& witness) {
101 update_max_witness_index(witness.value, af);
102 };
103
104 std::visit(
106 [&](const Acir::Opcode::AssertZero& arg) { update_max_witness_index_from_expression(arg.value, af); },
107 [&](const Acir::Opcode::BlackBoxFuncCall& arg) {
108 std::visit(overloaded{ [&](const Acir::BlackBoxFuncCall::AND& bb_arg) {
109 update_max_witness_index_from_function_input(bb_arg.lhs);
110 update_max_witness_index_from_function_input(bb_arg.rhs);
111 update_max_witness_index_from_witness(bb_arg.output);
112 },
113 [&](const Acir::BlackBoxFuncCall::XOR& bb_arg) {
114 update_max_witness_index_from_function_input(bb_arg.lhs);
115 update_max_witness_index_from_function_input(bb_arg.rhs);
116 update_max_witness_index_from_witness(bb_arg.output);
117 },
118 [&](const Acir::BlackBoxFuncCall::RANGE& bb_arg) {
119 update_max_witness_index_from_function_input(bb_arg.input);
120 },
121 [&](const Acir::BlackBoxFuncCall::AES128Encrypt& bb_arg) {
122 for (const auto& input : bb_arg.inputs) {
123 update_max_witness_index_from_function_input(input);
124 }
125 for (const auto& input : *bb_arg.iv) {
126 update_max_witness_index_from_function_input(input);
127 }
128 for (const auto& input : *bb_arg.key) {
129 update_max_witness_index_from_function_input(input);
130 }
131 for (const auto& output : bb_arg.outputs) {
132 update_max_witness_index_from_witness(output);
133 }
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 }
145 },
146 [&](const Acir::BlackBoxFuncCall::Blake2s& bb_arg) {
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 }
153 },
154 [&](const Acir::BlackBoxFuncCall::Blake3& bb_arg) {
155 for (const auto& input : bb_arg.inputs) {
156 update_max_witness_index_from_function_input(input);
157 }
158 for (const auto& output : *bb_arg.outputs) {
159 update_max_witness_index_from_witness(output);
160 }
161 },
162 [&](const Acir::BlackBoxFuncCall::EcdsaSecp256k1& bb_arg) {
163 for (const auto& input : *bb_arg.public_key_x) {
164 update_max_witness_index_from_function_input(input);
165 }
166 for (const auto& input : *bb_arg.public_key_y) {
167 update_max_witness_index_from_function_input(input);
168 }
169 for (const auto& input : *bb_arg.signature) {
170 update_max_witness_index_from_function_input(input);
171 }
172 for (const auto& input : *bb_arg.hashed_message) {
173 update_max_witness_index_from_function_input(input);
174 }
175 update_max_witness_index_from_function_input(bb_arg.predicate);
176 update_max_witness_index_from_witness(bb_arg.output);
177 },
178 [&](const Acir::BlackBoxFuncCall::EcdsaSecp256r1& bb_arg) {
179 for (const auto& input : *bb_arg.public_key_x) {
180 update_max_witness_index_from_function_input(input);
181 }
182 for (const auto& input : *bb_arg.public_key_y) {
183 update_max_witness_index_from_function_input(input);
184 }
185 for (const auto& input : *bb_arg.signature) {
186 update_max_witness_index_from_function_input(input);
187 }
188 for (const auto& input : *bb_arg.hashed_message) {
189 update_max_witness_index_from_function_input(input);
190 }
191 update_max_witness_index_from_function_input(bb_arg.predicate);
192 update_max_witness_index_from_witness(bb_arg.output);
193 },
194 [&](const Acir::BlackBoxFuncCall::MultiScalarMul& bb_arg) {
195 for (const auto& input : bb_arg.points) {
196 update_max_witness_index_from_function_input(input);
197 }
198 for (const auto& input : bb_arg.scalars) {
199 update_max_witness_index_from_function_input(input);
200 }
201 update_max_witness_index_from_function_input(bb_arg.predicate);
202 for (const auto& output : *bb_arg.outputs) {
203 update_max_witness_index_from_witness(output);
204 }
205 },
207 for (const auto& input : *bb_arg.input1) {
208 update_max_witness_index_from_function_input(input);
209 }
210 for (const auto& input : *bb_arg.input2) {
211 update_max_witness_index_from_function_input(input);
212 }
213 update_max_witness_index_from_function_input(bb_arg.predicate);
214 for (const auto& output : *bb_arg.outputs) {
215 update_max_witness_index_from_witness(output);
216 }
217 },
218 [&](const Acir::BlackBoxFuncCall::Keccakf1600& bb_arg) {
219 for (const auto& input : *bb_arg.inputs) {
220 update_max_witness_index_from_function_input(input);
221 }
222 for (const auto& output : *bb_arg.outputs) {
223 update_max_witness_index_from_witness(output);
224 }
225 },
227 for (const auto& input : bb_arg.verification_key) {
228 update_max_witness_index_from_function_input(input);
229 }
230 for (const auto& input : bb_arg.proof) {
231 update_max_witness_index_from_function_input(input);
232 }
233 for (const auto& input : bb_arg.public_inputs) {
234 update_max_witness_index_from_function_input(input);
235 }
236 update_max_witness_index_from_function_input(bb_arg.key_hash);
237 update_max_witness_index_from_function_input(bb_arg.predicate);
238 },
240 for (const auto& input : bb_arg.inputs) {
241 update_max_witness_index_from_function_input(input);
242 }
243 for (const auto& output : bb_arg.outputs) {
244 update_max_witness_index_from_witness(output);
245 }
246 } },
247 arg.value.value);
248 },
249 [&](const Acir::Opcode::MemoryInit& arg) {
250 for (const auto& init : arg.init) {
251 update_max_witness_index_from_witness(init);
252 }
253 },
254 [&](const Acir::Opcode::MemoryOp& arg) {
257 update_max_witness_index_from_expression(arg.op.operation, af);
258 },
259 [&](const Acir::Opcode::BrilligCall& arg) {
260 for (const auto& input : arg.inputs) {
261 std::visit(overloaded{
262 [&](const Acir::BrilligInputs::Single& e) {
264 },
265 [&](const Acir::BrilligInputs::Array& e) {
266 for (const auto& expr : e.value) {
268 }
269 },
271 // MemoryArray does not contain witnesses directly, so nothing to do here.
272 },
273 },
274 input.value);
275 }
276 for (const auto& output : arg.outputs) {
277 std::visit(overloaded{
278 [&](const Acir::BrilligOutputs::Simple& e) {
279 update_max_witness_index_from_witness(e.value);
280 },
281 [&](const Acir::BrilligOutputs::Array& e) {
282 for (const auto& witness : e.value) {
283 update_max_witness_index_from_witness(witness);
284 }
285 },
286 },
287 output.value);
288 }
290 },
291 [&](const Acir::Opcode::Call&) {
292 bb::assert_failure("acir_format::update_max_witness_index_from_opcode: Call opcode is not supported.");
293 },
294 },
295 opcode.value);
296}
297
299
300template <typename T>
301T deserialize_msgpack_compact(std::vector<uint8_t>&& buf, std::function<T(msgpack::object const&)> decode_msgpack)
302{
303 BB_ASSERT(!buf.empty(), "deserialize_msgpack_compact: buffer is empty");
304
305 // Expect format marker for msgpack or msgpack-compact
306 const uint8_t FORMAT_MSGPACK = 2;
307 const uint8_t FORMAT_MSGPACK_COMPACT = 3;
308 uint8_t format_u8 = buf[0];
309 BB_ASSERT(format_u8 == FORMAT_MSGPACK || format_u8 == FORMAT_MSGPACK_COMPACT,
310 "deserialize_msgpack_compact: expected msgpack format marker (2 or 3), got " + std::to_string(format_u8));
311
312 // Skip the format marker to get the data.
313 const char* buffer = &reinterpret_cast<const char*>(buf.data())[1];
314 size_t size = buf.size() - 1;
315
316 auto oh = msgpack::unpack(buffer, size);
317 auto o = oh.get();
318
319 // Expect ARRAY type for msgpack-compact format
320 BB_ASSERT(o.type == msgpack::type::ARRAY,
321 "deserialize_msgpack_compact: expected ARRAY type, got " + std::to_string(o.type));
322
323 return decode_msgpack(o);
324}
325
327{
329 circuit.opcodes.size(), UINT32_MAX, "acir_format::circuit_serde_to_acir_format: too many opcodes in circuit.");
330
331 AcirFormat af;
332 af.num_acir_opcodes = static_cast<uint32_t>(circuit.opcodes.size());
333 af.public_inputs = join({
335 [&](const Acir::Witness& e) {
336 update_max_witness_index(e.value, af);
337 return e.value;
338 }),
340 [&](const Acir::Witness& e) {
341 update_max_witness_index(e.value, af);
342 return e.value;
343 }),
344 });
345 // Map to a pair of: BlockConstraint, and list of opcodes associated with that BlockConstraint
346 // Block constraints are built as we process the opcodes, so we store them in this map and we add them to the
347 // AcirFormat struct at the end
348 // NOTE: We want to deterministically visit this map, so unordered_map should not be used.
350
351 for (size_t i = 0; i < circuit.opcodes.size(); ++i) {
352 const auto& gate = circuit.opcodes[i];
354 std::visit(
356 [&](const Acir::Opcode::AssertZero& arg) { assert_zero_to_quad_constraints(arg, af, i); },
358 [&](const Acir::Opcode::MemoryInit& arg) {
359 auto block = memory_init_to_block_constraint(arg);
360 uint32_t block_id = arg.block_id.value;
361 block_id_to_block_constraint[block_id] = { block, /*opcode_indices=*/{ i } };
362 },
363 [&](const Acir::Opcode::MemoryOp& arg) {
364 auto block = block_id_to_block_constraint.find(arg.block_id.value);
365 if (block == block_id_to_block_constraint.end()) {
366 bb::assert_failure("acir_format::circuit_serde_to_acir_format: unitialized MemoryOp.");
367 }
368 add_memory_op_to_block_constraint(arg, block->second.first);
369 block->second.second.push_back(i);
370 },
371 [&](const Acir::Opcode::BrilligCall&) {},
372 [&](const Acir::Opcode::Call&) {
373 bb::assert_failure("acir_format::circuit_serde_to_acir_format: Call opcode is not supported.");
374 },
375 },
376 gate.value);
377 }
378 // Add the block constraints to the AcirFormat struct
379 for (const auto& [_, block] : block_id_to_block_constraint) {
380 af.block_constraints.push_back(block.first);
381 af.original_opcode_indices.block_constraints.push_back(block.second);
382 }
383
384 return af;
385}
386
387AcirFormat circuit_buf_to_acir_format(std::vector<uint8_t>&& buf)
388{
389 // We need to deserialize into Acir::Program first because the buffer returned by Noir has this structure
390 auto program = deserialize_msgpack_compact<Acir::ProgramWithoutBrillig>(
391 std::move(buf), [](auto o) -> Acir::ProgramWithoutBrillig {
392 Acir::ProgramWithoutBrillig program_wob;
393 try {
394 // Deserialize into a partial structure that ignores the Brillig parts,
395 // so that new opcodes can be added without breaking Barretenberg.
396 o.convert(program_wob);
397 } catch (const msgpack::type_error&) {
398 std::cerr << o << std::endl;
400 "acir_format::circuit_buf_to_acir_format: failed to convert msgpack data to Program");
401 }
402 return program_wob;
403 });
404 BB_ASSERT_EQ(program.functions.size(), 1U, "circuit_buf_to_acir_format: expected single function in ACIR program");
405
406 return circuit_serde_to_acir_format(program.functions[0]);
407}
408
410{
411 // We need to deserialize into WitnessStack first because the buffer returned by Noir has this structure
412 auto witness_stack = deserialize_msgpack_compact<Witnesses::WitnessStack>(std::move(buf), [](auto o) {
413 Witnesses::WitnessStack witness_stack;
414 try {
415 o.convert(witness_stack);
416 } catch (const msgpack::type_error&) {
417 std::cerr << o << std::endl;
419 "acir_format::witness_buf_to_witness_vector: failed to convert msgpack data to WitnessStack");
420 }
421 return witness_stack;
422 });
423 BB_ASSERT_EQ(witness_stack.stack.size(),
424 1U,
425 "acir_format::witness_buf_to_witness_vector: expected single WitnessMap in WitnessStack");
426
427 return witness_map_to_witness_vector(witness_stack.stack[0].witness);
428}
429
431{
432 // Note that the WitnessMap is in increasing order of witness indices because the comparator for the Acir::Witness
433 // is defined in terms of the witness index.
434
435 WitnessVector witness_vector;
436 for (size_t index = 0; const auto& e : witness_map.value) {
437 // ACIR uses a sparse format for WitnessMap where unused witness indices may be left unassigned.
438 // To ensure that witnesses sit at the correct indices in the `WitnessVector`, we fill any indices
439 // which do not exist within the `WitnessMap` with the random values. We use random values instead of zero
440 // because unassigned witnesses indices are not supposed to be used in any constraint, so filling them with a
441 // random value helps catching bugs.
442 while (index < e.first.value) {
443 witness_vector.emplace_back(fr::random_element());
444 index++;
445 }
446 witness_vector.emplace_back(from_buffer_with_bound_checks(e.second));
447 index++;
448 }
449
450 return witness_vector;
451}
452
454
456 std::map<uint32_t, bb::fr>& linear_terms)
457{
458 // Lambda to add next linear term from linear_terms to the mul_quad_ gate and erase it from linear_terms
459 auto add_linear_term_and_erase = [](uint32_t& idx, fr& scaling, std::map<uint32_t, fr>& linear_terms) {
461 idx, bb::stdlib::IS_CONSTANT, "Attempting to override a non-constant witness index in mul_quad_ gate");
462 idx = linear_terms.begin()->first;
463 scaling += linear_terms.begin()->second;
464 linear_terms.erase(idx);
465 };
466
468 // We cannot precompute the exact number of gates that will result from the expression. Therefore, we reserve the
469 // maximum number of gates that could ever be needed: one per multiplication term plus one per linear term. The real
470 // number of gates will in general be lower than this.
471 BB_ASSERT_LTE(arg.mul_terms.size(),
472 SIZE_MAX - linear_terms.size(),
473 "split_into_mul_quad_gates: overflow when reserving space for mul_quad_ gates.");
474 result.reserve(arg.mul_terms.size() + linear_terms.size());
475
476 // Step 1. Add multiplication terms and linear terms with the same witness index
477 for (const auto& mul_term : arg.mul_terms) {
478 result.emplace_back(mul_quad_<fr>{
479 .a = std::get<1>(mul_term).value,
480 .b = std::get<2>(mul_term).value,
481 .c = bb::stdlib::IS_CONSTANT,
482 .d = bb::stdlib::IS_CONSTANT,
483 .mul_scaling = from_buffer_with_bound_checks(std::get<0>(mul_term)),
484 .a_scaling = fr::zero(),
485 .b_scaling = fr::zero(),
486 .c_scaling = fr::zero(),
487 .d_scaling = fr::zero(),
488 .const_scaling = fr::zero(),
489 });
490
491 // Add linear terms corresponding to the witnesses involved in the multiplication term
492 auto& mul_quad = result.back();
493 if (linear_terms.contains(mul_quad.a)) {
494 mul_quad.a_scaling += linear_terms.at(mul_quad.a);
495 linear_terms.erase(mul_quad.a); // Remove it as the linear term for a has been processed
496 }
497 if (linear_terms.contains(mul_quad.b)) {
498 // Note that we enter here only if b is different from a
499 mul_quad.b_scaling += linear_terms.at(mul_quad.b);
500 linear_terms.erase(mul_quad.b); // Remove it as the linear term for b has been processed
501 }
502 }
503
504 // Step 2. Add linear terms to existing gates
505 bool is_first_gate = true;
506 for (auto& mul_quad : result) {
507 if (!linear_terms.empty()) {
508 add_linear_term_and_erase(mul_quad.c, mul_quad.c_scaling, linear_terms);
509 }
510
511 if (is_first_gate) {
512 // First gate contains the constant term and uses all four wires
513 mul_quad.const_scaling = from_buffer_with_bound_checks(arg.q_c);
514 if (!linear_terms.empty()) {
515 add_linear_term_and_erase(mul_quad.d, mul_quad.d_scaling, linear_terms);
516 }
517 is_first_gate = false;
518 }
519 }
520
521 // Step 3. Add remaining linear terms
522 while (!linear_terms.empty()) {
523 // We need to create new mul_quad_ gates to accomodate the remaining linear terms
524 mul_quad_<fr> mul_quad = {
525 .a = bb::stdlib::IS_CONSTANT,
526 .b = bb::stdlib::IS_CONSTANT,
527 .c = bb::stdlib::IS_CONSTANT,
528 .d = bb::stdlib::IS_CONSTANT,
529 .mul_scaling = fr::zero(),
530 .a_scaling = fr::zero(),
531 .b_scaling = fr::zero(),
532 .c_scaling = fr::zero(),
533 .d_scaling = fr::zero(),
534 .const_scaling = fr::zero(),
535 };
536 if (!linear_terms.empty()) {
537 add_linear_term_and_erase(mul_quad.a, mul_quad.a_scaling, linear_terms);
538 }
539 if (!linear_terms.empty()) {
540 add_linear_term_and_erase(mul_quad.b, mul_quad.b_scaling, linear_terms);
541 }
542 if (!linear_terms.empty()) {
543 add_linear_term_and_erase(mul_quad.c, mul_quad.c_scaling, linear_terms);
544 }
545 if (is_first_gate) {
546 // First gate contains the constant term and uses all four wires
548 if (!linear_terms.empty()) {
549 add_linear_term_and_erase(mul_quad.d, mul_quad.d_scaling, linear_terms);
550 }
551 is_first_gate = false;
552 }
553
554 result.emplace_back(mul_quad);
555 }
556
557 BB_ASSERT(!result.empty(),
558 "split_into_mul_quad_gates: resulted in zero gates. This means that there is an expression with no "
559 "multiplication terms and no linear terms.");
560 result.shrink_to_fit();
561
562 return result;
563}
564
566{
567 // Lambda to detect zero gates
568 auto is_zero_gate = [](const mul_quad_<fr>& gate) {
569 return ((gate.mul_scaling == fr(0)) && (gate.a_scaling == fr(0)) && (gate.b_scaling == fr(0)) &&
570 (gate.c_scaling == fr(0)) && (gate.d_scaling == fr(0)) && (gate.const_scaling == fr(0)));
571 };
572
573 auto linear_terms = process_linear_terms(arg.value);
574
575 // Check for unsatisfiable constraint: no variables but a non-zero constant means the circuit requires
576 // `constant == 0` which can never be satisfied.
577 if (arg.value.mul_terms.empty() && linear_terms.empty()) {
579 BB_ASSERT_EQ(constant,
580 fr::zero(),
581 "circuit is unsatisfiable. An AssertZero opcode contains no variables but has a non-zero "
582 "constant, which can never equal zero.");
583 }
584
585 bool is_single_gate = is_single_arithmetic_gate(arg.value, linear_terms);
586 std::vector<mul_quad_<fr>> mul_quads = split_into_mul_quad_gates(arg.value, linear_terms);
587
588 if (is_single_gate) {
589 BB_ASSERT_EQ(mul_quads.size(), 1U, "acir_format::assert_zero_to_quad_constraints: expected a single gate.");
590 auto mul_quad = mul_quads[0];
591
592 af.quad_constraints.push_back(mul_quad);
593 af.original_opcode_indices.quad_constraints.push_back(opcode_index);
594 } else {
595 BB_ASSERT_GT(mul_quads.size(),
596 1U,
597 "acir_format::assert_zero_to_quad_constraints: expected multiple gates but found one.");
598 af.big_quad_constraints.push_back(BigQuadConstraint(mul_quads));
599 af.original_opcode_indices.big_quad_constraints.push_back(opcode_index);
600 }
601
602 for (auto const& mul_quad : mul_quads) {
603 BB_ASSERT(!is_zero_gate(mul_quad),
604 "acir_format::assert_zero_to_quad_constraints: produced an arithmetic zero gate.");
605 }
606}
607
609 AcirFormat& af,
610 size_t opcode_index)
611{
612 auto to_witness_or_constant = [](const Acir::FunctionInput& e) { return parse_input(e); };
613 auto to_witness = [](const Acir::Witness& e) { return e.value; };
614 auto to_witness_from_input = [](const Acir::FunctionInput& e) { return get_witness_from_function_input(e); };
615
616 std::visit(
617 overloaded{ [&](const Acir::BlackBoxFuncCall::AND& arg) {
619 .a = parse_input(arg.lhs),
620 .b = parse_input(arg.rhs),
621 .result = to_witness(arg.output),
622 .num_bits = arg.num_bits,
623 .is_xor_gate = false,
624 });
625 af.original_opcode_indices.logic_constraints.push_back(opcode_index);
626 },
627 [&](const Acir::BlackBoxFuncCall::XOR& arg) {
629 .a = parse_input(arg.lhs),
630 .b = parse_input(arg.rhs),
631 .result = to_witness(arg.output),
632 .num_bits = arg.num_bits,
633 .is_xor_gate = true,
634 });
635 af.original_opcode_indices.logic_constraints.push_back(opcode_index);
636 },
637 [&](const Acir::BlackBoxFuncCall::RANGE& arg) {
640 .num_bits = arg.num_bits,
641 });
642 af.original_opcode_indices.range_constraints.push_back(opcode_index);
643 },
646 .inputs = transform::map(arg.inputs, to_witness_or_constant),
647 .iv = transform::map(*arg.iv, to_witness_or_constant),
648 .key = transform::map(*arg.key, to_witness_or_constant),
649 .outputs = transform::map(arg.outputs, to_witness),
650 });
651 af.original_opcode_indices.aes128_constraints.push_back(opcode_index);
652 },
655 .inputs = transform::map(*arg.inputs, to_witness_or_constant),
656 .hash_values = transform::map(*arg.hash_values, to_witness_or_constant),
657 .result = transform::map(*arg.outputs, to_witness),
658 });
659 af.original_opcode_indices.sha256_compression.push_back(opcode_index);
660 },
661 [&](const Acir::BlackBoxFuncCall::Blake2s& arg) {
663 .inputs = transform::map(arg.inputs, to_witness_or_constant),
664 .result = transform::map(*arg.outputs, to_witness),
665 });
666 af.original_opcode_indices.blake2s_constraints.push_back(opcode_index);
667 },
668 [&](const Acir::BlackBoxFuncCall::Blake3& arg) {
670 .inputs = transform::map(arg.inputs, to_witness_or_constant),
671 .result = transform::map(*arg.outputs, to_witness),
672 });
673 af.original_opcode_indices.blake3_constraints.push_back(opcode_index);
674 },
678 .hashed_message = transform::map(*arg.hashed_message, to_witness_from_input),
679 .signature = transform::map(*arg.signature, to_witness_from_input),
680 .pub_x_indices = transform::map(*arg.public_key_x, to_witness_from_input),
681 .pub_y_indices = transform::map(*arg.public_key_y, to_witness_from_input),
682 .predicate = parse_input(arg.predicate),
683 .result = to_witness(arg.output),
684 });
685 af.original_opcode_indices.ecdsa_k1_constraints.push_back(opcode_index);
686 },
690 .hashed_message = transform::map(*arg.hashed_message, to_witness_from_input),
691 .signature = transform::map(*arg.signature, to_witness_from_input),
692 .pub_x_indices = transform::map(*arg.public_key_x, to_witness_from_input),
693 .pub_y_indices = transform::map(*arg.public_key_y, to_witness_from_input),
694 .predicate = parse_input(arg.predicate),
695 .result = to_witness(arg.output),
696 });
697 af.original_opcode_indices.ecdsa_r1_constraints.push_back(opcode_index);
698 },
701 .points = transform::map(arg.points, to_witness_or_constant),
702 .scalars = transform::map(arg.scalars, to_witness_or_constant),
703 .predicate = parse_input(arg.predicate),
704 .out_point_x = to_witness((*arg.outputs)[0]),
705 .out_point_y = to_witness((*arg.outputs)[1]),
706 .out_point_is_infinite = to_witness((*arg.outputs)[2]),
707 });
708 af.original_opcode_indices.multi_scalar_mul_constraints.push_back(opcode_index);
709 },
711 af.ec_add_constraints.push_back(EcAdd{
712 .input1_x = parse_input((*arg.input1)[0]),
713 .input1_y = parse_input((*arg.input1)[1]),
714 .input1_infinite = parse_input((*arg.input1)[2]),
715 .input2_x = parse_input((*arg.input2)[0]),
716 .input2_y = parse_input((*arg.input2)[1]),
717 .input2_infinite = parse_input((*arg.input2)[2]),
718 .predicate = parse_input(arg.predicate),
719 .result_x = to_witness((*arg.outputs)[0]),
720 .result_y = to_witness((*arg.outputs)[1]),
721 .result_infinite = to_witness((*arg.outputs)[2]),
722 });
723 af.original_opcode_indices.ec_add_constraints.push_back(opcode_index);
724 },
726 af.keccak_permutations.push_back(Keccakf1600{
727 .state = transform::map(*arg.inputs, to_witness_or_constant),
728 .result = transform::map(*arg.outputs, to_witness),
729 });
730 af.original_opcode_indices.keccak_permutations.push_back(opcode_index);
731 },
733 auto predicate = parse_input(arg.predicate);
734 if (predicate.is_constant && predicate.value.is_zero()) {
735 // No constraint if the recursion is disabled
736 return;
737 }
738 auto c = RecursionConstraint{
739 .key = transform::map(arg.verification_key, to_witness_from_input),
740 .proof = transform::map(arg.proof, to_witness_from_input),
741 .public_inputs = transform::map(arg.public_inputs, to_witness_from_input),
742 .key_hash = get_witness_from_function_input(arg.key_hash),
743 .proof_type = arg.proof_type,
744 .predicate = predicate,
745 };
746
747 // Add the recursion constraint to the appropriate container based on proof type
748 switch (c.proof_type) {
749 case HONK_ZK:
750 case HONK:
751 case ROLLUP_HONK:
752 case ROOT_ROLLUP_HONK:
753 af.honk_recursion_constraints.push_back(c);
754 af.original_opcode_indices.honk_recursion_constraints.push_back(opcode_index);
755 break;
756 case OINK:
757 case HN:
758 case HN_TAIL:
759 case HN_FINAL:
760 af.hn_recursion_constraints.push_back(c);
761 af.original_opcode_indices.hn_recursion_constraints.push_back(opcode_index);
762 break;
763 case AVM:
764 af.avm_recursion_constraints.push_back(c);
765 af.original_opcode_indices.avm_recursion_constraints.push_back(opcode_index);
766 break;
767 case CHONK:
768 af.chonk_recursion_constraints.push_back(c);
769 af.original_opcode_indices.chonk_recursion_constraints.push_back(opcode_index);
770 break;
771 default:
773 "acir_format::handle_black_box_fun_call: Invalid PROOF_TYPE in RecursionConstraint.");
774 }
775 },
778 .state = transform::map(arg.inputs, to_witness_or_constant),
779 .result = transform::map(arg.outputs, to_witness),
780 });
781 af.original_opcode_indices.poseidon2_constraints.push_back(opcode_index);
782 } },
783 arg.value.value);
784}
785
787{
788 // Noir doesn't distinguish between ROM and RAM table. Therefore, we initialize every table as a ROM table, and
789 // then we make it a RAM table if there is at least one write operation
790 BlockConstraint block{
791 .init = {},
792 .trace = {},
793 .type = BlockType::ROM,
794 .calldata_id = CallDataType::None,
795 };
796
797 for (const auto& init : mem_init.init) {
798 block.init.push_back(init.value);
799 }
800
801 // Databus is only supported for Goblin, non Goblin builders will treat call_data and return_data as normal
802 // array.
804 uint32_t calldata_id = std::get<Acir::BlockType::CallData>(mem_init.block_type.value).value;
805 BB_ASSERT_LTE(calldata_id,
806 MAX_APPS_PER_KERNEL,
807 "acir_format::handle_memory_init: calldata id exceeds kernel + MAX_APPS_PER_KERNEL app columns");
808
809 block.type = BlockType::CallData;
810 block.calldata_id = static_cast<CallDataType>(calldata_id);
812 block.type = BlockType::ReturnData;
813 }
814
815 return block;
816}
817
819{
820 // Lambda to convert an Acir::Expression to a witness index. Noir always emits a single unscaled witness term for
821 // memory op indices and values, so anything else is a malformed input.
822 auto acir_expression_to_witness = [](const Acir::Expression& expr) -> uint32_t {
823 BB_ASSERT(expr.mul_terms.empty(), "MemoryOp should not have multiplication terms");
824 BB_ASSERT_EQ(expr.linear_combinations.size(), 1U, "MemoryOp expression must be a single witness");
825
826 const fr a_scaling = from_buffer_with_bound_checks(std::get<0>(expr.linear_combinations[0]));
827 const fr constant_term = from_buffer_with_bound_checks(expr.q_c);
828
829 BB_ASSERT(a_scaling == fr::one() && constant_term == fr::zero(),
830 "MemoryOp expression must be a single unscaled witness with no constant term");
831
832 return std::get<1>(expr.linear_combinations[0]).value;
833 };
834
835 // Lambda to determine whether a memory operation is a read or write operation
836 auto is_read_operation = [](const Acir::Expression& expr) {
837 BB_ASSERT(expr.mul_terms.empty(), "MemoryOp expression should not have multiplication terms");
838 BB_ASSERT(expr.linear_combinations.empty(), "MemoryOp expression should not have linear terms");
839
840 const fr const_term = from_buffer_with_bound_checks(expr.q_c);
841
842 BB_ASSERT((const_term == fr::one()) || (const_term == fr::zero()),
843 "MemoryOp expression should be either zero or one");
844
845 // A read operation is given by a zero Expression
846 return const_term == fr::zero();
847 };
848
849 AccessType access_type = is_read_operation(mem_op.op.operation) ? AccessType::Read : AccessType::Write;
850 if (access_type == AccessType::Write) {
851 // We are not allowed to write on the databus
853 // Mark the table as a RAM table
854 block.type = BlockType::RAM;
855 }
856
857 MemOp acir_mem_op = MemOp{
858 .access_type = access_type,
859 .index = acir_expression_to_witness(mem_op.op.index),
860 .value = acir_expression_to_witness(mem_op.op.value),
861 };
862 block.trace.push_back(acir_mem_op);
863}
864
866{
867 static constexpr size_t NUM_WIRES = 4; // Equal to the number of wires in the arithmetization
868
869 // If there are more than 4 distinct witnesses in the linear terms, then we need multiple arithmetic gates
870 if (linear_terms.size() > NUM_WIRES) {
871 return false;
872 }
873
874 if (arg.mul_terms.size() > 1) {
875 // If there is more than one multiplication gate, then we need multiple arithmetic gates
876 return false;
877 }
878
879 if (arg.mul_terms.size() == 1) {
880 // In this case we have two witnesses coming from the multiplication term plus the linear terms.
881 // We proceed as follows:
882 // 0. Start from the assumption that all witnesses (from linear terms and multiplication) are distinct
883 // 1. Check if the lhs and rhs witness in the multiplication are already contained in the linear terms
884 // 2. Check if the lhs witness and the rhs witness are equal
885 // 2.a If they are distinct, update the total number of witnesses to be added to wires according to result
886 // of the check at step 1: each distinct witness already in the linear terms subtracts one from the
887 // total
888 // 2.b If they are equal, update the total number of witnesses to be added to wires according to result of
889 // the check at step 1: if the witness is already in the linear terms, it removes one from the total
890
891 // Number of witnesses to be put in wires if the witnesses from the linear terms and the multiplication term are
892 // all different
893 size_t num_witnesses_to_be_put_in_wires = 2 + linear_terms.size();
894
895 uint32_t witness_idx_lhs = std::get<1>(arg.mul_terms[0]).value;
896 uint32_t witness_idx_rhs = std::get<2>(arg.mul_terms[0]).value;
897
898 bool lhs_is_distinct_from_linear_terms = !linear_terms.contains(witness_idx_lhs);
899 bool rhs_is_distinct_from_linear_terms = !linear_terms.contains(witness_idx_rhs);
900
901 if (witness_idx_lhs != witness_idx_rhs) {
902 num_witnesses_to_be_put_in_wires -= lhs_is_distinct_from_linear_terms ? 0U : 1U;
903 num_witnesses_to_be_put_in_wires -= rhs_is_distinct_from_linear_terms ? 0U : 1U;
904 } else {
905 num_witnesses_to_be_put_in_wires -= lhs_is_distinct_from_linear_terms ? 0U : 1U;
906 }
907
908 return num_witnesses_to_be_put_in_wires <= NUM_WIRES;
909 }
910
911 return linear_terms.size() <= NUM_WIRES;
912}
913
915{
916 std::map<uint32_t, bb::fr> linear_terms;
917 for (const auto& linear_term : expr.linear_combinations) {
918 fr selector_value = from_buffer_with_bound_checks(std::get<0>(linear_term));
919 uint32_t witness_idx = std::get<1>(linear_term).value;
920 if (linear_terms.contains(witness_idx)) {
921 linear_terms[witness_idx] += selector_value; // Accumulate coefficients for duplicate witnesses
922 } else {
923 linear_terms[witness_idx] = selector_value;
924 }
925 }
926 return linear_terms;
927}
928
929} // 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...
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:60
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:155
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:3453
std::variant< Memory, CallData, ReturnData > value
Definition acir.hpp:3755
Acir::PublicInputs return_values
Definition acir.hpp:4799
std::vector< Acir::Opcode > opcodes
Definition acir.hpp:4796
Acir::PublicInputs public_parameters
Definition acir.hpp:4798
std::vector< std::tuple< std::vector< uint8_t >, Acir::Witness > > linear_combinations
Definition acir.hpp:3839
std::vector< uint8_t > q_c
Definition acir.hpp:3840
std::vector< std::tuple< std::vector< uint8_t >, Acir::Witness, Acir::Witness > > mul_terms
Definition acir.hpp:3838
std::variant< Constant, Witness > value
Definition acir.hpp:2843
Acir::Expression value
Definition acir.hpp:4146
Acir::Expression operation
Definition acir.hpp:4144
Acir::Expression index
Definition acir.hpp:4145
Acir::Expression value
Definition acir.hpp:4180
Acir::BlackBoxFuncCall value
Definition acir.hpp:4198
std::vector< Acir::Witness > init
Definition acir.hpp:4247
Acir::BlockType block_type
Definition acir.hpp:4248
std::variant< AssertZero, BlackBoxFuncCall, MemoryOp, MemoryInit, BrilligCall, Call > value
Definition acir.hpp:4355
std::vector< Acir::Witness > value
Definition acir.hpp:4776
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 is the witness index of the memory location, and value is the witness index o...
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
========= HELPERS ========= ///
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()