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}