use ff::PrimeField as _;
use halo2_gadgets::poseidon::primitives::{P128Pow5T3, Spec};
use pasta_curves::Fp;
pub struct PoseidonHasher {
round_constants: Vec<[Fp; 3]>,
mds: [[Fp; 3]; 3],
initial_capacity: Fp,
initial_capacity_3: Fp,
}
impl Default for PoseidonHasher {
fn default() -> Self {
Self::new()
}
}
impl PoseidonHasher {
pub fn new() -> Self {
let (round_constants, mds, _) = P128Pow5T3::constants();
let initial_capacity = Fp::from_u128(2u128 << 64);
let initial_capacity_3 = Fp::from_u128(3u128 << 64);
PoseidonHasher {
round_constants,
mds,
initial_capacity,
initial_capacity_3,
}
}
#[inline]
pub fn hash(&self, left: Fp, right: Fp) -> Fp {
let mut state = [left, right, self.initial_capacity];
self.permute(&mut state);
state[0]
}
#[inline]
pub fn hash3(&self, a: Fp, b: Fp, c: Fp) -> Fp {
let mut state = [a, b, self.initial_capacity_3];
self.permute(&mut state);
state[0] += c;
self.permute(&mut state);
state[0]
}
fn permute(&self, state: &mut [Fp; 3]) {
const R_F_HALF: usize = 4; const R_P: usize = 56;
let rcs = &self.round_constants;
let mut ri = 0;
for _ in 0..R_F_HALF {
let rc = &rcs[ri];
state[0] = Self::pow5(state[0] + rc[0]);
state[1] = Self::pow5(state[1] + rc[1]);
state[2] = Self::pow5(state[2] + rc[2]);
self.apply_mds(state);
ri += 1;
}
for _ in 0..R_P {
let rc = &rcs[ri];
state[0] += rc[0];
state[1] += rc[1];
state[2] += rc[2];
state[0] = Self::pow5(state[0]);
self.apply_mds(state);
ri += 1;
}
for _ in 0..R_F_HALF {
let rc = &rcs[ri];
state[0] = Self::pow5(state[0] + rc[0]);
state[1] = Self::pow5(state[1] + rc[1]);
state[2] = Self::pow5(state[2] + rc[2]);
self.apply_mds(state);
ri += 1;
}
}
#[inline(always)]
fn pow5(x: Fp) -> Fp {
let x2 = x.square();
let x4 = x2.square();
x4 * x
}
#[inline(always)]
fn apply_mds(&self, state: &mut [Fp; 3]) {
let [s0, s1, s2] = *state;
state[0] = self.mds[0][0] * s0 + self.mds[0][1] * s1 + self.mds[0][2] * s2;
state[1] = self.mds[1][0] * s0 + self.mds[1][1] * s1 + self.mds[1][2] * s2;
state[2] = self.mds[2][0] * s0 + self.mds[2][1] * s1 + self.mds[2][2] * s2;
}
}