ivc/poseidon_55_0_7_3_7/
interpreter.rs1use crate::poseidon_55_0_7_3_7::columns::PoseidonColumn;
15use ark_ff::PrimeField;
16use kimchi_msm::circuit_design::{ColAccessCap, ColWriteCap, HybridCopyCap};
17use num_bigint::BigUint;
18use num_integer::Integer;
19
20pub trait PoseidonParams<F: PrimeField, const STATE_SIZE: usize, const NB_FULL_ROUNDS: usize> {
28 fn constants(&self) -> [[F; STATE_SIZE]; NB_FULL_ROUNDS];
29 fn mds(&self) -> [[F; STATE_SIZE]; STATE_SIZE];
30}
31
32pub fn poseidon_circuit<
34 F: PrimeField,
35 const STATE_SIZE: usize,
36 const NB_FULL_ROUND: usize,
37 PARAMETERS,
38 Env,
39>(
40 env: &mut Env,
41 param: &PARAMETERS,
42 init_state: [Env::Variable; STATE_SIZE],
43) -> [Env::Variable; STATE_SIZE]
44where
45 PARAMETERS: PoseidonParams<F, STATE_SIZE, NB_FULL_ROUND>,
46 Env: ColWriteCap<F, PoseidonColumn<STATE_SIZE, NB_FULL_ROUND>>
47 + HybridCopyCap<F, PoseidonColumn<STATE_SIZE, NB_FULL_ROUND>>,
48{
49 init_state.iter().enumerate().for_each(|(i, value)| {
51 env.write_column(PoseidonColumn::Input(i), value);
52 });
53
54 apply_permutation(env, param)
56}
57
58pub fn apply_permutation<
61 F: PrimeField,
62 const STATE_SIZE: usize,
63 const NB_FULL_ROUND: usize,
64 PARAMETERS,
65 Env,
66>(
67 env: &mut Env,
68 param: &PARAMETERS,
69) -> [Env::Variable; STATE_SIZE]
70where
71 PARAMETERS: PoseidonParams<F, STATE_SIZE, NB_FULL_ROUND>,
72 Env: ColAccessCap<F, PoseidonColumn<STATE_SIZE, NB_FULL_ROUND>>
73 + HybridCopyCap<F, PoseidonColumn<STATE_SIZE, NB_FULL_ROUND>>,
74{
75 {
77 let one = BigUint::from(1u64);
78 let p: BigUint = TryFrom::try_from(<F as PrimeField>::MODULUS).unwrap();
79 let p_minus_one = p - one.clone();
80 let seven = BigUint::from(7u64);
81 assert_eq!(p_minus_one.gcd(&seven), one);
82 }
83
84 let mut final_state: [Env::Variable; STATE_SIZE] =
85 core::array::from_fn(|_| Env::constant(F::zero()));
86
87 for i in 0..NB_FULL_ROUND {
88 let state: [PoseidonColumn<STATE_SIZE, NB_FULL_ROUND>; STATE_SIZE] = {
89 if i == 0 {
90 core::array::from_fn(PoseidonColumn::Input)
91 } else {
92 core::array::from_fn(|j| PoseidonColumn::Round(i - 1, j))
93 }
94 };
95 let round_res = compute_one_round::<F, STATE_SIZE, NB_FULL_ROUND, PARAMETERS, Env>(
96 env, param, i, &state,
97 );
98
99 if i == NB_FULL_ROUND - 1 {
100 final_state = round_res
101 }
102 }
103
104 final_state
105}
106
107fn compute_one_round<
109 F: PrimeField,
110 const STATE_SIZE: usize,
111 const NB_FULL_ROUND: usize,
112 PARAMETERS,
113 Env,
114>(
115 env: &mut Env,
116 param: &PARAMETERS,
117 round: usize,
118 elements: &[PoseidonColumn<STATE_SIZE, NB_FULL_ROUND>; STATE_SIZE],
119) -> [Env::Variable; STATE_SIZE]
120where
121 PARAMETERS: PoseidonParams<F, STATE_SIZE, NB_FULL_ROUND>,
122 Env: ColAccessCap<F, PoseidonColumn<STATE_SIZE, NB_FULL_ROUND>>
123 + HybridCopyCap<F, PoseidonColumn<STATE_SIZE, NB_FULL_ROUND>>,
124{
125 assert!(
129 round < NB_FULL_ROUND,
130 "The round index {:} is higher than the number of full rounds encoded in the type",
131 round
132 );
133 let state: Vec<Env::Variable> = elements
135 .iter()
136 .map(|x| {
137 let x_col = env.read_column(*x);
138 let x_square = x_col.clone() * x_col.clone();
139 let x_four = x_square.clone() * x_square.clone();
140 x_four.clone() * x_square.clone() * x_col.clone()
141 })
142 .collect();
143
144 let mds = PoseidonParams::mds(param);
146 let state: Vec<Env::Variable> = mds
147 .into_iter()
148 .map(|m| {
149 state
150 .clone()
151 .into_iter()
152 .zip(m)
153 .fold(Env::constant(F::zero()), |acc, (s_i, mds_i_j)| {
154 Env::constant(mds_i_j) * s_i.clone() + acc.clone()
155 })
156 })
157 .collect();
158
159 let state: Vec<Env::Variable> = state
161 .iter()
162 .enumerate()
163 .map(|(i, x)| {
164 let rc = env.read_column(PoseidonColumn::RoundConstant(round, i));
165 x.clone() + rc
166 })
167 .collect();
168
169 let res_state: Vec<Env::Variable> = state
170 .iter()
171 .enumerate()
172 .map(|(i, res)| env.hcopy(res, PoseidonColumn::Round(round, i)))
173 .collect();
174
175 res_state
176 .try_into()
177 .expect("Resulting state must be of STATE_SIZE length")
178}