use miden_crypto::Word as Digest;
use super::Felt;
pub use crate::crypto::hash::Poseidon2 as Hasher;
pub const STATE_WIDTH: usize = Hasher::STATE_WIDTH;
pub const RATE_LEN: usize = 8;
pub const NUM_ROUNDS: usize = 31;
#[inline(always)]
pub fn merge(values: &[Digest; 2]) -> Digest {
Hasher::merge(values)
}
#[inline(always)]
pub fn merge_in_domain(values: &[Digest; 2], domain: Felt) -> Digest {
Hasher::merge_in_domain(values, domain)
}
#[inline(always)]
pub fn hash_elements(elements: &[Felt]) -> Digest {
Hasher::hash_elements(elements)
}
#[inline(always)]
pub fn apply_round(state: &mut [Felt; STATE_WIDTH], round: usize) {
apply_poseidon2_step(state, round)
}
#[inline(always)]
pub fn apply_permutation(state: &mut [Felt; STATE_WIDTH]) {
Hasher::apply_permutation(state)
}
#[inline(always)]
fn apply_poseidon2_step(state: &mut [Felt; STATE_WIDTH], step: usize) {
match step {
0 => {
Hasher::apply_matmul_external(state);
},
1..=4 => {
Hasher::add_rc(state, &Hasher::ARK_EXT_INITIAL[step - 1]);
Hasher::apply_sbox(state);
Hasher::apply_matmul_external(state);
},
5..=26 => {
state[0] += Hasher::ARK_INT[step - 5];
state[0] = state[0].exp_const_u64::<7>();
Hasher::matmul_internal(state, Hasher::MAT_DIAG);
},
27..=30 => {
Hasher::add_rc(state, &Hasher::ARK_EXT_TERMINAL[step - 27]);
Hasher::apply_sbox(state);
Hasher::apply_matmul_external(state);
},
_ => panic!("invalid poseidon2 step {step}, expected 0..30"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn apply_round_matches_permutation() {
let mut state_stepwise = [Felt::ZERO; STATE_WIDTH];
let mut state_permutation = [Felt::ZERO; STATE_WIDTH];
for i in 0..NUM_ROUNDS {
apply_round(&mut state_stepwise, i);
}
apply_permutation(&mut state_permutation);
assert_eq!(state_stepwise, state_permutation, "mismatch with zero state");
let mut state_stepwise: [Felt; STATE_WIDTH] = core::array::from_fn(|i| Felt::new(i as u64));
let mut state_permutation = state_stepwise;
for i in 0..NUM_ROUNDS {
apply_round(&mut state_stepwise, i);
}
apply_permutation(&mut state_permutation);
assert_eq!(state_stepwise, state_permutation, "mismatch with sequential state");
let mut state_stepwise: [Felt; STATE_WIDTH] = [
Felt::new(0x123456789abcdef0_u64),
Felt::new(0xfedcba9876543210_u64),
Felt::new(0x0011223344556677_u64),
Felt::new(0x8899aabbccddeeff_u64),
Felt::new(0xdeadbeefcafebabe_u64),
Felt::new(0x1234567890abcdef_u64),
Felt::new(0x1234567890abcdef_u64),
Felt::new(0x0badc0debadf00d0_u64),
Felt::new(0x1111111111111111_u64),
Felt::new(0x2222222222222222_u64),
Felt::new(0x3333333333333333_u64),
Felt::new(0x4444444444444444_u64),
];
let mut state_permutation = state_stepwise;
for i in 0..NUM_ROUNDS {
apply_round(&mut state_stepwise, i);
}
apply_permutation(&mut state_permutation);
assert_eq!(state_stepwise, state_permutation, "mismatch with random state");
}
#[test]
fn apply_round_intermediate_states() {
let init_state: [Felt; STATE_WIDTH] = core::array::from_fn(|i| Felt::new((i + 1) as u64));
let mut state_half1 = init_state;
for i in 0..15 {
apply_round(&mut state_half1, i);
}
let mut state_half2 = state_half1;
for i in 15..NUM_ROUNDS {
apply_round(&mut state_half2, i);
}
let mut state_full = init_state;
apply_permutation(&mut state_full);
assert_eq!(state_half2, state_full, "split application doesn't match full permutation");
}
}