Skip to content

Commit 80b3935

Browse files
committed
style: Make poseidon details easier to grok
1 parent 4d93b59 commit 80b3935

3 files changed

Lines changed: 130 additions & 68 deletions

File tree

utils/src/poseidon/poseidon_constants.rs

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -225,37 +225,43 @@ pub fn find_poseidon_ark_and_mds<F: PrimeField>(
225225
partial_rounds,
226226
);
227227

228-
let mut ark = Vec::<F>::with_capacity((full_rounds + partial_rounds) as usize);
229-
for _ in 0..(full_rounds + partial_rounds) {
230-
let values = lfsr.get_field_elements_rejection_sampling::<F>(rate);
231-
for el in values {
232-
ark.push(el);
228+
let ark = {
229+
let mut res = Vec::<F>::with_capacity((full_rounds + partial_rounds) as usize);
230+
for _ in 0..(full_rounds + partial_rounds) {
231+
let values = lfsr.get_field_elements_rejection_sampling::<F>(rate);
232+
for el in values {
233+
res.push(el);
234+
}
233235
}
234-
}
236+
res
237+
};
235238

236-
let mut mds = Vec::<Vec<F>>::with_capacity(rate);
237-
mds.resize(rate, vec![F::zero(); rate]);
239+
let mds = {
240+
let mut res = Vec::<Vec<F>>::with_capacity(rate);
241+
res.resize(rate, vec![F::zero(); rate]);
238242

239-
// Note that we build the MDS matrix generating 2*rate elements. If the matrix built is not secure (see checks with algorithm 1, 2, 3 in reference implementation)
240-
// it has to be skipped. Since here we do not implement such algorithm we allow to pass a parameter to skip generations of elements giving unsecure matrixes.
241-
// At the moment, the skip_matrices parameter has to be generated from the reference implementation and passed to this function
242-
for _ in 0..skip_matrices {
243-
let _ = lfsr.get_field_elements_mod_p::<F>(2 * (rate));
244-
}
243+
// Note that we build the MDS matrix generating 2*rate elements. If the matrix built is not secure (see checks with algorithm 1, 2, 3 in reference implementation)
244+
// it has to be skipped. Since here we do not implement such algorithm we allow to pass a parameter to skip generations of elements giving unsecure matrixes.
245+
// At the moment, the skip_matrices parameter has to be generated from the reference implementation and passed to this function
246+
for _ in 0..skip_matrices {
247+
let _ = lfsr.get_field_elements_mod_p::<F>(2 * (rate));
248+
}
245249

246-
// a qualifying matrix must satisfy the following requirements
247-
// - there is no duplication among the elements in x or y
248-
// - there is no i and j such that x[i] + y[j] = p
249-
// - the resultant MDS passes all the three tests
250+
// a qualifying matrix must satisfy the following requirements
251+
// - there is no duplication among the elements in x or y
252+
// - there is no i and j such that x[i] + y[j] = p
253+
// - the resultant MDS passes all the three tests
250254

251-
let xs = lfsr.get_field_elements_mod_p::<F>(rate);
252-
let ys = lfsr.get_field_elements_mod_p::<F>(rate);
255+
let xs = lfsr.get_field_elements_mod_p::<F>(rate);
256+
let ys = lfsr.get_field_elements_mod_p::<F>(rate);
253257

254-
for i in 0..(rate) {
255-
for (j, ys_item) in ys.iter().enumerate().take(rate) {
256-
mds[i][j] = (xs[i] + ys_item).inverse().unwrap();
258+
for i in 0..(rate) {
259+
for (j, ys_item) in ys.iter().enumerate().take(rate) {
260+
res[i][j] = (xs[i] + ys_item).inverse().unwrap();
261+
}
257262
}
258-
}
263+
res
264+
};
259265

260266
(ark, mds)
261267
}

utils/src/poseidon/poseidon_hash.rs

Lines changed: 98 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,49 +8,80 @@ use ark_ff::PrimeField;
88

99
#[derive(Debug, Clone, PartialEq, Eq)]
1010
pub struct RoundParameters<F: PrimeField> {
11+
// confirm: Is this "rate"? does this correlate with light-poseidon "width" parameter?
1112
pub t: usize,
12-
pub n_rounds_f: usize,
13-
pub n_rounds_p: usize,
13+
pub n_rounds_full: usize,
14+
pub n_rounds_partial: usize,
1415
pub skip_matrices: usize,
15-
pub c: Vec<F>,
16-
pub m: Vec<Vec<F>>,
16+
pub ark_consts: Vec<F>,
17+
pub mds: Vec<Vec<F>>,
18+
}
19+
#[derive(Debug, Clone, PartialEq, Eq)]
20+
pub struct RoundParameVec<F: PrimeField> {
21+
pub inner: Vec<RoundParameters<F>>,
1722
}
1823

24+
// Dev artifact: helps grok internal params against light-poseidon approach to params
25+
// /// Parameters for the Poseidon hash algorithm.
26+
// pub struct PoseidonParameters<F: PrimeField> {
27+
// /// Round constants.
28+
// pub ark: Vec<F>,
29+
// /// MDS matrix.
30+
// pub mds: Vec<Vec<F>>,
31+
// /// Number of full rounds (where S-box is applied to all elements of the
32+
// /// state).
33+
// pub full_rounds: usize,
34+
// /// Number of partial rounds (where S-box is applied only to the first
35+
// /// element of the state).
36+
// pub partial_rounds: usize,
37+
// /// Number of prime fields in the state.
38+
// pub width: usize,
39+
// /// Exponential used in S-box to power elements of the state.
40+
// pub alpha: u64,
41+
// }
42+
1943
pub struct Poseidon<F: PrimeField> {
2044
round_params: Vec<RoundParameters<F>>,
2145
}
22-
impl<F: PrimeField> Poseidon<F> {
23-
// Loads round parameters and generates round constants
24-
// poseidon_params is a vector containing tuples (t, RF, RP, skip_matrices)
25-
// where: t is the rate (input length + 1), RF is the number of full rounds, RP is the number of partial rounds
26-
// and skip_matrices is a (temporary) parameter used to generate secure MDS matrices (see comments in the description of find_poseidon_ark_and_mds)
27-
// TODO: implement automatic generation of round parameters
28-
pub fn from(poseidon_params: &[(usize, usize, usize, usize)]) -> Self {
46+
47+
impl<F: PrimeField> RoundParameVec<F> {
48+
fn make_param_vec(poseidon_params: &[(usize, usize, usize, usize)]) -> Self {
2949
let mut read_params = Vec::<RoundParameters<F>>::with_capacity(poseidon_params.len());
3050

31-
for &(t, n_rounds_f, n_rounds_p, skip_matrices) in poseidon_params {
51+
for &(t, n_rounds_full, n_rounds_partial, skip_matrices) in poseidon_params {
3252
let (ark, mds) = find_poseidon_ark_and_mds::<F>(
3353
1, // is_field = 1
3454
0, // is_sbox_inverse = 0
3555
F::MODULUS_BIT_SIZE as u64,
3656
t,
37-
n_rounds_f as u64,
38-
n_rounds_p as u64,
57+
n_rounds_full as u64,
58+
n_rounds_partial as u64,
3959
skip_matrices,
4060
);
4161
let rp = RoundParameters {
4262
t,
43-
n_rounds_p,
44-
n_rounds_f,
63+
n_rounds_partial,
64+
n_rounds_full,
4565
skip_matrices,
46-
c: ark,
47-
m: mds,
66+
ark_consts: ark,
67+
mds,
4868
};
4969
read_params.push(rp);
5070
}
51-
71+
Self { inner: read_params }
72+
}
73+
}
74+
impl<F: PrimeField> Poseidon<F> {
75+
// Loads round parameters and generates round constants
76+
// poseidon_params is a vector containing tuples (t, n_rounds_full, n_rounds_partial, skip_matrices)
77+
// where t is the rate (input length + 1)
78+
// and skip_matrices is a (temporary) parameter used to generate secure MDS matrices (see comments in the description of find_poseidon_ark_and_mds)
79+
// TODO: implement automatic generation of round parameters
80+
pub fn from(poseidon_params: &[(usize, usize, usize, usize)]) -> Self {
81+
let param_vec = RoundParameVec::make_param_vec(poseidon_params);
82+
// dbg!(&param_vec.inner);
5283
Poseidon {
53-
round_params: read_params,
84+
round_params: param_vec.inner,
5485
}
5586
}
5687

@@ -93,37 +124,24 @@ impl<F: PrimeField> Poseidon<F> {
93124
}
94125

95126
pub fn hash(&self, inp: &[F]) -> Result<F, String> {
127+
if inp.is_empty() {
128+
return Err("Attempt to hash empty data input".to_string());
129+
}
96130
// Note that the rate t becomes input length + 1; hence for length N we pick parameters with T = N + 1
97131
let t = inp.len() + 1;
98132

99-
// We seek the index (Poseidon's round_params is an ordered vector) for the parameters corresponding to t
100-
let param_index = self.round_params.iter().position(|el| el.t == t);
101-
102-
if inp.is_empty() || param_index.is_none() {
133+
let Some(params) = self.round_params.iter().find(|el| el.t == t) else {
103134
return Err("No parameters found for inputs length".to_string());
104-
}
105-
106-
let param_index = param_index.unwrap();
135+
};
107136

108137
let mut state = vec![F::ZERO; t];
109138
let mut state_2 = state.clone();
110139
state[1..].clone_from_slice(inp);
111140

112-
for i in 0..(self.round_params[param_index].n_rounds_f
113-
+ self.round_params[param_index].n_rounds_p)
114-
{
115-
self.ark(
116-
&mut state,
117-
&self.round_params[param_index].c,
118-
i * self.round_params[param_index].t,
119-
);
120-
self.sbox(
121-
self.round_params[param_index].n_rounds_f,
122-
self.round_params[param_index].n_rounds_p,
123-
&mut state,
124-
i,
125-
);
126-
self.mix_2(&state, &self.round_params[param_index].m, &mut state_2);
141+
for i in 0..(params.n_rounds_full + params.n_rounds_partial) {
142+
self.ark(&mut state, &params.ark_consts, i * params.t);
143+
self.sbox(params.n_rounds_full, params.n_rounds_partial, &mut state, i);
144+
self.mix_2(&state, &params.mds, &mut state_2);
127145
std::mem::swap(&mut state, &mut state_2);
128146
}
129147

@@ -140,3 +158,41 @@ where
140158
Self::from(&[])
141159
}
142160
}
161+
162+
// WIP artifact
163+
#[cfg(test)]
164+
mod test {
165+
use ark_bn254::Fr;
166+
167+
use super::*;
168+
const ROUND_PARAMS: [(usize, usize, usize, usize); 8] = [
169+
(2, 8, 56, 0),
170+
(3, 8, 57, 0),
171+
(4, 8, 56, 0),
172+
(5, 8, 60, 0),
173+
(6, 8, 60, 0),
174+
(7, 8, 63, 0),
175+
(8, 8, 64, 0),
176+
(9, 8, 63, 0),
177+
];
178+
// #[test]
179+
// fn see_params() {
180+
// let mut param_vec = RoundParameVec::<Fr>::make_param_vec(&ROUND_PARAMS);
181+
// let stats /* (rate, fulls, partual, sm, ark_n, mds_n) */ = param_vec.inner.into_iter().map(|RoundParameters { rate, n_rounds_full, n_rounds_partial, skip_matrices, ark_consts, mds }| (rate, n_rounds_full, n_rounds_partial, skip_matrices, ark_consts.len(), mds.len())).collect::<Vec<_>>();
182+
// println!("r f p s cl ml");
183+
// for s in stats.iter() {
184+
// println!("{:?}", s);
185+
// }
186+
// panic!();
187+
// }
188+
#[test]
189+
fn see_data() {
190+
let size = 10;
191+
let mut param_vec = RoundParameVec::<Fr>::make_param_vec(&ROUND_PARAMS);
192+
let mut values = Vec::with_capacity(size as usize);
193+
for i in 0..size {
194+
values.push([Fr::from(u128::MAX - i)]);
195+
}
196+
panic!("{:?}", values);
197+
}
198+
}

utils/tests/poseidon_constants.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3533,8 +3533,8 @@ mod test {
35333533
let poseidon_hasher = Poseidon::<Fr>::from(&ROUND_PARAMS);
35343534
let poseidon_parameters = poseidon_hasher.get_parameters();
35353535
for i in 0..poseidon_parameters.len() {
3536-
assert_eq!(loaded_c[i], poseidon_parameters[i].c);
3537-
assert_eq!(loaded_m[i], poseidon_parameters[i].m);
3536+
assert_eq!(loaded_c[i], poseidon_parameters[i].ark_consts);
3537+
assert_eq!(loaded_m[i], poseidon_parameters[i].mds);
35383538
}
35393539
} else {
35403540
unreachable!();

0 commit comments

Comments
 (0)