poseidon_primitives/
poseidon.rs

1//! The Poseidon algebraic hash function.
2
3pub mod primitives;
4/*
5use std::convert::TryInto;
6use std::fmt;
7use std::marker::PhantomData;
8
9use halo2_proofs::{
10    arithmetic::{Field, FieldExt},
11    circuit::{AssignedCell, Chip, Layouter},
12    plonk::{ConstraintSystem, Error},
13};
14
15mod pow5;
16pub use pow5::{Pow5Chip, Pow5Config, StateWord, Var};
17
18mod septidon;
19pub use septidon::{CachedConstants, SeptidonChip};
20
21pub mod primitives;
22use primitives::{Absorbing, ConstantLength, Domain, Spec, SpongeMode, Squeezing, State};
23use std::fmt::Debug as DebugT;
24
25/// A word from the padded input to a Poseidon sponge.
26#[derive(Clone, Debug)]
27pub enum PaddedWord<F: Field> {
28    /// A message word provided by the prover.
29    Message(AssignedCell<F, F>),
30    /// A padding word, that will be fixed in the circuit parameters.
31    Padding(F),
32}
33
34/// This trait is the interface to chips that implement a permutation.
35pub trait PermuteChip<F: FieldExt, S: Spec<F, T, RATE>, const T: usize, const RATE: usize>:
36    Chip<F> + Clone + DebugT + PoseidonInstructions<F, S, T, RATE>
37{
38    /// Configure the permutation chip.
39    fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config;
40
41    /// Get a chip from its config.
42    fn construct(config: Self::Config) -> Self;
43}
44
45/// The set of circuit instructions required to use the Poseidon permutation.
46pub trait PoseidonInstructions<F: FieldExt, S: Spec<F, T, RATE>, const T: usize, const RATE: usize>:
47    Chip<F>
48{
49    /// Variable representing the word over which the Poseidon permutation operates.
50    type Word: Clone
51        + fmt::Debug
52        + From<AssignedCell<F, F>>
53        + Into<AssignedCell<F, F>>
54        + Send
55        + Sync;
56
57    /// Applies the Poseidon permutation to the given state.
58    fn permute(
59        &self,
60        layouter: &mut impl Layouter<F>,
61        initial_states: &State<Self::Word, T>,
62    ) -> Result<State<Self::Word, T>, Error>;
63
64    /// Applies the Poseidon permutation to the given states.
65    fn permute_batch(
66        &self,
67        layouter: &mut impl Layouter<F>,
68        initial_states: &[State<Self::Word, T>],
69    ) -> Result<Vec<State<Self::Word, T>>, Error> {
70        initial_states
71            .iter()
72            .map(|initial_state| self.permute(layouter, initial_state))
73            .collect::<Result<Vec<_>, Error>>()
74    }
75}
76
77/// The set of circuit instructions required to use the [`Sponge`] and [`Hash`] gadgets.
78///
79/// [`Hash`]: self::Hash
80pub trait PoseidonSpongeInstructions<
81    F: FieldExt,
82    S: Spec<F, T, RATE>,
83    D: Domain<F, RATE>,
84    const T: usize,
85    const RATE: usize,
86>: PoseidonInstructions<F, S, T, RATE>
87{
88    /// Returns the initial empty state for the given domain.
89    fn initial_state(&self, layouter: &mut impl Layouter<F>)
90        -> Result<State<Self::Word, T>, Error>;
91
92    /// Adds the given input to the state.
93    fn add_input(
94        &self,
95        layouter: &mut impl Layouter<F>,
96        initial_state: &State<Self::Word, T>,
97        input: &Absorbing<PaddedWord<F>, RATE>,
98    ) -> Result<State<Self::Word, T>, Error>;
99
100    /// Extracts sponge output from the given state.
101    fn get_output(state: &State<Self::Word, T>) -> Squeezing<Self::Word, RATE>;
102}
103
104/// A word over which the Poseidon permutation operates.
105#[derive(Debug)]
106pub struct Word<
107    F: FieldExt,
108    PoseidonChip: PoseidonInstructions<F, S, T, RATE>,
109    S: Spec<F, T, RATE>,
110    const T: usize,
111    const RATE: usize,
112> {
113    inner: PoseidonChip::Word,
114}
115
116impl<
117        F: FieldExt,
118        PoseidonChip: PoseidonInstructions<F, S, T, RATE>,
119        S: Spec<F, T, RATE>,
120        const T: usize,
121        const RATE: usize,
122    > Word<F, PoseidonChip, S, T, RATE>
123{
124    /// The word contained in this gadget.
125    pub fn inner(&self) -> PoseidonChip::Word {
126        self.inner.clone()
127    }
128
129    /// Construct a [`Word`] gadget from the inner word.
130    pub fn from_inner(inner: PoseidonChip::Word) -> Self {
131        Self { inner }
132    }
133}
134
135fn poseidon_sponge<
136    F: FieldExt,
137    PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
138    S: Spec<F, T, RATE>,
139    D: Domain<F, RATE>,
140    const T: usize,
141    const RATE: usize,
142>(
143    chip: &PoseidonChip,
144    mut layouter: impl Layouter<F>,
145    state: &mut State<PoseidonChip::Word, T>,
146    input: Option<&Absorbing<PaddedWord<F>, RATE>>,
147) -> Result<Squeezing<PoseidonChip::Word, RATE>, Error> {
148    if let Some(input) = input {
149        *state = chip.add_input(&mut layouter, state, input)?;
150    }
151    *state = chip.permute(&mut layouter, state)?;
152    Ok(PoseidonChip::get_output(state))
153}
154
155/// A Poseidon sponge.
156#[derive(Debug)]
157pub struct Sponge<
158    F: FieldExt,
159    PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
160    S: Spec<F, T, RATE>,
161    M: SpongeMode,
162    D: Domain<F, RATE>,
163    const T: usize,
164    const RATE: usize,
165> {
166    chip: PoseidonChip,
167    mode: M,
168    state: State<PoseidonChip::Word, T>,
169    _marker: PhantomData<D>,
170}
171
172impl<
173        F: FieldExt,
174        PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
175        S: Spec<F, T, RATE>,
176        D: Domain<F, RATE>,
177        const T: usize,
178        const RATE: usize,
179    > Sponge<F, PoseidonChip, S, Absorbing<PaddedWord<F>, RATE>, D, T, RATE>
180{
181    /// Constructs a new duplex sponge for the given Poseidon specification.
182    pub fn new(chip: PoseidonChip, mut layouter: impl Layouter<F>) -> Result<Self, Error> {
183        chip.initial_state(&mut layouter).map(|state| Sponge {
184            chip,
185            mode: Absorbing(
186                (0..RATE)
187                    .map(|_| None)
188                    .collect::<Vec<_>>()
189                    .try_into()
190                    .unwrap(),
191            ),
192            state,
193            _marker: PhantomData::default(),
194        })
195    }
196
197    /// Absorbs an element into the sponge.
198    pub fn absorb(
199        &mut self,
200        mut layouter: impl Layouter<F>,
201        value: PaddedWord<F>,
202    ) -> Result<(), Error> {
203        for entry in self.mode.0.iter_mut() {
204            if entry.is_none() {
205                *entry = Some(value);
206                return Ok(());
207            }
208        }
209
210        // We've already absorbed as many elements as we can
211        let _ = poseidon_sponge(
212            &self.chip,
213            layouter.namespace(|| "PoseidonSponge"),
214            &mut self.state,
215            Some(&self.mode),
216        )?;
217        self.mode = Absorbing::init_with(value);
218
219        Ok(())
220    }
221
222    /// Transitions the sponge into its squeezing state.
223    #[allow(clippy::type_complexity)]
224    pub fn finish_absorbing(
225        mut self,
226        mut layouter: impl Layouter<F>,
227    ) -> Result<Sponge<F, PoseidonChip, S, Squeezing<PoseidonChip::Word, RATE>, D, T, RATE>, Error>
228    {
229        let mode = poseidon_sponge(
230            &self.chip,
231            layouter.namespace(|| "PoseidonSponge"),
232            &mut self.state,
233            Some(&self.mode),
234        )?;
235
236        Ok(Sponge {
237            chip: self.chip,
238            mode,
239            state: self.state,
240            _marker: PhantomData::default(),
241        })
242    }
243}
244
245impl<
246        F: FieldExt,
247        PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
248        S: Spec<F, T, RATE>,
249        D: Domain<F, RATE>,
250        const T: usize,
251        const RATE: usize,
252    > Sponge<F, PoseidonChip, S, Squeezing<PoseidonChip::Word, RATE>, D, T, RATE>
253{
254    /// Squeezes an element from the sponge.
255    pub fn squeeze(&mut self, mut layouter: impl Layouter<F>) -> Result<AssignedCell<F, F>, Error> {
256        loop {
257            for entry in self.mode.0.iter_mut() {
258                if let Some(inner) = entry.take() {
259                    return Ok(inner.into());
260                }
261            }
262
263            // We've already squeezed out all available elements
264            self.mode = poseidon_sponge(
265                &self.chip,
266                layouter.namespace(|| "PoseidonSponge"),
267                &mut self.state,
268                None,
269            )?;
270        }
271    }
272}
273
274/// A Poseidon hash function, built around a sponge.
275#[derive(Debug)]
276pub struct Hash<
277    F: FieldExt,
278    PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
279    S: Spec<F, T, RATE>,
280    D: Domain<F, RATE>,
281    const T: usize,
282    const RATE: usize,
283> {
284    sponge: Sponge<F, PoseidonChip, S, Absorbing<PaddedWord<F>, RATE>, D, T, RATE>,
285}
286
287impl<
288        F: FieldExt,
289        PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
290        S: Spec<F, T, RATE>,
291        D: Domain<F, RATE>,
292        const T: usize,
293        const RATE: usize,
294    > Hash<F, PoseidonChip, S, D, T, RATE>
295{
296    /// Initializes a new hasher.
297    pub fn init(chip: PoseidonChip, layouter: impl Layouter<F>) -> Result<Self, Error> {
298        Sponge::new(chip, layouter).map(|sponge| Hash { sponge })
299    }
300}
301
302impl<
303        F: FieldExt,
304        PoseidonChip: PoseidonSpongeInstructions<F, S, ConstantLength<L>, T, RATE>,
305        S: Spec<F, T, RATE>,
306        const T: usize,
307        const RATE: usize,
308        const L: usize,
309    > Hash<F, PoseidonChip, S, ConstantLength<L>, T, RATE>
310{
311    /// Hashes the given input.
312    pub fn hash(
313        mut self,
314        mut layouter: impl Layouter<F>,
315        message: [AssignedCell<F, F>; L],
316    ) -> Result<AssignedCell<F, F>, Error> {
317        for (i, value) in message
318            .into_iter()
319            .map(PaddedWord::Message)
320            .chain(<ConstantLength<L> as Domain<F, RATE>>::padding(L).map(PaddedWord::Padding))
321            .enumerate()
322        {
323            self.sponge
324                .absorb(layouter.namespace(|| format!("absorb_{i}")), value)?;
325        }
326        self.sponge
327            .finish_absorbing(layouter.namespace(|| "finish absorbing"))?
328            .squeeze(layouter.namespace(|| "squeeze"))
329    }
330}
331*/