Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
translator.test.cpp
Go to the documentation of this file.
10
11#include <gtest/gtest.h>
12using namespace bb;
13
17static auto& engine = numeric::get_debug_randomness();
18
19class TranslatorTests : public ::testing::Test {
21 using Fr = fr;
22 using Fq = fq;
24 using FF = Flavor::FF;
26
27 protected:
29
46 {
47 TranscriptManifest manifest;
48 constexpr size_t frs_per_G = FrCodec::calc_num_fields<Flavor::Commitment>();
49 constexpr size_t NUM_SUMCHECK_ROUNDS = 17; // CONST_TRANSLATOR_LOG_N + 2
50
51 // Round 0: vk_hash, Gemini masking, wire commitments
52 manifest.add_entry(0, "vk_hash", 1);
53 manifest.add_entry(0, "Gemini:masking_poly_comm", frs_per_G);
54
55 // Wire commitments (82 total, in order from the manifest dump)
56 // clang-format off
57 std::vector<std::string> wire_labels = {
58 "P_X_LOW_LIMBS", "P_X_HIGH_LIMBS", "P_Y_LOW_LIMBS", "P_Y_HIGH_LIMBS",
59 "Z_LOw_LIMBS", "Z_HIGH_LIMBS",
60 "ACCUMULATORS_BINARY_LIMBS_0", "ACCUMULATORS_BINARY_LIMBS_1",
61 "ACCUMULATORS_BINARY_LIMBS_2", "ACCUMULATORS_BINARY_LIMBS_3",
62 "QUOTIENT_LOW_BINARY_LIMBS", "QUOTIENT_HIGH_BINARY_LIMBS",
63 "RELATION_WIDE_LIMBS",
64 "P_X_LOW_LIMBS_RANGE_CONSTRAINT_0", "P_X_LOW_LIMBS_RANGE_CONSTRAINT_1",
65 "P_X_LOW_LIMBS_RANGE_CONSTRAINT_2", "P_X_LOW_LIMBS_RANGE_CONSTRAINT_3",
66 "P_X_LOW_LIMBS_RANGE_CONSTRAINT_4", "P_X_LOW_LIMBS_RANGE_CONSTRAINT_TAIL",
67 "P_X_HIGH_LIMBS_RANGE_CONSTRAINT_0", "P_X_HIGH_LIMBS_RANGE_CONSTRAINT_1",
68 "P_X_HIGH_LIMBS_RANGE_CONSTRAINT_2", "P_X_HIGH_LIMBS_RANGE_CONSTRAINT_3",
69 "P_X_HIGH_LIMBS_RANGE_CONSTRAINT_4", "P_X_HIGH_LIMBS_RANGE_CONSTRAINT_TAIL",
70 "P_Y_LOW_LIMBS_RANGE_CONSTRAINT_0", "P_Y_LOW_LIMBS_RANGE_CONSTRAINT_1",
71 "P_Y_LOW_LIMBS_RANGE_CONSTRAINT_2", "P_Y_LOW_LIMBS_RANGE_CONSTRAINT_3",
72 "P_Y_LOW_LIMBS_RANGE_CONSTRAINT_4", "P_Y_LOW_LIMBS_RANGE_CONSTRAINT_TAIL",
73 "P_Y_HIGH_LIMBS_RANGE_CONSTRAINT_0", "P_Y_HIGH_LIMBS_RANGE_CONSTRAINT_1",
74 "P_Y_HIGH_LIMBS_RANGE_CONSTRAINT_2", "P_Y_HIGH_LIMBS_RANGE_CONSTRAINT_3",
75 "P_Y_HIGH_LIMBS_RANGE_CONSTRAINT_4", "P_Y_HIGH_LIMBS_RANGE_CONSTRAINT_TAIL",
76 "Z_LOW_LIMBS_RANGE_CONSTRAINT_0", "Z_LOW_LIMBS_RANGE_CONSTRAINT_1",
77 "Z_LOW_LIMBS_RANGE_CONSTRAINT_2", "Z_LOW_LIMBS_RANGE_CONSTRAINT_3",
78 "Z_LOW_LIMBS_RANGE_CONSTRAINT_4", "Z_LOW_LIMBS_RANGE_CONSTRAINT_TAIL",
79 "Z_HIGH_LIMBS_RANGE_CONSTRAINT_0", "Z_HIGH_LIMBS_RANGE_CONSTRAINT_1",
80 "Z_HIGH_LIMBS_RANGE_CONSTRAINT_2", "Z_HIGH_LIMBS_RANGE_CONSTRAINT_3",
81 "Z_HIGH_LIMBS_RANGE_CONSTRAINT_4", "Z_HIGH_LIMBS_RANGE_CONSTRAINT_TAIL",
82 "ACCUMULATOR_LOW_LIMBS_RANGE_CONSTRAINT_0", "ACCUMULATOR_LOW_LIMBS_RANGE_CONSTRAINT_1",
83 "ACCUMULATOR_LOW_LIMBS_RANGE_CONSTRAINT_2", "ACCUMULATOR_LOW_LIMBS_RANGE_CONSTRAINT_3",
84 "ACCUMULATOR_LOW_LIMBS_RANGE_CONSTRAINT_4", "ACCUMULATOR_LOW_LIMBS_RANGE_CONSTRAINT_TAIL",
85 "ACCUMULATOR_HIGH_LIMBS_RANGE_CONSTRAINT_0", "ACCUMULATOR_HIGH_LIMBS_RANGE_CONSTRAINT_1",
86 "ACCUMULATOR_HIGH_LIMBS_RANGE_CONSTRAINT_2", "ACCUMULATOR_HIGH_LIMBS_RANGE_CONSTRAINT_3",
87 "ACCUMULATOR_HIGH_LIMBS_RANGE_CONSTRAINT_4", "ACCUMULATOR_HIGH_LIMBS_RANGE_CONSTRAINT_TAIL",
88 "QUOTIENT_LOW_LIMBS_RANGE_CONSTRAINT_0", "QUOTIENT_LOW_LIMBS_RANGE_CONSTRAINT_1",
89 "QUOTIENT_LOW_LIMBS_RANGE_CONSTRAINT_2", "QUOTIENT_LOW_LIMBS_RANGE_CONSTRAINT_3",
90 "QUOTIENT_LOW_LIMBS_RANGE_CONSTRAINT_4", "QUOTIENT_LOW_LIMBS_RANGE_CONSTRAINT_TAIL",
91 "QUOTIENT_HIGH_LIMBS_RANGE_CONSTRAINT_0", "QUOTIENT_HIGH_LIMBS_RANGE_CONSTRAINT_1",
92 "QUOTIENT_HIGH_LIMBS_RANGE_CONSTRAINT_2", "QUOTIENT_HIGH_LIMBS_RANGE_CONSTRAINT_3",
93 "QUOTIENT_HIGH_LIMBS_RANGE_CONSTRAINT_4", "QUOTIENT_HIGH_LIMBS_RANGE_CONSTRAINT_TAIL",
94 "RELATION_WIDE_LIMBS_RANGE_CONSTRAINT_0", "RELATION_WIDE_LIMBS_RANGE_CONSTRAINT_1",
95 "RELATION_WIDE_LIMBS_RANGE_CONSTRAINT_2", "RELATION_WIDE_LIMBS_RANGE_CONSTRAINT_3",
96 "ORDERED_RANGE_CONSTRAINTS_0", "ORDERED_RANGE_CONSTRAINTS_1",
97 "ORDERED_RANGE_CONSTRAINTS_2", "ORDERED_RANGE_CONSTRAINTS_3",
98 "ORDERED_RANGE_CONSTRAINTS_4",
99 };
100 // clang-format on
101 for (const auto& label : wire_labels) {
102 manifest.add_entry(0, label, frs_per_G);
103 }
104 manifest.add_challenge(0, "beta");
105
106 // Round 1: gamma challenge (no entries)
107 manifest.add_challenge(1, "gamma");
108
109 // Round 2: Z_PERM -> Sumcheck:alpha
110 manifest.add_entry(2, "Z_PERM", frs_per_G);
111 manifest.add_challenge(2, "Sumcheck:alpha");
112
113 // Rounds 3-19: Gate challenges (17 rounds)
114 for (size_t i = 0; i < NUM_SUMCHECK_ROUNDS; ++i) {
115 manifest.add_challenge(3 + i, "Sumcheck:gate_challenge_" + std::to_string(i));
116 }
117
118 // Round 20: Libra concatenation commitment + Sum -> Libra:Challenge
119 manifest.add_entry(20, "Libra:concatenation_commitment", frs_per_G);
120 manifest.add_entry(20, "Libra:Sum", 1);
121 manifest.add_challenge(20, "Libra:Challenge");
122
123 // Rounds 21-37: Sumcheck univariates (17 rounds)
124 for (size_t i = 0; i < NUM_SUMCHECK_ROUNDS; ++i) {
125 manifest.add_entry(21 + i, "Sumcheck:univariate_" + std::to_string(i), 9);
126 manifest.add_challenge(21 + i, "Sumcheck:u_" + std::to_string(i));
127 }
128
129 // Round 38: Sumcheck evaluations + Libra commitments -> rho
130 manifest.add_entry(38, "Sumcheck:evaluations", 188);
131 manifest.add_entry(38, "Libra:claimed_evaluation", 1);
132 manifest.add_entry(38, "Libra:grand_sum_commitment", frs_per_G);
133 manifest.add_entry(38, "Libra:quotient_commitment", frs_per_G);
134 manifest.add_challenge(38, "rho");
135
136 // Round 39: Gemini fold commitments -> Gemini:r
137 for (size_t i = 1; i <= 16; ++i) {
138 manifest.add_entry(39, "Gemini:FOLD_" + std::to_string(i), frs_per_G);
139 }
140 manifest.add_challenge(39, "Gemini:r");
141
142 // Round 40: Gemini evaluations + Libra evals -> Shplonk:nu
143 for (size_t i = 1; i <= 17; ++i) {
144 manifest.add_entry(40, "Gemini:a_" + std::to_string(i), 1);
145 }
146 manifest.add_entry(40, "Gemini:P_pos", 1);
147 manifest.add_entry(40, "Gemini:P_neg", 1);
148 manifest.add_entry(40, "Libra:concatenation_eval", 1);
149 manifest.add_entry(40, "Libra:shifted_grand_sum_eval", 1);
150 manifest.add_entry(40, "Libra:grand_sum_eval", 1);
151 manifest.add_entry(40, "Libra:quotient_eval", 1);
152 manifest.add_challenge(40, "Shplonk:nu");
153
154 // Round 41: Shplonk:Q -> Shplonk:z
155 manifest.add_entry(41, "Shplonk:Q", frs_per_G);
156 manifest.add_challenge(41, "Shplonk:z");
157
158 // Round 42: KZG:W -> KZG:masking_challenge
159 manifest.add_entry(42, "KZG:W", frs_per_G);
160 manifest.add_challenge(42, "KZG:masking_challenge");
161
162 return manifest;
163 }
164
165 // Helper function to add no-ops
166 static void add_random_ops(std::shared_ptr<bb::ECCOpQueue>& op_queue, size_t count = 1)
167 {
168 for (size_t i = 0; i < count; i++) {
169 op_queue->random_op_ultra_only();
170 }
171 }
172
173 static void add_mixed_ops(std::shared_ptr<bb::ECCOpQueue>& op_queue, size_t count = 100)
174 {
175 auto P1 = G1::random_element();
176 auto P2 = G1::random_element();
177 auto z = Fr::random_element();
178 for (size_t i = 0; i < count; i++) {
179 op_queue->add_accumulate(P1);
180 op_queue->mul_accumulate(P2, z);
181 }
182 op_queue->eq_and_reset();
183 }
184
185 // Construct a test circuit based on some random operations
186 static CircuitBuilder generate_test_circuit(const Fq& batching_challenge_v,
187 const Fq& evaluation_challenge_x,
188 const size_t circuit_size_parameter = 500)
189 {
190
191 // Add the same operations to the ECC op queue; the native computation is performed under the hood.
192 auto op_queue = std::make_shared<bb::ECCOpQueue>();
193 op_queue->no_op_ultra_only();
195 add_mixed_ops(op_queue, circuit_size_parameter / 2);
196 op_queue->merge();
197 add_mixed_ops(op_queue, circuit_size_parameter / 2);
199 op_queue->merge(MergeSettings::APPEND, ECCOpQueue::OP_QUEUE_SIZE - op_queue->get_current_subtable_size());
200
201 return CircuitBuilder{ batching_challenge_v, evaluation_challenge_x, op_queue };
202 }
203
204 static bool prove_and_verify(const CircuitBuilder& circuit_builder,
205 const Fq& evaluation_challenge_x,
206 const Fq& batching_challenge_v)
207 {
208 // Setup prover transcript
209 auto prover_transcript = std::make_shared<Transcript>();
210 prover_transcript->send_to_verifier("init", Fq::random_element());
211 auto initial_transcript = prover_transcript->export_proof();
212
213 // Setup verifier transcript
214 auto verifier_transcript = std::make_shared<Transcript>(initial_transcript);
215 verifier_transcript->template receive_from_prover<Fq>("init");
216
217 // Create proving key and prover
218 auto proving_key = std::make_shared<TranslatorProvingKey>(circuit_builder);
219 TranslatorProver prover{ proving_key, prover_transcript };
220
221 // Generate proof
222 auto proof = prover.construct_proof();
223
224 // Commit to op queue wires
226 op_queue_commitments[0] =
227 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.op);
228 op_queue_commitments[1] =
229 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.x_lo_y_hi);
230 op_queue_commitments[2] =
231 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.x_hi_z_1);
232 op_queue_commitments[3] =
233 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.y_lo_z_2);
234
235 // Get accumulated_result from the prover
236 uint256_t accumulated_result = prover.get_accumulated_result();
237
238 // Create verifier
239 TranslatorVerifier verifier(verifier_transcript,
240 proof,
241 evaluation_challenge_x,
242 batching_challenge_v,
243 accumulated_result,
244 op_queue_commitments);
245
246 // Verify proof: get reduction result and check all components
247 auto result = verifier.reduce_to_pairing_check();
248 return result.pairing_points.check() && result.reduction_succeeded;
249 }
250};
251
259TEST_F(TranslatorTests, ProofLengthCheck)
260{
261 using Fq = fq;
262
263 Fq batching_challenge_v = Fq::random_element();
264 Fq evaluation_challenge_x = Fq::random_element();
265
266 // Generate a circuit and its verification key (computed at runtime from the proving key)
267 CircuitBuilder circuit_builder = generate_test_circuit(batching_challenge_v, evaluation_challenge_x);
268
269 // Setup prover transcript
270 auto prover_transcript = std::make_shared<Transcript>();
271 prover_transcript->send_to_verifier("init", Fq::random_element());
272 prover_transcript->export_proof();
273 auto proving_key = std::make_shared<TranslatorProvingKey>(circuit_builder);
274 TranslatorProver prover{ proving_key, prover_transcript };
275
276 // Generate proof
277 auto proof = prover.construct_proof();
278
280}
281
287{
288 using Fq = fq;
289
290 Fq batching_challenge_v = Fq::random_element();
291 Fq evaluation_challenge_x = Fq::random_element();
292
293 // Generate a circuit without no-ops
294 CircuitBuilder circuit_builder = generate_test_circuit(batching_challenge_v, evaluation_challenge_x);
295
296 EXPECT_TRUE(TranslatorCircuitChecker::check(circuit_builder));
297 bool verified = prove_and_verify(circuit_builder, evaluation_challenge_x, batching_challenge_v);
298 EXPECT_TRUE(verified);
299}
300
307{
308 using Fq = fq;
309
310 Fq batching_challenge_v = Fq::random_element();
311 Fq evaluation_challenge_x = Fq::random_element();
312
313 // Add the same operations to the ECC op queue; the native computation is performed under the hood.
314 auto op_queue = std::make_shared<bb::ECCOpQueue>();
315 op_queue->no_op_ultra_only();
316 add_random_ops(op_queue, CircuitBuilder::NUM_RANDOM_OPS_START);
317 add_mixed_ops(op_queue, 100);
318 op_queue->merge();
319 auto circuit_builder = CircuitBuilder{ batching_challenge_v, evaluation_challenge_x, op_queue, /*avm_mode=*/true };
320
321 EXPECT_TRUE(TranslatorCircuitChecker::check(circuit_builder));
322 bool verified = prove_and_verify(circuit_builder, evaluation_challenge_x, batching_challenge_v);
323 EXPECT_TRUE(verified);
324}
325
335{
336 using Fq = fq;
337
338 auto prover_transcript = std::make_shared<Transcript>();
339 prover_transcript->send_to_verifier("init", Fq::random_element());
340 prover_transcript->export_proof();
341 Fq batching_challenge_v = Fq::random_element();
342 Fq evaluation_challenge_x = Fq::random_element();
343
344 // Generate the default fixed VK
346
347 // Lambda for manually computing a verification key for a given circuit and comparing it to the fixed VK
348 auto compare_computed_vk_against_fixed = [&](size_t circuit_size_parameter) {
349 CircuitBuilder circuit_builder =
350 generate_test_circuit(batching_challenge_v, evaluation_challenge_x, circuit_size_parameter);
351 auto proving_key = std::make_shared<TranslatorProvingKey>(circuit_builder);
352 TranslatorProver prover{ proving_key, prover_transcript };
353 TranslatorFlavor::VerificationKey computed_vk(proving_key->proving_key);
354 auto labels = TranslatorFlavor::VerificationKey::get_labels();
355 size_t index = 0;
356 for (auto [vk_commitment, fixed_commitment] : zip_view(computed_vk.get_all(), fixed_vk.get_all())) {
357 EXPECT_EQ(vk_commitment, fixed_commitment)
358 << "Mismatch between computed vk_commitment and fixed_commitment at label: " << labels[index];
359 ++index;
360 }
361
362 EXPECT_EQ(computed_vk, fixed_vk);
363 };
364
365 // Check consistency of the fixed VK with the computed VK for some different circuit sizes
366 const size_t circuit_size_parameter_1 = 1 << 2;
367 const size_t circuit_size_parameter_2 = 1 << 3;
368
369 compare_computed_vk_against_fixed(circuit_size_parameter_1);
370 compare_computed_vk_against_fixed(circuit_size_parameter_2);
371}
372
378TEST_F(TranslatorTests, TranscriptPinned)
379{
380 using Fq = fq;
381
382 Fq batching_challenge_v = Fq::random_element();
383 Fq evaluation_challenge_x = Fq::random_element();
384
385 CircuitBuilder circuit_builder = generate_test_circuit(batching_challenge_v, evaluation_challenge_x);
386
387 // Create proving key and prover
388 auto prover_transcript = std::make_shared<Transcript>();
389 auto proving_key = std::make_shared<TranslatorProvingKey>(circuit_builder);
390 TranslatorProver prover{ proving_key, prover_transcript };
391
392 // Generate proof
393 auto proof = prover.construct_proof();
394
395 // Setup verifier transcript with manifest tracking
396 auto verifier_transcript = std::make_shared<Transcript>(proof);
397 verifier_transcript->enable_manifest();
398
399 // Get accumulated_result from the prover
400 uint256_t accumulated_result = prover.get_accumulated_result();
401
402 // Commit to op queue wires
404 op_queue_commitments[0] = proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.op);
405 op_queue_commitments[1] =
406 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.x_lo_y_hi);
407 op_queue_commitments[2] =
408 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.x_hi_z_1);
409 op_queue_commitments[3] =
410 proving_key->proving_key->commitment_key.commit(proving_key->proving_key->polynomials.y_lo_z_2);
411
412 // Create verifier with all required inputs
413 TranslatorVerifier verifier(verifier_transcript,
414 proof,
415 evaluation_challenge_x,
416 batching_challenge_v,
417 accumulated_result,
418 op_queue_commitments);
419
420 // Run verification - just reduce to pairing check to exercise the transcript
421 [[maybe_unused]] auto result = verifier.reduce_to_pairing_check();
422
423 // Compare verifier manifest against hardcoded expected structure
424 auto expected_manifest = build_expected_translator_manifest();
425 auto verifier_manifest = verifier_transcript->get_manifest();
426
427 EXPECT_EQ(verifier_manifest, expected_manifest);
428}
static bool prove_and_verify(const CircuitBuilder &circuit_builder, const Fq &evaluation_challenge_x, const Fq &batching_challenge_v)
static void add_random_ops(std::shared_ptr< bb::ECCOpQueue > &op_queue, size_t count=1)
Flavor::Commitment Commitment
static CircuitBuilder generate_test_circuit(const Fq &batching_challenge_v, const Fq &evaluation_challenge_x, const size_t circuit_size_parameter=500)
static void SetUpTestSuite()
static void add_mixed_ops(std::shared_ptr< bb::ECCOpQueue > &op_queue, size_t count=100)
static TranscriptManifest build_expected_translator_manifest()
Build the expected transcript manifest for Translator verification.
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
Manages ECC operations for the Goblin proving system.
static const size_t OP_QUEUE_SIZE
void add_entry(size_t round, const std::string &element_label, size_t element_size)
void add_challenge(size_t round, const std::string &label)
Add a single challenge label to the manifest for the given round.
TranslatorCircuitBuilder creates a circuit that evaluates the correctness of the evaluation of EccOpQ...
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
The verification key is responsible for storing the commitments to the precomputed (non-witnessk) pol...
BaseTranscript< Codec, HashFunction > Transcript
TranslatorCircuitBuilder CircuitBuilder
Curve::ScalarField FF
Curve::AffineElement Commitment
static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS
Translator verifier class that verifies the proof of the Translator circuit.
ReductionResult reduce_to_pairing_check()
Reduce the translator proof to a pairing check.
static affine_element random_element(numeric::RNG *engine=nullptr) noexcept
Samples a random point on the curve.
group_elements::affine_element< Fq, Fr, Params > affine_element
Definition group.hpp:42
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:190
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
field< Bn254FqParams > fq
Definition fq.hpp:169
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:185
field< Bn254FrParams > fr
Definition fr.hpp:174
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
static field random_element(numeric::RNG *engine=nullptr) noexcept