use alloc::boxed::Box;
use alloc::vec::Vec;
use crate::{
Blake3Transcript, Hash, Poseidon1_16, Poseidon2_16, PrimeField64, Transcript, BLAKE3_TRANSCRIPT_STATE_WORDS,
BLAKE3_TRANSCRIPT_XOF_WORDS,
};
pub enum TranscriptDyn<F: PrimeField64> {
Poseidon1(Transcript<F, Poseidon1_16>),
Poseidon2(Transcript<F, Poseidon2_16>),
Blake3(Box<Blake3Transcript<F>>),
}
impl<F: PrimeField64> TranscriptDyn<F> {
pub fn put(&mut self, inputs: &[F]) {
match self {
TranscriptDyn::Poseidon1(t) => t.put(inputs),
TranscriptDyn::Poseidon2(t) => t.put(inputs),
TranscriptDyn::Blake3(t) => t.put(inputs),
}
}
pub fn get_state(&mut self) -> Vec<F> {
match self {
TranscriptDyn::Poseidon1(t) => t.get_state(),
TranscriptDyn::Poseidon2(t) => t.get_state(),
TranscriptDyn::Blake3(t) => t.get_state(),
}
}
pub fn get_chain_state(&mut self) -> Vec<F> {
match self {
TranscriptDyn::Poseidon1(t) => t.get_state(),
TranscriptDyn::Poseidon2(t) => t.get_state(),
TranscriptDyn::Blake3(t) => t.get_xof_block(),
}
}
pub fn get_field(&mut self, value: &mut [F]) {
match self {
TranscriptDyn::Poseidon1(t) => t.get_field(value),
TranscriptDyn::Poseidon2(t) => t.get_field(value),
TranscriptDyn::Blake3(t) => t.get_field(value),
}
}
}
pub fn new_transcript<F: PrimeField64>(hash_id: &str) -> TranscriptDyn<F> {
match hash_id {
"Poseidon1" => TranscriptDyn::Poseidon1(Transcript::<F, Poseidon1_16>::new()),
"Poseidon2" => TranscriptDyn::Poseidon2(Transcript::<F, Poseidon2_16>::new()),
"blake3" => TranscriptDyn::Blake3(Box::default()),
other => panic!("Unknown hash family: {other:?}"),
}
}
pub fn hash_state<F: PrimeField64>(hash_id: &str, state: &mut [F]) {
match (hash_id, state.len()) {
("Poseidon1", 16) => Poseidon1_16::hash(state.try_into().unwrap()),
("Poseidon2", 16) => Poseidon2_16::hash(state.try_into().unwrap()),
("blake3", BLAKE3_TRANSCRIPT_STATE_WORDS) => {
let mut transcript = Blake3Transcript::<F>::new();
transcript.put(state);
state.copy_from_slice(&transcript.get_state());
}
("blake3", BLAKE3_TRANSCRIPT_XOF_WORDS) => {
let mut transcript = Blake3Transcript::<F>::new();
transcript.put(state);
state.copy_from_slice(&transcript.get_xof_block());
}
(other, n) => panic!("Unknown hash family/width: {other:?}/{n}"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Goldilocks;
#[test]
fn chain_state_width_is_the_families_round_width() {
let cases = [("blake3", BLAKE3_TRANSCRIPT_XOF_WORDS), ("Poseidon1", 16), ("Poseidon2", 16)];
for (family, want) in cases {
let mut t = new_transcript::<Goldilocks>(family);
t.put(&[Goldilocks::from_u64(7)]);
assert_eq!(t.get_chain_state().len(), want, "{family} chain width");
}
}
#[test]
fn hash_state_accepts_every_chain_width() {
for family in ["blake3", "Poseidon1", "Poseidon2"] {
let mut t = new_transcript::<Goldilocks>(family);
t.put(&[Goldilocks::from_u64(1)]);
let mut state = t.get_chain_state();
hash_state(family, &mut state);
}
}
#[test]
fn blake3_chain_state_is_the_squeezed_xof_block() {
let input: Vec<Goldilocks> = (1..=5).map(Goldilocks::from_u64).collect();
let mut block = Blake3Transcript::<Goldilocks>::new();
block.put(&input);
let whole = block.get_xof_block();
assert_eq!(whole.len(), BLAKE3_TRANSCRIPT_XOF_WORDS);
let mut squeezed = Blake3Transcript::<Goldilocks>::new();
squeezed.put(&input);
let mut first_three = [Goldilocks::from_u64(0); 3];
squeezed.get_field(&mut first_three);
assert_eq!(&whole[..3], &first_three[..], "same XOF block, read two ways");
}
}