Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
mega_honk.test.cpp
Go to the documentation of this file.
1#include <cstddef>
2#include <cstdint>
3#include <gtest/gtest.h>
4
15
16using namespace bb;
17
19
20using FlavorTypes = ::testing::Types<MegaFlavor, MegaZKFlavor>;
21
22template <typename Flavor> class MegaHonkTests : public ::testing::Test {
23 public:
25
35
41 {
42 auto prover_instance = std::make_shared<ProverInstance>(builder);
43 auto verification_key = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
44 auto vk_and_hash = std::make_shared<typename Flavor::VKAndHash>(verification_key);
45 Prover prover(prover_instance, verification_key);
46 Verifier verifier(vk_and_hash);
47 auto proof = prover.construct_proof();
48 bool verified = verifier.verify_proof(proof).result;
49
50 return verified;
51 }
52};
53
55
65TYPED_TEST(MegaHonkTests, ProofLengthCheck)
66{
67 using Flavor = TypeParam;
70
71 auto builder = Builder{};
73
74 // Construct a mega proof and ensure its size matches expectation; if not, the constant may need to be updated
76 auto verification_key = std::make_shared<typename Flavor::VerificationKey>(prover_instance->get_precomputed());
77 UltraProver_<Flavor> prover(prover_instance, verification_key);
78 HonkProof mega_proof = prover.construct_proof();
79 EXPECT_EQ(mega_proof.size(),
82}
83
90{
91 using Flavor = TypeParam;
93
95
96 // Construct and verify Honk proof
97 bool honk_verified = this->construct_and_verify_honk_proof(builder);
98 EXPECT_TRUE(honk_verified);
99}
100
106TYPED_TEST(MegaHonkTests, DynamicVirtualSizeIncrease)
107{
108 using Flavor = TypeParam;
109
110 // In MegaZKFlavor, we mask witness polynomials by placing random values at the indices `dyadic_circuit_size`-i for
111 // i=1,2,3. This mechanism does not work with structured polynomials yet.
113 GTEST_SKIP() << "Skipping 'DynamicVirtualSizeIncrease' test for MegaZKFlavor.";
114 }
116 using Prover = UltraProver_<Flavor>;
117 using Verifier = UltraVerifier_<Flavor, DefaultIO>;
118
120
121 auto builder_copy = builder;
122
123 // Construct and verify Honk proof using a structured trace
124 auto prover_instance = std::make_shared<ProverInstance_<Flavor>>(builder);
125 auto prover_instance_copy = std::make_shared<ProverInstance_<Flavor>>(builder_copy);
126 auto circuit_size = prover_instance->dyadic_size();
127
128 auto doubled_circuit_size = 2 * circuit_size;
129 prover_instance_copy->polynomials.increase_polynomials_virtual_size(doubled_circuit_size);
130 // TODO(https://github.com/AztecProtocol/barretenberg/issues/1158)
131 // prover_instance_copy->dyadic_circuit_size = doubled_circuit_size;
132
133 auto verification_key = std::make_shared<typename Flavor::VerificationKey>(prover_instance->get_precomputed());
134 Prover prover(prover_instance, verification_key);
135
136 auto verification_key_copy = std::make_shared<typename Flavor::VerificationKey>(prover_instance->get_precomputed());
137 Prover prover_copy(prover_instance_copy, verification_key_copy);
138
139 for (auto [entry, entry_copy] : zip_view(verification_key->get_all(), verification_key_copy->get_all())) {
140 EXPECT_EQ(entry, entry_copy);
141 }
142
143 auto vk_and_hash = std::make_shared<typename Flavor::VKAndHash>(verification_key);
144 Verifier verifier(vk_and_hash);
145 auto proof = prover.construct_proof();
146
147 auto relation_failures =
148 RelationChecker<Flavor>::check_all(prover_instance->polynomials, prover_instance->relation_parameters);
149 EXPECT_TRUE(relation_failures.empty());
150 bool result = verifier.verify_proof(proof).result;
151 EXPECT_TRUE(result);
152
153 auto vk_and_hash_copy = std::make_shared<typename Flavor::VKAndHash>(verification_key_copy);
154 Verifier verifier_copy(vk_and_hash_copy);
155 auto proof_copy = prover_copy.construct_proof();
156
157 auto relation_failures_copy =
158 RelationChecker<Flavor>::check_all(prover_instance->polynomials, prover_instance->relation_parameters);
159 EXPECT_TRUE(relation_failures.empty());
160 bool result_copy = verifier_copy.verify_proof(proof_copy).result;
161 EXPECT_TRUE(result_copy);
162}
163
172{
173 using Flavor = TypeParam;
174 // In MegaZKFlavor, we mask witness polynomials by placing random values at the indices `dyadic_circuit_size`-i, for
175 // i=1,2,3. This mechanism does not work with structured polynomials yet.
177 GTEST_SKIP() << "Skipping 'PolySwap' test for MegaZKFlavor.";
178 }
180
181 // Construct a simple circuit and make a copy of it
184 auto builder_copy = builder;
185
186 // Construct two identical proving keys
188 auto prover_instance_2 = std::make_shared<typename TestFixture::ProverInstance>(builder_copy);
189
190 // Tamper with the polys of pkey 1 in such a way that verification should fail
191 for (size_t i = 0; i < prover_instance_1->dyadic_size(); ++i) {
192 if (prover_instance_1->polynomials.q_arith()[i] != 0) {
193 prover_instance_1->polynomials.w_l().at(i) += 1;
194 break;
195 }
196 }
197
198 // Swap the polys of the two proving keys; result should be pkey 1 is valid and pkey 2 should fail
199 std::swap(prover_instance_1->polynomials, prover_instance_2->polynomials);
200
201 { // Verification based on pkey 1 should succeed
202 auto verification_key =
203 std::make_shared<typename TestFixture::VerificationKey>(prover_instance_1->get_precomputed());
204 auto vk_and_hash = std::make_shared<typename Flavor::VKAndHash>(verification_key);
205 typename TestFixture::Prover prover(prover_instance_1, verification_key);
206 typename TestFixture::Verifier verifier(vk_and_hash);
207 auto proof = prover.construct_proof();
208 bool result = verifier.verify_proof(proof).result;
209 EXPECT_TRUE(result);
210 }
211
212 { // Verification based on pkey 2 should fail
213 auto verification_key =
214 std::make_shared<typename TestFixture::VerificationKey>(prover_instance_2->get_precomputed());
215 auto vk_and_hash = std::make_shared<typename Flavor::VKAndHash>(verification_key);
216 typename TestFixture::Prover prover(prover_instance_2, verification_key);
217 typename TestFixture::Verifier verifier(vk_and_hash);
218 auto proof = prover.construct_proof();
219 bool result = verifier.verify_proof(proof).result;
220 EXPECT_FALSE(result);
221 }
222}
223
231TYPED_TEST(MegaHonkTests, DyadicSizeJumpsToProtectMaskingArea)
232{
233 using Flavor = TypeParam;
234 if constexpr (!Flavor::HasZK) {
235 GTEST_SKIP() << "Masking area only exists for ZK flavors";
236 } else {
237 using Builder = typename Flavor::CircuitBuilder;
239
240 // Determine the baseline dyadic size (with ECC ops + finalization overhead, no extra user gates)
241 Builder baseline_builder;
243 auto baseline_instance = std::make_shared<ProverInstance>(baseline_builder);
244 const size_t baseline_dyadic = baseline_instance->dyadic_size();
245
246 // The disabled head region is always present.
247 // Verify active trace starts after it and dyadic size doubles when tightly packed.
248 size_t prev_dyadic = 0;
249 bool found_jump = false;
250 for (size_t num_extra_gates = 0; num_extra_gates <= baseline_dyadic; num_extra_gates++) {
253 if (num_extra_gates > 0) {
255 }
256 auto prover_instance = std::make_shared<ProverInstance>(builder);
257
258 const size_t dyadic_size = prover_instance->dyadic_size();
259 const size_t final_active_idx = prover_instance->get_final_active_wire_idx();
260
261 // Invariant: active trace doesn't overlap the disabled head region
262 ASSERT_GE(final_active_idx, ProverInstance::TRACE_OFFSET)
263 << "final_active_idx (" << final_active_idx << ") is within the disabled head region";
264
265 if (prev_dyadic != 0 && dyadic_size > prev_dyadic) {
266 EXPECT_EQ(dyadic_size, 2 * prev_dyadic);
267
268 // Prove and verify at the tightest packing (right before the jump)
269 Builder tight_builder;
271 MockCircuits::add_arithmetic_gates(tight_builder, num_extra_gates - 1);
272 bool verified = this->construct_and_verify_honk_proof(tight_builder);
273 EXPECT_TRUE(verified);
274
275 found_jump = true;
276 break;
277 }
278
279 prev_dyadic = dyadic_size;
280 }
281
282 EXPECT_TRUE(found_jump) << "should have found a dyadic size jump within " << baseline_dyadic << " extra gates";
283 }
284}
285
291TYPED_TEST(MegaHonkTests, WitnessPolynomialsMasked)
292{
293 using Flavor = TypeParam;
294 if constexpr (!Flavor::HasZK) {
295 GTEST_SKIP() << "Masking only applies to ZK flavors";
296 } else {
297 using Builder = typename Flavor::CircuitBuilder;
298
301 auto prover_instance = std::make_shared<ProverInstance_<Flavor>>(builder);
302
303 auto check_masked = [](const auto& poly, const std::string& label) {
304 bool has_masking = false;
305 for (size_t j = 0; j < NUM_MASKED_ROWS; j++) {
306 has_masking |= !poly[NUM_ZERO_ROWS + j].is_zero();
307 }
308 EXPECT_TRUE(has_masking) << label << " should be masked";
309 };
310
311 auto& polys = prover_instance->polynomials;
312 check_masked(polys.w_l(), "w_l");
313 check_masked(polys.w_r(), "w_r");
314 check_masked(polys.w_o(), "w_o");
315 check_masked(polys.w_4(), "w_4");
316 check_masked(polys.z_perm(), "z_perm");
317 if constexpr (Flavor::HasLogDerivLookup) {
318 check_masked(polys.lookup_read_counts(), "lookup_read_counts");
319 check_masked(polys.lookup_read_tags(), "lookup_read_tags");
320 check_masked(polys.lookup_inverses(), "lookup_inverses");
321 }
322 if constexpr (Flavor::HasDataBus) {
323 check_masked(polys.kernel_calldata_read_counts(), "kernel_calldata_read_counts");
324 check_masked(polys.kernel_calldata_inverses(), "kernel_calldata_inverses");
325 if constexpr (Flavor::NUM_BUS_COLUMNS >= 2) {
326 check_masked(polys.first_app_calldata(), "first_app_calldata");
327 check_masked(polys.first_app_calldata_read_counts(), "first_app_calldata_read_counts");
328 check_masked(polys.first_app_calldata_inverses(), "first_app_calldata_inverses");
329 check_masked(polys.second_app_calldata(), "second_app_calldata");
330 check_masked(polys.second_app_calldata_read_counts(), "second_app_calldata_read_counts");
331 check_masked(polys.second_app_calldata_inverses(), "second_app_calldata_inverses");
332 check_masked(polys.third_app_calldata(), "third_app_calldata");
333 check_masked(polys.third_app_calldata_read_counts(), "third_app_calldata_read_counts");
334 check_masked(polys.third_app_calldata_inverses(), "third_app_calldata_inverses");
335 check_masked(polys.fourth_app_calldata(), "fourth_app_calldata");
336 check_masked(polys.fourth_app_calldata_read_counts(), "fourth_app_calldata_read_counts");
337 check_masked(polys.fourth_app_calldata_inverses(), "fourth_app_calldata_inverses");
338 check_masked(polys.fifth_app_calldata(), "fifth_app_calldata");
339 check_masked(polys.fifth_app_calldata_read_counts(), "fifth_app_calldata_read_counts");
340 check_masked(polys.fifth_app_calldata_inverses(), "fifth_app_calldata_inverses");
341 check_masked(polys.return_data(), "return_data");
342 check_masked(polys.return_data_read_counts(), "return_data_read_counts");
343 check_masked(polys.return_data_inverses(), "return_data_inverses");
344 }
345 }
346 }
347}
348
354TYPED_TEST(MegaHonkTests, RepeatedCommitmentsIndicesCorrect)
355{
356 using Flavor = TypeParam;
357 using Builder = typename Flavor::CircuitBuilder;
359 using CommitmentKey = typename Flavor::CommitmentKey;
360 using Commitment = typename Flavor::Commitment;
361
362 auto builder = Builder{};
364 auto prover_instance = std::make_shared<ProverInstance_<Flavor>>(builder);
365 CommitmentKey ck(prover_instance->dyadic_size());
366
367 auto unshifted = prover_instance->polynomials.get_unshifted();
368 auto to_be_shifted = prover_instance->polynomials.get_to_be_shifted();
369
370 constexpr auto repeated = Flavor::REPEATED_COMMITMENTS;
371 ASSERT_EQ(to_be_shifted.size(), repeated.first.count);
372
373 // Build the commitment vector exactly as Shplemini does: [Q, unshifted..., to_be_shifted...]
374 std::vector<Commitment> commitments;
375 commitments.push_back(Commitment::one()); // dummy Q
376 for (auto& poly : unshifted) {
377 commitments.push_back(ck.commit(poly));
378 }
379 for (auto& poly : to_be_shifted) {
380 commitments.push_back(ck.commit(poly));
381 }
382
383 // Same offset logic as remove_repeated_commitments: leading commitments are Shplonk:Q (+1)
384 // and the gemini_masking_poly commitment when the flavor commits to one (+1 more).
385 constexpr size_t offset = flavor_has_gemini_masking<Flavor>() ? 2 : 1;
386 for (size_t i = 0; i < repeated.first.count; i++) {
387 EXPECT_EQ(commitments[repeated.first.original_start + offset + i],
388 commitments[repeated.first.duplicate_start + offset + i])
389 << "REPEATED_COMMITMENTS commitment mismatch at index " << i;
390 }
391}
392
393// =============================================================================
394// MegaZkOffsetBoundaryTest
395// Two-phase test isolating the offset-boundary relation's role in MegaZK:
396// 1. Honest prover — confirms the baseline verifies.
397// 2. Same setup, but `ecc_op_wire_1[2]` tampered to a non-zero value — confirms
398// sumcheck rejects. Row 2 is strictly in the offset area (rows
399// 0..TRACE_OFFSET-1 = 0..3), where honest construction places zero.
400// =============================================================================
401TYPED_TEST(MegaHonkTests, MaliciousEccOpWireInOffsetAreaRejected)
402{
403 using Flavor = TypeParam;
405 GTEST_SKIP() << "Offset-boundary relation applies only to MegaZKFlavor.";
406 } else {
408 using FF = Flavor::FF;
409
410 auto prove_and_verify = [](bool tamper) {
413
415 if (tamper) {
416 prover_instance->polynomials.ecc_op_wire_1().at(2) = FF(42);
417 }
418 auto verification_key =
419 std::make_shared<typename TestFixture::VerificationKey>(prover_instance->get_precomputed());
420 auto vk_and_hash = std::make_shared<typename Flavor::VKAndHash>(verification_key);
421
422 typename TestFixture::Prover prover(prover_instance, verification_key);
423 typename TestFixture::Verifier verifier(vk_and_hash);
424 auto proof = prover.construct_proof();
425 return verifier.verify_proof(proof).result;
426 };
427
428 // Phase 1: honest — baseline must pass.
429 ASSERT_TRUE(prove_and_verify(/*tamper=*/false));
430
431 // Phase 2: tamper — the boundary relation picks up the non-zero contribution.
432 EXPECT_FALSE(prove_and_verify(/*tamper=*/true));
433 }
434}
Curve::AffineElement Point
typename Flavor::VerificationKey VerificationKey
static void SetUpTestSuite()
bool construct_and_verify_honk_proof(auto &builder)
Construct and a verify a Honk proof.
CommitmentKey object over a pairing group 𝔾₁.
Manages the data that is propagated on the public inputs of an application/function circuit.
static constexpr size_t PUBLIC_INPUTS_SIZE
static void add_default(Builder &builder)
Add default IO values to a circuit builder (for native tests)
static constexpr bool HasZK
typename Curve::ScalarField FF
ECCVMCircuitBuilder CircuitBuilder
typename G1::affine_element Commitment
bb::CommitmentKey< Curve > CommitmentKey
FixedVKAndHash_< PrecomputedEntities< Commitment >, BF, ECCVMHardcodedVKAndHash > VerificationKey
The verification key stores commitments to the precomputed polynomials used by the verifier.
static void construct_simple_circuit(MegaBuilder &builder)
Generate a simple test circuit with some ECC op gates and conventional arithmetic gates.
static void add_arithmetic_gates(Builder &builder, const size_t num_gates=4)
Add a specified number of arithmetic gates to the provided circuit.
Contains all the information required by a Honk prover to create a proof, constructed from a finalize...
static constexpr size_t TRACE_OFFSET
static AllSubrelationFailures check_all(const auto &polynomials, const auto &params)
Check that the provided polynomials satisfy all relations for a given Flavor.
Output verify_proof(const Proof &proof)
Perform ultra verification.
The VerifierInstance encapsulates all the necessary information for a Honk Verifier to verify a proof...
typename Group::affine_element AffineElement
Definition bn254.hpp:22
bb::fr ScalarField
Definition bn254.hpp:18
Manages the data that is propagated on the public inputs of an application/function circuit.
AluTraceBuilder builder
Definition alu.test.cpp:124
std::string label
ssize_t offset
Definition engine.cpp:62
testing::Types< UltraFlavor, UltraKeccakFlavor, MegaFlavor > FlavorTypes
auto & engine
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:245
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
std::vector< fr > HonkProof
Definition proof.hpp:15
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
CommitmentKey< Curve > ck
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Full Honk proof layout (used by UltraVerifier).
VectorField result