1use midnight_proofs::{
42 circuit::{Chip, Layouter, Region, Value},
43 plonk::{
44 Advice, Column, ConstraintSystem, Constraints, Error, Expression, Fixed, Selector,
45 TableColumn,
46 },
47 poly::Rotation,
48};
49use num_integer::Integer;
50
51use crate::{
52 field::{decomposition::chip::P2RDecompositionChip, NativeChip, NativeGadget},
53 hash::sha256::{
54 types::{
55 AssignedMessageWord, AssignedPlain, AssignedPlainSpreaded, AssignedSpreaded,
56 CompressionState, LimbsOfA, LimbsOfE,
57 },
58 utils::{
59 expr_pow2_ip, expr_pow4_ip, gen_spread_table, get_even_and_odd_bits, negate_spreaded,
60 spread, spreaded_Sigma_0, spreaded_Sigma_1, spreaded_maj, spreaded_sigma_0,
61 spreaded_sigma_1, u32_in_be_limbs, MASK_EVN_64,
62 },
63 },
64 instructions::{assignments::AssignmentInstructions, DecompositionInstructions},
65 types::{AssignedByte, AssignedNative},
66 utils::{
67 util::{fe_to_u32, fe_to_u64, u32_to_fe, u64_to_fe},
68 ComposableChip,
69 },
70 CircuitField,
71};
72
73pub const NB_SHA256_ADVICE_COLS: usize = 8;
75
76pub const NB_SHA256_FIXED_COLS: usize = 2;
78
79pub(super) const ROUND_CONSTANTS: [u32; 64] = [
80 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
81 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
82 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
83 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
84 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
85 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
86 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
87 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
88];
89
90pub(super) const IV: [u32; 8] = [
91 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
92];
93
94enum Parity {
96 Evn,
97 Odd,
98}
99
100#[derive(Clone, Debug)]
102struct SpreadTable {
103 nbits_col: TableColumn,
104 plain_col: TableColumn,
105 sprdd_col: TableColumn,
106}
107
108#[derive(Clone, Debug)]
110pub struct Sha256Config {
111 advice_cols: [Column<Advice>; NB_SHA256_ADVICE_COLS],
112 fixed_cols: [Column<Fixed>; NB_SHA256_FIXED_COLS],
113
114 q_lookup: Selector,
115 table: SpreadTable,
116
117 q_maj: Selector,
118 q_half_ch: Selector,
119 q_Sigma_0: Selector,
120 q_Sigma_1: Selector,
121 q_sigma_0: Selector,
122 q_sigma_1: Selector,
123
124 q_11_11_10: Selector,
125 q_10_9_11_2: Selector,
126 q_7_12_2_5_6: Selector,
127 q_12_1x3_7_3_4_3: Selector,
128 q_add_mod_2_32: Selector,
129}
130
131#[derive(Clone, Debug)]
133pub struct Sha256Chip<F: CircuitField> {
134 config: Sha256Config,
135 pub(super) native_gadget: NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>,
136}
137
138impl<F: CircuitField> Chip<F> for Sha256Chip<F> {
139 type Config = Sha256Config;
140 type Loaded = ();
141
142 fn config(&self) -> &Self::Config {
143 &self.config
144 }
145
146 fn loaded(&self) -> &Self::Loaded {
147 &()
148 }
149}
150
151impl<F: CircuitField> ComposableChip<F> for Sha256Chip<F> {
152 type SharedResources = (
153 [Column<Advice>; NB_SHA256_ADVICE_COLS],
154 [Column<Fixed>; NB_SHA256_FIXED_COLS],
155 );
156
157 type InstructionDeps = NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>;
158
159 fn new(config: &Sha256Config, native_gadget: &Self::InstructionDeps) -> Self {
160 Self {
161 config: config.clone(),
162 native_gadget: native_gadget.clone(),
163 }
164 }
165
166 fn configure(
167 meta: &mut ConstraintSystem<F>,
168 shared_res: &Self::SharedResources,
169 ) -> Sha256Config {
170 let fixed_cols = shared_res.1;
171
172 let advice_cols = [6, 0, 7, 1, 2, 3, 4, 5].map(|i| shared_res.0[i]);
177 for (i, column) in advice_cols.iter().enumerate() {
178 if i != 0 && i != 2 {
179 meta.enable_equality(*column);
180 }
181 }
182
183 let q_lookup = meta.complex_selector();
184 let table = SpreadTable {
185 nbits_col: meta.lookup_table_column(),
186 plain_col: meta.lookup_table_column(),
187 sprdd_col: meta.lookup_table_column(),
188 };
189
190 let q_maj = meta.selector();
191 let q_half_ch = meta.selector();
192 let q_Sigma_0 = meta.selector();
193 let q_Sigma_1 = meta.selector();
194 let q_sigma_0 = meta.selector();
195 let q_sigma_1 = meta.selector();
196
197 let q_11_11_10 = meta.selector();
198 let q_10_9_11_2 = meta.selector();
199 let q_7_12_2_5_6 = meta.selector();
200 let q_12_1x3_7_3_4_3 = meta.selector();
201 let q_add_mod_2_32 = meta.selector();
202
203 (0..2).for_each(|idx| {
204 meta.lookup("plain-spreaded lookup", |meta| {
205 let q_lookup = meta.query_selector(q_lookup);
206
207 let nbits = meta.query_fixed(fixed_cols[idx], Rotation(0));
208 let plain = meta.query_advice(advice_cols[2 * idx], Rotation(0));
209 let sprdd = meta.query_advice(advice_cols[2 * idx + 1], Rotation(0));
210
211 vec![
212 (q_lookup.clone() * nbits, table.nbits_col),
213 (q_lookup.clone() * plain, table.plain_col),
214 (q_lookup * sprdd, table.sprdd_col),
215 ]
216 });
217 });
218
219 meta.create_gate("Maj(A, B, C)", |meta| {
220 let sA = meta.query_advice(advice_cols[5], Rotation(-1));
222 let sB = meta.query_advice(advice_cols[6], Rotation(-1));
223 let sC = meta.query_advice(advice_cols[5], Rotation(0));
224 let s_odd_11a = meta.query_advice(advice_cols[1], Rotation(-1));
225 let s_odd_11b = meta.query_advice(advice_cols[1], Rotation(0));
226 let s_odd_010 = meta.query_advice(advice_cols[1], Rotation(1));
227 let s_evn_11a = meta.query_advice(advice_cols[3], Rotation(-1));
228 let s_evn_11b = meta.query_advice(advice_cols[3], Rotation(0));
229 let s_evn_010 = meta.query_advice(advice_cols[3], Rotation(1));
230
231 let s_evn = expr_pow4_ip([21, 10, 0], [&s_evn_11a, &s_evn_11b, &s_evn_010]);
232 let s_odd = expr_pow4_ip([21, 10, 0], [&s_odd_11a, &s_odd_11b, &s_odd_010]);
233
234 let id = (sA + sB + sC) - (s_evn + Expression::from(2) * s_odd);
235
236 Constraints::with_selector(q_maj, vec![("Maj", id)])
237 });
238
239 meta.create_gate("half Ch(E, F, G)", |meta| {
240 let sX = meta.query_advice(advice_cols[5], Rotation(-1));
242 let sY = meta.query_advice(advice_cols[6], Rotation(-1));
243 let s_odd_11a = meta.query_advice(advice_cols[1], Rotation(-1));
244 let s_odd_11b = meta.query_advice(advice_cols[1], Rotation(0));
245 let s_odd_010 = meta.query_advice(advice_cols[1], Rotation(1));
246 let s_evn_11a = meta.query_advice(advice_cols[3], Rotation(-1));
247 let s_evn_11b = meta.query_advice(advice_cols[3], Rotation(0));
248 let s_evn_010 = meta.query_advice(advice_cols[3], Rotation(1));
249 let summand_1 = meta.query_advice(advice_cols[4], Rotation(0));
250 let summand_2 = meta.query_advice(advice_cols[5], Rotation(0));
251 let sum = meta.query_advice(advice_cols[6], Rotation(0));
252
253 let s_evn = expr_pow4_ip([21, 10, 0], [&s_evn_11a, &s_evn_11b, &s_evn_010]);
254 let s_odd = expr_pow4_ip([21, 10, 0], [&s_odd_11a, &s_odd_11b, &s_odd_010]);
255
256 let sprdd_id = (sX + sY) - (s_evn + Expression::from(2) * s_odd);
257 let sum_id = (summand_1 + summand_2) - sum;
258
259 Constraints::with_selector(
260 q_half_ch,
261 vec![
262 ("Half-Ch spreadded", sprdd_id),
263 ("Half Ch sum (2 terms)", sum_id),
264 ],
265 )
266 });
267
268 meta.create_gate("Σ₀(A)", |meta| {
269 let s10 = meta.query_advice(advice_cols[5], Rotation(-1));
271 let s09 = meta.query_advice(advice_cols[6], Rotation(-1));
272 let s11 = meta.query_advice(advice_cols[5], Rotation(0));
273 let s02 = meta.query_advice(advice_cols[6], Rotation(0));
274 let s_evn_11a = meta.query_advice(advice_cols[1], Rotation(-1));
275 let s_evn_11b = meta.query_advice(advice_cols[1], Rotation(0));
276 let s_evn_010 = meta.query_advice(advice_cols[1], Rotation(1));
277 let s_odd_11a = meta.query_advice(advice_cols[3], Rotation(-1));
278 let s_odd_11b = meta.query_advice(advice_cols[3], Rotation(0));
279 let s_odd_010 = meta.query_advice(advice_cols[3], Rotation(1));
280
281 let s_1st_rot = expr_pow4_ip([30, 20, 11, 0], [&s02, &s10, &s09, &s11]);
282 let s_2nd_rot = expr_pow4_ip([21, 19, 9, 0], [&s11, &s02, &s10, &s09]);
283 let s_3rd_rot = expr_pow4_ip([23, 12, 10, 0], [&s09, &s11, &s02, &s10]);
284
285 let s_evn = expr_pow4_ip([21, 10, 0], [&s_evn_11a, &s_evn_11b, &s_evn_010]);
286 let s_odd = expr_pow4_ip([21, 10, 0], [&s_odd_11a, &s_odd_11b, &s_odd_010]);
287
288 let id = (s_1st_rot + s_2nd_rot + s_3rd_rot) - (s_evn + Expression::from(2) * s_odd);
289
290 Constraints::with_selector(q_Sigma_0, vec![("Sigma_0", id)])
291 });
292
293 meta.create_gate("Σ₁(E)", |meta| {
294 let s07 = meta.query_advice(advice_cols[5], Rotation(-1));
296 let s12 = meta.query_advice(advice_cols[6], Rotation(-1));
297 let s02 = meta.query_advice(advice_cols[5], Rotation(0));
298 let s05 = meta.query_advice(advice_cols[6], Rotation(0));
299 let s06 = meta.query_advice(advice_cols[5], Rotation(1));
300 let s_evn_11a = meta.query_advice(advice_cols[1], Rotation(-1));
301 let s_evn_11b = meta.query_advice(advice_cols[1], Rotation(0));
302 let s_evn_10 = meta.query_advice(advice_cols[1], Rotation(1));
303 let s_odd_11a = meta.query_advice(advice_cols[3], Rotation(-1));
304 let s_odd_11b = meta.query_advice(advice_cols[3], Rotation(0));
305 let s_odd_10 = meta.query_advice(advice_cols[3], Rotation(1));
306
307 let s_1st_rot = expr_pow4_ip([26, 19, 7, 5, 0], [&s06, &s07, &s12, &s02, &s05]);
308 let s_2nd_rot = expr_pow4_ip([27, 21, 14, 2, 0], [&s05, &s06, &s07, &s12, &s02]);
309 let s_3rd_rot = expr_pow4_ip([20, 18, 13, 7, 0], [&s12, &s02, &s05, &s06, &s07]);
310
311 let s_evn = expr_pow4_ip([21, 10, 0], [&s_evn_11a, &s_evn_11b, &s_evn_10]);
312 let s_odd = expr_pow4_ip([21, 10, 0], [&s_odd_11a, &s_odd_11b, &s_odd_10]);
313
314 let id = (s_1st_rot + s_2nd_rot + s_3rd_rot) - (s_evn + Expression::from(2) * s_odd);
315
316 Constraints::with_selector(q_Sigma_1, vec![("Sigma_1", id)])
317 });
318
319 meta.create_gate("σ₀(W)", |meta| {
320 let s12 = meta.query_advice(advice_cols[5], Rotation(-1));
322 let s1a = meta.query_advice(advice_cols[6], Rotation(-1));
323 let s1b = meta.query_advice(advice_cols[4], Rotation(0));
324 let s1c = meta.query_advice(advice_cols[5], Rotation(0));
325 let s07 = meta.query_advice(advice_cols[6], Rotation(0));
326 let s3a = meta.query_advice(advice_cols[4], Rotation(1));
327 let s04 = meta.query_advice(advice_cols[5], Rotation(1));
328 let s3b = meta.query_advice(advice_cols[6], Rotation(1));
329 let s_evn_11a = meta.query_advice(advice_cols[1], Rotation(-1));
330 let s_evn_11b = meta.query_advice(advice_cols[1], Rotation(0));
331 let s_evn_10 = meta.query_advice(advice_cols[1], Rotation(1));
332 let s_odd_11a = meta.query_advice(advice_cols[3], Rotation(-1));
333 let s_odd_11b = meta.query_advice(advice_cols[3], Rotation(0));
334 let s_odd_10 = meta.query_advice(advice_cols[3], Rotation(1));
335
336 let sprdd_1st_shift = expr_pow4_ip(
337 [17, 16, 15, 14, 7, 4, 0],
338 [&s12, &s1a, &s1b, &s1c, &s07, &s3a, &s04],
339 );
340 let sprdd_2nd_rot = expr_pow4_ip(
341 [28, 25, 13, 12, 11, 10, 3, 0],
342 [&s04, &s3b, &s12, &s1a, &s1b, &s1c, &s07, &s3a],
343 );
344 let sprdd_3rd_rot = expr_pow4_ip(
345 [31, 24, 21, 17, 14, 2, 1, 0],
346 [&s1c, &s07, &s3a, &s04, &s3b, &s12, &s1a, &s1b],
347 );
348
349 let sprdd_evn = expr_pow4_ip([21, 10, 0], [&s_evn_11a, &s_evn_11b, &s_evn_10]);
350 let sprdd_odd = expr_pow4_ip([21, 10, 0], [&s_odd_11a, &s_odd_11b, &s_odd_10]);
351
352 let id = (sprdd_1st_shift + sprdd_2nd_rot + sprdd_3rd_rot)
353 - (sprdd_evn + Expression::from(2) * sprdd_odd);
354
355 Constraints::with_selector(q_sigma_0, vec![("sigma_0", id)])
356 });
357
358 meta.create_gate("σ₁(W)", |meta| {
359 let s12 = meta.query_advice(advice_cols[5], Rotation(-1));
361 let s1a = meta.query_advice(advice_cols[6], Rotation(-1));
362 let s1b = meta.query_advice(advice_cols[4], Rotation(0));
363 let s1c = meta.query_advice(advice_cols[5], Rotation(0));
364 let s07 = meta.query_advice(advice_cols[6], Rotation(0));
365 let s3a = meta.query_advice(advice_cols[4], Rotation(1));
366 let s04 = meta.query_advice(advice_cols[5], Rotation(1));
367 let s3b = meta.query_advice(advice_cols[6], Rotation(1));
368 let s_evn_11a = meta.query_advice(advice_cols[1], Rotation(-1));
369 let s_evn_11b = meta.query_advice(advice_cols[1], Rotation(0));
370 let s_evn_10 = meta.query_advice(advice_cols[1], Rotation(1));
371 let s_odd_11a = meta.query_advice(advice_cols[3], Rotation(-1));
372 let s_odd_11b = meta.query_advice(advice_cols[3], Rotation(0));
373 let s_odd_10 = meta.query_advice(advice_cols[3], Rotation(1));
374
375 let sprdd_1st_shift = expr_pow4_ip([10, 9, 8, 7, 0], [&s12, &s1a, &s1b, &s1c, &s07]);
376 let sprdd_2nd_rot = expr_pow4_ip(
377 [25, 22, 18, 15, 3, 2, 1, 0],
378 [&s07, &s3a, &s04, &s3b, &s12, &s1a, &s1b, &s1c],
379 );
380 let sprdd_3rd_rot = expr_pow4_ip(
381 [31, 30, 23, 20, 16, 13, 1, 0],
382 [&s1b, &s1c, &s07, &s3a, &s04, &s3b, &s12, &s1a],
383 );
384
385 let sprdd_evn = expr_pow4_ip([21, 10, 0], [&s_evn_11a, &s_evn_11b, &s_evn_10]);
386 let sprdd_odd = expr_pow4_ip([21, 10, 0], [&s_odd_11a, &s_odd_11b, &s_odd_10]);
387
388 let id = (sprdd_1st_shift + sprdd_2nd_rot + sprdd_3rd_rot)
389 - (sprdd_evn + Expression::from(2) * sprdd_odd);
390
391 Constraints::with_selector(q_sigma_1, vec![("sigma_1", id)])
392 });
393
394 meta.create_gate("11-11-10 decomposition", |meta| {
395 let p11a = meta.query_advice(advice_cols[0], Rotation(-1));
398 let p11b = meta.query_advice(advice_cols[0], Rotation(0));
399 let p_10 = meta.query_advice(advice_cols[0], Rotation(1));
400 let output = meta.query_advice(advice_cols[4], Rotation(-1));
401
402 let id = expr_pow2_ip([21, 10, 0], [&p11a, &p11b, &p_10]) - output;
403
404 Constraints::with_selector(q_11_11_10, vec![("11-11-10 decomposition", id)])
405 });
406
407 meta.create_gate("10-9-11-2 decomposition", |meta| {
408 let p10 = meta.query_advice(advice_cols[0], Rotation(-1));
410 let p09 = meta.query_advice(advice_cols[2], Rotation(-1));
411 let p11 = meta.query_advice(advice_cols[0], Rotation(0));
412 let p02 = meta.query_advice(advice_cols[2], Rotation(0));
413 let s10 = meta.query_advice(advice_cols[1], Rotation(-1));
414 let s09 = meta.query_advice(advice_cols[3], Rotation(-1));
415 let s11 = meta.query_advice(advice_cols[1], Rotation(0));
416 let s02 = meta.query_advice(advice_cols[3], Rotation(0));
417 let plain = meta.query_advice(advice_cols[4], Rotation(-1));
418 let sprdd = meta.query_advice(advice_cols[4], Rotation(0));
419
420 let plain_id = expr_pow2_ip([22, 13, 2, 0], [&p10, &p09, &p11, &p02]) - plain;
421 let sprdd_id = expr_pow4_ip([22, 13, 2, 0], [&s10, &s09, &s11, &s02]) - sprdd;
422
423 Constraints::with_selector(
424 q_10_9_11_2,
425 vec![
426 ("10_9_11_2 decomposition plain", plain_id),
427 ("10_9_11_2 decomposition sprdd", sprdd_id),
428 ],
429 )
430 });
431
432 meta.create_gate("7-12-2-5-6 decomposition", |meta| {
433 let p07 = meta.query_advice(advice_cols[0], Rotation(-1));
435 let p12 = meta.query_advice(advice_cols[2], Rotation(-1));
436 let p02 = meta.query_advice(advice_cols[0], Rotation(0));
437 let p05 = meta.query_advice(advice_cols[2], Rotation(0));
438 let p06 = meta.query_advice(advice_cols[0], Rotation(1));
439 let s07 = meta.query_advice(advice_cols[1], Rotation(-1));
440 let s12 = meta.query_advice(advice_cols[3], Rotation(-1));
441 let s02 = meta.query_advice(advice_cols[1], Rotation(0));
442 let s05 = meta.query_advice(advice_cols[3], Rotation(0));
443 let s06 = meta.query_advice(advice_cols[1], Rotation(1));
444 let plain = meta.query_advice(advice_cols[4], Rotation(-1));
445 let sprdd = meta.query_advice(advice_cols[4], Rotation(0));
446
447 let plain_id = expr_pow2_ip([25, 13, 11, 6, 0], [&p07, &p12, &p02, &p05, &p06]) - plain;
448 let sprdd_id = expr_pow4_ip([25, 13, 11, 6, 0], [&s07, &s12, &s02, &s05, &s06]) - sprdd;
449
450 Constraints::with_selector(
451 q_7_12_2_5_6,
452 vec![
453 ("7_12_2_5_6 decomposition plain", plain_id),
454 ("7_12_2_5_6 decomposition sprdd", sprdd_id),
455 ],
456 )
457 });
458
459 meta.create_gate("12-1x3-7-3-4-3 decomposition", |meta| {
460 let w12 = meta.query_advice(advice_cols[0], Rotation(-1));
463 let w07 = meta.query_advice(advice_cols[2], Rotation(-1));
464 let w3a = meta.query_advice(advice_cols[0], Rotation(0));
465 let w04 = meta.query_advice(advice_cols[2], Rotation(0));
466 let w3b = meta.query_advice(advice_cols[0], Rotation(1));
467 let w1a = meta.query_advice(advice_cols[7], Rotation(-1));
468 let w1b = meta.query_advice(advice_cols[7], Rotation(0));
469 let w1c = meta.query_advice(advice_cols[7], Rotation(1));
470 let plain = meta.query_advice(advice_cols[4], Rotation(-1));
471
472 let plain_id = expr_pow2_ip(
473 [20, 19, 18, 17, 10, 7, 3, 0],
474 [&w12, &w1a, &w1b, &w1c, &w07, &w3a, &w04, &w3b],
475 ) - plain;
476
477 let w_1a_check = w1a.clone() * (w1a - Expression::from(1));
479 let w_1b_check = w1b.clone() * (w1b - Expression::from(1));
480 let w_1c_check = w1c.clone() * (w1c - Expression::from(1));
481
482 Constraints::with_selector(
483 q_12_1x3_7_3_4_3,
484 vec![
485 ("12_1x3_7_3_4_3 decomposition ", plain_id),
486 ("W.1a 1-bit check", w_1a_check),
487 ("W.1b 1-bit check", w_1b_check),
488 ("W.1c 1-bit check", w_1c_check),
489 ],
490 )
491 });
492
493 meta.create_gate("add mod 2^32", |meta| {
494 let s0 = meta.query_advice(advice_cols[5], Rotation(-1));
496 let s1 = meta.query_advice(advice_cols[6], Rotation(-1));
497 let s2 = meta.query_advice(advice_cols[5], Rotation(0));
498 let s3 = meta.query_advice(advice_cols[6], Rotation(0));
499 let s4 = meta.query_advice(advice_cols[4], Rotation(1));
500 let s5 = meta.query_advice(advice_cols[5], Rotation(1));
501 let s6 = meta.query_advice(advice_cols[6], Rotation(1));
502
503 let carry = meta.query_advice(advice_cols[2], Rotation(1));
504 let result = meta.query_advice(advice_cols[4], Rotation(-1));
505
506 let summands = [s0, s1, s2, s3, s4, s5, s6];
507 let lhs = summands.into_iter().reduce(|acc, x| acc + x).unwrap();
508 let rhs = result + carry * Expression::Constant(F::from(1u64 << 32));
509
510 Constraints::with_selector(q_add_mod_2_32, vec![("add_mod_2_32", lhs - rhs)])
511 });
512
513 Sha256Config {
514 advice_cols,
515 fixed_cols,
516
517 q_lookup,
518 table,
519
520 q_maj,
521 q_half_ch,
522 q_Sigma_0,
523 q_Sigma_1,
524 q_sigma_0,
525 q_sigma_1,
526
527 q_11_11_10,
528 q_10_9_11_2,
529 q_7_12_2_5_6,
530 q_12_1x3_7_3_4_3,
531 q_add_mod_2_32,
532 }
533 }
534
535 fn load(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
536 let SpreadTable {
537 nbits_col,
538 plain_col,
539 sprdd_col,
540 } = self.config().table;
541
542 layouter.assign_table(
543 || "spread table",
544 |mut table| {
545 for (index, triple) in gen_spread_table::<F>().enumerate() {
546 table.assign_cell(|| "nbits", nbits_col, index, || Value::known(triple.0))?;
547 table.assign_cell(|| "plain", plain_col, index, || Value::known(triple.1))?;
548 table.assign_cell(|| "sprdd", sprdd_col, index, || Value::known(triple.2))?;
549 }
550 Ok(())
551 },
552 )
553 }
554}
555
556impl<F: CircuitField> Sha256Chip<F> {
557 pub(super) fn sha256(
559 &self,
560 layouter: &mut impl Layouter<F>,
561 input_bytes: &[AssignedByte<F>],
562 ) -> Result<[AssignedPlain<F, 32>; 8], Error> {
563 let mut state = CompressionState::<F>::fixed(layouter, &self.native_gadget, IV)?;
564
565 for block_bytes in self.pad(layouter, input_bytes)?.chunks(64) {
566 let block = self.block_from_bytes(layouter, block_bytes.try_into().unwrap())?;
567 let message_blocks = self.message_schedule(layouter, &block)?;
568 let mut compression_state = state.clone();
569 for i in 0..64 {
570 compression_state = self.compression_round(
571 layouter,
572 &compression_state,
573 ROUND_CONSTANTS[i],
574 &message_blocks[i],
575 )?;
576 }
577 state = state.add(self, layouter, &compression_state)?;
578 }
579
580 Ok(state.plain())
581 }
582
583 fn pad(
585 &self,
586 layouter: &mut impl Layouter<F>,
587 bytes: &[AssignedByte<F>],
588 ) -> Result<Vec<AssignedByte<F>>, Error> {
589 let l = 8 * bytes.len();
590 let k = 512 - (l + 65) % 512;
591
592 let mut padded = bytes.to_vec();
593 padded.push(self.native_gadget.assign_fixed(layouter, 128u8)?); padded.extend(vec![self.native_gadget.assign_fixed(layouter, 0u8)?; k / 8]);
595 for byte in u64::to_be_bytes(l as u64) {
596 padded.push(self.native_gadget.assign_fixed(layouter, byte)?);
597 }
598
599 Ok(padded)
600 }
601
602 pub(super) fn block_from_bytes(
606 &self,
607 layouter: &mut impl Layouter<F>,
608 bytes: &[AssignedByte<F>; 64],
609 ) -> Result<[AssignedPlain<F, 32>; 16], Error> {
610 Ok(bytes
611 .chunks(4)
612 .map(|word_bytes| {
613 self.native_gadget
614 .assigned_from_be_bytes(layouter, word_bytes)
615 .map(AssignedPlain)
616 })
617 .collect::<Result<Vec<_>, Error>>()?
618 .try_into()
619 .unwrap())
620 }
621
622 pub(super) fn message_schedule(
626 &self,
627 layouter: &mut impl Layouter<F>,
628 block: &[AssignedPlain<F, 32>; 16],
629 ) -> Result<[AssignedPlain<F, 32>; 64], Error> {
630 let message_word = self.prepare_message_word(layouter, &[block[0].clone()])?;
631 let mut message_words: [AssignedMessageWord<F>; 64] =
632 core::array::from_fn(|_| message_word.clone());
633
634 for word_idx in 1..16 {
637 message_words[word_idx] =
638 self.prepare_message_word(layouter, &[block[word_idx].clone()])?;
639 }
640 for word_idx in 16..64 {
644 let sigma0_w_i_minus_15 = &self.sigma_0(layouter, &message_words[word_idx - 15])?;
645 let sigma1_w_i_minus_2 = &self.sigma_1(layouter, &message_words[word_idx - 2])?;
646 message_words[word_idx] = self.prepare_message_word(
647 layouter,
648 &[
649 message_words[word_idx - 16].combined_plain.clone(),
650 message_words[word_idx - 7].combined_plain.clone(),
651 sigma0_w_i_minus_15.clone(),
652 sigma1_w_i_minus_2.clone(),
653 ],
654 )?;
655 }
656
657 Ok(message_words.map(|w| w.combined_plain))
658 }
659
660 pub(super) fn compression_round(
662 &self,
663 layouter: &mut impl Layouter<F>,
664 state: &CompressionState<F>,
665 round_k: u32,
666 round_w: &AssignedPlain<F, 32>,
667 ) -> Result<CompressionState<F>, Error> {
668 let round_k = AssignedPlain::<F, 32>::fixed(layouter, &self.native_gadget, round_k)?;
669
670 let Sigma_0_of_a = self.Sigma_0(layouter, &state.a)?;
671 let Maj_of_a_b_c = self.maj(
672 layouter,
673 &state.a.combined.spreaded,
674 &state.b.spreaded,
675 &state.c.spreaded,
676 )?;
677 let Sigma_1_of_e = self.Sigma_1(layouter, &state.e)?;
678 let Ch_of_e_f_g = self.ch(
679 layouter,
680 &state.e.combined.spreaded,
681 &state.f.spreaded,
682 &state.g.spreaded,
683 )?;
684
685 let next_a_summands = [
686 state.h.clone(),
687 Sigma_1_of_e.clone(),
688 Ch_of_e_f_g.clone(),
689 round_k.clone(),
690 round_w.clone(),
691 Sigma_0_of_a,
692 Maj_of_a_b_c,
693 ];
694
695 let next_e_summands = [
696 state.d.clone(),
697 state.h.clone(),
698 Sigma_1_of_e,
699 Ch_of_e_f_g,
700 round_k.clone(),
701 round_w.clone(),
702 ];
703
704 Ok(CompressionState {
705 a: self.prepare_A(layouter, &next_a_summands)?,
706 b: state.a.combined.clone(),
707 c: state.b.clone(),
708 d: state.c.plain.clone(),
709 e: self.prepare_E(layouter, &next_e_summands)?,
710 f: state.e.combined.clone(),
711 g: state.f.clone(),
712 h: state.g.plain.clone(),
713 })
714 }
715
716 fn maj(
718 &self,
719 layouter: &mut impl Layouter<F>,
720 sprdd_a: &AssignedSpreaded<F, 32>,
721 sprdd_b: &AssignedSpreaded<F, 32>,
722 sprdd_c: &AssignedSpreaded<F, 32>,
723 ) -> Result<AssignedPlain<F, 32>, Error> {
724 let adv_cols = self.config().advice_cols;
760
761 layouter.assign_region(
762 || "Maj(A, B, C)",
763 |mut region| {
764 self.config().q_maj.enable(&mut region, 1)?;
765
766 sprdd_a.0.copy_advice(|| "~A", &mut region, adv_cols[5], 0)?;
767 sprdd_b.0.copy_advice(|| "~B", &mut region, adv_cols[6], 0)?;
768 sprdd_c.0.copy_advice(|| "~C", &mut region, adv_cols[5], 1)?;
769
770 let val_of_sprdd_forms: Value<[u64; 3]> = Value::from_iter([
771 sprdd_a.0.value().copied().map(fe_to_u64),
772 sprdd_b.0.value().copied().map(fe_to_u64),
773 sprdd_c.0.value().copied().map(fe_to_u64),
774 ])
775 .map(|sprdd_forms: Vec<u64>| sprdd_forms.try_into().unwrap());
776
777 self.assign_sprdd_11_11_10(
778 &mut region,
779 val_of_sprdd_forms.map(spreaded_maj),
780 Parity::Odd,
781 0,
782 )
783 },
784 )
785 }
786
787 fn ch(
789 &self,
790 layouter: &mut impl Layouter<F>,
791 sprdd_E: &AssignedSpreaded<F, 32>,
792 sprdd_F: &AssignedSpreaded<F, 32>,
793 sprdd_G: &AssignedSpreaded<F, 32>,
794 ) -> Result<AssignedPlain<F, 32>, Error> {
795 let adv_cols = self.config().advice_cols;
844
845 let sprdd_E_val = sprdd_E.0.value().copied().map(fe_to_u64);
846 let sprdd_F_val = sprdd_F.0.value().copied().map(fe_to_u64);
847 let sprdd_G_val = sprdd_G.0.value().copied().map(fe_to_u64);
848 let sprdd_nE_val = sprdd_E_val.map(negate_spreaded);
849
850 let EpF_val = sprdd_E_val + sprdd_F_val;
851 let nEpG_val = sprdd_nE_val + sprdd_G_val;
852 let sprdd_nE_val: Value<F> = sprdd_nE_val.map(u64_to_fe);
853
854 let mask_evn_64: AssignedNative<F> =
855 self.native_gadget.assign_fixed(layouter, F::from(MASK_EVN_64))?;
856
857 layouter.assign_region(
858 || "Ch(E, F, G)",
859 |mut region| {
860 self.config().q_half_ch.enable(&mut region, 1)?;
861 self.config().q_half_ch.enable(&mut region, 4)?;
862
863 sprdd_E.0.copy_advice(|| "~E", &mut region, adv_cols[5], 0)?;
864 sprdd_E.0.copy_advice(|| "~E", &mut region, adv_cols[4], 4)?;
865
866 sprdd_F.0.copy_advice(|| "~F", &mut region, adv_cols[6], 0)?;
867 sprdd_G.0.copy_advice(|| "~G", &mut region, adv_cols[6], 3)?;
868
869 let sprdd_nE = region.assign_advice(|| "~(¬E)", adv_cols[5], 3, || sprdd_nE_val)?;
870 sprdd_nE.copy_advice(|| "~(¬E)", &mut region, adv_cols[5], 4)?;
871
872 mask_evn_64.copy_advice(|| "MASK_EVN_64", &mut region, adv_cols[6], 4)?;
873
874 let odd_EF = self.assign_sprdd_11_11_10(&mut region, EpF_val, Parity::Odd, 0)?;
875 odd_EF.0.copy_advice(|| "Odd_EF", &mut region, adv_cols[4], 1)?;
876
877 let odd_nEG = self.assign_sprdd_11_11_10(&mut region, nEpG_val, Parity::Odd, 3)?;
878 odd_nEG.0.copy_advice(|| "Odd_nEG", &mut region, adv_cols[5], 1)?;
879
880 let ret_val = odd_EF.0.value().copied() + odd_nEG.0.value().copied();
881 region
882 .assign_advice(|| "Ret", adv_cols[6], 1, || ret_val)
883 .map(AssignedPlain::<F, 32>)
884 },
885 )
886 }
887
888 fn Sigma_0(
890 &self,
891 layouter: &mut impl Layouter<F>,
892 a: &LimbsOfA<F>,
893 ) -> Result<AssignedPlain<F, 32>, Error> {
894 let adv_cols = self.config().advice_cols;
932
933 layouter.assign_region(
934 || "Σ₀(A)",
935 |mut region| {
936 self.config().q_Sigma_0.enable(&mut region, 1)?;
937
938 a.spreaded_limb_10.0.copy_advice(|| "~A.10", &mut region, adv_cols[5], 0)?;
940 a.spreaded_limb_09.0.copy_advice(|| "~A.09", &mut region, adv_cols[6], 0)?;
941 a.spreaded_limb_11.0.copy_advice(|| "~A.11", &mut region, adv_cols[5], 1)?;
942 a.spreaded_limb_02.0.copy_advice(|| "~A.02", &mut region, adv_cols[6], 1)?;
943
944 let val_of_sprdd_limbs: Value<[u64; 4]> = Value::from_iter([
949 a.spreaded_limb_10.0.value().copied().map(fe_to_u64),
950 a.spreaded_limb_09.0.value().copied().map(fe_to_u64),
951 a.spreaded_limb_11.0.value().copied().map(fe_to_u64),
952 a.spreaded_limb_02.0.value().copied().map(fe_to_u64),
953 ])
954 .map(|limbs: Vec<u64>| limbs.try_into().unwrap());
955
956 self.assign_sprdd_11_11_10(
957 &mut region,
958 val_of_sprdd_limbs.map(spreaded_Sigma_0),
959 Parity::Evn,
960 0,
961 )
962 },
963 )
964 }
965
966 fn Sigma_1(
968 &self,
969 layouter: &mut impl Layouter<F>,
970 e: &LimbsOfE<F>,
971 ) -> Result<AssignedPlain<F, 32>, Error> {
972 let adv_cols = self.config().advice_cols;
1010
1011 layouter.assign_region(
1012 || "Σ₁(E)",
1013 |mut region| {
1014 self.config().q_Sigma_1.enable(&mut region, 1)?;
1015
1016 e.spreaded_limb_07.0.copy_advice(|| "~E.07", &mut region, adv_cols[5], 0)?;
1018 e.spreaded_limb_12.0.copy_advice(|| "~E.12", &mut region, adv_cols[6], 0)?;
1019 e.spreaded_limb_02.0.copy_advice(|| "~E.02", &mut region, adv_cols[5], 1)?;
1020 e.spreaded_limb_05.0.copy_advice(|| "~E.05", &mut region, adv_cols[6], 1)?;
1021 e.spreaded_limb_06.0.copy_advice(|| "~E.06", &mut region, adv_cols[5], 2)?;
1022
1023 let val_of_sprdd_limbs: Value<[u64; 5]> = Value::from_iter([
1028 e.spreaded_limb_07.0.value().copied().map(fe_to_u64),
1029 e.spreaded_limb_12.0.value().copied().map(fe_to_u64),
1030 e.spreaded_limb_02.0.value().copied().map(fe_to_u64),
1031 e.spreaded_limb_05.0.value().copied().map(fe_to_u64),
1032 e.spreaded_limb_06.0.value().copied().map(fe_to_u64),
1033 ])
1034 .map(|limbs: Vec<u64>| limbs.try_into().unwrap());
1035
1036 self.assign_sprdd_11_11_10(
1037 &mut region,
1038 val_of_sprdd_limbs.map(spreaded_Sigma_1),
1039 Parity::Evn,
1040 0,
1041 )
1042 },
1043 )
1044 }
1045
1046 fn sigma_0(
1048 &self,
1049 layouter: &mut impl Layouter<F>,
1050 w: &AssignedMessageWord<F>,
1051 ) -> Result<AssignedPlain<F, 32>, Error> {
1052 let adv_cols = self.config().advice_cols;
1090
1091 layouter.assign_region(
1092 || "σ₀(W)",
1093 |mut region| {
1094 self.config().q_sigma_0.enable(&mut region, 1)?;
1095
1096 w.spreaded_w_12.0.copy_advice(|| "~W.12", &mut region, adv_cols[5], 0)?;
1097 w.spreaded_w_1a.0.copy_advice(|| "~W.1a", &mut region, adv_cols[6], 0)?;
1098 w.spreaded_w_1b.0.copy_advice(|| "~W.1b", &mut region, adv_cols[4], 1)?;
1099 w.spreaded_w_1c.0.copy_advice(|| "~W.1c", &mut region, adv_cols[5], 1)?;
1100 w.spreaded_w_07.0.copy_advice(|| "~W.07", &mut region, adv_cols[6], 1)?;
1101 w.spreaded_w_3a.0.copy_advice(|| "~W.3a", &mut region, adv_cols[4], 2)?;
1102 w.spreaded_w_04.0.copy_advice(|| "~W.04", &mut region, adv_cols[5], 2)?;
1103 w.spreaded_w_3b.0.copy_advice(|| "~W.3b", &mut region, adv_cols[6], 2)?;
1104
1105 let val_of_sprdd_limbs: Value<[u64; 8]> = Value::from_iter([
1106 w.spreaded_w_12.0.value().copied().map(fe_to_u64),
1107 w.spreaded_w_1a.0.value().copied().map(fe_to_u64),
1108 w.spreaded_w_1b.0.value().copied().map(fe_to_u64),
1109 w.spreaded_w_1c.0.value().copied().map(fe_to_u64),
1110 w.spreaded_w_07.0.value().copied().map(fe_to_u64),
1111 w.spreaded_w_3a.0.value().copied().map(fe_to_u64),
1112 w.spreaded_w_04.0.value().copied().map(fe_to_u64),
1113 w.spreaded_w_3b.0.value().copied().map(fe_to_u64),
1114 ])
1115 .map(|limbs: Vec<u64>| limbs.try_into().unwrap());
1116
1117 self.assign_sprdd_11_11_10(
1118 &mut region,
1119 val_of_sprdd_limbs.map(spreaded_sigma_0),
1120 Parity::Evn,
1121 0,
1122 )
1123 },
1124 )
1125 }
1126
1127 fn sigma_1(
1129 &self,
1130 layouter: &mut impl Layouter<F>,
1131 w: &AssignedMessageWord<F>,
1132 ) -> Result<AssignedPlain<F, 32>, Error> {
1133 let adv_cols = self.config().advice_cols;
1171
1172 layouter.assign_region(
1173 || "σ₁(W)",
1174 |mut region| {
1175 self.config().q_sigma_1.enable(&mut region, 1)?;
1176
1177 w.spreaded_w_12.0.copy_advice(|| "~W.12", &mut region, adv_cols[5], 0)?;
1178 w.spreaded_w_1a.0.copy_advice(|| "~W.1a", &mut region, adv_cols[6], 0)?;
1179 w.spreaded_w_1b.0.copy_advice(|| "~W.1b", &mut region, adv_cols[4], 1)?;
1180 w.spreaded_w_1c.0.copy_advice(|| "~W.1c", &mut region, adv_cols[5], 1)?;
1181 w.spreaded_w_07.0.copy_advice(|| "~W.07", &mut region, adv_cols[6], 1)?;
1182 w.spreaded_w_3a.0.copy_advice(|| "~W.3a", &mut region, adv_cols[4], 2)?;
1183 w.spreaded_w_04.0.copy_advice(|| "~W.04", &mut region, adv_cols[5], 2)?;
1184 w.spreaded_w_3b.0.copy_advice(|| "~W.3b", &mut region, adv_cols[6], 2)?;
1185
1186 let val_of_sprdd_limbs: Value<[u64; 8]> = Value::from_iter([
1187 w.spreaded_w_12.0.value().copied().map(fe_to_u64),
1188 w.spreaded_w_1a.0.value().copied().map(fe_to_u64),
1189 w.spreaded_w_1b.0.value().copied().map(fe_to_u64),
1190 w.spreaded_w_1c.0.value().copied().map(fe_to_u64),
1191 w.spreaded_w_07.0.value().copied().map(fe_to_u64),
1192 w.spreaded_w_3a.0.value().copied().map(fe_to_u64),
1193 w.spreaded_w_04.0.value().copied().map(fe_to_u64),
1194 w.spreaded_w_3b.0.value().copied().map(fe_to_u64),
1195 ])
1196 .map(|limbs: Vec<u64>| limbs.try_into().unwrap());
1197
1198 self.assign_sprdd_11_11_10(
1199 &mut region,
1200 val_of_sprdd_limbs.map(spreaded_sigma_1),
1201 Parity::Evn,
1202 0,
1203 )
1204 },
1205 )
1206 }
1207
1208 fn assign_sprdd_11_11_10(
1243 &self,
1244 region: &mut Region<'_, F>,
1245 value: Value<u64>,
1246 even_or_odd: Parity,
1247 offset: usize,
1248 ) -> Result<AssignedPlain<F, 32>, Error> {
1249 self.config().q_11_11_10.enable(region, offset + 1)?;
1250
1251 let (evn_val, odd_val) = value.map(get_even_and_odd_bits).unzip();
1252
1253 let [evn_11a, evn_11b, evn_10] =
1254 evn_val.map(|v| u32_in_be_limbs(v, [11, 11, 10])).transpose_array();
1255
1256 let [odd_11a, odd_11b, odd_10] =
1257 odd_val.map(|v| u32_in_be_limbs(v, [11, 11, 10])).transpose_array();
1258
1259 let idx = match even_or_odd {
1260 Parity::Evn => 0,
1261 Parity::Odd => 1,
1262 };
1263
1264 self.assign_plain_and_spreaded::<11>(region, evn_11a, offset, idx)?;
1265 self.assign_plain_and_spreaded::<11>(region, evn_11b, offset + 1, idx)?;
1266 self.assign_plain_and_spreaded::<10>(region, evn_10, offset + 2, idx)?;
1267
1268 self.assign_plain_and_spreaded::<11>(region, odd_11a, offset, 1 - idx)?;
1269 self.assign_plain_and_spreaded::<11>(region, odd_11b, offset + 1, 1 - idx)?;
1270 self.assign_plain_and_spreaded::<10>(region, odd_10, offset + 2, 1 - idx)?;
1271
1272 let out_col = self.config().advice_cols[4];
1273 match even_or_odd {
1274 Parity::Evn => {
1275 region.assign_advice(|| "Evn", out_col, offset, || evn_val.map(u32_to_fe))
1276 }
1277 Parity::Odd => {
1278 region.assign_advice(|| "Odd", out_col, offset, || odd_val.map(u32_to_fe))
1279 }
1280 }
1281 .map(AssignedPlain)
1282 }
1283
1284 fn prepare_A(
1291 &self,
1292 layouter: &mut impl Layouter<F>,
1293 summands: &[AssignedPlain<F, 32>],
1294 ) -> Result<LimbsOfA<F>, Error> {
1295 let zero = AssignedPlain::<F, 32>::fixed(layouter, &self.native_gadget, 0)?;
1326
1327 layouter.assign_region(
1328 || "decompose A in 10-9-11-2",
1329 |mut region| {
1330 self.config().q_10_9_11_2.enable(&mut region, 1)?;
1331
1332 let a_plain = self.assign_add_mod_2_32(&mut region, summands, &zero)?;
1333 let a_sprdd_val =
1334 a_plain.value().copied().map(fe_to_u32).map(spread).map(u64_to_fe);
1335 let a_sprdd = region
1336 .assign_advice(|| "~A", self.config().advice_cols[4], 1, || a_sprdd_val)
1337 .map(AssignedSpreaded)?;
1338
1339 let [val_10, val_09, val_11, val_02] = (a_plain.value().copied())
1340 .map(|a| u32_in_be_limbs(fe_to_u32(a), [10, 9, 11, 2]))
1341 .transpose_array();
1342
1343 let limb_10 = self.assign_plain_and_spreaded(&mut region, val_10, 0, 0)?;
1344 let limb_09 = self.assign_plain_and_spreaded(&mut region, val_09, 0, 1)?;
1345 let limb_11 = self.assign_plain_and_spreaded(&mut region, val_11, 1, 0)?;
1346 let limb_02 = self.assign_plain_and_spreaded(&mut region, val_02, 1, 1)?;
1347 let _zeros =
1348 self.assign_plain_and_spreaded::<0>(&mut region, Value::known(0), 2, 0)?;
1349
1350 Ok(LimbsOfA {
1351 combined: AssignedPlainSpreaded {
1352 plain: AssignedPlain(a_plain),
1353 spreaded: a_sprdd,
1354 },
1355 spreaded_limb_10: limb_10.spreaded,
1356 spreaded_limb_09: limb_09.spreaded,
1357 spreaded_limb_11: limb_11.spreaded,
1358 spreaded_limb_02: limb_02.spreaded,
1359 })
1360 },
1361 )
1362 }
1363
1364 fn prepare_E(
1371 &self,
1372 layouter: &mut impl Layouter<F>,
1373 summands: &[AssignedPlain<F, 32>],
1374 ) -> Result<LimbsOfE<F>, Error> {
1375 let zero = AssignedPlain::<F, 32>::fixed(layouter, &self.native_gadget, 0)?;
1404
1405 layouter.assign_region(
1406 || "decompose E in 7-12-2-5-6",
1407 |mut region| {
1408 self.config().q_7_12_2_5_6.enable(&mut region, 1)?;
1409
1410 let e_plain = self.assign_add_mod_2_32(&mut region, summands, &zero)?;
1411 let e_sprdd_val =
1412 (e_plain.value().copied()).map(fe_to_u32).map(spread).map(u64_to_fe);
1413 let e_sprdd = region
1414 .assign_advice(|| "~E", self.config().advice_cols[4], 1, || e_sprdd_val)
1415 .map(AssignedSpreaded)?;
1416
1417 let [val_07, val_12, val_02, val_05, val_06] = (e_plain.value().copied())
1418 .map(|e| u32_in_be_limbs(fe_to_u32(e), [7, 12, 2, 5, 6]))
1419 .transpose_array();
1420
1421 let limb_07 = self.assign_plain_and_spreaded(&mut region, val_07, 0, 0)?;
1422 let limb_12 = self.assign_plain_and_spreaded(&mut region, val_12, 0, 1)?;
1423 let limb_02 = self.assign_plain_and_spreaded(&mut region, val_02, 1, 0)?;
1424 let limb_05 = self.assign_plain_and_spreaded(&mut region, val_05, 1, 1)?;
1425 let limb_06 = self.assign_plain_and_spreaded(&mut region, val_06, 2, 0)?;
1426
1427 Ok(LimbsOfE {
1428 combined: AssignedPlainSpreaded {
1429 plain: AssignedPlain(e_plain),
1430 spreaded: e_sprdd,
1431 },
1432 spreaded_limb_07: limb_07.spreaded,
1433 spreaded_limb_12: limb_12.spreaded,
1434 spreaded_limb_02: limb_02.spreaded,
1435 spreaded_limb_05: limb_05.spreaded,
1436 spreaded_limb_06: limb_06.spreaded,
1437 })
1438 },
1439 )
1440 }
1441
1442 fn prepare_message_word(
1446 &self,
1447 layouter: &mut impl Layouter<F>,
1448 summands: &[AssignedPlain<F, 32>],
1449 ) -> Result<AssignedMessageWord<F>, Error> {
1450 let zero = AssignedPlain::<F, 32>::fixed(layouter, &self.native_gadget, 0)?;
1485
1486 layouter.assign_region(
1487 || "prepare message word",
1488 |mut region| {
1489 self.config().q_12_1x3_7_3_4_3.enable(&mut region, 1)?;
1490
1491 let w_i_plain = self.assign_add_mod_2_32(&mut region, summands, &zero)?;
1492
1493 let [val_12, val_1a, val_1b, val_1c, val_07, val_3a, val_04, val_3b] =
1494 (w_i_plain.value().copied())
1495 .map(|w| u32_in_be_limbs(fe_to_u32(w), [12, 1, 1, 1, 7, 3, 4, 3]))
1496 .transpose_array();
1497 let limb_12 = self.assign_plain_and_spreaded(&mut region, val_12, 0, 0)?;
1498 let limb_07 = self.assign_plain_and_spreaded(&mut region, val_07, 0, 1)?;
1499 let limb_3a = self.assign_plain_and_spreaded(&mut region, val_3a, 1, 0)?;
1500 let limb_04 = self.assign_plain_and_spreaded(&mut region, val_04, 1, 1)?;
1501 let limb_3b = self.assign_plain_and_spreaded(&mut region, val_3b, 2, 0)?;
1502
1503 let col = self.config().advice_cols[7];
1505 let limb_1a = region.assign_advice(|| "W.1a", col, 0, || val_1a.map(u32_to_fe))?;
1506 let limb_1b = region.assign_advice(|| "W.1b", col, 1, || val_1b.map(u32_to_fe))?;
1507 let limb_1c = region.assign_advice(|| "W.1c", col, 2, || val_1c.map(u32_to_fe))?;
1508
1509 Ok(AssignedMessageWord {
1510 combined_plain: AssignedPlain(w_i_plain),
1511 spreaded_w_12: limb_12.spreaded,
1512 spreaded_w_1a: AssignedSpreaded(limb_1a),
1513 spreaded_w_1b: AssignedSpreaded(limb_1b),
1514 spreaded_w_1c: AssignedSpreaded(limb_1c),
1515 spreaded_w_07: limb_07.spreaded,
1516 spreaded_w_3a: limb_3a.spreaded,
1517 spreaded_w_04: limb_04.spreaded,
1518 spreaded_w_3b: limb_3b.spreaded,
1519 })
1520 },
1521 )
1522 }
1523
1524 fn assign_plain_and_spreaded<const L: usize>(
1539 &self,
1540 region: &mut Region<'_, F>,
1541 plain_val: Value<u32>,
1542 offset: usize,
1543 lookup_idx: usize,
1544 ) -> Result<AssignedPlainSpreaded<F, L>, Error> {
1545 self.config().q_lookup.enable(region, offset)?;
1546
1547 let nbits_col = self.config().fixed_cols[lookup_idx]; let plain_col = self.config().advice_cols[2 * lookup_idx]; let sprdd_col = self.config().advice_cols[2 * lookup_idx + 1]; let nbits_val = Value::known(F::from(L as u64));
1552 let sprdd_val = plain_val.map(spread).map(u64_to_fe);
1553 let plain_val = plain_val.map(u32_to_fe);
1554
1555 region.assign_fixed(|| "nbits", nbits_col, offset, || nbits_val)?;
1556 let plain = region.assign_advice(|| "plain", plain_col, offset, || plain_val)?;
1557 let spreaded = region.assign_advice(|| "sprdd", sprdd_col, offset, || sprdd_val)?;
1558
1559 Ok(AssignedPlainSpreaded {
1560 plain: AssignedPlain(plain),
1561 spreaded: AssignedSpreaded(spreaded),
1562 })
1563 }
1564
1565 fn assign_add_mod_2_32(
1582 &self,
1583 region: &mut Region<'_, F>,
1584 summands: &[AssignedPlain<F, 32>],
1585 zero: &AssignedPlain<F, 32>,
1586 ) -> Result<AssignedNative<F>, Error> {
1587 assert!(summands.len() <= 7);
1600
1601 self.config().q_add_mod_2_32.enable(region, 1)?;
1602 let adv_cols = self.config().advice_cols;
1603
1604 let mut summands = summands.to_vec();
1605 summands.resize(7, zero.clone());
1606
1607 let (carry_val, sum_val): (Value<u32>, Value<F>) =
1608 Value::<Vec<F>>::from_iter(summands.iter().map(|s| s.0.value().copied()))
1609 .map(|v| v.into_iter().map(fe_to_u64).sum())
1610 .map(|s: u64| s.div_rem(&(1 << 32)))
1611 .map(|(carry, r)| (carry as u32, u64_to_fe(r)))
1612 .unzip();
1613
1614 summands[0].0.copy_advice(|| "S0", region, adv_cols[5], 0)?;
1615 summands[1].0.copy_advice(|| "S1", region, adv_cols[6], 0)?;
1616 summands[2].0.copy_advice(|| "S2", region, adv_cols[5], 1)?;
1617 summands[3].0.copy_advice(|| "S3", region, adv_cols[6], 1)?;
1618 summands[4].0.copy_advice(|| "S4", region, adv_cols[4], 2)?;
1619 summands[5].0.copy_advice(|| "S5", region, adv_cols[5], 2)?;
1620 summands[6].0.copy_advice(|| "S6", region, adv_cols[6], 2)?;
1621 let _carry: AssignedPlainSpreaded<F, 3> =
1622 self.assign_plain_and_spreaded(region, carry_val, 2, 1)?;
1623 region.assign_advice(|| "sum", adv_cols[4], 0, || sum_val)
1624 }
1625}
1626
1627impl<F: CircuitField> CompressionState<F> {
1628 pub fn add(
1630 &self,
1631 sha256_chip: &Sha256Chip<F>,
1632 layouter: &mut impl Layouter<F>,
1633 other: &Self,
1634 ) -> Result<Self, Error> {
1635 let a = sha256_chip.prepare_A(layouter, &[self.a.plain(), other.a.plain()])?;
1636 let b = sha256_chip.prepare_A(layouter, &[self.b.plain.clone(), other.b.plain.clone()])?;
1637 let c = sha256_chip.prepare_A(layouter, &[self.c.plain.clone(), other.c.plain.clone()])?;
1638 let d = sha256_chip.prepare_A(layouter, &[self.d.clone(), other.d.clone()])?;
1639 let e = sha256_chip.prepare_E(layouter, &[self.e.plain(), other.e.plain()])?;
1642 let f = sha256_chip.prepare_E(layouter, &[self.f.plain.clone(), other.f.plain.clone()])?;
1643 let g = sha256_chip.prepare_E(layouter, &[self.g.plain.clone(), other.g.plain.clone()])?;
1644 let h = sha256_chip.prepare_E(layouter, &[self.h.clone(), other.h.clone()])?;
1645 Ok(Self {
1648 a,
1649 b: b.combined,
1650 c: c.combined,
1651 d: d.combined.plain,
1652 e,
1653 f: f.combined,
1654 g: g.combined,
1655 h: h.combined.plain,
1656 })
1657 }
1658}
1659
1660#[cfg(any(test, feature = "testing"))]
1661use midnight_proofs::plonk::Instance;
1662
1663#[cfg(any(test, feature = "testing"))]
1664use crate::{field::decomposition::chip::P2RDecompositionConfig, testing_utils::FromScratch};
1665
1666#[cfg(any(test, feature = "testing"))]
1667impl<F: CircuitField> FromScratch<F> for Sha256Chip<F> {
1668 type Config = (Sha256Config, P2RDecompositionConfig);
1669
1670 fn new_from_scratch(config: &Self::Config) -> Self {
1671 Self {
1672 config: config.0.clone(),
1673 native_gadget: NativeGadget::new_from_scratch(&config.1),
1674 }
1675 }
1676
1677 fn configure_from_scratch(
1678 meta: &mut ConstraintSystem<F>,
1679 advice_columns: &mut Vec<Column<Advice>>,
1680 fixed_columns: &mut Vec<Column<Fixed>>,
1681 instance_columns: &[Column<Instance>; 2],
1682 ) -> Self::Config {
1683 use std::cmp::max;
1684
1685 use crate::field::{
1686 decomposition::pow2range::Pow2RangeChip,
1687 native::{NB_ARITH_COLS, NB_ARITH_FIXED_COLS},
1688 };
1689
1690 let nb_advice_needed = max(NB_ARITH_COLS, NB_SHA256_ADVICE_COLS);
1691 let nb_fixed_needed = max(NB_ARITH_FIXED_COLS, NB_SHA256_FIXED_COLS);
1692
1693 while advice_columns.len() < nb_advice_needed {
1694 advice_columns.push(meta.advice_column());
1695 }
1696 while fixed_columns.len() < nb_fixed_needed {
1697 fixed_columns.push(meta.fixed_column());
1698 }
1699
1700 let native_config = NativeChip::configure(
1701 meta,
1702 &(
1703 advice_columns[..NB_ARITH_COLS].try_into().unwrap(),
1704 fixed_columns[..NB_ARITH_FIXED_COLS].try_into().unwrap(),
1705 *instance_columns,
1706 ),
1707 );
1708
1709 let pow2range_config = Pow2RangeChip::configure(meta, &advice_columns[1..=4]);
1710 let core_decomposition_config =
1711 P2RDecompositionChip::configure(meta, &(native_config, pow2range_config));
1712
1713 let sha256_config = Sha256Chip::configure(
1714 meta,
1715 &(
1716 advice_columns[..NB_SHA256_ADVICE_COLS].try_into().unwrap(),
1717 fixed_columns[..NB_SHA256_FIXED_COLS].try_into().unwrap(),
1718 ),
1719 );
1720
1721 (sha256_config, core_decomposition_config)
1722 }
1723
1724 fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
1725 self.native_gadget.load_from_scratch(layouter)?;
1726 self.load(layouter)
1727 }
1728}