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 }
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 }
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 auto ck = CommitmentKey(folded_accumulator.dyadic_size);
258 HypernovaDeciderProver decider_prover(prover_transcript);
259 auto decider_proof = decider_prover.construct_proof(ck, folded_accumulator);
260
261 // Natively verify the folding
262 auto native_transcript = std::make_shared<NativeTranscript>();
263 NativeHypernovaVerifier native_verifier(native_transcript);
264 auto [first_sumcheck_native, second_sumcheck_native, folded_verifier_accumulator_native] =
265 native_verifier.verify_folding_proof(incoming_verifier_instance, folding_proof);
266
267 // Enable manifest tracking before decider verification
268 native_transcript->enable_manifest();
269
270 // Natively verify the decider proof
271 NativeHypernovaDeciderVerifier decider_verifier(native_transcript);
272 auto native_pairing_points = decider_verifier.verify_proof(folded_verifier_accumulator_native, decider_proof);
273 bool native_verified = native_pairing_points.check();
274
275 // Recursively verify the folding
277
278 auto stdlib_incoming_instance = create_recursive_verifier_instance(&builder, incoming_verifier_instance);
279 auto recursive_verifier_transcript = std::make_shared<RecursiveTranscript>();
280 RecursiveHypernovaVerifier recursive_verifier(recursive_verifier_transcript);
281 RecursiveProof proof(builder, folding_proof);
282 auto [first_sumcheck_recursive, second_sumcheck_recursive, folded_verifier_accumulator] =
283 recursive_verifier.verify_folding_proof(stdlib_incoming_instance, proof);
284
285 // Recursively verify the Decider proof
286 RecursiveProof stdlib_proof(builder, decider_proof);
287 RecursiveHypernovaDeciderVerifier recursive_decider_verifier(recursive_verifier_transcript);
288 auto recursive_pairing_points =
289 recursive_decider_verifier.verify_proof(folded_verifier_accumulator, stdlib_proof);
290
291 // Natively verify pairing points
292 auto recursive_verified = recursive_pairing_points.check();
293
294 // The circuit is valid if and only if we have not tampered or we have tampered the folded accumulator
297 // Pairing point verification should pass as long as we have not tampered the folded accumulator or the
298 // accumulator
299 EXPECT_EQ(recursive_verified, mode != TamperingMode::FoldedAccumulator && mode != TamperingMode::Accumulator);
300 EXPECT_EQ(recursive_verified, native_verified);
301 // First sumcheck fails if the instance has been tampered with
302 EXPECT_EQ(first_sumcheck_recursive, mode != TamperingMode::Instance);
303 EXPECT_EQ(first_sumcheck_recursive, first_sumcheck_native);
304 // Second sumcheck fails if the accumulator has been tampered with
305 EXPECT_EQ(second_sumcheck_recursive, mode != TamperingMode::Accumulator);
306 EXPECT_EQ(second_sumcheck_recursive, second_sumcheck_native);
307
308 // Pin the decider transcript manifest (only check when not tampering)
309 if (mode == TamperingMode::None) {
310 auto expected_manifest = build_expected_decider_manifest();
311 auto verifier_manifest = native_transcript->get_manifest();
312 EXPECT_EQ(verifier_manifest, expected_manifest);
313 }
314 }
315};
316
318{
319 test_decider(TamperingMode::None);
320}
321
323{
325 test_decider(TamperingMode::Accumulator);
326}
327
329{
331 test_decider(TamperingMode::Instance);
332}
333
334TEST_F(HypernovaDeciderVerifierTests, TamperWithFoldedAccumulator)
335{
336 test_decider(TamperingMode::FoldedAccumulator);
337}
#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(const CommitmentKey &ck, 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:172
A ProverInstance is normally constructed from a finalized circuit and it contains all the information...
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
CommitmentKey< Curve > ck
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.