Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
sumcheck_client_ivc.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
16
17namespace bb {
18
19// Constructor
21 : num_circuits(num_circuits)
22 , goblin(bn254_commitment_key)
23{
24 BB_ASSERT_GT(num_circuits, 0UL, "Number of circuits must be specified and greater than 0.");
25 // Allocate BN254 commitment key based on translator circuit size.
26 // https://github.com/AztecProtocol/barretenberg/issues/1319): Account for Translator only when it's necessary
27 size_t commitment_key_size = 1UL << TranslatorFlavor::CONST_TRANSLATOR_LOG_N;
28 info("BN254 commitment key size: ", commitment_key_size);
30}
31
43 ClientCircuit& circuit, const std::vector<std::shared_ptr<RecursiveVKAndHash>>& input_keys)
44{
45 bool vkeys_provided = !input_keys.empty();
46 if (vkeys_provided) {
48 input_keys.size(),
49 "Incorrect number of verification keys provided in "
50 "stdlib verification queue instantiation.");
51 }
52
53 size_t key_idx = 0;
54 while (!verification_queue.empty()) {
55 const VerifierInputs& entry = verification_queue.front();
56
57 // Construct stdlib proof directly from the internal native queue data
58 StdlibProof stdlib_proof(circuit, entry.proof);
59
60 // Use the provided stdlib vkey if present, otherwise construct one from the internal native queue
61 std::shared_ptr<RecursiveVKAndHash> stdlib_vk_and_hash;
62 if (vkeys_provided) {
63 stdlib_vk_and_hash = input_keys[key_idx++];
64 } else {
65 stdlib_vk_and_hash = std::make_shared<RecursiveVKAndHash>(circuit, entry.honk_vk);
66 }
67
68 stdlib_verification_queue.emplace_back(stdlib_proof, stdlib_vk_and_hash, entry.type, entry.is_kernel);
69
70 verification_queue.pop_front(); // the native data is not needed beyond this point
71 }
72}
73
75 ClientCircuit& circuit,
76 const std::shared_ptr<RecursiveVerifierInstance>& verifier_instance,
78 const StdlibProof& proof)
79{
80 using VerifierCommitments = typename RecursiveFlavor::VerifierCommitments;
82
83 transcript->load_proof(proof);
84 OinkRecursiveVerifier verifier{ verifier_instance, transcript };
85 verifier.verify();
86
87 verifier_instance->target_sum = StdlibFF::from_witness_index(&circuit, circuit.zero_idx());
88 // Get the gate challenges for sumcheck/combiner computation
89 verifier_instance->gate_challenges =
90 transcript->template get_powers_of_challenge<StdlibFF>("gate_challenge", Flavor::VIRTUAL_LOG_N);
91
92 VerifierCommitments commitments{ verifier_instance->vk_and_hash->vk, verifier_instance->witness_commitments };
93 // DeciderRecursiveVerifier's log circuit size is fixed, hence we are using a trivial `padding_indicator_array`.
94 std::vector<StdlibFF> padding_indicator_array(Flavor::VIRTUAL_LOG_N, 1);
95
96 Sumcheck sumcheck(transcript, verifier_instance->alphas, Flavor::VIRTUAL_LOG_N, verifier_instance->target_sum);
97 SumcheckOutput<RecursiveFlavor> sumcheck_output = sumcheck.verify(
98 verifier_instance->relation_parameters, verifier_instance->gate_challenges, padding_indicator_array);
99
100 BB_ASSERT_EQ(sumcheck_output.verified || circuit.has_dummy_witnesses(),
101 true,
102 "Sumcheck: Failed to recursively verify first sumcheck.");
103
104 RecursiveFirstSumcheckOutput output{ .challenge = sumcheck_output.challenge,
105 .claimed_evaluations = sumcheck_output.claimed_evaluations };
106 auto recursive_accumulator = output.batch(verifier_instance, transcript);
107
108 return recursive_accumulator;
109}
110
112 ClientCircuit& circuit,
113 const std::optional<RecursiveVerifierAccumulator>& verifier_accumulator,
114 const std::shared_ptr<RecursiveVerifierInstance>& verifier_instance,
115 const std::shared_ptr<RecursiveTranscript>& transcript,
116 const StdlibProof& proof,
117 std::optional<StdlibFF>& prev_accum_hash,
118 bool is_kernel)
119{
120 BB_ASSERT_NEQ(verifier_accumulator.has_value(),
121 false,
122 "verifier_accumulator cannot be null in folding recursive verification");
123
124 auto incoming_verifier_accumulator =
125 execute_first_sumcheck_recursive_verification(circuit, verifier_instance, transcript, proof);
126
127 // Update previous accumulator hash so that we can set it as a public input
128 if (is_kernel) {
129 prev_accum_hash = verifier_accumulator->hash_through_transcript("", *transcript);
130 }
131
133 auto [verified, new_accumulator] = batching_verifier.verify_proof();
134 BB_ASSERT_EQ(verified || circuit.has_dummy_witnesses(),
135 true,
136 "Batching Sumcheck: Failed to recursively verify sumcheck batching.");
137
138 return RecursiveVerifierAccumulator(new_accumulator.challenge,
139 { new_accumulator.non_shifted_evaluation, new_accumulator.shifted_evaluation },
140 { new_accumulator.non_shifted_commitment, new_accumulator.shifted_commitment });
141}
142
165 ClientCircuit& circuit,
166 const StdlibVerifierInputs& verifier_inputs,
167 const std::optional<RecursiveVerifierAccumulator>& input_verifier_accumulator,
168 const TableCommitments& T_prev_commitments,
169 const std::shared_ptr<RecursiveTranscript>& accumulation_recursive_transcript)
170{
171 auto num_rows = circuit.op_queue->get_num_rows();
172 auto num_ops = circuit.op_queue->get_current_subtable_size();
173 info("NUM ROWS WHEN ENTERING: ", num_rows);
174 info("NUM OPS WHEN ENTERING: ", num_ops);
175
177
178 // The pairing points produced by the verification of the decider proof
179 PairingPoints decider_pairing_points;
180
181 // Input commitments to be passed to the merge recursive verification
182 MergeCommitments merge_commitments{ .T_prev_commitments = T_prev_commitments };
183
184 auto verifier_instance = std::make_shared<RecursiveVerifierInstance>(&circuit, verifier_inputs.honk_vk_and_hash);
185
186 std::optional<RecursiveVerifierAccumulator> output_verifier_accumulator;
187 std::optional<StdlibFF> prev_accum_hash = std::nullopt;
188 // The decider proof exists if the tail kernel has been accumulated
189 bool is_hiding_kernel = !pcs_proof.empty();
190
191 switch (verifier_inputs.type) {
192 case QUEUE_TYPE::OINK: {
193 BB_ASSERT_EQ(input_verifier_accumulator.has_value(), false);
194
195 output_verifier_accumulator = execute_first_sumcheck_recursive_verification(
196 circuit, verifier_instance, accumulation_recursive_transcript, verifier_inputs.proof);
197
198 // T_prev = 0 in the first recursive verification
199 merge_commitments.T_prev_commitments = stdlib::recursion::honk::empty_ecc_op_tables(circuit);
200 break;
201 }
202 case QUEUE_TYPE::PG:
203 case QUEUE_TYPE::PG_TAIL: {
204 output_verifier_accumulator = perform_folding_recursive_verification(circuit,
205 input_verifier_accumulator,
206 verifier_instance,
207 accumulation_recursive_transcript,
208 verifier_inputs.proof,
209 prev_accum_hash,
210 verifier_inputs.is_kernel);
211 break;
212 }
214 using ClaimBatcher = ClaimBatcher_<stdlib::bn254<ClientCircuit>>;
215 using ClaimBatch = ClaimBatcher::Batch;
217 using PCS = typename RecursiveFlavor::PCS;
218
219 BB_ASSERT_EQ(stdlib_verification_queue.size(), size_t(1));
220
222
223 auto final_verifier_accumulator = perform_folding_recursive_verification(circuit,
224 input_verifier_accumulator,
225 verifier_instance,
226 accumulation_recursive_transcript,
227 verifier_inputs.proof,
228 prev_accum_hash,
229 verifier_inputs.is_kernel);
230
231 StdlibProof stdlib_pcs_proof(circuit, pcs_proof);
232 accumulation_recursive_transcript->load_proof(stdlib_pcs_proof);
233
234 // DeciderRecursiveVerifier's log circuit size is fixed, hence we are using a trivial `padding_indicator_array`.
235 std::vector<StdlibFF> padding_indicator_array(RecursiveFlavor::VIRTUAL_LOG_N, 1);
236
237 // Execute Shplemini rounds.
238 ClaimBatcher claim_batcher{ .unshifted = ClaimBatch{ final_verifier_accumulator.batched_commitments[0],
239 final_verifier_accumulator.batched_evaluations[0] },
240 .shifted = ClaimBatch{ final_verifier_accumulator.batched_commitments[1],
241 final_verifier_accumulator.batched_evaluations[1] } };
242 const auto opening_claim = Shplemini::compute_batch_opening_claim(padding_indicator_array,
243 claim_batcher,
244 final_verifier_accumulator.challenge,
245 RecursiveCommitment::one(&circuit),
246 accumulation_recursive_transcript);
247 decider_pairing_points =
248 PCS::reduce_verify_batch_opening_claim(opening_claim, accumulation_recursive_transcript);
249
250 BB_ASSERT_EQ(output_verifier_accumulator.has_value(), false);
251 break;
252 }
253 default: {
254 throw_or_abort("Invalid queue type! Only OINK, PG, PG_TAIL and PG_FINAL are supported");
255 }
256 }
257
258 // Extract the witness commitments and public inputs from the incoming verifier instance
259 WitnessCommitments witness_commitments = std::move(verifier_instance->witness_commitments);
260 std::vector<StdlibFF> public_inputs = std::move(verifier_instance->public_inputs);
261
262 PairingPoints nested_pairing_points; // to be extracted from public inputs of app or kernel proof just verified
263
264 if (verifier_inputs.is_kernel) {
265 // Reconstruct the input from the previous kernel from its public inputs
266 KernelIO kernel_input; // pairing points, databus return data commitments
267 kernel_input.reconstruct_from_public(public_inputs);
268 nested_pairing_points = kernel_input.pairing_inputs;
269 // Perform databus consistency checks
270 kernel_input.kernel_return_data.incomplete_assert_equal(witness_commitments.calldata);
271 kernel_input.app_return_data.incomplete_assert_equal(witness_commitments.secondary_calldata);
272
273 // T_prev is read by the public input of the previous kernel K_{i-1} at the beginning of the recursive
274 // verification of of the folding of K_{i-1} (kernel), A_{i,1} (app), .., A_{i, n} (app). This verification
275 // happens in K_{i}
276 merge_commitments.T_prev_commitments = std::move(kernel_input.ecc_op_tables);
277
278 BB_ASSERT_EQ(verifier_inputs.type == QUEUE_TYPE::PG || verifier_inputs.type == QUEUE_TYPE::PG_TAIL ||
279 verifier_inputs.type == QUEUE_TYPE::PG_FINAL,
280 true,
281 "Kernel circuits should be folded.");
282 // Get the previous accum hash
283 info("PG accum hash from IO: ", kernel_input.output_pg_accum_hash);
284 ASSERT(prev_accum_hash.has_value());
285 kernel_input.output_pg_accum_hash.assert_equal(*prev_accum_hash);
286
287 if (!is_hiding_kernel) {
288 // The hiding kernel has no return data; it uses the traditional public-inputs mechanism
289 bus_depot.set_kernel_return_data_commitment(witness_commitments.return_data);
290 }
291 } else {
292 // Reconstruct the input from the previous app from its public inputs
293 AppIO app_input; // pairing points
294 app_input.reconstruct_from_public(public_inputs);
295 nested_pairing_points = app_input.pairing_inputs;
296
297 // Set the app return data commitment to be propagated via the public inputs
298 bus_depot.set_app_return_data_commitment(witness_commitments.return_data);
299 }
300
301 info("NUM ROWS DIFF: ", circuit.op_queue->get_num_rows() - num_rows);
302 info("NUM OPS DIFF: ", circuit.op_queue->get_current_subtable_size() - num_ops);
303
304 // Extract the commitments to the subtable corresponding to the incoming circuit
305 merge_commitments.t_commitments = witness_commitments.get_ecc_op_wires().get_copy();
306
307 // Recursively verify the corresponding merge proof
308 auto [pairing_points, merged_table_commitments] =
309 goblin.recursively_verify_merge(circuit, merge_commitments, accumulation_recursive_transcript);
310
311 pairing_points.aggregate(nested_pairing_points);
312 if (is_hiding_kernel) {
313 pairing_points.aggregate(decider_pairing_points);
314 // Add randomness at the end of the hiding kernel (whose ecc ops fall right at the end of the op queue table) to
315 // ensure the CIVC proof doesn't leak information about the actual content of the op queue
317 }
318
319 info("NUM ROWS DIFF AT THE END: ", circuit.op_queue->get_num_rows() - num_rows);
320 info("NUM OPS DIFF AT THE END: ", circuit.op_queue->get_current_subtable_size() - num_ops);
321
322 return { output_verifier_accumulator, pairing_points, merged_table_commitments };
323}
324
334{
335
336 // Transcript to be shared shared across recursive verification of the folding of K_{i-1} (kernel), A_{i,1} (app),
337 // .., A_{i, n} (app) (all circuits accumulated between the previous kernel and current one)
338 auto accumulation_recursive_transcript = std::make_shared<RecursiveTranscript>();
339
340 // Commitment to the previous state of the op_queue in the recursive verification
341 TableCommitments T_prev_commitments;
342
343 // Instantiate stdlib verifier inputs from their native counterparts
344 if (stdlib_verification_queue.empty()) {
346 }
347
348 bool is_init_kernel =
350
351 bool is_tail_kernel =
353
354 bool is_hiding_kernel =
356
357 // The ECC-op subtable for a kernel begins with an eq-and-reset to ensure that the preceeding circuit's subtable
358 // cannot affect the ECC-op accumulator for the kernel. For the tail kernel, we additionally add a preceeding no-op
359 // to ensure the op queue wires in translator are shiftable, i.e. their 0th coefficient is 0. (The tail kernel
360 // subtable is at the top of the final aggregate table since it is the last to be prepended).
361 if (is_tail_kernel) {
362 BB_ASSERT_EQ(circuit.op_queue->get_current_subtable_size(),
363 0U,
364 "tail kernel ecc ops table should be empty at this point");
365 circuit.queue_ecc_no_op();
366 // Add randomness at the begining of the tail kernel (whose ecc ops fall at the beginning of the op queue table)
367 // to ensure the CIVC proof doesn't leak information about the actual content of the op queue
369 }
370 circuit.queue_ecc_eq();
371
372 // Perform Oink/PG and Merge recursive verification + databus consistency checks for each entry in the queue
373 PairingPoints points_accumulator;
374 std::optional<RecursiveVerifierAccumulator> current_stdlib_verifier_accumulator;
375 if (!is_init_kernel) {
376 current_stdlib_verifier_accumulator = RecursiveVerifierAccumulator(&circuit, recursive_verifier_native_accum);
377 }
378 while (!stdlib_verification_queue.empty()) {
379 const StdlibVerifierInputs& verifier_input = stdlib_verification_queue.front();
380
381 auto [output_stdlib_verifier_accumulator, pairing_points, merged_table_commitments] =
383 verifier_input,
384 current_stdlib_verifier_accumulator,
385 T_prev_commitments,
386 accumulation_recursive_transcript);
387 points_accumulator.aggregate(pairing_points);
388 // Update commitment to the status of the op_queue
389 T_prev_commitments = merged_table_commitments;
390 // Update the output verifier accumulator
391 current_stdlib_verifier_accumulator = output_stdlib_verifier_accumulator;
392
393 stdlib_verification_queue.pop_front();
394 }
395 // Set the kernel output data to be propagated via the public inputs
396 if (is_hiding_kernel) {
397 BB_ASSERT_EQ(current_stdlib_verifier_accumulator.has_value(), false);
398 HidingKernelIO hiding_output{ points_accumulator, T_prev_commitments };
399 hiding_output.set_public();
400 } else {
401 BB_ASSERT_NEQ(current_stdlib_verifier_accumulator.has_value(), false);
402 // Extract native verifier accumulator from the stdlib accum for use on the next round
403 recursive_verifier_native_accum = current_stdlib_verifier_accumulator->get_value();
404
405 KernelIO kernel_output;
406 kernel_output.pairing_inputs = points_accumulator;
409 kernel_output.ecc_op_tables = T_prev_commitments;
410 RecursiveTranscript hash_transcript;
411 kernel_output.output_pg_accum_hash =
412 current_stdlib_verifier_accumulator->hash_through_transcript("", hash_transcript);
413 info("kernel output pg hash: ", kernel_output.output_pg_accum_hash);
414 kernel_output.set_public();
415 }
416}
417
419 const std::shared_ptr<ProverInstance>& prover_instance,
421 const std::shared_ptr<Transcript>& transcript)
422{
423 vinfo("computing sumcheck proof...");
424 MegaOinkProver oink_prover{ prover_instance, honk_vk, transcript };
425 oink_prover.prove();
426
427 // Determine the number of rounds in the sumcheck based on whether or not padding is employed
428 const size_t virtual_log_n = Flavor::VIRTUAL_LOG_N;
429
430 prover_instance->gate_challenges =
431 transcript->template get_powers_of_challenge<FF>("Sumcheck:gate_challenge", virtual_log_n);
432
433 // Run sumcheck
434 DeciderProver decider_prover(prover_instance, transcript);
435 decider_prover.execute_relation_check_rounds();
436
437 // First sumcheck output
438 info("Dyadic size: ", prover_instance->dyadic_size());
439 FirstSumcheckOutput sumcheck_output{ .challenge = decider_prover.sumcheck_output.challenge,
440 .claimed_evaluations = decider_prover.sumcheck_output.claimed_evaluations,
441 .full_batched_size = prover_instance->dyadic_size() };
442
443 // Commitments
444 Flavor::VerifierCommitments verifier_commitments(honk_vk, prover_instance->commitments);
445
446 // Batching
447 ProverAccumulator result = sumcheck_output.batch(prover_instance->polynomials, verifier_commitments, transcript);
448
449 return result;
450}
451
453 const std::shared_ptr<VerifierInstance>& verifier_instance,
454 const std::shared_ptr<Transcript>& transcript,
455 const HonkProof& proof)
456{
457 using VerifierCommitments = typename Flavor::VerifierCommitments;
458 using Sumcheck = ::bb::SumcheckVerifier<Flavor>;
459
460 transcript->load_proof(proof);
461 OinkVerifier<Flavor> verifier{ verifier_instance, transcript };
462 verifier.verify();
463
464 verifier_instance->target_sum = 0;
465 // Get the gate challenges for sumcheck/combiner computation
466 verifier_instance->gate_challenges =
467 transcript->template get_powers_of_challenge<FF>("gate_challenge", Flavor::VIRTUAL_LOG_N);
468
469 VerifierCommitments commitments{ verifier_instance->vk, verifier_instance->witness_commitments };
470 // DeciderVerifier's log circuit size is fixed, hence we are using a trivial `padding_indicator_array`.
471 std::vector<FF> padding_indicator_array(Flavor::VIRTUAL_LOG_N, 1);
472
473 Sumcheck sumcheck(transcript, verifier_instance->alphas, Flavor::VIRTUAL_LOG_N, verifier_instance->target_sum);
474 SumcheckOutput<Flavor> sumcheck_output = sumcheck.verify(
475 verifier_instance->relation_parameters, verifier_instance->gate_challenges, padding_indicator_array);
476
477 BB_ASSERT_EQ(sumcheck_output.verified, true, "Sumcheck: Failed native first sumcheck verification.");
478
479 FirstSumcheckOutput output{ .challenge = sumcheck_output.challenge,
480 .claimed_evaluations = sumcheck_output.claimed_evaluations };
481 auto accumulator = output.batch(verifier_instance, transcript);
482
483 return accumulator;
484}
485
486HonkProof SumcheckClientIVC::construct_sumcheck_proof(const std::shared_ptr<ProverInstance>& prover_instance,
488 const std::shared_ptr<Transcript>& transcript)
489{
491 return transcript->export_proof();
492}
493
494HonkProof SumcheckClientIVC::construct_folding_proof(const std::shared_ptr<ProverInstance>& prover_instance,
496 const std::shared_ptr<Transcript>& transcript)
497{
498 vinfo("computing folding proof...");
499
500 auto verifier_instance = std::make_shared<VerifierInstance_<Flavor>>(honk_vk);
501
502 ProverAccumulator incoming_accumulator =
503 SumcheckClientIVC::execute_first_sumcheck(prover_instance, honk_vk, transcript);
504
505 MultilinearBatchingProverClaim accumulator_claim{
507 .shifted_evaluation = prover_accumulator.batched_evaluations[1],
508 .non_shifted_evaluation = prover_accumulator.batched_evaluations[0],
509 .non_shifted_polynomial = prover_accumulator.batched_polynomials[0],
510 .shifted_polynomial = prover_accumulator.batched_polynomials[1],
511 .non_shifted_commitment = prover_accumulator.batched_commitments[0],
512 .shifted_commitment = prover_accumulator.batched_commitments[1],
513 .dyadic_size = prover_accumulator.dyadic_size
514 };
515
516 MultilinearBatchingProverClaim incoming_claim{
517 .challenge = incoming_accumulator.challenge,
518 .shifted_evaluation = incoming_accumulator.batched_evaluations[1],
519 .non_shifted_evaluation = incoming_accumulator.batched_evaluations[0],
520 .non_shifted_polynomial = incoming_accumulator.batched_polynomials[0],
521 .shifted_polynomial = incoming_accumulator.batched_polynomials[1],
522 .non_shifted_commitment = incoming_accumulator.batched_commitments[0],
523 .shifted_commitment = incoming_accumulator.batched_commitments[1],
524 .dyadic_size = incoming_accumulator.dyadic_size
525 };
526
529 transcript);
530
531 HonkProof proof = batching_prover.construct_proof();
532 batching_prover.compute_new_claim();
533 auto new_accumulator = batching_prover.get_new_claim();
534
536 .challenge = new_accumulator.challenge,
537 .batched_evaluations = { new_accumulator.non_shifted_evaluation, new_accumulator.shifted_evaluation },
538 .batched_polynomials = { new_accumulator.non_shifted_polynomial, new_accumulator.shifted_polynomial },
539 .batched_commitments = { new_accumulator.non_shifted_commitment, new_accumulator.shifted_commitment },
540 .dyadic_size = new_accumulator.dyadic_size,
541 };
542
543 return proof;
544}
545
550{
551 // first app
552 if (num_circuits_accumulated == 0) {
553 return QUEUE_TYPE::OINK;
554 }
555 // app (excluding first) or kernel (inner or reset)
557 return QUEUE_TYPE::PG;
558 }
559 // last kernel prior to tail kernel
561 return QUEUE_TYPE::PG_TAIL;
562 }
563 // tail kernel
566 }
567 // hiding kernel
569 return QUEUE_TYPE::MEGA;
570 }
571 return QUEUE_TYPE{};
572}
573
585{
588 "SumcheckClientIVC: Attempting to accumulate more circuits than expected.");
589
590 ASSERT(precomputed_vk != nullptr, "SumcheckClientIVC::accumulate - VK expected for the provided circuit");
591
592 // Construct the prover instance for circuit
593 std::shared_ptr<ProverInstance> prover_instance = std::make_shared<ProverInstance>(circuit);
594
595 // If the current circuit overflows past the current size of the commitment key, reinitialize accordingly.
596 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1319)
597 if (prover_instance->dyadic_size() > bn254_commitment_key.dyadic_size) {
598 bn254_commitment_key = CommitmentKey<curve::BN254>(prover_instance->dyadic_size());
600 }
601 prover_instance->commitment_key = bn254_commitment_key;
602
603 // We're accumulating a kernel if the verification queue is empty (because the kernel circuit contains recursive
604 // verifiers for all the entries previously present in the verification queue) and if it's not the first accumulate
605 // call (which will always be for an app circuit).
606 bool is_kernel = verification_queue.empty() && num_circuits_accumulated > 0;
607
608 // Transcript to be shared across folding of K_{i} (kernel) (the current kernel), A_{i+1,1} (app), .., A_{i+1,
609 // n} (app)
610 if (is_kernel) {
612 }
613
614 // make a copy of the prover_accumulation_transcript for the verifier to use
615 auto verifier_transcript =
617
618 QUEUE_TYPE queue_type = get_queue_type();
619 HonkProof proof;
620 switch (queue_type) {
621 case QUEUE_TYPE::OINK:
622 vinfo("Accumulating first app circuit with OINK");
623 BB_ASSERT_EQ(is_kernel, false, "First circuit accumulated must always be an app");
624 proof = construct_sumcheck_proof(prover_instance, precomputed_vk, prover_accumulation_transcript);
625 break;
626 case QUEUE_TYPE::PG:
628 proof = construct_folding_proof(prover_instance, precomputed_vk, prover_accumulation_transcript);
629 break;
631 proof = construct_folding_proof(prover_instance, precomputed_vk, prover_accumulation_transcript);
633 break;
634 case QUEUE_TYPE::MEGA:
635 proof = construct_honk_proof_for_hiding_kernel(circuit, precomputed_vk);
636 break;
637 }
638
639 VerifierInputs queue_entry{ std::move(proof), precomputed_vk, queue_type, is_kernel };
640 verification_queue.push_back(queue_entry);
641
642 // Update native verifier accumulator and construct merge proof (excluded for hiding kernel since PG terminates with
643 // tail kernel and hiding merge proof is constructed as part of goblin proving)
644 if (queue_entry.type != QUEUE_TYPE::MEGA) {
645 update_native_verifier_accumulator(queue_entry, verifier_transcript);
647 }
648
650}
651
665{
666 Point random_point = Point::random_element();
667 FF random_scalar = FF::random_element();
668 circuit.queue_ecc_mul_accum(random_point, random_scalar);
669 circuit.queue_ecc_eq();
670}
671
722
736
742 ClientCircuit& circuit, const std::shared_ptr<MegaVerificationKey>& verification_key)
743{
744 // Note: a structured trace is not used for the hiding kernel
745 auto hiding_prover_inst = std::make_shared<DeciderZKProvingKey>(circuit, TraceSettings(), bn254_commitment_key);
746
747 // Hiding circuit is proven by a MegaZKProver
748 MegaZKProver prover(hiding_prover_inst, verification_key, transcript);
749 HonkProof proof = prover.construct_proof();
750
751 return proof;
752}
753
760{
761 // deallocate the protogalaxy accumulator
763 auto mega_proof = verification_queue.front().proof;
764
765 // A transcript is shared between the Hiding circuit prover and the Goblin prover
767
768 // Returns a proof for the hiding circuit and the Goblin proof. The latter consists of Translator and ECCVM proof
769 // for the whole ecc op table and the merge proof for appending the subtable coming from the hiding circuit. The
770 // final merging is done via appending to facilitate creating a zero-knowledge merge proof. This enables us to add
771 // randomness to the beginning of the tail kernel and the end of the hiding kernel, hiding the commitments and
772 // evaluations of both the previous table and the incoming subtable.
773 // https://github.com/AztecProtocol/barretenberg/issues/1360
774 return { mega_proof, goblin.prove(MergeSettings::APPEND) };
775};
776
778{
780 // Create a transcript to be shared by MegaZK-, Merge-, ECCVM-, and Translator- Verifiers.
782 // Verify the hiding circuit proof
783 MegaZKVerifier verifier{ vk.mega, /*ipa_verification_key=*/{}, civc_verifier_transcript };
784 auto [mega_verified, T_prev_commitments] = verifier.template verify_proof<bb::HidingKernelIO>(proof.mega_proof);
785 vinfo("Mega verified: ", mega_verified);
786 // Extract the commitments to the subtable corresponding to the incoming circuit
787 TableCommitments t_commitments = verifier.verifier_instance->witness_commitments.get_ecc_op_wires().get_copy();
788
789 // Goblin verification (final merge, eccvm, translator)
790 bool goblin_verified = Goblin::verify(
791 proof.goblin_proof, { t_commitments, T_prev_commitments }, civc_verifier_transcript, MergeSettings::APPEND);
792 vinfo("Goblin verified: ", goblin_verified);
793
794 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1396): State tracking in CIVC verifiers.
795 return goblin_verified && mega_verified;
796}
797
803HonkProof SumcheckClientIVC::construct_pcs_proof(const std::shared_ptr<Transcript>& transcript)
804{
805 vinfo("prove PCS...");
806
809
811 size_t actual_size = prover_accumulator.batched_polynomials[0].virtual_size();
812
813 PolynomialBatcher polynomial_batcher(actual_size);
816
817 OpeningClaim prover_opening_claim;
818 prover_opening_claim =
819 ShpleminiProver_<Curve>::prove(actual_size, polynomial_batcher, prover_accumulator.challenge, ck, transcript);
820
821 vinfo("executed multivariate-to-univariate reduction");
822 Flavor::PCS::compute_opening_proof(ck, prover_opening_claim, transcript);
823 vinfo("computed opening proof");
824
825 return transcript->export_proof();
826}
827
828// Proof methods
830{
831 return mega_proof.size() + goblin_proof.size();
832}
833
835{
836 HonkProof proof;
837
838 proof.insert(proof.end(), mega_proof.begin(), mega_proof.end());
839 proof.insert(proof.end(), goblin_proof.merge_proof.begin(), goblin_proof.merge_proof.end());
840 proof.insert(
841 proof.end(), goblin_proof.eccvm_proof.pre_ipa_proof.begin(), goblin_proof.eccvm_proof.pre_ipa_proof.end());
842 proof.insert(proof.end(), goblin_proof.eccvm_proof.ipa_proof.begin(), goblin_proof.eccvm_proof.ipa_proof.end());
843 proof.insert(proof.end(), goblin_proof.translator_proof.begin(), goblin_proof.translator_proof.end());
844 return proof;
845};
846
848{
849 HonkProof mega_proof;
850 GoblinProof goblin_proof;
851
852 size_t custom_public_inputs_size = fields.size() - SumcheckClientIVC::Proof::PROOF_LENGTH();
853
854 // Mega proof
855 auto start_idx = fields.begin();
856 auto end_idx = start_idx + static_cast<std::ptrdiff_t>(
858 bb::HidingKernelIO::PUBLIC_INPUTS_SIZE + custom_public_inputs_size);
859 mega_proof.insert(mega_proof.end(), start_idx, end_idx);
860
861 // Merge proof
862 start_idx = end_idx;
863 end_idx += static_cast<std::ptrdiff_t>(MERGE_PROOF_SIZE);
864 goblin_proof.merge_proof.insert(goblin_proof.merge_proof.end(), start_idx, end_idx);
865
866 // ECCVM pre-ipa proof
867 start_idx = end_idx;
868 end_idx += static_cast<std::ptrdiff_t>(ECCVMFlavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS - IPA_PROOF_LENGTH);
869 goblin_proof.eccvm_proof.pre_ipa_proof.insert(goblin_proof.eccvm_proof.pre_ipa_proof.end(), start_idx, end_idx);
870
871 // ECCVM ipa proof
872 start_idx = end_idx;
873 end_idx += static_cast<std::ptrdiff_t>(IPA_PROOF_LENGTH);
874 goblin_proof.eccvm_proof.ipa_proof.insert(goblin_proof.eccvm_proof.ipa_proof.end(), start_idx, end_idx);
875
876 // Translator proof
877 start_idx = end_idx;
879 goblin_proof.translator_proof.insert(goblin_proof.translator_proof.end(), start_idx, end_idx);
880
881 return { mega_proof, goblin_proof };
882};
883
885{
886 msgpack::sbuffer buffer;
887 msgpack::pack(buffer, *this);
888 return buffer;
889}
890
892{
893 msgpack::sbuffer buffer = to_msgpack_buffer();
894
895 std::vector<uint8_t> buf(buffer.data(), buffer.data() + buffer.size());
896 return to_heap_buffer(buf);
897}
898
900{
901 auto uint8_buffer = from_buffer<std::vector<uint8_t>>(buffer);
902
903 msgpack::sbuffer sbuf;
904 sbuf.write(reinterpret_cast<char*>(uint8_buffer.data()), uint8_buffer.size());
905
906 return from_msgpack_buffer(sbuf);
907}
908
910{
911 msgpack::object_handle oh = msgpack::unpack(buffer.data(), buffer.size());
912 msgpack::object obj = oh.get();
913 Proof proof;
914 obj.convert(proof);
915 return proof;
916}
917
918void SumcheckClientIVC::Proof::to_file_msgpack(const std::string& filename) const
919{
920 msgpack::sbuffer buffer = to_msgpack_buffer();
921 std::ofstream ofs(filename, std::ios::binary);
922 if (!ofs.is_open()) {
923 throw_or_abort("Failed to open file for writing.");
924 }
925 ofs.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
926 ofs.close();
927}
928
930{
931 std::ifstream ifs(filename, std::ios::binary);
932 if (!ifs.is_open()) {
933 throw_or_abort("Failed to open file for reading.");
934 }
935
936 ifs.seekg(0, std::ios::end);
937 size_t file_size = static_cast<size_t>(ifs.tellg());
938 ifs.seekg(0, std::ios::beg);
939
940 std::vector<char> buffer(file_size);
941 ifs.read(buffer.data(), static_cast<std::streamsize>(file_size));
942 ifs.close();
943 msgpack::sbuffer msgpack_buffer;
944 msgpack_buffer.write(buffer.data(), file_size);
945
946 return Proof::from_msgpack_buffer(msgpack_buffer);
947}
948
949// VerificationKey construction
951{
952 BB_ASSERT_EQ(verification_queue.size(), 1UL);
953 BB_ASSERT_EQ(verification_queue.front().type == QUEUE_TYPE::MEGA, true);
954 auto verification_key = verification_queue.front().honk_vk;
955 return { verification_key,
958}
959
961 const std::shared_ptr<Transcript>& verifier_transcript)
962{
963 auto verifier_inst = std::make_shared<VerifierInstance>(queue_entry.honk_vk);
964
965 auto incoming_accumulator =
966 execute_first_sumcheck_native_verification(verifier_inst, verifier_transcript, queue_entry.proof);
967
968 if (queue_entry.type != QUEUE_TYPE::OINK) {
969 MultilinearBatchingVerifier<MultilinearBatchingFlavor> batching_verifier(verifier_transcript);
970 auto [verified, new_accumulator] = batching_verifier.verify_proof();
971 BB_ASSERT_EQ(verified, true, "Batching Sumcheck: Failed native sumcheck batching verification");
972
974 .challenge = new_accumulator.challenge,
975 .batched_evaluations = { new_accumulator.non_shifted_evaluation, new_accumulator.shifted_evaluation },
976 .batched_commitments = { new_accumulator.non_shifted_commitment, new_accumulator.shifted_commitment },
977 };
978 } else {
979 native_verifier_accum = incoming_accumulator;
980 }
981}
982
983} // namespace bb
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:118
#define BB_ASSERT_NEQ(actual, expected,...)
Definition assert.hpp:103
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:88
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:148
#define ASSERT(expression,...)
Definition assert.hpp:77
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
static std::shared_ptr< BaseTranscript > convert_prover_transcript_to_verifier_transcript(const std::shared_ptr< BaseTranscript > &prover_transcript)
Convert a prover transcript to a verifier transcript.
CommitmentKey object over a pairing group 𝔾₁.
SumcheckOutput< Flavor > sumcheck_output
BB_PROFILE void execute_relation_check_rounds()
Run Sumcheck to establish that ∑_i pow(\vec{β*})f_i(ω) = e*. This results in u = (u_1,...
static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS
Class responsible for computation of the batched multilinear polynomials required by the Gemini proto...
Definition gemini.hpp:129
void set_to_be_shifted_by_one(RefVector< Polynomial > polynomials)
Definition gemini.hpp:167
void set_unshifted(RefVector< Polynomial > polynomials)
Definition gemini.hpp:166
static bool verify(const GoblinProof &proof, const MergeCommitments &merge_commitments, const std::shared_ptr< Transcript > &transcript, const MergeSettings merge_settings=MergeSettings::PREPEND)
Verify a full Goblin proof (ECCVM, Translator, merge)
Definition goblin.cpp:93
std::pair< PairingPoints, RecursiveTableCommitments > recursively_verify_merge(MegaBuilder &builder, const RecursiveMergeCommitments &merge_commitments, const std::shared_ptr< RecursiveTranscript > &transcript, const MergeSettings merge_settings=MergeSettings::PREPEND)
Recursively verify the next merge proof in the merge verification queue.
Definition goblin.cpp:73
MergeVerifier::TableCommitments TableCommitments
Definition goblin.hpp:41
void prove_merge(const std::shared_ptr< Transcript > &transcript=std::make_shared< Transcript >(), const MergeSettings merge_settings=MergeSettings::PREPEND)
Construct a merge proof for the goblin ECC ops in the provided circuit; append the proof to the merge...
Definition goblin.cpp:25
GoblinProof prove(const MergeSettings merge_settings=MergeSettings::PREPEND)
Constuct a full Goblin proof (ECCVM, Translator, merge)
Definition goblin.cpp:52
CommitmentKey< curve::BN254 > commitment_key
Definition goblin.hpp:49
std::shared_ptr< Transcript > transcript
Definition goblin.hpp:55
static constexpr size_t PUBLIC_INPUTS_SIZE
void queue_ecc_random_op()
Mechanism for populating two rows with randomness. This "operation" doesn't return a tuple representi...
ecc_op_tuple queue_ecc_mul_accum(const g1::affine_element &point, const FF &scalar, bool in_finalize=false)
Add point mul-then-accumulate operation to the op queue and add corresponding gates.
std::shared_ptr< ECCOpQueue > op_queue
ecc_op_tuple queue_ecc_eq(bool in_finalize=true)
Add point equality operation to the op queue based on the value of the internal accumulator and add c...
ecc_op_tuple queue_ecc_no_op()
Logic for a no-op operation.
Container for all witness polynomials used/constructed by the prover.
static constexpr size_t VIRTUAL_LOG_N
VerifierCommitments_< Commitment, VerificationKey > VerifierCommitments
static constexpr size_t VIRTUAL_LOG_N
MegaFlavor::VerifierCommitments_< Commitment, VerificationKey > VerifierCommitments
static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS(size_t virtual_log_n=MegaFlavor::VIRTUAL_LOG_N)
MultilinearBatchingProverClaim get_new_claim()
std::pair< bool, VerifierClaim > verify_proof()
Class for all the oink rounds, which are shared between the folding prover and ultra prover.
void prove()
Oink Prover function that runs all the rounds of the verifier.
Verifier class for all the presumcheck rounds, which are shared between the folding verifier and ultr...
void verify()
Oink Verifier function that runs all the rounds of the verifier.
Unverified claim (C,r,v) for some witness polynomial p(X) such that.
Definition claim.hpp:53
Polynomial p and an opening pair (r,v) such that p(r) = v.
Definition claim.hpp:34
static OpeningClaim prove(const FF circuit_size, PolynomialBatcher &polynomial_batcher, std::span< FF > multilinear_challenge, const CommitmentKey< Curve > &commitment_key, const std::shared_ptr< Transcript > &transcript, const std::array< Polynomial, NUM_SMALL_IPA_EVALUATIONS > &libra_polynomials={}, const std::vector< Polynomial > &sumcheck_round_univariates={}, const std::vector< std::array< FF, 3 > > &sumcheck_round_evaluations={})
Definition shplemini.hpp:35
An efficient verifier for the evaluation proofs of multilinear polynomials and their shifts.
VerificationKey get_vk() const
static VerifierAccumulator execute_first_sumcheck_native_verification(const std::shared_ptr< VerifierInstance > &verifier_instance, const std::shared_ptr< Transcript > &transcript, const HonkProof &proof)
MegaFlavor::CommitmentKey bn254_commitment_key
std::shared_ptr< Transcript > prover_accumulation_transcript
Proof prove()
Construct a proof for the IVC, which, if verified, fully establishes its correctness.
static void hide_op_queue_accumulation_result(ClientCircuit &circuit)
Add a valid operation with random data to the op queue to prevent information leakage in Translator p...
stdlib::recursion::PairingPoints< ClientCircuit > PairingPoints
void instantiate_stdlib_verification_queue(ClientCircuit &circuit, const std::vector< std::shared_ptr< RecursiveVKAndHash > > &input_keys={})
Instantiate a stdlib verification queue for use in the kernel completion logic.
std::shared_ptr< Transcript > transcript
QUEUE_TYPE get_queue_type() const
Get queue type for the proof of a circuit about to be accumulated based on num circuits accumulated s...
VerifierAccumulator recursive_verifier_native_accum
static RecursiveVerifierAccumulator perform_folding_recursive_verification(ClientCircuit &circuit, const std::optional< RecursiveVerifierAccumulator > &verifier_accumulator, const std::shared_ptr< RecursiveVerifierInstance > &verifier_instance, const std::shared_ptr< RecursiveTranscript > &transcript, const StdlibProof &proof, std::optional< StdlibFF > &prev_accum_hash, bool is_kernel)
static bool verify(const Proof &proof, const VerificationKey &vk)
std::array< RecursiveFlavor::Commitment, ClientCircuit::NUM_WIRES > TableCommitments
VerifierAccumulator native_verifier_accum
VerificationQueue verification_queue
void update_native_verifier_accumulator(const VerifierInputs &queue_entry, const std::shared_ptr< Transcript > &verifier_transcript)
Runs either Oink or PG native verifier to update the native verifier accumulator.
Flavor::Curve::AffineElement Point
ProverAccumulator prover_accumulator
HonkProof construct_sumcheck_proof(const std::shared_ptr< ProverInstance > &prover_instance, const std::shared_ptr< MegaVerificationKey > &honk_vk, const std::shared_ptr< Transcript > &transcript)
HonkProof construct_honk_proof_for_hiding_kernel(ClientCircuit &circuit, const std::shared_ptr< MegaVerificationKey > &verification_key)
Construct a zero-knowledge proof for the hiding circuit, which recursively verifies the last folding,...
static ProverAccumulator execute_first_sumcheck(const std::shared_ptr< ProverInstance > &prover_instance, const std::shared_ptr< MegaVerificationKey > &honk_vk, const std::shared_ptr< Transcript > &transcript)
std::tuple< std::optional< RecursiveVerifierAccumulator >, PairingPoints, TableCommitments > perform_recursive_verification_and_databus_consistency_checks(ClientCircuit &circuit, const StdlibVerifierInputs &verifier_inputs, const std::optional< RecursiveVerifierAccumulator > &input_verifier_accumulator, const TableCommitments &T_prev_commitments, const std::shared_ptr< RecursiveTranscript > &accumulation_recursive_transcript)
Populate the provided circuit with constraints for (1) recursive verification of the provided accumul...
HonkProof construct_folding_proof(const std::shared_ptr< ProverInstance > &prover_instance, const std::shared_ptr< MegaVerificationKey > &honk_vk, const std::shared_ptr< Transcript > &transcript)
HonkProof construct_pcs_proof(const std::shared_ptr< Transcript > &transcript)
Internal method for constructing a decider proof.
StdlibVerificationQueue stdlib_verification_queue
void accumulate(ClientCircuit &circuit, const std::shared_ptr< MegaVerificationKey > &precomputed_vk) override
Perform prover work for accumulation (e.g. PG folding, merge proving)
static void hide_op_queue_content_in_tail(ClientCircuit &circuit)
Adds three random ops to the tail kernel.
SumcheckClientIVC(size_t num_circuits)
void complete_kernel_circuit_logic(ClientCircuit &circuit)
Append logic to complete a kernel circuit.
static RecursiveVerifierAccumulator execute_first_sumcheck_recursive_verification(ClientCircuit &circuit, const std::shared_ptr< RecursiveVerifierInstance > &verifier_instance, const std::shared_ptr< RecursiveTranscript > &transcript, const StdlibProof &proof)
static void hide_op_queue_content_in_hiding(ClientCircuit &circuit)
Adds two random ops to the hiding kernel.
Implementation of the sumcheck Verifier for statements of the form for multilinear polynomials .
Definition sumcheck.hpp:698
static constexpr size_t CONST_TRANSLATOR_LOG_N
static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS
Commitment get_kernel_return_data_commitment(Builder &builder)
Get the previously set kernel return data commitment if it exists, else a default one.
Definition databus.hpp:128
Commitment get_app_return_data_commitment(Builder &builder)
Get the previously set app return data commitment if it exists, else a default one.
Definition databus.hpp:141
void set_app_return_data_commitment(const Commitment &commitment)
Definition databus.hpp:105
void set_kernel_return_data_commitment(const Commitment &commitment)
Definition databus.hpp:99
Manages the data that is propagated on the public inputs of an application/function circuit.
void reconstruct_from_public(const std::vector< FF > &public_inputs)
Reconstructs the IO components from a public inputs array.
Manages the data that is propagated on the public inputs of a hiding kernel circuit.
Manages the data that is propagated on the public inputs of a kernel circuit.
void reconstruct_from_public(const std::vector< FF > &public_inputs)
Reconstructs the IO components from a public inputs array.
void set_public()
Set each IO component to be a public input of the underlying circuit.
#define vinfo(...)
Definition log.hpp:79
void info(Args... args)
Definition log.hpp:74
uint8_t const * buf
Definition data_store.hpp:9
uint8_t buffer[RANDOM_BUFFER_SIZE]
Definition engine.cpp:34
std::array< typename bn254< Builder >::Group, Builder::NUM_WIRES > empty_ecc_op_tables(Builder &builder)
Construct commitments to empty subtables.
Entry point for Barretenberg command-line interface.
std::vector< fr > HonkProof
Definition proof.hpp:15
CommitmentKey< Curve > ck
VerifierCommitmentKey< Curve > vk
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
uint8_t * to_heap_buffer(T const &value)
Logic to support batching opening claims for unshifted and shifted polynomials in Shplemini.
HonkProof pre_ipa_proof
Definition proof.hpp:24
HonkProof ipa_proof
Definition proof.hpp:25
ECCVMProof eccvm_proof
Definition types.hpp:22
HonkProof merge_proof
Definition types.hpp:21
size_t size() const
Definition types.hpp:25
HonkProof translator_proof
Definition types.hpp:23
A full proof for the IVC scheme containing a Mega proof showing correctness of the hiding circuit (wh...
static constexpr size_t PROOF_LENGTH(size_t virtual_log_n=MegaZKFlavor::VIRTUAL_LOG_N)
The size of a ClientIVC proof with backend-added public inputs: HidingKernelIO.
static Proof from_msgpack_buffer(uint8_t const *&buffer)
static Proof from_file_msgpack(const std::string &filename)
static Proof from_field_elements(const std::vector< SumcheckClientIVC::FF > &fields)
msgpack::sbuffer to_msgpack_buffer() const
std::vector< FF > to_field_elements() const
Serialize proof to field elements.
void to_file_msgpack(const std::string &filename) const
uint8_t * to_msgpack_heap_buffer() const
Very quirky method to convert a msgpack buffer to a "heap" buffer.
std::array< Polynomial< FF >, 2 > batched_polynomials
std::shared_ptr< RecursiveVKAndHash > honk_vk_and_hash
std::shared_ptr< MegaVerificationKey > honk_vk
Contains the evaluations of multilinear polynomials at the challenge point . These are computed by S...
ClaimedEvaluations claimed_evaluations
std::vector< FF > challenge
static field random_element(numeric::RNG *engine=nullptr) noexcept
An object storing two EC points that represent the inputs to a pairing check.
void aggregate(PairingPoints const &other)
Compute a linear combination of the present pairing points with an input set of pairing points.
uint32_t set_public()
Set the witness indices for the limbs of the pairing points to public.
void throw_or_abort(std::string const &err)