Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
sha256_trace.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstddef>
5
12
13namespace bb::avm2::tracegen {
14
15using C = Column;
16
17namespace {
18
19constexpr FF TAG_U32_AS_FF = FF(static_cast<uint8_t>(MemoryTag::U32));
20
21// Precomputed inverses for values 0..65. Covers:
22// - rounds_remaining inverses: FF(64 - i) for i in [0, 63], i.e., values 1..64
23// - input_rounds_rem inverses: values 1..16
24// Index 0 maps to FF(0) which is not invertible; batch_invert leaves it as 0.
25const std::array<FF, 65> PRECOMPUTED_INVERSES = []() {
26 std::array<FF, 65> inverses;
27 for (size_t i = 0; i < 65; i++) {
28 inverses[i] = FF(i);
29 }
30 FF::batch_invert(inverses);
31 return inverses;
32}();
33
34// These are some useful groupings of columns for the SHA256 trace that we will iterate over.
35constexpr std::array<C, 8> STATE_COLS = {
36 C::sha256_a, C::sha256_b, C::sha256_c, C::sha256_d, C::sha256_e, C::sha256_f, C::sha256_g, C::sha256_h,
37};
38
39constexpr std::array<C, 8> INIT_STATE_COLS = {
40 C::sha256_init_a, C::sha256_init_b, C::sha256_init_c, C::sha256_init_d,
41 C::sha256_init_e, C::sha256_init_f, C::sha256_init_g, C::sha256_init_h,
42};
43
44constexpr std::array<C, 16> W_COLS = {
45 C::sha256_helper_w0, C::sha256_helper_w1, C::sha256_helper_w2, C::sha256_helper_w3,
46 C::sha256_helper_w4, C::sha256_helper_w5, C::sha256_helper_w6, C::sha256_helper_w7,
47 C::sha256_helper_w8, C::sha256_helper_w9, C::sha256_helper_w10, C::sha256_helper_w11,
48 C::sha256_helper_w12, C::sha256_helper_w13, C::sha256_helper_w14, C::sha256_helper_w15,
49};
50
51constexpr std::array<C, 16> OUTPUT_COLS = {
52 C::sha256_output_a_lhs, C::sha256_output_a_rhs, C::sha256_output_b_lhs, C::sha256_output_b_rhs,
53 C::sha256_output_c_lhs, C::sha256_output_c_rhs, C::sha256_output_d_lhs, C::sha256_output_d_rhs,
54 C::sha256_output_e_lhs, C::sha256_output_e_rhs, C::sha256_output_f_lhs, C::sha256_output_f_rhs,
55 C::sha256_output_g_lhs, C::sha256_output_g_rhs, C::sha256_output_h_lhs, C::sha256_output_h_rhs,
56};
57
58constexpr std::array<uint32_t, 64> ROUND_CONSTANTS = {
59 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
60 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
61 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
62 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
63 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
64 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
65 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
66 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
67};
68
69}; // namespace
70
77{
78 for (size_t i = 0; i < 16; i++) {
79 trace.set(row, { { { W_COLS[i], prev_w_helpers[i] } } });
80 }
81}
82
88void Sha256TraceBuilder::set_state_cols(const std::array<uint32_t, 8>& state, TraceContainer& trace) const
89{
90 for (size_t i = 0; i < 8; i++) {
91 trace.set(row, { { { STATE_COLS[i], state[i] } } });
92 }
93}
94
100void Sha256TraceBuilder::set_init_state_cols(const std::array<uint32_t, 8>& init_state, TraceContainer& trace) const
101{
102 for (size_t i = 0; i < 8; i++) {
103 trace.set(row, { { { INIT_STATE_COLS[i], init_state[i] } } });
104 }
105}
106
120 uint64_t a, const uint8_t b, C col_lhs, C col_rhs, TraceContainer& trace) const
121{
122 uint32_t a_lhs = static_cast<uint32_t>(a >> b);
123 uint32_t a_rhs = static_cast<uint32_t>(a) & static_cast<uint32_t>((static_cast<uint64_t>(1) << b) - 1);
124 trace.set(row, { { { col_lhs, a_lhs }, { col_rhs, a_rhs } } });
125}
126
147 const uint32_t val, const uint8_t shift, C col_result, C col_rhs, TraceContainer& trace) const
148{
149 uint32_t result = (val >> shift) | (val << (32U - shift));
150 uint32_t rhs = val & ((static_cast<uint32_t>(1) << shift) - 1);
151 trace.set(row, { { { col_result, result }, { col_rhs, rhs } } });
152 return result;
153}
154
171 const uint32_t val, const uint8_t shift, C col_lhs, C col_rhs, TraceContainer& trace) const
172{
173 auto result = val >> shift;
174 into_limbs_with_witness(val, shift, col_lhs, col_rhs, trace);
175 return result;
176}
177
189 TraceContainer& trace) const
190{
191
192 // Computing w[j] := w[j-16] + s0 + w[j-7] + s1
193
194 // Step (1) s0 := ror(w[i - 15], 7) ^ ror(w[i - 15], 18) ^ (w[i - 15] >> 3);
195 // Compute ror(w[i - 15], 7)
196 uint32_t rot_7 = ror_with_witness(prev_w_helpers[1], 7, C::sha256_w_15_rotr_7, C::sha256_rhs_w_7, trace);
197 trace.set(C::sha256_two_pow_7, row, 128); // Store 2^7 for reference
198 // Compute ror(w[i - 15], 18)
199 uint32_t rot_18 = ror_with_witness(prev_w_helpers[1], 18, C::sha256_w_15_rotr_18, C::sha256_rhs_w_18, trace);
200 trace.set(C::sha256_two_pow_18, row, 262144); // Store 2^18 for reference
201 // Compute (w[i - 15] >> 3)
202 uint32_t shift_3 = shr_with_witness(prev_w_helpers[1], 3, C::sha256_lhs_w_3, C::sha256_rhs_w_3, trace);
203 trace.set(C::sha256_two_pow_3, row, 8); // Store 2^3 for reference
204
205 // Compute ror(w[i - 15], 7) ^ ror(w[i - 15], 18)
206 trace.set(C::sha256_w_15_rotr_7_xor_w_15_rotr_18, row, rot_7 ^ rot_18);
207 // Compute s0;
208 uint32_t w_s_0 = rot_7 ^ rot_18 ^ shift_3;
209 trace.set(C::sha256_w_s_0, row, w_s_0);
210
211 // Step (2) s1 := ror(w[i - 2], 17) ^ ror(w[i - 2], 19) ^ (w[i - 2] >> 10);
212 // Compute ror(w[i - 2], 17)
213 uint32_t rot_17 = ror_with_witness(prev_w_helpers[14], 17, C::sha256_w_2_rotr_17, C::sha256_rhs_w_17, trace);
214 trace.set(C::sha256_two_pow_17, row, 131072); // Store 2^17 for reference
215 // Compute ror(wi - 2, 19)
216 uint32_t rot_19 = ror_with_witness(prev_w_helpers[14], 19, C::sha256_w_2_rotr_19, C::sha256_rhs_w_19, trace);
217 trace.set(C::sha256_two_pow_19, row, 524288); // Store 2^19 for reference
218 // Compute (w[i - 2] >> 10)
219 uint32_t shift_10 = shr_with_witness(prev_w_helpers[14], 10, C::sha256_lhs_w_10, C::sha256_rhs_w_10, trace);
220 trace.set(C::sha256_two_pow_10, row, 1024); // Store 2^10 for reference
221
222 // Compute ror(w[i - 2], 17) ^ ror(w[i - 2], 19)
223 trace.set(C::sha256_w_2_rotr_17_xor_w_2_rotr_19, row, rot_17 ^ rot_19);
224 // Compute s1;
225 uint32_t w_s_1 = rot_17 ^ rot_19 ^ shift_10;
226 trace.set(C::sha256_w_s_1, row, w_s_1);
227
228 // Compute w:= w[0] + s0 + w[9] + s1
229 // The computation of w can overflow 32 bits so we need to use a 64-bit integer and perform modulo reduction
230 uint64_t computed_w = static_cast<uint64_t>(prev_w_helpers[0]) + static_cast<uint64_t>(w_s_0) +
231 static_cast<uint64_t>(prev_w_helpers[9]) + static_cast<uint64_t>(w_s_1);
232
233 into_limbs_with_witness(computed_w, 32, C::sha256_computed_w_lhs, C::sha256_computed_w_rhs, trace);
234 return static_cast<uint32_t>(computed_w);
235}
236
249std::array<uint32_t, 8> Sha256TraceBuilder::compute_compression_with_witness(const std::array<uint32_t, 8>& state,
250 uint32_t round_w,
251 uint32_t round_constant,
252 TraceContainer& trace) const
253{
254
255 // Apply SHA-256 compression function to the message schedule
256 // Compute S1 := ror(e, 6U) ^ ror(e, 11U) ^ ror(e, 25U);
257 // Compute ror(e, 6)
258 uint32_t rot_6 = ror_with_witness(state[4], 6, C::sha256_e_rotr_6, C::sha256_rhs_e_6, trace);
259 trace.set(C::sha256_two_pow_6, row, 64); // Store 2^6 for reference
260 // Compute ror(e, 11)
261 uint32_t rot_11 = ror_with_witness(state[4], 11, C::sha256_e_rotr_11, C::sha256_rhs_e_11, trace);
262 trace.set(C::sha256_two_pow_11, row, 2048); // Store 2^11 for reference
263 // Compute ror(e, 25)
264 uint32_t rot_25 = ror_with_witness(state[4], 25, C::sha256_e_rotr_25, C::sha256_rhs_e_25, trace);
265 trace.set(C::sha256_two_pow_25, row, 33554432); // Store 2^25 for reference
266
267 // Compute ror(e, 6) ^ ror(e, 11)
268 trace.set(C::sha256_e_rotr_6_xor_e_rotr_11, row, rot_6 ^ rot_11);
269 // Compute S1, this can't overflow but we expand to uint64_t for later use
270 uint64_t S1 = rot_6 ^ rot_11 ^ rot_25;
271 trace.set(C::sha256_s_1, row, S1);
272
273 // Compute ch := (e & f) ^ (~e & g);
274 // Compute ~e
275 uint32_t not_e = ~state[4];
276 trace.set(C::sha256_not_e, row, not_e);
277 // Compute e & f
278 uint32_t e_and_f = state[4] & state[5];
279 trace.set(C::sha256_e_and_f, row, e_and_f);
280 // Compute ~e & g
281 uint32_t not_e_and_g = not_e & state[6];
282 trace.set(C::sha256_not_e_and_g, row, not_e_and_g);
283 // Compute (e & f) ^ (~e & g),this can't overflow but we expand to uint64_t for later use
284 uint64_t ch = e_and_f ^ not_e_and_g;
285 trace.set(C::sha256_ch, row, ch);
286
287 // Compute S0 := ror(a, 2U) ^ ror(a, 13U) ^ ror(a, 22U);
288 // Compute ror(a, 2)
289 uint32_t rot_2 = ror_with_witness(state[0], 2, C::sha256_a_rotr_2, C::sha256_rhs_a_2, trace);
290 trace.set(C::sha256_two_pow_2, row, 4); // Store 2^2 for reference
291 // Compute ror(a, 13)
292 uint32_t rot_13 = ror_with_witness(state[0], 13, C::sha256_a_rotr_13, C::sha256_rhs_a_13, trace);
293 trace.set(C::sha256_two_pow_13, row, 8192); // Store 2^13 for reference
294 // Compute ror(a, 22)
295 uint32_t rot_22 = ror_with_witness(state[0], 22, C::sha256_a_rotr_22, C::sha256_rhs_a_22, trace);
296 trace.set(C::sha256_two_pow_22, row, 4194304); // Store 2^22 for reference
297
298 // Compute ror(a, 2) ^ ror(a, 13)
299 trace.set(C::sha256_a_rotr_2_xor_a_rotr_13, row, rot_2 ^ rot_13);
300 // Compute S0, this can't overflow but we expand to uint64_t for later use
301 uint64_t S0 = rot_2 ^ rot_13 ^ rot_22;
302 trace.set(C::sha256_s_0, row, S0);
303
304 // Compute Maj := (a & b) ^ (a & c) ^ (b & c);
305 // Compute a & b
306 uint32_t a_and_b = state[0] & state[1];
307 trace.set(C::sha256_a_and_b, row, a_and_b);
308 // Compute a & c
309 uint32_t a_and_c = state[0] & state[2];
310 trace.set(C::sha256_a_and_c, row, a_and_c);
311 // Compute b & c
312 uint32_t b_and_c = state[1] & state[2];
313 trace.set(C::sha256_b_and_c, row, b_and_c);
314 // Compute (a & b) ^ (a & c)
315 trace.set(C::sha256_a_and_b_xor_a_and_c, row, a_and_b ^ a_and_c);
316 // Compute Maj, this is expanded to uint64_t to detect later overflows
317 uint64_t maj = a_and_b ^ a_and_c ^ b_and_c;
318 trace.set(C::sha256_maj, row, maj);
319
320 // Compute temp values, these need be 64-bit integers and performed modulo 2^32
321 uint64_t temp1 = static_cast<uint64_t>(state[7]) + S1 + ch + static_cast<uint64_t>(round_constant) +
322 static_cast<uint64_t>(round_w);
323 uint64_t temp2 = S0 + maj;
324 uint64_t next_a = temp1 + temp2;
325 into_limbs_with_witness(next_a, 32, C::sha256_next_a_lhs, C::sha256_next_a_rhs, trace);
326 trace.set(C::sha256_round_constant, row, round_constant);
327 uint32_t a = static_cast<uint32_t>(next_a);
328
329 // Additions can overflow 32 bits so we perform modulo reduction
330 uint64_t next_e = static_cast<uint64_t>(state[3]) + temp1;
331 into_limbs_with_witness(next_e, 32, C::sha256_next_e_lhs, C::sha256_next_e_rhs, trace);
332 uint32_t e = static_cast<uint32_t>(next_e);
333
334 return {
335 a, /*a = temp1 + temp2*/
336 state[0], /*b = a*/
337 state[1], /*c = b*/
338 state[2], /*d = c*/
339 e, /*e = d + temp1*/
340 state[4], /*f = e*/
341 state[5], /*g = f*/
342 state[6], /*h = g*/
343 };
344}
345
352void Sha256TraceBuilder::compute_sha256_output(const std::array<uint32_t, 8>& out_state,
353 const std::array<uint32_t, 8>& init_state,
354 TraceContainer& trace) const
355{
356 uint32_t counter = 0;
357 for (const auto& [init, state] : zip_view(init_state, out_state)) {
358 uint64_t output = static_cast<uint64_t>(init) + static_cast<uint64_t>(state);
359 into_limbs_with_witness(output, 32, OUTPUT_COLS[counter], OUTPUT_COLS[counter + 1], trace);
360 counter += 2;
361 }
362}
363
383 TraceContainer& trace)
384{
385 // row is a class member and is initialized to 1 (we use shifted columns) in the constructor.
386
387 for (const auto& event : events) {
388
390 // Memory Components of SHA-256 Compression Function
392 // Upcast addresses to uint64_t to avoid overflow issues
393 uint64_t state_addr = static_cast<uint64_t>(event.state_addr);
394 uint64_t input_addr = static_cast<uint64_t>(event.input_addr);
395 uint64_t output_addr = static_cast<uint64_t>(event.output_addr);
396
397 uint64_t max_state_addr = state_addr + 7; // State is 8 elements
398 uint64_t max_input_addr = input_addr + 15; // Input is 16 elements
399 uint64_t max_output_addr = output_addr + 7; // Output is 8 elements
400
401 // These are unconditional values that must always be set at the start
402 trace.set(row,
403 { {
404 { C::sha256_sel, 1 },
405 { C::sha256_start, 1 },
406 { C::sha256_execution_clk, event.execution_clk },
407 { C::sha256_space_id, event.space_id },
408 { C::sha256_u32_tag, TAG_U32_AS_FF },
409 // Operand Addresses
410 { C::sha256_state_addr, state_addr },
411 { C::sha256_input_addr, input_addr },
412 { C::sha256_output_addr, output_addr },
413 // Helpers
414 { C::sha256_max_mem_addr, AVM_HIGHEST_MEM_ADDRESS },
415 { C::sha256_max_state_addr, max_state_addr },
416 { C::sha256_max_input_addr, max_input_addr },
417 { C::sha256_max_output_addr, max_output_addr },
418 { C::sha256_input_rounds_rem, 16 }, // Number of inputs
419 { C::sha256_input_rounds_rem_inv, PRECOMPUTED_INVERSES[16] },
420 { C::sha256_sel_is_input_round, 1 },
421 { C::sha256_rounds_remaining, 64 }, // Number of Sha256 Rounds
422 } });
423
425 // Error Handling - Memory Out of Range
427 bool state_out_of_range = max_state_addr > AVM_HIGHEST_MEM_ADDRESS;
428 bool input_out_of_range = max_input_addr > AVM_HIGHEST_MEM_ADDRESS;
429 bool output_out_of_range = max_output_addr > AVM_HIGHEST_MEM_ADDRESS;
430
431 bool out_of_range_err = output_out_of_range || input_out_of_range || state_out_of_range;
432 if (out_of_range_err) {
433 trace.set(row,
434 { {
435 // Error flags
436 { C::sha256_sel_state_out_of_range_err, state_out_of_range ? 1 : 0 },
437 { C::sha256_sel_input_out_of_range_err, input_out_of_range ? 1 : 0 },
438 { C::sha256_sel_output_out_of_range_err, output_out_of_range ? 1 : 0 },
439 { C::sha256_mem_out_of_range_err, 1 },
440 { C::sha256_err, 1 }, // Set the error flag
441 { C::sha256_end, 1 }, // End is set on error
442 } });
443 row++;
444 continue; // Skip to the next event if we have an out of range error
445 }
446
448 // Load Initial State from Memory
450 // If we get here we are safe to load the memory, we need to split this up between the parallel and sequential
451 // loading. State is loaded in parallel, whilst inputs are loaded sequential.
452
453 // Since we treat them as separate temporality groups, if there is an error in the state loading, we will not
454 // load the input
455 trace.set(row,
456 { {
457 // State Loading Selectors
458 { C::sha256_sel_mem_state_or_output, 1 },
459 // State Addresses
460 { C::sha256_memory_address_0_, state_addr },
461 { C::sha256_memory_address_1_, state_addr + 1 },
462 { C::sha256_memory_address_2_, state_addr + 2 },
463 { C::sha256_memory_address_3_, state_addr + 3 },
464 { C::sha256_memory_address_4_, state_addr + 4 },
465 { C::sha256_memory_address_5_, state_addr + 5 },
466 { C::sha256_memory_address_6_, state_addr + 6 },
467 { C::sha256_memory_address_7_, state_addr + 7 },
468 // State Values
469 { C::sha256_memory_register_0_, event.state[0].as_ff() },
470 { C::sha256_memory_register_1_, event.state[1].as_ff() },
471 { C::sha256_memory_register_2_, event.state[2].as_ff() },
472 { C::sha256_memory_register_3_, event.state[3].as_ff() },
473 { C::sha256_memory_register_4_, event.state[4].as_ff() },
474 { C::sha256_memory_register_5_, event.state[5].as_ff() },
475 { C::sha256_memory_register_6_, event.state[6].as_ff() },
476 { C::sha256_memory_register_7_, event.state[7].as_ff() },
477 // Values need to match initial state of sha256 compression
478 { C::sha256_init_a, event.state[0].as_ff() },
479 { C::sha256_init_b, event.state[1].as_ff() },
480 { C::sha256_init_c, event.state[2].as_ff() },
481 { C::sha256_init_d, event.state[3].as_ff() },
482 { C::sha256_init_e, event.state[4].as_ff() },
483 { C::sha256_init_f, event.state[5].as_ff() },
484 { C::sha256_init_g, event.state[6].as_ff() },
485 { C::sha256_init_h, event.state[7].as_ff() },
486 // State Memory Tags
487 { C::sha256_memory_tag_0_, static_cast<uint8_t>(event.state[0].get_tag()) },
488 { C::sha256_memory_tag_1_, static_cast<uint8_t>(event.state[1].get_tag()) },
489 { C::sha256_memory_tag_2_, static_cast<uint8_t>(event.state[2].get_tag()) },
490 { C::sha256_memory_tag_3_, static_cast<uint8_t>(event.state[3].get_tag()) },
491 { C::sha256_memory_tag_4_, static_cast<uint8_t>(event.state[4].get_tag()) },
492 { C::sha256_memory_tag_5_, static_cast<uint8_t>(event.state[5].get_tag()) },
493 { C::sha256_memory_tag_6_, static_cast<uint8_t>(event.state[6].get_tag()) },
494 { C::sha256_memory_tag_7_, static_cast<uint8_t>(event.state[7].get_tag()) },
495 } });
496
498 // Check for Tag Errors in State
500 bool invalid_state_tag_err = std::ranges::any_of(
501 event.state, [](const MemoryValue& state) { return state.get_tag() != MemoryTag::U32; });
502
503 if (invalid_state_tag_err) {
504 // This is the more efficient batched tag check we perform in the circuit
505 FF batched_tag_check = 0;
506 // Batch the state tag checks
507 for (uint32_t i = 0; i < event.state.size(); i++) {
508 // Compute the batched tag check step by step to match the circuit implementation
509 FF mem_tag = FF(static_cast<uint8_t>(event.state[i].get_tag()));
510 FF state_tag_diff = mem_tag - TAG_U32_AS_FF;
511 FF exponent = FF(1 << (i * 3)); // exponent is 1, 8, 64, 512, ...
512 batched_tag_check += state_tag_diff * exponent;
513 }
514 trace.set(row,
515 { {
516 { C::sha256_sel_invalid_state_tag_err, 1 },
517 // Inline inversion (not batched): this is an error path that should not often occur in
518 // production. Guaranteed non-zero (so inversion is safe) since we have an invalid tag.
519 { C::sha256_batch_tag_inv, batched_tag_check.invert() },
520 { C::sha256_end, 1 },
521 { C::sha256_err, 1 }, // Set the error flag
522 } });
523
524 row++;
525 continue; // Skip to the next event if we have an invalid state tag error
526 }
527
529 // Load Hash inputs and check for tag errors
531 // The inputs vector is expected to 16 elements and each element is expected to be a 32-bit value
532 // If during simulation we encounter an invalid tag, it will have been the last element we retrieved
533 // before we threw an error - so it will be the last element in the input vector.
534 // Therefore, it is just sufficient to check the tag of the last element
535 BB_ASSERT(!event.input.empty(), "SHA256 input cannot be empty");
536 bool invalid_input_tag_err = event.input.back().get_tag() != MemoryTag::U32;
537
538 // Note that if we encountered an invalid tag error, the row that loaded the invalid tag needs to contain
539 // sel_invalid_input_ROW_tag_err. And all the rows before need to contain sel_invalid_input_tag_err.
540 // The former is used to constrain the specific error, while the latter is used to propagate the error
541 // to the start row (to communicate back to execution) and to turn off any computation constraints.
542 for (uint32_t i = 0; i < event.input.size(); i++) {
543 const uint32_t input_rounds_rem = 16 - i;
544 FF input_rounds_rem_inv = PRECOMPUTED_INVERSES[input_rounds_rem];
545
546 const MemoryValue& round_input = event.input[i];
547 FF input_tag = FF(static_cast<uint8_t>(round_input.get_tag()));
548 FF input_tag_diff = input_tag - TAG_U32_AS_FF;
549 // Inline inversion (not batched): this is an error path that should not often occur in production.
550 FF input_tag_diff_inv = input_tag_diff == 0 ? 0 : input_tag_diff.invert();
551
552 bool is_last = (i == event.input.size() - 1);
553 trace.set(row + i,
554 { {
555 { C::sha256_sel, 1 },
556 // Propagated Fields
557 { C::sha256_execution_clk, event.execution_clk },
558 { C::sha256_space_id, event.space_id },
559 { C::sha256_output_addr, output_addr },
560 { C::sha256_sel_is_input_round, 1 },
561 { C::sha256_u32_tag, TAG_U32_AS_FF },
562 { C::sha256_sel_read_input_from_memory, 1 },
563 // Input Rounds Control Flow
564 { C::sha256_input_rounds_rem, input_rounds_rem },
565 { C::sha256_input_rounds_rem_inv, input_rounds_rem_inv },
566 { C::sha256_input_addr, input_addr + i },
567 { C::sha256_input, round_input.as_ff() },
568 { C::sha256_input_tag, input_tag },
569 { C::sha256_input_tag_diff_inv, input_tag_diff_inv },
570 // Set input value
571 { C::sha256_w, round_input.as_ff() },
572 // Error Columns
573 // Propagated tag error columns
574 { C::sha256_sel_invalid_input_tag_err, invalid_input_tag_err ? 1 : 0 },
575 // Invalid Row Tag Error Columns
576 { C::sha256_sel_invalid_input_row_tag_err, (is_last && invalid_input_tag_err) ? 1 : 0 },
577 { C::sha256_err, invalid_input_tag_err ? 1 : 0 },
578 { C::sha256_end, (is_last && invalid_input_tag_err) ? 1 : 0 },
579 } });
580 }
581
582 if (invalid_input_tag_err) {
583 // We need to increment the row counter for the next event (since we may have added rows for input loading)
584 row += static_cast<uint32_t>(event.input.size());
585 continue;
586 }
587
588 // If we get to this point, we are safe to proceed with the SHA-256 compression function
589 // and we won't encounter any more errors
590
592 // Execute SHA-256 Compression Function
594 std::array<uint32_t, 8> state;
595 std::ranges::transform(event.state.begin(), event.state.end(), state.begin(), [](const MemoryValue& val) {
596 return val.as<uint32_t>();
597 });
598
599 std::array<uint32_t, 16> prev_w_helpers;
600 std::ranges::transform(event.input.begin(),
601 event.input.end(),
602 prev_w_helpers.begin(),
603 [](const MemoryValue& val) { return val.as<uint32_t>(); });
604 std::array<uint32_t, 8> round_state = state;
605
606 // Each event results in 65 rows in the trace.
607 // 64 rows for the 64 rounds of the SHA-256 compression function
608 // 1 row for the final state
609
610 // Begin the rounds loop
611 for (size_t i = 0; i < 64; i++) {
612 // Detect if we are still using the inputs for values of w
613 bool is_an_input_round = i < 16;
614 // Used to check we non-zero rounds remaining
615 FF inv = PRECOMPUTED_INVERSES[64 - i];
616 uint32_t round_w =
617 is_an_input_round ? event.input[i].as<uint32_t>() : compute_w_with_witness(prev_w_helpers, trace);
618 // For input_addr: during input rounds (0-15), it increments by 1 each row.
619 // After input rounds (16-63), it stays constant at input_addr + 16.
620 // This satisfies CONTINUITY_INPUT_ADDR: input_addr' = input_addr + sel_is_input_round
621 uint64_t round_input_addr = is_an_input_round ? (input_addr + i) : (input_addr + 16);
622 trace.set(row,
623 { {
624 { C::sha256_sel, 1 },
625 // Propagated Fields
626 { C::sha256_execution_clk, event.execution_clk },
627 { C::sha256_space_id, event.space_id },
628 { C::sha256_output_addr, output_addr },
629 { C::sha256_input_addr, round_input_addr },
630 { C::sha256_u32_tag, TAG_U32_AS_FF },
631 { C::sha256_two_pow_32, static_cast<uint64_t>(1) << 32 },
632 // For round selectors
633 { C::sha256_xor_op_id, AVM_BITWISE_XOR_OP_ID },
634 { C::sha256_and_op_id, AVM_BITWISE_AND_OP_ID },
635 { C::sha256_perform_round, 1 },
636 { C::sha256_round_count, i },
637 { C::sha256_rounds_remaining, 64 - i },
638 { C::sha256_rounds_remaining_inv, inv },
639 { C::sha256_w, round_w },
640 { C::sha256_sel_compute_w, is_an_input_round ? 0 : 1 },
641 } });
642 // Set the init state columns - propagated down
644 // Set the state columns
645 set_state_cols(round_state, trace);
646 // Set the round columns
647 set_helper_cols(prev_w_helpers, trace);
648
649 // Apply SHA-256 compression function to the message schedule and update the state
650 round_state = compute_compression_with_witness(round_state, round_w, ROUND_CONSTANTS[i], trace);
651
652 // Update the prev_w_helpers, we shift all the values to the left and add the new round_w to
653 // the end
654 for (size_t j = 0; j < 15; j++) {
655 prev_w_helpers[j] = prev_w_helpers[j + 1];
656 }
657 prev_w_helpers[15] = round_w;
658
659 row++;
660 }
661
662 // Set the final row
663 // input_addr stays constant at input_addr + 16 (satisfies CONTINUITY_INPUT_ADDR from row 63)
664 trace.set(row,
665 { {
666 { C::sha256_sel, 1 },
667 { C::sha256_end, 1 },
668 { C::sha256_last, 1 },
669 { C::sha256_round_count, 64 },
670 { C::sha256_input_addr, input_addr + 16 },
671 } });
672
673 // Set the init state columns - propagated down
675 // Set the state column
676 set_state_cols(round_state, trace);
677 // Set the round columns
678 set_helper_cols(prev_w_helpers, trace);
679 // Compute the output from the final round state
680 compute_sha256_output(round_state, state, trace);
681
683 // Write output memory
685 trace.set(row,
686 { {
687 // Memory Fields
688 { C::sha256_execution_clk, event.execution_clk },
689 { C::sha256_space_id, event.space_id },
690 { C::sha256_sel_mem_state_or_output, 1 },
691 { C::sha256_rw, 1 }, // Writing output
692 { C::sha256_u32_tag, TAG_U32_AS_FF },
693 { C::sha256_two_pow_32, static_cast<uint64_t>(1) << 32 },
694 { C::sha256_output_addr, output_addr },
695 // Output Addresses
696 { C::sha256_memory_address_0_, output_addr },
697 { C::sha256_memory_address_1_, output_addr + 1 },
698 { C::sha256_memory_address_2_, output_addr + 2 },
699 { C::sha256_memory_address_3_, output_addr + 3 },
700 { C::sha256_memory_address_4_, output_addr + 4 },
701 { C::sha256_memory_address_5_, output_addr + 5 },
702 { C::sha256_memory_address_6_, output_addr + 6 },
703 { C::sha256_memory_address_7_, output_addr + 7 },
704 // Output Values
705 // The addition round_state[i] + state[i] is performed modulo 2^32.
706 { C::sha256_memory_register_0_, round_state[0] + state[0] },
707 { C::sha256_memory_register_1_, round_state[1] + state[1] },
708 { C::sha256_memory_register_2_, round_state[2] + state[2] },
709 { C::sha256_memory_register_3_, round_state[3] + state[3] },
710 { C::sha256_memory_register_4_, round_state[4] + state[4] },
711 { C::sha256_memory_register_5_, round_state[5] + state[5] },
712 { C::sha256_memory_register_6_, round_state[6] + state[6] },
713 { C::sha256_memory_register_7_, round_state[7] + state[7] },
714 // Output Memory Tags
715 { C::sha256_memory_tag_0_, TAG_U32_AS_FF },
716 { C::sha256_memory_tag_1_, TAG_U32_AS_FF },
717 { C::sha256_memory_tag_2_, TAG_U32_AS_FF },
718 { C::sha256_memory_tag_3_, TAG_U32_AS_FF },
719 { C::sha256_memory_tag_4_, TAG_U32_AS_FF },
720 { C::sha256_memory_tag_5_, TAG_U32_AS_FF },
721 { C::sha256_memory_tag_6_, TAG_U32_AS_FF },
722 { C::sha256_memory_tag_7_, TAG_U32_AS_FF },
723 } });
724
725 row++;
726 }
727}
728
732 // GT Interactions
733 .add<InteractionType::LookupGeneric, lookup_sha256_mem_check_state_addr_in_range_settings>(C::gt_sel)
735 .add<InteractionType::LookupGeneric, lookup_sha256_mem_check_output_addr_in_range_settings>(C::gt_sel)
736 // Bitwise operations
738 .add<InteractionType::LookupGeneric, lookup_sha256_w_s_0_xor_1_settings>(C::bitwise_start)
740 .add<InteractionType::LookupGeneric, lookup_sha256_w_s_1_xor_1_settings>(C::bitwise_start)
742 .add<InteractionType::LookupGeneric, lookup_sha256_s_1_xor_1_settings>(C::bitwise_start)
744 .add<InteractionType::LookupGeneric, lookup_sha256_ch_and_1_settings>(C::bitwise_start)
746 .add<InteractionType::LookupGeneric, lookup_sha256_s_0_xor_0_settings>(C::bitwise_start)
748 .add<InteractionType::LookupGeneric, lookup_sha256_maj_and_0_settings>(C::bitwise_start)
750 .add<InteractionType::LookupGeneric, lookup_sha256_maj_and_2_settings>(C::bitwise_start)
752 .add<InteractionType::LookupGeneric, lookup_sha256_maj_xor_1_settings>(C::bitwise_start)
753 // GT Checks for Rotations and Shifts
755 .add<InteractionType::LookupGeneric, lookup_sha256_range_rhs_w_18_settings>(C::gt_sel)
757 .add<InteractionType::LookupGeneric, lookup_sha256_range_rhs_w_17_settings>(C::gt_sel)
759 .add<InteractionType::LookupGeneric, lookup_sha256_range_rhs_w_10_settings>(C::gt_sel)
761 .add<InteractionType::LookupGeneric, lookup_sha256_range_rhs_e_11_settings>(C::gt_sel)
763 .add<InteractionType::LookupGeneric, lookup_sha256_range_rhs_a_2_settings>(C::gt_sel)
765 .add<InteractionType::LookupGeneric, lookup_sha256_range_rhs_a_22_settings>(C::gt_sel)
766 // GT Checks for modulo add (rhs only; lhs uses range-8 precomputed lookups or boolean constraints)
768 .add<InteractionType::LookupGeneric, lookup_sha256_range_comp_w_rhs_settings>(C::gt_sel)
770 .add<InteractionType::LookupGeneric, lookup_sha256_range_comp_next_a_rhs_settings>(C::gt_sel)
772 .add<InteractionType::LookupGeneric, lookup_sha256_range_comp_next_e_rhs_settings>(C::gt_sel)
773 // Output rhs GT checks (output lhs uses boolean constraints, no lookup needed)
775 .add<InteractionType::LookupGeneric, lookup_sha256_range_comp_b_rhs_settings>(C::gt_sel)
777 .add<InteractionType::LookupGeneric, lookup_sha256_range_comp_d_rhs_settings>(C::gt_sel)
779 .add<InteractionType::LookupGeneric, lookup_sha256_range_comp_f_rhs_settings>(C::gt_sel)
781 .add<InteractionType::LookupGeneric, lookup_sha256_range_comp_h_rhs_settings>(C::gt_sel);
782
783} // namespace bb::avm2::tracegen
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define AVM_BITWISE_AND_OP_ID
#define AVM_BITWISE_XOR_OP_ID
#define AVM_HIGHEST_MEM_ADDRESS
ValueTag get_tag() const
InteractionDefinition & add(auto &&... args)
void process(const simulation::EventEmitterInterface< simulation::Sha256CompressionEvent >::Container &events, TraceContainer &trace)
Process the SHA-256 compression events and populate the relevant columns in the trace.
uint32_t ror_with_witness(const uint32_t val, const uint8_t shift, Column col_result, Column col_rhs, TraceContainer &trace) const
Perform a 32-bit right rotation and insert the result and rhs limb into the trace.
void set_state_cols(const std::array< uint32_t, 8 > &state, TraceContainer &trace) const
Set the 8 round-state columns (a..h) at the current row.
void set_helper_cols(const std::array< uint32_t, 16 > &prev_w_helpers, TraceContainer &trace) const
Set the 16 message-schedule helper columns (w0..w15) at the current row.
uint32_t compute_w_with_witness(const std::array< uint32_t, 16 > &prev_w_helpers, TraceContainer &trace) const
Compute the message schedule word w[j] for a non-input round and insert witness data into the trace.
static const InteractionDefinition interactions
uint32_t shr_with_witness(const uint32_t val, const uint8_t shift, Column col_lhs, Column col_rhs, TraceContainer &trace) const
Perform a 32-bit right shift and insert the limb decomposition into the trace.
void compute_sha256_output(const std::array< uint32_t, 8 > &out_state, const std::array< uint32_t, 8 > &init_state, TraceContainer &trace) const
Compute the final SHA-256 output (init_state + final_round_state mod 2^32) and write to the trace.
void into_limbs_with_witness(const uint64_t, const uint8_t b, Column col_lhs, Column col_rhs, TraceContainer &trace) const
Decompose a value into high and low limbs at a given bit position and write them to the trace.
void set_init_state_cols(const std::array< uint32_t, 8 > &init_state, TraceContainer &trace) const
Set the 8 initial-state columns (init_a..init_h) at the current row.
std::array< uint32_t, 8 > compute_compression_with_witness(const std::array< uint32_t, 8 > &state, uint32_t round_w, uint32_t round_constant, TraceContainer &trace) const
Perform one round of the SHA-256 compression function and insert all witness data into the trace.
TestTraceContainer trace
FF a
FF b
const auto init
Definition fr.bench.cpp:135
AvmFlavorSettings::FF FF
Definition field.hpp:10
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
simulation::PublicDataTreeReadWriteEvent event
Settings to be passed ot GenericLookupRelationImpl.
constexpr field invert() const noexcept
static void batch_invert(C &coeffs) noexcept
Batch invert a collection of field elements using Montgomery's trick.