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 -> KZG:masking_challenge
92 manifest.add_entry(LAST_FOLDING_ROUND + 4, "KZG:W", frs_per_G);
93 manifest.add_challenge(LAST_FOLDING_ROUND + 4, "KZG:masking_challenge");
94
95 return manifest;
96 }
97
110
112 const NativeVerifierAccumulator& rhs)
113 {
114 for (size_t idx = 0; auto [challenge_lhs, challenge_rhs] : zip_view(lhs.challenge, rhs.challenge)) {
115 if (challenge_lhs != challenge_rhs) {
116 info("Mismatch in the challenges at index ", idx);
117 return false;
118 }
119 }
120 if (lhs.non_shifted_commitment != rhs.non_shifted_commitment) {
121 info("Mismatch in the unshifted commitments");
122 return false;
123 }
124 if (lhs.shifted_commitment != rhs.shifted_commitment) {
125 info("Mismatch in the shifted commitments");
126 return false;
127 }
128 if (lhs.non_shifted_evaluation != rhs.non_shifted_evaluation) {
129 info("Mismatch in the unshifted evaluations");
130 return false;
131 }
132 if (lhs.shifted_evaluation != rhs.shifted_evaluation) {
133 info("Mismatch in the shifted evaluations");
134 return false;
135 }
136 return true;
137 }
138
145 {
146 using FF = RecursiveFlavor::FF;
147 using Commitment = RecursiveFlavor::Commitment;
148 using VerificationKey = RecursiveFlavor::VerificationKey;
149 using VKAndHash = RecursiveFlavor::VKAndHash;
150
151 // Create recursive VK from native VK
152 auto recursive_vk =
154 FF::from_witness(builder, native_instance->get_vk()->hash()));
155
156 // Create recursive instance with the recursive VK
157 auto recursive_instance = std::make_shared<RecursiveVerifierInstance>(recursive_vk);
158
159 // Convert alpha
160 recursive_instance->alpha = FF::from_witness(builder, native_instance->alpha);
161
162 // Convert witness commitments
163 auto native_comms = native_instance->witness_commitments.get_all();
164 for (auto [native_comm, recursive_comm] :
165 zip_view(native_comms, recursive_instance->witness_commitments.get_all())) {
166 recursive_comm = Commitment::from_witness(builder, native_comm);
167 }
168
169 // Convert gate challenges
170 recursive_instance->gate_challenges = std::vector<FF>(native_instance->gate_challenges.size());
171 for (auto [native_challenge, recursive_challenge] :
172 zip_view(native_instance->gate_challenges, recursive_instance->gate_challenges)) {
173 recursive_challenge = FF::from_witness(builder, native_challenge);
174 }
175
176 // Convert relation parameters
177 recursive_instance->relation_parameters.eta =
178 FF::from_witness(builder, native_instance->relation_parameters.eta);
179 recursive_instance->relation_parameters.eta_two =
180 FF::from_witness(builder, native_instance->relation_parameters.eta_two);
181 recursive_instance->relation_parameters.eta_three =
182 FF::from_witness(builder, native_instance->relation_parameters.eta_three);
183 recursive_instance->relation_parameters.beta =
184 FF::from_witness(builder, native_instance->relation_parameters.beta);
185 recursive_instance->relation_parameters.gamma =
186 FF::from_witness(builder, native_instance->relation_parameters.gamma);
187 recursive_instance->relation_parameters.public_input_delta =
188 FF::from_witness(builder, native_instance->relation_parameters.public_input_delta);
189
190 // For ZK flavors: convert gemini_masking_commitment
191 if constexpr (NativeFlavor::HasZK) {
192 recursive_instance->gemini_masking_commitment =
193 Commitment::from_witness(builder, native_instance->gemini_masking_commitment);
194 }
195
196 return recursive_instance;
197 }
198
200 {
201 switch (mode) {
204 break;
206 // Tamper with the accumulator by changing the challenge, this should invalidate the decider proof but not
207 // the folding
208 accumulator.challenge[0] = NativeFF::random_element();
209 break;
211 // Tamper the folded accumulator by changing one commitment, this should invalidate the PCS but not the
212 // folding.
213 accumulator.non_shifted_commitment =
214 accumulator.non_shifted_commitment + NativeFlavor::Curve::AffineElement::one();
215 break;
216 }
217 };
218
220 {
221 switch (mode) {
225 break;
227 // Tamper with the instance by changing w_l. This should invalidate the first sumcheck
228 instance->polynomials.w_l.at(1) = NativeFF::random_element();
229 break;
230 }
231 };
232
233 static void test_decider(const TamperingMode& mode)
234 {
235 // Generate accumulator
237 auto transcript = std::make_shared<NativeTranscript>();
238
239 HypernovaFoldingProver prover(transcript);
240 auto accumulator = prover.instance_to_accumulator(instance);
241 tamper_with_accumulator(accumulator, mode);
242
243 // Folding
244 auto incoming_instance = generate_new_instance(5);
245 tamper_with_instance(incoming_instance, mode);
246
247 auto incoming_vk = std::make_shared<NativeVerificationKey>(incoming_instance->get_precomputed());
248 auto incoming_verifier_instance =
250
251 auto prover_transcript = std::make_shared<NativeTranscript>();
252 HypernovaFoldingProver folding_prover(prover_transcript);
253 auto [folding_proof, folded_accumulator] = folding_prover.fold(std::move(accumulator), incoming_instance);
254 tamper_with_accumulator(folded_accumulator, mode);
255
256 // Construct Decider proof
257 HypernovaDeciderProver decider_prover(prover_transcript);
258 auto decider_proof = decider_prover.construct_proof(folded_accumulator);
259
260 // Natively verify the folding
261 auto native_transcript = std::make_shared<NativeTranscript>();
262 NativeHypernovaVerifier native_verifier(native_transcript);
263 auto [first_sumcheck_native, second_sumcheck_native, folded_verifier_accumulator_native] =
264 native_verifier.verify_folding_proof(incoming_verifier_instance, folding_proof);
265
266 // Enable manifest tracking before decider verification
267 native_transcript->enable_manifest();
268
269 // Natively verify the decider proof
270 NativeHypernovaDeciderVerifier decider_verifier(native_transcript);
271 auto native_pairing_points = decider_verifier.verify_proof(folded_verifier_accumulator_native, decider_proof);
272 bool native_verified = native_pairing_points.check();
273
274 // Recursively verify the folding
276
277 auto stdlib_incoming_instance = create_recursive_verifier_instance(&builder, incoming_verifier_instance);
278 auto recursive_verifier_transcript = std::make_shared<RecursiveTranscript>();
279 RecursiveHypernovaVerifier recursive_verifier(recursive_verifier_transcript);
280 RecursiveProof proof(builder, folding_proof);
281 auto [first_sumcheck_recursive, second_sumcheck_recursive, folded_verifier_accumulator] =
282 recursive_verifier.verify_folding_proof(stdlib_incoming_instance, proof);
283
284 // Recursively verify the Decider proof
285 RecursiveProof stdlib_proof(builder, decider_proof);
286 RecursiveHypernovaDeciderVerifier recursive_decider_verifier(recursive_verifier_transcript);
287 auto recursive_pairing_points =
288 recursive_decider_verifier.verify_proof(folded_verifier_accumulator, stdlib_proof);
289
290 // Natively verify pairing points
291 auto recursive_verified = recursive_pairing_points.check();
292
293 // The circuit is valid if and only if we have not tampered or we have tampered the folded accumulator
296 // Pairing point verification should pass as long as we have not tampered the folded accumulator or the
297 // accumulator
298 EXPECT_EQ(recursive_verified, mode != TamperingMode::FoldedAccumulator && mode != TamperingMode::Accumulator);
299 EXPECT_EQ(recursive_verified, native_verified);
300 // First sumcheck fails if the instance has been tampered with
301 EXPECT_EQ(first_sumcheck_recursive, mode != TamperingMode::Instance);
302 EXPECT_EQ(first_sumcheck_recursive, first_sumcheck_native);
303 // Second sumcheck fails if the accumulator has been tampered with
304 EXPECT_EQ(second_sumcheck_recursive, mode != TamperingMode::Accumulator);
305 EXPECT_EQ(second_sumcheck_recursive, second_sumcheck_native);
306
307 // Pin the decider transcript manifest (only check when not tampering)
308 if (mode == TamperingMode::None) {
309 auto expected_manifest = build_expected_decider_manifest();
310 auto verifier_manifest = native_transcript->get_manifest();
311 EXPECT_EQ(verifier_manifest, expected_manifest);
312 }
313 }
314};
315
317{
318 test_decider(TamperingMode::None);
319}
320
322{
324 test_decider(TamperingMode::Accumulator);
325}
326
328{
330 test_decider(TamperingMode::Instance);
331}
332
333TEST_F(HypernovaDeciderVerifierTests, TamperWithFoldedAccumulator)
334{
335 test_decider(TamperingMode::FoldedAccumulator);
336}
#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:142
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.