use crate::FieldElement;
use generic_array::typenum::U2;
use nova_snark::frontend::gadgets::poseidon::{
IOPattern, PoseidonConstants, Simplex, Sponge, SpongeAPI, SpongeOp, SpongeTrait, Strength,
};
use once_cell::sync::Lazy;
static POSEIDON_CONSTANTS: Lazy<PoseidonConstants<FieldElement, U2>> =
Lazy::new(|| Sponge::<FieldElement, U2>::api_constants(Strength::Standard));
static IO_PATTERN_2: Lazy<IOPattern> =
Lazy::new(|| IOPattern(vec![SpongeOp::Absorb(2), SpongeOp::Squeeze(1)]));
static IO_PATTERN_3: Lazy<IOPattern> =
Lazy::new(|| IOPattern(vec![SpongeOp::Absorb(3), SpongeOp::Squeeze(1)]));
mod tag_values {
pub const LEAF: u64 = 1;
pub const NODE: u64 = 2;
pub const CHALLENGE: u64 = 6;
pub const STATE_UPDATE: u64 = 7;
pub const ROOT_COMMITMENT: u64 = 8;
pub const CHALLENGE_PER_FILE: u64 = 9;
pub const CHALLENGE_ID: u64 = 10;
}
pub mod domain_tags {
use super::tag_values;
use ff::PrimeField;
pub fn leaf<F: PrimeField>() -> F {
F::from(tag_values::LEAF)
}
pub fn node<F: PrimeField>() -> F {
F::from(tag_values::NODE)
}
pub fn challenge<F: PrimeField>() -> F {
F::from(tag_values::CHALLENGE)
}
pub fn state_update<F: PrimeField>() -> F {
F::from(tag_values::STATE_UPDATE)
}
pub fn root_commitment<F: PrimeField>() -> F {
F::from(tag_values::ROOT_COMMITMENT)
}
pub fn challenge_per_file<F: PrimeField>() -> F {
F::from(tag_values::CHALLENGE_PER_FILE)
}
pub fn challenge_id<F: PrimeField>() -> F {
F::from(tag_values::CHALLENGE_ID)
}
}
pub fn poseidon_hash2(left: FieldElement, right: FieldElement) -> FieldElement {
let mut sponge = Sponge::<FieldElement, U2>::new_with_constants(&POSEIDON_CONSTANTS, Simplex);
let mut acc = ();
sponge.start(IO_PATTERN_2.clone(), None, &mut acc);
SpongeAPI::absorb(&mut sponge, 2, &[left, right], &mut acc);
let output = SpongeAPI::squeeze(&mut sponge, 1, &mut acc);
sponge
.finish(&mut acc)
.expect("Poseidon sponge finish should not fail");
output[0]
}
pub fn poseidon_hash_tagged(tag: FieldElement, x: FieldElement, y: FieldElement) -> FieldElement {
let mut sponge = Sponge::<FieldElement, U2>::new_with_constants(&POSEIDON_CONSTANTS, Simplex);
let mut acc = ();
sponge.start(IO_PATTERN_3.clone(), None, &mut acc);
SpongeAPI::absorb(&mut sponge, 3, &[tag, x, y], &mut acc);
let output = SpongeAPI::squeeze(&mut sponge, 1, &mut acc);
sponge
.finish(&mut acc)
.expect("Poseidon sponge finish should not fail");
output[0]
}
pub fn calculate_root_commitment(root: FieldElement, depth: FieldElement) -> FieldElement {
poseidon_hash_tagged(domain_tags::root_commitment(), root, depth)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::FieldElement;
#[test]
fn test_domain_separation() {
let x = FieldElement::from(42u64);
let y = FieldElement::from(123u64);
let h_leaf = poseidon_hash_tagged(domain_tags::leaf(), x, y);
let h_node = poseidon_hash_tagged(domain_tags::node(), x, y);
let h_root_commitment = poseidon_hash_tagged(domain_tags::root_commitment(), x, y);
let h_challenge = poseidon_hash_tagged(domain_tags::challenge(), x, y);
let h_state = poseidon_hash_tagged(domain_tags::state_update(), x, y);
assert_ne!(h_leaf, h_node, "leaf and node hashes should differ");
assert_ne!(
h_leaf, h_root_commitment,
"leaf and root_commitment hashes should differ"
);
assert_ne!(
h_leaf, h_challenge,
"leaf and challenge hashes should differ"
);
assert_ne!(h_leaf, h_state, "leaf and state hashes should differ");
assert_ne!(
h_node, h_root_commitment,
"node and root_commitment hashes should differ"
);
assert_ne!(
h_node, h_challenge,
"node and challenge hashes should differ"
);
assert_ne!(h_node, h_state, "node and state hashes should differ");
assert_ne!(
h_root_commitment, h_challenge,
"root_commitment and challenge hashes should differ"
);
assert_ne!(
h_root_commitment, h_state,
"root_commitment and state hashes should differ"
);
assert_ne!(
h_challenge, h_state,
"challenge and state hashes should differ"
);
let root = FieldElement::from(1u64);
let depth = FieldElement::from(10u64);
let rc_commitment = calculate_root_commitment(root, depth);
let rc_commitment2 = calculate_root_commitment(root, depth);
assert_eq!(
rc_commitment, rc_commitment2,
"RC commitment should be deterministic"
);
}
}