ivc/poseidon_55_0_7_3_7/
mod.rs

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