Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ultra_transcript.test.cpp
Go to the documentation of this file.
15
16#include "gtest/gtest.h"
17
18using namespace bb;
19
20#ifdef STARKNET_GARAGA_FLAVORS
21using FlavorTypes = ::testing::Types<UltraFlavor,
23 UltraStarknetFlavor,
24 UltraStarknetZKFlavor,
28#else
30 ::testing::Types<UltraFlavor, UltraKeccakFlavor, UltraRollupFlavor, UltraZKFlavor, UltraKeccakZKFlavor>;
31#endif
32template <typename Flavor> class UltraTranscriptTests : public ::testing::Test {
33 public:
35
37 using FF = Flavor::FF;
45
58 {
59 TranscriptManifest manifest_expected;
60
61 const size_t virtual_log_n = Flavor::USE_PADDING ? CONST_PROOF_SIZE_LOG_N : log_n;
62
63 size_t MAX_PARTIAL_RELATION_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH;
64 // Size of types is number of bb::frs needed to represent the types
65 // UltraKeccak uses uint256_t for commitments and frs, so we need to handle that differently.
66 size_t data_types_per_Frs = [] {
67 if constexpr (IsKeccakFlavor<Flavor>) {
68 return U256Codec::calc_num_fields<FF>();
69 } else {
70 return FrCodec::calc_num_fields<FF>();
71 }
72 }();
73 size_t data_types_per_G = [] {
74 if constexpr (IsKeccakFlavor<Flavor>) {
75 return U256Codec::calc_num_fields<Commitment>();
76 } else {
77 return FrCodec::calc_num_fields<Commitment>();
78 }
79 }();
80 size_t frs_per_uni = MAX_PARTIAL_RELATION_LENGTH * data_types_per_Frs;
81 size_t frs_per_evals = (Flavor::NUM_ALL_ENTITIES)*data_types_per_Frs;
82
83 size_t round = 0;
84 manifest_expected.add_entry(round, "vk_hash", data_types_per_Frs);
85
86 manifest_expected.add_entry(round, "public_input_0", data_types_per_Frs);
87 constexpr size_t PUBLIC_INPUTS_SIZE =
89 for (size_t i = 0; i < PUBLIC_INPUTS_SIZE; i++) {
90 manifest_expected.add_entry(round, "public_input_" + std::to_string(1 + i), data_types_per_Frs);
91 }
92
93 // For ZK flavors: Gemini masking polynomial commitment is sent at end of oink
94 if constexpr (Flavor::HasZK) {
95 manifest_expected.add_entry(round, "Gemini:masking_poly_comm", data_types_per_G);
96 }
97 manifest_expected.add_entry(round, "W_L", data_types_per_G);
98 manifest_expected.add_entry(round, "W_R", data_types_per_G);
99 manifest_expected.add_entry(round, "W_O", data_types_per_G);
100 manifest_expected.add_challenge(round, std::array{ "eta", "eta_two", "eta_three" });
101
102 round++;
103 manifest_expected.add_entry(round, "LOOKUP_READ_COUNTS", data_types_per_G);
104 manifest_expected.add_entry(round, "LOOKUP_READ_TAGS", data_types_per_G);
105 manifest_expected.add_entry(round, "W_4", data_types_per_G);
106 manifest_expected.add_challenge(round, std::array{ "beta", "gamma" });
107
108 round++;
109 manifest_expected.add_entry(round, "LOOKUP_INVERSES", data_types_per_G);
110 manifest_expected.add_entry(round, "Z_PERM", data_types_per_G);
111
112 manifest_expected.add_challenge(round, "alpha");
113 round++;
114
115 manifest_expected.add_challenge(round, "Sumcheck:gate_challenge");
116 round++;
117
118 if constexpr (Flavor::HasZK) {
119 manifest_expected.add_entry(round, "Libra:concatenation_commitment", data_types_per_G);
120 manifest_expected.add_entry(round, "Libra:Sum", data_types_per_Frs);
121 manifest_expected.add_challenge(round, "Libra:Challenge");
122 round++;
123 }
124
125 for (size_t i = 0; i < virtual_log_n; ++i) {
126 std::string idx = std::to_string(i);
127 manifest_expected.add_entry(round, "Sumcheck:univariate_" + idx, frs_per_uni);
128 std::string label = "Sumcheck:u_" + idx;
129 manifest_expected.add_challenge(round, label);
130 round++;
131 }
132
133 manifest_expected.add_entry(round, "Sumcheck:evaluations", frs_per_evals);
134
135 if constexpr (Flavor::HasZK) {
136 manifest_expected.add_entry(round, "Libra:claimed_evaluation", data_types_per_Frs);
137 manifest_expected.add_entry(round, "Libra:grand_sum_commitment", data_types_per_G);
138 manifest_expected.add_entry(round, "Libra:quotient_commitment", data_types_per_G);
139 }
140
141 manifest_expected.add_challenge(round, "rho");
142
143 round++;
144 for (size_t i = 1; i < virtual_log_n; ++i) {
145 std::string idx = std::to_string(i);
146 manifest_expected.add_entry(round, "Gemini:FOLD_" + idx, data_types_per_G);
147 }
148 manifest_expected.add_challenge(round, "Gemini:r");
149 round++;
150 for (size_t i = 1; i <= virtual_log_n; ++i) {
151 std::string idx = std::to_string(i);
152 manifest_expected.add_entry(round, "Gemini:a_" + idx, data_types_per_Frs);
153 }
154
155 if constexpr (Flavor::HasZK) {
156 manifest_expected.add_entry(round, "Libra:concatenation_eval", data_types_per_Frs);
157 manifest_expected.add_entry(round, "Libra:shifted_grand_sum_eval", data_types_per_Frs);
158 manifest_expected.add_entry(round, "Libra:grand_sum_eval", data_types_per_Frs);
159 manifest_expected.add_entry(round, "Libra:quotient_eval", data_types_per_Frs);
160 }
161
162 manifest_expected.add_challenge(round, "Shplonk:nu");
163 round++;
164 manifest_expected.add_entry(round, "Shplonk:Q", data_types_per_G);
165 manifest_expected.add_challenge(round, "Shplonk:z");
166
167 round++;
168 manifest_expected.add_entry(round, "KZG:W", data_types_per_G);
169 manifest_expected.add_challenge(round, "KZG:masking_challenge");
170
171 return manifest_expected;
172 }
173
175 {
176 FF a = 1;
177 builder.add_variable(a);
178 builder.add_public_variable(a);
179 if constexpr (HasIPAAccumulator<Flavor>) {
181 } else {
183 }
184 }
185
187 {
188 auto a = FF::random_element();
189 auto b = FF::random_element();
190 builder.add_variable(a);
191 builder.add_public_variable(a);
192 builder.add_public_variable(b);
193
194 if constexpr (HasIPAAccumulator<Flavor>) {
195 auto [stdlib_opening_claim, ipa_proof] =
196 IPA<stdlib::grumpkin<Builder>>::create_random_valid_ipa_claim_and_proof(builder);
197 stdlib_opening_claim.set_public();
198 builder.ipa_proof = ipa_proof;
199 }
200 }
201
202 Proof export_serialized_proof(Prover& prover, const size_t num_public_inputs)
203 {
204 // reset internal variables needed for exporting the proof
205 // Note: compute_proof_length_for_export excludes IPA proof length since export_proof appends it separately
206 size_t proof_length = compute_proof_length_for_export<Flavor>(num_public_inputs);
207 prover.transcript->test_set_proof_parsing_state(0, proof_length);
208 return prover.export_proof();
209 }
210};
211
213
218TYPED_TEST(UltraTranscriptTests, ProverManifestConsistency)
219{
220 // Construct a simple circuit of size n = 8 (i.e. the minimum circuit size)
221 auto builder = typename TestFixture::Builder();
222 TestFixture::generate_test_circuit(builder);
223
224 // Automatically generate a transcript manifest by constructing a proof
226 auto verification_key = std::make_shared<typename TestFixture::VerificationKey>(prover_instance->get_precomputed());
227 typename TestFixture::Prover prover(prover_instance, verification_key);
228 prover.transcript->enable_manifest();
229 auto proof = prover.construct_proof();
230
231 // Check that the prover generated manifest agrees with the manifest hard coded in this suite
232 auto manifest_expected = TestFixture::construct_ultra_honk_manifest(prover.prover_instance->log_dyadic_size());
233 auto prover_manifest = prover.transcript->get_manifest();
234 // Note: a manifest can be printed using manifest.print()
235 manifest_expected.print();
236 prover_manifest.print();
237 ASSERT_GT(manifest_expected.size(), 0);
238 for (size_t round = 0; round < manifest_expected.size(); ++round) {
239 if (prover_manifest[round] != manifest_expected[round]) {
240 info("Prover manifest discrepency in round ", round);
241 info("Prover manifest:");
242 prover_manifest[round].print();
243 info("Expected manifest:");
244 manifest_expected[round].print();
245 FAIL();
246 }
247 }
248}
249
255TYPED_TEST(UltraTranscriptTests, VerifierManifestConsistency)
256{
257 // Construct a simple circuit of size n = 8 (i.e. the minimum circuit size)
258 auto builder = typename TestFixture::Builder();
259 TestFixture::generate_test_circuit(builder);
260
261 // Automatically generate a transcript manifest in the prover by constructing a proof
263 auto verification_key = std::make_shared<typename TestFixture::VerificationKey>(prover_instance->get_precomputed());
264 auto vk_and_hash = std::make_shared<typename TypeParam::VKAndHash>(verification_key);
265 typename TestFixture::Prover prover(prover_instance, verification_key);
266 prover.transcript->enable_manifest();
267 auto proof = prover.construct_proof();
268
269 // Automatically generate a transcript manifest in the verifier by verifying a proof
270 auto verifier_transcript = std::make_shared<typename TypeParam::Transcript>();
271 verifier_transcript->enable_manifest();
272 typename TestFixture::Verifier verifier(vk_and_hash, verifier_transcript);
273 [[maybe_unused]] auto _ = verifier.verify_proof(proof);
274
275 // Check consistency between the manifests generated by the prover and verifier
276 auto prover_manifest = prover.transcript->get_manifest();
277 auto verifier_manifest = verifier.get_transcript()->get_manifest();
278
279 // Note: a manifest can be printed using manifest.print()
280 ASSERT_GT(prover_manifest.size(), 0);
281 for (size_t round = 0; round < prover_manifest.size(); ++round) {
282 ASSERT_EQ(prover_manifest[round], verifier_manifest[round])
283 << "Prover/Verifier manifest discrepency in round " << round;
284 }
285}
286
292TYPED_TEST(UltraTranscriptTests, ChallengeGenerationTest)
293{
294 using Flavor = TypeParam;
295 using FF = Flavor::FF;
296 // initialized with random value sent to verifier
297 auto transcript = TypeParam::Transcript::prover_init_empty();
298 // test a bunch of challenges
299 std::vector<std::string> challenge_labels{ "a", "b", "c", "d", "e", "f" };
300 auto challenges = transcript->template get_challenges<FF>(challenge_labels);
301 // check they are not 0
302 for (size_t i = 0; i < challenges.size(); ++i) {
303 ASSERT_NE(challenges[i], 0) << "Challenge " << i << " is 0";
304 }
305 constexpr uint32_t random_val{ 17 }; // arbitrary
306 transcript->send_to_verifier("random val", random_val);
307 // test more challenges
308 challenge_labels = { "a", "b", "c" };
309 challenges = transcript->template get_challenges<FF>(challenge_labels);
310 ASSERT_NE(challenges[0], 0) << "Challenge a is 0";
311 ASSERT_NE(challenges[1], 0) << "Challenge b is 0";
312 ASSERT_NE(challenges[2], 0) << "Challenge c is 0";
313}
314
316{
317 using Flavor = TypeParam;
318 using FF = Flavor::FF;
319 using Commitment = Flavor::Commitment;
320 // Construct a simple circuit of size n = 8 (i.e. the minimum circuit size)
321 auto builder = typename TestFixture::Builder();
322 TestFixture::generate_test_circuit(builder);
323
324 // Automatically generate a transcript manifest by constructing a proof
326 auto verification_key = std::make_shared<typename TestFixture::VerificationKey>(prover_instance->get_precomputed());
327 auto vk_and_hash = std::make_shared<typename TypeParam::VKAndHash>(verification_key);
328 typename TestFixture::Prover prover(prover_instance, verification_key);
329 auto proof = prover.construct_proof();
330 typename TestFixture::Verifier verifier(vk_and_hash);
331 EXPECT_TRUE(verifier.verify_proof(proof).result);
332
333 const size_t virtual_log_n = Flavor::USE_PADDING ? CONST_PROOF_SIZE_LOG_N : prover_instance->log_dyadic_size();
334
335 // Use StructuredProof test utility to deserialize/serialize proof data
336 StructuredProof<Flavor> proof_structure;
337
338 // try deserializing and serializing with no changes and check proof is still valid
339 proof_structure.deserialize(
340 prover.transcript->test_get_proof_data(), verification_key->num_public_inputs, virtual_log_n);
341 proof_structure.serialize(prover.transcript->test_get_proof_data(), virtual_log_n);
342
343 proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs());
344 // we have changed nothing so proof is still valid
345 typename TestFixture::Verifier verifier2(vk_and_hash);
346 EXPECT_TRUE(verifier2.verify_proof(proof).result);
347
348 Commitment one_group_val = Commitment::one();
349 FF rand_val = FF::random_element();
350 proof_structure.z_perm_comm = one_group_val * rand_val; // choose random object to modify
351 proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs());
352 // we have not serialized it back to the proof so it should still be fine
353 typename TestFixture::Verifier verifier3(vk_and_hash);
354 EXPECT_TRUE(verifier3.verify_proof(proof).result);
355
356 proof_structure.serialize(prover.transcript->test_get_proof_data(), virtual_log_n);
357 proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs());
358 // the proof is now wrong after serializing it
359 typename TestFixture::Verifier verifier4(vk_and_hash);
360 EXPECT_FALSE(verifier4.verify_proof(proof).result);
361
362 proof_structure.deserialize(
363 prover.transcript->test_get_proof_data(), verification_key->num_public_inputs, virtual_log_n);
364 EXPECT_EQ(static_cast<Commitment>(proof_structure.z_perm_comm), one_group_val * rand_val);
365}
void generate_random_test_circuit(Builder &builder)
Flavor::Commitment Commitment
Proof export_serialized_proof(Prover &prover, const size_t num_public_inputs)
typename Flavor::Transcript::Proof Proof
TranscriptManifest construct_ultra_honk_manifest(const size_t &log_n)
Construct a manifest for a Ultra Honk proof.
void generate_test_circuit(Builder &builder)
std::conditional_t< HasIPAAccumulator< Flavor >, RollupIO, DefaultIO > IO
Manages the data that is propagated on the public inputs of an application/function circuit.
static constexpr size_t PUBLIC_INPUTS_SIZE
The verification key is responsible for storing the commitments to the precomputed (non-witnessk) pol...
static constexpr bool HasZK
typename Curve::ScalarField FF
static constexpr size_t NUM_ALL_ENTITIES
ECCVMCircuitBuilder CircuitBuilder
typename G1::affine_element Commitment
static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH
static constexpr bool USE_PADDING
IPA (inner product argument) commitment scheme class.
Definition ipa.hpp:93
A ProverInstance is normally constructed from a finalized circuit and it contains all the information...
The data that is propagated on the public inputs of a rollup circuit.
static constexpr size_t PUBLIC_INPUTS_SIZE
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.
std::shared_ptr< Transcript > transcript
UltraRollupFlavor extends UltraFlavor with IPA proof support.
Child class of UltraFlavor that runs with ZK Sumcheck.
static void add_default(Builder &builder)
Add default public inputs when they are not present.
static void add_default(Builder &builder)
Add default public inputs when they are not present.
void info(Args... args)
Definition log.hpp:89
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
Base class templates for structures that contain data parameterized by the fundamental polynomials of...
testing::Types< MegaFlavor, UltraFlavor, UltraZKFlavor, UltraRollupFlavor > 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(ShpleminiTest, TestSettings)
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
Test utility for deserializing/serializing proof data into typed structures.
static field random_element(numeric::RNG *engine=nullptr) noexcept