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 <optional>
13#include <tuple>
14#include <utility>
15
26
27namespace acir_format {
28
29using namespace bb;
30
32
33template <class... Ts> struct overloaded : Ts... {
34 using Ts::operator()...;
35};
36
37bb::fr from_buffer_with_bound_checks(const std::vector<uint8_t>& buffer)
38{
39 BB_ASSERT_EQ(buffer.size(), 32U, "acir_format::from_buffer_with_bound_checks: buffer size must be 32 bytes.");
40 return fr::serialize_from_buffer(buffer.data());
41}
42
44{
47 .index = e.value.value,
48 .value = bb::fr::zero(),
49 .is_constant = false,
50 };
51 },
54 .index = bb::stdlib::IS_CONSTANT,
55 .value = from_buffer_with_bound_checks(e.value),
56 .is_constant = true,
57 };
58 } },
59 input.value);
60 return result;
61}
62
64{
66 "acir_format::get_witness_from_function_input: input must be a Witness variant. An error here means "
67 "there was a serialization error.");
68
69 return std::get<Acir::FunctionInput::Witness>(input.value).value.value;
70}
71
72void update_max_witness_index(const uint32_t witness_idx, AcirFormat& af)
73{
74 if (witness_idx != stdlib::IS_CONSTANT) {
75 af.max_witness_index = std::max(af.max_witness_index, witness_idx);
76 }
77}
78
80{
81 // Process multiplication terms: each term has two witness indices
82 for (const auto& mul_term : expr.mul_terms) {
85 }
86
87 // Process linear combinations: each term has one witness index
88 for (const auto& linear_term : expr.linear_combinations) {
90 }
91}
92
94{
95 auto update_max_witness_index_from_function_input = [&](const Acir::FunctionInput& input) {
98 }
99 };
100
101 auto update_max_witness_index_from_witness = [&](const Acir::Witness& witness) {
102 update_max_witness_index(witness.value, af);
103 };
104
105 std::visit(
107 [&](const Acir::Opcode::AssertZero& arg) { update_max_witness_index_from_expression(arg.value, af); },
108 [&](const Acir::Opcode::BlackBoxFuncCall& arg) {
109 std::visit(overloaded{ [&](const Acir::BlackBoxFuncCall::AND& bb_arg) {
110 update_max_witness_index_from_function_input(bb_arg.lhs);
111 update_max_witness_index_from_function_input(bb_arg.rhs);
112 update_max_witness_index_from_witness(bb_arg.output);
113 },
114 [&](const Acir::BlackBoxFuncCall::XOR& bb_arg) {
115 update_max_witness_index_from_function_input(bb_arg.lhs);
116 update_max_witness_index_from_function_input(bb_arg.rhs);
117 update_max_witness_index_from_witness(bb_arg.output);
118 },
119 [&](const Acir::BlackBoxFuncCall::RANGE& bb_arg) {
120 update_max_witness_index_from_function_input(bb_arg.input);
121 },
122 [&](const Acir::BlackBoxFuncCall::AES128Encrypt& bb_arg) {
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 }
135 },
137 for (const auto& input : *bb_arg.inputs) {
138 update_max_witness_index_from_function_input(input);
139 }
140 for (const auto& input : *bb_arg.hash_values) {
141 update_max_witness_index_from_function_input(input);
142 }
143 for (const auto& output : *bb_arg.outputs) {
144 update_max_witness_index_from_witness(output);
145 }
146 },
147 [&](const Acir::BlackBoxFuncCall::Blake2s& bb_arg) {
148 for (const auto& input : bb_arg.inputs) {
149 update_max_witness_index_from_function_input(input);
150 }
151 for (const auto& output : *bb_arg.outputs) {
152 update_max_witness_index_from_witness(output);
153 }
154 },
155 [&](const Acir::BlackBoxFuncCall::Blake3& bb_arg) {
156 for (const auto& input : bb_arg.inputs) {
157 update_max_witness_index_from_function_input(input);
158 }
159 for (const auto& output : *bb_arg.outputs) {
160 update_max_witness_index_from_witness(output);
161 }
162 },
163 [&](const Acir::BlackBoxFuncCall::EcdsaSecp256k1& bb_arg) {
164 for (const auto& input : *bb_arg.public_key_x) {
165 update_max_witness_index_from_function_input(input);
166 }
167 for (const auto& input : *bb_arg.public_key_y) {
168 update_max_witness_index_from_function_input(input);
169 }
170 for (const auto& input : *bb_arg.signature) {
171 update_max_witness_index_from_function_input(input);
172 }
173 for (const auto& input : *bb_arg.hashed_message) {
174 update_max_witness_index_from_function_input(input);
175 }
176 update_max_witness_index_from_function_input(bb_arg.predicate);
177 update_max_witness_index_from_witness(bb_arg.output);
178 },
179 [&](const Acir::BlackBoxFuncCall::EcdsaSecp256r1& bb_arg) {
180 for (const auto& input : *bb_arg.public_key_x) {
181 update_max_witness_index_from_function_input(input);
182 }
183 for (const auto& input : *bb_arg.public_key_y) {
184 update_max_witness_index_from_function_input(input);
185 }
186 for (const auto& input : *bb_arg.signature) {
187 update_max_witness_index_from_function_input(input);
188 }
189 for (const auto& input : *bb_arg.hashed_message) {
190 update_max_witness_index_from_function_input(input);
191 }
192 update_max_witness_index_from_function_input(bb_arg.predicate);
193 update_max_witness_index_from_witness(bb_arg.output);
194 },
195 [&](const Acir::BlackBoxFuncCall::MultiScalarMul& bb_arg) {
196 for (const auto& input : bb_arg.points) {
197 update_max_witness_index_from_function_input(input);
198 }
199 for (const auto& input : bb_arg.scalars) {
200 update_max_witness_index_from_function_input(input);
201 }
202 update_max_witness_index_from_function_input(bb_arg.predicate);
203 for (const auto& output : *bb_arg.outputs) {
204 update_max_witness_index_from_witness(output);
205 }
206 },
208 for (const auto& input : *bb_arg.input1) {
209 update_max_witness_index_from_function_input(input);
210 }
211 for (const auto& input : *bb_arg.input2) {
212 update_max_witness_index_from_function_input(input);
213 }
214 update_max_witness_index_from_function_input(bb_arg.predicate);
215 for (const auto& output : *bb_arg.outputs) {
216 update_max_witness_index_from_witness(output);
217 }
218 },
219 [&](const Acir::BlackBoxFuncCall::Keccakf1600& bb_arg) {
220 for (const auto& input : *bb_arg.inputs) {
221 update_max_witness_index_from_function_input(input);
222 }
223 for (const auto& output : *bb_arg.outputs) {
224 update_max_witness_index_from_witness(output);
225 }
226 },
228 for (const auto& input : bb_arg.verification_key) {
229 update_max_witness_index_from_function_input(input);
230 }
231 for (const auto& input : bb_arg.proof) {
232 update_max_witness_index_from_function_input(input);
233 }
234 for (const auto& input : bb_arg.public_inputs) {
235 update_max_witness_index_from_function_input(input);
236 }
237 update_max_witness_index_from_function_input(bb_arg.key_hash);
238 update_max_witness_index_from_function_input(bb_arg.predicate);
239 },
241 for (const auto& input : bb_arg.inputs) {
242 update_max_witness_index_from_function_input(input);
243 }
244 for (const auto& output : bb_arg.outputs) {
245 update_max_witness_index_from_witness(output);
246 }
247 } },
248 arg.value.value);
249 },
250 [&](const Acir::Opcode::MemoryInit& arg) {
251 for (const auto& init : arg.init) {
252 update_max_witness_index_from_witness(init);
253 }
254 },
255 [&](const Acir::Opcode::MemoryOp& arg) {
256 update_max_witness_index_from_witness(arg.op.index);
257 update_max_witness_index_from_witness(arg.op.value);
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, msgpack-compact or msgpack-tagged
306 const uint8_t FORMAT_MSGPACK = 2;
307 const uint8_t FORMAT_MSGPACK_COMPACT = 3;
308 const uint8_t FORMAT_MSGPACK_TAGGED = 4;
309 uint8_t format_u8 = buf[0];
310 BB_ASSERT(format_u8 == FORMAT_MSGPACK || format_u8 == FORMAT_MSGPACK_COMPACT || format_u8 == FORMAT_MSGPACK_TAGGED,
311 "deserialize_msgpack_compact: expected msgpack format marker (2, 3 or 4), got " +
312 std::to_string(format_u8));
313
314 // Skip the format marker to get the data.
315 const char* buffer = &reinterpret_cast<const char*>(buf.data())[1];
316 size_t size = buf.size() - 1;
317
318 auto oh = msgpack::unpack(buffer, size);
319 auto o = oh.get();
320
321 // Expect ARRAY type for msgpack-compact format
322 if (format_u8 == FORMAT_MSGPACK_COMPACT) {
323 BB_ASSERT(o.type == msgpack::type::ARRAY,
324 "deserialize_msgpack_compact: expected ARRAY type, got " + std::to_string(o.type));
325 }
326
327 return decode_msgpack(o);
328}
329
331{
333 circuit.opcodes.size(), UINT32_MAX, "acir_format::circuit_serde_to_acir_format: too many opcodes in circuit.");
334
335 AcirFormat af;
336 af.num_acir_opcodes = static_cast<uint32_t>(circuit.opcodes.size());
337 af.public_inputs = join({
339 [&](const Acir::Witness& e) {
340 update_max_witness_index(e.value, af);
341 return e.value;
342 }),
344 [&](const Acir::Witness& e) {
345 update_max_witness_index(e.value, af);
346 return e.value;
347 }),
348 });
349 // Map to a pair of: BlockConstraint, and list of opcodes associated with that BlockConstraint
350 // Block constraints are built as we process the opcodes, so we store them in this map and we add them to the
351 // AcirFormat struct at the end
352 // NOTE: We want to deterministically visit this map, so unordered_map should not be used.
354
355 // Linear AssertZeros that can be batched into one rows
356 // They are batched later via the function batched_eq_assert_zeros_into_constraints
357 std::vector<BatchedEqEntry> batched_eq_assert_zeros;
358
359 for (size_t i = 0; i < circuit.opcodes.size(); ++i) {
360 const auto& gate = circuit.opcodes[i];
362 std::visit(
364 [&](const Acir::Opcode::AssertZero& arg) {
365 assert_zero_to_constraints(arg, af, i, batched_eq_assert_zeros, is_mega);
366 },
368 [&](const Acir::Opcode::MemoryInit& arg) {
369 auto block = memory_init_to_block_constraint(arg);
370 uint32_t block_id = arg.block_id.value;
371 block_id_to_block_constraint[block_id] = { block, /*opcode_indices=*/{ i } };
372 },
373 [&](const Acir::Opcode::MemoryOp& arg) {
374 auto block = block_id_to_block_constraint.find(arg.block_id.value);
375 if (block == block_id_to_block_constraint.end()) {
376 bb::assert_failure("acir_format::circuit_serde_to_acir_format: unitialized MemoryOp.");
377 }
378 add_memory_op_to_block_constraint(arg, block->second.first);
379 block->second.second.push_back(i);
380 },
381 [&](const Acir::Opcode::BrilligCall&) {},
382 [&](const Acir::Opcode::Call&) {
383 bb::assert_failure("acir_format::circuit_serde_to_acir_format: Call opcode is not supported.");
384 },
385 },
386 gate.value);
387 }
388 // Pair any buffered batched-eq AssertZeros (≤2-witness linear opcodes) into BATCHED_EQ rows.
389 batched_eq_assert_zeros_into_constraints(af, batched_eq_assert_zeros);
390
391 // Add the block constraints to the AcirFormat struct
392 for (const auto& [_, block] : block_id_to_block_constraint) {
393 af.block_constraints.push_back(block.first);
394 af.original_opcode_indices.block_constraints.push_back(block.second);
395 }
396
398 UINT32_MAX,
399 "Max witness index above UINT32_MAX, this value is reserved for unset witnesses that will be replaced "
400 "with the zero index.");
401
402 return af;
403}
404
405AcirFormat circuit_buf_to_acir_format(std::vector<uint8_t>&& buf, bool is_mega)
406{
407 // We need to deserialize into Acir::Program first because the buffer returned by Noir has this structure
408 auto program = deserialize_msgpack_compact<Acir::ProgramWithoutBrillig>(
409 std::move(buf), [](auto o) -> Acir::ProgramWithoutBrillig {
410 Acir::ProgramWithoutBrillig program_wob;
411 try {
412 // Deserialize into a partial structure that ignores the Brillig parts,
413 // so that new opcodes can be added without breaking Barretenberg.
414 o.convert(program_wob);
415 } catch (const msgpack::type_error&) {
416 std::cerr << o << std::endl;
418 "acir_format::circuit_buf_to_acir_format: failed to convert msgpack data to Program");
419 }
420 return program_wob;
421 });
422 BB_ASSERT_EQ(program.functions.size(), 1U, "circuit_buf_to_acir_format: expected single function in ACIR program");
423
424 return circuit_serde_to_acir_format(program.functions[0], is_mega);
425}
426
428{
429 return circuit_buf_to_acir_format(std::move(buf), true);
430}
431
433{
434 // We need to deserialize into WitnessStack first because the buffer returned by Noir has this structure
435 auto witness_stack = deserialize_msgpack_compact<Witnesses::WitnessStack>(std::move(buf), [](auto o) {
436 Witnesses::WitnessStack witness_stack;
437 try {
438 o.convert(witness_stack);
439 } catch (const msgpack::type_error&) {
440 std::cerr << o << std::endl;
442 "acir_format::witness_buf_to_witness_vector: failed to convert msgpack data to WitnessStack");
443 }
444 return witness_stack;
445 });
446 BB_ASSERT_EQ(witness_stack.stack.size(),
447 1U,
448 "acir_format::witness_buf_to_witness_vector: expected single WitnessMap in WitnessStack");
449
450 return witness_map_to_witness_vector(witness_stack.stack[0].witness);
451}
452
454{
455 // Note that the WitnessMap is in increasing order of witness indices because the comparator for the Acir::Witness
456 // is defined in terms of the witness index.
457
458 WitnessVector witness_vector;
459 for (size_t index = 0; const auto& e : witness_map.value) {
460 // ACIR uses a sparse format for WitnessMap where unused witness indices may be left unassigned.
461 // To ensure that witnesses sit at the correct indices in the `WitnessVector`, we fill any indices
462 // which do not exist within the `WitnessMap` with the random values. We use random values instead of zero
463 // because unassigned witnesses indices are not supposed to be used in any constraint, so filling them with a
464 // random value helps catching bugs.
465 while (index < e.first.value) {
466 witness_vector.emplace_back(fr::random_element());
467 index++;
468 }
469 witness_vector.emplace_back(from_buffer_with_bound_checks(e.second));
470 index++;
471 }
472
473 return witness_vector;
474}
475
477
479 std::map<uint32_t, bb::fr>& linear_terms)
480{
481 // Lambda to add next linear term from linear_terms to the mul_quad_ gate and erase it from linear_terms
482 auto add_linear_term_and_erase = [](uint32_t& idx, fr& scaling, std::map<uint32_t, fr>& linear_terms) {
484 idx, bb::stdlib::IS_CONSTANT, "Attempting to override a non-constant witness index in mul_quad_ gate");
485 idx = linear_terms.begin()->first;
486 scaling += linear_terms.begin()->second;
487 linear_terms.erase(idx);
488 };
489
491 // We cannot precompute the exact number of gates that will result from the expression. Therefore, we reserve the
492 // maximum number of gates that could ever be needed: one per multiplication term plus one per linear term. The real
493 // number of gates will in general be lower than this.
494 BB_ASSERT_LTE(arg.mul_terms.size(),
495 SIZE_MAX - linear_terms.size(),
496 "split_into_mul_quad_gates: overflow when reserving space for mul_quad_ gates.");
497 result.reserve(arg.mul_terms.size() + linear_terms.size());
498
499 // Step 1. Add multiplication terms and linear terms with the same witness index
500 for (const auto& mul_term : arg.mul_terms) {
501 result.emplace_back(mul_quad_<fr>{
502 .a = std::get<1>(mul_term).value,
503 .b = std::get<2>(mul_term).value,
504 .c = bb::stdlib::IS_CONSTANT,
505 .d = bb::stdlib::IS_CONSTANT,
506 .mul_scaling = from_buffer_with_bound_checks(std::get<0>(mul_term)),
507 .a_scaling = fr::zero(),
508 .b_scaling = fr::zero(),
509 .c_scaling = fr::zero(),
510 .d_scaling = fr::zero(),
511 .const_scaling = fr::zero(),
512 });
513
514 // Add linear terms corresponding to the witnesses involved in the multiplication term
515 auto& mul_quad = result.back();
516 if (linear_terms.contains(mul_quad.a)) {
517 mul_quad.a_scaling += linear_terms.at(mul_quad.a);
518 linear_terms.erase(mul_quad.a); // Remove it as the linear term for a has been processed
519 }
520 if (linear_terms.contains(mul_quad.b)) {
521 // Note that we enter here only if b is different from a
522 mul_quad.b_scaling += linear_terms.at(mul_quad.b);
523 linear_terms.erase(mul_quad.b); // Remove it as the linear term for b has been processed
524 }
525 }
526
527 // Step 2. Add linear terms to existing gates
528 bool is_first_gate = true;
529 for (auto& mul_quad : result) {
530 if (!linear_terms.empty()) {
531 add_linear_term_and_erase(mul_quad.c, mul_quad.c_scaling, linear_terms);
532 }
533
534 if (is_first_gate) {
535 // First gate contains the constant term and uses all four wires
536 mul_quad.const_scaling = from_buffer_with_bound_checks(arg.q_c);
537 if (!linear_terms.empty()) {
538 add_linear_term_and_erase(mul_quad.d, mul_quad.d_scaling, linear_terms);
539 }
540 is_first_gate = false;
541 }
542 }
543
544 // Step 3. Add remaining linear terms
545 while (!linear_terms.empty()) {
546 // We need to create new mul_quad_ gates to accomodate the remaining linear terms
547 mul_quad_<fr> mul_quad = {
548 .a = bb::stdlib::IS_CONSTANT,
549 .b = bb::stdlib::IS_CONSTANT,
550 .c = bb::stdlib::IS_CONSTANT,
551 .d = bb::stdlib::IS_CONSTANT,
552 .mul_scaling = fr::zero(),
553 .a_scaling = fr::zero(),
554 .b_scaling = fr::zero(),
555 .c_scaling = fr::zero(),
556 .d_scaling = fr::zero(),
557 .const_scaling = fr::zero(),
558 };
559 if (!linear_terms.empty()) {
560 add_linear_term_and_erase(mul_quad.a, mul_quad.a_scaling, linear_terms);
561 }
562 if (!linear_terms.empty()) {
563 add_linear_term_and_erase(mul_quad.b, mul_quad.b_scaling, linear_terms);
564 }
565 if (!linear_terms.empty()) {
566 add_linear_term_and_erase(mul_quad.c, mul_quad.c_scaling, linear_terms);
567 }
568 if (is_first_gate) {
569 // First gate contains the constant term and uses all four wires
571 if (!linear_terms.empty()) {
572 add_linear_term_and_erase(mul_quad.d, mul_quad.d_scaling, linear_terms);
573 }
574 is_first_gate = false;
575 }
576
577 result.emplace_back(mul_quad);
578 }
579
580 BB_ASSERT(!result.empty(),
581 "split_into_mul_quad_gates: resulted in zero gates. This means that there is an expression with no "
582 "multiplication terms and no linear terms.");
583 result.shrink_to_fit();
584
585 return result;
586}
587
588bool resolve_shared_wire_products(Acir::Expression const& arg, uint32_t& w_l, uint32_t& w_r, uint32_t& w_o)
589{
590 // Two products: a * b, c * d
591 const uint32_t a = std::get<1>(arg.mul_terms[0]).value;
592 const uint32_t b = std::get<2>(arg.mul_terms[0]).value;
593 const uint32_t c = std::get<1>(arg.mul_terms[1]).value;
594 const uint32_t d = std::get<2>(arg.mul_terms[1]).value;
595
596 uint32_t shared_index = bb::stdlib::IS_CONSTANT;
597 size_t num_shared_indices = 0;
598
599 // Lambda to check whether the witness index matches one of the witness indices from the pair (c,d)
600 auto process_witness_index = [&](uint32_t w) {
601 if (w == c || w == d) {
602 shared_index = w;
603 ++num_shared_indices;
604 }
605 };
606
607 process_witness_index(a);
608 if (a != b) {
609 // If a != b, we need to check b as well
610 process_witness_index(b);
611 }
612
613 // The condition we are looking for is num_shared_indices == 1
614 // num_shared_indices == 0 means two disjoint products
615 // num_shared_indices == 2 is the same wire-pair, which should not happen
616 if (num_shared_indices != 1) {
617 return false;
618 }
619
621 shared_index, bb::stdlib::IS_CONSTANT, "acir_format::resolve_shared_wire_products: no matched shared_index.");
622 w_l = shared_index;
623 w_r = (a == shared_index) ? b : a;
624 w_o = (c == shared_index) ? d : c;
625 return true;
626}
627
628bool is_bilinear(Acir::Expression const& arg, const std::map<uint32_t, bb::fr>& linear_terms)
629{
630 if (arg.mul_terms.size() != 2) {
631 return false;
632 }
633 uint32_t w_l = bb::stdlib::IS_CONSTANT;
634 uint32_t w_r = bb::stdlib::IS_CONSTANT;
635 uint32_t w_o = bb::stdlib::IS_CONSTANT;
636 if (!resolve_shared_wire_products(arg, w_l, w_r, w_o)) {
637 return false;
638 }
639 // Linear terms must lie on the three product wires plus at most one extra witness, which becomes the
640 // linear-only fourth wire w_4.
641 bool extra_seen = false;
642 for (const auto& [witness, coeff] : linear_terms) {
643 if (witness == w_l || witness == w_r || witness == w_o) {
644 continue;
645 }
646 if (extra_seen) {
647 return false;
648 }
649 extra_seen = true;
650 }
651 return true;
652}
653
654bool is_batched_eq(Acir::Expression const& arg, const std::map<uint32_t, bb::fr>& linear_terms)
655{
656 return arg.mul_terms.empty() && !linear_terms.empty() && linear_terms.size() <= 2;
657}
658
660 const std::map<uint32_t, bb::fr>& linear_terms)
661{
662 uint32_t w_l = bb::stdlib::IS_CONSTANT;
663 uint32_t w_r = bb::stdlib::IS_CONSTANT;
664 uint32_t w_o = bb::stdlib::IS_CONSTANT;
665 bool resolved = resolve_shared_wire_products(arg, w_l, w_r, w_o);
666 BB_ASSERT(resolved, "acir_format::build_bilinear_constraint: the two products must share exactly one wire.");
667
668 // The fourth wire carries only a linear term; default to the IS_CONSTANT sentinel and bind it to the
669 // single linear witness outside {w_l, w_r, w_o} if one is present.
670 uint32_t w_4 = bb::stdlib::IS_CONSTANT;
671
672 fr q_l = fr::zero();
673 fr q_r = fr::zero();
674 fr q_o = fr::zero();
675 fr q_4 = fr::zero();
676 // The following loop is safe because linear_terms has distinct witnesses
677 for (const auto& [w, c] : linear_terms) {
678 if (w == w_l) {
679 q_l = c;
680 } else if (w == w_r) {
681 q_r = c;
682 } else if (w == w_o) {
683 q_o = c;
684 } else {
685 // Guaranteed by is_bilinear: at most one linear-only witness, which becomes the fourth wire.
686 BB_ASSERT(w_4 == bb::stdlib::IS_CONSTANT,
687 "acir_format::build_bilinear_constraint: more than one linear-only witness.");
688 w_4 = w;
689 q_4 = c;
690 }
691 }
692
693 return BilinearConstraint{
694 .a = w_l,
695 .b = w_r,
696 .c = w_o,
697 .d = w_4,
699 .q_l = q_l,
700 .q_r = q_r,
701 .q_o = q_o,
702 .q_4 = q_4,
705 };
706}
707
709 const std::map<uint32_t, bb::fr>& linear_terms,
710 size_t opcode_index)
711{
712 BB_ASSERT(!linear_terms.empty() && linear_terms.size() <= 2, "BatchedEq gate requires at most two linear terms.");
713 BatchedEqEntry entry{
714 .w1 = bb::stdlib::IS_CONSTANT,
715 .w2 = bb::stdlib::IS_CONSTANT,
716 .c1 = fr::zero(),
717 .c2 = fr::zero(),
719 .opcode_index = opcode_index,
720 };
721 auto it = linear_terms.begin();
722 entry.w1 = it->first;
723 entry.c1 = it->second;
724 if (++it != linear_terms.end()) {
725 entry.w2 = it->first;
726 entry.c2 = it->second;
727 }
728 return entry;
729}
730
732 const std::optional<BatchedEqEntry>& entry2)
733{
734 bool entry2_has_value = entry2.has_value();
736 .a = entry1.w1,
737 .b = entry1.w2,
738 .c = entry2_has_value ? entry2->w1 : bb::stdlib::IS_CONSTANT,
739 .d = entry2_has_value ? entry2->w2 : bb::stdlib::IS_CONSTANT,
740 .q_l = entry1.c1,
741 .q_r = entry1.c2,
742 .q_o = entry2_has_value ? entry2->c1 : fr::zero(),
743 .q_4 = entry2_has_value ? entry2->c2 : fr::zero(),
744 .q_c = entry1.q_c,
745 .q_m = entry2_has_value ? entry2->q_c : fr::zero(),
746 };
747}
748
750{
751 for (size_t i = 0; i + 1 < pending.size(); i += 2) {
752 af.batched_eq_check_constraints.push_back(build_batched_eq_check_constraint(pending[i], pending[i + 1]));
754 { pending[i].opcode_index, pending[i + 1].opcode_index });
755 }
756 if (pending.size() % 2 == 1) {
758 af.original_opcode_indices.batched_eq_check_constraints.push_back({ pending.back().opcode_index, SIZE_MAX });
759 }
760 pending.clear();
761}
762
764 AcirFormat& af,
765 size_t opcode_index,
766 std::vector<BatchedEqEntry>& batched_eq_assert_zeros,
767 bool is_mega)
768{
769 // Lambda to detect zero gates in mul_quad
770 auto is_zero_gate = [](const mul_quad_<fr>& gate) {
771 return ((gate.mul_scaling == fr(0)) && (gate.a_scaling == fr(0)) && (gate.b_scaling == fr(0)) &&
772 (gate.c_scaling == fr(0)) && (gate.d_scaling == fr(0)) && (gate.const_scaling == fr(0)));
773 };
774
775 // Lambda to detect zero gates in batched_eq
776 auto is_zero_batched_eq_gate = [](const BatchedEqEntry& gate) {
777 return (gate.c1 == fr(0) && gate.c2 == fr(0) && gate.q_c == fr(0));
778 };
779
780 auto linear_terms = process_linear_terms(arg.value);
781
782 // Check for unsatisfiable constraint: no variables but a non-zero constant means the circuit requires
783 // `constant == 0` which can never be satisfied.
784 if (arg.value.mul_terms.empty() && linear_terms.empty()) {
786 BB_ASSERT_EQ(constant,
787 fr::zero(),
788 "circuit is unsatisfiable. An AssertZero opcode contains no variables but has a non-zero "
789 "constant, which can never equal zero.");
790 }
791
792 // Classify the opcode, then route it to the matching handler. The bilinear / batched-eq gate is Mega-only, so
793 // classify_assert_zero only returns Bilinear/BatchedEq when is_mega is true.
794 AssertZeroGate gate = classify_assert_zero(arg.value, linear_terms, is_mega);
795
796 switch (gate) {
798 if (!is_mega) {
799 throw_or_abort("acir_format::assert_zero_to_constraint: selected AssertZeroGate::Bilinear variant "
800 "when using UltraCircuitBuilder.");
801 }
802 af.bilinear_constraints.push_back(build_bilinear_constraint(arg.value, linear_terms));
803 af.original_opcode_indices.bilinear_constraints.push_back(opcode_index);
804 break;
805 }
807 if (!is_mega) {
808 throw_or_abort("acir_format::assert_zero_to_constraint: selected AssertZeroGate::BatchedEq variant "
809 "when using UltraCircuitBuilder.");
810 }
811 batched_eq_assert_zeros.push_back(build_batched_eq_entry(arg.value, linear_terms, opcode_index));
812 BB_ASSERT(!is_zero_batched_eq_gate(batched_eq_assert_zeros.back()),
813 "acir_format::asser_zero_to_constraints: produced a BatcheqEq zero gate");
814 break;
815 }
817 std::vector<mul_quad_<fr>> mul_quads = split_into_mul_quad_gates(arg.value, linear_terms);
818 BB_ASSERT_EQ(mul_quads.size(), 1U, "acir_format::assert_zero_to_constraints: expected a single gate.");
819 BB_ASSERT(!is_zero_gate(mul_quads[0]),
820 "acir_format::assert_zero_to_constraints: produced a SingleArithmetic zero gate.");
821 af.quad_constraints.push_back(mul_quads[0]);
822 af.original_opcode_indices.quad_constraints.push_back(opcode_index);
823 break;
824 }
826 std::vector<mul_quad_<fr>> mul_quads = split_into_mul_quad_gates(arg.value, linear_terms);
828 mul_quads.size(), 1U, "acir_format::assert_zero_to_constraints: expected multiple gates but found one.");
829 for (auto const& mul_quad : mul_quads) {
830 BB_ASSERT(!is_zero_gate(mul_quad),
831 "acir_format::assert_zero_to_constraints: produced a MultiArithmetic zero gate.");
832 }
833 af.big_quad_constraints.push_back(BigQuadConstraint(mul_quads));
834 af.original_opcode_indices.big_quad_constraints.push_back(opcode_index);
835 break;
836 }
837 }
838}
839
841 AcirFormat& af,
842 size_t opcode_index)
843{
844 auto to_witness_or_constant = [](const Acir::FunctionInput& e) { return parse_input(e); };
845 auto to_witness = [](const Acir::Witness& e) { return e.value; };
846 auto to_witness_from_input = [](const Acir::FunctionInput& e) { return get_witness_from_function_input(e); };
847
848 std::visit(
849 overloaded{ [&](const Acir::BlackBoxFuncCall::AND& arg) {
851 .a = parse_input(arg.lhs),
852 .b = parse_input(arg.rhs),
853 .result = to_witness(arg.output),
854 .num_bits = arg.num_bits,
855 .is_xor_gate = false,
856 });
857 af.original_opcode_indices.logic_constraints.push_back(opcode_index);
858 },
859 [&](const Acir::BlackBoxFuncCall::XOR& arg) {
861 .a = parse_input(arg.lhs),
862 .b = parse_input(arg.rhs),
863 .result = to_witness(arg.output),
864 .num_bits = arg.num_bits,
865 .is_xor_gate = true,
866 });
867 af.original_opcode_indices.logic_constraints.push_back(opcode_index);
868 },
869 [&](const Acir::BlackBoxFuncCall::RANGE& arg) {
872 .num_bits = arg.num_bits,
873 });
874 af.original_opcode_indices.range_constraints.push_back(opcode_index);
875 },
878 .inputs = transform::map(arg.inputs, to_witness_or_constant),
879 .iv = transform::map(*arg.iv, to_witness_or_constant),
880 .key = transform::map(*arg.key, to_witness_or_constant),
881 .outputs = transform::map(arg.outputs, to_witness),
882 });
883 af.original_opcode_indices.aes128_constraints.push_back(opcode_index);
884 },
887 .inputs = transform::map(*arg.inputs, to_witness_or_constant),
888 .hash_values = transform::map(*arg.hash_values, to_witness_or_constant),
889 .result = transform::map(*arg.outputs, to_witness),
890 });
891 af.original_opcode_indices.sha256_compression.push_back(opcode_index);
892 },
893 [&](const Acir::BlackBoxFuncCall::Blake2s& arg) {
895 .inputs = transform::map(arg.inputs, to_witness_or_constant),
896 .result = transform::map(*arg.outputs, to_witness),
897 });
898 af.original_opcode_indices.blake2s_constraints.push_back(opcode_index);
899 },
900 [&](const Acir::BlackBoxFuncCall::Blake3& arg) {
902 .inputs = transform::map(arg.inputs, to_witness_or_constant),
903 .result = transform::map(*arg.outputs, to_witness),
904 });
905 af.original_opcode_indices.blake3_constraints.push_back(opcode_index);
906 },
910 .hashed_message = transform::map(*arg.hashed_message, to_witness_from_input),
911 .signature = transform::map(*arg.signature, to_witness_from_input),
912 .pub_x_indices = transform::map(*arg.public_key_x, to_witness_from_input),
913 .pub_y_indices = transform::map(*arg.public_key_y, to_witness_from_input),
914 .predicate = parse_input(arg.predicate),
915 .result = to_witness(arg.output),
916 });
917 af.original_opcode_indices.ecdsa_k1_constraints.push_back(opcode_index);
918 },
922 .hashed_message = transform::map(*arg.hashed_message, to_witness_from_input),
923 .signature = transform::map(*arg.signature, to_witness_from_input),
924 .pub_x_indices = transform::map(*arg.public_key_x, to_witness_from_input),
925 .pub_y_indices = transform::map(*arg.public_key_y, to_witness_from_input),
926 .predicate = parse_input(arg.predicate),
927 .result = to_witness(arg.output),
928 });
929 af.original_opcode_indices.ecdsa_r1_constraints.push_back(opcode_index);
930 },
933 .points = transform::map(arg.points, to_witness_or_constant),
934 .scalars = transform::map(arg.scalars, to_witness_or_constant),
935 .predicate = parse_input(arg.predicate),
936 .out_point_x = to_witness((*arg.outputs)[0]),
937 .out_point_y = to_witness((*arg.outputs)[1]),
938 });
939 af.original_opcode_indices.multi_scalar_mul_constraints.push_back(opcode_index);
940 },
942 af.ec_add_constraints.push_back(EcAdd{
943 .input1_x = parse_input((*arg.input1)[0]),
944 .input1_y = parse_input((*arg.input1)[1]),
945 .input2_x = parse_input((*arg.input2)[0]),
946 .input2_y = parse_input((*arg.input2)[1]),
947 .predicate = parse_input(arg.predicate),
948 .result_x = to_witness((*arg.outputs)[0]),
949 .result_y = to_witness((*arg.outputs)[1]),
950 });
951 af.original_opcode_indices.ec_add_constraints.push_back(opcode_index);
952 },
954 af.keccak_permutations.push_back(Keccakf1600{
955 .state = transform::map(*arg.inputs, to_witness_or_constant),
956 .result = transform::map(*arg.outputs, to_witness),
957 });
958 af.original_opcode_indices.keccak_permutations.push_back(opcode_index);
959 },
961 auto predicate = parse_input(arg.predicate);
962 if (predicate.is_constant && predicate.value.is_zero()) {
963 // No constraint if the recursion is disabled
964 return;
965 }
966 auto c = RecursionConstraint{
967 .key = transform::map(arg.verification_key, to_witness_from_input),
968 .proof = transform::map(arg.proof, to_witness_from_input),
969 .public_inputs = transform::map(arg.public_inputs, to_witness_from_input),
970 .key_hash = get_witness_from_function_input(arg.key_hash),
971 .proof_type = arg.proof_type,
972 .predicate = predicate,
973 };
974
975 // Add the recursion constraint to the appropriate container based on proof type
976 switch (c.proof_type) {
977 case HONK_ZK:
978 case HONK:
979 case ROLLUP_HONK:
980 case ROOT_ROLLUP_HONK:
981 af.honk_recursion_constraints.push_back(c);
982 af.original_opcode_indices.honk_recursion_constraints.push_back(opcode_index);
983 break;
984 case OINK:
985 case HN:
986 case HN_FINAL:
987 af.hn_recursion_constraints.push_back(c);
988 af.original_opcode_indices.hn_recursion_constraints.push_back(opcode_index);
989 break;
990 case AVM:
991 af.avm_recursion_constraints.push_back(c);
992 af.original_opcode_indices.avm_recursion_constraints.push_back(opcode_index);
993 break;
994 case CHONK:
995 af.chonk_recursion_constraints.push_back(c);
996 af.original_opcode_indices.chonk_recursion_constraints.push_back(opcode_index);
997 break;
998 default:
1000 "acir_format::handle_black_box_fun_call: Invalid PROOF_TYPE in RecursionConstraint.");
1001 }
1002 },
1005 .state = transform::map(arg.inputs, to_witness_or_constant),
1006 .result = transform::map(arg.outputs, to_witness),
1007 });
1008 af.original_opcode_indices.poseidon2_constraints.push_back(opcode_index);
1009 } },
1010 arg.value.value);
1011}
1012
1014{
1015 // Noir doesn't distinguish between ROM and RAM table. Therefore, we initialize every table as a ROM table, and
1016 // then we make it a RAM table if there is at least one write operation
1017 BlockConstraint block{
1018 .init = {},
1019 .trace = {},
1020 .type = BlockType::ROM,
1021 .calldata_id = CallDataType::None,
1022 };
1023
1024 for (const auto& init : mem_init.init) {
1025 block.init.push_back(init.value);
1026 }
1027
1028 // Databus is only supported for Goblin, non Goblin builders will treat call_data and return_data as normal
1029 // array.
1031 uint32_t calldata_id = std::get<Acir::BlockType::CallData>(mem_init.block_type.value).value;
1032 BB_ASSERT_LTE(calldata_id,
1033 MAX_APPS_PER_KERNEL,
1034 "acir_format::handle_memory_init: calldata id exceeds kernel + MAX_APPS_PER_KERNEL app columns");
1035
1036 block.type = BlockType::CallData;
1037 block.calldata_id = static_cast<CallDataType>(calldata_id);
1039 block.type = BlockType::ReturnData;
1040 }
1041
1042 return block;
1043}
1044
1046{
1047 // Acir::MemOp::read is the serialized MemOpKind bool: false = Read, true = Write.
1048 AccessType access_type = mem_op.op.read ? AccessType::Write : AccessType::Read;
1049 if (access_type == AccessType::Write) {
1050 // We are not allowed to write on the databus
1052 // Mark the table as a RAM table
1053 block.type = BlockType::RAM;
1054 }
1055
1056 MemOp acir_mem_op = MemOp{
1057 .access_type = access_type,
1058 .index = mem_op.op.index.value,
1059 .value = mem_op.op.value.value,
1060 };
1061 block.trace.push_back(acir_mem_op);
1062}
1063
1065{
1066 // If there are more than NUM_WIRES distinct witnesses in the linear terms, then we need multiple arithmetic gates
1067 if (linear_terms.size() > NUM_WIRES) {
1068 return false;
1069 }
1070
1071 if (arg.mul_terms.size() > 1) {
1072 // If there is more than one multiplication gate, then we need multiple arithmetic gates
1073 return false;
1074 }
1075
1076 if (arg.mul_terms.size() == 1) {
1077 // In this case we have two witnesses coming from the multiplication term plus the linear terms.
1078 // We proceed as follows:
1079 // 0. Start from the assumption that all witnesses (from linear terms and multiplication) are distinct
1080 // 1. Check if the lhs and rhs witness in the multiplication are already contained in the linear terms
1081 // 2. Check if the lhs witness and the rhs witness are equal
1082 // 2.a If they are distinct, update the total number of witnesses to be added to wires according to result
1083 // of the check at step 1: each distinct witness already in the linear terms subtracts one from the
1084 // total
1085 // 2.b If they are equal, update the total number of witnesses to be added to wires according to result of
1086 // the check at step 1: if the witness is already in the linear terms, it removes one from the total
1087
1088 // Number of witnesses to be put in wires if the witnesses from the linear terms and the multiplication term are
1089 // all different
1090 size_t num_witnesses_to_be_put_in_wires = 2 + linear_terms.size();
1091
1092 uint32_t witness_idx_lhs = std::get<1>(arg.mul_terms[0]).value;
1093 uint32_t witness_idx_rhs = std::get<2>(arg.mul_terms[0]).value;
1094
1095 bool lhs_is_distinct_from_linear_terms = !linear_terms.contains(witness_idx_lhs);
1096 bool rhs_is_distinct_from_linear_terms = !linear_terms.contains(witness_idx_rhs);
1097
1098 if (witness_idx_lhs != witness_idx_rhs) {
1099 num_witnesses_to_be_put_in_wires -= lhs_is_distinct_from_linear_terms ? 0U : 1U;
1100 num_witnesses_to_be_put_in_wires -= rhs_is_distinct_from_linear_terms ? 0U : 1U;
1101 } else {
1102 num_witnesses_to_be_put_in_wires -= lhs_is_distinct_from_linear_terms ? 0U : 1U;
1103 }
1104
1105 return num_witnesses_to_be_put_in_wires <= NUM_WIRES;
1106 }
1107
1108 return linear_terms.size() <= NUM_WIRES;
1109}
1110
1112 const std::map<uint32_t, bb::fr>& linear_terms,
1113 bool is_mega)
1114{
1115 // The bilinear / batched-eq gate is Mega-only; prefer it over the standard arithmetic path when
1116 // the opcode fits.
1117 if (is_mega) {
1118 if (is_bilinear(arg, linear_terms)) {
1120 }
1121 if (is_batched_eq(arg, linear_terms)) {
1123 }
1124 }
1127}
1128
1130{
1131 std::map<uint32_t, bb::fr> linear_terms;
1132 for (const auto& linear_term : expr.linear_combinations) {
1133 fr selector_value = from_buffer_with_bound_checks(std::get<0>(linear_term));
1134 uint32_t witness_idx = std::get<1>(linear_term).value;
1135 if (linear_terms.contains(witness_idx)) {
1136 linear_terms[witness_idx] += selector_value; // Accumulate coefficients for duplicate witnesses
1137 } else {
1138 linear_terms[witness_idx] = selector_value;
1139 }
1140 }
1141 return linear_terms;
1142}
1143
1144} // namespace acir_format
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:113
#define BB_ASSERT_NEQ(actual, expected,...)
Definition assert.hpp:98
#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...
FF a
FF b
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, bool is_mega)
Convert an Acir::Circuit into an AcirFormat by processing all the opcodes.
bool resolve_shared_wire_products(Acir::Expression const &arg, uint32_t &w_l, uint32_t &w_r, uint32_t &w_o)
Given an Arithmetic expression with two multiplication terms, determine whether they share a witness ...
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.
bool is_batched_eq(Acir::Expression const &arg, const std::map< uint32_t, bb::fr > &linear_terms)
Whether an AssertZero is "batched-eq" — a pure linear constraint with 1 or 2 witnesses.
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 ========= ///
AssertZeroGate classify_assert_zero(Acir::Expression const &arg, const std::map< uint32_t, bb::fr > &linear_terms, bool is_mega)
Classify an Acir::Expression with its processed linear terms into the gate it lowers to.
AcirFormat circuit_buf_to_acir_format(std::vector< uint8_t > &&buf, bool is_mega)
Convert a buffer representing a circuit into Barretenberg's internal AcirFormat representation.
BatchedEqCheckConstraint build_batched_eq_check_constraint(const BatchedEqEntry &entry1, const std::optional< BatchedEqEntry > &entry2)
Build a BATCHED_EQ row from batched-eq halves.
AcirFormat circuit_buf_to_mega_acir_format(std::vector< uint8_t > &&buf)
Specialization for Mega constructor.
std::vector< bb::fr > WitnessVector
void batched_eq_assert_zeros_into_constraints(AcirFormat &af, std::vector< BatchedEqEntry > &pending)
Pair buffered batched-eq AssertZeros into BATCHED_EQ rows (and emit any leftover as a single-half row...
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 ...
void assert_zero_to_constraints(Acir::Opcode::AssertZero const &arg, AcirFormat &af, size_t opcode_index, std::vector< BatchedEqEntry > &batched_eq_assert_zeros, bool is_mega)
Single entrypoint for processing arithmetic (AssertZero) opcodes.
AssertZeroGate
How an AssertZero opcode is lowered to gates.
BilinearConstraint build_bilinear_constraint(Acir::Expression const &arg, const std::map< uint32_t, bb::fr > &linear_terms)
Build the bilinear-gate constraint for an AssertZero already classified as Bilinear.
BlockConstraint memory_init_to_block_constraint(Acir::Opcode::MemoryInit const &mem_init)
========= MEMORY OPERATIONS ========== ///
bb::fr from_buffer_with_bound_checks(const std::vector< uint8_t > &buffer)
========= HELPERS ========= ///
BatchedEqEntry build_batched_eq_entry(Acir::Expression const &arg, const std::map< uint32_t, bb::fr > &linear_terms, size_t opcode_index)
Build the batched-eq entry for an AssertZero already classified as BatchedEq.
void add_blackbox_func_call_to_acir_format(Acir::Opcode::BlackBoxFuncCall const &arg, AcirFormat &af, size_t opcode_index)
bool is_bilinear(Acir::Expression const &arg, const std::map< uint32_t, bb::fr > &linear_terms)
Whether an AssertZero fits a single bilinear-gate row.
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:5259
std::variant< Memory, CallData, ReturnData > value
Definition acir.hpp:5733
Acir::PublicInputs return_values
Definition acir.hpp:7235
std::vector< Acir::Opcode > opcodes
Definition acir.hpp:7232
Acir::PublicInputs public_parameters
Definition acir.hpp:7234
std::vector< std::tuple< std::vector< uint8_t >, Acir::Witness > > linear_combinations
Definition acir.hpp:5856
std::vector< uint8_t > q_c
Definition acir.hpp:5857
std::vector< std::tuple< std::vector< uint8_t >, Acir::Witness, Acir::Witness > > mul_terms
Definition acir.hpp:5855
std::variant< Constant, Witness > value
Definition acir.hpp:4288
bool read
Definition acir.hpp:6273
Acir::Witness index
Definition acir.hpp:6274
Acir::Witness value
Definition acir.hpp:6275
Acir::Expression value
Definition acir.hpp:6330
Acir::BlackBoxFuncCall value
Definition acir.hpp:6348
std::vector< Acir::Witness > init
Definition acir.hpp:6415
Acir::BlockType block_type
Definition acir.hpp:6416
std::variant< AssertZero, BlackBoxFuncCall, MemoryOp, MemoryInit, BrilligCall, Call > value
Definition acir.hpp:6592
std::vector< Acir::Witness > value
Definition acir.hpp:7213
uint32_t value
Definition acir.hpp:4233
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< BatchedEqCheckConstraint > batched_eq_check_constraints
std::vector< Keccakf1600 > keccak_permutations
std::vector< BilinearConstraint > bilinear_constraints
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::array< size_t, 2 > > batched_eq_check_constraints
std::vector< std::vector< size_t > > block_constraints
BatchedEq constraint — BATCHED_EQ mode of the bilinear_batched_eq gate (see bilinear_or_batched_eq_ch...
Linear AssertZero (≤2 witnesses + constant)
bb::fr c2
uint32_t w2
bb::fr q_c
uint32_t w1
bb::fr c1
Bilinear constraint — BILINEAR mode of the bilinear_batched_eq gate (see bilinear_or_batched_eq_check...
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 field random_element(numeric::RNG *engine=nullptr) noexcept
static field serialize_from_buffer(const uint8_t *buffer)
static constexpr field zero()
void throw_or_abort(std::string const &err)
VectorField result