Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
transcript.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Sergei], commit: 777717f6af324188ecd6bb68c3c86ee7befef94d}
3// external_1: { status: Complete, auditors: [@ed25519 (Spearbit)], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#pragma once
8
20#include "origin_tag.hpp"
22#include <atomic>
23#include <concepts>
24
25namespace bb {
26
27// A concept for detecting whether a type is native or in-circuit
28template <typename T>
30
31// A static counter for the number of transcripts created
32// This is used to generate unique labels for the transcript origin tags
33
34// 'inline' (since C++17) ensures a single shared definition with external linkage.
35inline std::atomic<size_t> unique_transcript_index{ 0 };
36
41template <typename Codec_, typename HashFunction_> class BaseTranscript {
42 public:
43 using Codec = Codec_;
44 using HashFunction = HashFunction_;
45 using DataType = typename Codec::DataType;
46 using Proof = std::vector<DataType>;
47
48 // Detects whether the transcript is in-circuit or not
49 static constexpr bool in_circuit = InCircuit<DataType>;
50 // A `DataType` challenge is split into two limbs that consitute challenge buffer
51 static constexpr size_t CHALLENGE_BUFFER_SIZE = 2;
52
54 {
55 // If we are in circuit, we need to get a unique index for the transcript
56 if constexpr (in_circuit) {
58 }
59 }
60
61 // Verifier-specific constructor.
62 explicit BaseTranscript(const Proof& proof) { load_proof(proof); }
63
64 protected:
65 Proof proof_data; // Contains the raw data sent by the prover.
66
67 private:
68 // Friend function for secure tag context extraction
69 template <typename T> friend OriginTag bb::extract_transcript_tag(const T& transcript);
70
71 // Fiat-Shamir Round Tracking
72 size_t transcript_index = 0; // Unique transcript ID (PRIVATE - access via extract_transcript_tag)
73 size_t round_index = 0; // Current FS round (PRIVATE - access via extract_transcript_tag)
74 bool challenge_generation_phase = false; // Whether currently generating challenges (vs sending/receiving data)
75
76 // Challenge generatopm state==
77 bool is_first_challenge = true; // Indicates if this is the first challenge this transcript is generating
78 DataType previous_challenge{}; // Previous challenge buffer (default-initialized to zeros)
79 std::vector<DataType> current_round_data; // Data for the current round that will be hashed to generate challenges
80
81 // Proof parsing state
83 size_t num_frs_written = 0; // Number of frs written to proof_data by the prover
84 size_t num_frs_read = 0; // Number of frs read from proof_data by the verifier
85
86 // Manifest (debugging tool)
87 bool use_manifest = false; // Indicates whether the manifest is turned on (only for manifest tests)
88 TranscriptManifest manifest; // Records a summary of the transcript interactions
89
98 {
99
100 std::vector<DataType> full_buffer;
101
102 const size_t size_bump = (is_first_challenge) ? 0 : 1;
103
104 full_buffer.resize(current_round_data.size() + size_bump);
105
106 // concatenate the previous challenge (if this is not the first challenge) with the current round data.
107 if (!is_first_challenge) {
108 // if not the first challenge, we can use the previous_challenge
109 full_buffer[0] = previous_challenge;
110 } else {
111 // Prevent challenge generation if this is the first challenge we're generating,
112 // AND nothing was sent by the prover.
114 // Update is_first_challenge for the future
115 is_first_challenge = false;
116 }
117
119 current_round_data.end(),
120 full_buffer.begin() + static_cast<std::ptrdiff_t>(size_bump));
121 current_round_data.clear();
122
123 // Hash the full buffer
124 DataType new_challenge = HashFunction::hash(full_buffer);
125 // update previous challenge buffer for next time we call this function
126 previous_challenge = new_challenge;
127 return new_challenge;
128 }
129
136 {
137 return Codec::split_challenge(get_next_challenge_hash());
138 }
139
140 protected:
148 {
149 if (use_manifest) {
150 // Add an entry to the current round of the manifest
151 manifest.add_entry(round_index, label, element_frs.size());
152 }
153
154 current_round_data.insert(current_round_data.end(), element_frs.begin(), element_frs.end());
155 }
156
165 template <typename T> void serialize_to_buffer(const T& element, Proof& proof_data)
166 {
167 auto element_frs = Codec::serialize_to_fields(element);
168 proof_data.insert(proof_data.end(), element_frs.begin(), element_frs.end());
169 }
179 template <typename T> T deserialize_from_buffer(const Proof& proof_data, size_t& offset) const
180 {
181 constexpr size_t element_fr_size = Codec::template calc_num_fields<T>();
182 if (offset + element_fr_size > proof_data.size()) {
183 throw_or_abort("Transcript: deserialize_from_buffer out of bounds");
184 }
185
186 auto element_frs = std::span{ proof_data }.subspan(offset, element_fr_size);
187 offset += element_fr_size;
188
189 auto element = Codec::template deserialize_from_fields<T>(element_frs);
190
191 return element;
192 }
193
194 public:
202 std::vector<DataType> export_proof()
203 {
204 std::vector<DataType> result(num_frs_written);
207 num_frs_written = 0;
208 return result;
209 };
210
216 void load_proof(const std::vector<DataType>& proof)
217 {
218 proof_data.insert(proof_data.end(), proof.begin(), proof.end());
219 }
220
221 // Return the size of proof_data
222 size_t get_proof_size() { return proof_data.size(); }
223
224 // Enables the manifest
225 void enable_manifest() { use_manifest = true; }
226
234 template <typename ChallengeType>
236 {
237 if (use_manifest) {
238 // Add challenge labels for current round to the manifest
239 for (const auto& label : labels) {
241 }
242 }
243
244 // In case the transcript is used for recursive verification, we need to sanitize current round data so we
245 // don't get an origin tag violation inside the hasher. We are doing this to ensure that the free witness
246 // tagged elements that are sent to the transcript and are assigned tags externally, don't trigger the origin
247 // tag security mechanism while we are hashing them.
248 bb::unset_free_witness_tags<in_circuit, DataType>(current_round_data);
249
250 std::vector<ChallengeType> challenges(labels.size());
251 fill(challenges);
252
253 // Track Fiat-Shamir round transitions: entering challenge generation mode.
256 }
257
258 bb::assign_origin_tag<in_circuit>(challenges, OriginTag(transcript_index, round_index, /*is_submitted=*/false));
259
260 return challenges;
261 }
262
271 template <typename ChallengeType>
273 {
274 return generate_challenges<ChallengeType>(labels, [&](std::vector<ChallengeType>& challenges) {
275 const size_t num_challenges = challenges.size();
276 // Generate the challenges by iteratively hashing over the previous challenge, two 127-bit limbs per hash.
277 for (size_t i = 0; i < num_challenges / 2; ++i) {
279 challenges[2 * i] = Codec::template convert_short_challenge<ChallengeType>(challenge_buffer[0]);
280 challenges[(2 * i) + 1] = Codec::template convert_short_challenge<ChallengeType>(challenge_buffer[1]);
281 }
282 if ((num_challenges & 1) == 1) {
284 challenges[num_challenges - 1] =
285 Codec::template convert_short_challenge<ChallengeType>(challenge_buffer[0]);
286 }
287 });
288 }
289
298 {
299 return generate_challenges<ChallengeType>(labels, [&](std::vector<ChallengeType>& challenges) {
300 // One full field challenge per label
301 for (auto& challenge : challenges) {
302 challenge = Codec::template convert_full_challenge<ChallengeType>(get_next_challenge_hash());
303 }
304 });
305 }
306
310 template <typename ChallengeType, size_t N>
312 {
313 std::span<const std::string> labels_span{ labels.data(), labels.size() };
314 auto vec = get_short_challenges<ChallengeType>(labels_span);
316 std::move(vec.begin(), vec.end(), out.begin());
317 return out;
318 }
319
326 template <typename ChallengeType, size_t N>
328 {
329 std::span<const std::string> labels_span{ labels.data(), labels.size() };
330 auto vec = get_challenges<ChallengeType>(labels_span); // calls the const-span overload
332 std::move(vec.begin(), vec.end(), out.begin());
333 return out;
334 }
335
344 template <typename ChallengeType>
345 std::vector<ChallengeType> get_dyadic_powers_of_challenge(const std::string& label, size_t num_challenges)
346 {
347 BB_ASSERT(num_challenges > 0, "get_dyadic_powers_of_challenge called with num_challenges=0");
348 ChallengeType challenge = get_challenge<ChallengeType>(label);
349 std::vector<ChallengeType> pows(num_challenges);
350 pows[0] = challenge;
351 for (size_t i = 1; i < num_challenges; i++) {
352 pows[i] = pows[i - 1].sqr();
353 }
354 return pows;
355 }
356
365 template <class T> void add_to_hash_buffer(const std::string& label, const T& element)
366 {
367 DEBUG_LOG(label, element);
368 // Track Fiat-Shamir round transitions: if we were generating challenges,
369 // now we're adding data, which means a new round has started
372 round_index++;
373 }
374
375 bb::assign_origin_tag<in_circuit>(element, OriginTag(transcript_index, round_index, /*is_submitted=*/true));
376 auto elements = Codec::serialize_to_fields(element);
377
379 }
380
394 template <class T> void send_to_verifier(const std::string& label, const T& element)
395 {
396 DEBUG_LOG(label, element);
397 // Track Fiat-Shamir round transitions: if we were generating challenges,
398 // now we're sending data, which means a new round has started
401 round_index++;
402 }
403
404 auto element_frs = Codec::template serialize_to_fields<T>(element);
405 proof_data.insert(proof_data.end(), element_frs.begin(), element_frs.end());
406 num_frs_written += element_frs.size();
407
409 }
410
418 template <class T> T receive_from_prover(const std::string& label)
419 {
420 const size_t element_size = Codec::template calc_num_fields<T>();
421 if (num_frs_read + element_size > proof_data.size()) {
422 throw_or_abort("Transcript: receive_from_prover out of bounds (proof too short)");
423 }
424
425 auto element_frs = std::span{ proof_data }.subspan(num_frs_read, element_size);
426 // Track Fiat-Shamir round transitions: if we were generating challenges,
427 // now we're receiving data, which means a new round has started
430 round_index++;
431 }
432 // Assign an origin tag to the elements going into the hash buffer
433 bb::assign_origin_tag<in_circuit>(element_frs, OriginTag(transcript_index, round_index, /*is_submitted=*/true));
434
435 num_frs_read += element_size;
436
438
439 auto element = Codec::template deserialize_from_fields<T>(element_frs);
440 DEBUG_LOG(label, element);
441
442 // Ensure that the element got assigned an origin tag
443 bb::check_origin_tag<in_circuit>(element, OriginTag(transcript_index, round_index, /*is_submitted=*/true));
444
445 return element;
446 }
447
452 template <typename ChallengeType> ChallengeType get_challenge(const std::string& label)
453 {
454 std::span<const std::string> label_span(&label, 1);
455 auto result = get_challenges<ChallengeType>(label_span);
456
458 return result[0];
459 }
460
464 template <typename ChallengeType> ChallengeType get_short_challenge(const std::string& label)
465 {
466 std::span<const std::string> label_span(&label, 1);
467 auto result = get_short_challenges<ChallengeType>(label_span);
468
470 return result[0];
471 }
472
480 const std::shared_ptr<BaseTranscript>& prover_transcript)
481 {
482 // We expect this function to only be used when the transcript has just been exported.
483 BB_ASSERT_EQ(prover_transcript->num_frs_written, 0UL, "Expected to be empty");
484 auto verifier_transcript = std::make_shared<BaseTranscript>(*prover_transcript);
485 verifier_transcript->num_frs_read = static_cast<size_t>(verifier_transcript->proof_start);
486 verifier_transcript->proof_start = 0;
487 return verifier_transcript;
488 }
489
490 // Serialize an element of type T to a vector of fields
491 template <typename T> static std::vector<DataType> serialize(const T& element)
492 {
493 return Codec::serialize_to_fields(element);
494 }
495
496 template <typename T> static T deserialize(std::span<const DataType> frs)
497 {
498 return Codec::template deserialize_from_fields<T>(frs);
499 }
500
501 [[nodiscard]] const TranscriptManifest& get_manifest() const { return manifest; };
502
503 void print()
504 {
505 if (!use_manifest) {
506 info("Warning: manifest is not enabled!");
507 }
508 manifest.print();
509 }
510
511 // Test-specific utils
512
520 {
521 auto transcript = std::make_shared<BaseTranscript>();
522 constexpr uint32_t init{ 42 }; // arbitrary
523 transcript->send_to_verifier("Init", init);
524 return transcript;
525 };
526
535 {
536 auto verifier_transcript = std::make_shared<BaseTranscript>(transcript->proof_data);
537 [[maybe_unused]] auto _ = verifier_transcript->template receive_from_prover<DataType>("Init");
538 return verifier_transcript;
539 };
540
546 {
547 this->proof_start = start;
548 this->num_frs_written = written;
549 }
550
556
562 const Proof& test_get_proof_data() const { return proof_data; }
563};
564
567
568template <typename Builder>
574
579template <typename Curve, bool = Curve::is_stdlib_type> struct TranscriptFor {
581};
582
583template <typename Curve> struct TranscriptFor<Curve, true> {
585};
586
587template <typename Curve> using TranscriptFor_t = typename TranscriptFor<Curve>::type;
588
589} // namespace bb
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
typename Codec::DataType DataType
static constexpr bool in_circuit
DataType previous_challenge
ChallengeType get_short_challenge(const std::string &label)
Generate a single short (127-bit) challenge for label. See get_short_challenges.
BaseTranscript(const Proof &proof)
static std::shared_ptr< BaseTranscript > test_prover_init_empty()
For testing: initializes transcript with some arbitrary data so that a challenge can be generated aft...
const Proof & test_get_proof_data() const
T receive_from_prover(const std::string &label)
Reads the next element of type T from the transcript, with a predefined label, only used by verifier.
std::vector< ChallengeType > generate_challenges(std::span< const std::string > labels, auto &&fill)
Shared core of get_challenges / get_short_challenges: the bookkeeping common to every challenge round...
bool challenge_generation_phase
ChallengeType get_challenge(const std::string &label)
Generate a single full-width (~254-bit) challenge for label (the default). See get_challenges.
void test_set_proof_parsing_state(std::ptrdiff_t start, size_t written)
Test utility: Set proof parsing state for export after deserialization.
std::vector< DataType > current_round_data
void add_element_frs_to_hash_buffer(const std::string &label, std::span< const DataType > element_frs)
Adds challenge elements to the current_round_buffer and updates the manifest.
Proof & test_get_proof_data()
Test utility: Get mutable reference to proof_data.
void serialize_to_buffer(const T &element, Proof &proof_data)
Serializes object and appends it to proof_data.
static std::shared_ptr< BaseTranscript > test_verifier_init_empty(const std::shared_ptr< BaseTranscript > &transcript)
For testing: initializes transcript based on proof data then receives junk data produced by BaseTrans...
std::vector< ChallengeType > get_dyadic_powers_of_challenge(const std::string &label, size_t num_challenges)
Get a challenge and compute its dyadic powers [δ, δ², δ⁴, ..., δ^(2^(num_challenges-1))].
std::vector< DataType > export_proof()
Return the proof data starting at proof_start.
DataType get_next_challenge_hash()
Compute the next challenge c_next = H( Compress(c_prev || round_buffer) ).
static T deserialize(std::span< const DataType > frs)
void add_to_hash_buffer(const std::string &label, const T &element)
Adds an element to the transcript.
void send_to_verifier(const std::string &label, const T &element)
Adds a prover message to the transcript, only intended to be used by the prover.
const TranscriptManifest & get_manifest() const
void load_proof(const std::vector< DataType > &proof)
Verifier-specific method. The verifier needs to load a proof or its segment before the verification.
HashFunction_ HashFunction
std::ptrdiff_t proof_start
std::array< DataType, CHALLENGE_BUFFER_SIZE > get_next_duplex_challenge_buffer()
Compute the next challenge and split it into the two 127-bit limbs of the short-challenge buffer.
std::array< ChallengeType, N > get_challenges(const std::array< std::string, N > &labels)
Wrapper around get_challenges to handle array of challenges.
TranscriptManifest manifest
static std::vector< DataType > serialize(const T &element)
std::ptrdiff_t test_get_proof_start() const
Test utility: Get proof_start for validation.
std::vector< DataType > Proof
T deserialize_from_buffer(const Proof &proof_data, size_t &offset) const
Deserializes the frs starting at offset into the typed element and returns that element.
std::vector< ChallengeType > get_short_challenges(std::span< const std::string > labels)
Generate short (127-bit) challenges for the given labels.
std::vector< ChallengeType > get_challenges(std::span< const std::string > labels)
Generate full-width (~254-bit) challenges for the given labels (the default).
static constexpr size_t CHALLENGE_BUFFER_SIZE
static std::shared_ptr< BaseTranscript > convert_prover_transcript_to_verifier_transcript(const std::shared_ptr< BaseTranscript > &prover_transcript)
Convert a prover transcript to a verifier transcript.
std::array< ChallengeType, N > get_short_challenges(const std::array< std::string, N > &labels)
Wrapper around get_short_challenges to handle array of challenges.
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.
stdlib class that evaluates in-circuit poseidon2 hashes, consistent with behavior in crypto::poseidon...
Definition poseidon2.hpp:20
#define DEBUG_LOG(...)
#define info(...)
Definition log.hpp:93
std::string label
ssize_t offset
Definition engine.cpp:62
const auto init
Definition fr.bench.cpp:135
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
std::atomic< size_t > unique_transcript_index
OriginTag extract_transcript_tag(const TranscriptType &transcript)
Extract origin tag context from a transcript.
BaseTranscript< FrCodec, bb::crypto::Poseidon2< bb::crypto::Poseidon2Bn254ScalarFieldParams > > NativeTranscript
typename TranscriptFor< Curve >::type TranscriptFor_t
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
This file contains part of the logic for the Origin Tag mechanism that tracks the use of in-circuit p...
bb::VectorAffineElementPushSpan< BaseParams > out
StdlibCodec for in-circuit (recursive) verification transcript handling.
Helper to get the appropriate Transcript type for a given Curve.
void throw_or_abort(std::string const &err)
VectorField result