Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
gate_patterns.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Planned, auditors: [], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
9#include <cstddef>
10#include <cstdint>
11#include <functional>
12#include <string_view>
13#include <vector>
14
16
17using bb::GateKind;
19
20enum class Wire : uint8_t {
21 W_L,
22 W_R,
23 W_O,
24 W_4,
29};
30
38struct Selectors {
39 // The gate selector value (q_arith, q_elliptic, etc. depending on block)
40 int64_t gate_selector = 0;
41
42 // Secondary selectors (can be arbitrary field elements, so track non-zero)
43 bool q_m_nz = false;
44 bool q_1_nz = false;
45 bool q_2_nz = false;
46 bool q_3_nz = false;
47 bool q_4_nz = false;
48 bool q_c_nz = false;
49};
50
51using Predicate = std::function<bool(const Selectors&)>;
52
53struct WireSpec {
55 Predicate condition = [](const Selectors&) { return true; };
56};
57
65 std::string_view name;
67};
68
69// ============================================================================
70// Arithmetic Pattern (from ultra_arithmetic_relation.hpp)
71//
72// Subrelation 1:
73// q_arith * [ (-1/2) * (q_arith - 3) * (q_m * w_1 * w_2)
74// + q_1*w_1 + q_2*w_2 + q_3*w_3 + q_4*w_4 + q_c
75// + (q_arith - 1) * w_4_shift ]
76//
77// Subrelation 2 (only when q_arith == 3):
78// q_arith * (q_arith-1) * (q_arith-2) * (w_1 + w_4 - w_1_shift + q_m)
79//
80// gate_selector = q_arith
81// ============================================================================
82
83inline const GatePattern
84 ARITHMETIC = { .name = "arithmetic",
85 .wires = {
86 // w_l: linear term OR mul term (disabled when q_arith==3) OR subrel2
87 { Wire::W_L,
88 [](const Selectors& sel) {
89 return sel.q_1_nz || (sel.q_m_nz && sel.gate_selector != 3) || sel.gate_selector == 3;
90 } },
91 // w_r: linear term OR mul term (disabled when q_arith==3)
92 { Wire::W_R,
93 [](const Selectors& sel) { return sel.q_2_nz || (sel.q_m_nz && sel.gate_selector != 3); } },
94 // w_o: linear term
95 { Wire::W_O, [](const Selectors& sel) { return sel.q_3_nz; } },
96 // w_4: linear term OR subrel2 (subrel2 active when q_arith == 3)
97 { Wire::W_4, [](const Selectors& sel) { return sel.q_4_nz || sel.gate_selector == 3; } },
98 // w_4_shift: when q_arith == 2 or 3 (subrel1 has (q_arith - 1) * w_4_shift)
100 [](const Selectors& sel) { return sel.gate_selector == 2 || sel.gate_selector == 3; } },
101 // w_l_shift: subrel2 when q_arith == 3
102 { Wire::W_L_SHIFT, [](const Selectors& sel) { return sel.gate_selector == 3; } },
103 } };
104
105// ============================================================================
106// Elliptic Pattern (from elliptic_relation.hpp)
107//
108// Point addition (q_is_double == 0, i.e., q_m == 0):
109// x1 = w_r, y1 = w_o, x2 = w_l_shift, x3 = w_r_shift, y3 = w_o_shift, y2 = w_4_shift
110//
111// Point doubling (q_is_double == 1, i.e., q_m == 1):
112// x1 = w_r, y1 = w_o, x3 = w_r_shift, y3 = w_o_shift
113//
114// gate_selector = q_elliptic
115// ============================================================================
116
117inline const GatePattern ELLIPTIC = { .name = "elliptic",
118 .wires = {
119 // x1, y1: always used (both addition and doubling)
120 { Wire::W_R, [](const Selectors&) { return true; } },
121 { Wire::W_O, [](const Selectors&) { return true; } },
122 // x3, y3: always used (both addition and doubling)
123 { Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
124 { Wire::W_O_SHIFT, [](const Selectors&) { return true; } },
125 // x2: only for addition (q_m == 0)
126 { Wire::W_L_SHIFT, [](const Selectors& sel) { return !sel.q_m_nz; } },
127 // y2: only for addition (q_m == 0)
128 { Wire::W_4_SHIFT, [](const Selectors& sel) { return !sel.q_m_nz; } },
129 } };
130
131// ============================================================================
132// Non-Native Field Pattern (from non_native_field_relation.hpp)
133//
134// | gate type | q_2 | q_3 | q_4 | q_m | wires constrained |
135// |---------------|-----|-----|-----|-----|------------------------------------------------|
136// | Limb Accum 1 | 0 | 1 | 1 | 0 | w_l, w_r, w_o, w_4, w_l', w_r' |
137// | Limb Accum 2 | 0 | 1 | 0 | 1 | w_o, w_4, w_l', w_r', w_o', w_4' |
138// | Product 1 | 1 | 1 | 0 | 0 | w_l, w_r, w_o, w_4, w_l', w_r' |
139// | Product 2 | 1 | 0 | 1 | 0 | all 8 wires |
140// | Product 3 | 1 | 0 | 0 | 1 | w_l, w_r, w_4, w_l', w_r', w_o', w_4' |
141//
142// gate_selector = q_nnf
143// ============================================================================
144
145namespace nnf_helpers {
146// Limb Accum 2: !q_2 && q_3 && !q_4 && q_m
147inline bool is_limb_accum_2(const Selectors& sel)
148{
149 return !sel.q_2_nz && sel.q_3_nz && !sel.q_4_nz && sel.q_m_nz;
150}
151// Product 3: q_2 && !q_3 && !q_4 && q_m
152inline bool is_product_3(const Selectors& sel)
153{
154 return sel.q_2_nz && !sel.q_3_nz && !sel.q_4_nz && sel.q_m_nz;
155}
156} // namespace nnf_helpers
157
158inline const GatePattern
159 NON_NATIVE_FIELD = { .name = "non_native_field",
160 .wires = {
161 // w_l, w_r: all gates except Limb Accum 2
162 { Wire::W_L, [](const Selectors& sel) { return !nnf_helpers::is_limb_accum_2(sel); } },
163 { Wire::W_R, [](const Selectors& sel) { return !nnf_helpers::is_limb_accum_2(sel); } },
164 // w_o: all gates except Product 3
165 { Wire::W_O, [](const Selectors& sel) { return !nnf_helpers::is_product_3(sel); } },
166 // w_4: all gates
167 { Wire::W_4, [](const Selectors&) { return true; } },
168 // w_l_shift, w_r_shift: all gates
169 { Wire::W_L_SHIFT, [](const Selectors&) { return true; } },
170 { Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
171 // w_o_shift, w_4_shift: Limb Accum 2, Product 2, Product 3
172 // = q_m || (q_2 && q_4)
174 [](const Selectors& sel) { return sel.q_m_nz || (sel.q_2_nz && sel.q_4_nz); } },
176 [](const Selectors& sel) { return sel.q_m_nz || (sel.q_2_nz && sel.q_4_nz); } },
177 } };
178
179// ============================================================================
180// Memory Pattern (from memory_relation.hpp)
181//
182// | gate type | q_1 | q_2 | q_3 | q_4 | q_m | wires constrained |
183// |---------------------|-----|-----|-----|-----|-----|------------------------------|
184// | RAM/ROM access | 1 | 0 | 0 | 0 | 1 | w_l, w_r, w_o, w_4 |
185// | RAM timestamp check | 1 | 0 | 0 | 1 | 0 | w_l, w_r, w_o, w_l', w_r' |
186// | ROM consistency | 1 | 1 | 0 | 0 | 0 | w_l, w_l', w_4, w_4' |
187// | RAM consistency | 0 | 0 | 1 | 0 | 0 | all 8 wires |
188//
189// gate_selector = q_memory
190// ============================================================================
191
192namespace memory_helpers {
193// RAM timestamp check: q_1 && q_4
194inline bool is_timestamp_check(const Selectors& sel)
195{
196 return sel.q_1_nz && sel.q_4_nz;
197}
198// ROM consistency: q_1 && q_2
199inline bool is_rom_consistency(const Selectors& sel)
200{
201 return sel.q_1_nz && sel.q_2_nz;
202}
203// RAM consistency: q_3
204inline bool is_ram_consistency(const Selectors& sel)
205{
206 return sel.q_3_nz;
207}
208} // namespace memory_helpers
209
210// Note: Access gates (q_1 && q_m) are NOT handled here - they're processed separately
211// via ROM/RAM transcript methods.
212inline const GatePattern
213 MEMORY = { .name = "memory",
214 .wires = {
215 { Wire::W_L,
216 [](const Selectors& sel) {
219 } },
220 { Wire::W_R,
221 [](const Selectors& sel) {
224 } },
225 { Wire::W_O,
226 [](const Selectors& sel) {
229 } },
230 { Wire::W_4,
231 [](const Selectors& sel) {
233 } },
235 [](const Selectors& sel) {
238 } },
240 [](const Selectors& sel) {
242 } },
243 { Wire::W_O_SHIFT, [](const Selectors& sel) { return memory_helpers::is_ram_consistency(sel); } },
245 [](const Selectors& sel) {
247 } },
248 } };
249
250// ============================================================================
251// Lookup Pattern (from logderiv_lookup_relation.hpp)
252//
253// The read term uses: w_l, w_r, w_o (always)
254// Shifted wires used when step_size != 0:
255// w_l_shift if q_2 (q_r) != 0
256// w_r_shift if q_m != 0
257// w_o_shift if q_c != 0
258//
259// gate_selector = q_lookup
260// ============================================================================
261
262inline const GatePattern LOOKUP = { .name = "lookup",
263 .wires = {
264 { Wire::W_L, [](const Selectors&) { return true; } },
265 { Wire::W_R, [](const Selectors&) { return true; } },
266 { Wire::W_O, [](const Selectors&) { return true; } },
267 { Wire::W_L_SHIFT, [](const Selectors& sel) { return sel.q_2_nz; } },
268 { Wire::W_R_SHIFT, [](const Selectors& sel) { return sel.q_m_nz; } },
269 { Wire::W_O_SHIFT, [](const Selectors& sel) { return sel.q_c_nz; } },
270 } };
271
272// ============================================================================
273// Delta Range Pattern (from delta_range_constraint_relation.hpp)
274//
275// D_0 = w_2 - w_1, D_1 = w_3 - w_2, D_2 = w_4 - w_3, D_3 = w_1_shift - w_4
276//
277// gate_selector = q_delta_range
278// ============================================================================
279
280inline const GatePattern DELTA_RANGE = { .name = "delta_range",
281 .wires = {
282 { Wire::W_L, [](const Selectors&) { return true; } },
283 { Wire::W_R, [](const Selectors&) { return true; } },
284 { Wire::W_O, [](const Selectors&) { return true; } },
285 { Wire::W_4, [](const Selectors&) { return true; } },
286 { Wire::W_L_SHIFT, [](const Selectors&) { return true; } },
287 } };
288
289// ============================================================================
290// Poseidon2 Internal Pattern (from poseidon2_internal_relation.hpp)
291//
292// All 4 current wires and all 4 shifted wires are constrained
293//
294// gate_selector = q_poseidon2_internal
295// ============================================================================
296
297inline const GatePattern POSEIDON2_INTERNAL = { .name = "poseidon2_internal",
298 .wires = {
299 { Wire::W_L, [](const Selectors&) { return true; } },
300 { Wire::W_R, [](const Selectors&) { return true; } },
301 { Wire::W_O, [](const Selectors&) { return true; } },
302 { Wire::W_4, [](const Selectors&) { return true; } },
303 { Wire::W_L_SHIFT, [](const Selectors&) { return true; } },
304 { Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
305 { Wire::W_O_SHIFT, [](const Selectors&) { return true; } },
306 { Wire::W_4_SHIFT, [](const Selectors&) { return true; } },
307 } };
308
309// ============================================================================
310// Poseidon2 External Pattern (from poseidon2_external_relation.hpp)
311//
312// All 4 current wires and all 4 shifted wires are constrained
313//
314// gate_selector = q_poseidon2_external
315// ============================================================================
316
317inline const GatePattern POSEIDON2_EXTERNAL = { .name = "poseidon2_external",
318 .wires = {
319 { Wire::W_L, [](const Selectors&) { return true; } },
320 { Wire::W_R, [](const Selectors&) { return true; } },
321 { Wire::W_O, [](const Selectors&) { return true; } },
322 { Wire::W_4, [](const Selectors&) { return true; } },
323 { Wire::W_L_SHIFT, [](const Selectors&) { return true; } },
324 { Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
325 { Wire::W_O_SHIFT, [](const Selectors&) { return true; } },
326 { Wire::W_4_SHIFT, [](const Selectors&) { return true; } },
327 } };
328
329// ============================================================================
330// Poseidon2 Initial External Pattern (from poseidon2_initial_external_relation.hpp)
331//
332// All 4 current wires and all 4 shifted wires are constrained
333//
334// gate_selector = q_poseidon2_external_initial
335// ============================================================================
336
337inline const GatePattern POSEIDON2_INITIAL_EXTERNAL = { .name = "poseidon2_initial_external",
338 .wires = {
339 { Wire::W_L, [](const Selectors&) { return true; } },
340 { Wire::W_R, [](const Selectors&) { return true; } },
341 { Wire::W_O, [](const Selectors&) { return true; } },
342 { Wire::W_4, [](const Selectors&) { return true; } },
343 { Wire::W_L_SHIFT, [](const Selectors&) { return true; } },
344 { Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
345 { Wire::W_O_SHIFT, [](const Selectors&) { return true; } },
346 { Wire::W_4_SHIFT, [](const Selectors&) { return true; } },
347 } };
348
349// ============================================================================
350// Poseidon2 Quad-Internal Pattern (from poseidon2_quad_internal_relation.hpp)
351//
352// gate_selector = q_poseidon2_quad_internal
353// ============================================================================
354
355inline const GatePattern POSEIDON2_QUAD_INTERNAL = { .name = "poseidon2_quad_internal",
356 .wires = {
357 { Wire::W_L, [](const Selectors&) { return true; } },
358 { Wire::W_R, [](const Selectors&) { return true; } },
359 { Wire::W_O, [](const Selectors&) { return true; } },
360 { Wire::W_4, [](const Selectors&) { return true; } },
361 { Wire::W_L_SHIFT, [](const Selectors&) { return true; } },
362 { Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
363 { Wire::W_O_SHIFT, [](const Selectors&) { return true; } },
364 { Wire::W_4_SHIFT, [](const Selectors&) { return true; } },
365 } };
366
367// ============================================================================
368// Poseidon2 Quad-Internal Terminal Pattern
369// (from poseidon2_quad_internal_terminal_relation.hpp)
370//
371// gate_selector = q_poseidon2_quad_internal_terminal
372// ============================================================================
373
374inline const GatePattern
375 POSEIDON2_QUAD_INTERNAL_TERMINAL = { .name = "poseidon2_quad_internal_terminal",
376 .wires = {
377 { Wire::W_L, [](const Selectors&) { return true; } },
378 { Wire::W_R, [](const Selectors&) { return true; } },
379 { Wire::W_O, [](const Selectors&) { return true; } },
380 { Wire::W_4, [](const Selectors&) { return true; } },
381 { Wire::W_L_SHIFT, [](const Selectors&) { return true; } },
382 { Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
383 { Wire::W_O_SHIFT, [](const Selectors&) { return true; } },
384 { Wire::W_4_SHIFT, [](const Selectors&) { return true; } },
385 } };
386
387// ============================================================================
388// Poseidon2 Transition Entry Pattern (from poseidon2_transition_entry_relation.hpp)
389//
390// gate_selector = q_poseidon2_transition_entry
391// ============================================================================
392
393inline const GatePattern POSEIDON2_TRANSITION_ENTRY = { .name = "poseidon2_transition_entry",
394 .wires = {
395 { Wire::W_L, [](const Selectors&) { return true; } },
396 { Wire::W_R, [](const Selectors&) { return true; } },
397 { Wire::W_O, [](const Selectors&) { return true; } },
398 { Wire::W_4, [](const Selectors&) { return true; } },
399 { Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
400 { Wire::W_O_SHIFT, [](const Selectors&) { return true; } },
401 { Wire::W_4_SHIFT, [](const Selectors&) { return true; } },
402 } };
403
404// ============================================================================
405// Databus Pattern (from databus_lookup_relation.hpp)
406//
407// Read term uses: w_l (value), w_r (index)
408//
409// gate_selector = q_busread
410// ============================================================================
411
412inline const GatePattern DATABUS = { .name = "databus",
413 .wires = {
414 { Wire::W_L, [](const Selectors&) { return true; } },
415 { Wire::W_R, [](const Selectors&) { return true; } },
416 } };
417
418// ============================================================================
419// Helper functions
420// ============================================================================
421
422template <typename Block> Selectors read_selectors(Block& block, size_t gate_index, GateKind kind)
423{
424 return Selectors{
425 .gate_selector = static_cast<int64_t>(static_cast<uint64_t>(read_gate_selector(block, kind, gate_index))),
426 .q_m_nz = !block.q_m()[gate_index].is_zero(),
427 .q_1_nz = !block.q_1()[gate_index].is_zero(),
428 .q_2_nz = !block.q_2()[gate_index].is_zero(),
429 .q_3_nz = !block.q_3()[gate_index].is_zero(),
430 .q_4_nz = !block.q_4()[gate_index].is_zero(),
431 .q_c_nz = !block.q_c()[gate_index].is_zero(),
432 };
433}
434
435template <typename Block> uint32_t get_wire(Block& block, size_t gate_index, Wire wire)
436{
437 switch (wire) {
438 case Wire::W_L:
439 return block.w_l()[gate_index];
440 case Wire::W_R:
441 return block.w_r()[gate_index];
442 case Wire::W_O:
443 return block.w_o()[gate_index];
444 case Wire::W_4:
445 return block.w_4()[gate_index];
446 case Wire::W_L_SHIFT:
447 return block.w_l()[gate_index + 1];
448 case Wire::W_R_SHIFT:
449 return block.w_r()[gate_index + 1];
450 case Wire::W_O_SHIFT:
451 return block.w_o()[gate_index + 1];
452 case Wire::W_4_SHIFT:
453 return block.w_4()[gate_index + 1];
454 }
455 return 0;
456}
457
458inline bool is_shifted(Wire wire)
459{
460 return wire >= Wire::W_L_SHIFT;
461}
462
463template <typename Block>
464std::vector<uint32_t> extract_wires(Block& block,
465 size_t gate_index,
466 const GatePattern& pattern,
467 const Selectors& selectors)
468{
469 std::vector<uint32_t> result;
470 for (const auto& wire_spec : pattern.wires) {
471 // Bounds check for shifted wires
472 if (is_shifted(wire_spec.wire) && gate_index + 1 >= block.size()) {
473 continue;
474 }
475 if (wire_spec.condition(selectors)) {
476 result.push_back(get_wire(block, gate_index, wire_spec.wire));
477 }
478 }
479 return result;
480}
481
482} // namespace bb::gate_patterns
bool is_timestamp_check(const Selectors &sel)
bool is_rom_consistency(const Selectors &sel)
bool is_ram_consistency(const Selectors &sel)
bool is_limb_accum_2(const Selectors &sel)
bool is_product_3(const Selectors &sel)
const GatePattern POSEIDON2_TRANSITION_ENTRY
uint32_t get_wire(Block &block, size_t gate_index, Wire wire)
const GatePattern POSEIDON2_EXTERNAL
const GatePattern POSEIDON2_INTERNAL
const GatePattern LOOKUP
Selectors read_selectors(Block &block, size_t gate_index, GateKind kind)
std::function< bool(const Selectors &)> Predicate
const GatePattern POSEIDON2_INITIAL_EXTERNAL
const GatePattern DATABUS
const GatePattern POSEIDON2_QUAD_INTERNAL_TERMINAL
const GatePattern NON_NATIVE_FIELD
const GatePattern ELLIPTIC
const GatePattern DELTA_RANGE
const GatePattern ARITHMETIC
const GatePattern MEMORY
std::vector< uint32_t > extract_wires(Block &block, size_t gate_index, const GatePattern &pattern, const Selectors &selectors)
const GatePattern POSEIDON2_QUAD_INTERNAL
bool is_shifted(Wire wire)
FF read_gate_selector(const ExecutionTraceBlock< FF, NUM_WIRES > &block, GateKind kind, size_t idx)
Gate-selector value at (block, idx) for kind, returning zero if block does not own this kind....
GateKind
Tag identifying which gate selector a block owns. Used by cross-block readers to decide whether (bloc...
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Pattern defining which wires are constrained by a gate type.
std::vector< WireSpec > wires
Selector values read from a gate.