Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
acir_format.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
7#include "acir_format.hpp"
8
25
26#include <cstddef>
27#include <cstdint>
28#include <memory>
29
30namespace acir_format {
31
32using namespace bb;
33
34template <typename Builder>
35void build_constraints(Builder& builder, AcirFormat& constraints, const ProgramMetadata& metadata)
36{
37 bool collect_gates_per_opcode = metadata.collect_gates_per_opcode;
38
39 if (collect_gates_per_opcode) {
40 constraints.gates_per_opcode.resize(constraints.num_acir_opcodes, 0);
41 }
42
43 GateCounter gate_counter{ &builder, collect_gates_per_opcode };
44
45 // Size hints: reserve the row storage of the blocks whose growth is predictable from the ACIR
46 // constraint counts. Estimates only need to be the right order of magnitude — a single reserve
47 // replaces the log2(size) reallocation-and-copy steps of geometric growth. Gadget-generated
48 // gates (hashes, ECDSA, recursion) are excluded; those blocks still grow but from a warmer start.
49 constexpr size_t GATES_PER_BIG_QUAD_HINT = 4; // a big quad expands to a short chain of arith gates
50 constexpr size_t GATES_PER_SMALL_RANGE_HINT = 3; // delta-range rows per range constraint, order of magnitude
51 constexpr size_t ARITH_RESERVE_SLACK = 1024; // headroom for gadget-generated arith gates
52 constexpr size_t BLOCK_RESERVE_FLOOR = 512; // warm start for blocks that grow only via gadgets
53 builder.blocks.arithmetic.reserve(constraints.quad_constraints.size() + constraints.bilinear_constraints.size() +
54 constraints.batched_eq_check_constraints.size() +
55 GATES_PER_BIG_QUAD_HINT * constraints.big_quad_constraints.size() +
56 ARITH_RESERVE_SLACK);
57 builder.blocks.delta_range.reserve(GATES_PER_SMALL_RANGE_HINT * constraints.range_constraints.size() +
58 BLOCK_RESERVE_FLOOR);
59 builder.blocks.memory.reserve(BLOCK_RESERVE_FLOOR);
60 builder.blocks.nnf.reserve(BLOCK_RESERVE_FLOOR);
61
62 // Add standard width-4 Ultra arithmetic gates
63 {
64 BB_BENCH_ONLY_NAME("acir::quad_constraints");
65 for (auto [constraint, opcode_idx] :
68 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
69 }
70 }
71
72 // When an expression doesn't fit into a single width-4 gate, we split it across multiple gates and we leverage
73 // w4_shift to use the least possible number of intermediate witnesses. See the documentation of
74 // split_into_mul_quad_gates for more information.
75 {
76 BB_BENCH_ONLY_NAME("acir::big_quad_constraints");
77 for (auto [big_constraint, opcode_idx] :
79 create_big_quad_constraint(builder, big_constraint);
80 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
81 }
82 }
83
84 // Bilinear rows: AssertZero opcodes with two products routed to the bilinear gate (Mega-only; see
85 // bilinear_or_batched_eq_check_relation.hpp). Each row originates from a single AssertZero.
87 "Bilinear constraints should only be present when using MegaCircuitBuilder.");
88 {
89 BB_BENCH_ONLY_NAME("acir::bilinear_constraints");
90 for (auto [constraint, opcode_idx] :
93 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
94 }
95 }
96
97 // BatchedEq rows: pairs of linear AssertZeros batched into one row (Mega-only). Each row originates from 1
98 // (unpaired single-half) or 2 (paired) AssertZero opcodes.
100 "BatchedEq constraints should only be present when using MegaCircuitBuilder.");
101 {
102 BB_BENCH_ONLY_NAME("acir::batched_eq_check_constraints");
103 for (auto [constraint, opcode_idx] :
107 // For paired BATCHED_EQ rows, the single emitted gate is shared between two opcodes; attribute the
108 // delta only to the first (track_diff would record 0 for the second anyway since no new gates
109 // were added between the calls).
110 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx[0]);
111 }
112 }
113
114 // Add logic constraint
115 {
116 BB_BENCH_ONLY_NAME("acir::logic_constraints");
117 for (const auto& [constraint, opcode_idx] :
120 builder, constraint.a, constraint.b, constraint.result, constraint.num_bits, constraint.is_xor_gate);
121 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
122 }
123 }
124
125 // Add range constraint
126 {
127 BB_BENCH_ONLY_NAME("acir::range_constraints");
128 for (const auto& [constraint, opcode_idx] :
130 // The failure message is only consumed when the witness is out of range; formatting it eagerly
131 // dominates the cost of this loop, so only build it for values that will actually fail.
132 std::string msg;
133 if (uint256_t(builder.get_variable(constraint.witness)).get_msb() >= constraint.num_bits) {
134 msg = std::format("acir_format::build_constraints: range constraint at opcode index {} failed",
135 opcode_idx);
136 }
137 builder.create_dyadic_range_constraint(constraint.witness, constraint.num_bits, msg);
138 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
139 }
140 }
141
142 // Add aes128 constraints
143 {
144 BB_BENCH_ONLY_NAME("acir::aes128_constraints");
145 for (const auto& [constraint, opcode_idx] :
148 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
149 }
150 }
151
152 // Add sha256 constraints
153 {
154 BB_BENCH_ONLY_NAME("acir::sha256_compression");
155 for (const auto& [constraint, opcode_idx] :
158 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
159 }
160 }
161
162 // Add ECDSA k1 constraints
163 {
164 BB_BENCH_ONLY_NAME("acir::ecdsa_k1_constraints");
165 for (const auto& [constraint, opcode_idx] :
167 create_ecdsa_verify_constraints<stdlib::secp256k1<Builder>>(builder, constraint);
168 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
169 }
170 }
171
172 // Add ECDSA r1 constraints
173 {
174 BB_BENCH_ONLY_NAME("acir::ecdsa_r1_constraints");
175 for (const auto& [constraint, opcode_idx] :
177 create_ecdsa_verify_constraints<stdlib::secp256r1<Builder>>(builder, constraint);
178 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
179 }
180 }
181 // Add blake2s constraints
182 {
183 BB_BENCH_ONLY_NAME("acir::blake2s_constraints");
184 for (const auto& [constraint, opcode_idx] :
187 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
188 }
189 }
190
191 // Add blake3 constraints
192 {
193 BB_BENCH_ONLY_NAME("acir::blake3_constraints");
194 for (const auto& [constraint, opcode_idx] :
197 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
198 }
199 }
200
201 // Add keccak permutations
202 {
203 BB_BENCH_ONLY_NAME("acir::keccak_permutations");
204 for (const auto& [constraint, opcode_idx] :
207 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
208 }
209 }
210
211 // Add poseidon2 constraints
212 {
213 BB_BENCH_ONLY_NAME("acir::poseidon2_constraints");
214 for (const auto& [constraint, opcode_idx] :
217 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
218 }
219 }
220
221 // Add multi scalar mul constraints
222 {
223 BB_BENCH_ONLY_NAME("acir::multi_scalar_mul_constraints");
224 for (const auto& [constraint, opcode_idx] :
228 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
229 }
230 }
231
232 // Add ec add constraints
233 {
234 BB_BENCH_ONLY_NAME("acir::ec_add_constraints");
235 for (const auto& [constraint, opcode_idx] :
238 gate_counter.track_diff(constraints.gates_per_opcode, opcode_idx);
239 }
240 }
241
242 // Add block constraints
243 {
244 BB_BENCH_ONLY_NAME("acir::block_constraints");
245 for (const auto& [constraint, opcode_indices] :
248 if (collect_gates_per_opcode) {
249 // Each block constraint may correspond to multiple opcodes, so we record the average number of gates
250 // added by the entire constraint as the number of gates for each opcode.
251 size_t avg_gates_per_opcode = gate_counter.compute_diff() / opcode_indices.size();
252 for (size_t opcode_index : opcode_indices) {
253 constraints.gates_per_opcode[opcode_index] = avg_gates_per_opcode;
254 }
255 }
256 }
257 }
258
259 // RecursionConstraints
260 BB_BENCH_ONLY_NAME("acir::recursion_constraints");
261 const bool is_hn_recursion_constraints = !constraints.hn_recursion_constraints.empty();
262 HonkRecursionConstraintsOutput<Builder> output = create_recursion_constraints<Builder>(
263 builder,
264 gate_counter,
265 constraints.gates_per_opcode,
266 metadata.ivc,
267 /*honk_recursion_data=*/
268 { constraints.honk_recursion_constraints, constraints.original_opcode_indices.honk_recursion_constraints },
269 /*avm_recursion_data=*/
270 { constraints.avm_recursion_constraints, constraints.original_opcode_indices.avm_recursion_constraints },
271 /*hn_recursion_data=*/
272 { constraints.hn_recursion_constraints, constraints.original_opcode_indices.hn_recursion_constraints },
273 /*chonk_recursion_data=*/
274 { constraints.chonk_recursion_constraints, constraints.original_opcode_indices.chonk_recursion_constraints });
275
276 // Process the result of adding recursion constraints and propagate the public inputs as needed
277 output.finalize(builder, is_hn_recursion_constraints, metadata.has_ipa_claim);
278}
279
286template <> UltraCircuitBuilder create_circuit(AcirProgram& program, const ProgramMetadata& metadata)
287{
288 BB_BENCH();
289 AcirFormat& constraints = program.constraints;
290 WitnessVector& witness = program.witness;
291 const bool is_write_vk_mode = witness.empty();
292
293 if (!is_write_vk_mode) {
294 BB_ASSERT_EQ(witness.size(),
295 constraints.max_witness_index + 1,
296 "ACIR witness size (" << witness.size() << ") does not match max witness index + 1 ("
297 << (constraints.max_witness_index + 1) << ").");
298 } else {
299 witness.resize(constraints.max_witness_index + 1, 0);
300 }
301
302 UltraCircuitBuilder builder{ witness, constraints.public_inputs, is_write_vk_mode };
303
304 // Populate constraints in the builder
305 build_constraints(builder, constraints, metadata);
306
307 vinfo("Created circuit");
308
309 return builder;
310};
311
318template <> MegaCircuitBuilder create_circuit(AcirProgram& program, const ProgramMetadata& metadata)
319{
320 BB_BENCH();
321 AcirFormat& constraints = program.constraints;
322 WitnessVector& witness = program.witness;
323 const bool is_write_vk_mode = witness.empty();
324
325 if (!is_write_vk_mode) {
326 BB_ASSERT_EQ(witness.size(),
327 constraints.max_witness_index + 1,
328 "ACIR witness size (" << witness.size() << ") does not match max witness index + 1 ("
329 << (constraints.max_witness_index + 1) << ").");
330 } else {
331 witness.resize(constraints.max_witness_index + 1, 0);
332 }
333
334 auto op_queue = (metadata.ivc == nullptr) ? std::make_shared<ECCOpQueue>() : metadata.ivc->get_goblin().op_queue;
335
336 // Construct a builder using the witness and public input data from acir and with the goblin-owned op_queue
337 MegaCircuitBuilder builder{ op_queue, witness, constraints.public_inputs, is_write_vk_mode };
338
339 // Populate constraints in the builder
340 build_constraints(builder, constraints, metadata);
341
342 vinfo("Created circuit");
343
344 return builder;
345};
346
349
350} // namespace acir_format
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define BB_BENCH()
Definition bb_bench.hpp:268
#define BB_BENCH_ONLY_NAME(name)
Definition bb_bench.hpp:257
Shared type definitions for the Barretenberg RPC API.
Utility class for tracking the gate count of acir constraints.
constexpr uint64_t get_msb() const
#define vinfo(...)
Definition log.hpp:94
AluTraceBuilder builder
Definition alu.test.cpp:124
Base class templates shared across Honk flavors.
void create_big_quad_constraint(Builder &builder, BigQuadConstraint &big_constraint)
template void build_constraints< MegaCircuitBuilder >(MegaCircuitBuilder &, AcirFormat &, const ProgramMetadata &)
void create_batched_eq_check_constraint(Builder &builder, const BatchedEqCheckConstraint &constraint)
Emit a BATCHED_EQ-mode bilinear_batched_eq gate row described by constraint on builder.
template void build_constraints< UltraCircuitBuilder >(UltraCircuitBuilder &, AcirFormat &, const ProgramMetadata &)
void create_blake3_constraints(Builder &builder, const Blake3Constraint &constraint)
std::vector< bb::fr > WitnessVector
void create_keccak_permutations_constraints(Builder &builder, const Keccakf1600 &constraint)
void create_ec_add_constraint(Builder &builder, const EcAdd &input)
Create constraints for addition of two points on the Grumpkin curve.
void build_constraints(Builder &builder, AcirFormat &constraints, const ProgramMetadata &metadata)
Add to the builder the constraints contained in an AcirFormat instance.
void create_blake2s_constraints(Builder &builder, const Blake2sConstraint &constraint)
void create_block_constraints(UltraCircuitBuilder &builder, const BlockConstraint &constraint)
Create block constraints; Specialization for Ultra arithmetization.
UltraCircuitBuilder create_circuit(AcirProgram &program, const ProgramMetadata &metadata)
Specialization for creating an Ultra circuit from an acir program.
void create_sha256_compression_constraints(Builder &builder, const Sha256Compression &constraint)
void create_aes128_constraints(Builder &builder, const AES128Constraint &constraint)
void create_multi_scalar_mul_constraint(Builder &builder, const MultiScalarMul &constraint_input)
Create constraints for multi-scalar multiplication on the Grumpkin curve.
void create_poseidon2_permutations_constraints(Builder &builder, const Poseidon2Constraint &constraint)
void create_quad_constraint(Builder &builder, QuadConstraint &mul_quad)
Create a simple width-4 Ultra arithmetic gate constraint representing the equation.
void create_bilinear_constraint(Builder &builder, const BilinearConstraint &constraint)
Emit a BILINEAR-mode bilinear_batched_eq gate row.
void create_logic_gate(Builder &builder, const WitnessOrConstant< bb::fr > a, const WitnessOrConstant< bb::fr > b, const uint32_t result, const size_t num_bits, const bool is_xor_gate)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
StdlibCodec for in-circuit (recursive) verification transcript handling.
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< 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< size_t > gates_per_opcode
std::vector< std::array< size_t, 2 > > batched_eq_check_constraints
std::vector< std::vector< size_t > > block_constraints
Struct containing both the constraints to be added to the circuit and the witness vector.
Container for the output of multiple recursive verifications.
void finalize(Builder &builder, bool is_hn_recursion_constraints=false, bool has_ipa_claim=false)
Finalize the output by accumulating IPA claims/proofs, performing full IPA verification,...
Metadata required to create a circuit.
std::shared_ptr< bb::Chonk > ivc