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