mina_hasher/
poseidon.rs

1//! Mina Poseidon hasher
2//!
3//! An implementation of Mina's hasher based on the poseidon arithmetic sponge
4
5use alloc::{vec, vec::Vec};
6
7use core::marker::PhantomData;
8
9use crate::DomainParameter;
10use mina_curves::pasta::Fp;
11use mina_poseidon::{
12    constants::{PlonkSpongeConstantsKimchi, PlonkSpongeConstantsLegacy, SpongeConstants},
13    pasta,
14    poseidon::{ArithmeticSponge, ArithmeticSpongeParams, Sponge, SpongeState},
15};
16
17use super::{domain_prefix_to_field, Hashable, Hasher};
18
19/// Poseidon hasher context
20//
21//  The arithmetic sponge parameters are large and costly to initialize,
22//  so we only want to do this once and then re-use the Poseidon context
23//  for many hashes. Also, following approach of the mina code we store
24//  a backup of the initialized sponge state for efficient reuse.
25pub struct Poseidon<SC: SpongeConstants, H: Hashable> {
26    sponge: ArithmeticSponge<Fp, SC>,
27    sponge_state: SpongeState,
28    /// The state of the sponge
29    pub state: Vec<Fp>,
30    phantom: PhantomData<H>,
31}
32
33impl<SC: SpongeConstants, H: Hashable> Poseidon<SC, H> {
34    fn new(domain_param: H::D, sponge_params: &'static ArithmeticSpongeParams<Fp>) -> Self {
35        let mut poseidon = Poseidon::<SC, H> {
36            sponge: ArithmeticSponge::<Fp, SC>::new(sponge_params),
37            sponge_state: SpongeState::Absorbed(0),
38            state: vec![],
39            phantom: PhantomData,
40        };
41
42        poseidon.init(domain_param);
43
44        poseidon
45    }
46}
47
48/// Poseidon hasher type with legacy plonk sponge constants
49pub type PoseidonHasherLegacy<H> = Poseidon<PlonkSpongeConstantsLegacy, H>;
50
51/// Create a legacy hasher context
52pub(crate) fn new_legacy<H: Hashable>(domain_param: H::D) -> PoseidonHasherLegacy<H> {
53    Poseidon::<PlonkSpongeConstantsLegacy, H>::new(domain_param, pasta::fp_legacy::static_params())
54}
55
56/// Poseidon hasher type with experimental kimchi plonk sponge constants
57pub type PoseidonHasherKimchi<H> = Poseidon<PlonkSpongeConstantsKimchi, H>;
58
59/// Create an experimental kimchi hasher context
60pub(crate) fn new_kimchi<H: Hashable>(domain_param: H::D) -> PoseidonHasherKimchi<H> {
61    Poseidon::<PlonkSpongeConstantsKimchi, H>::new(domain_param, pasta::fp_kimchi::static_params())
62}
63
64impl<SC: SpongeConstants, H: Hashable> Hasher<H> for Poseidon<SC, H>
65where
66    H::D: DomainParameter,
67{
68    fn reset(&mut self) -> &mut dyn Hasher<H> {
69        // Efficient reset
70        self.sponge.sponge_state = self.sponge_state.clone();
71        self.sponge.state.clone_from(&self.state);
72
73        self
74    }
75
76    fn init(&mut self, domain_param: H::D) -> &mut dyn Hasher<H> {
77        // Set sponge initial state and save it so the hasher context can be
78        // reused efficiently
79        // N.B. Mina sets the sponge's initial state by hashing the input type's
80        // domain bytes
81        self.sponge.reset();
82
83        if let Some(domain_string) = H::domain_string(domain_param) {
84            self.sponge
85                .absorb(&[domain_prefix_to_field::<Fp>(domain_string)]);
86            self.sponge.squeeze();
87        }
88
89        // Save initial state for efficient reset
90        self.sponge_state = self.sponge.sponge_state.clone();
91        self.state.clone_from(&self.sponge.state);
92
93        self
94    }
95
96    fn update(&mut self, input: &H) -> &mut dyn Hasher<H> {
97        self.sponge.absorb(&input.to_roinput().to_fields());
98
99        self
100    }
101
102    fn digest(&mut self) -> Fp {
103        let output = self.sponge.squeeze();
104        self.sponge.reset();
105        output
106    }
107}