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 }
66 info("Mismatch in the unshifted commitments");
67 return false;
68 }
70 info("Mismatch in the shifted commitments");
71 return false;
72 }
74 info("Mismatch in the unshifted evaluations");
75 return false;
76 }
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 the instance by changing w_l. This should invalidate the first sumcheck
151 instance->polynomials.w_l.at(1) = NativeFF::random_element();
152 break;
153 }
154 };
155
166 {
167 TranscriptManifest manifest;
168 constexpr size_t frs_per_G = FrCodec::calc_num_fields<curve::BN254::AffineElement>();
169 constexpr size_t NUM_SUMCHECK_UNIVARIATES = NativeFlavor::VIRTUAL_LOG_N; // 21
170
171 // Round 0: Oink preamble + wires + ECC ops + databus
172 manifest.add_entry(0, "vk_hash", 1);
173 for (size_t i = 0; i < 4; ++i) {
174 manifest.add_entry(0, "public_input_" + std::to_string(i), 1);
175 }
176 for (const auto& wire : { "W_L", "W_R", "W_O" }) {
177 manifest.add_entry(0, wire, frs_per_G);
178 }
179 for (const auto& wire : { "ECC_OP_WIRE_1", "ECC_OP_WIRE_2", "ECC_OP_WIRE_3", "ECC_OP_WIRE_4" }) {
180 manifest.add_entry(0, wire, frs_per_G);
181 }
182 for (const auto& bus : { "CALLDATA", "SECONDARY_CALLDATA", "RETURN_DATA" }) {
183 manifest.add_entry(0, bus, frs_per_G);
184 manifest.add_entry(0, std::string(bus) + "_READ_COUNTS", frs_per_G);
185 manifest.add_entry(0, std::string(bus) + "_READ_TAGS", frs_per_G);
186 }
187 manifest.add_challenge(0, std::array{ "eta", "eta_two", "eta_three" });
188
189 // Round 1: lookup + w_4
190 manifest.add_entry(1, "LOOKUP_READ_COUNTS", frs_per_G);
191 manifest.add_entry(1, "LOOKUP_READ_TAGS", frs_per_G);
192 manifest.add_entry(1, "W_4", frs_per_G);
193 manifest.add_challenge(1, std::array{ "beta", "gamma" });
194
195 // Round 2: inverses + z_perm
196 manifest.add_entry(2, "LOOKUP_INVERSES", frs_per_G);
197 manifest.add_entry(2, "CALLDATA_INVERSES", frs_per_G);
198 manifest.add_entry(2, "SECONDARY_CALLDATA_INVERSES", frs_per_G);
199 manifest.add_entry(2, "RETURN_DATA_INVERSES", frs_per_G);
200 manifest.add_entry(2, "Z_PERM", frs_per_G);
201 manifest.add_challenge(2, "alpha");
202
203 // Round 3: gate challenge
204 manifest.add_challenge(3, "HypernovaFoldingProver:gate_challenge");
205
206 // Rounds 4-24: main sumcheck univariates
207 for (size_t i = 0; i < NUM_SUMCHECK_UNIVARIATES; ++i) {
208 manifest.add_entry(4 + i, "Sumcheck:univariate_" + std::to_string(i), 8);
209 manifest.add_challenge(4 + i, "Sumcheck:u_" + std::to_string(i));
210 }
211
212 // Round 25: evaluations + unshifted batching challenges
213 manifest.add_entry(25, "Sumcheck:evaluations", 60);
214 for (size_t i = 0; i < 55; ++i) {
215 manifest.add_challenge(25, "unshifted_challenge_" + std::to_string(i));
216 }
217
218 // Round 26: shifted batching challenges
219 for (size_t i = 0; i < 5; ++i) {
220 manifest.add_challenge(26, "shifted_challenge_" + std::to_string(i));
221 }
222
223 // Round 27: MLB accumulator data
224 manifest.add_entry(27, "non_shifted_accumulator_commitment", frs_per_G);
225 manifest.add_entry(27, "shifted_accumulator_commitment", frs_per_G);
226 for (size_t i = 0; i < NUM_SUMCHECK_UNIVARIATES; ++i) {
227 manifest.add_entry(27, "accumulator_challenge_" + std::to_string(i), 1);
228 }
229 manifest.add_entry(27, "accumulator_evaluation_0", 1);
230 manifest.add_entry(27, "accumulator_evaluation_1", 1);
231 manifest.add_challenge(27, "Sumcheck:alpha");
232
233 // Rounds 28-48: MLB sumcheck univariates
234 for (size_t i = 0; i < NUM_SUMCHECK_UNIVARIATES; ++i) {
235 manifest.add_entry(28 + i, "Sumcheck:univariate_" + std::to_string(i), 4);
236 manifest.add_challenge(28 + i, "Sumcheck:u_" + std::to_string(i));
237 }
238
239 // Round 49: final evaluations + claim_batching_challenge
240 manifest.add_entry(49, "Sumcheck:evaluations", 6);
241 manifest.add_challenge(49, "claim_batching_challenge");
242
243 return manifest;
244 }
245
246 static void test_folding(const TamperingMode& mode)
247 {
248 // Generate accumulator
250 auto transcript = std::make_shared<NativeTranscript>();
251
252 bb::HypernovaFoldingProver prover(transcript);
253 auto accumulator = prover.instance_to_accumulator(instance);
254
255 // Folding
256 auto incoming_instance = generate_new_instance(5);
257 tampering(incoming_instance, mode);
258 auto incoming_vk = std::make_shared<NativeVerificationKey>(incoming_instance->get_precomputed());
259 auto incoming_verifier_instance =
261
262 auto folding_transcript = std::make_shared<NativeTranscript>();
263 HypernovaFoldingProver folding_prover(folding_transcript);
264 auto [folding_proof, folded_accumulator] = folding_prover.fold(accumulator, incoming_instance);
265
266 // Natively verify the folding (with manifest tracking)
267 auto native_verifier_transcript = std::make_shared<NativeTranscript>();
268 native_verifier_transcript->enable_manifest();
269 NativeHypernovaVerifier native_verifier(native_verifier_transcript);
270 auto [first_sumcheck_native, second_sumcheck_native, folded_verifier_accumulator_native] =
271 native_verifier.verify_folding_proof(incoming_verifier_instance, folding_proof);
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 // If the instance has been tampered with, then the first sumcheck should fail (hence the circuit is not
284 // satisfied), but the second should pass
286 EXPECT_EQ(first_sumcheck_recursive, mode == TamperingMode::None);
287 EXPECT_EQ(first_sumcheck_recursive, first_sumcheck_native);
288 EXPECT_TRUE(second_sumcheck_recursive);
289 EXPECT_EQ(second_sumcheck_recursive, second_sumcheck_native);
291 folded_accumulator, folded_verifier_accumulator.get_value<NativeVerifierAccumulator>()));
292
293 // Pin the folding transcript manifest (only check when not tampering)
294 if (mode == TamperingMode::None) {
295 auto expected_manifest = build_expected_folding_manifest();
296 auto verifier_manifest = native_verifier_transcript->get_manifest();
297 EXPECT_EQ(verifier_manifest, expected_manifest);
298 }
299 }
300};
301
303{
304 test_folding(TamperingMode::None);
305}
306
308{
310 test_folding(TamperingMode::Instance);
311}
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:33
std::shared_ptr< Napi::ThreadSafeFunction > instance
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
RecursiveHypernovaVerifier::VerifierInstance RecursiveVerifierInstance
static TranscriptManifest build_expected_folding_manifest()
Build the expected transcript manifest for HyperNova folding.
static void test_folding(const TamperingMode &mode)
NativeHypernovaVerifier::VerifierInstance NativeVerifierInstance
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
std::pair< HonkProof, Accumulator > fold(const Accumulator &accumulator, const std::shared_ptr< ProverInstance > &instance, const std::shared_ptr< VerificationKey > &honk_vk=nullptr)
Fold an instance into an accumulator. Folding happens in place.
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.
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 >, typename HypernovaRecursiveTypes::Proof, typename HypernovaNativeTypes::Proof > Proof
std::conditional_t< IsRecursiveFlavor< Flavor >, typename HypernovaRecursiveTypes::VerifierInstance, typename HypernovaNativeTypes::VerifierInstance > VerifierInstance
MultilinearBatchingVerifierClaim< Curve > Accumulator
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:141
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.
void info(Args... args)
Definition log.hpp:89
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:185
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)