2021-11-02 07:52:11 -07:00
|
|
|
use std::convert::TryInto;
|
2021-07-15 04:52:15 -07:00
|
|
|
use std::iter;
|
|
|
|
|
2021-02-08 14:56:58 -08:00
|
|
|
use halo2::{
|
|
|
|
arithmetic::FieldExt,
|
|
|
|
circuit::{Cell, Chip, Layouter, Region},
|
2021-07-15 04:52:15 -07:00
|
|
|
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector},
|
2021-02-08 14:56:58 -08:00
|
|
|
poly::Rotation,
|
|
|
|
};
|
|
|
|
|
2021-04-08 19:06:05 -07:00
|
|
|
use super::{PoseidonDuplexInstructions, PoseidonInstructions};
|
2021-06-06 04:13:20 -07:00
|
|
|
use crate::circuit::gadget::utilities::{CellValue, Var};
|
2021-04-08 19:06:05 -07:00
|
|
|
use crate::primitives::poseidon::{Domain, Mds, Spec, SpongeState, State};
|
2021-02-08 14:56:58 -08:00
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
/// Configuration for a [`Pow5Chip`].
|
2021-02-08 14:56:58 -08:00
|
|
|
#[derive(Clone, Debug)]
|
2021-11-02 07:52:11 -07:00
|
|
|
pub struct Pow5Config<F: FieldExt, const WIDTH: usize, const RATE: usize> {
|
2021-06-06 05:05:51 -07:00
|
|
|
pub(in crate::circuit) state: [Column<Advice>; WIDTH],
|
2021-02-08 14:56:58 -08:00
|
|
|
partial_sbox: Column<Advice>,
|
|
|
|
rc_a: [Column<Fixed>; WIDTH],
|
|
|
|
rc_b: [Column<Fixed>; WIDTH],
|
|
|
|
s_full: Selector,
|
|
|
|
s_partial: Selector,
|
2021-04-08 19:06:05 -07:00
|
|
|
s_pad_and_add: Selector,
|
2021-02-08 14:56:58 -08:00
|
|
|
|
|
|
|
half_full_rounds: usize,
|
|
|
|
half_partial_rounds: usize,
|
|
|
|
alpha: [u64; 4],
|
|
|
|
round_constants: Vec<[F; WIDTH]>,
|
|
|
|
m_reg: Mds<F, WIDTH>,
|
|
|
|
m_inv: Mds<F, WIDTH>,
|
|
|
|
}
|
|
|
|
|
2021-11-23 12:29:56 -08:00
|
|
|
/// A Poseidon chip using an $x^5$ S-Box.
|
|
|
|
///
|
|
|
|
/// The chip is implemented using a single round per row for full rounds, and two rounds
|
|
|
|
/// per row for partial rounds.
|
2021-02-08 14:56:58 -08:00
|
|
|
#[derive(Debug)]
|
2021-11-02 07:52:11 -07:00
|
|
|
pub struct Pow5Chip<F: FieldExt, const WIDTH: usize, const RATE: usize> {
|
|
|
|
config: Pow5Config<F, WIDTH, RATE>,
|
2021-02-08 14:56:58 -08:00
|
|
|
}
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
impl<F: FieldExt, const WIDTH: usize, const RATE: usize> Pow5Chip<F, WIDTH, RATE> {
|
2021-02-08 14:56:58 -08:00
|
|
|
/// Configures this chip for use in a circuit.
|
2021-07-15 04:52:15 -07:00
|
|
|
///
|
|
|
|
/// # Side-effects
|
|
|
|
///
|
|
|
|
/// All columns in `state` will be equality-enabled.
|
2021-02-08 14:56:58 -08:00
|
|
|
//
|
|
|
|
// TODO: Does the rate need to be hard-coded here, or only the width? It probably
|
|
|
|
// needs to be known wherever we implement the hashing gadget, but it isn't strictly
|
|
|
|
// necessary for the permutation.
|
2021-11-02 07:52:11 -07:00
|
|
|
pub fn configure<S: Spec<F, WIDTH, RATE>>(
|
2021-02-08 14:56:58 -08:00
|
|
|
meta: &mut ConstraintSystem<F>,
|
|
|
|
state: [Column<Advice>; WIDTH],
|
2021-06-07 05:45:33 -07:00
|
|
|
partial_sbox: Column<Advice>,
|
2021-07-21 04:13:47 -07:00
|
|
|
rc_a: [Column<Fixed>; WIDTH],
|
|
|
|
rc_b: [Column<Fixed>; WIDTH],
|
2021-11-02 07:52:11 -07:00
|
|
|
) -> Pow5Config<F, WIDTH, RATE> {
|
|
|
|
assert_eq!(RATE, WIDTH - 1);
|
2021-02-08 14:56:58 -08:00
|
|
|
// Generate constants for the Poseidon permutation.
|
|
|
|
// This gadget requires R_F and R_P to be even.
|
|
|
|
assert!(S::full_rounds() & 1 == 0);
|
|
|
|
assert!(S::partial_rounds() & 1 == 0);
|
|
|
|
let half_full_rounds = S::full_rounds() / 2;
|
|
|
|
let half_partial_rounds = S::partial_rounds() / 2;
|
2021-11-03 06:12:24 -07:00
|
|
|
let (round_constants, m_reg, m_inv) = S::constants();
|
2021-02-08 14:56:58 -08:00
|
|
|
|
2021-04-08 19:06:05 -07:00
|
|
|
// This allows state words to be initialized (by constraining them equal to fixed
|
|
|
|
// values), and used in a permutation from an arbitrary region. rc_a is used in
|
|
|
|
// every permutation round, while rc_b is empty in the initial and final full
|
|
|
|
// rounds, so we use rc_b as "scratch space" for fixed values (enabling potential
|
|
|
|
// layouter optimisations).
|
2021-07-15 04:52:15 -07:00
|
|
|
for column in iter::empty()
|
|
|
|
.chain(state.iter().cloned().map(|c| c.into()))
|
|
|
|
.chain(rc_b.iter().cloned().map(|c| c.into()))
|
|
|
|
{
|
|
|
|
meta.enable_equality(column);
|
|
|
|
}
|
2021-04-08 19:06:05 -07:00
|
|
|
|
2021-02-08 14:56:58 -08:00
|
|
|
let s_full = meta.selector();
|
|
|
|
let s_partial = meta.selector();
|
2021-04-08 19:06:05 -07:00
|
|
|
let s_pad_and_add = meta.selector();
|
2021-02-08 14:56:58 -08:00
|
|
|
|
|
|
|
let alpha = [5, 0, 0, 0];
|
|
|
|
let pow_5 = |v: Expression<F>| {
|
|
|
|
let v2 = v.clone() * v.clone();
|
|
|
|
v2.clone() * v2 * v
|
|
|
|
};
|
|
|
|
|
|
|
|
meta.create_gate("full round", |meta| {
|
2021-06-05 15:39:00 -07:00
|
|
|
let s_full = meta.query_selector(s_full);
|
2021-02-08 14:56:58 -08:00
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
(0..WIDTH)
|
|
|
|
.map(|next_idx| {
|
|
|
|
let state_next = meta.query_advice(state[next_idx], Rotation::next());
|
2021-11-23 12:38:55 -08:00
|
|
|
let expr = (0..WIDTH)
|
|
|
|
.map(|idx| {
|
|
|
|
let state_cur = meta.query_advice(state[idx], Rotation::cur());
|
|
|
|
let rc_a = meta.query_fixed(rc_a[idx], Rotation::cur());
|
|
|
|
pow_5(state_cur + rc_a) * m_reg[next_idx][idx]
|
|
|
|
})
|
|
|
|
.reduce(|acc, term| acc + term)
|
|
|
|
.expect("WIDTH > 0");
|
|
|
|
s_full.clone() * (expr - state_next)
|
2021-11-02 07:52:11 -07:00
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
2021-02-08 14:56:58 -08:00
|
|
|
});
|
|
|
|
|
2021-07-28 18:10:32 -07:00
|
|
|
meta.create_gate("partial rounds", |meta| {
|
2021-02-08 14:56:58 -08:00
|
|
|
let cur_0 = meta.query_advice(state[0], Rotation::cur());
|
|
|
|
let mid_0 = meta.query_advice(partial_sbox, Rotation::cur());
|
|
|
|
|
|
|
|
let rc_a0 = meta.query_fixed(rc_a[0], Rotation::cur());
|
|
|
|
let rc_b0 = meta.query_fixed(rc_b[0], Rotation::cur());
|
|
|
|
|
2021-06-05 15:39:00 -07:00
|
|
|
let s_partial = meta.query_selector(s_partial);
|
2021-02-08 14:56:58 -08:00
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
use halo2::plonk::VirtualCells;
|
|
|
|
let mid = |idx: usize, meta: &mut VirtualCells<F>| {
|
|
|
|
let mid = mid_0.clone() * m_reg[idx][0];
|
|
|
|
(1..WIDTH).fold(mid, |acc, cur_idx| {
|
|
|
|
let cur = meta.query_advice(state[cur_idx], Rotation::cur());
|
|
|
|
let rc_a = meta.query_fixed(rc_a[cur_idx], Rotation::cur());
|
|
|
|
acc + (cur + rc_a) * m_reg[idx][cur_idx]
|
|
|
|
})
|
2021-02-08 14:56:58 -08:00
|
|
|
};
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
let next = |idx: usize, meta: &mut VirtualCells<F>| {
|
2021-11-23 12:29:56 -08:00
|
|
|
(0..WIDTH)
|
|
|
|
.map(|next_idx| {
|
|
|
|
let next = meta.query_advice(state[next_idx], Rotation::next());
|
|
|
|
next * m_inv[idx][next_idx]
|
|
|
|
})
|
|
|
|
.reduce(|acc, next| acc + next)
|
|
|
|
.expect("WIDTH > 0")
|
2021-11-02 07:52:11 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
let partial_round_linear = |idx: usize, meta: &mut VirtualCells<F>| {
|
|
|
|
let expr = {
|
|
|
|
let rc_b = meta.query_fixed(rc_b[idx], Rotation::cur());
|
|
|
|
mid(idx, meta) + rc_b - next(idx, meta)
|
|
|
|
};
|
|
|
|
s_partial.clone() * expr
|
|
|
|
};
|
|
|
|
|
|
|
|
std::iter::empty()
|
|
|
|
// state[0] round a
|
|
|
|
.chain(Some(
|
2021-07-28 18:10:32 -07:00
|
|
|
s_partial.clone() * (pow_5(cur_0 + rc_a0) - mid_0.clone()),
|
2021-11-02 07:52:11 -07:00
|
|
|
))
|
|
|
|
// state[0] round b
|
|
|
|
.chain(Some(
|
|
|
|
s_partial.clone() * (pow_5(mid(0, meta) + rc_b0) - next(0, meta)),
|
|
|
|
))
|
|
|
|
.chain((1..WIDTH).map(|idx| partial_round_linear(idx, meta)))
|
|
|
|
.collect::<Vec<_>>()
|
2021-02-08 14:56:58 -08:00
|
|
|
});
|
|
|
|
|
2021-04-08 19:06:05 -07:00
|
|
|
meta.create_gate("pad-and-add", |meta| {
|
2021-11-02 07:52:11 -07:00
|
|
|
let initial_state_rate = meta.query_advice(state[RATE], Rotation::prev());
|
|
|
|
let output_state_rate = meta.query_advice(state[RATE], Rotation::next());
|
2021-04-08 19:06:05 -07:00
|
|
|
|
2021-06-05 15:39:00 -07:00
|
|
|
let s_pad_and_add = meta.query_selector(s_pad_and_add);
|
2021-04-08 19:06:05 -07:00
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
let pad_and_add = |idx: usize| {
|
|
|
|
let initial_state = meta.query_advice(state[idx], Rotation::prev());
|
|
|
|
let input = meta.query_advice(state[idx], Rotation::cur());
|
|
|
|
let output_state = meta.query_advice(state[idx], Rotation::next());
|
|
|
|
|
2021-04-08 19:06:05 -07:00
|
|
|
// We pad the input by storing the required padding in fixed columns and
|
|
|
|
// then constraining the corresponding input columns to be equal to it.
|
|
|
|
s_pad_and_add.clone() * (initial_state + input - output_state)
|
|
|
|
};
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
(0..RATE)
|
|
|
|
.map(pad_and_add)
|
2021-04-08 19:06:05 -07:00
|
|
|
// The capacity element is never altered by the input.
|
2021-11-02 07:52:11 -07:00
|
|
|
.chain(Some(
|
|
|
|
s_pad_and_add.clone() * (initial_state_rate - output_state_rate),
|
|
|
|
))
|
|
|
|
.collect::<Vec<_>>()
|
2021-04-08 19:06:05 -07:00
|
|
|
});
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
Pow5Config {
|
2021-02-08 14:56:58 -08:00
|
|
|
state,
|
|
|
|
partial_sbox,
|
|
|
|
rc_a,
|
|
|
|
rc_b,
|
|
|
|
s_full,
|
|
|
|
s_partial,
|
2021-04-08 19:06:05 -07:00
|
|
|
s_pad_and_add,
|
2021-02-08 14:56:58 -08:00
|
|
|
half_full_rounds,
|
|
|
|
half_partial_rounds,
|
|
|
|
alpha,
|
|
|
|
round_constants,
|
|
|
|
m_reg,
|
|
|
|
m_inv,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 18:07:28 -07:00
|
|
|
/// Construct a [`Pow5Chip`].
|
2021-11-02 07:52:11 -07:00
|
|
|
pub fn construct(config: Pow5Config<F, WIDTH, RATE>) -> Self {
|
|
|
|
Pow5Chip { config }
|
2021-02-08 14:56:58 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
impl<F: FieldExt, const WIDTH: usize, const RATE: usize> Chip<F> for Pow5Chip<F, WIDTH, RATE> {
|
|
|
|
type Config = Pow5Config<F, WIDTH, RATE>;
|
2021-02-08 14:56:58 -08:00
|
|
|
type Loaded = ();
|
|
|
|
|
|
|
|
fn config(&self) -> &Self::Config {
|
|
|
|
&self.config
|
|
|
|
}
|
|
|
|
|
|
|
|
fn loaded(&self) -> &Self::Loaded {
|
|
|
|
&()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
impl<F: FieldExt, S: Spec<F, WIDTH, RATE>, const WIDTH: usize, const RATE: usize>
|
|
|
|
PoseidonInstructions<F, S, WIDTH, RATE> for Pow5Chip<F, WIDTH, RATE>
|
|
|
|
{
|
2021-04-01 21:03:56 -07:00
|
|
|
type Word = StateWord<F>;
|
2021-02-08 14:56:58 -08:00
|
|
|
|
|
|
|
fn permute(
|
|
|
|
&self,
|
|
|
|
layouter: &mut impl Layouter<F>,
|
2021-04-01 21:03:56 -07:00
|
|
|
initial_state: &State<Self::Word, WIDTH>,
|
|
|
|
) -> Result<State<Self::Word, WIDTH>, Error> {
|
2021-02-08 14:56:58 -08:00
|
|
|
let config = self.config();
|
|
|
|
|
|
|
|
layouter.assign_region(
|
|
|
|
|| "permute state",
|
|
|
|
|mut region| {
|
|
|
|
// Load the initial state into this region.
|
2021-11-02 07:52:11 -07:00
|
|
|
let state = Pow5State::load(&mut region, config, initial_state)?;
|
2021-02-08 14:56:58 -08:00
|
|
|
|
|
|
|
let state = (0..config.half_full_rounds).fold(Ok(state), |res, r| {
|
2021-06-21 10:01:35 -07:00
|
|
|
res.and_then(|state| state.full_round(&mut region, config, r, r))
|
2021-02-08 14:56:58 -08:00
|
|
|
})?;
|
|
|
|
|
|
|
|
let state = (0..config.half_partial_rounds).fold(Ok(state), |res, r| {
|
|
|
|
res.and_then(|state| {
|
|
|
|
state.partial_round(
|
|
|
|
&mut region,
|
2021-06-21 10:01:35 -07:00
|
|
|
config,
|
2021-02-08 14:56:58 -08:00
|
|
|
config.half_full_rounds + 2 * r,
|
|
|
|
config.half_full_rounds + r,
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})?;
|
|
|
|
|
2021-04-01 21:03:56 -07:00
|
|
|
let state = (0..config.half_full_rounds).fold(Ok(state), |res, r| {
|
2021-02-08 14:56:58 -08:00
|
|
|
res.and_then(|state| {
|
2021-03-12 20:32:11 -08:00
|
|
|
state.full_round(
|
|
|
|
&mut region,
|
2021-06-21 10:01:35 -07:00
|
|
|
config,
|
2021-03-12 20:32:11 -08:00
|
|
|
config.half_full_rounds + 2 * config.half_partial_rounds + r,
|
|
|
|
config.half_full_rounds + config.half_partial_rounds + r,
|
|
|
|
)
|
2021-02-08 14:56:58 -08:00
|
|
|
})
|
2021-04-01 21:03:56 -07:00
|
|
|
})?;
|
|
|
|
|
|
|
|
Ok(state.0)
|
2021-02-08 14:56:58 -08:00
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
impl<F: FieldExt, S: Spec<F, WIDTH, RATE>, const WIDTH: usize, const RATE: usize>
|
|
|
|
PoseidonDuplexInstructions<F, S, WIDTH, RATE> for Pow5Chip<F, WIDTH, RATE>
|
2021-04-08 19:06:05 -07:00
|
|
|
{
|
|
|
|
fn initial_state(
|
|
|
|
&self,
|
|
|
|
layouter: &mut impl Layouter<F>,
|
2021-11-02 07:52:11 -07:00
|
|
|
domain: &impl Domain<F, WIDTH, RATE>,
|
2021-04-08 19:06:05 -07:00
|
|
|
) -> Result<State<Self::Word, WIDTH>, Error> {
|
|
|
|
let config = self.config();
|
2021-11-02 07:52:11 -07:00
|
|
|
let state = layouter.assign_region(
|
2021-04-08 19:06:05 -07:00
|
|
|
|| format!("initial state for domain {:?}", domain),
|
|
|
|
|mut region| {
|
2021-11-02 07:52:11 -07:00
|
|
|
let mut state = Vec::with_capacity(WIDTH);
|
|
|
|
let mut load_state_word = |i: usize, value: F| -> Result<_, Error> {
|
2021-07-23 15:13:10 -07:00
|
|
|
let var = region.assign_advice_from_constant(
|
2021-04-08 19:06:05 -07:00
|
|
|
|| format!("state_{}", i),
|
|
|
|
config.state[i],
|
|
|
|
0,
|
2021-07-23 15:13:10 -07:00
|
|
|
value,
|
2021-04-08 19:06:05 -07:00
|
|
|
)?;
|
2021-11-02 07:52:11 -07:00
|
|
|
state.push(StateWord {
|
2021-04-08 19:06:05 -07:00
|
|
|
var,
|
|
|
|
value: Some(value),
|
2021-11-02 07:52:11 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
Ok(())
|
2021-04-08 19:06:05 -07:00
|
|
|
};
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
for i in 0..RATE {
|
|
|
|
load_state_word(i, F::zero())?;
|
|
|
|
}
|
|
|
|
load_state_word(RATE, domain.initial_capacity_element())?;
|
|
|
|
|
|
|
|
Ok(state)
|
2021-04-08 19:06:05 -07:00
|
|
|
},
|
2021-11-02 07:52:11 -07:00
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(state.try_into().unwrap())
|
2021-04-08 19:06:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn pad_and_add(
|
|
|
|
&self,
|
|
|
|
layouter: &mut impl Layouter<F>,
|
2021-11-02 07:52:11 -07:00
|
|
|
domain: &impl Domain<F, WIDTH, RATE>,
|
2021-04-08 19:06:05 -07:00
|
|
|
initial_state: &State<Self::Word, WIDTH>,
|
2021-11-02 07:52:11 -07:00
|
|
|
input: &SpongeState<Self::Word, RATE>,
|
2021-04-08 19:06:05 -07:00
|
|
|
) -> Result<State<Self::Word, WIDTH>, Error> {
|
|
|
|
let config = self.config();
|
|
|
|
layouter.assign_region(
|
|
|
|
|| format!("pad-and-add for domain {:?}", domain),
|
|
|
|
|mut region| {
|
|
|
|
config.s_pad_and_add.enable(&mut region, 1)?;
|
|
|
|
|
|
|
|
// Load the initial state into this region.
|
2021-11-02 07:52:11 -07:00
|
|
|
let load_state_word = |i: usize| {
|
2021-04-08 19:06:05 -07:00
|
|
|
let value = initial_state[i].value;
|
|
|
|
let var = region.assign_advice(
|
|
|
|
|| format!("load state_{}", i),
|
|
|
|
config.state[i],
|
|
|
|
0,
|
2021-11-23 16:21:44 -08:00
|
|
|
|| value.ok_or(Error::Synthesis),
|
2021-04-08 19:06:05 -07:00
|
|
|
)?;
|
2021-07-15 04:52:15 -07:00
|
|
|
region.constrain_equal(initial_state[i].var, var)?;
|
2021-04-08 19:06:05 -07:00
|
|
|
Ok(StateWord { var, value })
|
|
|
|
};
|
2021-11-02 07:52:11 -07:00
|
|
|
let initial_state: Result<Vec<_>, Error> =
|
|
|
|
(0..WIDTH).map(load_state_word).collect();
|
|
|
|
let initial_state = initial_state?;
|
2021-04-08 19:06:05 -07:00
|
|
|
|
|
|
|
let padding_values = domain.padding();
|
|
|
|
|
|
|
|
// Load the input and padding into this region.
|
2021-11-02 07:52:11 -07:00
|
|
|
let load_input_word = |i: usize| {
|
2021-04-08 19:06:05 -07:00
|
|
|
let (constraint_var, value) = match (input[i], padding_values[i]) {
|
|
|
|
(Some(word), None) => (word.var, word.value),
|
|
|
|
(None, Some(padding_value)) => {
|
|
|
|
let padding_var = region.assign_fixed(
|
|
|
|
|| format!("load pad_{}", i),
|
|
|
|
config.rc_b[i],
|
|
|
|
1,
|
|
|
|
|| Ok(padding_value),
|
|
|
|
)?;
|
|
|
|
(padding_var, Some(padding_value))
|
|
|
|
}
|
|
|
|
_ => panic!("Input and padding don't match"),
|
|
|
|
};
|
|
|
|
let var = region.assign_advice(
|
|
|
|
|| format!("load input_{}", i),
|
|
|
|
config.state[i],
|
|
|
|
1,
|
2021-11-23 16:21:44 -08:00
|
|
|
|| value.ok_or(Error::Synthesis),
|
2021-04-08 19:06:05 -07:00
|
|
|
)?;
|
2021-07-15 04:52:15 -07:00
|
|
|
region.constrain_equal(constraint_var, var)?;
|
2021-04-08 19:06:05 -07:00
|
|
|
|
|
|
|
Ok(StateWord { var, value })
|
|
|
|
};
|
2021-11-02 07:52:11 -07:00
|
|
|
let input: Result<Vec<_>, Error> = (0..RATE).map(load_input_word).collect();
|
|
|
|
let input = input?;
|
2021-04-08 19:06:05 -07:00
|
|
|
|
|
|
|
// Constrain the output.
|
2021-11-02 07:52:11 -07:00
|
|
|
let constrain_output_word = |i: usize| {
|
2021-04-08 19:06:05 -07:00
|
|
|
let value = initial_state[i].value.and_then(|initial_word| {
|
|
|
|
input
|
|
|
|
.get(i)
|
|
|
|
.map(|word| word.value)
|
|
|
|
// The capacity element is never altered by the input.
|
|
|
|
.unwrap_or_else(|| Some(F::zero()))
|
|
|
|
.map(|input_word| initial_word + input_word)
|
|
|
|
});
|
|
|
|
let var = region.assign_advice(
|
|
|
|
|| format!("load output_{}", i),
|
|
|
|
config.state[i],
|
|
|
|
2,
|
2021-11-23 16:21:44 -08:00
|
|
|
|| value.ok_or(Error::Synthesis),
|
2021-04-08 19:06:05 -07:00
|
|
|
)?;
|
|
|
|
Ok(StateWord { var, value })
|
|
|
|
};
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
let output: Result<Vec<_>, Error> = (0..WIDTH).map(constrain_output_word).collect();
|
|
|
|
output.map(|output| output.try_into().unwrap())
|
2021-04-08 19:06:05 -07:00
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
fn get_output(state: &State<Self::Word, WIDTH>) -> SpongeState<Self::Word, RATE> {
|
|
|
|
state[..RATE]
|
|
|
|
.iter()
|
|
|
|
.map(|word| Some(*word))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.try_into()
|
|
|
|
.unwrap()
|
2021-04-08 19:06:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 18:07:28 -07:00
|
|
|
/// A word in the Poseidon state.
|
2021-04-08 19:06:05 -07:00
|
|
|
#[derive(Clone, Copy, Debug)]
|
2021-04-01 21:03:56 -07:00
|
|
|
pub struct StateWord<F: FieldExt> {
|
2021-02-08 14:56:58 -08:00
|
|
|
var: Cell,
|
|
|
|
value: Option<F>,
|
|
|
|
}
|
|
|
|
|
2021-06-06 04:13:20 -07:00
|
|
|
impl<F: FieldExt> From<StateWord<F>> for CellValue<F> {
|
|
|
|
fn from(state_word: StateWord<F>) -> CellValue<F> {
|
|
|
|
CellValue::new(state_word.var, state_word.value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-04 04:07:27 -07:00
|
|
|
impl<F: FieldExt> From<CellValue<F>> for StateWord<F> {
|
|
|
|
fn from(cell_value: CellValue<F>) -> StateWord<F> {
|
|
|
|
StateWord::new(cell_value.cell(), cell_value.value())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 18:07:28 -07:00
|
|
|
impl<F: FieldExt> Var<F> for StateWord<F> {
|
|
|
|
fn new(var: Cell, value: Option<F>) -> Self {
|
|
|
|
Self { var, value }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn cell(&self) -> Cell {
|
|
|
|
self.var
|
|
|
|
}
|
|
|
|
|
|
|
|
fn value(&self) -> Option<F> {
|
|
|
|
self.value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-08 14:56:58 -08:00
|
|
|
#[derive(Debug)]
|
2021-11-02 07:52:11 -07:00
|
|
|
struct Pow5State<F: FieldExt, const WIDTH: usize>([StateWord<F>; WIDTH]);
|
2021-02-08 14:56:58 -08:00
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
impl<F: FieldExt, const WIDTH: usize> Pow5State<F, WIDTH> {
|
|
|
|
fn full_round<const RATE: usize>(
|
2021-02-08 14:56:58 -08:00
|
|
|
self,
|
|
|
|
region: &mut Region<F>,
|
2021-11-02 07:52:11 -07:00
|
|
|
config: &Pow5Config<F, WIDTH, RATE>,
|
2021-02-08 14:56:58 -08:00
|
|
|
round: usize,
|
|
|
|
offset: usize,
|
|
|
|
) -> Result<Self, Error> {
|
|
|
|
Self::round(region, config, round, offset, config.s_full, |_| {
|
2021-11-02 07:52:11 -07:00
|
|
|
let q = self
|
|
|
|
.0
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(idx, word)| word.value.map(|v| v + config.round_constants[round][idx]));
|
|
|
|
let r: Option<Vec<F>> = q.map(|q| q.map(|q| q.pow(&config.alpha))).collect();
|
2021-02-08 14:56:58 -08:00
|
|
|
let m = &config.m_reg;
|
2021-11-02 07:52:11 -07:00
|
|
|
let state = m.iter().map(|m_i| {
|
|
|
|
r.as_ref().map(|r| {
|
|
|
|
r.iter()
|
|
|
|
.enumerate()
|
|
|
|
.fold(F::zero(), |acc, (j, r_j)| acc + m_i[j] * r_j)
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
Ok((round + 1, state.collect::<Vec<_>>().try_into().unwrap()))
|
2021-02-08 14:56:58 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
fn partial_round<const RATE: usize>(
|
2021-02-08 14:56:58 -08:00
|
|
|
self,
|
|
|
|
region: &mut Region<F>,
|
2021-11-02 07:52:11 -07:00
|
|
|
config: &Pow5Config<F, WIDTH, RATE>,
|
2021-02-08 14:56:58 -08:00
|
|
|
round: usize,
|
|
|
|
offset: usize,
|
|
|
|
) -> Result<Self, Error> {
|
|
|
|
Self::round(region, config, round, offset, config.s_partial, |region| {
|
|
|
|
let m = &config.m_reg;
|
2021-11-02 07:52:11 -07:00
|
|
|
let p: Option<Vec<_>> = self.0.iter().map(|word| word.value).collect();
|
|
|
|
|
|
|
|
let r: Option<Vec<_>> = p.map(|p| {
|
|
|
|
let r_0 = (p[0] + config.round_constants[round][0]).pow(&config.alpha);
|
|
|
|
let r_i = p[1..]
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, p_i)| *p_i + config.round_constants[round][i + 1]);
|
|
|
|
std::iter::empty().chain(Some(r_0)).chain(r_i).collect()
|
2021-02-08 14:56:58 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
region.assign_advice(
|
|
|
|
|| format!("round_{} partial_sbox", round),
|
|
|
|
config.partial_sbox,
|
|
|
|
offset,
|
2021-11-23 16:21:44 -08:00
|
|
|
|| r.as_ref().map(|r| r[0]).ok_or(Error::Synthesis),
|
2021-02-08 14:56:58 -08:00
|
|
|
)?;
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
let p_mid: Option<Vec<_>> = m
|
|
|
|
.iter()
|
|
|
|
.map(|m_i| {
|
|
|
|
r.as_ref().map(|r| {
|
2021-11-23 12:29:56 -08:00
|
|
|
m_i.iter()
|
|
|
|
.zip(r.iter())
|
|
|
|
.fold(F::zero(), |acc, (m_ij, r_j)| acc + *m_ij * r_j)
|
2021-11-02 07:52:11 -07:00
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect();
|
2021-02-08 14:56:58 -08:00
|
|
|
|
|
|
|
// Load the second round constants.
|
|
|
|
let mut load_round_constant = |i: usize| {
|
|
|
|
region.assign_fixed(
|
|
|
|
|| format!("round_{} rc_{}", round + 1, i),
|
|
|
|
config.rc_b[i],
|
|
|
|
offset,
|
|
|
|
|| Ok(config.round_constants[round + 1][i]),
|
|
|
|
)
|
|
|
|
};
|
|
|
|
for i in 0..WIDTH {
|
|
|
|
load_round_constant(i)?;
|
|
|
|
}
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
let r_mid: Option<Vec<_>> = p_mid.map(|p| {
|
|
|
|
let r_0 = (p[0] + config.round_constants[round + 1][0]).pow(&config.alpha);
|
|
|
|
let r_i = p[1..]
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, p_i)| *p_i + config.round_constants[round + 1][i + 1]);
|
|
|
|
std::iter::empty().chain(Some(r_0)).chain(r_i).collect()
|
2021-02-08 14:56:58 -08:00
|
|
|
});
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
let state: Vec<Option<_>> = m
|
|
|
|
.iter()
|
|
|
|
.map(|m_i| {
|
|
|
|
r_mid.as_ref().map(|r| {
|
2021-11-23 12:29:56 -08:00
|
|
|
m_i.iter()
|
|
|
|
.zip(r.iter())
|
|
|
|
.fold(F::zero(), |acc, (m_ij, r_j)| acc + *m_ij * r_j)
|
2021-11-02 07:52:11 -07:00
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
Ok((round + 2, state.try_into().unwrap()))
|
2021-02-08 14:56:58 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
fn load<const RATE: usize>(
|
2021-02-08 14:56:58 -08:00
|
|
|
region: &mut Region<F>,
|
2021-11-02 07:52:11 -07:00
|
|
|
config: &Pow5Config<F, WIDTH, RATE>,
|
2021-04-01 21:03:56 -07:00
|
|
|
initial_state: &State<StateWord<F>, WIDTH>,
|
2021-02-08 14:56:58 -08:00
|
|
|
) -> Result<Self, Error> {
|
2021-11-02 07:52:11 -07:00
|
|
|
let load_state_word = |i: usize| {
|
2021-04-01 21:03:56 -07:00
|
|
|
let value = initial_state[i].value;
|
2021-02-08 14:56:58 -08:00
|
|
|
let var = region.assign_advice(
|
|
|
|
|| format!("load state_{}", i),
|
|
|
|
config.state[i],
|
|
|
|
0,
|
2021-11-23 16:21:44 -08:00
|
|
|
|| value.ok_or(Error::Synthesis),
|
2021-02-08 14:56:58 -08:00
|
|
|
)?;
|
2021-07-15 04:52:15 -07:00
|
|
|
region.constrain_equal(initial_state[i].var, var)?;
|
2021-02-08 14:56:58 -08:00
|
|
|
Ok(StateWord { var, value })
|
|
|
|
};
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
let state: Result<Vec<_>, _> = (0..WIDTH).map(load_state_word).collect();
|
|
|
|
state.map(|state| Pow5State(state.try_into().unwrap()))
|
2021-02-08 14:56:58 -08:00
|
|
|
}
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
fn round<const RATE: usize>(
|
2021-02-08 14:56:58 -08:00
|
|
|
region: &mut Region<F>,
|
2021-11-02 07:52:11 -07:00
|
|
|
config: &Pow5Config<F, WIDTH, RATE>,
|
2021-02-08 14:56:58 -08:00
|
|
|
round: usize,
|
|
|
|
offset: usize,
|
|
|
|
round_gate: Selector,
|
|
|
|
round_fn: impl FnOnce(&mut Region<F>) -> Result<(usize, [Option<F>; WIDTH]), Error>,
|
|
|
|
) -> Result<Self, Error> {
|
|
|
|
// Enable the required gate.
|
|
|
|
round_gate.enable(region, offset)?;
|
|
|
|
|
|
|
|
// Load the round constants.
|
|
|
|
let mut load_round_constant = |i: usize| {
|
|
|
|
region.assign_fixed(
|
|
|
|
|| format!("round_{} rc_{}", round, i),
|
|
|
|
config.rc_a[i],
|
|
|
|
offset,
|
|
|
|
|| Ok(config.round_constants[round][i]),
|
|
|
|
)
|
|
|
|
};
|
|
|
|
for i in 0..WIDTH {
|
|
|
|
load_round_constant(i)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the next round's state.
|
|
|
|
let (next_round, next_state) = round_fn(region)?;
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
let next_state_word = |i: usize| {
|
2021-02-08 14:56:58 -08:00
|
|
|
let value = next_state[i];
|
|
|
|
let var = region.assign_advice(
|
|
|
|
|| format!("round_{} state_{}", next_round, i),
|
|
|
|
config.state[i],
|
|
|
|
offset + 1,
|
2021-11-23 16:21:44 -08:00
|
|
|
|| value.ok_or(Error::Synthesis),
|
2021-02-08 14:56:58 -08:00
|
|
|
)?;
|
|
|
|
Ok(StateWord { var, value })
|
|
|
|
};
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
let next_state: Result<Vec<_>, _> = (0..WIDTH).map(next_state_word).collect();
|
|
|
|
next_state.map(|next_state| Pow5State(next_state.try_into().unwrap()))
|
2021-02-08 14:56:58 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-05-18 13:33:11 -07:00
|
|
|
use ff::PrimeField;
|
2021-02-08 14:56:58 -08:00
|
|
|
use halo2::{
|
2021-03-12 20:31:02 -08:00
|
|
|
arithmetic::FieldExt,
|
2021-07-08 18:56:27 -07:00
|
|
|
circuit::{Layouter, SimpleFloorPlanner},
|
2021-02-08 14:56:58 -08:00
|
|
|
dev::MockProver,
|
|
|
|
pasta::Fp,
|
2021-07-08 18:56:27 -07:00
|
|
|
plonk::{Circuit, ConstraintSystem, Error},
|
2021-02-08 14:56:58 -08:00
|
|
|
};
|
2021-05-18 13:33:11 -07:00
|
|
|
use pasta_curves::pallas;
|
2021-02-08 14:56:58 -08:00
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
use super::{PoseidonInstructions, Pow5Chip, Pow5Config, StateWord};
|
2021-04-08 19:38:16 -07:00
|
|
|
use crate::{
|
2021-11-04 04:07:27 -07:00
|
|
|
circuit::gadget::{
|
|
|
|
poseidon::Hash,
|
|
|
|
utilities::{CellValue, Var},
|
|
|
|
},
|
2021-08-19 23:54:24 -07:00
|
|
|
primitives::poseidon::{self, ConstantLength, P128Pow5T3 as OrchardNullifier, Spec},
|
2021-04-08 19:38:16 -07:00
|
|
|
};
|
2021-11-03 06:37:02 -07:00
|
|
|
use std::convert::TryInto;
|
|
|
|
use std::marker::PhantomData;
|
2021-02-08 14:56:58 -08:00
|
|
|
|
2021-11-03 06:37:02 -07:00
|
|
|
struct PermuteCircuit<S: Spec<Fp, WIDTH, RATE>, const WIDTH: usize, const RATE: usize>(
|
|
|
|
PhantomData<S>,
|
|
|
|
);
|
2021-11-02 07:52:11 -07:00
|
|
|
|
2021-11-03 06:37:02 -07:00
|
|
|
impl<S: Spec<Fp, WIDTH, RATE>, const WIDTH: usize, const RATE: usize> Circuit<Fp>
|
|
|
|
for PermuteCircuit<S, WIDTH, RATE>
|
|
|
|
{
|
2021-11-02 07:52:11 -07:00
|
|
|
type Config = Pow5Config<Fp, WIDTH, RATE>;
|
2021-07-08 18:56:27 -07:00
|
|
|
type FloorPlanner = SimpleFloorPlanner;
|
|
|
|
|
|
|
|
fn without_witnesses(&self) -> Self {
|
2021-11-03 06:37:02 -07:00
|
|
|
PermuteCircuit::<S, WIDTH, RATE>(PhantomData)
|
2021-07-08 18:56:27 -07:00
|
|
|
}
|
2021-02-08 14:56:58 -08:00
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
fn configure(meta: &mut ConstraintSystem<Fp>) -> Pow5Config<Fp, WIDTH, RATE> {
|
2021-11-03 06:37:02 -07:00
|
|
|
let state = (0..WIDTH).map(|_| meta.advice_column()).collect::<Vec<_>>();
|
2021-06-07 05:45:33 -07:00
|
|
|
let partial_sbox = meta.advice_column();
|
2021-02-08 14:56:58 -08:00
|
|
|
|
2021-11-03 06:37:02 -07:00
|
|
|
let rc_a = (0..WIDTH).map(|_| meta.fixed_column()).collect::<Vec<_>>();
|
|
|
|
let rc_b = (0..WIDTH).map(|_| meta.fixed_column()).collect::<Vec<_>>();
|
2021-07-21 04:13:47 -07:00
|
|
|
|
2021-11-03 06:37:02 -07:00
|
|
|
Pow5Chip::configure::<S>(
|
|
|
|
meta,
|
|
|
|
state.try_into().unwrap(),
|
|
|
|
partial_sbox,
|
|
|
|
rc_a.try_into().unwrap(),
|
|
|
|
rc_b.try_into().unwrap(),
|
|
|
|
)
|
2021-02-08 14:56:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
fn synthesize(
|
|
|
|
&self,
|
2021-11-02 07:52:11 -07:00
|
|
|
config: Pow5Config<Fp, WIDTH, RATE>,
|
2021-07-08 18:56:27 -07:00
|
|
|
mut layouter: impl Layouter<Fp>,
|
2021-02-08 14:56:58 -08:00
|
|
|
) -> Result<(), Error> {
|
|
|
|
let initial_state = layouter.assign_region(
|
|
|
|
|| "prepare initial state",
|
|
|
|
|mut region| {
|
2021-11-03 06:37:02 -07:00
|
|
|
let state_word = |i: usize| {
|
2021-02-08 14:56:58 -08:00
|
|
|
let value = Some(Fp::from(i as u64));
|
|
|
|
let var = region.assign_advice(
|
|
|
|
|| format!("load state_{}", i),
|
|
|
|
config.state[i],
|
|
|
|
0,
|
2021-11-23 16:21:44 -08:00
|
|
|
|| value.ok_or(Error::Synthesis),
|
2021-02-08 14:56:58 -08:00
|
|
|
)?;
|
|
|
|
Ok(StateWord { var, value })
|
|
|
|
};
|
|
|
|
|
2021-11-03 06:37:02 -07:00
|
|
|
let state: Result<Vec<_>, Error> = (0..WIDTH).map(state_word).collect();
|
|
|
|
Ok(state?.try_into().unwrap())
|
2021-02-08 14:56:58 -08:00
|
|
|
},
|
|
|
|
)?;
|
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
let chip = Pow5Chip::construct(config.clone());
|
|
|
|
let final_state = <Pow5Chip<_, WIDTH, RATE> as PoseidonInstructions<
|
2021-04-01 21:03:56 -07:00
|
|
|
Fp,
|
2021-11-03 06:37:02 -07:00
|
|
|
S,
|
2021-04-01 21:03:56 -07:00
|
|
|
WIDTH,
|
2021-11-03 06:37:02 -07:00
|
|
|
RATE,
|
2021-04-01 21:03:56 -07:00
|
|
|
>>::permute(&chip, &mut layouter, &initial_state)?;
|
2021-03-12 20:31:02 -08:00
|
|
|
|
|
|
|
// For the purpose of this test, compute the real final state inline.
|
2021-11-03 06:37:02 -07:00
|
|
|
let mut expected_final_state = (0..WIDTH)
|
|
|
|
.map(|idx| Fp::from_u64(idx as u64))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.try_into()
|
|
|
|
.unwrap();
|
|
|
|
let (round_constants, mds, _) = S::constants();
|
|
|
|
poseidon::permute::<_, S, WIDTH, RATE>(
|
2021-03-12 20:31:02 -08:00
|
|
|
&mut expected_final_state,
|
|
|
|
&mds,
|
|
|
|
&round_constants,
|
|
|
|
);
|
|
|
|
|
|
|
|
layouter.assign_region(
|
|
|
|
|| "constrain final state",
|
|
|
|
|mut region| {
|
|
|
|
let mut final_state_word = |i: usize| {
|
|
|
|
let var = region.assign_advice(
|
|
|
|
|| format!("load final_state_{}", i),
|
|
|
|
config.state[i],
|
|
|
|
0,
|
|
|
|
|| Ok(expected_final_state[i]),
|
|
|
|
)?;
|
2021-07-15 04:52:15 -07:00
|
|
|
region.constrain_equal(final_state[i].var, var)
|
2021-03-12 20:31:02 -08:00
|
|
|
};
|
|
|
|
|
2021-11-30 17:08:56 -08:00
|
|
|
for i in 0..(WIDTH) {
|
2021-11-03 06:37:02 -07:00
|
|
|
final_state_word(i)?;
|
|
|
|
}
|
|
|
|
|
2021-11-30 17:08:56 -08:00
|
|
|
Ok(())
|
2021-03-12 20:31:02 -08:00
|
|
|
},
|
|
|
|
)
|
2021-02-08 14:56:58 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-08 18:32:08 -07:00
|
|
|
fn poseidon_permute() {
|
2021-02-08 14:56:58 -08:00
|
|
|
let k = 6;
|
2021-11-03 06:37:02 -07:00
|
|
|
let circuit = PermuteCircuit::<OrchardNullifier, 3, 2>(PhantomData);
|
2021-02-08 14:56:58 -08:00
|
|
|
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
|
|
|
|
assert_eq!(prover.verify(), Ok(()))
|
|
|
|
}
|
|
|
|
|
2021-11-30 17:08:56 -08:00
|
|
|
struct HashCircuit<
|
|
|
|
S: Spec<Fp, WIDTH, RATE>,
|
|
|
|
const WIDTH: usize,
|
|
|
|
const RATE: usize,
|
|
|
|
const L: usize,
|
|
|
|
> {
|
|
|
|
message: Option<[Fp; L]>,
|
2021-05-18 13:33:11 -07:00
|
|
|
// For the purpose of this test, witness the result.
|
|
|
|
// TODO: Move this into an instance column.
|
|
|
|
output: Option<Fp>,
|
2021-11-03 06:37:02 -07:00
|
|
|
_spec: PhantomData<S>,
|
2021-04-08 19:38:16 -07:00
|
|
|
}
|
|
|
|
|
2021-11-30 17:08:56 -08:00
|
|
|
impl<S: Spec<Fp, WIDTH, RATE>, const WIDTH: usize, const RATE: usize, const L: usize>
|
|
|
|
Circuit<Fp> for HashCircuit<S, WIDTH, RATE, L>
|
2021-11-03 06:37:02 -07:00
|
|
|
{
|
2021-11-02 07:52:11 -07:00
|
|
|
type Config = Pow5Config<Fp, WIDTH, RATE>;
|
2021-07-08 18:56:27 -07:00
|
|
|
type FloorPlanner = SimpleFloorPlanner;
|
|
|
|
|
|
|
|
fn without_witnesses(&self) -> Self {
|
2021-11-03 06:37:02 -07:00
|
|
|
Self {
|
|
|
|
message: None,
|
|
|
|
output: None,
|
|
|
|
_spec: PhantomData,
|
|
|
|
}
|
2021-07-08 18:56:27 -07:00
|
|
|
}
|
2021-04-08 19:38:16 -07:00
|
|
|
|
2021-11-02 07:52:11 -07:00
|
|
|
fn configure(meta: &mut ConstraintSystem<Fp>) -> Pow5Config<Fp, WIDTH, RATE> {
|
2021-11-03 06:37:02 -07:00
|
|
|
let state = (0..WIDTH).map(|_| meta.advice_column()).collect::<Vec<_>>();
|
2021-06-07 05:45:33 -07:00
|
|
|
let partial_sbox = meta.advice_column();
|
2021-04-08 19:38:16 -07:00
|
|
|
|
2021-11-03 06:37:02 -07:00
|
|
|
let rc_a = (0..WIDTH).map(|_| meta.fixed_column()).collect::<Vec<_>>();
|
|
|
|
let rc_b = (0..WIDTH).map(|_| meta.fixed_column()).collect::<Vec<_>>();
|
2021-07-21 04:13:47 -07:00
|
|
|
|
2021-07-23 15:13:10 -07:00
|
|
|
meta.enable_constant(rc_b[0]);
|
|
|
|
|
2021-11-03 06:37:02 -07:00
|
|
|
Pow5Chip::configure::<S>(
|
|
|
|
meta,
|
|
|
|
state.try_into().unwrap(),
|
|
|
|
partial_sbox,
|
|
|
|
rc_a.try_into().unwrap(),
|
|
|
|
rc_b.try_into().unwrap(),
|
|
|
|
)
|
2021-04-08 19:38:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn synthesize(
|
|
|
|
&self,
|
2021-11-02 07:52:11 -07:00
|
|
|
config: Pow5Config<Fp, WIDTH, RATE>,
|
2021-07-08 18:56:27 -07:00
|
|
|
mut layouter: impl Layouter<Fp>,
|
2021-04-08 19:38:16 -07:00
|
|
|
) -> Result<(), Error> {
|
2021-11-02 07:52:11 -07:00
|
|
|
let chip = Pow5Chip::construct(config.clone());
|
2021-04-08 19:38:16 -07:00
|
|
|
|
|
|
|
let message = layouter.assign_region(
|
|
|
|
|| "load message",
|
|
|
|
|mut region| {
|
2021-11-03 06:37:02 -07:00
|
|
|
let message_word = |i: usize| {
|
2021-04-08 19:38:16 -07:00
|
|
|
let value = self.message.map(|message_vals| message_vals[i]);
|
2021-11-04 04:07:27 -07:00
|
|
|
let cell = region.assign_advice(
|
2021-04-08 19:38:16 -07:00
|
|
|
|| format!("load message_{}", i),
|
|
|
|
config.state[i],
|
|
|
|
0,
|
2021-11-23 16:21:44 -08:00
|
|
|
|| value.ok_or(Error::Synthesis),
|
2021-04-08 19:38:16 -07:00
|
|
|
)?;
|
2021-11-04 04:07:27 -07:00
|
|
|
Ok(CellValue::new(cell, value))
|
2021-04-08 19:38:16 -07:00
|
|
|
};
|
|
|
|
|
2021-11-30 17:08:56 -08:00
|
|
|
let message: Result<Vec<_>, Error> = (0..L).map(message_word).collect();
|
2021-11-03 06:37:02 -07:00
|
|
|
Ok(message?.try_into().unwrap())
|
2021-04-08 19:38:16 -07:00
|
|
|
},
|
|
|
|
)?;
|
|
|
|
|
2021-11-03 06:37:02 -07:00
|
|
|
let hasher = Hash::<_, _, S, _, WIDTH, RATE>::init(
|
2021-11-04 04:07:27 -07:00
|
|
|
chip,
|
|
|
|
layouter.namespace(|| "init"),
|
2021-11-30 17:08:56 -08:00
|
|
|
ConstantLength::<L>,
|
2021-11-04 04:07:27 -07:00
|
|
|
)?;
|
2021-04-08 19:38:16 -07:00
|
|
|
let output = hasher.hash(layouter.namespace(|| "hash"), message)?;
|
|
|
|
|
|
|
|
layouter.assign_region(
|
|
|
|
|| "constrain output",
|
|
|
|
|mut region| {
|
|
|
|
let expected_var = region.assign_advice(
|
|
|
|
|| "load output",
|
|
|
|
config.state[0],
|
|
|
|
0,
|
2021-11-23 16:21:44 -08:00
|
|
|
|| self.output.ok_or(Error::Synthesis),
|
2021-04-08 19:38:16 -07:00
|
|
|
)?;
|
2021-11-18 21:04:27 -08:00
|
|
|
region.constrain_equal(output.cell(), expected_var)
|
2021-04-08 19:38:16 -07:00
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn poseidon_hash() {
|
|
|
|
let message = [Fp::rand(), Fp::rand()];
|
2021-11-03 06:12:24 -07:00
|
|
|
let output =
|
|
|
|
poseidon::Hash::<_, OrchardNullifier, _, 3, 2>::init(ConstantLength::<2>).hash(message);
|
2021-04-08 19:38:16 -07:00
|
|
|
|
|
|
|
let k = 6;
|
2021-11-30 17:08:56 -08:00
|
|
|
let circuit = HashCircuit::<OrchardNullifier, 3, 2, 2> {
|
2021-04-08 19:38:16 -07:00
|
|
|
message: Some(message),
|
2021-05-18 13:33:11 -07:00
|
|
|
output: Some(output),
|
2021-11-03 06:37:02 -07:00
|
|
|
_spec: PhantomData,
|
2021-04-08 19:38:16 -07:00
|
|
|
};
|
|
|
|
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
|
|
|
|
assert_eq!(prover.verify(), Ok(()))
|
|
|
|
}
|
|
|
|
|
2021-05-18 13:33:11 -07:00
|
|
|
#[test]
|
|
|
|
fn hash_test_vectors() {
|
2021-08-31 02:06:15 -07:00
|
|
|
for tv in crate::primitives::poseidon::test_vectors::fp::hash() {
|
2021-05-18 13:33:11 -07:00
|
|
|
let message = [
|
|
|
|
pallas::Base::from_repr(tv.input[0]).unwrap(),
|
|
|
|
pallas::Base::from_repr(tv.input[1]).unwrap(),
|
|
|
|
];
|
2021-11-03 06:12:24 -07:00
|
|
|
let output =
|
|
|
|
poseidon::Hash::<_, OrchardNullifier, _, 3, 2>::init(ConstantLength).hash(message);
|
2021-05-18 13:33:11 -07:00
|
|
|
|
|
|
|
let k = 6;
|
2021-11-30 17:08:56 -08:00
|
|
|
let circuit = HashCircuit::<OrchardNullifier, 3, 2, 2> {
|
2021-05-18 13:33:11 -07:00
|
|
|
message: Some(message),
|
|
|
|
output: Some(output),
|
2021-11-03 06:37:02 -07:00
|
|
|
_spec: PhantomData,
|
2021-05-18 13:33:11 -07:00
|
|
|
};
|
|
|
|
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
|
|
|
|
assert_eq!(prover.verify(), Ok(()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-08 14:56:58 -08:00
|
|
|
#[cfg(feature = "dev-graph")]
|
|
|
|
#[test]
|
|
|
|
fn print_poseidon_chip() {
|
|
|
|
use plotters::prelude::*;
|
|
|
|
|
|
|
|
let root = BitMapBackend::new("poseidon-chip-layout.png", (1024, 768)).into_drawing_area();
|
|
|
|
root.fill(&WHITE).unwrap();
|
|
|
|
let root = root
|
|
|
|
.titled("Poseidon Chip Layout", ("sans-serif", 60))
|
|
|
|
.unwrap();
|
|
|
|
|
2021-11-30 17:08:56 -08:00
|
|
|
let circuit = HashCircuit::<OrchardNullifier, 3, 2, 2> {
|
2021-05-18 13:33:11 -07:00
|
|
|
message: None,
|
|
|
|
output: None,
|
2021-11-03 06:37:02 -07:00
|
|
|
_spec: PhantomData,
|
2021-05-18 13:33:11 -07:00
|
|
|
};
|
2021-07-08 18:56:27 -07:00
|
|
|
halo2::dev::CircuitLayout::default()
|
2021-07-26 05:54:27 -07:00
|
|
|
.render(6, &circuit, &root)
|
2021-07-08 18:56:27 -07:00
|
|
|
.unwrap();
|
2021-02-08 14:56:58 -08:00
|
|
|
}
|
|
|
|
}
|