Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
goblin_verifier.test.cpp
Go to the documentation of this file.
11
13class GoblinRecursiveVerifierTests : public testing::Test {
14 public:
18
23
25 using RecursiveCommitment = bb::GoblinRecursiveVerifier::MergeVerifier::Commitment;
27 using RecursiveMergeCommitments = bb::GoblinRecursiveVerifier::MergeVerifier::InputCommitments;
32
33 // Compute the size of a Translator commitment (in bb::fr's)
34 static constexpr size_t comm_frs = FrCodec::calc_num_fields<Commitment>(); // 4
35 static constexpr size_t eval_frs = FrCodec::calc_num_fields<FF>(); // 1
36
42 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1298):
43 // Better recursion testing - create more flexible proof tampering tests.
44 // Tamper with the `op` commitment in the merge commitments (op commitments are no longer in translator proof)
45 static void tamper_with_op_commitment(MergeCommitments& merge_commitments)
46 {
47 // The first commitment in merged table is the `op` wire commitment
48 merge_commitments.t_commitments[0] = merge_commitments.t_commitments[0] * FF(2);
49 };
50
51 // Translator proof ends with [..., Libra:quotient_eval, Shplonk:Q, KZG:W]. We invalidate the proof by multiplying
52 // the eval by 2 (it leads to a Libra consistency check failure).
53 static void tamper_with_libra_eval(HonkProof& translator_proof)
54 {
55 // Proof tail size
56 static constexpr size_t tail_size = 2 * comm_frs + eval_frs; // 2*4 + 1 = 9
57
58 // Index of the target field (one fr) from the beginning
59 const size_t idx = translator_proof.size() - tail_size;
60
61 // Tamper: multiply by 2 (or tweak however you like)
62 translator_proof[idx] = translator_proof[idx] + translator_proof[idx];
63 };
64
65 // ECCVM pre-IPA proof ends with evaluations including `op`. We tamper with the `op` evaluation.
66 // The structure is: [..., op_eval, x_lo_y_hi_eval, x_hi_z_1_eval, y_lo_z_2_eval, IPA_proof...]
67 // So op_eval is 3 fields before the IPA proof starts.
68 static void tamper_with_eccvm_op_eval(HonkProof& eccvm_proof)
69 {
70 // The `op` evaluation is located 3 evaluations before the end of pre-IPA proof
71 // (followed by x_lo_y_hi, x_hi_z_1, y_lo_z_2 evaluations)
72 static constexpr size_t evals_after_op = 3; // x_lo_y_hi, x_hi_z_1, y_lo_z_2
73 const size_t op_eval_idx = eccvm_proof.size() - evals_after_op;
74
75 // Tamper with the op evaluation
76 eccvm_proof[op_eval_idx] += FF(1);
77 };
78
84 static ProverOutput create_goblin_prover_output(Builder* outer_builder = nullptr, const size_t num_circuits = 5)
85 {
86
87 Goblin goblin;
89
90 // Merge the ecc ops from the newly constructed circuit
91 auto goblin_proof = goblin.prove();
92 // Subtable values and commitments - needed for (Recursive)MergeVerifier
93 MergeCommitments merge_commitments;
94 auto t_current = goblin.op_queue->construct_current_ultra_ops_subtable_columns();
95 auto T_prev = goblin.op_queue->construct_previous_ultra_ops_table_columns();
96 CommitmentKey<curve::BN254> pcs_commitment_key(goblin.op_queue->get_ultra_ops_table_num_rows());
97 for (size_t idx = 0; idx < MegaFlavor::NUM_WIRES; idx++) {
98 merge_commitments.t_commitments[idx] = pcs_commitment_key.commit(t_current[idx]);
99 merge_commitments.T_prev_commitments[idx] = pcs_commitment_key.commit(T_prev[idx]);
100 }
101
102 RecursiveMergeCommitments recursive_merge_commitments;
103 if (outer_builder != nullptr) {
104 for (size_t idx = 0; idx < MegaFlavor::NUM_WIRES; idx++) {
105 recursive_merge_commitments.t_commitments[idx] =
106 RecursiveCommitment::from_witness(outer_builder, merge_commitments.t_commitments[idx]);
107 recursive_merge_commitments.T_prev_commitments[idx] =
108 RecursiveCommitment::from_witness(outer_builder, merge_commitments.T_prev_commitments[idx]);
109 // Removing the free witness tag, since the merge commitments in the full scheme are supposed to
110 // be fiat-shamirred earlier
111 recursive_merge_commitments.t_commitments[idx].unset_free_witness_tag();
112 recursive_merge_commitments.T_prev_commitments[idx].unset_free_witness_tag();
113 }
114 }
115
116 // Output is a goblin proof plus merge commitments
117 return { goblin_proof, merge_commitments, recursive_merge_commitments };
118 }
119};
120
126{
127 auto [proof, merge_commitments, _] = create_goblin_prover_output();
128
129 auto transcript = std::make_shared<NativeTranscript>();
130 bb::GoblinVerifier verifier(transcript, proof, merge_commitments, MergeSettings::APPEND);
131 auto result = verifier.reduce_to_pairing_check_and_ipa_opening();
132
133 // Check pairing points (aggregate merge + translator)
134 result.translator_pairing_points.aggregate(result.merge_pairing_points);
135 bool pairing_verified = result.translator_pairing_points.check();
136
137 // Verify IPA opening
138 auto ipa_transcript = std::make_shared<NativeTranscript>(result.ipa_proof);
140 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, result.ipa_claim, ipa_transcript);
141
142 EXPECT_TRUE(pairing_verified && ipa_verified);
143}
144
150{
152
153 auto [proof, merge_commitments, recursive_merge_commitments] = create_goblin_prover_output(&builder);
154
155 auto transcript = std::make_shared<Transcript>();
156 GoblinStdlibProof stdlib_proof(builder, proof);
158 transcript, stdlib_proof, recursive_merge_commitments, MergeSettings::APPEND
159 };
160 auto output = verifier.reduce_to_pairing_check_and_ipa_opening();
161
162 // Aggregate merge + translator pairing points
163 output.translator_pairing_points.aggregate(output.merge_pairing_points);
164
166 inputs.pairing_inputs = output.translator_pairing_points;
167 inputs.ipa_claim = output.ipa_claim;
168 inputs.set_public();
169
170 builder.ipa_proof = output.ipa_proof.get_value();
171
172 info("Recursive Verifier: num gates = ", builder.num_gates());
173
174 EXPECT_EQ(builder.failed(), false) << builder.err();
175
176 EXPECT_TRUE(CircuitChecker::check(builder));
177
178 // Construct and verify a proof for the Goblin Recursive Verifier circuit
179 {
180 auto prover_instance = std::make_shared<OuterProverInstance>(builder);
181 auto verification_key =
182 std::make_shared<typename OuterFlavor::VerificationKey>(prover_instance->get_precomputed());
183 auto vk_and_hash = std::make_shared<typename OuterFlavor::VKAndHash>(verification_key);
184 OuterProver prover(prover_instance, verification_key);
185 OuterVerifier verifier(vk_and_hash);
186 auto proof = prover.construct_proof();
187 bool verified = verifier.verify_proof(proof).result;
188
189 ASSERT_TRUE(verified);
190 }
191}
192
193// Check that the GoblinRecursiveVerifier circuit does not depend on the inputs.
195{
196 // Retrieves the trace blocks (each consisting of a specific gate) from the recursive verifier circuit
197 auto get_blocks = [](size_t inner_size)
198 -> std::tuple<typename Builder::ExecutionTrace, std::shared_ptr<OuterFlavor::VerificationKey>> {
200
201 auto [proof, merge_commitments, recursive_merge_commitments] =
202 create_goblin_prover_output(&builder, inner_size);
203
204 auto transcript = std::make_shared<Transcript>();
205 GoblinStdlibProof stdlib_proof(builder, proof);
207 transcript, stdlib_proof, recursive_merge_commitments, MergeSettings::APPEND
208 };
209 auto output = verifier.reduce_to_pairing_check_and_ipa_opening();
210
211 // Aggregate merge + translator pairing points
212 output.translator_pairing_points.aggregate(output.merge_pairing_points);
213
215 inputs.pairing_inputs = output.translator_pairing_points;
216 inputs.ipa_claim = output.ipa_claim;
217 inputs.set_public();
218
219 builder.ipa_proof = output.ipa_proof.get_value();
220
221 info("Recursive Verifier: num gates = ", builder.num_gates());
222
223 // Construct and verify a proof for the Goblin Recursive Verifier circuit
224 auto prover_instance = std::make_shared<OuterProverInstance>(builder);
225 auto outer_verification_key =
226 std::make_shared<typename OuterFlavor::VerificationKey>(prover_instance->get_precomputed());
227 auto vk_and_hash = std::make_shared<typename OuterFlavor::VKAndHash>(outer_verification_key);
228 OuterProver prover(prover_instance, outer_verification_key);
229 OuterVerifier outer_verifier(vk_and_hash);
230 return { builder.blocks, outer_verification_key };
231 };
232
233 auto [blocks_5, verification_key_5] = get_blocks(5);
234 auto [blocks_6, verification_key_6] = get_blocks(6);
235
236 compare_ultra_blocks_and_verification_keys<OuterFlavor>({ blocks_5, blocks_6 },
237 { verification_key_5, verification_key_6 });
238}
239
245{
246 BB_DISABLE_ASSERTS(); // Avoid on_curve assertion failure in cycle_group etc
248
249 auto [proof, merge_commitments, recursive_merge_commitments] = create_goblin_prover_output(&builder);
250
251 // Tamper with the ECCVM proof
252 for (auto& val : proof.eccvm_proof) {
253 if (val > 0) { // tamper by finding the first non-zero value and incrementing it by 1
254 val += 1;
255 break;
256 }
257 }
258
259 auto transcript = std::make_shared<Transcript>();
260 GoblinStdlibProof stdlib_proof(builder, proof);
262 transcript, stdlib_proof, recursive_merge_commitments, MergeSettings::APPEND
263 };
264 auto goblin_rec_verifier_output = verifier.reduce_to_pairing_check_and_ipa_opening();
265 EXPECT_FALSE(CircuitChecker::check(builder));
266
268 auto crs_factory = srs::get_grumpkin_crs_factory();
269 VerifierCommitmentKey<curve::Grumpkin> grumpkin_verifier_commitment_key(1 << CONST_ECCVM_LOG_N, crs_factory);
270 OpeningClaim<curve::Grumpkin> native_claim = goblin_rec_verifier_output.ipa_claim.get_native_opening_claim();
271 auto native_ipa_transcript = std::make_shared<NativeTranscript>(goblin_rec_verifier_output.ipa_proof.get_value());
272
273 bool native_result =
274 IPA<curve::Grumpkin>::reduce_verify(grumpkin_verifier_commitment_key, native_claim, native_ipa_transcript);
275 EXPECT_FALSE(native_result);
276}
277
283{
284 auto [proof, merge_commitments, _] = create_goblin_prover_output();
285
286 // Tamper with the op commitment in merge commitments (used by Translator verifier)
287 {
288 MergeCommitments tampered_merge_commitments = merge_commitments;
289 tamper_with_op_commitment(tampered_merge_commitments);
291
292 RecursiveMergeCommitments recursive_merge_commitments;
293 for (size_t idx = 0; idx < MegaFlavor::NUM_WIRES; idx++) {
294 recursive_merge_commitments.t_commitments[idx] =
295 RecursiveCommitment::from_witness(&builder, tampered_merge_commitments.t_commitments[idx]);
296 recursive_merge_commitments.T_prev_commitments[idx] =
297 RecursiveCommitment::from_witness(&builder, tampered_merge_commitments.T_prev_commitments[idx]);
298 recursive_merge_commitments.t_commitments[idx].fix_witness();
299 recursive_merge_commitments.T_prev_commitments[idx].fix_witness();
300 }
301
302 auto transcript = std::make_shared<Transcript>();
303 GoblinStdlibProof stdlib_proof(builder, proof);
305 transcript, stdlib_proof, recursive_merge_commitments, MergeSettings::APPEND
306 };
307 auto goblin_rec_verifier_output = verifier.reduce_to_pairing_check_and_ipa_opening();
308
309 // Aggregate merge + translator pairing points
310 goblin_rec_verifier_output.translator_pairing_points.aggregate(goblin_rec_verifier_output.merge_pairing_points);
311
312 // Circuit is correct but pairing check should fail
313 EXPECT_TRUE(CircuitChecker::check(builder));
314
315 // Check that the pairing fails natively
316 bb::PairingPoints<curve::BN254> native_pairing_points(
317 goblin_rec_verifier_output.translator_pairing_points.P0.get_value(),
318 goblin_rec_verifier_output.translator_pairing_points.P1.get_value());
319 bool pairing_result = native_pairing_points.check();
320 EXPECT_FALSE(pairing_result);
321 }
322 // Tamper with the Translator proof non - preamble values
323 {
324 auto tampered_proof = proof;
325 tamper_with_libra_eval(tampered_proof.translator_proof);
326
328
329 RecursiveMergeCommitments recursive_merge_commitments;
330 for (size_t idx = 0; idx < MegaFlavor::NUM_WIRES; idx++) {
331 recursive_merge_commitments.t_commitments[idx] =
332 RecursiveCommitment::from_witness(&builder, merge_commitments.t_commitments[idx]);
333 recursive_merge_commitments.T_prev_commitments[idx] =
334 RecursiveCommitment::from_witness(&builder, merge_commitments.T_prev_commitments[idx]);
335 recursive_merge_commitments.t_commitments[idx].fix_witness();
336 recursive_merge_commitments.T_prev_commitments[idx].fix_witness();
337 }
338
339 auto transcript = std::make_shared<Transcript>();
340 GoblinStdlibProof stdlib_proof(builder, tampered_proof);
342 transcript, stdlib_proof, recursive_merge_commitments, MergeSettings::APPEND
343 };
344 [[maybe_unused]] auto goblin_rec_verifier_output = verifier.reduce_to_pairing_check_and_ipa_opening();
345 EXPECT_FALSE(CircuitChecker::check(builder));
346 }
347}
348
353TEST_F(GoblinRecursiveVerifierTests, TranslationEvaluationsFailure)
354{
356
357 auto [proof, merge_commitments, recursive_merge_commitments] = create_goblin_prover_output(&builder);
358
359 // Tamper with the `op` evaluation in the ECCVM proof using the helper function
360 tamper_with_eccvm_op_eval(proof.eccvm_proof);
361
362 auto transcript = std::make_shared<Transcript>();
363 GoblinStdlibProof stdlib_proof(builder, proof);
365 transcript, stdlib_proof, recursive_merge_commitments, MergeSettings::APPEND
366 };
367 [[maybe_unused]] auto goblin_rec_verifier_output = verifier.reduce_to_pairing_check_and_ipa_opening();
368
369 EXPECT_FALSE(CircuitChecker::check(builder));
370}
371
376TEST_F(GoblinRecursiveVerifierTests, TranslatorMergeConsistencyFailure)
377{
378
379 {
380
382
383 auto [proof, merge_commitments, recursive_merge_commitments] = create_goblin_prover_output(&builder);
384
385 // Check natively that the proof is correct.
386 auto native_transcript = std::make_shared<NativeTranscript>();
387 bb::GoblinVerifier native_verifier(native_transcript, proof, merge_commitments, MergeSettings::APPEND);
388 auto native_result = native_verifier.reduce_to_pairing_check_and_ipa_opening();
389 // Aggregate merge + translator pairing points before checking
390 native_result.translator_pairing_points.aggregate(native_result.merge_pairing_points);
391 bool pairing_verified = native_result.translator_pairing_points.check();
392 auto ipa_transcript = std::make_shared<NativeTranscript>(native_result.ipa_proof);
394 bool ipa_verified = IPA<curve::Grumpkin>::reduce_verify(ipa_vk, native_result.ipa_claim, ipa_transcript);
395 EXPECT_TRUE(pairing_verified && ipa_verified);
396
397 // Tamper with the op commitment in merge commitments (used by Translator verifier)
398 MergeCommitments tampered_merge_commitments = merge_commitments;
399 tamper_with_op_commitment(tampered_merge_commitments);
400
401 // Construct and check the Goblin Recursive Verifier circuit
402
403 RecursiveMergeCommitments tampered_recursive_merge_commitments;
404 for (size_t idx = 0; idx < MegaFlavor::NUM_WIRES; idx++) {
405 tampered_recursive_merge_commitments.t_commitments[idx] =
406 RecursiveCommitment::from_witness(&builder, tampered_merge_commitments.t_commitments[idx]);
407 tampered_recursive_merge_commitments.T_prev_commitments[idx] =
408 RecursiveCommitment::from_witness(&builder, tampered_merge_commitments.T_prev_commitments[idx]);
409 tampered_recursive_merge_commitments.t_commitments[idx].fix_witness();
410 tampered_recursive_merge_commitments.T_prev_commitments[idx].fix_witness();
411 }
412
413 auto transcript = std::make_shared<Transcript>();
414 GoblinStdlibProof stdlib_proof(builder, proof);
416 transcript, stdlib_proof, tampered_recursive_merge_commitments, MergeSettings::APPEND
417 };
418 auto goblin_rec_verifier_output = verifier.reduce_to_pairing_check_and_ipa_opening();
419
420 // Aggregate merge + translator pairing points
421 goblin_rec_verifier_output.translator_pairing_points.aggregate(goblin_rec_verifier_output.merge_pairing_points);
422
423 // Circuit is correct but pairing check should fail
424 EXPECT_TRUE(CircuitChecker::check(builder));
425
426 // Check that the pairing fails natively
427 bb::PairingPoints<curve::BN254> native_pairing_points(
428 goblin_rec_verifier_output.translator_pairing_points.P0.get_value(),
429 goblin_rec_verifier_output.translator_pairing_points.P1.get_value());
430 bool pairing_result = native_pairing_points.check();
431 EXPECT_FALSE(pairing_result);
432 }
433}
434} // namespace bb::stdlib::recursion::honk
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:33
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
CommitmentKey object over a pairing group 𝔾₁.
Commitment commit(PolynomialSpan< const Fr > polynomial) const
Uses the ProverSRS to create a commitment to p(X)
static constexpr size_t ECCVM_FIXED_SIZE
Simple verification key class for fixed-size circuits (ECCVM, Translator).
Definition flavor.hpp:136
TranslatorFlavor::VerificationKey TranslatorVerificationKey
Definition goblin.hpp:42
GoblinProof prove()
Constuct a full Goblin proof (ECCVM, Translator, merge)
Definition goblin.cpp:62
std::shared_ptr< OpQueue > op_queue
Definition goblin.hpp:54
ECCVMFlavor::VerificationKey ECCVMVerificationKey
Definition goblin.hpp:41
static void construct_and_merge_mock_circuits(Goblin &goblin, const size_t num_circuits=3)
Unified Goblin verifier for both native and recursive verification.
ReductionResult reduce_to_pairing_check_and_ipa_opening()
Reduce Goblin proof to pairing check and IPA opening claim.
IPA (inner product argument) commitment scheme class.
Definition ipa.hpp:92
static constexpr size_t NUM_WIRES
typename Curve::AffineElement Commitment
Unverified claim (C,r,v) for some witness polynomial p(X) such that.
Definition claim.hpp:53
auto get_native_opening_claim() const
Definition claim.hpp:129
An object storing two EC points that represent the inputs to a pairing check.
bool check() const
Perform the pairing check.
A ProverInstance is normally constructed from a finalized circuit and it contains all the information...
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
Curve::ScalarField FF
UltraRollupFlavor extends UltraFlavor with IPA proof support.
Representation of the Grumpkin Verifier Commitment Key inside a bn254 circuit.
static ProverOutput create_goblin_prover_output(Builder *outer_builder=nullptr, const size_t num_circuits=5)
Create a goblin proof and the VM verification keys needed by the goblin recursive verifier.
bb::GoblinRecursiveVerifier::MergeVerifier::InputCommitments RecursiveMergeCommitments
bb::GoblinRecursiveVerifier::MergeVerifier::Commitment RecursiveCommitment
static void tamper_with_op_commitment(MergeCommitments &merge_commitments)
static void tamper_with_libra_eval(HonkProof &translator_proof)
The data that is propagated on the public inputs of a rollup circuit.
#define info(...)
Definition log.hpp:93
AluTraceBuilder builder
Definition alu.test.cpp:124
AvmProvingInputs inputs
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
std::shared_ptr< factories::CrsFactory< curve::Grumpkin > > get_grumpkin_crs_factory()
TEST_F(BoomerangGoblinRecursiveVerifierTests, graph_description_basic)
Construct and check a goblin recursive verification circuit.
std::vector< fr > HonkProof
Definition proof.hpp:15
BaseTranscript< stdlib::StdlibCodec< stdlib::field_t< UltraCircuitBuilder > >, stdlib::poseidon2< UltraCircuitBuilder > > UltraStdlibTranscript
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13