Skip to main content

sp1_hypercube/operations/poseidon2/
trace.rs

1//! Trace (witness) population functions for the Poseidon2 operation.
2
3use std::borrow::Borrow;
4
5use slop_algebra::PrimeField32;
6use slop_koala_bear::{
7    KoalaBear_BEGIN_EXT_CONSTS, KoalaBear_END_EXT_CONSTS, KoalaBear_PARTIAL_CONSTS,
8};
9
10use super::{
11    air::{external_linear_layer, external_linear_layer_mut, internal_linear_layer_mut},
12    permutation::permutation_mut,
13    Poseidon2Operation, NUM_EXTERNAL_ROUNDS, NUM_INTERNAL_ROUNDS, NUM_POSEIDON2_OPERATION_COLUMNS,
14    WIDTH,
15};
16
17/// Populate a degree 3 `Poseidon2Operation`.
18pub fn populate_perm_deg3<F: PrimeField32>(
19    input: [F; WIDTH],
20    expected_output: Option<[F; WIDTH]>,
21) -> Poseidon2Operation<F> {
22    let mut row: Vec<F> = vec![F::zero(); NUM_POSEIDON2_OPERATION_COLUMNS];
23    populate_perm::<F, 3>(input, expected_output, row.as_mut_slice());
24    let op: &Poseidon2Operation<F> = row.as_slice().borrow();
25    *op
26}
27
28/// Populate a Poseidon2 AIR row.
29pub fn populate_perm<F: PrimeField32, const DEGREE: usize>(
30    input: [F; WIDTH],
31    expected_output: Option<[F; WIDTH]>,
32    input_row: &mut [F],
33) {
34    {
35        let permutation = permutation_mut::<F, DEGREE>(input_row);
36
37        let (external_rounds_state, internal_rounds_state, internal_rounds_s0, output_state) =
38            permutation.get_cols_mut();
39
40        external_rounds_state[0] = input;
41
42        // Apply the first half of external rounds.
43        for r in 0..NUM_EXTERNAL_ROUNDS / 2 {
44            let next_state = populate_external_round::<F, DEGREE>(external_rounds_state, r);
45            if r == NUM_EXTERNAL_ROUNDS / 2 - 1 {
46                *internal_rounds_state = next_state;
47            } else {
48                external_rounds_state[r + 1] = next_state;
49            }
50        }
51
52        // Apply the internal rounds.
53        external_rounds_state[NUM_EXTERNAL_ROUNDS / 2] =
54            populate_internal_rounds(internal_rounds_state, internal_rounds_s0);
55
56        // Apply the second half of external rounds.
57        for r in NUM_EXTERNAL_ROUNDS / 2..NUM_EXTERNAL_ROUNDS {
58            let next_state = populate_external_round::<F, DEGREE>(external_rounds_state, r);
59            if r == NUM_EXTERNAL_ROUNDS - 1 {
60                for i in 0..WIDTH {
61                    output_state[i] = next_state[i];
62                    if let Some(expected_output) = expected_output {
63                        assert_eq!(expected_output[i], next_state[i]);
64                    }
65                }
66            } else {
67                external_rounds_state[r + 1] = next_state;
68            }
69        }
70    }
71}
72
73/// Populate the `r`th external round.
74pub fn populate_external_round<F: PrimeField32, const DEGREE: usize>(
75    external_rounds_state: &[[F; WIDTH]],
76    r: usize,
77) -> [F; WIDTH] {
78    let mut state = {
79        // For the first round, apply the linear layer.
80        let round_state: &[F; WIDTH] = if r == 0 {
81            &external_linear_layer(&external_rounds_state[r])
82        } else {
83            &external_rounds_state[r]
84        };
85
86        // Add round constants.
87        //
88        // Optimization: Since adding a constant is a degree 1 operation, we can avoid adding
89        // columns for it, and instead include it in the constraint for the x^3 part of the
90        // sbox.
91        let mut add_rc = *round_state;
92        for i in 0..WIDTH {
93            add_rc[i] += if r < NUM_EXTERNAL_ROUNDS / 2 {
94                F::from_canonical_u32(KoalaBear_BEGIN_EXT_CONSTS[r][i].as_canonical_u32())
95            } else {
96                F::from_canonical_u32(
97                    KoalaBear_END_EXT_CONSTS[r - NUM_EXTERNAL_ROUNDS / 2][i].as_canonical_u32(),
98                )
99            };
100        }
101
102        // Apply the sboxes.
103        // Optimization: since the linear layer that comes after the sbox is degree 1, we can
104        // avoid adding columns for the result of the sbox, and instead include the x^3 -> x^7
105        // part of the sbox in the constraint for the linear layer
106        let mut sbox_deg_3: [F; 16] = [F::zero(); WIDTH];
107        for i in 0..WIDTH {
108            sbox_deg_3[i] = add_rc[i] * add_rc[i] * add_rc[i];
109        }
110
111        sbox_deg_3
112    };
113
114    // Apply the linear layer.
115    external_linear_layer_mut(&mut state);
116    state
117}
118
119/// Populate all internal rounds.
120pub fn populate_internal_rounds<F: PrimeField32>(
121    internal_rounds_state: &[F; WIDTH],
122    internal_rounds_s0: &mut [F; NUM_INTERNAL_ROUNDS - 1],
123) -> [F; WIDTH] {
124    let mut state: [F; WIDTH] = *internal_rounds_state;
125    for r in 0..NUM_INTERNAL_ROUNDS {
126        // Add the round constant to the 0th state element.
127        // Optimization: Since adding a constant is a degree 1 operation, we can avoid adding
128        // columns for it, just like for external rounds.
129        let add_rc =
130            state[0] + F::from_canonical_u32(KoalaBear_PARTIAL_CONSTS[r].as_canonical_u32());
131
132        // Apply the sboxes.
133        // Optimization: since the linear layer that comes after the sbox is degree 1, we can
134        // avoid adding columns for the result of the sbox, just like for external rounds.
135        let sbox_deg_3 = add_rc * add_rc * add_rc;
136
137        // Apply the linear layer.
138        state[0] = sbox_deg_3;
139        internal_linear_layer_mut(&mut state);
140
141        // Optimization: since we're only applying the sbox to the 0th state element, we only
142        // need to have columns for the 0th state element at every step. This is because the
143        // linear layer is degree 1, so all state elements at the end can be expressed as a
144        // degree-3 polynomial of the state at the beginning of the internal rounds and the 0th
145        // state element at rounds prior to the current round
146        if r < NUM_INTERNAL_ROUNDS - 1 {
147            internal_rounds_s0[r] = state[0];
148        }
149    }
150
151    state
152}