Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
protogalaxy_recursive_verifier.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
13
15
16template <class DeciderVerificationKeys>
18 const std::vector<FF>& proof)
19{
20 BB_ASSERT_EQ(keys_to_fold.NUM, 2UL, "Protogalaxy only supports folding 2 instances.");
21 transcript->load_proof(proof);
22 auto key = keys_to_fold[0];
23 auto domain_separator = std::to_string(0);
24 if (!key->is_complete) {
25 OinkRecursiveVerifier_<Flavor> oink_verifier{ builder, key, transcript, domain_separator + '_' };
26 oink_verifier.verify();
27 key->target_sum = FF::from_witness(builder, builder->zero_idx);
28 // Get the gate challenges for sumcheck/combiner computation
29 key->gate_challenges =
30 transcript->template get_powers_of_challenge<FF>(domain_separator + "_gate_challenge", CONST_PG_LOG_N);
31 }
32
33 key = keys_to_fold[1];
34 domain_separator = std::to_string(1);
35 OinkRecursiveVerifier_<Flavor> oink_verifier{ builder, key, transcript, domain_separator + '_' };
36 oink_verifier.verify();
37}
38
39template <class DeciderVerificationKeys>
41 DeciderVerificationKeys>::verify_folding_proof(const stdlib::Proof<Builder>& proof)
42{
43 static constexpr size_t BATCHED_EXTENDED_LENGTH = DeciderVerificationKeys::BATCHED_EXTENDED_LENGTH;
44 static constexpr size_t NUM_KEYS = DeciderVerificationKeys::NUM;
45 // The degree of the combiner quotient (K in the paper) is dk - k - 1 = k(d - 1) - 1.
46 // Hence we need k(d - 1) evaluations to represent it.
47 static constexpr size_t COMBINER_LENGTH = BATCHED_EXTENDED_LENGTH - NUM_KEYS;
48
49 run_oink_verifier_on_each_incomplete_key(proof);
50
51 const std::shared_ptr<DeciderVK>& accumulator = keys_to_fold[0];
52
53 // Perturbator round
54 const std::vector<FF> deltas = transcript->template get_powers_of_challenge<FF>("delta", CONST_PG_LOG_N);
55 std::vector<FF> perturbator_coeffs(CONST_PG_LOG_N + 1, 0);
56 for (size_t idx = 1; idx <= CONST_PG_LOG_N; idx++) {
57 perturbator_coeffs[idx] = transcript->template receive_from_prover<FF>("perturbator_" + std::to_string(idx));
58 }
59 const FF perturbator_challenge = transcript->template get_challenge<FF>("perturbator_challenge");
60
61 // Combiner quotient round
62 perturbator_coeffs[0] = accumulator->target_sum;
63 const FF perturbator_evaluation = evaluate_perturbator(perturbator_coeffs, perturbator_challenge);
64
65 std::array<FF, COMBINER_LENGTH> combiner_quotient_evals;
66 for (size_t idx = 0; idx < COMBINER_LENGTH; idx++) {
67 combiner_quotient_evals[idx] =
68 transcript->template receive_from_prover<FF>("combiner_quotient_" + std::to_string(idx + NUM_KEYS));
69 }
70
71 // Folding
72 const FF combiner_challenge = transcript->template get_challenge<FF>("combiner_quotient_challenge");
73 const Univariate<FF, BATCHED_EXTENDED_LENGTH, NUM_KEYS> combiner_quotient(combiner_quotient_evals);
74 const FF combiner_quotient_at_challenge = combiner_quotient.evaluate(combiner_challenge);
75
76 const FF vanishing_polynomial_at_challenge = combiner_challenge * (combiner_challenge - FF(1));
77 const std::vector<FF> lagranges = { FF(1) - combiner_challenge, combiner_challenge };
78
79 /*
80 Fold the commitments
81 Note: we use additional challenges to reduce the amount of elliptic curve work performed by the ECCVM
82
83 For an accumulator commitment [P'] and an instance commitment [P] , we compute folded commitment [P''] where
84 [P''] = L0(combiner_challenge).[P'] + L1(combiner_challenge).[P]
85 For the size-2 case this becomes:
86 P'' = (1 - combiner_challenge).[P'] + combiner_challenge.[P] = combiner_challenge.[P - P'] + [P']
87
88 This requires a large number of size-1 scalar muls (about 53)
89 The ECCVM can perform a size-k MSM in 32 + roundup((k/4)) rows, if each scalar multiplier is <128 bits
90 i.e. number of ECCVM rows = 53 * 64 = painful
91
92 To optimize, we generate challenges `c_i` for each commitment and evaluate the relation:
93
94 [A] = \sum c_i.[P_i]
95 [B] = \sum c_i.[P'_i]
96 [C] = \sum c_i.[P''_i]
97 and validate
98 (1 - combiner_challenge).[A] + combiner_challenge.[B] == [C]
99
100
101 This reduces the relation to 3 large MSMs where each commitment requires 3 size-128bit scalar
102 multiplications For a flavor with 53 instance/witness commitments, this is 53 * 24 rows
103
104 Note: there are more efficient ways to evaluate this relationship if one solely wants to reduce number of
105 scalar muls, however we must also consider the number of ECCVM operations being executed, as each operation
106 incurs a cost in the translator circuit Each ECCVM opcode produces 5 rows in the translator circuit, which is
107 approx. equivalent to 9 ECCVM rows. Something to pay attention to
108 */
109
110 // New transcript for challenge generation
111 Transcript batch_mul_transcript = transcript->branch_transcript();
112
113 std::vector<Commitment> accumulator_commitments;
114 std::vector<Commitment> instance_commitments;
115 for (const auto& precomputed : keys_to_fold.get_precomputed_commitments()) {
116 BB_ASSERT_EQ(precomputed.size(), 2U);
117 accumulator_commitments.emplace_back(precomputed[0]);
118 instance_commitments.emplace_back(precomputed[1]);
119 }
120 for (const auto& witness : keys_to_fold.get_witness_commitments()) {
121 BB_ASSERT_EQ(witness.size(), 2U);
122 accumulator_commitments.emplace_back(witness[0]);
123 instance_commitments.emplace_back(witness[1]);
124 }
125
126 // derive output commitment witnesses
127 std::vector<Commitment> output_commitments;
128 for (size_t i = 0; i < accumulator_commitments.size(); ++i) {
129 const auto lhs_scalar = (FF(1) - combiner_challenge).get_value();
130 const auto rhs_scalar = combiner_challenge.get_value();
131
132 const auto lhs = accumulator_commitments[i].get_value();
133
134 const auto rhs = instance_commitments[i].get_value();
135 const auto output = lhs * lhs_scalar + rhs * rhs_scalar;
136 output_commitments.emplace_back(Commitment::from_witness(builder, output));
137 // Add the output commitment to the transcript to ensure the they can't be spoofed
138 batch_mul_transcript.add_to_hash_buffer("new_accumulator_commitment_" + std::to_string(i),
139 output_commitments[i]);
140 }
141
143 for (size_t idx = 0; idx < Flavor::NUM_FOLDED_ENTITIES; ++idx) {
144 args[idx] = "accumulator_combination_challenges" + std::to_string(idx);
145 }
147 batch_mul_transcript.template get_challenges<FF>(args);
148 std::vector<FF> scalars(folding_challenges.begin(), folding_challenges.end());
149
150 Commitment accumulator_sum = Commitment::batch_mul(accumulator_commitments,
151 scalars,
152 /*max_num_bits=*/0,
153 /*handle_edge_cases=*/IsUltraBuilder<Builder>);
154
155 Commitment instance_sum = Commitment::batch_mul(instance_commitments,
156 scalars,
157 /*max_num_bits=*/0,
158 /*handle_edge_cases=*/IsUltraBuilder<Builder>);
159
160 Commitment output_sum = Commitment::batch_mul(output_commitments,
161 scalars,
162 /*max_num_bits=*/0,
163 /*handle_edge_cases=*/IsUltraBuilder<Builder>);
164
165 Commitment folded_sum = Commitment::batch_mul({ accumulator_sum, instance_sum },
166 lagranges,
167 /*max_num_bits=*/0,
168 /*handle_edge_cases=*/IsUltraBuilder<Builder>);
169
170 output_sum.x.assert_equal(folded_sum.x);
171 output_sum.y.assert_equal(folded_sum.y);
172
173 // Compute next folding parameters
174 accumulator->target_sum =
175 perturbator_evaluation * lagranges[0] + vanishing_polynomial_at_challenge * combiner_quotient_at_challenge;
176
177 accumulator->gate_challenges = update_gate_challenges(perturbator_challenge, accumulator->gate_challenges, deltas);
178
179 // Define a constant virtual log circuit size for the accumulator
180 FF virtual_log_n = FF::from_witness(builder, CONST_PG_LOG_N);
181 virtual_log_n.fix_witness();
182 accumulator->vk_and_hash->vk->log_circuit_size = virtual_log_n;
183
184 // Fold the relation parameters
185 for (auto [combination, to_combine] : zip_view(accumulator->alphas, keys_to_fold.get_alphas())) {
186 combination = linear_combination(to_combine, lagranges);
187 }
188
189 for (auto [combination, to_combine] :
190 zip_view(accumulator->relation_parameters.get_to_fold(), keys_to_fold.get_relation_parameters())) {
191 combination = linear_combination(to_combine, lagranges);
192 }
193
194 auto accumulator_vkey = accumulator->vk_and_hash->vk->get_all();
195 for (size_t i = 0; i < Flavor::NUM_PRECOMPUTED_ENTITIES; ++i) {
196 accumulator_vkey[i] = output_commitments[i];
197 }
198
199 auto accumulator_witnesses = accumulator->witness_commitments.get_all();
200 for (size_t i = 0; i < Flavor::NUM_WITNESS_ENTITIES; ++i) {
201 accumulator_witnesses[i] = output_commitments[i + accumulator_vkey.size()];
202 }
203
204 return accumulator;
205}
206
207// Instantiate the template with specific flavors and builders
208template class ProtogalaxyRecursiveVerifier_<
210template class ProtogalaxyRecursiveVerifier_<
212
213} // namespace bb::stdlib::recursion::honk
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:59
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
void add_to_hash_buffer(const std::string &label, const T &element)
Adds an element to the transcript.
BaseTranscript branch_transcript()
Branch a transcript to perform verifier-only computations.
static constexpr size_t NUM_PRECOMPUTED_ENTITIES
static constexpr size_t NUM_WITNESS_ENTITIES
static constexpr size_t NUM_FOLDED_ENTITIES
A univariate polynomial represented by its values on {domain_start, domain_start + 1,...
Fr evaluate(const Fr &u) const
Evaluate a univariate at a point u not known at compile time and assumed not to be in the domain (els...
A simple wrapper around a vector of stdlib field elements representing a proof.
Definition proof.hpp:19
void run_oink_verifier_on_each_incomplete_key(const std::vector< FF > &)
Process the public data ϕ for the decider verification keys to be folded.
constexpr size_t NUM_KEYS
AluTraceBuilder builder
Definition alu.test.cpp:123
std::vector< FF > update_gate_challenges(const FF &perturbator_challenge, const std::vector< FF > &gate_challenges, const std::vector< FF > &init_challenges)
Compute the gate challenges used in the combiner calculation.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)