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.
14
15#include "gtest/gtest.h"
16
17using namespace bb;
18
19#ifdef STARKNET_GARAGA_FLAVORS
20using FlavorTypes = ::testing::Types<UltraFlavor,
22 UltraStarknetFlavor,
23 UltraStarknetZKFlavor,
26#else
27using FlavorTypes = ::testing::Types<UltraFlavor, UltraKeccakFlavor, UltraZKFlavor, UltraKeccakZKFlavor>;
28#endif
29template <typename Flavor> class UltraTranscriptTests : public ::testing::Test {
30 public:
32
34 using FF = Flavor::FF;
39 using IO = DefaultIO; // Native IO for native flavors
42
55 {
56 TranscriptManifest manifest_expected;
57
58 const size_t virtual_log_n = Flavor::USE_PADDING ? CONST_PROOF_SIZE_LOG_N : log_n;
59
60 size_t MAX_PARTIAL_RELATION_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH;
61 // Size of types is number of bb::frs needed to represent the types
62 // UltraKeccak uses uint256_t for commitments and frs, so we need to handle that differently.
63 size_t data_types_per_Frs = [] {
64 if constexpr (IsKeccakFlavor<Flavor>) {
65 return U256Codec::calc_num_fields<FF>();
66 } else {
67 return FrCodec::calc_num_fields<FF>();
68 }
69 }();
70 size_t data_types_per_G = [] {
71 if constexpr (IsKeccakFlavor<Flavor>) {
72 return U256Codec::calc_num_fields<Commitment>();
73 } else {
74 return FrCodec::calc_num_fields<Commitment>();
75 }
76 }();
77 size_t frs_per_uni = MAX_PARTIAL_RELATION_LENGTH * data_types_per_Frs;
78 size_t frs_per_evals = (Flavor::NUM_ALL_ENTITIES)*data_types_per_Frs;
79
80 size_t round = 0;
81 manifest_expected.add_entry(round, "vk_hash", data_types_per_Frs);
82
83 manifest_expected.add_entry(round, "public_input_0", data_types_per_Frs);
84 constexpr size_t PUBLIC_INPUTS_SIZE = IO::PUBLIC_INPUTS_SIZE;
85 for (size_t i = 0; i < PUBLIC_INPUTS_SIZE; i++) {
86 manifest_expected.add_entry(round, "public_input_" + std::to_string(1 + i), data_types_per_Frs);
87 }
88
89 // For ZK flavors: Gemini masking polynomial commitment is sent at end of oink
90 if constexpr (Flavor::HasZK) {
91 manifest_expected.add_entry(round, "Gemini:masking_poly_comm", data_types_per_G);
92 }
93 manifest_expected.add_entry(round, "W_L", data_types_per_G);
94 manifest_expected.add_entry(round, "W_R", data_types_per_G);
95 manifest_expected.add_entry(round, "W_O", data_types_per_G);
96 manifest_expected.add_challenge(round, "eta");
97
98 round++;
99 manifest_expected.add_entry(round, "LOOKUP_READ_COUNTS", data_types_per_G);
100 manifest_expected.add_entry(round, "LOOKUP_READ_TAGS", data_types_per_G);
101 manifest_expected.add_entry(round, "W_4", data_types_per_G);
102 manifest_expected.add_challenge(round, std::array{ "beta", "gamma" });
103
104 round++;
105 manifest_expected.add_entry(round, "LOOKUP_INVERSES", data_types_per_G);
106 manifest_expected.add_entry(round, "Z_PERM", data_types_per_G);
107
108 manifest_expected.add_challenge(round, "alpha");
109 manifest_expected.add_challenge(round, "Sumcheck:gate_challenge");
110 round++;
111
112 if constexpr (Flavor::HasZK) {
113 manifest_expected.add_entry(round, "Libra:concatenation_commitment", data_types_per_G);
114 manifest_expected.add_entry(round, "Libra:Sum", data_types_per_Frs);
115 manifest_expected.add_challenge(round, "Libra:Challenge");
116 round++;
117 }
118
119 for (size_t i = 0; i < virtual_log_n; ++i) {
120 std::string idx = std::to_string(i);
121 manifest_expected.add_entry(round, "Sumcheck:univariate_" + idx, frs_per_uni);
122 std::string label = "Sumcheck:u_" + idx;
123 manifest_expected.add_challenge(round, label);
124 round++;
125 }
126
127 manifest_expected.add_entry(round, "Sumcheck:evaluations", frs_per_evals);
128
129 if constexpr (Flavor::HasZK) {
130 manifest_expected.add_entry(round, "Libra:claimed_evaluation", data_types_per_Frs);
131 manifest_expected.add_entry(round, "Libra:grand_sum_commitment", data_types_per_G);
132 manifest_expected.add_entry(round, "Libra:quotient_commitment", data_types_per_G);
133 }
134
135 manifest_expected.add_challenge(round, "rho");
136
137 round++;
138 for (size_t i = 1; i < virtual_log_n; ++i) {
139 std::string idx = std::to_string(i);
140 manifest_expected.add_entry(round, "Gemini:FOLD_" + idx, data_types_per_G);
141 }
142 manifest_expected.add_challenge(round, "Gemini:r");
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:a_" + idx, data_types_per_Frs);
147 }
148
149 if constexpr (Flavor::HasZK) {
150 manifest_expected.add_entry(round, "Libra:concatenation_eval", data_types_per_Frs);
151 manifest_expected.add_entry(round, "Libra:shifted_grand_sum_eval", data_types_per_Frs);
152 manifest_expected.add_entry(round, "Libra:grand_sum_eval", data_types_per_Frs);
153 manifest_expected.add_entry(round, "Libra:quotient_eval", data_types_per_Frs);
154 }
155
156 manifest_expected.add_challenge(round, "Shplonk:nu");
157 round++;
158 manifest_expected.add_entry(round, "Shplonk:Q", data_types_per_G);
159 manifest_expected.add_challenge(round, "Shplonk:z");
160
161 round++;
162 manifest_expected.add_entry(round, "KZG:W", data_types_per_G);
163 manifest_expected.add_challenge(round, "KZG:masking_challenge");
164
165 return manifest_expected;
166 }
167
169 {
170 FF a = 1;
171 builder.add_variable(a);
172 builder.add_public_variable(a);
174 }
175
177 {
178 auto a = FF::random_element();
179 auto b = FF::random_element();
180 builder.add_variable(a);
181 builder.add_public_variable(a);
182 builder.add_public_variable(b);
183
184 if constexpr (IO::HasIPA) {
185 auto [stdlib_opening_claim, ipa_proof] =
186 IPA<stdlib::grumpkin<Builder>>::create_random_valid_ipa_claim_and_proof(builder);
187 stdlib_opening_claim.set_public();
188 builder.ipa_proof = ipa_proof;
189 }
190 }
191
192 Proof export_serialized_proof(Prover& prover, const size_t num_public_inputs, const size_t log_n)
193 {
194 // reset internal variables needed for exporting the proof
195 // Note: compute_proof_length_for_export excludes IPA proof length since export_proof appends it separately
196 size_t proof_length = compute_proof_length_for_export<Flavor>(num_public_inputs, log_n);
197 prover.transcript->test_set_proof_parsing_state(0, proof_length);
198 return prover.export_proof();
199 }
200};
201
203
208TYPED_TEST(UltraTranscriptTests, ProverManifestConsistency)
209{
210 // Construct a simple circuit of size n = 8 (i.e. the minimum circuit size)
211 auto builder = typename TestFixture::Builder();
212 TestFixture::generate_test_circuit(builder);
213
214 // Automatically generate a transcript manifest by constructing a proof
216 auto verification_key = std::make_shared<typename TestFixture::VerificationKey>(prover_instance->get_precomputed());
217 typename TestFixture::Prover prover(prover_instance, verification_key);
218 prover.transcript->enable_manifest();
219 auto proof = prover.construct_proof();
220
221 // Check that the prover generated manifest agrees with the manifest hard coded in this suite
222 auto manifest_expected = TestFixture::construct_ultra_honk_manifest(prover.prover_instance->log_dyadic_size());
223 auto prover_manifest = prover.transcript->get_manifest();
224 // Note: a manifest can be printed using manifest.print()
225 manifest_expected.print();
226 prover_manifest.print();
227 ASSERT_GT(manifest_expected.size(), 0);
228 for (size_t round = 0; round < manifest_expected.size(); ++round) {
229 if (prover_manifest[round] != manifest_expected[round]) {
230 info("Prover manifest discrepency in round ", round);
231 info("Prover manifest:");
232 prover_manifest[round].print();
233 info("Expected manifest:");
234 manifest_expected[round].print();
235 FAIL();
236 }
237 }
238}
239
245TYPED_TEST(UltraTranscriptTests, VerifierManifestConsistency)
246{
247 // Construct a simple circuit of size n = 8 (i.e. the minimum circuit size)
248 auto builder = typename TestFixture::Builder();
249 TestFixture::generate_test_circuit(builder);
250
251 // Automatically generate a transcript manifest in the prover by constructing a proof
253 auto verification_key = std::make_shared<typename TestFixture::VerificationKey>(prover_instance->get_precomputed());
254 auto vk_and_hash = std::make_shared<typename TypeParam::VKAndHash>(verification_key);
255 typename TestFixture::Prover prover(prover_instance, verification_key);
256 prover.transcript->enable_manifest();
257 auto proof = prover.construct_proof();
258
259 // Automatically generate a transcript manifest in the verifier by verifying a proof
260 auto verifier_transcript = std::make_shared<typename TypeParam::Transcript>();
261 verifier_transcript->enable_manifest();
262 typename TestFixture::Verifier verifier(vk_and_hash, verifier_transcript);
263 [[maybe_unused]] auto _ = verifier.verify_proof(proof);
264
265 // Check consistency between the manifests generated by the prover and verifier
266 auto prover_manifest = prover.transcript->get_manifest();
267 auto verifier_manifest = verifier.get_transcript()->get_manifest();
268
269 // Note: a manifest can be printed using manifest.print()
270 ASSERT_GT(prover_manifest.size(), 0);
271 for (size_t round = 0; round < prover_manifest.size(); ++round) {
272 ASSERT_EQ(prover_manifest[round], verifier_manifest[round])
273 << "Prover/Verifier manifest discrepency in round " << round;
274 }
275}
276
282TYPED_TEST(UltraTranscriptTests, ChallengeGenerationTest)
283{
284 using Flavor = TypeParam;
285 using FF = Flavor::FF;
286 // initialized with random value sent to verifier
287 auto transcript = TypeParam::Transcript::test_prover_init_empty();
288 // test a bunch of challenges
289 std::vector<std::string> challenge_labels{ "a", "b", "c", "d", "e", "f" };
290 auto challenges = transcript->template get_challenges<FF>(challenge_labels);
291 // check they are not 0
292 for (size_t i = 0; i < challenges.size(); ++i) {
293 ASSERT_NE(challenges[i], 0) << "Challenge " << i << " is 0";
294 }
295 constexpr uint32_t random_val{ 17 }; // arbitrary
296 transcript->send_to_verifier("random val", random_val);
297 // test more challenges
298 challenge_labels = { "a", "b", "c" };
299 challenges = transcript->template get_challenges<FF>(challenge_labels);
300 ASSERT_NE(challenges[0], 0) << "Challenge a is 0";
301 ASSERT_NE(challenges[1], 0) << "Challenge b is 0";
302 ASSERT_NE(challenges[2], 0) << "Challenge c is 0";
303}
304
306{
307 using Flavor = TypeParam;
308 using FF = Flavor::FF;
309 using Commitment = Flavor::Commitment;
310 // Construct a simple circuit of size n = 8 (i.e. the minimum circuit size)
311 auto builder = typename TestFixture::Builder();
312 TestFixture::generate_test_circuit(builder);
313
314 // Automatically generate a transcript manifest by constructing a proof
316 auto verification_key = std::make_shared<typename TestFixture::VerificationKey>(prover_instance->get_precomputed());
317 auto vk_and_hash = std::make_shared<typename TypeParam::VKAndHash>(verification_key);
318 typename TestFixture::Prover prover(prover_instance, verification_key);
319 auto proof = prover.construct_proof();
320 typename TestFixture::Verifier verifier(vk_and_hash);
321 EXPECT_TRUE(verifier.verify_proof(proof).result);
322
323 const size_t virtual_log_n = Flavor::USE_PADDING ? CONST_PROOF_SIZE_LOG_N : prover_instance->log_dyadic_size();
324
325 // Use StructuredProof test utility to deserialize/serialize proof data
326 StructuredProof<Flavor> proof_structure;
327
328 // try deserializing and serializing with no changes and check proof is still valid
329 proof_structure.deserialize(
330 prover.transcript->test_get_proof_data(), verification_key->num_public_inputs, virtual_log_n);
331 proof_structure.serialize(prover.transcript->test_get_proof_data(), virtual_log_n);
332
333 proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs(), virtual_log_n);
334 // we have changed nothing so proof is still valid
335 typename TestFixture::Verifier verifier2(vk_and_hash);
336 EXPECT_TRUE(verifier2.verify_proof(proof).result);
337
338 Commitment one_group_val = Commitment::one();
339 FF rand_val = FF::random_element();
340 proof_structure.z_perm_comm = one_group_val * rand_val; // choose random object to modify
341 proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs(), virtual_log_n);
342 // we have not serialized it back to the proof so it should still be fine
343 typename TestFixture::Verifier verifier3(vk_and_hash);
344 EXPECT_TRUE(verifier3.verify_proof(proof).result);
345
346 proof_structure.serialize(prover.transcript->test_get_proof_data(), virtual_log_n);
347 proof = TestFixture::export_serialized_proof(prover, prover_instance->num_public_inputs(), virtual_log_n);
348 // the proof is now wrong after serializing it
349 typename TestFixture::Verifier verifier4(vk_and_hash);
350 EXPECT_FALSE(verifier4.verify_proof(proof).result);
351
352 proof_structure.deserialize(
353 prover.transcript->test_get_proof_data(), verification_key->num_public_inputs, virtual_log_n);
354 EXPECT_EQ(static_cast<Commitment>(proof_structure.z_perm_comm), one_group_val * rand_val);
355}
void generate_random_test_circuit(Builder &builder)
Flavor::Commitment Commitment
typename Flavor::Transcript::Proof Proof
TranscriptManifest construct_ultra_honk_manifest(const size_t &log_n)
Construct a manifest for a Ultra Honk proof.
Proof export_serialized_proof(Prover &prover, const size_t num_public_inputs, const size_t log_n)
void generate_test_circuit(Builder &builder)
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 HasIPA
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).
Definition flavor.hpp:136
IPA (inner product argument) commitment scheme class.
Definition ipa.hpp:85
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.
std::shared_ptr< Transcript > transcript
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
FF b
Base class templates for structures that contain data parameterized by the fundamental polynomials of...
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)
Test utility for deserializing/serializing proof data into typed structures.
static field random_element(numeric::RNG *engine=nullptr) noexcept