Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
hypernova_decider_verifier.test.cpp
Go to the documentation of this file.
8#include "gtest/gtest.h"
9
10using namespace bb;
11
12// TODO(https://github.com/AztecProtocol/barretenberg/issues/1553): improve testing
13class HypernovaDeciderVerifierTests : public ::testing::Test {
14 protected:
16
17 public:
18 // Recursive decider verifier
22 using Builder = RecursiveFlavor::CircuitBuilder;
25
26 // Native decider verifier
29 using CommitmentKey = NativeFlavor::CommitmentKey;
30 using NativeFF = NativeFlavor::FF;
32 using NativeVerificationKey = NativeFlavor::VerificationKey;
34
35 // Provers
40
41 // Recursive Verifier
44
45 // Native Verifier
48
50
63 {
64 TranscriptManifest manifest;
65 constexpr size_t frs_per_G = FrCodec::calc_num_fields<curve::BN254::AffineElement>();
66 constexpr size_t NUM_GEMINI_FOLDS = NativeFlavor::VIRTUAL_LOG_N - 1;
67 constexpr size_t NUM_GEMINI_EVALS = NativeFlavor::VIRTUAL_LOG_N;
68 // Folding uses 3 Oink rounds + VIRTUAL_LOG_N sumcheck + 1 batching + 1 MLB data + VIRTUAL_LOG_N MLB sumcheck.
69 // The last round ends with claim_batching_challenge. rho shares the same round.
70 constexpr size_t LAST_FOLDING_ROUND = (2 * NativeFlavor::VIRTUAL_LOG_N) + 5;
71
72 // rho challenge (same round as folding's claim_batching_challenge)
73 manifest.add_challenge(LAST_FOLDING_ROUND, "rho");
74
75 // Gemini FOLD commitments -> Gemini:r
76 for (size_t i = 1; i <= NUM_GEMINI_FOLDS; ++i) {
77 manifest.add_entry(LAST_FOLDING_ROUND + 1, "Gemini:FOLD_" + std::to_string(i), frs_per_G);
78 }
79 manifest.add_challenge(LAST_FOLDING_ROUND + 1, "Gemini:r");
80
81 // Gemini evaluations -> Shplonk:nu
82 for (size_t i = 1; i <= NUM_GEMINI_EVALS; ++i) {
83 manifest.add_entry(LAST_FOLDING_ROUND + 2, "Gemini:a_" + std::to_string(i), 1);
84 }
85 manifest.add_challenge(LAST_FOLDING_ROUND + 2, "Shplonk:nu");
86
87 // Shplonk:Q -> Shplonk:z
88 manifest.add_entry(LAST_FOLDING_ROUND + 3, "Shplonk:Q", frs_per_G);
89 manifest.add_challenge(LAST_FOLDING_ROUND + 3, "Shplonk:z");
90
91 // KZG:W
92 manifest.add_entry(LAST_FOLDING_ROUND + 4, "KZG:W", frs_per_G);
93
94 return manifest;
95 }
96
109
111 const NativeVerifierAccumulator& rhs)
112 {
113 for (size_t idx = 0; auto [challenge_lhs, challenge_rhs] : zip_view(lhs.challenge, rhs.challenge)) {
114 if (challenge_lhs != challenge_rhs) {
115 info("Mismatch in the challenges at index ", idx);
116 return false;
117 }
118 }
119 if (lhs.non_shifted_commitment != rhs.non_shifted_commitment) {
120 info("Mismatch in the unshifted commitments");
121 return false;
122 }
123 if (lhs.shifted_commitment != rhs.shifted_commitment) {
124 info("Mismatch in the shifted commitments");
125 return false;
126 }
127 if (lhs.non_shifted_evaluation != rhs.non_shifted_evaluation) {
128 info("Mismatch in the unshifted evaluations");
129 return false;
130 }
131 if (lhs.shifted_evaluation != rhs.shifted_evaluation) {
132 info("Mismatch in the shifted evaluations");
133 return false;
134 }
135 return true;
136 }
137
144 {
145 using FF = RecursiveFlavor::FF;
146 using Commitment = RecursiveFlavor::Commitment;
147 using VerificationKey = RecursiveFlavor::VerificationKey;
148 using VKAndHash = RecursiveFlavor::VKAndHash;
149
150 // Create recursive VK from native VK
151 auto recursive_vk =
153 FF::from_witness(builder, native_instance->get_vk()->hash()));
154
155 // Create recursive instance with the recursive VK
156 auto recursive_instance = std::make_shared<RecursiveVerifierInstance>(recursive_vk);
157
158 // Convert alpha
159 recursive_instance->alpha = FF::from_witness(builder, native_instance->alpha);
160
161 // Convert witness commitments
162 auto native_comms = native_instance->witness_commitments.get_all();
163 for (auto [native_comm, recursive_comm] :
164 zip_view(native_comms, recursive_instance->witness_commitments.get_all())) {
165 recursive_comm = Commitment::from_witness(builder, native_comm);
166 }
167
168 // Convert gate challenges
169 recursive_instance->gate_challenges = std::vector<FF>(native_instance->gate_challenges.size());
170 for (auto [native_challenge, recursive_challenge] :
171 zip_view(native_instance->gate_challenges, recursive_instance->gate_challenges)) {
172 recursive_challenge = FF::from_witness(builder, native_challenge);
173 }
174
175 // Convert relation parameters
176 recursive_instance->relation_parameters.eta =
177 FF::from_witness(builder, native_instance->relation_parameters.eta);
178 recursive_instance->relation_parameters.eta_two =
179 FF::from_witness(builder, native_instance->relation_parameters.eta_two);
180 recursive_instance->relation_parameters.eta_three =
181 FF::from_witness(builder, native_instance->relation_parameters.eta_three);
182 recursive_instance->relation_parameters.beta =
183 FF::from_witness(builder, native_instance->relation_parameters.beta);
184 recursive_instance->relation_parameters.gamma =
185 FF::from_witness(builder, native_instance->relation_parameters.gamma);
186 recursive_instance->relation_parameters.public_input_delta =
187 FF::from_witness(builder, native_instance->relation_parameters.public_input_delta);
188
189 // For ZK flavors: convert gemini_masking_commitment
190 if constexpr (NativeFlavor::HasZK) {
191 recursive_instance->gemini_masking_commitment =
192 Commitment::from_witness(builder, native_instance->gemini_masking_commitment);
193 }
194
195 return recursive_instance;
196 }
197
199 {
200 switch (mode) {
203 break;
205 // Tamper with the accumulator by changing the challenge, this should invalidate the decider proof but not
206 // the folding
207 accumulator.challenge[0] = NativeFF::random_element();
208 break;
210 // Tamper the folded accumulator by changing one commitment, this should invalidate the PCS but not the
211 // folding.
212 accumulator.non_shifted_commitment =
213 accumulator.non_shifted_commitment + NativeFlavor::Curve::AffineElement::one();
214 break;
215 }
216 };
217
219 {
220 switch (mode) {
224 break;
226 // Tamper with w_l at the first row where q_arith is non-zero (an active arithmetic gate).
227 auto& q_arith = instance->polynomials.q_arith;
228 for (size_t i = ProverInstance::TRACE_OFFSET; i < q_arith.end_index(); i++) {
229 if (!q_arith[i].is_zero()) {
230 instance->polynomials.w_l.at(i) = NativeFF::random_element();
231 break;
232 }
233 }
234 } break;
235 }
236 };
237
238 static void test_decider(const TamperingMode& mode)
239 {
240 // Generate accumulator
242 auto transcript = std::make_shared<NativeTranscript>();
243
244 HypernovaFoldingProver prover(transcript);
245 auto accumulator = prover.instance_to_accumulator(instance);
246 tamper_with_accumulator(accumulator, mode);
247
248 // Folding
249 auto incoming_instance = generate_new_instance(5);
250 tamper_with_instance(incoming_instance, mode);
251
252 auto incoming_vk = std::make_shared<NativeVerificationKey>(incoming_instance->get_precomputed());
253 auto incoming_verifier_instance =
255
256 auto prover_transcript = std::make_shared<NativeTranscript>();
257 HypernovaFoldingProver folding_prover(prover_transcript);
258 auto [folding_proof, folded_accumulator] = folding_prover.fold(std::move(accumulator), incoming_instance);
259 tamper_with_accumulator(folded_accumulator, mode);
260
261 // Construct Decider proof
262 HypernovaDeciderProver decider_prover(prover_transcript);
263 auto decider_proof = decider_prover.construct_proof(folded_accumulator);
264
265 // Natively verify the folding
266 auto native_transcript = std::make_shared<NativeTranscript>();
267 NativeHypernovaVerifier native_verifier(native_transcript);
268 auto [first_sumcheck_native, second_sumcheck_native, folded_verifier_accumulator_native] =
269 native_verifier.verify_folding_proof(incoming_verifier_instance, folding_proof);
270
271 // Enable manifest tracking before decider verification
272 native_transcript->enable_manifest();
273
274 // Natively verify the decider proof
275 NativeHypernovaDeciderVerifier decider_verifier(native_transcript);
276 auto native_pairing_points = decider_verifier.verify_proof(folded_verifier_accumulator_native, decider_proof);
277 bool native_verified = native_pairing_points.check();
278
279 // Recursively verify the folding
281
282 auto stdlib_incoming_instance = create_recursive_verifier_instance(&builder, incoming_verifier_instance);
283 auto recursive_verifier_transcript = std::make_shared<RecursiveTranscript>();
284 RecursiveHypernovaVerifier recursive_verifier(recursive_verifier_transcript);
285 RecursiveProof proof(builder, folding_proof);
286 auto [first_sumcheck_recursive, second_sumcheck_recursive, folded_verifier_accumulator] =
287 recursive_verifier.verify_folding_proof(stdlib_incoming_instance, proof);
288
289 // Recursively verify the Decider proof
290 RecursiveProof stdlib_proof(builder, decider_proof);
291 RecursiveHypernovaDeciderVerifier recursive_decider_verifier(recursive_verifier_transcript);
292 auto recursive_pairing_points =
293 recursive_decider_verifier.verify_proof(folded_verifier_accumulator, stdlib_proof);
294
295 // Natively verify pairing points
296 auto recursive_verified = recursive_pairing_points.check();
297
298 // The circuit is valid if and only if we have not tampered or we have tampered the folded accumulator
301 // Pairing point verification should pass as long as we have not tampered the folded accumulator or the
302 // accumulator
303 EXPECT_EQ(recursive_verified, mode != TamperingMode::FoldedAccumulator && mode != TamperingMode::Accumulator);
304 EXPECT_EQ(recursive_verified, native_verified);
305 // First sumcheck fails if the instance has been tampered with
306 EXPECT_EQ(first_sumcheck_recursive, mode != TamperingMode::Instance);
307 EXPECT_EQ(first_sumcheck_recursive, first_sumcheck_native);
308 // Second sumcheck fails if the accumulator has been tampered with
309 EXPECT_EQ(second_sumcheck_recursive, mode != TamperingMode::Accumulator);
310 EXPECT_EQ(second_sumcheck_recursive, second_sumcheck_native);
311
312 // Pin the decider transcript manifest (only check when not tampering)
313 if (mode == TamperingMode::None) {
314 auto expected_manifest = build_expected_decider_manifest();
315 auto verifier_manifest = native_transcript->get_manifest();
316 EXPECT_EQ(verifier_manifest, expected_manifest);
317 }
318 }
319};
320
322{
323 test_decider(TamperingMode::None);
324}
325
327{
329 test_decider(TamperingMode::Accumulator);
330}
331
333{
335 test_decider(TamperingMode::Instance);
336}
337
338TEST_F(HypernovaDeciderVerifierTests, TamperWithFoldedAccumulator)
339{
340 test_decider(TamperingMode::FoldedAccumulator);
341}
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:33
std::shared_ptr< Napi::ThreadSafeFunction > instance
NativeHypernovaDeciderVerifier::Flavor NativeFlavor
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.
static std::shared_ptr< ProverInstance > generate_new_instance(size_t log_num_gates=4)
RecursiveHypernovaDeciderVerifier::Proof RecursiveProof
NativeFlavor::VerificationKey NativeVerificationKey
static void tamper_with_accumulator(NativeProverAccumulator &accumulator, const TamperingMode &mode)
static bool compare_prover_verifier_accumulators(const NativeProverAccumulator &lhs, const NativeVerifierAccumulator &rhs)
static void test_decider(const TamperingMode &mode)
RecursiveHypernovaDeciderVerifier::Flavor RecursiveFlavor
static TranscriptManifest build_expected_decider_manifest()
Build the expected transcript manifest for HyperNova decider.
static void tamper_with_instance(std::shared_ptr< ProverInstance > &instance, const TamperingMode &mode)
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
HyperNova decider prover. Produces final opening proof for the accumulated claim.
HonkProof construct_proof(Accumulator &accumulator)
HyperNova decider verifier (native + recursive). Verifies final opening proof.
HypernovaFoldingVerifier< Flavor >::Accumulator Accumulator
PairingPoints verify_proof(Accumulator &accumulator, const Proof &proof)
HypernovaFoldingVerifier< Flavor >::Proof Proof
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.
VerifierInstance_< Flavor > VerifierInstance
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.