barretenberg
Loading...
Searching...
No Matches
transcript.hpp
1#pragma once
2
3#include "../../commitment/pedersen/pedersen.hpp"
4#include "../../hash/blake3s/blake3s.hpp"
5#include "../../primitives/bigfield/bigfield.hpp"
6#include "../../primitives/biggroup/biggroup.hpp"
7#include "../../primitives/bool/bool.hpp"
8#include "../../primitives/curves/bn254.hpp"
9#include "../../primitives/field/field.hpp"
10#include "../../primitives/witness/witness.hpp"
11#include "../verification_key/verification_key.hpp"
12#include "barretenberg/ecc/curves/bn254/fq.hpp"
13#include "barretenberg/ecc/curves/bn254/fr.hpp"
14#include "barretenberg/ecc/curves/bn254/g1.hpp"
15#include "barretenberg/plonk/transcript/transcript.hpp"
16
17namespace proof_system::plonk::stdlib::recursion {
18template <typename Builder> class Transcript {
19 public:
25
26 Transcript(Builder* in_context, const transcript::Manifest& input_manifest)
27 : context(in_context)
28 , transcript_base(input_manifest, transcript::HashType::PedersenBlake3s, 16)
29 , current_challenge(in_context)
30 {}
31
32 Transcript(Builder* in_context,
33 const std::vector<uint8_t>& input_transcript,
34 const transcript::Manifest& input_manifest)
35 : context(in_context)
36 , transcript_base(input_transcript, input_manifest, transcript::HashType::PedersenBlake3s, 16)
37 , current_challenge(in_context)
38 /*, transcript_bytes(in_context) */
39 {
40 // for (size_t i = 0; i < input_transcript.size(); ++i)
41 // {
42 // field_t<Builder> data(witness_pt<Builder>(context, input_transcript[i]));
43 // transcript_bytes.write(byte_array<Builder>(data));
44 // }
45 }
46
57 Transcript(Builder* in_context,
58 const transcript::Manifest& input_manifest,
59 const std::vector<field_pt>& field_buffer,
60 const size_t num_public_inputs)
61 : context(in_context)
62 , transcript_base(input_manifest, transcript::HashType::PedersenBlake3s, 16)
63 , current_challenge(in_context)
64 {
65 size_t count = 0;
66
67 const auto num_rounds = input_manifest.get_num_rounds();
68 for (size_t i = 0; i < num_rounds; ++i) {
69 for (auto manifest_element : input_manifest.get_round_manifest(i).elements) {
70 if (!manifest_element.derived_by_verifier) {
71 if (manifest_element.num_bytes == 32 && manifest_element.name != "public_inputs") {
72 add_field_element(manifest_element.name, field_buffer[count++]);
73 } else if (manifest_element.num_bytes == 64 && manifest_element.name != "public_inputs") {
74 const auto x_lo = field_buffer[count++];
75 const auto x_hi = field_buffer[count++];
76 const auto y_lo = field_buffer[count++];
77 const auto y_hi = field_buffer[count++];
78 fq_pt x(x_lo, x_hi);
79 fq_pt y(y_lo, y_hi);
80 group_pt element(x, y);
81 add_group_element(manifest_element.name, element);
82 } else {
83 ASSERT(manifest_element.name == "public_inputs");
84 std::vector<field_pt> public_inputs;
85 for (size_t i = 0; i < num_public_inputs; ++i) {
86 public_inputs.emplace_back(field_buffer[count++]);
87 }
88 add_field_element_vector(manifest_element.name, public_inputs);
89 }
90 }
91 }
92 }
93 }
94
95 transcript::Manifest get_manifest() const { return transcript_base.get_manifest(); }
96
97 int check_field_element_cache(const std::string& element_name) const
98 {
99 for (size_t i = 0; i < field_keys.size(); ++i) {
100 if (field_keys[i] == element_name) {
101 return static_cast<int>(i);
102 }
103 }
104 return -1;
105 }
106
107 int check_field_element_vector_cache(const std::string& element_name) const
108 {
109 for (size_t i = 0; i < field_vector_keys.size(); ++i) {
110 if (field_vector_keys[i] == element_name) {
111 return static_cast<int>(i);
112 }
113 }
114 return -1;
115 }
116
117 int check_group_element_cache(const std::string& element_name) const
118 {
119 for (size_t i = 0; i < group_keys.size(); ++i) {
120 if (group_keys[i] == element_name) {
121 return static_cast<int>(i);
122 }
123 }
124 return -1;
125 }
126
127 int check_challenge_cache(const std::string& challenge_name, const size_t challenge_idx) const
128 {
129 for (size_t i = 0; i < challenge_keys.size(); ++i) {
130 if (challenge_keys[i] == challenge_name) {
131 ASSERT(challenge_values[i].size() > challenge_idx);
132 return static_cast<int>(i);
133 }
134 }
135 return -1;
136 }
137
138 void add_field_element(const std::string& element_name, const field_pt& element)
139 {
140 std::vector<uint8_t> buffer = element.get_value().to_buffer();
141 const size_t element_size = transcript_base.get_element_size(element_name);
142 // uint8_t* begin = &buffer[0] + 32 - element_size;
143 // uint8_t* end = &buffer[buffer.size()];
144 std::vector<uint8_t> sliced_buffer(buffer.end() - (std::ptrdiff_t)element_size, buffer.end());
145 transcript_base.add_element(element_name, sliced_buffer);
146 field_keys.push_back(element_name);
147 field_values.push_back(element);
148 }
149
150 void add_group_element(const std::string& element_name, const group_pt& element)
151 {
152 uint256_t x = element.x.get_value().lo;
153 uint256_t y = element.y.get_value().lo;
155 transcript_base.add_element(element_name, converted.to_buffer());
156 group_keys.push_back(element_name);
157 group_values.push_back(element);
158 }
159
160 void add_field_element_vector(const std::string& element_name, const std::vector<field_pt>& elements)
161 {
162 std::vector<barretenberg::fr> values;
163 for (size_t i = 0; i < elements.size(); ++i) {
164 values.push_back(elements[i].get_value());
165 }
166 transcript_base.add_element(element_name, to_buffer(values));
167 field_vector_keys.push_back(element_name);
168 field_vector_values.push_back(elements);
169 }
170
171 void apply_fiat_shamir(const std::string& challenge_name)
172 {
173 const size_t num_challenges = get_manifest().get_round_manifest(current_round).num_challenges;
174 transcript_base.apply_fiat_shamir(challenge_name);
175
176 if (num_challenges == 0) {
177 ++current_round;
178 return;
179 }
180 field_pt working_element(context);
181
182 // maximum number of bytes we can store in a field element w/o wrapping modulus is 31.
183 // while we could store more *bits*, we want `preimage_buffer` to mirror how data is formatted
184 // when we serialize field/group elements natively (i.e. a byte array)
185 static constexpr size_t NUM_BITS_PER_PREIMAGE_ELEMENT = 31UL * 8UL;
186 PedersenPreimageBuilder<Builder, NUM_BITS_PER_PREIMAGE_ELEMENT> preimage_buffer(context);
187 if (current_round > 0) {
188 preimage_buffer.add_element(current_challenge);
189 }
190 for (auto manifest_element : get_manifest().get_round_manifest(current_round).elements) {
191 if (manifest_element.num_bytes == 32 && manifest_element.name != "public_inputs") {
192 preimage_buffer.add_element(get_field_element(manifest_element.name));
193 } else if (manifest_element.num_bytes == 64 && manifest_element.name != "public_inputs") {
194 group_pt point = get_circuit_group_element(manifest_element.name);
195
196 // In our buffer, we want to represent each field element as occupying 256 bits of data (to match what
197 // the native transcript does)
198 const auto& x = point.x;
199 const auto& y = point.y;
200 constexpr size_t last_limb_bits = 256 - (fq_pt::NUM_LIMB_BITS * 3);
201 preimage_buffer.add_element_with_existing_range_constraint(y.binary_basis_limbs[3].element,
202 last_limb_bits);
203 preimage_buffer.add_element_with_existing_range_constraint(y.binary_basis_limbs[2].element,
204 fq_pt::NUM_LIMB_BITS);
205 preimage_buffer.add_element_with_existing_range_constraint(y.binary_basis_limbs[1].element,
206 fq_pt::NUM_LIMB_BITS);
207 preimage_buffer.add_element_with_existing_range_constraint(y.binary_basis_limbs[0].element,
208 fq_pt::NUM_LIMB_BITS);
209 preimage_buffer.add_element_with_existing_range_constraint(x.binary_basis_limbs[3].element,
210 last_limb_bits);
211 preimage_buffer.add_element_with_existing_range_constraint(x.binary_basis_limbs[2].element,
212 fq_pt::NUM_LIMB_BITS);
213 preimage_buffer.add_element_with_existing_range_constraint(x.binary_basis_limbs[1].element,
214 fq_pt::NUM_LIMB_BITS);
215 preimage_buffer.add_element_with_existing_range_constraint(x.binary_basis_limbs[0].element,
216 fq_pt::NUM_LIMB_BITS);
217
218 } else if (manifest_element.name == "public_inputs") {
219 std::vector<field_pt> field_array = get_field_element_vector(manifest_element.name);
220 for (size_t i = 0; i < field_array.size(); ++i) {
221 preimage_buffer.add_element(field_array[i]);
222 }
223 } else if (manifest_element.num_bytes < 32 && manifest_element.name != "public_inputs") {
224 // TODO(zac): init round data is being grabbed out of the manifest and not the vkey
225 preimage_buffer.add_element_with_existing_range_constraint(get_field_element(manifest_element.name),
226 manifest_element.num_bytes * 8);
227 }
228 }
229 std::vector<field_pt> round_challenges_new;
230
231 field_pt T0;
232 T0 = preimage_buffer.hash();
233
234 // helper method to slice a challenge into 128-bit slices
235 const auto slice_into_halves = [&](const field_pt& in, const size_t low_bits = 128) {
236 uint256_t v = in.get_value();
237 uint256_t lo = v.slice(0, low_bits);
238 uint256_t hi = v.slice(low_bits, 256);
239
240 field_pt y_lo = field_pt::from_witness(context, lo);
241 field_pt y_hi = field_pt::from_witness(context, hi);
242
243 y_lo.create_range_constraint(low_bits);
244 y_hi.create_range_constraint(254 - low_bits);
245
246 in.add_two(-y_lo, -y_hi * (uint256_t(1) << low_bits)).assert_equal(0);
247
248 // Validate the sum of our two halves does not exceed the circuit modulus over the integers
249 constexpr uint256_t modulus = fr::modulus;
250 const field_pt r_lo = field_pt(context, modulus.slice(0, low_bits));
251 const field_pt r_hi = field_pt(context, modulus.slice(low_bits, 256));
252
253 bool need_borrow = (uint256_t(y_lo.get_value()) > uint256_t(r_lo.get_value()));
254 field_pt borrow = field_pt::from_witness(context, need_borrow);
255
256 // directly call `create_new_range_constraint` to avoid creating an arithmetic gate
257 if constexpr (HasPlookup<Builder>) {
258 context->create_new_range_constraint(borrow.get_witness_index(), 1, "borrow");
259 } else {
260 context->create_range_constraint(borrow.get_witness_index(), 1, "borrow");
261 }
262
263 // Hi range check = r_hi - y_hi - borrow
264 // Lo range check = r_lo - y_lo + borrow * 2^{126}
265 field_pt res_hi = (r_hi - y_hi) - borrow;
266 field_pt res_lo = (r_lo - y_lo) + (borrow * (uint256_t(1) << low_bits));
267
268 res_hi.create_range_constraint(modulus.get_msb() + 1 - low_bits);
269 res_lo.create_range_constraint(low_bits);
270
271 return std::array<field_pt, 2>{ y_lo, y_hi };
272 };
273
274 field_pt base_hash = stdlib::pedersen_hash<Builder>::hash(std::vector<field_pt>{ T0 }, 0);
275 auto hash_halves = slice_into_halves(base_hash);
276 round_challenges_new.push_back(hash_halves[1]);
277
278 if (num_challenges > 1) {
279 round_challenges_new.push_back(hash_halves[0]);
280 }
281 base_hash = (slice_into_halves(base_hash, 8)[1] * 256).normalize();
282
283 // This block of code only executes for num_challenges > 2, which (currently) only happens in the nu round
284 // when we need to generate short scalars. In this case, we generate 32-byte challenges and split them in
285 // half to get the relevant challenges.
286 for (size_t i = 2; i < num_challenges; i += 2) {
287 // TODO(@zac-williamson) make this a Poseidon hash not a Pedersen hash
288 field_pt hash_output = stdlib::pedersen_hash<Builder>::hash(
289 std::vector<field_pt>{ (base_hash + field_pt(i / 2)).normalize() }, 0);
290 auto hash_halves = slice_into_halves(hash_output);
291 round_challenges_new.push_back(hash_halves[1]);
292 if (i + 1 < num_challenges) {
293 round_challenges_new.push_back(hash_halves[0]);
294 }
295 }
296 current_challenge = round_challenges_new[round_challenges_new.size() - 1];
297 ++current_round;
298 challenge_keys.push_back(challenge_name);
299
300 std::vector<field_pt> challenge_elements;
301 for (const auto& challenge : round_challenges_new) {
302 challenge_elements.push_back(challenge);
303 }
304 challenge_values.push_back(challenge_elements);
305 }
306
307 field_pt get_field_element(const std::string& element_name) const
308 {
309 const int cache_idx = check_field_element_cache(element_name);
310 if (cache_idx != -1) {
311 return field_values[static_cast<size_t>(cache_idx)];
312 }
313 barretenberg::fr value =
314 barretenberg::fr::serialize_from_buffer(&(transcript_base.get_element(element_name))[0]);
315 field_pt result(witness_pt(context, value));
316 field_keys.push_back(element_name);
317 field_values.push_back(result);
318 return result;
319 }
320
321 std::vector<field_pt> get_field_element_vector(const std::string& element_name) const
322 {
323 const int cache_idx = check_field_element_vector_cache(element_name);
324 if (cache_idx != -1) {
325 return field_vector_values[static_cast<size_t>(cache_idx)];
326 }
327 std::vector<barretenberg::fr> values = many_from_buffer<fr>(transcript_base.get_element(element_name));
328 std::vector<field_pt> result;
329
330 for (auto& value : values) {
331 result.push_back(witness_pt(context, value));
332 }
333
334 field_vector_keys.push_back(element_name);
335 field_vector_values.push_back(result);
336 return result;
337 }
338
339 bool has_challenge(const std::string& challenge_name) const
340 {
341 return transcript_base.has_challenge(challenge_name);
342 }
343
344 field_pt get_challenge_field_element(const std::string& challenge_name, const size_t challenge_idx = 0) const
345 {
346 const int cache_idx = check_challenge_cache(challenge_name, challenge_idx);
347 ASSERT(cache_idx != -1);
348 return challenge_values[static_cast<size_t>(cache_idx)][challenge_idx];
349 }
350
351 field_pt get_challenge_field_element_from_map(const std::string& challenge_name,
352 const std::string& challenge_map_name) const
353 {
354 const int challenge_idx = transcript_base.get_challenge_index_from_map(challenge_map_name);
355 if (challenge_idx == -1) {
356 return field_pt(nullptr, 1);
357 }
358 const int cache_idx = check_challenge_cache(challenge_name, static_cast<size_t>(challenge_idx));
359 ASSERT(cache_idx != -1);
360 return challenge_values[static_cast<size_t>(cache_idx)][static_cast<size_t>(challenge_idx)];
361 }
362
363 barretenberg::g1::affine_element get_group_element(const std::string& element_name) const
364 {
366 barretenberg::g1::affine_element::serialize_from_buffer(&(transcript_base.get_element(element_name))[0]);
367 return value;
368 }
369
370 group_pt get_circuit_group_element(const std::string& element_name) const
371 {
372 int cache_idx = check_group_element_cache(element_name);
373 if (cache_idx != -1) {
374 return group_values[static_cast<size_t>(cache_idx)];
375 }
377 barretenberg::g1::affine_element::serialize_from_buffer(&(transcript_base.get_element(element_name))[0]);
378 group_pt result = convert_g1(context, value);
379 group_keys.push_back(element_name);
380 group_values.push_back(result);
381 return result;
382 }
383
384 static fq_pt convert_fq(Builder* ctx, const barretenberg::fq& input) { return fq_pt::from_witness(ctx, input); };
385
386 static group_pt convert_g1(Builder* ctx, const barretenberg::g1::affine_element& input)
387 {
388 return group_pt::from_witness(ctx, input);
389 };
390
391 size_t get_num_challenges(const std::string& challenge_name) const
392 {
393 return transcript_base.get_num_challenges(challenge_name);
394 }
395
396 Builder* context;
397
398 private:
399 transcript::Transcript transcript_base;
400 field_pt current_challenge;
401
402 mutable std::vector<std::string> field_vector_keys;
403 mutable std::vector<std::vector<field_pt>> field_vector_values;
404
405 mutable std::vector<std::string> field_keys;
406 mutable std::vector<field_pt> field_values;
407
408 mutable std::vector<std::string> group_keys;
409 mutable std::vector<group_pt> group_values;
410
411 std::vector<std::string> challenge_keys;
412 std::vector<std::vector<field_pt>> challenge_values;
413
414 size_t current_round = 0;
415};
416} // namespace proof_system::plonk::stdlib::recursion
Definition: affine_element.hpp:11
Definition: uint256.hpp:25
constexpr uint256_t slice(uint64_t start, uint64_t end) const
Definition: uint256_impl.hpp:157
Definition: standard_circuit_builder.hpp:12
Definition: biggroup.hpp:22
Definition: field.hpp:10
Transcript(Builder *in_context, const transcript::Manifest &input_manifest, const std::vector< field_pt > &field_buffer, const size_t num_public_inputs)
Construct a new Transcript object using a proof represented as a field_pt vector.
Definition: transcript.hpp:57
Definition: witness.hpp:10
Definition: manifest.hpp:11
Definition: transcript.hpp:34
std::vector< uint8_t > get_element(const std::string &element_name) const
Definition: transcript.cpp:392
size_t get_num_challenges(const std::string &challenge_name) const
Definition: transcript.cpp:377
int get_challenge_index_from_map(const std::string &challenge_map_name) const
Definition: transcript.cpp:324
bool has_challenge(const std::string &challenge_name) const
Definition: transcript.cpp:337
void apply_fiat_shamir(const std::string &challenge_name)
Definition: transcript.cpp:171
size_t get_element_size(const std::string &element_name) const
Definition: transcript.cpp:405
Contains all the headers required to adequately compile the types defined in circuit_builders_fwd....
Definition: circuit_builders.hpp:11