o1vm/interpreters/keccak/interpreter.rs
1//! This module defines the Keccak interpreter in charge of triggering the
2//! Keccak workflow
3
4use crate::{
5 interpreters::keccak::{
6 column::{PAD_BYTES_LEN, PAD_SUFFIX_LEN, ROUND_CONST_LEN},
7 grid_index,
8 helpers::{ArithHelpers, BoolHelpers, LogupHelpers},
9 Absorbs::*,
10 Constraint::{self, *},
11 KeccakColumn,
12 Sponges::*,
13 Steps::{self, *},
14 WORDS_IN_HASH,
15 },
16 lookups::{Lookup, LookupTableIDs::*},
17};
18use ark_ff::{One, Zero};
19use kimchi::{
20 auto_clone_array,
21 circuits::polynomials::keccak::{
22 constants::{
23 CHI_SHIFTS_B_LEN, CHI_SHIFTS_SUM_LEN, DIM, PIRHO_DENSE_E_LEN, PIRHO_DENSE_ROT_E_LEN,
24 PIRHO_EXPAND_ROT_E_LEN, PIRHO_QUOTIENT_E_LEN, PIRHO_REMAINDER_E_LEN,
25 PIRHO_SHIFTS_E_LEN, QUARTERS, RATE_IN_BYTES, SHIFTS, SHIFTS_LEN, SPONGE_BYTES_LEN,
26 SPONGE_SHIFTS_LEN, SPONGE_ZEROS_LEN, STATE_LEN, THETA_DENSE_C_LEN,
27 THETA_DENSE_ROT_C_LEN, THETA_EXPAND_ROT_C_LEN, THETA_QUOTIENT_C_LEN,
28 THETA_REMAINDER_C_LEN, THETA_SHIFTS_C_LEN, THETA_STATE_A_LEN,
29 },
30 OFF,
31 },
32 grid,
33};
34use std::{array, fmt::Debug};
35
36/// This trait includes functionalities needed to obtain the variables of the
37/// Keccak circuit needed for constraints and witness
38pub trait Interpreter<F: One + Debug + Zero> {
39 type Variable: std::ops::Mul<Self::Variable, Output = Self::Variable>
40 + std::ops::Add<Self::Variable, Output = Self::Variable>
41 + std::ops::Sub<Self::Variable, Output = Self::Variable>
42 + Clone
43 + Debug
44 + One
45 + Zero;
46
47 /// Creates a variable from a constant integer
48 fn constant(x: u64) -> Self::Variable;
49
50 /// Creates a variable from a constant field element
51 fn constant_field(x: F) -> Self::Variable;
52
53 /// Returns the variable corresponding to a given column alias.
54 fn variable(&self, column: KeccakColumn) -> Self::Variable;
55
56 /// Adds one KeccakConstraint to the environment if the selector holds
57 fn constrain(&mut self, tag: Constraint, if_true: Self::Variable, x: Self::Variable);
58
59 /// Adds a given Lookup to the environment if the condition holds
60 fn add_lookup(&mut self, if_true: Self::Variable, lookup: Lookup<Self::Variable>);
61}
62
63pub trait KeccakInterpreter<F: One + Debug + Zero>
64where
65 Self: Interpreter<F> + LogupHelpers<F> + BoolHelpers<F> + ArithHelpers<F>,
66{
67 /// Creates all 879 constraints/checks to the environment:
68 /// - 733 constraints of degree 1
69 /// - 146 constraints of degree 2
70 ///
71 /// Where:
72 /// - if Steps::Round(_) -> only 389 constraints added
73 /// - if Steps::Sponge::Absorb::First -> only 332 constraints added (232 + 100)
74 /// - if Steps::Sponge::Absorb::Middle -> only 232 constraints added
75 /// - if Steps::Sponge::Absorb::Last -> only 374 constraints added (232 + 136 + 6)
76 /// - if Steps::Sponge::Absorb::Only -> only 474 constraints added (232 + 136 + 100 + 6)
77 /// - if Steps::Sponge::Squeeze -> only 16 constraints added
78 ///
79 /// So:
80 /// - At most, 474 constraints are added per row
81 ///
82 /// In particular, after folding:
83 /// - 136 columns should be added for the degree-2 constraints of the flags
84 /// - 5 columns should be added for the degree-2 constraints of the round
85 /// - 10 columns should be added for the degree-2 constraints of the sponge
86 /// - for each of the 5 constraints, 2 columns are added for block_in_padding
87 fn constraints(&mut self, step: Steps) {
88 // CORRECTNESS OF FLAGS: 136 CONSTRAINTS
89 // - 136 constraints of degree 2
90 // Of which:
91 // - 136 constraints are added only if is_pad() holds
92 self.constrain_flags(step);
93
94 // SPONGE CONSTRAINTS: 32 + 3*100 + 16 + 6 = 354 CONSTRAINTS
95 // - 349 of degree 1
96 // - 5 of degree 2
97 // Of which:
98 // - 232 constraints are added only if is_absorb() holds
99 // - 100 constraints are added only if is_root() holds
100 // - 6 constraints are added only if is_pad() holds
101 // - 16 constraints are added only if is_squeeze() holds
102 self.constrain_sponge(step);
103
104 // ROUND CONSTRAINTS: 35 + 150 + 200 + 4 = 389 CONSTRAINTS
105 // - 384 constraints of degree 1
106 // - 5 constraints of degree 2
107 // Of which:
108 // - 389 constraints are added only if is_round() holds
109 self.constrain_round(step);
110 }
111
112 /// Constrains 136 checks of correctness of mode flags
113 /// - 136 constraints of degree 2
114 ///
115 /// Of which:
116 /// - 136 constraints are added only if is_pad() holds
117 fn constrain_flags(&mut self, step: Steps)
118 where
119 Self: Interpreter<F>,
120 {
121 // Booleanity of sponge flags:
122 // - 136 constraints of degree 2
123 self.constrain_booleanity(step);
124 }
125
126 /// Constrains 136 checks of booleanity for some mode flags.
127 /// - 136 constraints of degree 2
128 ///
129 /// Of which,
130 /// - 136 constraints are added only if is_pad() holds
131 fn constrain_booleanity(&mut self, step: Steps)
132 where
133 Self: Interpreter<F>,
134 {
135 for i in 0..RATE_IN_BYTES {
136 // Bytes are either involved on padding or not
137 self.constrain(
138 BooleanityPadding(i),
139 self.is_pad(step),
140 Self::is_boolean(self.in_padding(i)),
141 );
142 }
143 }
144
145 /// Constrains 354 checks of sponge steps
146 /// - 349 of degree 1
147 /// - 5 of degree 2
148 ///
149 /// Of which:
150 /// - 232 constraints are added only if is_absorb() holds
151 /// - 100 constraints are added only if is_root() holds
152 /// - 6 constraints are added only if is_pad() holds
153 /// - 16 constraints are added only if is_squeeze() holds
154 fn constrain_sponge(&mut self, step: Steps) {
155 self.constrain_absorb(step);
156 self.constrain_padding(step);
157 self.constrain_squeeze(step);
158 }
159
160 /// Constrains 332 checks of absorb sponges
161 /// - 332 of degree 1
162 ///
163 /// Of which:
164 /// - 232 constraints are added only if is_absorb() holds
165 /// - 100 constraints are added only if is_root() holds
166 fn constrain_absorb(&mut self, step: Steps) {
167 for (i, zero) in self.sponge_zeros().iter().enumerate() {
168 // Absorb phase pads with zeros the new state
169 self.constrain(AbsorbZeroPad(i), self.is_absorb(step), zero.clone());
170 }
171 for i in 0..QUARTERS * DIM * DIM {
172 // In first absorb, root state is all zeros
173 self.constrain(
174 AbsorbRootZero(i),
175 self.is_root(step),
176 self.old_state(i).clone(),
177 );
178 // Absorbs the new block by performing XOR with the old state
179 self.constrain(
180 AbsorbXor(i),
181 self.is_absorb(step),
182 self.xor_state(i).clone() - (self.old_state(i).clone() + self.new_state(i).clone()),
183 );
184 // In absorb, Check shifts correspond to the decomposition of the new state
185 self.constrain(
186 AbsorbShifts(i),
187 self.is_absorb(step),
188 self.new_state(i).clone()
189 - Self::from_shifts(&self.vec_sponge_shifts(), Some(i), None, None, None),
190 );
191 }
192 }
193
194 /// Constrains 6 checks of padding absorb sponges
195 /// - 1 of degree 1
196 /// - 5 of degree 2
197 ///
198 /// Of which:
199 /// - 6 constraints are added only if is_pad() holds
200 fn constrain_padding(&mut self, step: Steps) {
201 // Check that the padding is located at the end of the message
202 let pad_at_end = (0..RATE_IN_BYTES).fold(Self::zero(), |acc, i| {
203 acc * Self::two() + self.in_padding(i)
204 });
205 self.constrain(
206 PadAtEnd,
207 self.is_pad(step),
208 self.two_to_pad() - Self::one() - pad_at_end,
209 );
210 // Check that the padding value is correct
211 for i in 0..PAD_SUFFIX_LEN {
212 self.constrain(
213 PaddingSuffix(i),
214 self.is_pad(step),
215 self.block_in_padding(i) - self.pad_suffix(i),
216 );
217 }
218 }
219
220 /// Constrains 16 checks of squeeze sponges
221 /// - 16 of degree 1
222 ///
223 /// Of which:
224 /// - 16 constraints are added only if is_squeeze() holds
225 fn constrain_squeeze(&mut self, step: Steps) {
226 let sponge_shifts = self.vec_sponge_shifts();
227 for i in 0..QUARTERS * WORDS_IN_HASH {
228 // In squeeze, check shifts correspond to the 256-bit prefix digest of the old state (current)
229 self.constrain(
230 SqueezeShifts(i),
231 self.is_squeeze(step),
232 self.old_state(i).clone()
233 - Self::from_shifts(&sponge_shifts, Some(i), None, None, None),
234 );
235 }
236 }
237
238 /// Constrains 389 checks of round steps
239 /// - 384 constraints of degree 1
240 /// - 5 constraints of degree 2
241 ///
242 /// Of which:
243 /// - 389 constraints are added only if is_round() holds
244 fn constrain_round(&mut self, step: Steps) {
245 // STEP theta: 5 * ( 3 + 4 * 1 ) = 35 constraints
246 // - 30 constraints of degree 1
247 // - 5 constraints of degree 2
248 let state_e = self.constrain_theta(step);
249
250 // STEP pirho: 5 * 5 * (2 + 4 * 1) = 150 constraints
251 // - 150 of degree 1
252 let state_b = self.constrain_pirho(step, state_e);
253
254 // STEP chi: 4 * 5 * 5 * 2 = 200 constraints
255 // - 200 of degree 1
256 let state_f = self.constrain_chi(step, state_b);
257
258 // STEP iota: 4 constraints
259 // - 4 of degree 1
260 self.constrain_iota(step, state_f);
261 }
262
263 /// Constrains 35 checks of the theta algorithm in round steps
264 /// - 30 constraints of degree 1
265 /// - 5 constraints of degree 2
266 fn constrain_theta(&mut self, step: Steps) -> Vec<Vec<Vec<Self::Variable>>> {
267 // Define vectors storing expressions which are not in the witness layout for efficiency
268 let mut state_c = vec![vec![Self::zero(); QUARTERS]; DIM];
269 let mut state_d = vec![vec![Self::zero(); QUARTERS]; DIM];
270 let mut state_e = vec![vec![vec![Self::zero(); QUARTERS]; DIM]; DIM];
271
272 for x in 0..DIM {
273 let word_c = Self::from_quarters(&self.vec_dense_c(), None, x);
274 let rem_c = Self::from_quarters(&self.vec_remainder_c(), None, x);
275 let rot_c = Self::from_quarters(&self.vec_dense_rot_c(), None, x);
276
277 self.constrain(
278 ThetaWordC(x),
279 self.is_round(step),
280 word_c * Self::two_pow(1)
281 - (self.quotient_c(x) * Self::two_pow(64) + rem_c.clone()),
282 );
283 self.constrain(
284 ThetaRotatedC(x),
285 self.is_round(step),
286 rot_c - (self.quotient_c(x) + rem_c),
287 );
288 self.constrain(
289 ThetaQuotientC(x),
290 self.is_round(step),
291 Self::is_boolean(self.quotient_c(x)),
292 );
293
294 for q in 0..QUARTERS {
295 state_c[x][q] = self.state_a(0, x, q)
296 + self.state_a(1, x, q)
297 + self.state_a(2, x, q)
298 + self.state_a(3, x, q)
299 + self.state_a(4, x, q);
300 self.constrain(
301 ThetaShiftsC(x, q),
302 self.is_round(step),
303 state_c[x][q].clone()
304 - Self::from_shifts(&self.vec_shifts_c(), None, None, Some(x), Some(q)),
305 );
306
307 state_d[x][q] =
308 self.shifts_c(0, (x + DIM - 1) % DIM, q) + self.expand_rot_c((x + 1) % DIM, q);
309
310 for (y, column_e) in state_e.iter_mut().enumerate() {
311 column_e[x][q] = self.state_a(y, x, q) + state_d[x][q].clone();
312 }
313 }
314 }
315 state_e
316 }
317
318 /// Constrains 150 checks of the pirho algorithm in round steps
319 /// - 150 of degree 1
320 fn constrain_pirho(
321 &mut self,
322 step: Steps,
323 state_e: Vec<Vec<Vec<Self::Variable>>>,
324 ) -> Vec<Vec<Vec<Self::Variable>>> {
325 // Define vectors storing expressions which are not in the witness layout for efficiency
326 let mut state_b = vec![vec![vec![Self::zero(); QUARTERS]; DIM]; DIM];
327
328 for (y, col) in OFF.iter().enumerate() {
329 for (x, off) in col.iter().enumerate() {
330 let word_e = Self::from_quarters(&self.vec_dense_e(), Some(y), x);
331 let quo_e = Self::from_quarters(&self.vec_quotient_e(), Some(y), x);
332 let rem_e = Self::from_quarters(&self.vec_remainder_e(), Some(y), x);
333 let rot_e = Self::from_quarters(&self.vec_dense_rot_e(), Some(y), x);
334
335 self.constrain(
336 PiRhoWordE(y, x),
337 self.is_round(step),
338 word_e * Self::two_pow(*off)
339 - (quo_e.clone() * Self::two_pow(64) + rem_e.clone()),
340 );
341 self.constrain(
342 PiRhoRotatedE(y, x),
343 self.is_round(step),
344 rot_e - (quo_e.clone() + rem_e),
345 );
346
347 for q in 0..QUARTERS {
348 self.constrain(
349 PiRhoShiftsE(y, x, q),
350 self.is_round(step),
351 state_e[y][x][q].clone()
352 - Self::from_shifts(
353 &self.vec_shifts_e(),
354 None,
355 Some(y),
356 Some(x),
357 Some(q),
358 ),
359 );
360 state_b[(2 * x + 3 * y) % DIM][y][q] = self.expand_rot_e(y, x, q);
361 }
362 }
363 }
364 state_b
365 }
366
367 /// Constrains 200 checks of the chi algorithm in round steps
368 /// - 200 of degree 1
369 fn constrain_chi(
370 &mut self,
371 step: Steps,
372 state_b: Vec<Vec<Vec<Self::Variable>>>,
373 ) -> Vec<Vec<Vec<Self::Variable>>> {
374 // Define vectors storing expressions which are not in the witness layout for efficiency
375 let mut state_f = vec![vec![vec![Self::zero(); QUARTERS]; DIM]; DIM];
376
377 for q in 0..QUARTERS {
378 for x in 0..DIM {
379 for y in 0..DIM {
380 let not = Self::constant(0x1111111111111111u64)
381 - self.shifts_b(0, y, (x + 1) % DIM, q);
382 let sum = not + self.shifts_b(0, y, (x + 2) % DIM, q);
383 let and = self.shifts_sum(1, y, x, q);
384
385 self.constrain(
386 ChiShiftsB(y, x, q),
387 self.is_round(step),
388 state_b[y][x][q].clone()
389 - Self::from_shifts(
390 &self.vec_shifts_b(),
391 None,
392 Some(y),
393 Some(x),
394 Some(q),
395 ),
396 );
397 self.constrain(
398 ChiShiftsSum(y, x, q),
399 self.is_round(step),
400 sum - Self::from_shifts(
401 &self.vec_shifts_sum(),
402 None,
403 Some(y),
404 Some(x),
405 Some(q),
406 ),
407 );
408 state_f[y][x][q] = self.shifts_b(0, y, x, q) + and;
409 }
410 }
411 }
412 state_f
413 }
414
415 /// Constrains 4 checks of the iota algorithm in round steps
416 /// - 4 of degree 1
417 fn constrain_iota(&mut self, step: Steps, state_f: Vec<Vec<Vec<Self::Variable>>>) {
418 for (q, c) in self.round_constants().to_vec().iter().enumerate() {
419 self.constrain(
420 IotaStateG(q),
421 self.is_round(step),
422 self.state_g(q).clone() - (state_f[0][0][q].clone() + c.clone()),
423 );
424 }
425 }
426
427 ////////////////////////
428 // LOOKUPS OPERATIONS //
429 ////////////////////////
430
431 /// Creates all possible lookups to the Keccak constraints environment:
432 /// - 2225 lookups for the step row
433 /// - 2 lookups for the inter-step channel
434 /// - 136 lookups for the syscall channel (preimage bytes)
435 /// - 1 lookups for the syscall channel (hash)
436 ///
437 /// Of which:
438 /// - 1623 lookups if Step::Round (1621 + 2)
439 /// - 537 lookups if Step::Absorb::First (400 + 1 + 136)
440 /// - 538 lookups if Step::Absorb::Middle (400 + 2 + 136)
441 /// - 539 lookups if Step::Absorb::Last (401 + 2 + 136)
442 /// - 538 lookups if Step::Absorb::Only (401 + 1 + 136)
443 /// - 602 lookups if Step::Squeeze (600 + 1 + 1)
444 fn lookups(&mut self, step: Steps) {
445 // SPONGE LOOKUPS
446 // -> adds 400 lookups if is_sponge
447 // -> adds +200 lookups if is_squeeze
448 // -> adds +1 lookups if is_pad
449 self.lookups_sponge(step);
450
451 // ROUND LOOKUPS
452 // -> adds 1621 lookups if is_round
453 {
454 // THETA LOOKUPS
455 self.lookups_round_theta(step);
456 // PIRHO LOOKUPS
457 self.lookups_round_pirho(step);
458 // CHI LOOKUPS
459 self.lookups_round_chi(step);
460 // IOTA LOOKUPS
461 self.lookups_round_iota(step);
462 }
463
464 // INTER-STEP CHANNEL
465 // Write outputs for next step if not a squeeze and read inputs of curr step if not a root
466 // -> adds 1 lookup if is_root
467 // -> adds 1 lookup if is_squeeze
468 // -> adds 2 lookups otherwise
469 self.lookup_steps(step);
470
471 // COMMUNICATION CHANNEL: read bytes of current block
472 // -> adds 136 lookups if is_absorb
473 self.lookup_syscall_preimage(step);
474
475 // COMMUNICATION CHANNEL: Write hash output
476 // -> adds 1 lookup if is_squeeze
477 self.lookup_syscall_hash(step);
478 }
479
480 /// When in Absorb mode, reads Lookups containing the 136 bytes of the block of the preimage
481 /// - if is_absorb, adds 136 lookups
482 /// - otherwise, adds 0 lookups
483 // TODO: optimize this by using a single lookup reusing PadSuffix
484 fn lookup_syscall_preimage(&mut self, step: Steps) {
485 for i in 0..RATE_IN_BYTES {
486 self.read_syscall(
487 self.is_absorb(step),
488 vec![
489 self.hash_index(),
490 self.block_index() * Self::constant(RATE_IN_BYTES as u64)
491 + Self::constant(i as u64),
492 self.sponge_byte(i),
493 ],
494 );
495 }
496 }
497
498 /// When in Squeeze mode, writes a Lookup containing the 31byte output of
499 /// the hash (excludes the MSB)
500 /// - if is_squeeze, adds 1 lookup
501 /// - otherwise, adds 0 lookups
502 ///
503 /// NOTE: this is excluding the MSB (which is then substituted with the
504 /// file descriptor).
505 fn lookup_syscall_hash(&mut self, step: Steps) {
506 let bytes31 = (1..32).fold(Self::zero(), |acc, i| {
507 acc * Self::two_pow(8) + self.sponge_byte(i)
508 });
509 self.write_syscall(self.is_squeeze(step), vec![self.hash_index(), bytes31]);
510 }
511
512 /// Reads a Lookup containing the input of a step
513 /// and writes a Lookup containing the output of the next step
514 /// - if is_root, only adds 1 lookup
515 /// - if is_squeeze, only adds 1 lookup
516 /// - otherwise, adds 2 lookups
517 fn lookup_steps(&mut self, step: Steps) {
518 // (if not a root) Output of previous step is input of current step
519 self.add_lookup(
520 Self::not(self.is_root(step)),
521 Lookup::read_one(KeccakStepLookup, self.input_of_step()),
522 );
523 // (if not a squeeze) Input for next step is output of current step
524 self.add_lookup(
525 Self::not(self.is_squeeze(step)),
526 Lookup::write_one(KeccakStepLookup, self.output_of_step()),
527 );
528 }
529
530 /// Adds the 601 lookups required for the sponge
531 /// - 400 lookups if is_sponge()
532 /// - 200 extra lookups if is_squeeze()
533 /// - 1 extra lookup if is_pad()
534 fn lookups_sponge(&mut self, step: Steps) {
535 // PADDING LOOKUPS
536 // Power of two corresponds to 2^pad_length
537 // Pad suffixes correspond to 10*1 rule
538 self.lookup_pad(
539 self.is_pad(step),
540 vec![
541 self.pad_length(),
542 self.two_to_pad(),
543 self.pad_suffix(0),
544 self.pad_suffix(1),
545 self.pad_suffix(2),
546 self.pad_suffix(3),
547 self.pad_suffix(4),
548 ],
549 );
550 // BYTES LOOKUPS
551 // Checking the 200 bytes of the absorb phase together with the length
552 // bytes is performed in the SyscallReadPreimage rows (4 byte lookups
553 // per row).
554 // Here, this only checks the 200 bytes of the squeeze phase (digest)
555 // TODO: could this be just 32 and we check the shifts only for those?
556 for i in 0..200 {
557 // Bytes are <2^8
558 self.lookup_byte(self.is_squeeze(step), self.sponge_byte(i));
559 }
560 // SHIFTS LOOKUPS
561 for i in 100..SHIFTS_LEN {
562 // Shifts1, Shifts2, Shifts3 are in the Sparse table
563 self.lookup_sparse(self.is_sponge(step), self.sponge_shifts(i));
564 }
565 for i in 0..STATE_LEN {
566 // Shifts0 together with Bytes composition by pairs are in the Reset table
567 let dense = self.sponge_byte(2 * i) + self.sponge_byte(2 * i + 1) * Self::two_pow(8);
568 self.lookup_reset(self.is_sponge(step), dense, self.sponge_shifts(i));
569 }
570 }
571
572 /// Adds the 120 lookups required for Theta in the round
573 fn lookups_round_theta(&mut self, step: Steps) {
574 for q in 0..QUARTERS {
575 for x in 0..DIM {
576 // Check that ThetaRemainderC < 2^64
577 self.lookup_rc16(self.is_round(step), self.remainder_c(x, q));
578 // Check ThetaExpandRotC is the expansion of ThetaDenseRotC
579 self.lookup_reset(
580 self.is_round(step),
581 self.dense_rot_c(x, q),
582 self.expand_rot_c(x, q),
583 );
584 // Check ThetaShiftC0 is the expansion of ThetaDenseC
585 self.lookup_reset(
586 self.is_round(step),
587 self.dense_c(x, q),
588 self.shifts_c(0, x, q),
589 );
590 // Check that the rest of ThetaShiftsC are in the Sparse table
591 for i in 1..SHIFTS {
592 self.lookup_sparse(self.is_round(step), self.shifts_c(i, x, q));
593 }
594 }
595 }
596 }
597
598 /// Adds the 700 lookups required for PiRho in the round
599 fn lookups_round_pirho(&mut self, step: Steps) {
600 for q in 0..QUARTERS {
601 for x in 0..DIM {
602 for y in 0..DIM {
603 // Check that PiRhoRemainderE < 2^64 and PiRhoQuotientE < 2^64
604 self.lookup_rc16(self.is_round(step), self.remainder_e(y, x, q));
605 self.lookup_rc16(self.is_round(step), self.quotient_e(y, x, q));
606 // Check PiRhoExpandRotE is the expansion of PiRhoDenseRotE
607 self.lookup_reset(
608 self.is_round(step),
609 self.dense_rot_e(y, x, q),
610 self.expand_rot_e(y, x, q),
611 );
612 // Check PiRhoShift0E is the expansion of PiRhoDenseE
613 self.lookup_reset(
614 self.is_round(step),
615 self.dense_e(y, x, q),
616 self.shifts_e(0, y, x, q),
617 );
618 // Check that the rest of PiRhoShiftsE are in the Sparse table
619 for i in 1..SHIFTS {
620 self.lookup_sparse(self.is_round(step), self.shifts_e(i, y, x, q));
621 }
622 }
623 }
624 }
625 }
626
627 /// Adds the 800 lookups required for Chi in the round
628 fn lookups_round_chi(&mut self, step: Steps) {
629 let shifts_b = self.vec_shifts_b();
630 let shifts_sum = self.vec_shifts_sum();
631 for i in 0..SHIFTS_LEN {
632 // Check ChiShiftsB and ChiShiftsSum are in the Sparse table
633 self.lookup_sparse(self.is_round(step), shifts_b[i].clone());
634 self.lookup_sparse(self.is_round(step), shifts_sum[i].clone());
635 }
636 }
637
638 /// Adds the 1 lookup required for Iota in the round
639 fn lookups_round_iota(&mut self, step: Steps) {
640 // Check round constants correspond with the current round
641 let round_constants = self.round_constants();
642 self.lookup_round_constants(
643 self.is_round(step),
644 vec![
645 self.round(),
646 round_constants[3].clone(),
647 round_constants[2].clone(),
648 round_constants[1].clone(),
649 round_constants[0].clone(),
650 ],
651 );
652 }
653
654 ///////////////////////////
655 /// SELECTOR OPERATIONS ///
656 ///////////////////////////
657
658 /// Returns a degree-2 variable that encodes whether the current step is a
659 /// sponge (1 = yes)
660 fn is_sponge(&self, step: Steps) -> Self::Variable {
661 Self::xor(self.is_absorb(step), self.is_squeeze(step))
662 }
663
664 /// Returns a variable that encodes whether the current step is an absorb
665 /// sponge (1 = yes)
666 fn is_absorb(&self, step: Steps) -> Self::Variable {
667 Self::or(
668 Self::or(self.mode_root(step), self.mode_rootpad(step)),
669 Self::or(self.mode_pad(step), self.mode_absorb(step)),
670 )
671 }
672
673 /// Returns a variable that encodes whether the current step is a squeeze
674 /// sponge (1 = yes)
675 fn is_squeeze(&self, step: Steps) -> Self::Variable {
676 self.mode_squeeze(step)
677 }
678
679 /// Returns a variable that encodes whether the current step is the first
680 /// absorb sponge (1 = yes)
681 fn is_root(&self, step: Steps) -> Self::Variable {
682 Self::or(self.mode_root(step), self.mode_rootpad(step))
683 }
684
685 /// Returns a degree-1 variable that encodes whether the current step is the
686 /// last absorb sponge (1 = yes)
687 fn is_pad(&self, step: Steps) -> Self::Variable {
688 Self::or(self.mode_pad(step), self.mode_rootpad(step))
689 }
690
691 /// Returns a variable that encodes whether the current step is a
692 /// permutation round (1 = yes)
693 fn is_round(&self, step: Steps) -> Self::Variable {
694 self.mode_round(step)
695 }
696
697 /// Returns a variable that encodes whether the current step is an absorb
698 /// sponge (1 = yes)
699 fn mode_absorb(&self, step: Steps) -> Self::Variable {
700 match step {
701 Sponge(Absorb(Middle)) => Self::one(),
702 _ => Self::zero(),
703 }
704 }
705
706 /// Returns a variable that encodes whether the current step is a squeeze
707 /// sponge (1 = yes)
708 fn mode_squeeze(&self, step: Steps) -> Self::Variable {
709 match step {
710 Sponge(Squeeze) => Self::one(),
711 _ => Self::zero(),
712 }
713 }
714
715 /// Returns a variable that encodes whether the current step is the first
716 /// absorb sponge (1 = yes)
717 fn mode_root(&self, step: Steps) -> Self::Variable {
718 match step {
719 Sponge(Absorb(First)) => Self::one(),
720 _ => Self::zero(),
721 }
722 }
723
724 /// Returns a degree-1 variable that encodes whether the current step is the
725 /// last absorb sponge (1 = yes)
726 fn mode_pad(&self, step: Steps) -> Self::Variable {
727 match step {
728 Sponge(Absorb(Last)) => Self::one(),
729 _ => Self::zero(),
730 }
731 }
732
733 /// Returns a degree-1 variable that encodes whether the current step is the
734 /// first and last absorb sponge (1 = yes)
735 fn mode_rootpad(&self, step: Steps) -> Self::Variable {
736 match step {
737 Sponge(Absorb(Only)) => Self::one(),
738 _ => Self::zero(),
739 }
740 }
741
742 /// Returns a variable that encodes whether the current step is a
743 /// permutation round (1 = yes)
744 fn mode_round(&self, step: Steps) -> Self::Variable {
745 // The actual round number in the selector carries no information for witness nor constraints
746 // because in the witness, any usize is mapped to the same index inside the mode flags
747 match step {
748 Round(_) => Self::one(),
749 _ => Self::zero(),
750 }
751 }
752
753 /////////////////////////
754 /// COLUMN OPERATIONS ///
755 /////////////////////////
756
757 /// This function returns the composed sparse variable from shifts of any
758 /// correct length:
759 /// - When the length is 400, two index configurations are possible:
760 /// - If `i` is `Some`, then this sole index could range between [0..400)
761 /// - If `i` is `None`, then `y`, `x` and `q` must be `Some` and
762 /// - `y` must range between [0..5)
763 /// - `x` must range between [0..5)
764 /// - `q` must range between [0..4)
765 /// - When the length is 80, both `i` and `y` should be `None`, and `x` and
766 /// `q` must be `Some` with:
767 /// - `x` must range between [0..5)
768 /// - `q` must range between [0..4)
769 fn from_shifts(
770 shifts: &[Self::Variable],
771 i: Option<usize>,
772 y: Option<usize>,
773 x: Option<usize>,
774 q: Option<usize>,
775 ) -> Self::Variable {
776 match shifts.len() {
777 400 => {
778 if let Some(i) = i {
779 auto_clone_array!(shifts);
780 shifts(i)
781 + Self::two_pow(1) * shifts(100 + i)
782 + Self::two_pow(2) * shifts(200 + i)
783 + Self::two_pow(3) * shifts(300 + i)
784 } else {
785 let shifts = grid!(400, shifts);
786 shifts(0, y.unwrap(), x.unwrap(), q.unwrap())
787 + Self::two_pow(1) * shifts(1, y.unwrap(), x.unwrap(), q.unwrap())
788 + Self::two_pow(2) * shifts(2, y.unwrap(), x.unwrap(), q.unwrap())
789 + Self::two_pow(3) * shifts(3, y.unwrap(), x.unwrap(), q.unwrap())
790 }
791 }
792 80 => {
793 let shifts = grid!(80, shifts);
794 shifts(0, x.unwrap(), q.unwrap())
795 + Self::two_pow(1) * shifts(1, x.unwrap(), q.unwrap())
796 + Self::two_pow(2) * shifts(2, x.unwrap(), q.unwrap())
797 + Self::two_pow(3) * shifts(3, x.unwrap(), q.unwrap())
798 }
799 _ => panic!("Invalid length of shifts"),
800 }
801 }
802
803 /// This function returns the composed variable from dense quarters of any
804 /// correct length:
805 /// - When `y` is `Some`, then the length must be 100 and:
806 /// - `y` must range between [0..5)
807 /// - `x` must range between [0..5)
808 /// - When `y` is `None`, then the length must be 20 and:
809 /// - `x` must range between [0..5)
810 fn from_quarters(quarters: &[Self::Variable], y: Option<usize>, x: usize) -> Self::Variable {
811 if let Some(y) = y {
812 assert!(quarters.len() == 100, "Invalid length of quarters");
813 let quarters = grid!(100, quarters);
814 quarters(y, x, 0)
815 + Self::two_pow(16) * quarters(y, x, 1)
816 + Self::two_pow(32) * quarters(y, x, 2)
817 + Self::two_pow(48) * quarters(y, x, 3)
818 } else {
819 assert!(quarters.len() == 20, "Invalid length of quarters");
820 let quarters = grid!(20, quarters);
821 quarters(x, 0)
822 + Self::two_pow(16) * quarters(x, 1)
823 + Self::two_pow(32) * quarters(x, 2)
824 + Self::two_pow(48) * quarters(x, 3)
825 }
826 }
827
828 /// Returns a variable that encodes the current round number [0..24)
829 fn round(&self) -> Self::Variable {
830 self.variable(KeccakColumn::RoundNumber)
831 }
832
833 /// Returns a variable that encodes the bytelength of the padding if any
834 /// [0..136)
835 fn pad_length(&self) -> Self::Variable {
836 self.variable(KeccakColumn::PadLength)
837 }
838
839 /// Returns a variable that encodes the value 2^pad_length
840 fn two_to_pad(&self) -> Self::Variable {
841 self.variable(KeccakColumn::TwoToPad)
842 }
843
844 /// Returns a variable that encodes whether the `idx`-th byte of the new
845 /// block is involved in the padding (1 = yes)
846 fn in_padding(&self, idx: usize) -> Self::Variable {
847 self.variable(KeccakColumn::PadBytesFlags(idx))
848 }
849
850 /// Returns a variable that encodes the `idx`-th chunk of the padding suffix
851 /// - if `idx` = 0, then the length is 12 bytes at most
852 /// - if `idx` = [1..5), then the length is 31 bytes at most
853 fn pad_suffix(&self, idx: usize) -> Self::Variable {
854 self.variable(KeccakColumn::PadSuffix(idx))
855 }
856
857 /// Returns a variable that encodes the `idx`-th block of bytes of the new block
858 /// by composing the bytes variables, with `idx` in [0..5)
859 fn bytes_block(&self, idx: usize) -> Vec<Self::Variable> {
860 let sponge_bytes = self.sponge_bytes();
861 match idx {
862 0 => sponge_bytes[0..12].to_vec(),
863 1..=4 => sponge_bytes[12 + (idx - 1) * 31..12 + idx * 31].to_vec(),
864 _ => panic!("No more blocks of bytes can be part of padding"),
865 }
866 }
867
868 /// Returns the 136 flags indicating which bytes of the new block are
869 /// involved in the padding, as variables
870 fn pad_bytes_flags(&self) -> [Self::Variable; PAD_BYTES_LEN] {
871 array::from_fn(|idx| self.variable(KeccakColumn::PadBytesFlags(idx)))
872 }
873
874 /// Returns a vector of pad bytes flags as variables, with `idx` in [0..5)
875 /// - if `idx` = 0, then the length of the block is at most 12
876 /// - if `idx` = [1..5), then the length of the block is at most 31
877 fn flags_block(&self, idx: usize) -> Vec<Self::Variable> {
878 let pad_bytes_flags = self.pad_bytes_flags();
879 match idx {
880 0 => pad_bytes_flags[0..12].to_vec(),
881 1..=4 => pad_bytes_flags[12 + (idx - 1) * 31..12 + idx * 31].to_vec(),
882 _ => panic!("No more blocks of flags can be part of padding"),
883 }
884 }
885
886 /// This function returns a degree-2 variable that is computed as the
887 /// accumulated value of the operation `byte * flag * 2^8` for each byte
888 /// block and flag block of the new block. This function will be used in
889 /// constraints to determine whether the padding is located at the end of
890 /// the preimage data, as consecutive bits that are involved in the padding.
891 fn block_in_padding(&self, idx: usize) -> Self::Variable {
892 let bytes = self.bytes_block(idx);
893 let flags = self.flags_block(idx);
894 assert_eq!(bytes.len(), flags.len());
895 let pad = bytes
896 .iter()
897 .zip(flags)
898 .fold(Self::zero(), |acc, (byte, flag)| {
899 acc * Self::two_pow(8) + byte.clone() * flag.clone()
900 });
901
902 pad
903 }
904
905 /// Returns the 4 expanded quarters that encode the round constant, as
906 /// variables
907 fn round_constants(&self) -> [Self::Variable; ROUND_CONST_LEN] {
908 array::from_fn(|idx| self.variable(KeccakColumn::RoundConstants(idx)))
909 }
910
911 /// Returns the `idx`-th old state expanded quarter, as a variable
912 fn old_state(&self, idx: usize) -> Self::Variable {
913 self.variable(KeccakColumn::Input(idx))
914 }
915
916 /// Returns the `idx`-th new state expanded quarter, as a variable
917 fn new_state(&self, idx: usize) -> Self::Variable {
918 self.variable(KeccakColumn::SpongeNewState(idx))
919 }
920
921 /// Returns the output of an absorb sponge, which is the XOR of the old
922 /// state and the new state
923 fn xor_state(&self, idx: usize) -> Self::Variable {
924 self.variable(KeccakColumn::Output(idx))
925 }
926
927 /// Returns the last 32 terms that are added to the new block in an absorb
928 /// sponge, as variables which should be zeros
929 fn sponge_zeros(&self) -> [Self::Variable; SPONGE_ZEROS_LEN] {
930 array::from_fn(|idx| self.variable(KeccakColumn::SpongeZeros(idx)))
931 }
932
933 /// Returns the 400 terms that compose the shifts of the sponge, as variables
934 fn vec_sponge_shifts(&self) -> [Self::Variable; SPONGE_SHIFTS_LEN] {
935 array::from_fn(|idx| self.variable(KeccakColumn::SpongeShifts(idx)))
936 }
937 /// Returns the `idx`-th term of the shifts of the sponge, as a variable
938 fn sponge_shifts(&self, idx: usize) -> Self::Variable {
939 self.variable(KeccakColumn::SpongeShifts(idx))
940 }
941
942 /// Returns the 200 bytes of the sponge, as variables
943 fn sponge_bytes(&self) -> [Self::Variable; SPONGE_BYTES_LEN] {
944 array::from_fn(|idx| self.variable(KeccakColumn::SpongeBytes(idx)))
945 }
946
947 /// Returns the `idx`-th byte of the sponge, as a variable
948 fn sponge_byte(&self, idx: usize) -> Self::Variable {
949 self.variable(KeccakColumn::SpongeBytes(idx))
950 }
951
952 /// Returns the (y,x,q)-th input of the theta algorithm, as a variable
953 fn state_a(&self, y: usize, x: usize, q: usize) -> Self::Variable {
954 let idx = grid_index(THETA_STATE_A_LEN, 0, y, x, q);
955 self.variable(KeccakColumn::Input(idx))
956 }
957
958 /// Returns the 80 variables corresponding to ThetaShiftsC
959 fn vec_shifts_c(&self) -> [Self::Variable; THETA_SHIFTS_C_LEN] {
960 array::from_fn(|idx| self.variable(KeccakColumn::ThetaShiftsC(idx)))
961 }
962
963 /// Returns the (i,x,q)-th variable of ThetaShiftsC
964 fn shifts_c(&self, i: usize, x: usize, q: usize) -> Self::Variable {
965 let idx = grid_index(THETA_SHIFTS_C_LEN, i, 0, x, q);
966 self.variable(KeccakColumn::ThetaShiftsC(idx))
967 }
968
969 /// Returns the 20 variables corresponding to ThetaDenseC
970 fn vec_dense_c(&self) -> [Self::Variable; THETA_DENSE_C_LEN] {
971 array::from_fn(|idx| self.variable(KeccakColumn::ThetaDenseC(idx)))
972 }
973 /// Returns the (x,q)-th term of ThetaDenseC, as a variable
974 fn dense_c(&self, x: usize, q: usize) -> Self::Variable {
975 let idx = grid_index(THETA_DENSE_C_LEN, 0, 0, x, q);
976 self.variable(KeccakColumn::ThetaDenseC(idx))
977 }
978
979 /// Returns the 5 variables corresponding to ThetaQuotientC
980 fn vec_quotient_c(&self) -> [Self::Variable; THETA_QUOTIENT_C_LEN] {
981 array::from_fn(|idx| self.variable(KeccakColumn::ThetaQuotientC(idx)))
982 }
983
984 /// Returns the (x)-th term of ThetaQuotientC, as a variable
985 fn quotient_c(&self, x: usize) -> Self::Variable {
986 self.variable(KeccakColumn::ThetaQuotientC(x))
987 }
988
989 /// Returns the 20 variables corresponding to ThetaRemainderC
990 fn vec_remainder_c(&self) -> [Self::Variable; THETA_REMAINDER_C_LEN] {
991 array::from_fn(|idx| self.variable(KeccakColumn::ThetaRemainderC(idx)))
992 }
993 /// Returns the (x,q)-th variable of ThetaRemainderC
994 fn remainder_c(&self, x: usize, q: usize) -> Self::Variable {
995 let idx = grid_index(THETA_REMAINDER_C_LEN, 0, 0, x, q);
996 self.variable(KeccakColumn::ThetaRemainderC(idx))
997 }
998
999 /// Returns the 20 variables corresponding to ThetaDenseRotC
1000 fn vec_dense_rot_c(&self) -> [Self::Variable; THETA_DENSE_ROT_C_LEN] {
1001 array::from_fn(|idx| self.variable(KeccakColumn::ThetaDenseRotC(idx)))
1002 }
1003
1004 /// Returns the (x,q)-th variable of ThetaDenseRotC
1005 fn dense_rot_c(&self, x: usize, q: usize) -> Self::Variable {
1006 let idx = grid_index(THETA_DENSE_ROT_C_LEN, 0, 0, x, q);
1007 self.variable(KeccakColumn::ThetaDenseRotC(idx))
1008 }
1009
1010 /// Returns the 20 variables corresponding to ThetaExpandRotC
1011 fn vec_expand_rot_c(&self) -> [Self::Variable; THETA_EXPAND_ROT_C_LEN] {
1012 array::from_fn(|idx| self.variable(KeccakColumn::ThetaExpandRotC(idx)))
1013 }
1014 /// Returns the (x,q)-th variable of ThetaExpandRotC
1015 fn expand_rot_c(&self, x: usize, q: usize) -> Self::Variable {
1016 let idx = grid_index(THETA_EXPAND_ROT_C_LEN, 0, 0, x, q);
1017 self.variable(KeccakColumn::ThetaExpandRotC(idx))
1018 }
1019
1020 /// Returns the 400 variables corresponding to PiRhoShiftsE
1021 fn vec_shifts_e(&self) -> [Self::Variable; PIRHO_SHIFTS_E_LEN] {
1022 array::from_fn(|idx| self.variable(KeccakColumn::PiRhoShiftsE(idx)))
1023 }
1024
1025 /// Returns the (i,y,x,q)-th variable of PiRhoShiftsE
1026 fn shifts_e(&self, i: usize, y: usize, x: usize, q: usize) -> Self::Variable {
1027 let idx = grid_index(PIRHO_SHIFTS_E_LEN, i, y, x, q);
1028 self.variable(KeccakColumn::PiRhoShiftsE(idx))
1029 }
1030
1031 /// Returns the 100 variables corresponding to PiRhoDenseE
1032 fn vec_dense_e(&self) -> [Self::Variable; PIRHO_DENSE_E_LEN] {
1033 array::from_fn(|idx: usize| self.variable(KeccakColumn::PiRhoDenseE(idx)))
1034 }
1035
1036 /// Returns the (y,x,q)-th variable of PiRhoDenseE
1037 fn dense_e(&self, y: usize, x: usize, q: usize) -> Self::Variable {
1038 let idx = grid_index(PIRHO_DENSE_E_LEN, 0, y, x, q);
1039 self.variable(KeccakColumn::PiRhoDenseE(idx))
1040 }
1041
1042 /// Returns the 100 variables corresponding to PiRhoQuotientE
1043 fn vec_quotient_e(&self) -> [Self::Variable; PIRHO_QUOTIENT_E_LEN] {
1044 array::from_fn(|idx| self.variable(KeccakColumn::PiRhoQuotientE(idx)))
1045 }
1046
1047 /// Returns the (y,x,q)-th variable of PiRhoQuotientE
1048 fn quotient_e(&self, y: usize, x: usize, q: usize) -> Self::Variable {
1049 let idx = grid_index(PIRHO_QUOTIENT_E_LEN, 0, y, x, q);
1050 self.variable(KeccakColumn::PiRhoQuotientE(idx))
1051 }
1052
1053 /// Returns the 100 variables corresponding to PiRhoRemainderE
1054 fn vec_remainder_e(&self) -> [Self::Variable; PIRHO_REMAINDER_E_LEN] {
1055 array::from_fn(|idx| self.variable(KeccakColumn::PiRhoRemainderE(idx)))
1056 }
1057 /// Returns the (y,x,q)-th variable of PiRhoRemainderE
1058 fn remainder_e(&self, y: usize, x: usize, q: usize) -> Self::Variable {
1059 let idx = grid_index(PIRHO_REMAINDER_E_LEN, 0, y, x, q);
1060 self.variable(KeccakColumn::PiRhoRemainderE(idx))
1061 }
1062
1063 /// Returns the 100 variables corresponding to PiRhoDenseRotE
1064 fn vec_dense_rot_e(&self) -> [Self::Variable; PIRHO_DENSE_ROT_E_LEN] {
1065 array::from_fn(|idx| self.variable(KeccakColumn::PiRhoDenseRotE(idx)))
1066 }
1067
1068 /// Returns the (y,x,q)-th variable of PiRhoDenseRotE
1069 fn dense_rot_e(&self, y: usize, x: usize, q: usize) -> Self::Variable {
1070 let idx = grid_index(PIRHO_DENSE_ROT_E_LEN, 0, y, x, q);
1071 self.variable(KeccakColumn::PiRhoDenseRotE(idx))
1072 }
1073
1074 /// Returns the 100 variables corresponding to PiRhoExpandRotE
1075 fn vec_expand_rot_e(&self) -> [Self::Variable; PIRHO_EXPAND_ROT_E_LEN] {
1076 array::from_fn(|idx| self.variable(KeccakColumn::PiRhoExpandRotE(idx)))
1077 }
1078 /// Returns the (y,x,q)-th variable of PiRhoExpandRotE
1079 fn expand_rot_e(&self, y: usize, x: usize, q: usize) -> Self::Variable {
1080 let idx = grid_index(PIRHO_EXPAND_ROT_E_LEN, 0, y, x, q);
1081 self.variable(KeccakColumn::PiRhoExpandRotE(idx))
1082 }
1083
1084 /// Returns the 400 variables corresponding to ChiShiftsB
1085 fn vec_shifts_b(&self) -> [Self::Variable; CHI_SHIFTS_B_LEN] {
1086 array::from_fn(|idx| self.variable(KeccakColumn::ChiShiftsB(idx)))
1087 }
1088
1089 /// Returns the (i,y,x,q)-th variable of ChiShiftsB
1090 fn shifts_b(&self, i: usize, y: usize, x: usize, q: usize) -> Self::Variable {
1091 let idx = grid_index(CHI_SHIFTS_B_LEN, i, y, x, q);
1092 self.variable(KeccakColumn::ChiShiftsB(idx))
1093 }
1094
1095 /// Returns the 400 variables corresponding to ChiShiftsSum
1096 fn vec_shifts_sum(&self) -> [Self::Variable; CHI_SHIFTS_SUM_LEN] {
1097 array::from_fn(|idx| self.variable(KeccakColumn::ChiShiftsSum(idx)))
1098 }
1099 /// Returns the (i,y,x,q)-th variable of ChiShiftsSum
1100 fn shifts_sum(&self, i: usize, y: usize, x: usize, q: usize) -> Self::Variable {
1101 let idx = grid_index(CHI_SHIFTS_SUM_LEN, i, y, x, q);
1102 self.variable(KeccakColumn::ChiShiftsSum(idx))
1103 }
1104
1105 /// Returns the `idx`-th output of a round step as a variable
1106 fn state_g(&self, idx: usize) -> Self::Variable {
1107 self.variable(KeccakColumn::Output(idx))
1108 }
1109
1110 /// Returns the hash index as a variable
1111 fn hash_index(&self) -> Self::Variable {
1112 self.variable(KeccakColumn::HashIndex)
1113 }
1114
1115 /// Returns the block index as a variable
1116 fn block_index(&self) -> Self::Variable {
1117 self.variable(KeccakColumn::BlockIndex)
1118 }
1119
1120 /// Returns the step index as a variable
1121 fn step_index(&self) -> Self::Variable {
1122 self.variable(KeccakColumn::StepIndex)
1123 }
1124
1125 /// Returns the 100 step input variables, which correspond to the:
1126 /// - State A when the current step is a permutation round
1127 /// - Old state when the current step is a non-root sponge
1128 fn input(&self) -> [Self::Variable; STATE_LEN] {
1129 array::from_fn::<_, STATE_LEN, _>(|idx| self.variable(KeccakColumn::Input(idx)))
1130 }
1131 /// Returns a slice of the input variables of the current step
1132 /// including the current hash index and step index
1133 fn input_of_step(&self) -> Vec<Self::Variable> {
1134 let mut input_of_step = Vec::with_capacity(STATE_LEN + 2);
1135 input_of_step.push(self.hash_index());
1136 input_of_step.push(self.step_index());
1137 input_of_step.extend_from_slice(&self.input());
1138 input_of_step
1139 }
1140
1141 /// Returns the 100 step output variables, which correspond to the:
1142 /// - State G when the current step is a permutation round
1143 /// - Xor state when the current step is an absorb sponge
1144 fn output(&self) -> [Self::Variable; STATE_LEN] {
1145 array::from_fn::<_, STATE_LEN, _>(|idx| self.variable(KeccakColumn::Output(idx)))
1146 }
1147
1148 /// Returns a slice of the output variables of the current step (= input of
1149 /// next step) including the current hash index and step index
1150 fn output_of_step(&self) -> Vec<Self::Variable> {
1151 let mut output_of_step = Vec::with_capacity(STATE_LEN + 2);
1152 output_of_step.push(self.hash_index());
1153 output_of_step.push(self.step_index() + Self::one());
1154 output_of_step.extend_from_slice(&self.output());
1155 output_of_step
1156 }
1157}