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
8#include <cstddef>
9#include <cstdint>
10#include <functional>
11#include <string_view>
12#include <vector>
13
15
16enum class Wire : uint8_t {
17 W_L,
18 W_R,
19 W_O,
20 W_4,
25};
26
34struct Selectors {
35 // The gate selector value (q_arith, q_elliptic, etc. depending on block)
36 int64_t gate_selector = 0;
37
38 // Secondary selectors (can be arbitrary field elements, so track non-zero)
39 bool q_m_nz = false;
40 bool q_1_nz = false;
41 bool q_2_nz = false;
42 bool q_3_nz = false;
43 bool q_4_nz = false;
44 bool q_c_nz = false;
45};
46
47using Predicate = std::function<bool(const Selectors&)>;
48
49struct WireSpec {
51 Predicate condition = [](const Selectors&) { return true; };
52};
53
61 std::string_view name;
63};
64
65// ============================================================================
66// Arithmetic Pattern (from ultra_arithmetic_relation.hpp)
67//
68// Subrelation 1:
69// q_arith * [ (-1/2) * (q_arith - 3) * (q_m * w_1 * w_2)
70// + q_1*w_1 + q_2*w_2 + q_3*w_3 + q_4*w_4 + q_c
71// + (q_arith - 1) * w_4_shift ]
72//
73// Subrelation 2 (only when q_arith == 3):
74// q_arith * (q_arith-1) * (q_arith-2) * (w_1 + w_4 - w_1_shift + q_m)
75//
76// gate_selector = q_arith
77// ============================================================================
78
79inline const GatePattern
80 ARITHMETIC = { .name = "arithmetic",
81 .wires = {
82 // w_l: linear term OR mul term (disabled when q_arith==3) OR subrel2
83 { Wire::W_L,
84 [](const Selectors& sel) {
85 return sel.q_1_nz || (sel.q_m_nz && sel.gate_selector != 3) || sel.gate_selector == 3;
86 } },
87 // w_r: linear term OR mul term (disabled when q_arith==3)
88 { Wire::W_R,
89 [](const Selectors& sel) { return sel.q_2_nz || (sel.q_m_nz && sel.gate_selector != 3); } },
90 // w_o: linear term
91 { Wire::W_O, [](const Selectors& sel) { return sel.q_3_nz; } },
92 // w_4: linear term OR subrel2 (subrel2 active when q_arith == 3)
93 { Wire::W_4, [](const Selectors& sel) { return sel.q_4_nz || sel.gate_selector == 3; } },
94 // w_4_shift: when q_arith == 2 or 3 (subrel1 has (q_arith - 1) * w_4_shift)
96 [](const Selectors& sel) { return sel.gate_selector == 2 || sel.gate_selector == 3; } },
97 // w_l_shift: subrel2 when q_arith == 3
98 { Wire::W_L_SHIFT, [](const Selectors& sel) { return sel.gate_selector == 3; } },
99 } };
100
101// ============================================================================
102// Elliptic Pattern (from elliptic_relation.hpp)
103//
104// Point addition (q_is_double == 0, i.e., q_m == 0):
105// x1 = w_r, y1 = w_o, x2 = w_l_shift, x3 = w_r_shift, y3 = w_o_shift, y2 = w_4_shift
106//
107// Point doubling (q_is_double == 1, i.e., q_m == 1):
108// x1 = w_r, y1 = w_o, x3 = w_r_shift, y3 = w_o_shift
109//
110// gate_selector = q_elliptic
111// ============================================================================
112
113inline const GatePattern ELLIPTIC = { .name = "elliptic",
114 .wires = {
115 // x1, y1: always used (both addition and doubling)
116 { Wire::W_R, [](const Selectors&) { return true; } },
117 { Wire::W_O, [](const Selectors&) { return true; } },
118 // x3, y3: always used (both addition and doubling)
119 { Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
120 { Wire::W_O_SHIFT, [](const Selectors&) { return true; } },
121 // x2: only for addition (q_m == 0)
122 { Wire::W_L_SHIFT, [](const Selectors& sel) { return !sel.q_m_nz; } },
123 // y2: only for addition (q_m == 0)
124 { Wire::W_4_SHIFT, [](const Selectors& sel) { return !sel.q_m_nz; } },
125 } };
126
127// ============================================================================
128// Non-Native Field Pattern (from non_native_field_relation.hpp)
129//
130// | gate type | q_2 | q_3 | q_4 | q_m | wires constrained |
131// |---------------|-----|-----|-----|-----|------------------------------------------------|
132// | Limb Accum 1 | 0 | 1 | 1 | 0 | w_l, w_r, w_o, w_4, w_l', w_r' |
133// | Limb Accum 2 | 0 | 1 | 0 | 1 | w_o, w_4, w_l', w_r', w_o', w_4' |
134// | Product 1 | 1 | 1 | 0 | 0 | w_l, w_r, w_o, w_4, w_l', w_r' |
135// | Product 2 | 1 | 0 | 1 | 0 | all 8 wires |
136// | Product 3 | 1 | 0 | 0 | 1 | w_l, w_r, w_4, w_l', w_r', w_o', w_4' |
137//
138// gate_selector = q_nnf
139// ============================================================================
140
141namespace nnf_helpers {
142// Limb Accum 2: !q_2 && q_3 && !q_4 && q_m
143inline bool is_limb_accum_2(const Selectors& sel)
144{
145 return !sel.q_2_nz && sel.q_3_nz && !sel.q_4_nz && sel.q_m_nz;
146}
147// Product 3: q_2 && !q_3 && !q_4 && q_m
148inline bool is_product_3(const Selectors& sel)
149{
150 return sel.q_2_nz && !sel.q_3_nz && !sel.q_4_nz && sel.q_m_nz;
151}
152} // namespace nnf_helpers
153
154inline const GatePattern
155 NON_NATIVE_FIELD = { .name = "non_native_field",
156 .wires = {
157 // w_l, w_r: all gates except Limb Accum 2
158 { Wire::W_L, [](const Selectors& sel) { return !nnf_helpers::is_limb_accum_2(sel); } },
159 { Wire::W_R, [](const Selectors& sel) { return !nnf_helpers::is_limb_accum_2(sel); } },
160 // w_o: all gates except Product 3
161 { Wire::W_O, [](const Selectors& sel) { return !nnf_helpers::is_product_3(sel); } },
162 // w_4: all gates
163 { Wire::W_4, [](const Selectors&) { return true; } },
164 // w_l_shift, w_r_shift: all gates
165 { Wire::W_L_SHIFT, [](const Selectors&) { return true; } },
166 { Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
167 // w_o_shift, w_4_shift: Limb Accum 2, Product 2, Product 3
168 // = q_m || (q_2 && q_4)
170 [](const Selectors& sel) { return sel.q_m_nz || (sel.q_2_nz && sel.q_4_nz); } },
172 [](const Selectors& sel) { return sel.q_m_nz || (sel.q_2_nz && sel.q_4_nz); } },
173 } };
174
175// ============================================================================
176// Memory Pattern (from memory_relation.hpp)
177//
178// | gate type | q_1 | q_2 | q_3 | q_4 | q_m | wires constrained |
179// |---------------------|-----|-----|-----|-----|-----|------------------------------|
180// | RAM/ROM access | 1 | 0 | 0 | 0 | 1 | w_l, w_r, w_o, w_4 |
181// | RAM timestamp check | 1 | 0 | 0 | 1 | 0 | w_l, w_r, w_o, w_l', w_r' |
182// | ROM consistency | 1 | 1 | 0 | 0 | 0 | w_l, w_l', w_4, w_4' |
183// | RAM consistency | 0 | 0 | 1 | 0 | 0 | all 8 wires |
184//
185// gate_selector = q_memory
186// ============================================================================
187
188namespace memory_helpers {
189// RAM timestamp check: q_1 && q_4
190inline bool is_timestamp_check(const Selectors& sel)
191{
192 return sel.q_1_nz && sel.q_4_nz;
193}
194// ROM consistency: q_1 && q_2
195inline bool is_rom_consistency(const Selectors& sel)
196{
197 return sel.q_1_nz && sel.q_2_nz;
198}
199// RAM consistency: q_3
200inline bool is_ram_consistency(const Selectors& sel)
201{
202 return sel.q_3_nz;
203}
204} // namespace memory_helpers
205
206// Note: Access gates (q_1 && q_m) are NOT handled here - they're processed separately
207// via ROM/RAM transcript methods.
208inline const GatePattern
209 MEMORY = { .name = "memory",
210 .wires = {
211 { Wire::W_L,
212 [](const Selectors& sel) {
215 } },
216 { Wire::W_R,
217 [](const Selectors& sel) {
220 } },
221 { Wire::W_O,
222 [](const Selectors& sel) {
225 } },
226 { Wire::W_4,
227 [](const Selectors& sel) {
229 } },
231 [](const Selectors& sel) {
234 } },
236 [](const Selectors& sel) {
238 } },
239 { Wire::W_O_SHIFT, [](const Selectors& sel) { return memory_helpers::is_ram_consistency(sel); } },
241 [](const Selectors& sel) {
243 } },
244 } };
245
246// ============================================================================
247// Lookup Pattern (from logderiv_lookup_relation.hpp)
248//
249// The read term uses: w_l, w_r, w_o (always)
250// Shifted wires used when step_size != 0:
251// w_l_shift if q_2 (q_r) != 0
252// w_r_shift if q_m != 0
253// w_o_shift if q_c != 0
254//
255// gate_selector = q_lookup
256// ============================================================================
257
258inline const GatePattern LOOKUP = { .name = "lookup",
259 .wires = {
260 { Wire::W_L, [](const Selectors&) { return true; } },
261 { Wire::W_R, [](const Selectors&) { return true; } },
262 { Wire::W_O, [](const Selectors&) { return true; } },
263 { Wire::W_L_SHIFT, [](const Selectors& sel) { return sel.q_2_nz; } },
264 { Wire::W_R_SHIFT, [](const Selectors& sel) { return sel.q_m_nz; } },
265 { Wire::W_O_SHIFT, [](const Selectors& sel) { return sel.q_c_nz; } },
266 } };
267
268// ============================================================================
269// Delta Range Pattern (from delta_range_constraint_relation.hpp)
270//
271// 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
272//
273// gate_selector = q_delta_range
274// ============================================================================
275
276inline const GatePattern DELTA_RANGE = { .name = "delta_range",
277 .wires = {
278 { Wire::W_L, [](const Selectors&) { return true; } },
279 { Wire::W_R, [](const Selectors&) { return true; } },
280 { Wire::W_O, [](const Selectors&) { return true; } },
281 { Wire::W_4, [](const Selectors&) { return true; } },
282 { Wire::W_L_SHIFT, [](const Selectors&) { return true; } },
283 } };
284
285// ============================================================================
286// Poseidon2 Internal Pattern (from poseidon2_internal_relation.hpp)
287//
288// All 4 current wires and all 4 shifted wires are constrained
289//
290// gate_selector = q_poseidon2_internal
291// ============================================================================
292
293inline const GatePattern POSEIDON2_INTERNAL = { .name = "poseidon2_internal",
294 .wires = {
295 { Wire::W_L, [](const Selectors&) { return true; } },
296 { Wire::W_R, [](const Selectors&) { return true; } },
297 { Wire::W_O, [](const Selectors&) { return true; } },
298 { Wire::W_4, [](const Selectors&) { return true; } },
299 { Wire::W_L_SHIFT, [](const Selectors&) { return true; } },
300 { Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
301 { Wire::W_O_SHIFT, [](const Selectors&) { return true; } },
302 { Wire::W_4_SHIFT, [](const Selectors&) { return true; } },
303 } };
304
305// ============================================================================
306// Poseidon2 External Pattern (from poseidon2_external_relation.hpp)
307//
308// All 4 current wires and all 4 shifted wires are constrained
309//
310// gate_selector = q_poseidon2_external
311// ============================================================================
312
313inline const GatePattern POSEIDON2_EXTERNAL = { .name = "poseidon2_external",
314 .wires = {
315 { Wire::W_L, [](const Selectors&) { return true; } },
316 { Wire::W_R, [](const Selectors&) { return true; } },
317 { Wire::W_O, [](const Selectors&) { return true; } },
318 { Wire::W_4, [](const Selectors&) { return true; } },
319 { Wire::W_L_SHIFT, [](const Selectors&) { return true; } },
320 { Wire::W_R_SHIFT, [](const Selectors&) { return true; } },
321 { Wire::W_O_SHIFT, [](const Selectors&) { return true; } },
322 { Wire::W_4_SHIFT, [](const Selectors&) { return true; } },
323 } };
324
325// ============================================================================
326// Databus Pattern (from databus_lookup_relation.hpp)
327//
328// Read term uses: w_l (value), w_r (index)
329//
330// gate_selector = q_busread
331// ============================================================================
332
333inline const GatePattern DATABUS = { .name = "databus",
334 .wires = {
335 { Wire::W_L, [](const Selectors&) { return true; } },
336 { Wire::W_R, [](const Selectors&) { return true; } },
337 } };
338
339// ============================================================================
340// Helper functions
341// ============================================================================
342
343template <typename Block, typename GateSelectorColumn>
344Selectors read_selectors(Block& block, size_t gate_index, const GateSelectorColumn& gate_selector_column)
345{
346 return Selectors{
347 .gate_selector = static_cast<int64_t>(static_cast<uint64_t>(gate_selector_column[gate_index])),
348 .q_m_nz = !block.q_m()[gate_index].is_zero(),
349 .q_1_nz = !block.q_1()[gate_index].is_zero(),
350 .q_2_nz = !block.q_2()[gate_index].is_zero(),
351 .q_3_nz = !block.q_3()[gate_index].is_zero(),
352 .q_4_nz = !block.q_4()[gate_index].is_zero(),
353 .q_c_nz = !block.q_c()[gate_index].is_zero(),
354 };
355}
356
357template <typename Block> uint32_t get_wire(Block& block, size_t gate_index, Wire wire)
358{
359 switch (wire) {
360 case Wire::W_L:
361 return block.w_l()[gate_index];
362 case Wire::W_R:
363 return block.w_r()[gate_index];
364 case Wire::W_O:
365 return block.w_o()[gate_index];
366 case Wire::W_4:
367 return block.w_4()[gate_index];
368 case Wire::W_L_SHIFT:
369 return block.w_l()[gate_index + 1];
370 case Wire::W_R_SHIFT:
371 return block.w_r()[gate_index + 1];
372 case Wire::W_O_SHIFT:
373 return block.w_o()[gate_index + 1];
374 case Wire::W_4_SHIFT:
375 return block.w_4()[gate_index + 1];
376 }
377 return 0;
378}
379
380inline bool is_shifted(Wire wire)
381{
382 return wire >= Wire::W_L_SHIFT;
383}
384
385template <typename Block>
386std::vector<uint32_t> extract_wires(Block& block,
387 size_t gate_index,
388 const GatePattern& pattern,
389 const Selectors& selectors)
390{
391 std::vector<uint32_t> result;
392 for (const auto& wire_spec : pattern.wires) {
393 // Bounds check for shifted wires
394 if (is_shifted(wire_spec.wire) && gate_index + 1 >= block.size()) {
395 continue;
396 }
397 if (wire_spec.condition(selectors)) {
398 result.push_back(get_wire(block, gate_index, wire_spec.wire));
399 }
400 }
401 return result;
402}
403
404} // 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)
uint32_t get_wire(Block &block, size_t gate_index, Wire wire)
const GatePattern POSEIDON2_EXTERNAL
const GatePattern POSEIDON2_INTERNAL
const GatePattern LOOKUP
std::function< bool(const Selectors &)> Predicate
const GatePattern DATABUS
const GatePattern NON_NATIVE_FIELD
const GatePattern ELLIPTIC
const GatePattern DELTA_RANGE
Selectors read_selectors(Block &block, size_t gate_index, const GateSelectorColumn &gate_selector_column)
const GatePattern ARITHMETIC
const GatePattern MEMORY
std::vector< uint32_t > extract_wires(Block &block, size_t gate_index, const GatePattern &pattern, const Selectors &selectors)
bool is_shifted(Wire wire)
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.