Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
hypernova_verifier.test.cpp
Go to the documentation of this file.
6#include "gtest/gtest.h"
7
8using namespace bb;
9
10// TODO(https://github.com/AztecProtocol/barretenberg/issues/1553): improve testing
11class HypernovaFoldingVerifierTests : public ::testing::Test {
12 protected:
14
15 public:
16 // Recursive verifier
20 using Builder = RecursiveFlavor::CircuitBuilder;
23
24 // Native verifier
27 using NativeFF = NativeFlavor::FF;
29 using NativeVerificationKey = NativeFlavor::VerificationKey;
32
33 // Prover
37
38 enum class TamperingMode : uint8_t {
39 None,
41 };
42
55
58 {
59 for (size_t idx = 0; auto [challenge_lhs, challenge_rhs] : zip_view(lhs.challenge, rhs.challenge)) {
60 if (challenge_lhs != challenge_rhs) {
61 info("Mismatch in the challenges at index ", idx);
62 return false;
63 }
64 }
65 if (lhs.non_shifted_commitment != rhs.non_shifted_commitment) {
66 info("Mismatch in the unshifted commitments");
67 return false;
68 }
69 if (lhs.shifted_commitment != rhs.shifted_commitment) {
70 info("Mismatch in the shifted commitments");
71 return false;
72 }
73 if (lhs.non_shifted_evaluation != rhs.non_shifted_evaluation) {
74 info("Mismatch in the unshifted evaluations");
75 return false;
76 }
77 if (lhs.shifted_evaluation != rhs.shifted_evaluation) {
78 info("Mismatch in the shifted evaluations");
79 return false;
80 }
81 return true;
82 }
83
90 {
91 using FF = RecursiveFlavor::FF;
92 using Commitment = RecursiveFlavor::Commitment;
93 using VerificationKey = RecursiveFlavor::VerificationKey;
94 using VKAndHash = RecursiveFlavor::VKAndHash;
95
96 // Create recursive VK from native VK
97 auto recursive_vk =
99 FF::from_witness(builder, native_instance->get_vk()->hash()));
100
101 // Create recursive instance with the recursive VK
102 auto recursive_instance = std::make_shared<RecursiveVerifierInstance>(recursive_vk);
103
104 // Convert alpha
105 recursive_instance->alpha = FF::from_witness(builder, native_instance->alpha);
106
107 // Convert witness commitments
108 auto native_comms = native_instance->witness_commitments.get_all();
109 for (auto [native_comm, recursive_comm] :
110 zip_view(native_comms, recursive_instance->witness_commitments.get_all())) {
111 recursive_comm = Commitment::from_witness(builder, native_comm);
112 }
113
114 // Convert gate challenges
115 recursive_instance->gate_challenges = std::vector<FF>(native_instance->gate_challenges.size());
116 for (auto [native_challenge, recursive_challenge] :
117 zip_view(native_instance->gate_challenges, recursive_instance->gate_challenges)) {
118 recursive_challenge = FF::from_witness(builder, native_challenge);
119 }
120
121 // Convert relation parameters
122 recursive_instance->relation_parameters.eta =
123 FF::from_witness(builder, native_instance->relation_parameters.eta);
124 recursive_instance->relation_parameters.eta_two =
125 FF::from_witness(builder, native_instance->relation_parameters.eta_two);
126 recursive_instance->relation_parameters.eta_three =
127 FF::from_witness(builder, native_instance->relation_parameters.eta_three);
128 recursive_instance->relation_parameters.beta =
129 FF::from_witness(builder, native_instance->relation_parameters.beta);
130 recursive_instance->relation_parameters.gamma =
131 FF::from_witness(builder, native_instance->relation_parameters.gamma);
132 recursive_instance->relation_parameters.public_input_delta =
133 FF::from_witness(builder, native_instance->relation_parameters.public_input_delta);
134
135 // For ZK flavors: convert gemini_masking_commitment
136 if constexpr (NativeFlavor::HasZK) {
137 recursive_instance->gemini_masking_commitment =
138 Commitment::from_witness(builder, native_instance->gemini_masking_commitment);
139 }
140
141 return recursive_instance;
142 }
143
145 {
146 switch (mode) {
148 break;
150 // Tamper with w_l at the first row where q_arith is non-zero (an active arithmetic gate).
151 auto& q_arith = instance->polynomials.q_arith;
152 for (size_t i = ProverInstance::TRACE_OFFSET; i < q_arith.end_index(); i++) {
153 if (!q_arith[i].is_zero()) {
154 instance->polynomials.w_l.at(i) = NativeFF::random_element();
155 break;
156 }
157 }
158 } break;
159 }
160 };
161
173 {
174 TranscriptManifest manifest;
175 constexpr size_t frs_per_G = FrCodec::calc_num_fields<curve::BN254::AffineElement>();
176 constexpr size_t NUM_SUMCHECK_UNIVARIATES = NativeFlavor::VIRTUAL_LOG_N; // 21
177
178 size_t round = 0;
179
180 // Round 0: Oink preamble + wires + ECC ops + databus -> eta challenge
181 manifest.add_challenge(round, "eta");
182 manifest.add_entry(round, "vk_hash", 1);
183 for (size_t i = 0; i < 4; ++i) {
184 manifest.add_entry(round, "public_input_" + std::to_string(i), 1);
185 }
186 for (const auto& wire : { "W_L", "W_R", "W_O" }) {
187 manifest.add_entry(round, wire, frs_per_G);
188 }
189 for (const auto& wire : { "ECC_OP_WIRE_1", "ECC_OP_WIRE_2", "ECC_OP_WIRE_3", "ECC_OP_WIRE_4" }) {
190 manifest.add_entry(round, wire, frs_per_G);
191 }
192 for (const auto& bus : { "CALLDATA", "SECONDARY_CALLDATA", "RETURN_DATA" }) {
193 manifest.add_entry(round, bus, frs_per_G);
194 manifest.add_entry(round, std::string(bus) + "_READ_COUNTS", frs_per_G);
195 }
196 round++;
197
198 // Round 1: lookup + w_4 -> beta, gamma challenges
199 manifest.add_challenge(round, std::array{ "beta", "gamma" });
200 manifest.add_entry(round, "LOOKUP_READ_COUNTS", frs_per_G);
201 manifest.add_entry(round, "LOOKUP_READ_TAGS", frs_per_G);
202 manifest.add_entry(round, "W_4", frs_per_G);
203 round++;
204
205 // Round 2: inverses + z_perm -> alpha + gate_challenge (consecutive challenges in same round)
206 manifest.add_challenge(round, "alpha");
207 manifest.add_challenge(round, "HypernovaFoldingProver:gate_challenge");
208 manifest.add_entry(round, "LOOKUP_INVERSES", frs_per_G);
209 manifest.add_entry(round, "CALLDATA_INVERSES", frs_per_G);
210 manifest.add_entry(round, "SECONDARY_CALLDATA_INVERSES", frs_per_G);
211 manifest.add_entry(round, "RETURN_DATA_INVERSES", frs_per_G);
212 manifest.add_entry(round, "Z_PERM", frs_per_G);
213 round++;
214
215 // Rounds 3-23: main sumcheck univariates (21 rounds)
216 for (size_t i = 0; i < NUM_SUMCHECK_UNIVARIATES; ++i) {
217 manifest.add_challenge(round, "Sumcheck:u_" + std::to_string(i));
218 manifest.add_entry(round, "Sumcheck:univariate_" + std::to_string(i), 8);
219 round++;
220 }
221
222 // Round 24: unshifted batching challenges + shifted batching challenges + evaluations
223 for (size_t i = 0; i < MegaFlavor::NUM_UNSHIFTED_ENTITIES - 1; ++i) {
224 manifest.add_challenge(round, "unshifted_challenge_" + std::to_string(i));
225 }
226 for (size_t i = 0; i < MegaFlavor::NUM_SHIFTED_ENTITIES - 1; ++i) {
227 manifest.add_challenge(round, "shifted_challenge_" + std::to_string(i));
228 }
229 manifest.add_entry(round, "Sumcheck:evaluations", 57);
230 round++;
231
232 // Round 25: Sumcheck:alpha + MLB accumulator data (Sumcheck:alpha is consecutive challenge)
233 manifest.add_challenge(round, "Sumcheck:alpha");
234 manifest.add_entry(round, "non_shifted_accumulator_commitment", frs_per_G);
235 manifest.add_entry(round, "shifted_accumulator_commitment", frs_per_G);
236 for (size_t i = 0; i < NUM_SUMCHECK_UNIVARIATES; ++i) {
237 manifest.add_entry(round, "accumulator_challenge_" + std::to_string(i), 1);
238 }
239 manifest.add_entry(round, "accumulator_evaluation_0", 1);
240 manifest.add_entry(round, "accumulator_evaluation_1", 1);
241 round++;
242
243 // Rounds 26-46: MLB sumcheck univariates (21 rounds)
244 for (size_t i = 0; i < NUM_SUMCHECK_UNIVARIATES; ++i) {
245 manifest.add_challenge(round, "Sumcheck:u_" + std::to_string(i));
246 manifest.add_entry(round, "Sumcheck:univariate_" + std::to_string(i), 4);
247 round++;
248 }
249
250 // Round 47: final evaluations + claim_batching_challenge
251 manifest.add_challenge(round, "claim_batching_challenge");
252 manifest.add_entry(round, "Sumcheck:evaluations", 6);
253
254 return manifest;
255 }
256
257 static void test_folding(const TamperingMode& mode)
258 {
259 // Generate accumulator
260 auto instance = generate_new_instance();
261 auto transcript = std::make_shared<NativeTranscript>();
262
263 bb::HypernovaFoldingProver prover(transcript);
264 auto accumulator = prover.instance_to_accumulator(instance);
265
266 // Folding
267 auto incoming_instance = generate_new_instance(5);
268 tampering(incoming_instance, mode);
269 auto incoming_vk = std::make_shared<NativeVerificationKey>(incoming_instance->get_precomputed());
270 auto incoming_verifier_instance =
272
273 auto folding_transcript = std::make_shared<NativeTranscript>();
274 HypernovaFoldingProver folding_prover(folding_transcript);
275 auto [folding_proof, folded_accumulator] = folding_prover.fold(std::move(accumulator), incoming_instance);
276
277 // Natively verify the folding (with manifest tracking)
278 auto native_verifier_transcript = std::make_shared<NativeTranscript>();
279 native_verifier_transcript->enable_manifest();
280 NativeHypernovaVerifier native_verifier(native_verifier_transcript);
281 auto [first_sumcheck_native, second_sumcheck_native, folded_verifier_accumulator_native] =
282 native_verifier.verify_folding_proof(incoming_verifier_instance, folding_proof);
283
284 // Recursively verify the folding
286
287 auto stdlib_incoming_instance = create_recursive_verifier_instance(&builder, incoming_verifier_instance);
288 auto recursive_verifier_transcript = std::make_shared<RecursiveTranscript>();
289 RecursiveHypernovaVerifier recursive_verifier(recursive_verifier_transcript);
290 RecursiveProof proof(builder, folding_proof);
291 auto [first_sumcheck_recursive, second_sumcheck_recursive, folded_verifier_accumulator] =
292 recursive_verifier.verify_folding_proof(stdlib_incoming_instance, proof);
293
294 // If the instance has been tampered with, then the first sumcheck should fail (hence the circuit is not
295 // satisfied), but the second should pass
297 EXPECT_EQ(first_sumcheck_recursive, mode == TamperingMode::None);
298 EXPECT_EQ(first_sumcheck_recursive, first_sumcheck_native);
299 EXPECT_TRUE(second_sumcheck_recursive);
300 EXPECT_EQ(second_sumcheck_recursive, second_sumcheck_native);
302 folded_accumulator, folded_verifier_accumulator.get_value<NativeVerifierAccumulator>()));
303
304 // Pin the folding transcript manifest (only check when not tampering)
305 if (mode == TamperingMode::None) {
306 auto expected_manifest = build_expected_folding_manifest();
307 auto verifier_manifest = native_verifier_transcript->get_manifest();
308 EXPECT_EQ(verifier_manifest, expected_manifest);
309 }
310 }
311};
312
314{
315 test_folding(TamperingMode::None);
316}
317
319{
321 test_folding(TamperingMode::Instance);
322}
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:33
NativeFlavor::VerificationKey NativeVerificationKey
static void tampering(std::shared_ptr< ProverInstance > &instance, const TamperingMode &mode)
static std::shared_ptr< ProverInstance > generate_new_instance(size_t log_num_gates=4)
static std::shared_ptr< RecursiveVerifierInstance > create_recursive_verifier_instance(Builder *builder, const std::shared_ptr< NativeVerifierInstance > &native_instance)
Test helper to create a recursive verifier instance from a native one.
RecursiveHypernovaVerifier::Flavor RecursiveFlavor
static TranscriptManifest build_expected_folding_manifest()
Build the expected transcript manifest for HyperNova folding.
static void test_folding(const TamperingMode &mode)
NativeHypernovaVerifier::Flavor NativeFlavor
RecursiveFlavor::CircuitBuilder Builder
static bool compare_prover_verifier_accumulators(const NativeProverAccumulator &lhs, const NativeVerifierAccumulator &rhs)
RecursiveHypernovaVerifier::Proof RecursiveProof
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
HyperNova folding prover. Folds circuit instances into accumulators, deferring PCS verification.
MultilinearBatchingProverClaim Accumulator
ProverInstance_< Flavor > ProverInstance
Accumulator instance_to_accumulator(const std::shared_ptr< ProverInstance > &instance, const std::shared_ptr< VerificationKey > &honk_vk=nullptr)
Turn an instance into an accumulator by running Sumcheck.
std::pair< HonkProof, Accumulator > fold(Accumulator &&accumulator, const std::shared_ptr< ProverInstance > &instance, const std::shared_ptr< VerificationKey > &honk_vk=nullptr)
Fold an instance into an accumulator.
HyperNova folding verifier (native + recursive). Verifies folding proofs and maintains accumulators.
std::tuple< bool, bool, Accumulator > verify_folding_proof(const std::shared_ptr< typename HypernovaFoldingVerifier::VerifierInstance > &instance, const Proof &proof)
Verify folding proof. Return the new accumulator and the results of the two sumchecks.
std::conditional_t< IsRecursiveFlavor< Flavor >, stdlib::Proof< MegaCircuitBuilder >, HonkProof > Proof
VerifierInstance_< Flavor > VerifierInstance
MultilinearBatchingVerifierClaim< Curve > Accumulator
static constexpr size_t NUM_SHIFTED_ENTITIES
static constexpr size_t NUM_UNSHIFTED_ENTITIES
static void add_arithmetic_gates_with_public_inputs(Builder &builder, const size_t num_gates=4)
Add a specified number of arithmetic gates (with public inputs) to the provided circuit.
static void add_lookup_gates(Builder &builder, size_t num_iterations=1)
Add lookup gates using the uint32 XOR lookup table (table size 4096)
static void add_arithmetic_gates(Builder &builder, const size_t num_gates=4)
Add a specified number of arithmetic gates to the provided circuit.
Base Native verification key class.
Definition flavor.hpp:135
Contains all the information required by a Honk prover to create a proof, constructed from a finalize...
static constexpr size_t TRACE_OFFSET
void add_entry(size_t round, const std::string &element_label, size_t element_size)
void add_challenge(size_t round, const std::string &label)
Add a single challenge label to the manifest for the given round.
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
The VerifierInstance encapsulates all the necessary information for a Honk Verifier to verify a proof...
#define info(...)
Definition log.hpp:93
AluTraceBuilder builder
Definition alu.test.cpp:124
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
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:155
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
Prover's claim for multilinear batching - contains polynomials and their evaluation claims.
Verifier's claim for multilinear batching - contains commitments and evaluation claims.