Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
goblin_recursive_verifier.test.cpp
Go to the documentation of this file.
11
13class GoblinRecursiveVerifierTests : public testing::Test {
14 public:
18
23
28
30
37
43 static ProverOutput create_goblin_prover_output(Builder* outer_builder = nullptr, const size_t NUM_CIRCUITS = 3)
44 {
45
46 Goblin goblin;
47 // Construct and accumulate multiple circuits
48 for (size_t idx = 0; idx < NUM_CIRCUITS - 1; ++idx) {
51 goblin.prove_merge();
52 }
53
54 Goblin goblin_final;
55 goblin_final.op_queue = goblin.op_queue;
56 MegaCircuitBuilder builder{ goblin_final.op_queue };
58
59 // Merge the ecc ops from the newly constructed circuit
60 goblin_final.op_queue->merge();
61
62 // Subtable values and commitments - needed for (Recursive)MergeVerifier
63 MergeCommitments merge_commitments;
64 auto t_current = goblin_final.op_queue->construct_current_ultra_ops_subtable_columns();
65 auto T_prev = goblin_final.op_queue->construct_previous_ultra_ops_table_columns();
66 CommitmentKey<curve::BN254> pcs_commitment_key(goblin_final.op_queue->get_ultra_ops_table_num_rows());
67 for (size_t idx = 0; idx < MegaFlavor::NUM_WIRES; idx++) {
68 merge_commitments.t_commitments[idx] = pcs_commitment_key.commit(t_current[idx]);
69 merge_commitments.T_prev_commitments[idx] = pcs_commitment_key.commit(T_prev[idx]);
70 }
71
72 RecursiveMergeCommitments recursive_merge_commitments;
73 if (outer_builder != nullptr) {
74 for (size_t idx = 0; idx < MegaFlavor::NUM_WIRES; idx++) {
75 recursive_merge_commitments.t_commitments[idx] =
76 RecursiveCommitment::from_witness(outer_builder, merge_commitments.t_commitments[idx]);
77 recursive_merge_commitments.T_prev_commitments[idx] =
78 RecursiveCommitment::from_witness(outer_builder, merge_commitments.T_prev_commitments[idx]);
79 }
80 }
81
82 // Output is a goblin proof plus ECCVM/Translator verification keys
83 return { goblin_final.prove(),
85 merge_commitments,
86 recursive_merge_commitments };
87 }
88};
89
95{
96 auto [proof, verifier_input, merge_commitments, _] = create_goblin_prover_output();
97
99
100 EXPECT_TRUE(Goblin::verify(proof, merge_commitments, verifier_transcript));
101}
102
108{
110
111 auto [proof, verifier_input, merge_commitments, recursive_merge_commitments] =
112 create_goblin_prover_output(&builder);
113
114 GoblinRecursiveVerifier verifier{ &builder, verifier_input };
115 GoblinRecursiveVerifierOutput output = verifier.verify(proof, recursive_merge_commitments);
117
118 info("Recursive Verifier: num gates = ", builder.num_gates);
119
120 EXPECT_EQ(builder.failed(), false) << builder.err();
121
122 EXPECT_TRUE(CircuitChecker::check(builder));
123
124 // Construct and verify a proof for the Goblin Recursive Verifier circuit
125 {
127 auto verification_key = std::make_shared<typename OuterFlavor::VerificationKey>(proving_key->get_precomputed());
128 OuterProver prover(proving_key, verification_key);
129 OuterVerifier verifier(verification_key);
130 auto proof = prover.construct_proof();
131 bool verified = verifier.template verify_proof<bb::DefaultIO>(proof).result;
132
133 ASSERT_TRUE(verified);
134 }
135}
136
137// Check that the GoblinRecursiveVerifier circuit does not depend on the inputs.
139{
140 // Retrieves the trace blocks (each consisting of a specific gate) from the recursive verifier circuit
141 auto get_blocks = [](size_t inner_size)
144
145 auto [proof, verifier_input, merge_commitments, recursive_merge_commitments] =
146 create_goblin_prover_output(&builder, inner_size);
147
148 GoblinRecursiveVerifier verifier{ &builder, verifier_input };
149 GoblinRecursiveVerifierOutput output = verifier.verify(proof, recursive_merge_commitments);
151
152 info("Recursive Verifier: num gates = ", builder.num_gates);
153
154 // Construct and verify a proof for the Goblin Recursive Verifier circuit
156 auto outer_verification_key =
157 std::make_shared<typename OuterFlavor::VerificationKey>(proving_key->get_precomputed());
158 OuterProver prover(proving_key, outer_verification_key);
159 OuterVerifier outer_verifier(outer_verification_key);
160 return { builder.blocks, outer_verification_key };
161 };
162
163 auto [blocks_2, verification_key_2] = get_blocks(2);
164 auto [blocks_4, verification_key_4] = get_blocks(4);
165
166 compare_ultra_blocks_and_verification_keys<OuterFlavor>({ blocks_2, blocks_4 },
167 { verification_key_2, verification_key_4 });
168}
169
175{
177
178 auto [proof, verifier_input, merge_commitments, recursive_merge_commitments] =
179 create_goblin_prover_output(&builder);
180
181 // Tamper with the ECCVM proof
182 for (auto& val : proof.eccvm_proof.pre_ipa_proof) {
183 if (val > 0) { // tamper by finding the first non-zero value and incrementing it by 1
184 val += 1;
185 break;
186 }
187 }
188
189 GoblinRecursiveVerifier verifier{ &builder, verifier_input };
190 GoblinRecursiveVerifierOutput goblin_rec_verifier_output = verifier.verify(proof, recursive_merge_commitments);
191
193 auto crs_factory = srs::get_grumpkin_crs_factory();
194 VerifierCommitmentKey<curve::Grumpkin> grumpkin_verifier_commitment_key(1 << CONST_ECCVM_LOG_N, crs_factory);
195 OpeningClaim<curve::Grumpkin> native_claim = goblin_rec_verifier_output.opening_claim.get_native_opening_claim();
196 auto native_ipa_transcript = std::make_shared<NativeTranscript>();
197 auto native_ipa_proof = goblin_rec_verifier_output.ipa_proof.get_value();
198 native_ipa_transcript->load_proof(native_ipa_proof);
199
201 IPA<curve::Grumpkin>::reduce_verify(grumpkin_verifier_commitment_key, native_claim, native_ipa_transcript),
202 ".*IPA verification fails.*");
203}
204
210{
211 auto [proof, verifier_input, merge_commitments, _] = create_goblin_prover_output();
212
213 // Tamper with the Translator proof preamble
214 {
215 GoblinProof tampered_proof = proof;
216 for (auto& val : tampered_proof.translator_proof) {
217 if (val > 0) { // tamper by finding the first non-zero value and incrementing it by 1
218 val += 1;
219 break;
220 }
221 }
222
224
225 RecursiveMergeCommitments recursive_merge_commitments;
226 for (size_t idx = 0; idx < MegaFlavor::NUM_WIRES; idx++) {
227 recursive_merge_commitments.t_commitments[idx] =
228 RecursiveCommitment::from_witness(&builder, merge_commitments.t_commitments[idx]);
229 recursive_merge_commitments.T_prev_commitments[idx] =
230 RecursiveCommitment::from_witness(&builder, merge_commitments.T_prev_commitments[idx]);
231 }
232
233 GoblinRecursiveVerifier verifier{ &builder, verifier_input };
234 [[maybe_unused]] auto goblin_rec_verifier_output = verifier.verify(tampered_proof, recursive_merge_commitments);
235 EXPECT_FALSE(CircuitChecker::check(builder));
236 }
237 // Tamper with the Translator proof non-preamble values
238 {
239 auto tampered_proof = proof;
240 int seek = 10;
241 for (auto& val : tampered_proof.translator_proof) {
242 if (val > 0) { // tamper by finding the tenth non-zero value and incrementing it by 1
243 if (--seek == 0) {
244 val += 1;
245 break;
246 }
247 }
248 }
249
251
252 RecursiveMergeCommitments recursive_merge_commitments;
253 for (size_t idx = 0; idx < MegaFlavor::NUM_WIRES; idx++) {
254 recursive_merge_commitments.t_commitments[idx] =
255 RecursiveCommitment::from_witness(&builder, merge_commitments.t_commitments[idx]);
256 recursive_merge_commitments.T_prev_commitments[idx] =
257 RecursiveCommitment::from_witness(&builder, merge_commitments.T_prev_commitments[idx]);
258 }
259
260 GoblinRecursiveVerifier verifier{ &builder, verifier_input };
261 [[maybe_unused]] auto goblin_rec_verifier_output = verifier.verify(tampered_proof, recursive_merge_commitments);
262 EXPECT_FALSE(CircuitChecker::check(builder));
263 }
264}
265
270TEST_F(GoblinRecursiveVerifierTests, TranslationEvaluationsFailure)
271{
273
274 auto [proof, verifier_input, merge_commitments, recursive_merge_commitments] =
275 create_goblin_prover_output(&builder);
276
277 // Tamper with the evaluation of `op` witness. The index is computed manually.
278 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1298):
279 // Better recursion testing - create more flexible proof tampering tests.
280 const size_t op_limb_index = 593;
281 proof.eccvm_proof.pre_ipa_proof[op_limb_index] += 1;
282
283 GoblinRecursiveVerifier verifier{ &builder, verifier_input };
284 [[maybe_unused]] auto goblin_rec_verifier_output = verifier.verify(proof, recursive_merge_commitments);
285
286 EXPECT_FALSE(CircuitChecker::check(builder));
287}
288
293TEST_F(GoblinRecursiveVerifierTests, TranslatorMergeConsistencyFailure)
294{
295
296 {
297 using Commitment = TranslatorFlavor::Commitment;
298 using FF = TranslatorFlavor::FF;
299 using BF = TranslatorFlavor::BF;
300
302
303 auto [proof, verifier_input, merge_commitments, recursive_merge_commitments] =
304 create_goblin_prover_output(&builder);
305
307
308 // Check natively that the proof is correct.
309 EXPECT_TRUE(Goblin::verify(proof, merge_commitments, verifier_transcript));
310
311 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1298):
312 // Better recursion testing - create more flexible proof tampering tests.
313 // Modify the `op` commitment which a part of the Merge protocol.
314 auto tamper_with_op_commitment = [](HonkProof& translator_proof) {
315 // Compute the size of a Translator commitment (in bb::fr's)
316 static constexpr size_t num_frs_comm = bb::field_conversion::calc_num_bn254_frs<Commitment>();
317 // The `op` wire commitment is currently the second element of the proof, following the
318 // `accumulated_result` which is a BN254 BaseField element.
319 static constexpr size_t offset = bb::field_conversion::calc_num_bn254_frs<BF>();
320 // Extract `op` fields and convert them to a Commitment object
321 auto element_frs = std::span{ translator_proof }.subspan(offset, num_frs_comm);
322 auto op_commitment = NativeTranscriptParams::template deserialize<Commitment>(element_frs);
323 // Modify the commitment
324 op_commitment = op_commitment * FF(2);
325 // Serialize the tampered commitment into the proof (overwriting the valid one).
326 auto op_commitment_reserialized = bb::NativeTranscriptParams::serialize(op_commitment);
327 std::copy(op_commitment_reserialized.begin(),
328 op_commitment_reserialized.end(),
329 translator_proof.begin() + static_cast<std::ptrdiff_t>(offset));
330 };
331
332 tamper_with_op_commitment(proof.translator_proof);
333 // Construct and check the Goblin Recursive Verifier circuit
334
335 GoblinRecursiveVerifier verifier{ &builder, verifier_input };
336 [[maybe_unused]] auto goblin_rec_verifier_output = verifier.verify(proof, recursive_merge_commitments);
337
338 EXPECT_FALSE(CircuitChecker::check(builder));
339 }
340
341 // TODO(https://github.com/AztecProtocol/barretenberg/issues/787)
342}
343} // namespace bb::stdlib::recursion::honk
#define EXPECT_THROW_OR_ABORT(statement, matcher)
Definition assert.hpp:150
CommitmentKey object over a pairing group 𝔾₁.
Commitment commit(PolynomialSpan< const Fr > polynomial) const
Uses the ProverSRS to create a commitment to p(X)
A DeciderProvingKey is normally constructed from a finalized circuit and it contains all the informat...
The verification key is responsible for storing the commitments to the precomputed (non-witnessk) pol...
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:97
TranslatorFlavor::VerificationKey TranslatorVerificationKey
Definition goblin.hpp:38
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:24
std::shared_ptr< OpQueue > op_queue
Definition goblin.hpp:48
GoblinProof prove(const MergeSettings merge_settings=MergeSettings::PREPEND)
Constuct a full Goblin proof (ECCVM, Translator, merge)
Definition goblin.cpp:50
ECCVMFlavor::VerificationKey ECCVMVerificationKey
Definition goblin.hpp:37
static void construct_simple_circuit(MegaBuilder &builder, bool last_circuit=false)
Generate a simple test circuit with some ECC op gates and conventional arithmetic gates.
IPA (inner product argument) commitment scheme class.
Definition ipa.hpp:95
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
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
The verification key is responsible for storing the commitments to the precomputed (non-witnessk) pol...
Curve::ScalarField FF
Curve::AffineElement Commitment
Representation of the Grumpkin Verifier Commitment Key inside a bn254 circuit.
GoblinRecursiveVerifierOutput verify(const GoblinProof &, const MergeCommitments &merge_commitments, const MergeSettings merge_settings=MergeSettings::PREPEND)
Creates a circuit that executes the ECCVM, Translator and Merge verifiers.
GoblinRecursiveVerifier::MergeVerifier::Commitment RecursiveCommitment
static ProverOutput create_goblin_prover_output(Builder *outer_builder=nullptr, const size_t NUM_CIRCUITS=3)
Create a goblin proof and the VM verification keys needed by the goblin recursive verifier.
void info(Args... args)
Definition log.hpp:70
AluTraceBuilder builder
Definition alu.test.cpp:123
ssize_t offset
Definition engine.cpp:36
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
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
HonkProof translator_proof
Definition types.hpp:23
static std::vector< DataType > serialize(const T &element)
uint32_t set_public()
Set the witness indices for the limbs of the pairing points to public.