barretenberg
Loading...
Searching...
No Matches
transcript.hpp
1#pragma once
2
3#include "barretenberg/common/serialize.hpp"
4#include "barretenberg/crypto/blake3s/blake3s.hpp"
5#include "barretenberg/crypto/pedersen_hash/pedersen.hpp"
6
7// #define LOG_CHALLENGES
8// #define LOG_INTERACTIONS
9
10namespace proof_system::honk {
11
12template <typename T, typename... U>
13concept Loggable = (std::same_as<T, barretenberg::fr> || std::same_as<T, grumpkin::fr> ||
14 std::same_as<T, barretenberg::g1::affine_element> ||
15 std::same_as<T, grumpkin::g1::affine_element> || std::same_as<T, uint32_t>);
16
17// class TranscriptManifest;
19 struct RoundData {
20 std::vector<std::string> challenge_label;
21 std::vector<std::pair<std::string, size_t>> entries;
22
23 bool operator==(const RoundData& other) const = default;
24 };
25
26 std::map<size_t, RoundData> manifest;
27
28 public:
29 void print()
30 {
31 for (auto& round : manifest) {
32 info("Round: ", round.first);
33 for (auto& label : round.second.challenge_label) {
34 info("\tchallenge: ", label);
35 }
36 for (auto& entry : round.second.entries) {
37 info("\telement (", entry.second, "): ", entry.first);
38 }
39 }
40 }
41
42 template <typename... Strings> void add_challenge(size_t round, Strings&... labels)
43 {
44 manifest[round].challenge_label = { labels... };
45 }
46 void add_entry(size_t round, const std::string& element_label, size_t element_size)
47 {
48 manifest[round].entries.emplace_back(element_label, element_size);
49 }
50
51 [[nodiscard]] size_t size() const { return manifest.size(); }
52
53 RoundData operator[](const size_t& round) { return manifest[round]; };
54
55 bool operator==(const TranscriptManifest& other) const = default;
56};
57
63 public:
64 using Proof = std::vector<uint8_t>;
65
66 BaseTranscript() = default;
67
73 explicit BaseTranscript(const Proof& proof_data)
74 : proof_data(proof_data.begin(), proof_data.end())
75 {}
76 static constexpr size_t HASH_OUTPUT_SIZE = 32;
77
78 std::ptrdiff_t proof_start = 0;
79 size_t num_bytes_written = 0; // the number of bytes written to proof_data by the prover or the verifier
80 size_t num_bytes_read = 0; // the number of bytes read from proof_data by the verifier
81 size_t round_number = 0; // current round for manifest
82
83 private:
84 static constexpr size_t MIN_BYTES_PER_CHALLENGE = 128 / 8; // 128 bit challenges
85 bool is_first_challenge = true; // indicates if this is the first challenge this transcript is generating
86 std::array<uint8_t, HASH_OUTPUT_SIZE> previous_challenge_buffer{}; // default-initialized to zeros
87 std::vector<uint8_t> current_round_data;
88
89 // "Manifest" object that records a summary of the transcript interactions
90 TranscriptManifest manifest;
91
100 [[nodiscard]] std::array<uint8_t, HASH_OUTPUT_SIZE> get_next_challenge_buffer()
101 {
102 // Prevent challenge generation if this is the first challenge we're generating,
103 // AND nothing was sent by the prover.
104 if (is_first_challenge) {
105 ASSERT(!current_round_data.empty());
106 }
107
108 // concatenate the previous challenge (if this is not the first challenge) with the current round data.
109 // TODO(Adrian): Do we want to use a domain separator as the initial challenge buffer?
110 // We could be cheeky and use the hash of the manifest as domain separator, which would prevent us from having
111 // to domain separate all the data. (See https://safe-hash.dev)
112 std::vector<uint8_t> full_buffer;
113 if (!is_first_challenge) {
114 // if not the first challenge, we can use the previous_challenge_buffer
115 full_buffer.insert(full_buffer.end(), previous_challenge_buffer.begin(), previous_challenge_buffer.end());
116 } else {
117 // Update is_first_challenge for the future
118 is_first_challenge = false;
119 }
120 if (!current_round_data.empty()) {
121 full_buffer.insert(full_buffer.end(), current_round_data.begin(), current_round_data.end());
122 current_round_data.clear(); // clear the round data buffer since it has been used
123 }
124
125 // Pre-hash the full buffer to minimize the amount of data passed to the cryptographic hash function.
126 // Only a collision-resistant hash-function like Pedersen is required for this step.
127 // Note: this pre-hashing is an efficiency trick that may be discareded if using a SNARK-friendly or in contexts
128 // (eg smart contract verification) where the cost of elliptic curve operations is high.
129 std::vector<uint8_t> compressed_buffer = to_buffer(crypto::pedersen_hash::hash_buffer(full_buffer));
130
131 // Use a strong hash function to derive the new challenge_buffer.
132 auto base_hash = blake3::blake3s(compressed_buffer);
133
134 std::array<uint8_t, HASH_OUTPUT_SIZE> new_challenge_buffer;
135 std::copy_n(base_hash.begin(), HASH_OUTPUT_SIZE, new_challenge_buffer.begin());
136 // update previous challenge buffer for next time we call this function
137 previous_challenge_buffer = new_challenge_buffer;
138 return new_challenge_buffer;
139 };
140
141 protected:
148 void consume_prover_element_bytes(const std::string& label, std::span<const uint8_t> element_bytes)
149 {
150 // Add an entry to the current round of the manifest
151 manifest.add_entry(round_number, label, element_bytes.size());
152
153 current_round_data.insert(current_round_data.end(), element_bytes.begin(), element_bytes.end());
154
155 num_bytes_written += element_bytes.size();
156 }
157
166 template <typename T> void serialize_to_buffer(const T& element, Proof& proof_data)
167 {
168 auto element_bytes = to_buffer(element);
169 proof_data.insert(proof_data.end(), element_bytes.begin(), element_bytes.end());
170 }
180 template <typename T> T deserialize_from_buffer(const Proof& proof_data, size_t& offset) const
181 {
182 constexpr size_t element_size = sizeof(T);
183 ASSERT(offset + element_size <= proof_data.size());
184
185 auto element_bytes = std::span{ proof_data }.subspan(offset, element_size);
186 offset += element_size;
187
188 T element = from_buffer<T>(element_bytes);
189
190 return element;
191 }
192
193 public:
194 // Contains the raw data sent by the prover.
195 Proof proof_data;
196
201 std::vector<uint8_t> export_proof()
202 {
203 std::vector<uint8_t> result(num_bytes_written);
204 std::copy_n(proof_data.begin() + proof_start, num_bytes_written, result.begin());
205 proof_start += static_cast<std::ptrdiff_t>(num_bytes_written);
206 num_bytes_written = 0;
207 return result;
208 };
209
210 void load_proof(const std::vector<uint8_t>& proof)
211 {
212 std::copy(proof.begin(), proof.end(), std::back_inserter(proof_data));
213 }
214
226 template <typename... Strings> std::array<uint256_t, sizeof...(Strings)> get_challenges(const Strings&... labels)
227 {
228 constexpr size_t num_challenges = sizeof...(Strings);
229
230 // Add challenge labels for current round to the manifest
231 manifest.add_challenge(round_number, labels...);
232
233 // Compute the new challenge buffer from which we derive the challenges.
234
235 // Create challenges from bytes.
236 std::array<uint256_t, num_challenges> challenges{};
237
238 // Generate the challenges by iteratively hashing over the previous challenge.
239 for (size_t i = 0; i < num_challenges; i++) {
240 auto next_challenge_buffer = get_next_challenge_buffer(); // get next challenge buffer
241 std::array<uint8_t, sizeof(uint256_t)> field_element_buffer{};
242 // copy half of the hash to lower 128 bits of challenge
243 // Note: because of how read() from buffers to fields works (in field_declarations.hpp),
244 // we use the later half of the buffer
245 std::copy_n(next_challenge_buffer.begin(),
246 HASH_OUTPUT_SIZE / 2,
247 field_element_buffer.begin() + HASH_OUTPUT_SIZE / 2);
248 challenges[i] = from_buffer<uint256_t>(field_element_buffer);
249 }
250
251 // Prepare for next round.
252 ++round_number;
253
254 return challenges;
255 }
256
270 template <class T> void send_to_verifier(const std::string& label, const T& element)
271 {
272 using serialize::write;
273 // TODO(Adrian): Ensure that serialization of affine elements (including point at infinity) is consistent.
274 // TODO(Adrian): Consider restricting serialization (via concepts) to types T for which sizeof(T) reliably
275 // returns the size of T in bytes. (E.g. this is true for std::array but not for std::vector).
276 auto element_bytes = to_buffer(element);
277 proof_data.insert(proof_data.end(), element_bytes.begin(), element_bytes.end());
278
279#ifdef LOG_INTERACTIONS
280 if constexpr (Loggable<T>) {
281 info("sent: ", label, ": ", element);
282 }
283#endif
285 }
286
293 template <class T> T receive_from_prover(const std::string& label)
294 {
295 constexpr size_t element_size = sizeof(T);
296 ASSERT(num_bytes_read + element_size <= proof_data.size());
297
298 auto element_bytes = std::span{ proof_data }.subspan(num_bytes_read, element_size);
299 num_bytes_read += element_size;
300
302
303 T element = from_buffer<T>(element_bytes);
304
305#ifdef LOG_INTERACTIONS
306 if constexpr (Loggable<T>) {
307 info("received: ", label, ": ", element);
308 }
309#endif
310 return element;
311 }
312
319 static std::shared_ptr<BaseTranscript> prover_init_empty()
320 {
321 auto transcript = std::make_shared<BaseTranscript>();
322 constexpr uint32_t init{ 42 }; // arbitrary
323 transcript->send_to_verifier("Init", init);
324 return transcript;
325 };
326
334 static std::shared_ptr<BaseTranscript> verifier_init_empty(const std::shared_ptr<BaseTranscript>& transcript)
335 {
336 auto verifier_transcript = std::make_shared<BaseTranscript>(transcript->proof_data);
337 [[maybe_unused]] auto _ = verifier_transcript->template receive_from_prover<uint32_t>("Init");
338 return verifier_transcript;
339 };
340
341 uint256_t get_challenge(const std::string& label)
342 {
343 uint256_t result = get_challenges(label)[0];
344#if defined LOG_CHALLENGES || defined LOG_INTERACTIONS
345 info("challenge: ", label, ": ", result);
346#endif
347 return result;
348 }
349
350 [[nodiscard]] TranscriptManifest get_manifest() const { return manifest; };
351
352 void print() { manifest.print(); }
353};
354
360template <typename FF, typename T, size_t N> std::array<FF, N> challenges_to_field_elements(std::array<T, N>&& arr)
361{
362 std::array<FF, N> result;
363 std::move(arr.begin(), arr.end(), result.begin());
364 return result;
365}
366} // namespace proof_system::honk
static Fq hash_buffer(const std::vector< uint8_t > &input, GeneratorContext context={})
Given an arbitrary length of bytes, convert them to fields and hash the result using the default gene...
Definition: pedersen.cpp:69
Definition: uint256.hpp:25
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
Definition: transcript.hpp:62
T deserialize_from_buffer(const Proof &proof_data, size_t &offset) const
Deserializes the bytes starting at offset into the typed element and returns that element.
Definition: transcript.hpp:180
void consume_prover_element_bytes(const std::string &label, std::span< const uint8_t > element_bytes)
Adds challenge elements to the current_round_buffer and updates the manifest.
Definition: transcript.hpp:148
void serialize_to_buffer(const T &element, Proof &proof_data)
Serializes object and appends it to proof_data.
Definition: transcript.hpp:166
std::array< uint256_t, sizeof...(Strings)> get_challenges(const Strings &... labels)
After all the prover messages have been sent, finalize the round by hashing all the data and then cre...
Definition: transcript.hpp:226
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.
Definition: transcript.hpp:270
static std::shared_ptr< BaseTranscript > prover_init_empty()
For testing: initializes transcript with some arbitrary data so that a challenge can be generated aft...
Definition: transcript.hpp:319
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.
Definition: transcript.hpp:293
static std::shared_ptr< BaseTranscript > verifier_init_empty(const std::shared_ptr< BaseTranscript > &transcript)
For testing: initializes transcript based on proof data then receives junk data produced by BaseTrans...
Definition: transcript.hpp:334
BaseTranscript(const Proof &proof_data)
Construct a new Base Transcript object for Verifier using proof_data.
Definition: transcript.hpp:73
std::vector< uint8_t > export_proof()
Return the proof data starting at proof_start.
Definition: transcript.hpp:201
Definition: transcript.hpp:18
Definition: transcript.hpp:13
Defines particular circuit builder types expected to be used for circuit construction in stdlib and c...
Definition: claim.hpp:6
std::array< FF, N > challenges_to_field_elements(std::array< T, N > &&arr)
Convert an array of uint256_t's to an array of field elements.
Definition: transcript.hpp:360