Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
honk_transcript.test.cpp
Go to the documentation of this file.
16
17#include <gtest/gtest.h>
18
19using namespace bb;
20
21#ifdef STARKNET_GARAGA_FLAVORS
22using FlavorTypes = ::testing::Types<UltraFlavor,
24 UltraStarknetFlavor,
25 UltraStarknetZKFlavor,
30#else
32 ::testing::Types<UltraFlavor, UltraKeccakFlavor, UltraZKFlavor, UltraKeccakZKFlavor, MegaFlavor, MegaZKFlavor>;
33#endif
34
35template <typename Flavor> class HonkTranscriptTests : public ::testing::Test {
36 public:
38
40 using FF = Flavor::FF;
45 using IO = DefaultIO;
48
61 {
62 TranscriptManifest manifest_expected;
63
64 const size_t virtual_log_n = Flavor::USE_PADDING ? Flavor::VIRTUAL_LOG_N : log_n;
65
66 size_t MAX_PARTIAL_RELATION_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH;
67 // Size of types is number of bb::frs needed to represent the types
68 // UltraKeccak uses uint256_t for commitments and frs, so we need to handle that differently.
69 size_t data_types_per_Frs = [] {
70 if constexpr (IsKeccakFlavor<Flavor>) {
71 return U256Codec::calc_num_fields<FF>();
72 } else {
73 return FrCodec::calc_num_fields<FF>();
74 }
75 }();
76 size_t data_types_per_G = [] {
77 if constexpr (IsKeccakFlavor<Flavor>) {
78 return U256Codec::calc_num_fields<Commitment>();
79 } else {
80 return FrCodec::calc_num_fields<Commitment>();
81 }
82 }();
83 size_t frs_per_uni = MAX_PARTIAL_RELATION_LENGTH * data_types_per_Frs;
84 size_t frs_per_evals = (Flavor::NUM_ALL_ENTITIES)*data_types_per_Frs;
85
86 size_t round = 0;
87 manifest_expected.add_entry(round, "vk_hash", data_types_per_Frs);
88
89 manifest_expected.add_entry(round, "public_input_0", data_types_per_Frs);
90 constexpr size_t PUBLIC_INPUTS_SIZE = IO::PUBLIC_INPUTS_SIZE;
91 for (size_t i = 0; i < PUBLIC_INPUTS_SIZE; i++) {
92 manifest_expected.add_entry(round, "public_input_" + std::to_string(1 + i), data_types_per_Frs);
93 }
94
95 // For flavors with Gemini masking: masking polynomial commitment is sent at end of oink
96 if constexpr (flavor_has_gemini_masking<Flavor>()) {
97 manifest_expected.add_entry(round, "Gemini:masking_poly_comm", data_types_per_G);
98 }
99 manifest_expected.add_entry(round, "W_L", data_types_per_G);
100 manifest_expected.add_entry(round, "W_R", data_types_per_G);
101 manifest_expected.add_entry(round, "W_O", data_types_per_G);
102
103 // Mega-specific witness commitments: ECC op wires and databus polynomials
104 if constexpr (IsMegaFlavor<Flavor>) {
105 manifest_expected.add_entry(round, "ECC_OP_WIRE_1", data_types_per_G);
106 manifest_expected.add_entry(round, "ECC_OP_WIRE_2", data_types_per_G);
107 manifest_expected.add_entry(round, "ECC_OP_WIRE_3", data_types_per_G);
108 manifest_expected.add_entry(round, "ECC_OP_WIRE_4", data_types_per_G);
109 manifest_expected.add_entry(round, "KERNEL_CALLDATA", data_types_per_G);
110 manifest_expected.add_entry(round, "KERNEL_CALLDATA_READ_COUNTS", data_types_per_G);
111 manifest_expected.add_entry(round, "FIRST_APP_CALLDATA", data_types_per_G);
112 manifest_expected.add_entry(round, "FIRST_APP_CALLDATA_READ_COUNTS", data_types_per_G);
113 manifest_expected.add_entry(round, "SECOND_APP_CALLDATA", data_types_per_G);
114 manifest_expected.add_entry(round, "SECOND_APP_CALLDATA_READ_COUNTS", data_types_per_G);
115 manifest_expected.add_entry(round, "THIRD_APP_CALLDATA", data_types_per_G);
116 manifest_expected.add_entry(round, "THIRD_APP_CALLDATA_READ_COUNTS", data_types_per_G);
117 manifest_expected.add_entry(round, "RETURN_DATA", data_types_per_G);
118 manifest_expected.add_entry(round, "RETURN_DATA_READ_COUNTS", data_types_per_G);
119 }
120
121 manifest_expected.add_challenge(round, "eta");
122
123 round++;
124 manifest_expected.add_entry(round, "LOOKUP_READ_COUNTS", data_types_per_G);
125 manifest_expected.add_entry(round, "LOOKUP_READ_TAGS", data_types_per_G);
126 manifest_expected.add_entry(round, "W_4", data_types_per_G);
127 manifest_expected.add_challenge(round, std::array{ "beta", "gamma" });
128
129 round++;
130 manifest_expected.add_entry(round, "LOOKUP_INVERSES", data_types_per_G);
131 // Mega-specific databus inverse commitments
132 if constexpr (IsMegaFlavor<Flavor>) {
133 manifest_expected.add_entry(round, "KERNEL_CALLDATA_INVERSES", data_types_per_G);
134 manifest_expected.add_entry(round, "FIRST_APP_CALLDATA_INVERSES", data_types_per_G);
135 manifest_expected.add_entry(round, "SECOND_APP_CALLDATA_INVERSES", data_types_per_G);
136 manifest_expected.add_entry(round, "THIRD_APP_CALLDATA_INVERSES", data_types_per_G);
137 manifest_expected.add_entry(round, "RETURN_DATA_INVERSES", data_types_per_G);
138 }
139 manifest_expected.add_entry(round, "Z_PERM", data_types_per_G);
140
141 manifest_expected.add_challenge(round, "alpha");
142 manifest_expected.add_challenge(round, "Sumcheck:gate_challenge");
143 round++;
144
145 if constexpr (Flavor::HasZK) {
146 manifest_expected.add_entry(round, "Libra:concatenation_commitment", data_types_per_G);
147 manifest_expected.add_entry(round, "Libra:Sum", data_types_per_Frs);
148 manifest_expected.add_challenge(round, "Libra:Challenge");
149 round++;
150 }
151
152 for (size_t i = 0; i < virtual_log_n; ++i) {
153 std::string idx = std::to_string(i);
154 manifest_expected.add_entry(round, "Sumcheck:univariate_" + idx, frs_per_uni);
155 std::string label = "Sumcheck:u_" + idx;
156 manifest_expected.add_challenge(round, label);
157 round++;
158 }
159
160 manifest_expected.add_entry(round, "Sumcheck:evaluations", frs_per_evals);
161
162 if constexpr (Flavor::HasZK) {
163 manifest_expected.add_entry(round, "Libra:claimed_evaluation", data_types_per_Frs);
164 manifest_expected.add_entry(round, "Libra:grand_sum_commitment", data_types_per_G);
165 manifest_expected.add_entry(round, "Libra:quotient_commitment", data_types_per_G);
166 }
167
168 manifest_expected.add_challenge(round, "rho");
169
170 round++;
171 for (size_t i = 1; i < virtual_log_n; ++i) {
172 std::string idx = std::to_string(i);
173 manifest_expected.add_entry(round, "Gemini:FOLD_" + idx, data_types_per_G);
174 }
175 manifest_expected.add_challenge(round, "Gemini:r");
176 round++;
177 for (size_t i = 1; i <= virtual_log_n; ++i) {
178 std::string idx = std::to_string(i);
179 manifest_expected.add_entry(round, "Gemini:a_" + idx, data_types_per_Frs);
180 }
181
182 if constexpr (Flavor::HasZK) {
183 manifest_expected.add_entry(round, "Libra:concatenation_eval", data_types_per_Frs);
184 manifest_expected.add_entry(round, "Libra:shifted_grand_sum_eval", data_types_per_Frs);
185 manifest_expected.add_entry(round, "Libra:grand_sum_eval", data_types_per_Frs);
186 manifest_expected.add_entry(round, "Libra:quotient_eval", data_types_per_Frs);
187 }
188
189 manifest_expected.add_challenge(round, "Shplonk:nu");
190 round++;
191 manifest_expected.add_entry(round, "Shplonk:Q", data_types_per_G);
192 manifest_expected.add_challenge(round, "Shplonk:z");
193
194 round++;
195 manifest_expected.add_entry(round, "KZG:W", data_types_per_G);
196
197 return manifest_expected;
198 }
199
201 {
202 FF a = 1;
203 builder.add_variable(a);
204 builder.add_public_variable(a);
206 }
207
208 Proof export_serialized_proof(Prover& prover, const size_t num_public_inputs, const size_t log_n)
209 {
210 // reset internal variables needed for exporting the proof
211 // Note: this excludes IPA proof length since export_proof appends it separately
212 size_t proof_length = ProofLength::Honk<Flavor>::LENGTH_WITHOUT_PUB_INPUTS(log_n) + num_public_inputs;
213 prover.get_transcript()->test_set_proof_parsing_state(0, proof_length);
214 return prover.export_proof();
215 }
216};
217
219
224TYPED_TEST(HonkTranscriptTests, ProverManifestConsistency)
225{
226 // Construct a simple circuit of size n = 8 (i.e. the minimum circuit size)
227 auto builder = typename TestFixture::Builder();
228 TestFixture::generate_test_circuit(builder);
229
230 // Automatically generate a transcript manifest by constructing a proof
232 auto verification_key = std::make_shared<typename TestFixture::VerificationKey>(prover_instance->get_precomputed());
233 typename TestFixture::Prover prover(prover_instance, verification_key);
234 prover.get_transcript()->enable_manifest();
235 auto proof = prover.construct_proof();
236
237 // Check that the prover generated manifest agrees with the manifest hard coded in this suite
238 auto manifest_expected = TestFixture::construct_honk_manifest(prover.log_dyadic_size());
239 auto prover_manifest = prover.get_transcript()->get_manifest();
240 // Note: a manifest can be printed using manifest.print()
241 manifest_expected.print();
242 prover_manifest.print();
243 ASSERT_GT(manifest_expected.size(), 0);
244 for (size_t round = 0; round < manifest_expected.size(); ++round) {
245 if (prover_manifest[round] != manifest_expected[round]) {
246 info("Prover manifest discrepency in round ", round);
247 info("Prover manifest:");
248 prover_manifest[round].print();
249 info("Expected manifest:");
250 manifest_expected[round].print();
251 FAIL();
252 }
253 }
254}
255
260TYPED_TEST(HonkTranscriptTests, VerifierManifestConsistency)
261{
262 // Construct a simple circuit of size n = 8 (i.e. the minimum circuit size)
263 auto builder = typename TestFixture::Builder();
264 TestFixture::generate_test_circuit(builder);
265
266 // Automatically generate a transcript manifest in the prover by constructing a proof
268 auto verification_key = std::make_shared<typename TestFixture::VerificationKey>(prover_instance->get_precomputed());
269 auto vk_and_hash = std::make_shared<typename TypeParam::VKAndHash>(verification_key);
270 typename TestFixture::Prover prover(prover_instance, verification_key);
271 prover.get_transcript()->enable_manifest();
272 auto proof = prover.construct_proof();
273
274 // Automatically generate a transcript manifest in the verifier by verifying a proof
275 auto verifier_transcript = std::make_shared<typename TypeParam::Transcript>();
276 verifier_transcript->enable_manifest();
277 typename TestFixture::Verifier verifier(vk_and_hash, verifier_transcript);
278 [[maybe_unused]] auto _ = verifier.verify_proof(proof);
279
280 // Check consistency between the manifests generated by the prover and verifier
281 auto prover_manifest = prover.get_transcript()->get_manifest();
282 auto verifier_manifest = verifier.get_transcript()->get_manifest();
283
284 // Note: a manifest can be printed using manifest.print()
285 ASSERT_GT(prover_manifest.size(), 0);
286 for (size_t round = 0; round < prover_manifest.size(); ++round) {
287 ASSERT_EQ(prover_manifest[round], verifier_manifest[round])
288 << "Prover/Verifier manifest discrepency in round " << round;
289 }
290}
291
296TYPED_TEST(HonkTranscriptTests, ChallengeGenerationTest)
297{
298 using Flavor = TypeParam;
299 using FF = Flavor::FF;
300 // initialized with random value sent to verifier
301 auto transcript = TypeParam::Transcript::test_prover_init_empty();
302 // test a bunch of challenges
303 std::vector<std::string> challenge_labels{ "a", "b", "c", "d", "e", "f" };
304 auto challenges = transcript->template get_challenges<FF>(challenge_labels);
305 // check they are not 0
306 for (size_t i = 0; i < challenges.size(); ++i) {
307 ASSERT_NE(challenges[i], 0) << "Challenge " << i << " is 0";
308 }
309 constexpr uint32_t random_val{ 17 }; // arbitrary
310 transcript->send_to_verifier("random val", random_val);
311 // test more challenges
312 challenge_labels = { "a", "b", "c" };
313 challenges = transcript->template get_challenges<FF>(challenge_labels);
314 ASSERT_NE(challenges[0], 0) << "Challenge a is 0";
315 ASSERT_NE(challenges[1], 0) << "Challenge b is 0";
316 ASSERT_NE(challenges[2], 0) << "Challenge c is 0";
317}
318
320{
321 using Flavor = TypeParam;
322 using FF = Flavor::FF;
323 using Commitment = Flavor::Commitment;
324 // Construct a simple circuit of size n = 8 (i.e. the minimum circuit size)
325 auto builder = typename TestFixture::Builder();
326 TestFixture::generate_test_circuit(builder);
327
328 // Automatically generate a transcript manifest by constructing a proof
330 auto verification_key = std::make_shared<typename TestFixture::VerificationKey>(prover_instance->get_precomputed());
331 auto vk_and_hash = std::make_shared<typename TypeParam::VKAndHash>(verification_key);
332 typename TestFixture::Prover prover(prover_instance, verification_key);
333 auto proof = prover.construct_proof();
334 typename TestFixture::Verifier verifier(vk_and_hash);
335 EXPECT_TRUE(verifier.verify_proof(proof).result);
336
337 const size_t virtual_log_n = Flavor::USE_PADDING ? Flavor::VIRTUAL_LOG_N : prover_instance->log_dyadic_size();
338
339 // Use StructuredProof test utility to deserialize/serialize proof data
340 StructuredProof<Flavor> proof_structure;
341
342 // try deserializing and serializing with no changes and check proof is still valid
343 proof_structure.deserialize(
344 prover.get_transcript()->test_get_proof_data(), verification_key->num_public_inputs, virtual_log_n);
345 proof_structure.serialize(prover.get_transcript()->test_get_proof_data(), virtual_log_n);
346
347 proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs(), virtual_log_n);
348 // we have changed nothing so proof is still valid
349 typename TestFixture::Verifier verifier2(vk_and_hash);
350 EXPECT_TRUE(verifier2.verify_proof(proof).result);
351
352 Commitment one_group_val = Commitment::one();
353 FF rand_val = FF::random_element();
354 proof_structure.z_perm_comm = one_group_val * rand_val; // choose random object to modify
355 proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs(), virtual_log_n);
356 // we have not serialized it back to the proof so it should still be fine
357 typename TestFixture::Verifier verifier3(vk_and_hash);
358 EXPECT_TRUE(verifier3.verify_proof(proof).result);
359
360 proof_structure.serialize(prover.get_transcript()->test_get_proof_data(), virtual_log_n);
361 proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs(), virtual_log_n);
362 // the proof is now wrong after serializing it
363 typename TestFixture::Verifier verifier4(vk_and_hash);
364 EXPECT_FALSE(verifier4.verify_proof(proof).result);
365
366 proof_structure.deserialize(
367 prover.get_transcript()->test_get_proof_data(), verification_key->num_public_inputs, virtual_log_n);
368 EXPECT_EQ(static_cast<Commitment>(proof_structure.z_perm_comm), one_group_val * rand_val);
369}
typename Flavor::Transcript::Proof Proof
TranscriptManifest construct_honk_manifest(const size_t &log_n)
Construct a manifest for a Honk proof (Ultra or Mega)
void generate_test_circuit(Builder &builder)
Proof export_serialized_proof(Prover &prover, const size_t num_public_inputs, const size_t log_n)
Flavor::Commitment Commitment
Manages the data that is propagated on the public inputs of an application/function circuit.
static constexpr size_t PUBLIC_INPUTS_SIZE
static void add_default(Builder &builder)
Add default IO values to a circuit builder (for native tests)
static constexpr bool HasZK
typename Curve::ScalarField FF
static constexpr size_t NUM_ALL_ENTITIES
ECCVMCircuitBuilder CircuitBuilder
typename G1::affine_element Commitment
FixedVKAndHash_< PrecomputedEntities< Commitment >, BF, ECCVMHardcodedVKAndHash > VerificationKey
The verification key stores commitments to the precomputed polynomials used by the verifier.
static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH
static constexpr bool USE_PADDING
Simple verification key class for fixed-size circuits (ECCVM, Translator, AVM).
Definition flavor.hpp:101
Child class of MegaFlavor that runs with ZK Sumcheck.
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.
const std::shared_ptr< Transcript > & get_transcript() const
Proof export_proof()
Export the complete proof, including IPA proof for rollup circuits.
Child class of UltraFlavor that runs with ZK Sumcheck.
#define info(...)
Definition log.hpp:93
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
Base class templates shared across Honk flavors.
testing::Types< UltraFlavor, UltraKeccakFlavor, MegaFlavor > FlavorTypes
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
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
static constexpr size_t LENGTH_WITHOUT_PUB_INPUTS(size_t log_n)
Test utility for deserializing/serializing proof data into typed structures.
static field random_element(numeric::RNG *engine=nullptr) noexcept