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*/