ivc/poseidon_8_56_5_3_2/
mod.rs

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