barretenberg
Loading...
Searching...
No Matches
verifier.hpp
1#pragma once
2
3#include "barretenberg/ecc/curves/bn254/fq12.hpp"
4#include "barretenberg/ecc/curves/bn254/pairing.hpp"
5#include "barretenberg/flavor/plonk_flavors.hpp"
6#include "barretenberg/plonk/proof_system/public_inputs/public_inputs.hpp"
7#include "barretenberg/plonk/proof_system/types/proof.hpp"
8#include "barretenberg/plonk/proof_system/utils/kate_verification.hpp"
9#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp"
10#include "barretenberg/stdlib/primitives/biggroup/biggroup.hpp"
11#include "barretenberg/stdlib/primitives/bool/bool.hpp"
12#include "barretenberg/stdlib/primitives/field/field.hpp"
13#include "barretenberg/stdlib/recursion/aggregation_state/aggregation_state.hpp"
14#include "barretenberg/stdlib/recursion/transcript/transcript.hpp"
15#include "barretenberg/stdlib/recursion/verifier/program_settings.hpp"
16
17namespace proof_system::plonk {
18namespace stdlib {
19namespace recursion {
20
21template <typename Builder> struct lagrange_evaluations {
22 field_t<Builder> l_start;
23 field_t<Builder> l_end;
24 field_t<Builder> vanishing_poly;
25};
26
27template <typename Curve, typename Transcript, typename program_settings>
28void populate_kate_element_map(typename Curve::Builder* ctx,
29 typename Transcript::Key* key,
30 const Transcript& transcript,
31 std::map<std::string, typename Curve::Group>& kate_g1_elements,
32 std::map<std::string, typename Curve::ScalarField>& kate_fr_elements_at_zeta,
33 std::map<std::string, typename Curve::ScalarField>& kate_fr_elements_at_zeta_large,
34 std::map<std::string, typename Curve::ScalarField>& kate_fr_elements_at_zeta_omega,
35 typename Curve::ScalarField& batch_opening_scalar)
36{
37 using fr_ct = typename Curve::ScalarField;
38 const auto& polynomial_manifest = key->polynomial_manifest;
39 for (size_t i = 0; i < key->polynomial_manifest.size(); ++i) {
40 const auto& item = polynomial_manifest[i];
41 const std::string label(item.commitment_label);
42 const std::string poly_label(item.polynomial_label);
43 switch (item.source) {
44 case PolynomialSource::WITNESS: {
45 // get_circuit_group_element validates that the point produced lies on the curve
46 const auto element = transcript.get_circuit_group_element(label);
47 ASSERT(element.get_value().on_curve());
48 if (element.get_value().is_point_at_infinity()) {
49 std::cerr << label << " witness is point at infinity! Error!" << std::endl;
50 ctx->failure("witness " + label + " is point at infinity");
51 }
52 kate_g1_elements.insert({ label, element });
53 break;
54 }
55 case PolynomialSource::SELECTOR:
56 case PolynomialSource::PERMUTATION: {
57 const auto element = key->commitments.at(label);
58 // TODO: with user-defined circuits, we will need verify that the point
59 // lies on the curve with constraints
60 if (!element.get_value().on_curve()) {
61 std::cerr << label << " commitment not on curve!" << std::endl;
62 }
63 if (element.get_value().is_point_at_infinity()) {
64 std::cerr << label << " commitment is point at infinity! Error!" << std::endl;
65 ctx->failure("commitment " + label + " is point at infinity");
66 }
67 kate_g1_elements.insert({ label, element });
68 break;
69 }
70 case PolynomialSource::OTHER: {
71 break;
72 }
73 }
74 if (item.requires_shifted_evaluation) {
75 const auto challenge = transcript.get_challenge_field_element_from_map("nu", poly_label + "_omega");
76 kate_fr_elements_at_zeta_omega.insert({ label, challenge });
77 } else {
78 const auto challenge = transcript.get_challenge_field_element_from_map("nu", poly_label);
79 kate_fr_elements_at_zeta.insert({ label, challenge });
80 }
81 }
82
83 const auto zeta = transcript.get_challenge_field_element("z", 0);
84 const auto quotient_nu = transcript.get_challenge_field_element_from_map("nu", "t");
85
86 fr_ct z_power = 1;
87 for (size_t i = 0; i < program_settings::program_width; ++i) {
88 std::string quotient_label = "T_" + std::to_string(i + 1);
89 const auto element = transcript.get_circuit_group_element(quotient_label);
90
91 kate_g1_elements.insert({ quotient_label, element });
92 kate_fr_elements_at_zeta_large.insert({ quotient_label, quotient_nu * z_power });
93 z_power *= key->z_pow_n;
94 }
95
96 const auto PI_Z = transcript.get_circuit_group_element("PI_Z");
97 const auto PI_Z_OMEGA = transcript.get_circuit_group_element("PI_Z_OMEGA");
98
99 fr_ct u = transcript.get_challenge_field_element("separator", 0);
100
101 fr_ct batch_evaluation =
102 proof_system::plonk::compute_kate_batch_evaluation<fr_ct, Transcript, program_settings>(key, transcript);
103 batch_opening_scalar = -batch_evaluation;
104
105 kate_g1_elements.insert({ "PI_Z_OMEGA", PI_Z_OMEGA });
106 kate_fr_elements_at_zeta_large.insert({ "PI_Z_OMEGA", zeta * key->domain.root * u });
107
108 kate_g1_elements.insert({ "PI_Z", PI_Z });
109 kate_fr_elements_at_zeta.insert({ "PI_Z", zeta });
110}
111
112template <typename Curve>
113lagrange_evaluations<typename Curve::Builder> get_lagrange_evaluations(
114 const typename Curve::ScalarField& z,
116 const size_t num_roots_cut_out_of_vanishing_polynomial = 4)
117{
118 // compute Z_H*(z), l_start(z), l_{end}(z)
119 // Note that as we modify the vanishing polynomial by cutting out some roots, we must simultaneously ensure that
120 // the lagrange polynomials we require would be l_1(z) and l_{n-k}(z) where k =
121 // num_roots_cut_out_of_vanishing_polynomial. For notational simplicity, we call l_1 as l_start and l_{n-k} as
122 // l_end.
123 //
124 // NOTE: If in future, there arises a need to cut off more zeros, this method will not require any changes.
125 //
126
127 typedef typename Curve::ScalarField fr_ct;
128 typedef typename Curve::Builder Builder;
129
130 fr_ct z_pow = z.pow(field_t<Builder>(domain.size));
131 fr_ct numerator = z_pow - fr_ct(1);
132
133 // compute modified vanishing polynomial Z_H*(z)
134 // (z^{n} - 1)
135 // Z_H*(z) = --------------------------------------------
136 // (z - w^{n-1})(z - w^{n-2})...(z - w^{n - k})
137 //
138 fr_ct denominators_vanishing_poly = fr_ct(1);
139 lagrange_evaluations<Builder> result;
140
141 fr_ct work_root = domain.root_inverse;
142 for (size_t i = 0; i < num_roots_cut_out_of_vanishing_polynomial; ++i) {
143 denominators_vanishing_poly *= (z - work_root);
144 work_root *= domain.root_inverse;
145 }
146 result.vanishing_poly = numerator / denominators_vanishing_poly;
147
148 // The expressions of the lagrange polynomials are:
149 // (X^n - 1)
150 // L_1(X) = -----------
151 // X - 1
152 //
153 // L_{i}(X) = L_1(X.w^{-i+1})
154 // (X^n - 1)
155 // => L_{n-k}(X) = L_1(X.w^{k-n+1}) = L_1(X.w^{k + 1}) = ----------------
156 // (X.w^{k+1} - 1)
157 //
158 numerator *= domain.domain_inverse;
159
160 result.l_start = numerator / (z - fr_ct(1));
161
162 // compute w^{num_roots_cut_out_of_vanishing_polynomial + 1}
163 fr_ct l_end_root = (num_roots_cut_out_of_vanishing_polynomial & 1) ? domain.root.sqr() : domain.root;
164 for (size_t i = 0; i < num_roots_cut_out_of_vanishing_polynomial / 2; ++i) {
165 l_end_root *= domain.root.sqr();
166 }
167 result.l_end = numerator / ((z * l_end_root) - fr_ct(1));
168
169 return result;
170}
171
176template <typename Curve, typename program_settings>
177aggregation_state<Curve> verify_proof(typename Curve::Builder* context,
178 std::shared_ptr<verification_key<Curve>> key,
179 const transcript::Manifest& manifest,
180 const plonk::proof& proof,
181 const aggregation_state<Curve> previous_output = aggregation_state<Curve>())
182{
183 using Builder = typename Curve::Builder;
184
185 key->program_width = program_settings::program_width;
186
187 Transcript<Builder> transcript = Transcript<Builder>(context, proof.proof_data, manifest);
188
189 return verify_proof_<Curve, program_settings>(context, key, transcript, previous_output);
190}
191
196template <typename Curve, typename program_settings>
197aggregation_state<Curve> verify_proof_(typename Curve::Builder* context,
198 std::shared_ptr<verification_key<Curve>> key,
199 Transcript<typename Curve::Builder>& transcript,
200 const aggregation_state<Curve> previous_output = aggregation_state<Curve>())
201{
202 using fr_ct = typename Curve::ScalarField;
203 using fq_ct = typename Curve::BaseField;
204 using g1_ct = typename Curve::Group;
205 using Builder = typename Curve::Builder;
206
207 key->program_width = program_settings::program_width;
208
209 std::map<std::string, g1_ct> kate_g1_elements;
210 std::map<std::string, fr_ct> kate_fr_elements_at_zeta;
211 std::map<std::string, fr_ct> kate_fr_elements_at_zeta_large;
212 std::map<std::string, fr_ct> kate_fr_elements_at_zeta_omega;
213
214 const auto PI_Z = transcript.get_circuit_group_element("PI_Z");
215 const auto PI_Z_OMEGA = transcript.get_circuit_group_element("PI_Z_OMEGA");
216
217 field_t circuit_size = key->n;
218 field_t public_input_size = key->num_public_inputs;
219
220 transcript.add_field_element("circuit_size", circuit_size);
221 transcript.add_field_element("public_input_size", public_input_size);
222
223 transcript.apply_fiat_shamir("init");
224 transcript.apply_fiat_shamir("eta");
225 transcript.apply_fiat_shamir("beta");
226 transcript.apply_fiat_shamir("alpha");
227 transcript.apply_fiat_shamir("z");
228
229 fr_ct alpha = transcript.get_challenge_field_element("alpha");
230 fr_ct zeta = transcript.get_challenge_field_element("z");
231
232 key->z_pow_n = zeta.pow(key->domain.domain);
233
234 lagrange_evaluations<Builder> lagrange_evals = get_lagrange_evaluations<Curve>(zeta, key->domain);
235
236 // reconstruct evaluation of quotient polynomial from prover messages
237
238 fr_ct quotient_numerator_eval = fr_ct(0);
239 program_settings::compute_quotient_evaluation_contribution(key.get(), alpha, transcript, quotient_numerator_eval);
240
241 fr_ct t_eval = quotient_numerator_eval / lagrange_evals.vanishing_poly;
242 transcript.add_field_element("t", t_eval);
243
244 transcript.apply_fiat_shamir("nu");
245 transcript.apply_fiat_shamir("separator");
246
247 fr_ct u = transcript.get_challenge_field_element("separator", 0);
248
249 fr_ct batch_opening_scalar;
250
251 populate_kate_element_map<Curve, Transcript<Builder>, program_settings>(context,
252 key.get(),
253 transcript,
254 kate_g1_elements,
255 kate_fr_elements_at_zeta,
256 kate_fr_elements_at_zeta_large,
257 kate_fr_elements_at_zeta_omega,
258 batch_opening_scalar);
259
260 std::vector<fr_ct> double_opening_scalars;
261 std::vector<g1_ct> double_opening_elements;
262 std::vector<fr_ct> opening_scalars;
263 std::vector<g1_ct> opening_elements;
264 std::vector<fr_ct> big_opening_scalars;
265 std::vector<g1_ct> big_opening_elements;
266 std::vector<g1_ct> elements_to_add;
267
268 for (const auto& [label, fr_value] : kate_fr_elements_at_zeta) {
269 const auto& g1_value = kate_g1_elements[label];
270 if (fr_value.get_value() == 0 && fr_value.witness_index != IS_CONSTANT) {
271 std::cerr << "bad scalar zero at " << label << std::endl;
272 }
273 if (fr_value.get_value() == 0 && fr_value.witness_index == IS_CONSTANT) {
274 std::cerr << "scalar zero at " << label << std::endl;
275 continue;
276 }
277
278 if (fr_value.get_value() == 1 && fr_value.witness_index == IS_CONSTANT) {
279 elements_to_add.emplace_back(g1_value);
280 continue;
281 }
282 opening_scalars.emplace_back(fr_value);
283 opening_elements.emplace_back(g1_value);
284 }
285
286 for (const auto& [label, fr_value] : kate_fr_elements_at_zeta_large) {
287 const auto& g1_value = kate_g1_elements[label];
288 if (fr_value.get_value() == 0 && fr_value.witness_index != IS_CONSTANT) {
289 std::cerr << "bad scalar zero at " << label << std::endl;
290 }
291 if (fr_value.get_value() == 0 && fr_value.witness_index == IS_CONSTANT) {
292 std::cerr << "scalar zero at " << label << std::endl;
293 continue;
294 }
295
296 if (fr_value.get_value() == 1 && fr_value.witness_index == IS_CONSTANT) {
297 elements_to_add.emplace_back(g1_value);
298 continue;
299 }
300 big_opening_scalars.emplace_back(fr_value);
301 big_opening_elements.emplace_back(g1_value);
302 }
303
304 for (const auto& [label, fr_value] : kate_fr_elements_at_zeta_omega) {
305 const auto& g1_value = kate_g1_elements[label];
306 double_opening_scalars.emplace_back(fr_value);
307 double_opening_elements.emplace_back(g1_value);
308 }
309
310 const auto double_opening_result = g1_ct::batch_mul(double_opening_elements, double_opening_scalars, 128);
311
312 opening_elements.emplace_back(double_opening_result);
313 opening_scalars.emplace_back(u);
314
315 std::vector<g1_ct> rhs_elements;
316 std::vector<fr_ct> rhs_scalars;
317
318 rhs_elements.push_back(PI_Z_OMEGA);
319 rhs_scalars.push_back(u);
320
321 if (previous_output.has_data) {
322 fr_ct random_separator = transcript.get_challenge_field_element("separator", 1);
323
324 opening_elements.push_back(previous_output.P0);
325 opening_scalars.push_back(random_separator);
326
327 rhs_elements.push_back((-(previous_output.P1)));
328 rhs_scalars.push_back(random_separator);
329 }
330
338 if (key->contains_recursive_proof) {
339 const auto public_inputs = transcript.get_field_element_vector("public_inputs");
340 const auto recover_fq_from_public_inputs =
341 [&public_inputs](const size_t idx0, const size_t idx1, const size_t idx2, const size_t idx3) {
342 const fr_ct l0 = public_inputs[idx0];
343 const fr_ct l1 = public_inputs[idx1];
344 const fr_ct l2 = public_inputs[idx2];
345 const fr_ct l3 = public_inputs[idx3];
346 l0.create_range_constraint(fq_ct::NUM_LIMB_BITS, "l0");
347 l1.create_range_constraint(fq_ct::NUM_LIMB_BITS, "l1");
348 l2.create_range_constraint(fq_ct::NUM_LIMB_BITS, "l2");
349 l3.create_range_constraint(fq_ct::NUM_LAST_LIMB_BITS, "l3");
350 return fq_ct(l0, l1, l2, l3, false);
351 };
352
353 fr_ct recursion_separator_challenge = transcript.get_challenge_field_element("separator", 2);
354
355 const auto x0 = recover_fq_from_public_inputs(key->recursive_proof_public_input_indices[0],
356 key->recursive_proof_public_input_indices[1],
357 key->recursive_proof_public_input_indices[2],
358 key->recursive_proof_public_input_indices[3]);
359 const auto y0 = recover_fq_from_public_inputs(key->recursive_proof_public_input_indices[4],
360 key->recursive_proof_public_input_indices[5],
361 key->recursive_proof_public_input_indices[6],
362 key->recursive_proof_public_input_indices[7]);
363 const auto x1 = recover_fq_from_public_inputs(key->recursive_proof_public_input_indices[8],
364 key->recursive_proof_public_input_indices[9],
365 key->recursive_proof_public_input_indices[10],
366 key->recursive_proof_public_input_indices[11]);
367 const auto y1 = recover_fq_from_public_inputs(key->recursive_proof_public_input_indices[12],
368 key->recursive_proof_public_input_indices[13],
369 key->recursive_proof_public_input_indices[14],
370 key->recursive_proof_public_input_indices[15]);
371
372 opening_elements.push_back(g1_ct(x0, y0));
373 opening_scalars.push_back(recursion_separator_challenge);
374
375 rhs_elements.push_back((-g1_ct(x1, y1)));
376 rhs_scalars.push_back(recursion_separator_challenge);
377 }
378
379 auto opening_result = g1_ct::template bn254_endo_batch_mul_with_generator(
380 big_opening_elements, big_opening_scalars, opening_elements, opening_scalars, batch_opening_scalar, 128);
381
382 opening_result = opening_result + double_opening_result;
383 for (const auto& to_add : elements_to_add) {
384 opening_result = opening_result + to_add;
385 }
386
387 g1_ct rhs = g1_ct::template wnaf_batch_mul<128>(rhs_elements, rhs_scalars);
388
389 rhs = (-rhs) - PI_Z;
390
391 // TODO(zac: remove this once a3-packages has migrated to calling `assign_object_to_proof_outputs`)
392 std::vector<uint32_t> proof_witness_indices = {
393 opening_result.x.binary_basis_limbs[0].element.normalize().witness_index,
394 opening_result.x.binary_basis_limbs[1].element.normalize().witness_index,
395 opening_result.x.binary_basis_limbs[2].element.normalize().witness_index,
396 opening_result.x.binary_basis_limbs[3].element.normalize().witness_index,
397 opening_result.y.binary_basis_limbs[0].element.normalize().witness_index,
398 opening_result.y.binary_basis_limbs[1].element.normalize().witness_index,
399 opening_result.y.binary_basis_limbs[2].element.normalize().witness_index,
400 opening_result.y.binary_basis_limbs[3].element.normalize().witness_index,
401 rhs.x.binary_basis_limbs[0].element.normalize().witness_index,
402 rhs.x.binary_basis_limbs[1].element.normalize().witness_index,
403 rhs.x.binary_basis_limbs[2].element.normalize().witness_index,
404 rhs.x.binary_basis_limbs[3].element.normalize().witness_index,
405 rhs.y.binary_basis_limbs[0].element.normalize().witness_index,
406 rhs.y.binary_basis_limbs[1].element.normalize().witness_index,
407 rhs.y.binary_basis_limbs[2].element.normalize().witness_index,
408 rhs.y.binary_basis_limbs[3].element.normalize().witness_index,
409 };
410 auto result = aggregation_state<Curve>{
411 opening_result, rhs, transcript.get_field_element_vector("public_inputs"), proof_witness_indices, true
412 };
413 return result;
414}
415
416template <typename Flavor>
417aggregation_state<bn254<typename Flavor::CircuitBuilder>> verify_proof(
418 typename Flavor::CircuitBuilder* context,
419 std::shared_ptr<verification_key<bn254<typename Flavor::CircuitBuilder>>> key,
420 const plonk::proof& proof,
421 const aggregation_state<bn254<typename Flavor::CircuitBuilder>> previous_output =
422 aggregation_state<bn254<typename Flavor::CircuitBuilder>>())
423{
424 // TODO(Cody): Be sure this is kosher
425 const auto manifest =
426 Flavor::create_manifest(static_cast<size_t>(key->num_public_inputs.get_value().from_montgomery_form().data[0]));
427 return verify_proof<bn254<typename Flavor::CircuitBuilder>,
428 recursion::recursive_ultra_verifier_settings<stdlib::bn254<typename Flavor::CircuitBuilder>>>(
429 context, key, manifest, proof, previous_output);
430}
431
432} // namespace recursion
433} // namespace stdlib
434} // namespace proof_system::plonk
Definition: standard_circuit_builder.hpp:12
Definition: biggroup.hpp:22
Definition: field.hpp:10
field_t pow(const field_t &exponent) const
raise a field_t to a power of an exponent (field_t). Note that the exponent must not exceed 32 bits a...
Definition: field.cpp:346
Definition: manifest.hpp:11
Definition: widget.bench.cpp:13
BBERG_INLINE constexpr field sqr() const noexcept
Definition: field_impl.hpp:61
Definition: proof.hpp:11