Skip to main content

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::{self, FULL_ROUNDS},
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, const FULL_ROUNDS: usize> {
26    sponge: ArithmeticSponge<Fp, SC, FULL_ROUNDS>,
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, const FULL_ROUNDS: usize> Poseidon<SC, H, FULL_ROUNDS> {
34    fn new(
35        domain_param: H::D,
36        sponge_params: &'static ArithmeticSpongeParams<Fp, FULL_ROUNDS>,
37    ) -> Self {
38        let mut poseidon = Self {
39            sponge: ArithmeticSponge::<Fp, SC, FULL_ROUNDS>::new(sponge_params),
40            sponge_state: SpongeState::Absorbed(0),
41            state: vec![],
42            phantom: PhantomData,
43        };
44
45        poseidon.init(domain_param);
46
47        poseidon
48    }
49}
50
51/// Poseidon hasher type with legacy plonk sponge constants
52pub type PoseidonHasherLegacy<H> = Poseidon<PlonkSpongeConstantsLegacy, H, 100>;
53
54/// Create a legacy hasher context
55pub(crate) fn new_legacy<H: Hashable>(domain_param: H::D) -> PoseidonHasherLegacy<H> {
56    Poseidon::<PlonkSpongeConstantsLegacy, H, 100>::new(
57        domain_param,
58        pasta::fp_legacy::static_params(),
59    )
60}
61
62/// Poseidon hasher type with experimental kimchi plonk sponge constants
63pub type PoseidonHasherKimchi<H> = Poseidon<PlonkSpongeConstantsKimchi, H, FULL_ROUNDS>;
64
65/// Create an experimental kimchi hasher context
66pub(crate) fn new_kimchi<H: Hashable>(domain_param: H::D) -> PoseidonHasherKimchi<H> {
67    Poseidon::<PlonkSpongeConstantsKimchi, H, FULL_ROUNDS>::new(
68        domain_param,
69        pasta::fp_kimchi::static_params(),
70    )
71}
72
73impl<SC: SpongeConstants, H: Hashable, const FULL_ROUNDS: usize> Hasher<H>
74    for Poseidon<SC, H, FULL_ROUNDS>
75where
76    H::D: DomainParameter,
77{
78    fn reset(&mut self) -> &mut dyn Hasher<H> {
79        // Efficient reset
80        self.sponge.sponge_state = self.sponge_state.clone();
81        self.sponge.state.clone_from(&self.state);
82
83        self
84    }
85
86    fn init(&mut self, domain_param: H::D) -> &mut dyn Hasher<H> {
87        // Set sponge initial state and save it so the hasher context can be
88        // reused efficiently
89        // N.B. Mina sets the sponge's initial state by hashing the input type's
90        // domain bytes
91        self.sponge.reset();
92
93        if let Some(domain_string) = H::domain_string(domain_param) {
94            self.sponge
95                .absorb(&[domain_prefix_to_field::<Fp>(domain_string)]);
96            self.sponge.squeeze();
97        }
98
99        // Save initial state for efficient reset
100        self.sponge_state = self.sponge.sponge_state.clone();
101        self.state.clone_from(&self.sponge.state);
102
103        self
104    }
105
106    fn update(&mut self, input: &H) -> &mut dyn Hasher<H> {
107        self.sponge.absorb(&input.to_roinput().to_fields());
108
109        self
110    }
111
112    fn digest(&mut self) -> Fp {
113        let output = self.sponge.squeeze();
114        self.sponge.reset();
115        output
116    }
117}