2021-01-29 06:26:52 -08:00
|
|
|
//! Gadget and chips for the Poseidon algebraic hash function.
|
|
|
|
|
2021-04-08 19:38:16 -07:00
|
|
|
use std::array;
|
2021-12-01 15:52:25 -08:00
|
|
|
use std::convert::TryInto;
|
2021-01-29 06:26:52 -08:00
|
|
|
use std::fmt;
|
2021-11-24 09:01:33 -08:00
|
|
|
use std::marker::PhantomData;
|
2021-01-29 06:26:52 -08:00
|
|
|
|
2021-12-09 18:07:34 -08:00
|
|
|
use group::ff::Field;
|
2022-01-27 15:28:02 -08:00
|
|
|
use halo2_proofs::{
|
2021-01-29 06:26:52 -08:00
|
|
|
arithmetic::FieldExt,
|
2021-12-01 18:51:46 -08:00
|
|
|
circuit::{AssignedCell, Chip, Layouter},
|
2021-01-29 06:26:52 -08:00
|
|
|
plonk::Error,
|
|
|
|
};
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
mod pow5;
|
|
|
|
pub use pow5::{Pow5Chip, Pow5Config, StateWord};
|
2021-02-08 14:56:58 -08:00
|
|
|
|
2021-11-24 09:01:33 -08:00
|
|
|
use crate::primitives::poseidon::{
|
2021-12-09 18:25:09 -08:00
|
|
|
Absorbing, ConstantLength, Domain, Spec, SpongeMode, Squeezing, State,
|
2021-11-24 09:01:33 -08:00
|
|
|
};
|
2021-04-01 21:03:56 -07:00
|
|
|
|
2021-12-09 18:07:34 -08:00
|
|
|
/// A word from the padded input to a Poseidon sponge.
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub enum PaddedWord<F: Field> {
|
|
|
|
/// A message word provided by the prover.
|
|
|
|
Message(AssignedCell<F, F>),
|
|
|
|
/// A padding word, that will be fixed in the circuit parameters.
|
|
|
|
Padding(F),
|
|
|
|
}
|
|
|
|
|
2021-04-01 21:03:56 -07:00
|
|
|
/// The set of circuit instructions required to use the Poseidon permutation.
|
|
|
|
pub trait PoseidonInstructions<F: FieldExt, S: Spec<F, T, RATE>, const T: usize, const RATE: usize>:
|
|
|
|
Chip<F>
|
|
|
|
{
|
|
|
|
/// Variable representing the word over which the Poseidon permutation operates.
|
2021-12-01 18:51:46 -08:00
|
|
|
type Word: Clone + fmt::Debug + From<AssignedCell<F, F>> + Into<AssignedCell<F, F>>;
|
2021-01-29 06:26:52 -08:00
|
|
|
|
|
|
|
/// Applies the Poseidon permutation to the given state.
|
|
|
|
fn permute(
|
|
|
|
&self,
|
|
|
|
layouter: &mut impl Layouter<F>,
|
2021-04-01 21:03:56 -07:00
|
|
|
initial_state: &State<Self::Word, T>,
|
|
|
|
) -> Result<State<Self::Word, T>, Error>;
|
2021-01-29 06:26:52 -08:00
|
|
|
}
|
2021-04-08 19:06:05 -07:00
|
|
|
|
2021-11-24 09:01:33 -08:00
|
|
|
/// The set of circuit instructions required to use the [`Sponge`] and [`Hash`] gadgets.
|
2021-04-08 19:06:05 -07:00
|
|
|
///
|
|
|
|
/// [`Hash`]: self::Hash
|
2021-11-24 09:01:33 -08:00
|
|
|
pub trait PoseidonSpongeInstructions<
|
2021-04-08 19:06:05 -07:00
|
|
|
F: FieldExt,
|
|
|
|
S: Spec<F, T, RATE>,
|
2021-12-09 18:07:34 -08:00
|
|
|
D: Domain<F, RATE>,
|
2021-04-08 19:06:05 -07:00
|
|
|
const T: usize,
|
|
|
|
const RATE: usize,
|
|
|
|
>: PoseidonInstructions<F, S, T, RATE>
|
|
|
|
{
|
|
|
|
/// Returns the initial empty state for the given domain.
|
2021-12-09 12:23:27 -08:00
|
|
|
fn initial_state(&self, layouter: &mut impl Layouter<F>)
|
|
|
|
-> Result<State<Self::Word, T>, Error>;
|
2021-04-08 19:06:05 -07:00
|
|
|
|
2021-12-09 18:07:34 -08:00
|
|
|
/// Adds the given input to the state.
|
|
|
|
fn add_input(
|
2021-04-08 19:06:05 -07:00
|
|
|
&self,
|
|
|
|
layouter: &mut impl Layouter<F>,
|
|
|
|
initial_state: &State<Self::Word, T>,
|
2021-12-09 18:25:09 -08:00
|
|
|
input: &Absorbing<PaddedWord<F>, RATE>,
|
2021-04-08 19:06:05 -07:00
|
|
|
) -> Result<State<Self::Word, T>, Error>;
|
|
|
|
|
|
|
|
/// Extracts sponge output from the given state.
|
2021-12-09 18:25:09 -08:00
|
|
|
fn get_output(state: &State<Self::Word, T>) -> Squeezing<Self::Word, RATE>;
|
2021-04-08 19:06:05 -07:00
|
|
|
}
|
2021-04-08 19:38:16 -07:00
|
|
|
|
|
|
|
/// A word over which the Poseidon permutation operates.
|
2021-11-04 04:07:27 -07:00
|
|
|
#[derive(Debug)]
|
2021-04-08 19:38:16 -07:00
|
|
|
pub struct Word<
|
|
|
|
F: FieldExt,
|
|
|
|
PoseidonChip: PoseidonInstructions<F, S, T, RATE>,
|
|
|
|
S: Spec<F, T, RATE>,
|
|
|
|
const T: usize,
|
|
|
|
const RATE: usize,
|
|
|
|
> {
|
2021-07-22 07:14:34 -07:00
|
|
|
inner: PoseidonChip::Word,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<
|
|
|
|
F: FieldExt,
|
|
|
|
PoseidonChip: PoseidonInstructions<F, S, T, RATE>,
|
|
|
|
S: Spec<F, T, RATE>,
|
|
|
|
const T: usize,
|
|
|
|
const RATE: usize,
|
|
|
|
> Word<F, PoseidonChip, S, T, RATE>
|
|
|
|
{
|
2021-11-03 18:07:28 -07:00
|
|
|
/// The word contained in this gadget.
|
|
|
|
pub fn inner(&self) -> PoseidonChip::Word {
|
2021-12-01 15:52:25 -08:00
|
|
|
self.inner.clone()
|
2021-07-22 07:14:34 -07:00
|
|
|
}
|
|
|
|
|
2021-11-03 18:07:28 -07:00
|
|
|
/// Construct a [`Word`] gadget from the inner word.
|
|
|
|
pub fn from_inner(inner: PoseidonChip::Word) -> Self {
|
2021-07-22 07:14:34 -07:00
|
|
|
Self { inner }
|
|
|
|
}
|
2021-04-08 19:38:16 -07:00
|
|
|
}
|
|
|
|
|
2021-11-24 09:01:33 -08:00
|
|
|
fn poseidon_sponge<
|
2021-04-08 19:38:16 -07:00
|
|
|
F: FieldExt,
|
2021-12-09 12:23:27 -08:00
|
|
|
PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
|
2021-04-08 19:38:16 -07:00
|
|
|
S: Spec<F, T, RATE>,
|
2021-12-09 18:07:34 -08:00
|
|
|
D: Domain<F, RATE>,
|
2021-04-08 19:38:16 -07:00
|
|
|
const T: usize,
|
|
|
|
const RATE: usize,
|
|
|
|
>(
|
|
|
|
chip: &PoseidonChip,
|
|
|
|
mut layouter: impl Layouter<F>,
|
|
|
|
state: &mut State<PoseidonChip::Word, T>,
|
2021-12-09 18:25:09 -08:00
|
|
|
input: Option<&Absorbing<PaddedWord<F>, RATE>>,
|
|
|
|
) -> Result<Squeezing<PoseidonChip::Word, RATE>, Error> {
|
|
|
|
if let Some(input) = input {
|
|
|
|
*state = chip.add_input(&mut layouter, state, input)?;
|
|
|
|
}
|
2021-04-08 19:38:16 -07:00
|
|
|
*state = chip.permute(&mut layouter, state)?;
|
|
|
|
Ok(PoseidonChip::get_output(state))
|
|
|
|
}
|
|
|
|
|
2021-11-24 09:01:33 -08:00
|
|
|
/// A Poseidon sponge.
|
2021-11-03 18:07:28 -07:00
|
|
|
#[derive(Debug)]
|
2021-11-24 09:01:33 -08:00
|
|
|
pub struct Sponge<
|
2021-04-08 19:38:16 -07:00
|
|
|
F: FieldExt,
|
2021-12-09 12:23:27 -08:00
|
|
|
PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
|
2021-04-08 19:38:16 -07:00
|
|
|
S: Spec<F, T, RATE>,
|
2021-11-24 09:01:33 -08:00
|
|
|
M: SpongeMode,
|
2021-12-09 18:07:34 -08:00
|
|
|
D: Domain<F, RATE>,
|
2021-04-08 19:38:16 -07:00
|
|
|
const T: usize,
|
|
|
|
const RATE: usize,
|
|
|
|
> {
|
|
|
|
chip: PoseidonChip,
|
2021-11-24 09:01:33 -08:00
|
|
|
mode: M,
|
2021-04-08 19:38:16 -07:00
|
|
|
state: State<PoseidonChip::Word, T>,
|
2021-12-15 05:01:53 -08:00
|
|
|
_marker: PhantomData<D>,
|
2021-04-08 19:38:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<
|
|
|
|
F: FieldExt,
|
2021-12-09 12:23:27 -08:00
|
|
|
PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
|
2021-04-08 19:38:16 -07:00
|
|
|
S: Spec<F, T, RATE>,
|
2021-12-09 18:07:34 -08:00
|
|
|
D: Domain<F, RATE>,
|
2021-04-08 19:38:16 -07:00
|
|
|
const T: usize,
|
|
|
|
const RATE: usize,
|
2021-12-09 18:07:34 -08:00
|
|
|
> Sponge<F, PoseidonChip, S, Absorbing<PaddedWord<F>, RATE>, D, T, RATE>
|
2021-04-08 19:38:16 -07:00
|
|
|
{
|
|
|
|
/// Constructs a new duplex sponge for the given Poseidon specification.
|
2021-12-09 12:23:27 -08:00
|
|
|
pub fn new(chip: PoseidonChip, mut layouter: impl Layouter<F>) -> Result<Self, Error> {
|
|
|
|
chip.initial_state(&mut layouter).map(|state| Sponge {
|
|
|
|
chip,
|
|
|
|
mode: Absorbing(
|
|
|
|
(0..RATE)
|
|
|
|
.map(|_| None)
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.try_into()
|
|
|
|
.unwrap(),
|
|
|
|
),
|
|
|
|
state,
|
|
|
|
_marker: PhantomData::default(),
|
|
|
|
})
|
2021-04-08 19:38:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Absorbs an element into the sponge.
|
|
|
|
pub fn absorb(
|
|
|
|
&mut self,
|
|
|
|
mut layouter: impl Layouter<F>,
|
2021-12-09 18:07:34 -08:00
|
|
|
value: PaddedWord<F>,
|
2021-04-08 19:38:16 -07:00
|
|
|
) -> Result<(), Error> {
|
2021-11-24 09:01:33 -08:00
|
|
|
for entry in self.mode.0.iter_mut() {
|
|
|
|
if entry.is_none() {
|
2021-12-09 18:07:34 -08:00
|
|
|
*entry = Some(value);
|
2021-11-24 09:01:33 -08:00
|
|
|
return Ok(());
|
2021-04-08 19:38:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-24 09:01:33 -08:00
|
|
|
// We've already absorbed as many elements as we can
|
|
|
|
let _ = poseidon_sponge(
|
|
|
|
&self.chip,
|
|
|
|
layouter.namespace(|| "PoseidonSponge"),
|
|
|
|
&mut self.state,
|
2021-12-09 18:25:09 -08:00
|
|
|
Some(&self.mode),
|
2021-11-24 09:01:33 -08:00
|
|
|
)?;
|
2021-12-09 18:07:34 -08:00
|
|
|
self.mode = Absorbing::init_with(value);
|
2021-11-24 09:01:33 -08:00
|
|
|
|
2021-04-08 19:38:16 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-11-24 09:01:33 -08:00
|
|
|
/// Transitions the sponge into its squeezing state.
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
pub fn finish_absorbing(
|
|
|
|
mut self,
|
|
|
|
mut layouter: impl Layouter<F>,
|
|
|
|
) -> Result<Sponge<F, PoseidonChip, S, Squeezing<PoseidonChip::Word, RATE>, D, T, RATE>, Error>
|
|
|
|
{
|
2021-12-09 18:25:09 -08:00
|
|
|
let mode = poseidon_sponge(
|
2021-11-24 09:01:33 -08:00
|
|
|
&self.chip,
|
|
|
|
layouter.namespace(|| "PoseidonSponge"),
|
|
|
|
&mut self.state,
|
2021-12-09 18:25:09 -08:00
|
|
|
Some(&self.mode),
|
|
|
|
)?;
|
2021-11-24 09:01:33 -08:00
|
|
|
|
|
|
|
Ok(Sponge {
|
|
|
|
chip: self.chip,
|
|
|
|
mode,
|
|
|
|
state: self.state,
|
|
|
|
_marker: PhantomData::default(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<
|
|
|
|
F: FieldExt,
|
2021-12-09 12:23:27 -08:00
|
|
|
PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
|
2021-11-24 09:01:33 -08:00
|
|
|
S: Spec<F, T, RATE>,
|
2021-12-09 18:07:34 -08:00
|
|
|
D: Domain<F, RATE>,
|
2021-11-24 09:01:33 -08:00
|
|
|
const T: usize,
|
|
|
|
const RATE: usize,
|
|
|
|
> Sponge<F, PoseidonChip, S, Squeezing<PoseidonChip::Word, RATE>, D, T, RATE>
|
|
|
|
{
|
2021-04-08 19:38:16 -07:00
|
|
|
/// Squeezes an element from the sponge.
|
2021-12-01 18:51:46 -08:00
|
|
|
pub fn squeeze(&mut self, mut layouter: impl Layouter<F>) -> Result<AssignedCell<F, F>, Error> {
|
2021-04-08 19:38:16 -07:00
|
|
|
loop {
|
2021-11-24 09:01:33 -08:00
|
|
|
for entry in self.mode.0.iter_mut() {
|
|
|
|
if let Some(inner) = entry.take() {
|
|
|
|
return Ok(inner.into());
|
2021-04-08 19:38:16 -07:00
|
|
|
}
|
|
|
|
}
|
2021-11-24 09:01:33 -08:00
|
|
|
|
|
|
|
// We've already squeezed out all available elements
|
2021-12-09 18:25:09 -08:00
|
|
|
self.mode = poseidon_sponge(
|
2021-11-24 09:01:33 -08:00
|
|
|
&self.chip,
|
|
|
|
layouter.namespace(|| "PoseidonSponge"),
|
|
|
|
&mut self.state,
|
2021-12-09 18:25:09 -08:00
|
|
|
None,
|
|
|
|
)?;
|
2021-04-08 19:38:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-24 09:01:33 -08:00
|
|
|
/// A Poseidon hash function, built around a sponge.
|
2021-11-03 18:07:28 -07:00
|
|
|
#[derive(Debug)]
|
2021-04-08 19:38:16 -07:00
|
|
|
pub struct Hash<
|
|
|
|
F: FieldExt,
|
2021-12-09 12:23:27 -08:00
|
|
|
PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
|
2021-04-08 19:38:16 -07:00
|
|
|
S: Spec<F, T, RATE>,
|
2021-12-09 18:07:34 -08:00
|
|
|
D: Domain<F, RATE>,
|
2021-04-08 19:38:16 -07:00
|
|
|
const T: usize,
|
|
|
|
const RATE: usize,
|
|
|
|
> {
|
2021-12-09 18:07:34 -08:00
|
|
|
sponge: Sponge<F, PoseidonChip, S, Absorbing<PaddedWord<F>, RATE>, D, T, RATE>,
|
2021-04-08 19:38:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<
|
|
|
|
F: FieldExt,
|
2021-12-09 12:23:27 -08:00
|
|
|
PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
|
2021-04-08 19:38:16 -07:00
|
|
|
S: Spec<F, T, RATE>,
|
2021-12-09 18:07:34 -08:00
|
|
|
D: Domain<F, RATE>,
|
2021-04-08 19:38:16 -07:00
|
|
|
const T: usize,
|
|
|
|
const RATE: usize,
|
|
|
|
> Hash<F, PoseidonChip, S, D, T, RATE>
|
|
|
|
{
|
|
|
|
/// Initializes a new hasher.
|
2021-12-09 12:23:27 -08:00
|
|
|
pub fn init(chip: PoseidonChip, layouter: impl Layouter<F>) -> Result<Self, Error> {
|
|
|
|
Sponge::new(chip, layouter).map(|sponge| Hash { sponge })
|
2021-04-08 19:38:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<
|
|
|
|
F: FieldExt,
|
2021-12-09 12:23:27 -08:00
|
|
|
PoseidonChip: PoseidonSpongeInstructions<F, S, ConstantLength<L>, T, RATE>,
|
2021-04-08 19:38:16 -07:00
|
|
|
S: Spec<F, T, RATE>,
|
|
|
|
const T: usize,
|
|
|
|
const RATE: usize,
|
|
|
|
const L: usize,
|
|
|
|
> Hash<F, PoseidonChip, S, ConstantLength<L>, T, RATE>
|
|
|
|
{
|
|
|
|
/// Hashes the given input.
|
|
|
|
pub fn hash(
|
|
|
|
mut self,
|
|
|
|
mut layouter: impl Layouter<F>,
|
2021-12-01 18:51:46 -08:00
|
|
|
message: [AssignedCell<F, F>; L],
|
|
|
|
) -> Result<AssignedCell<F, F>, Error> {
|
2021-12-09 18:07:34 -08:00
|
|
|
for (i, value) in array::IntoIter::new(message)
|
|
|
|
.map(PaddedWord::Message)
|
|
|
|
.chain(<ConstantLength<L> as Domain<F, RATE>>::padding(L).map(PaddedWord::Padding))
|
|
|
|
.enumerate()
|
|
|
|
{
|
2021-11-24 09:01:33 -08:00
|
|
|
self.sponge
|
2021-04-08 19:38:16 -07:00
|
|
|
.absorb(layouter.namespace(|| format!("absorb_{}", i)), value)?;
|
|
|
|
}
|
2021-11-24 09:01:33 -08:00
|
|
|
self.sponge
|
|
|
|
.finish_absorbing(layouter.namespace(|| "finish absorbing"))?
|
|
|
|
.squeeze(layouter.namespace(|| "squeeze"))
|
2021-04-08 19:38:16 -07:00
|
|
|
}
|
|
|
|
}
|