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; // 20
67 constexpr size_t NUM_GEMINI_EVALS = NativeFlavor::VIRTUAL_LOG_N; // 21
68 // Folding uses 48 rounds (0-47). The last round (47) ends with claim_batching_challenge.
69 // Since rho is also a challenge with no data between, it stays in round 47.
70 constexpr size_t LAST_FOLDING_ROUND = 47;
71
72 // Round 47: rho challenge (same round as folding's claim_batching_challenge)
73 manifest.add_challenge(LAST_FOLDING_ROUND, "rho");
74
75 // Round 48: 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 // Round 49: 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 // Round 50: 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 // Round 51: 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 the instance by changing w_l. This should invalidate the first sumcheck
227 instance->polynomials.w_l.at(1) = NativeFF::random_element();
228 break;
229 }
230 };
231
232 static void test_decider(const TamperingMode& mode)
233 {
234 // Generate accumulator
236 auto transcript = std::make_shared<NativeTranscript>();
237
238 HypernovaFoldingProver prover(transcript);
239 auto accumulator = prover.instance_to_accumulator(instance);
240 tamper_with_accumulator(accumulator, mode);
241
242 // Folding
243 auto incoming_instance = generate_new_instance(5);
244 tamper_with_instance(incoming_instance, mode);
245
246 auto incoming_vk = std::make_shared<NativeVerificationKey>(incoming_instance->get_precomputed());
247 auto incoming_verifier_instance =
249
250 auto prover_transcript = std::make_shared<NativeTranscript>();
251 HypernovaFoldingProver folding_prover(prover_transcript);
252 auto [folding_proof, folded_accumulator] = folding_prover.fold(std::move(accumulator), incoming_instance);
253 tamper_with_accumulator(folded_accumulator, mode);
254
255 // Construct Decider proof
256 HypernovaDeciderProver decider_prover(prover_transcript);
257 auto decider_proof = decider_prover.construct_proof(folded_accumulator);
258
259 // Natively verify the folding
260 auto native_transcript = std::make_shared<NativeTranscript>();
261 NativeHypernovaVerifier native_verifier(native_transcript);
262 auto [first_sumcheck_native, second_sumcheck_native, folded_verifier_accumulator_native] =
263 native_verifier.verify_folding_proof(incoming_verifier_instance, folding_proof);
264
265 // Enable manifest tracking before decider verification
266 native_transcript->enable_manifest();
267
268 // Natively verify the decider proof
269 NativeHypernovaDeciderVerifier decider_verifier(native_transcript);
270 auto native_pairing_points = decider_verifier.verify_proof(folded_verifier_accumulator_native, decider_proof);
271 bool native_verified = native_pairing_points.check();
272
273 // Recursively verify the folding
275
276 auto stdlib_incoming_instance = create_recursive_verifier_instance(&builder, incoming_verifier_instance);
277 auto recursive_verifier_transcript = std::make_shared<RecursiveTranscript>();
278 RecursiveHypernovaVerifier recursive_verifier(recursive_verifier_transcript);
279 RecursiveProof proof(builder, folding_proof);
280 auto [first_sumcheck_recursive, second_sumcheck_recursive, folded_verifier_accumulator] =
281 recursive_verifier.verify_folding_proof(stdlib_incoming_instance, proof);
282
283 // Recursively verify the Decider proof
284 RecursiveProof stdlib_proof(builder, decider_proof);
285 RecursiveHypernovaDeciderVerifier recursive_decider_verifier(recursive_verifier_transcript);
286 auto recursive_pairing_points =
287 recursive_decider_verifier.verify_proof(folded_verifier_accumulator, stdlib_proof);
288
289 // Natively verify pairing points
290 auto recursive_verified = recursive_pairing_points.check();
291
292 // The circuit is valid if and only if we have not tampered or we have tampered the folded accumulator
295 // Pairing point verification should pass as long as we have not tampered the folded accumulator or the
296 // accumulator
297 EXPECT_EQ(recursive_verified, mode != TamperingMode::FoldedAccumulator && mode != TamperingMode::Accumulator);
298 EXPECT_EQ(recursive_verified, native_verified);
299 // First sumcheck fails if the instance has been tampered with
300 EXPECT_EQ(first_sumcheck_recursive, mode != TamperingMode::Instance);
301 EXPECT_EQ(first_sumcheck_recursive, first_sumcheck_native);
302 // Second sumcheck fails if the accumulator has been tampered with
303 EXPECT_EQ(second_sumcheck_recursive, mode != TamperingMode::Accumulator);
304 EXPECT_EQ(second_sumcheck_recursive, second_sumcheck_native);
305
306 // Pin the decider transcript manifest (only check when not tampering)
307 if (mode == TamperingMode::None) {
308 auto expected_manifest = build_expected_decider_manifest();
309 auto verifier_manifest = native_transcript->get_manifest();
310 EXPECT_EQ(verifier_manifest, expected_manifest);
311 }
312 }
313};
314
316{
317 test_decider(TamperingMode::None);
318}
319
321{
323 test_decider(TamperingMode::Accumulator);
324}
325
327{
329 test_decider(TamperingMode::Instance);
330}
331
332TEST_F(HypernovaDeciderVerifierTests, TamperWithFoldedAccumulator)
333{
334 test_decider(TamperingMode::FoldedAccumulator);
335}
#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...
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.