ivc/poseidon_55_0_7_3_2/
mod.rs

1//! Specialised circuit for Poseidon where we have maximum degree 2 constraints.
2
3pub mod columns;
4pub mod interpreter;
5
6#[cfg(test)]
7mod tests {
8    use crate::{
9        poseidon_55_0_7_3_2::{columns::PoseidonColumn, interpreter, interpreter::PoseidonParams},
10        poseidon_params_55_0_7_3,
11        poseidon_params_55_0_7_3::PlonkSpongeConstantsIVC,
12    };
13    use ark_ff::{UniformRand, Zero};
14    use kimchi_msm::{
15        circuit_design::{ColAccessCap, ConstraintBuilderEnv, WitnessBuilderEnv},
16        columns::ColumnIndexer,
17        lookups::DummyLookupTable,
18        Fp,
19    };
20    use mina_poseidon::permutation::poseidon_block_cipher;
21
22    pub struct PoseidonBN254Parameters;
23
24    pub const STATE_SIZE: usize = 3;
25    pub const NB_FULL_ROUND: usize = 55;
26    type TestPoseidonColumn = PoseidonColumn<STATE_SIZE, NB_FULL_ROUND>;
27    pub const N_COL: usize = TestPoseidonColumn::N_COL;
28    pub const N_DSEL: usize = 0;
29    pub const N_FSEL: usize = 165;
30
31    impl PoseidonParams<Fp, STATE_SIZE, NB_FULL_ROUND> for PoseidonBN254Parameters {
32        fn constants(&self) -> [[Fp; STATE_SIZE]; NB_FULL_ROUND] {
33            let rc = &poseidon_params_55_0_7_3::static_params().round_constants;
34            core::array::from_fn(|i| core::array::from_fn(|j| Fp::from(rc[i][j])))
35        }
36
37        fn mds(&self) -> [[Fp; STATE_SIZE]; STATE_SIZE] {
38            let mds = &poseidon_params_55_0_7_3::static_params().mds;
39            core::array::from_fn(|i| core::array::from_fn(|j| Fp::from(mds[i][j])))
40        }
41    }
42
43    type PoseidonWitnessBuilderEnv = WitnessBuilderEnv<
44        Fp,
45        TestPoseidonColumn,
46        { <TestPoseidonColumn as ColumnIndexer<usize>>::N_COL },
47        { <TestPoseidonColumn as ColumnIndexer<usize>>::N_COL },
48        N_DSEL,
49        N_FSEL,
50        DummyLookupTable,
51    >;
52
53    #[test]
54    /// Tests that poseidon circuit is correctly formed (witness
55    /// generation + constraints match) and matches the CPU
56    /// specification of Poseidon. Fast to run, can be used for
57    /// debugging.
58    pub fn test_poseidon_circuit() {
59        let mut rng = o1_utils::tests::make_test_rng(None);
60        let domain_size = 1 << 4;
61
62        let mut witness_env: PoseidonWitnessBuilderEnv = WitnessBuilderEnv::create();
63
64        // Write constants
65        {
66            let rc = PoseidonBN254Parameters.constants();
67            rc.iter().enumerate().for_each(|(round, rcs)| {
68                rcs.iter().enumerate().for_each(|(state_index, rc)| {
69                    let rc = vec![*rc; domain_size];
70                    witness_env.set_fixed_selector_cix(
71                        PoseidonColumn::RoundConstant(round, state_index),
72                        rc,
73                    )
74                });
75            });
76        }
77
78        // Generate random inputs at each row
79        for _row in 0..domain_size {
80            let x: Fp = Fp::rand(&mut rng);
81            let y: Fp = Fp::rand(&mut rng);
82            let z: Fp = Fp::rand(&mut rng);
83
84            interpreter::poseidon_circuit(&mut witness_env, &PoseidonBN254Parameters, [x, y, z]);
85
86            // Check internal consistency of our circuit: that our
87            // computed values match the CPU-spec implementation of
88            // Poseidon.
89            {
90                let exp_output: Vec<Fp> = {
91                    let mut state: Vec<Fp> = vec![x, y, z];
92                    poseidon_block_cipher::<Fp, PlonkSpongeConstantsIVC>(
93                        poseidon_params_55_0_7_3::static_params(),
94                        &mut state,
95                    );
96                    state
97                };
98                let x_col: PoseidonColumn<STATE_SIZE, NB_FULL_ROUND> =
99                    PoseidonColumn::Round(NB_FULL_ROUND - 1, 4);
100                let y_col: PoseidonColumn<STATE_SIZE, NB_FULL_ROUND> =
101                    PoseidonColumn::Round(NB_FULL_ROUND - 1, 9);
102                let z_col: PoseidonColumn<STATE_SIZE, NB_FULL_ROUND> =
103                    PoseidonColumn::Round(NB_FULL_ROUND - 1, 14);
104                assert_eq!(witness_env.read_column(x_col), exp_output[0]);
105                assert_eq!(witness_env.read_column(y_col), exp_output[1]);
106                assert_eq!(witness_env.read_column(z_col), exp_output[2]);
107            }
108
109            witness_env.next_row();
110        }
111    }
112
113    #[test]
114    /// Checks that poseidon circuit can be proven and verified. Big domain.
115    pub fn heavy_test_completeness() {
116        let mut rng = o1_utils::tests::make_test_rng(None);
117        let domain_size: usize = 1 << 15;
118
119        let (relation_witness, fixed_selectors) = {
120            let mut witness_env: PoseidonWitnessBuilderEnv = WitnessBuilderEnv::create();
121
122            let mut fixed_selectors: [Vec<Fp>; N_FSEL] =
123                core::array::from_fn(|_| vec![Fp::zero(); 1]);
124            // Write constants
125            {
126                let rc = PoseidonBN254Parameters.constants();
127                rc.iter().enumerate().for_each(|(round, rcs)| {
128                    rcs.iter().enumerate().for_each(|(state_index, rc)| {
129                        witness_env.set_fixed_selector_cix(
130                            PoseidonColumn::RoundConstant(round, state_index),
131                            vec![*rc; domain_size],
132                        );
133                        fixed_selectors[round * STATE_SIZE + state_index] = vec![*rc; domain_size];
134                    });
135                });
136            }
137
138            // Generate random inputs at each row
139            for _row in 0..domain_size {
140                let x: Fp = Fp::rand(&mut rng);
141                let y: Fp = Fp::rand(&mut rng);
142                let z: Fp = Fp::rand(&mut rng);
143
144                interpreter::poseidon_circuit(
145                    &mut witness_env,
146                    &PoseidonBN254Parameters,
147                    [x, y, z],
148                );
149
150                witness_env.next_row();
151            }
152
153            (
154                witness_env.get_relation_witness(domain_size),
155                fixed_selectors,
156            )
157        };
158
159        let constraints = {
160            let mut constraint_env = ConstraintBuilderEnv::<Fp, DummyLookupTable>::create();
161            interpreter::apply_permutation(&mut constraint_env, &PoseidonBN254Parameters);
162            let constraints = constraint_env.get_constraints();
163
164            // We have 825 constraints in total
165            assert_eq!(constraints.len(), 5 * STATE_SIZE * NB_FULL_ROUND);
166            // Maximum degree of the constraints is 2
167            assert_eq!(constraints.iter().map(|c| c.degree(1, 0)).max().unwrap(), 2);
168
169            constraints
170        };
171
172        kimchi_msm::test::test_completeness_generic_no_lookups::<N_COL, N_COL, N_DSEL, N_FSEL, _>(
173            constraints,
174            Box::new(fixed_selectors),
175            relation_witness,
176            domain_size,
177            &mut rng,
178        );
179    }
180}