Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
relation_correctness.test.cpp
Go to the documentation of this file.
21
22#include <gtest/gtest.h>
23using namespace bb;
24
25void ensure_non_zero(auto& polynomial)
26{
27 bool has_non_zero_coefficient = false;
28 for (auto& coeff : polynomial.coeffs()) {
29 has_non_zero_coefficient |= !coeff.is_zero();
30 }
31 ASSERT_TRUE(has_non_zero_coefficient);
32}
33
34template <typename Flavor> void create_some_add_gates(auto& circuit_builder)
35{
36 using FF = typename Flavor::FF;
37 auto a = FF::random_element();
38
39 // Add some basic add gates; incorporate a public input for non-trivial PI-delta
40 uint32_t a_idx = circuit_builder.add_public_variable(a);
42 FF c = a + b;
43 FF d = a + c;
44 uint32_t b_idx = circuit_builder.add_variable(b);
45 uint32_t c_idx = circuit_builder.add_variable(c);
46 uint32_t d_idx = circuit_builder.add_variable(d);
47 for (size_t i = 0; i < 16; i++) {
48 circuit_builder.create_add_gate({ a_idx, b_idx, c_idx, 1, 1, -1, 0 });
49 circuit_builder.create_add_gate({ d_idx, c_idx, a_idx, 1, -1, -1, 0 });
50 }
51
52 // Add an Ultra-style big add gate with use of next row to test q_arith = 2
53 FF e = a + b + c + d;
54 uint32_t e_idx = circuit_builder.add_variable(e);
55
56 uint32_t zero_idx = circuit_builder.zero_idx();
57 circuit_builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, -1, -1, -1, -1, 0 }, true); // use next row
58 circuit_builder.create_big_add_gate({ zero_idx, zero_idx, zero_idx, e_idx, 0, 0, 0, 0, 0 }, false);
59}
60
61template <typename Flavor> void create_some_lookup_gates(auto& circuit_builder)
62{
63 using FF = typename Flavor::FF;
64 // Add some lookup gates (related to pedersen hashing)
65 auto pedersen_input_value = FF::random_element();
66 const auto input_hi =
67 uint256_t(pedersen_input_value)
68 .slice(plookup::fixed_base::table::BITS_PER_LO_SCALAR,
69 plookup::fixed_base::table::BITS_PER_LO_SCALAR + plookup::fixed_base::table::BITS_PER_HI_SCALAR);
70 const auto input_lo = uint256_t(pedersen_input_value).slice(0, bb::plookup::fixed_base::table::BITS_PER_LO_SCALAR);
71 const auto input_hi_index = circuit_builder.add_variable(FF(input_hi));
72 const auto input_lo_index = circuit_builder.add_variable(FF(input_lo));
73
74 const auto sequence_data_hi =
76 const auto sequence_data_lo =
78
79 circuit_builder.create_gates_from_plookup_accumulators(
80 plookup::MultiTableId::FIXED_BASE_LEFT_HI, sequence_data_hi, input_hi_index);
81 circuit_builder.create_gates_from_plookup_accumulators(
82 plookup::MultiTableId::FIXED_BASE_LEFT_LO, sequence_data_lo, input_lo_index);
83}
84
85template <typename Flavor> void create_some_delta_range_constraint_gates(auto& circuit_builder)
86{
87 // Add an `enforce_small_deltas` gate (simply checks that consecutive inputs have a difference of < 4)
88 using FF = typename Flavor::FF;
89 auto a_idx = circuit_builder.add_variable(FF(0));
90 auto b_idx = circuit_builder.add_variable(FF(1));
91 auto c_idx = circuit_builder.add_variable(FF(2));
92 auto d_idx = circuit_builder.add_variable(FF(3));
93 circuit_builder.enforce_small_deltas({ a_idx, b_idx, c_idx, d_idx });
94}
95
96template <typename Flavor> void create_some_RAM_gates(auto& circuit_builder)
97{
98 using FF = typename Flavor::FF;
99 // Add some RAM gates
100 uint32_t ram_values[8]{
101 circuit_builder.add_variable(FF::random_element()), circuit_builder.add_variable(FF::random_element()),
102 circuit_builder.add_variable(FF::random_element()), circuit_builder.add_variable(FF::random_element()),
103 circuit_builder.add_variable(FF::random_element()), circuit_builder.add_variable(FF::random_element()),
104 circuit_builder.add_variable(FF::random_element()), circuit_builder.add_variable(FF::random_element()),
105 };
106
107 size_t ram_id = circuit_builder.create_RAM_array(8);
108
109 for (size_t i = 0; i < 8; ++i) {
110 circuit_builder.init_RAM_element(ram_id, i, ram_values[i]);
111 }
112
113 auto a_idx = circuit_builder.read_RAM_array(ram_id, circuit_builder.add_variable(FF(5)));
114 EXPECT_EQ(a_idx != ram_values[5], true);
115
116 auto b_idx = circuit_builder.read_RAM_array(ram_id, circuit_builder.add_variable(FF(4)));
117 auto c_idx = circuit_builder.read_RAM_array(ram_id, circuit_builder.add_variable(FF(1)));
118
119 circuit_builder.write_RAM_array(ram_id, circuit_builder.add_variable(FF(4)), circuit_builder.add_variable(FF(500)));
120 auto d_idx = circuit_builder.read_RAM_array(ram_id, circuit_builder.add_variable(FF(4)));
121
122 EXPECT_EQ(circuit_builder.get_variable(d_idx), 500);
123
124 // ensure these vars get used in another arithmetic gate
125 const auto e_value = circuit_builder.get_variable(a_idx) + circuit_builder.get_variable(b_idx) +
126 circuit_builder.get_variable(c_idx) + circuit_builder.get_variable(d_idx);
127 auto e_idx = circuit_builder.add_variable(e_value);
128
129 circuit_builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, -1, -1, -1, -1, 0 }, true);
130 circuit_builder.create_big_add_gate(
131 {
132 circuit_builder.zero_idx(),
133 circuit_builder.zero_idx(),
134 circuit_builder.zero_idx(),
135 e_idx,
136 0,
137 0,
138 0,
139 0,
140 0,
141 },
142 false);
143}
144
145template <typename Flavor> void create_some_elliptic_curve_addition_gates(auto& circuit_builder)
146{
147 // Add an elliptic curve addition gate
148 grumpkin::g1::affine_element p1 = grumpkin::g1::affine_element::random_element();
149 grumpkin::g1::affine_element p2 = grumpkin::g1::affine_element::random_element();
150
152
153 uint32_t x1 = circuit_builder.add_variable(p1.x);
154 uint32_t y1 = circuit_builder.add_variable(p1.y);
155 uint32_t x2 = circuit_builder.add_variable(p2.x);
156 uint32_t y2 = circuit_builder.add_variable(p2.y);
157 uint32_t x3 = circuit_builder.add_variable(p3.x);
158 uint32_t y3 = circuit_builder.add_variable(p3.y);
159
160 circuit_builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, /*is_addition=*/false });
161}
162
163template <typename Flavor>
164void create_some_ecc_op_queue_gates(auto& circuit_builder)
165 requires(Flavor::HasEccOpQueue)
166{
167 using G1 = typename Flavor::Curve::Group;
168 using FF = typename Flavor::FF;
169 const size_t num_ecc_operations = 10; // arbitrary
170 for (size_t i = 0; i < num_ecc_operations; ++i) {
171 auto point = G1::affine_one * FF::random_element();
172 auto scalar = FF::random_element();
173 circuit_builder.queue_ecc_mul_accum(point, scalar);
174 }
175}
176
177template <typename Flavor>
179 requires Flavor::HasDataBus
180{
181 using FF = typename Flavor::FF;
182 auto val = builder.add_variable(FF::random_element());
183 builder.add_public_calldata(BusId::KERNEL_CALLDATA, val);
184 builder.read_calldata(BusId::KERNEL_CALLDATA, builder.add_variable(FF(0)));
185 for (size_t app_idx = 0; app_idx < MAX_APPS_PER_KERNEL; ++app_idx) {
186 auto bus_id = static_cast<BusId>(static_cast<size_t>(BusId::APP_CALLDATA) + app_idx);
187 builder.add_public_calldata(bus_id, val);
188 builder.read_calldata(bus_id, builder.add_variable(FF(0)));
189 }
190 builder.add_public_return_data(val);
191 builder.read_return_data(builder.add_variable(FF(0)));
192}
193
194template <typename Flavor> void create_some_non_native_field_gates(auto& builder)
195{
196 using Builder = typename Flavor::CircuitBuilder;
198
199 auto a = fq_ct::from_witness(&builder, bb::fq::random_element());
200 auto b = fq_ct::from_witness(&builder, bb::fq::random_element());
201 [[maybe_unused]] auto c = a * b;
202}
203
204template <typename Flavor> void create_some_bilinear_batched_eq_gates(auto& builder)
205{
206 using FF = typename Flavor::FF;
207
208 // BILINEAR row: enforce the full identity
209 // q_m·a·b + q_5·a·c + q_l·a + q_r·b + q_o·c + q_4·d + q_c = 0
210 // with two products sharing wire a, a linear term on each wire, and a constant. Pick a, b, c and
211 // solve for the linear-only wire d so the identity holds.
212 FF qm = FF(3);
213 FF q5 = -FF(5);
214 FF ql = FF(2);
215 FF qr = -FF(1);
216 FF qo = FF(7);
217 FF q4 = FF(4);
218 FF qc = FF(11);
219 FF a_val = FF::random_element();
220 FF b_val = FF::random_element();
221 FF c_val = FF::random_element();
222 // q_4·d = −(q_m·a·b + q_5·a·c + q_l·a + q_r·b + q_o·c + q_c) ⇒ d = rest / q_4
223 FF rest = -(qm * a_val * b_val + q5 * a_val * c_val + ql * a_val + qr * b_val + qo * c_val + qc);
224 FF d_val = rest * q4.invert();
225 uint32_t a_idx = builder.add_variable(a_val);
226 uint32_t b_idx = builder.add_variable(b_val);
227 uint32_t c_idx = builder.add_variable(c_val);
228 uint32_t d_idx = builder.add_variable(d_val);
229 builder.create_bilinear_batched_eq_gate({ BilinearBatchedEqMode::Bilinear,
230 a_idx,
231 b_idx,
232 c_idx,
233 d_idx,
234 /*q_l=*/ql,
235 /*q_r=*/qr,
236 /*q_o=*/qo,
237 /*q_4=*/q4,
238 /*q_c=*/qc,
239 /*q_m=*/qm,
240 /*q_5=*/q5 });
241
242 // BATCHED_EQ row: pair two equality-with-const constraints into one row.
243 // Half 1: w_l + 2·w_r − k1 = 0
244 // Half 2: 3·w_o − w_4 − k2 = 0
245 FF w_l_val = FF::random_element();
246 FF w_r_val = FF::random_element();
247 FF w_o_val = FF::random_element();
248 FF k1 = w_l_val + FF(2) * w_r_val;
249 FF k2 = FF(3) * w_o_val;
250 FF w_4_val = k2 * -FF(1) + FF(3) * w_o_val; // makes 3·w_o − w_4 − k2 = 0
251 uint32_t wl_idx = builder.add_variable(w_l_val);
252 uint32_t wr_idx = builder.add_variable(w_r_val);
253 uint32_t wo_idx = builder.add_variable(w_o_val);
254 uint32_t w4_idx = builder.add_variable(w_4_val);
255 builder.create_bilinear_batched_eq_gate({ BilinearBatchedEqMode::BatchedEq,
256 wl_idx,
257 wr_idx,
258 wo_idx,
259 w4_idx,
260 /*q_l=*/FF(1),
261 /*q_r=*/FF(2),
262 /*q_o=*/FF(3),
263 /*q_4=*/-FF(1),
264 /*q_c=*/-k1,
265 /*q_m=*/-k2,
266 /*q_5=*/0 });
267}
268
281
282class UltraRelationCorrectnessTests : public ::testing::Test {
283 protected:
285};
286
297// TODO(luke): Add a gate that sets q_arith = 3 to check secondary arithmetic relation
299{
300 using Flavor = UltraFlavor;
301
302 // Create a builder and then add an assortment of gates designed to ensure that the constraint(s) represented
303 // by each relation are non-trivially exercised.
305
306 // Create an assortment of representative gates
307 create_some_add_gates<Flavor>(builder);
308 create_some_lookup_gates<Flavor>(builder);
309 create_some_delta_range_constraint_gates<Flavor>(builder);
310 create_some_elliptic_curve_addition_gates<Flavor>(builder);
311 create_some_RAM_gates<Flavor>(builder);
312 create_some_non_native_field_gates<Flavor>(builder);
313 create_some_poseidon2_gates<Flavor>(builder);
315
316 // Create a prover (it will compute proving key and witness)
318
319 complete_prover_instance_for_test<Flavor>(prover_inst);
320
321 // Check that selectors are nonzero to ensure corresponding relation has nontrivial contribution
322 for (auto selector : prover_inst->polynomials.get_gate_selectors()) {
323 ensure_non_zero(selector);
324 }
325
326 auto& prover_polynomials = prover_inst->polynomials;
327 auto params = prover_inst->relation_parameters;
328
329 auto relation_failures = RelationChecker<Flavor>::check_all(prover_polynomials, params);
330 EXPECT_TRUE(relation_failures.empty());
331}
332
334{
335 using Flavor = MegaFlavor;
336
337 // Create a composer and then add an assortment of gates designed to ensure that the constraint(s) represented
338 // by each relation are non-trivially exercised.
340
341 // Create an assortment of representative gates
342 create_some_add_gates<Flavor>(builder);
343 create_some_lookup_gates<Flavor>(builder);
344 create_some_delta_range_constraint_gates<Flavor>(builder);
345 create_some_elliptic_curve_addition_gates<Flavor>(builder);
346 create_some_RAM_gates<Flavor>(builder);
347 create_some_ecc_op_queue_gates<Flavor>(builder);
348 create_some_databus_gates<Flavor>(builder);
349 create_some_non_native_field_gates<Flavor>(builder);
350 create_some_poseidon2_gates<Flavor>(builder);
351 create_some_bilinear_batched_eq_gates<Flavor>(builder);
353
354 // Create a prover (it will compute proving key and witness)
356
357 complete_prover_instance_for_test<Flavor>(prover_inst);
358
359 // Check that selectors and databus inverses are nonzero to ensure relations are non-trivially exercised
360 for (auto selector : prover_inst->polynomials.get_gate_selectors()) {
361 ensure_non_zero(selector);
362 }
363
364 // Check the databus read counts/tags/inverses are non-zero (data columns may be zero with DEFAULT_VALUE=0)
365 for (auto poly : prover_inst->polynomials.get_databus_inverses()) {
366 ensure_non_zero(poly);
367 }
368
369 auto& prover_polynomials = prover_inst->polynomials;
370 auto params = prover_inst->relation_parameters;
371
372 auto relation_failures = RelationChecker<Flavor>::check_all(prover_polynomials, params);
373 EXPECT_TRUE(relation_failures.empty());
374}
typename Curve::ScalarField FF
ECCVMCircuitBuilder CircuitBuilder
static AllSubrelationFailures check_all(const auto &polynomials, const auto &params)
Check that the provided polynomials satisfy all relations for a given Flavor.
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
Definition element.hpp:35
constexpr uint256_t slice(uint64_t start, uint64_t end) const
static void add_default(Builder &builder)
Add default public inputs when they are not present.
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
@ Ultra
bn254::BaseField fq_ct
stdlib::witness_t< Builder > witness_ct
AvmProvingInputs inputs
ReadData< bb::fr > get_lookup_accumulators(const MultiTableId id, const fr &key_a, const fr &key_b, const bool is_2_to_1_lookup)
Given a table ID and the key(s) for a key-value lookup, return the lookup accumulators.
@ FIXED_BASE_LEFT_LO
Definition types.hpp:110
@ FIXED_BASE_LEFT_HI
Definition types.hpp:111
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:160
BusId
Definition databus.hpp:75
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
MegaCircuitBuilder_< field< Bn254FrParams > > MegaCircuitBuilder
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Curve::AffineElement G1
constexpr field invert() const noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept
static constexpr size_t BITS_PER_LO_SCALAR
void create_some_delta_range_constraint_gates(auto &circuit_builder)
void create_some_databus_gates(auto &builder)
void create_some_RAM_gates(auto &circuit_builder)
void create_some_poseidon2_gates(auto &builder)
void create_some_ecc_op_queue_gates(auto &circuit_builder)
void create_some_elliptic_curve_addition_gates(auto &circuit_builder)
void create_some_lookup_gates(auto &circuit_builder)
void create_some_bilinear_batched_eq_gates(auto &builder)
void ensure_non_zero(auto &polynomial)
void create_some_add_gates(auto &circuit_builder)