mirror of https://github.com/zcash/orchard.git
Merge pull request #220 from zcash/bench-poseidon
Generalise Poseidon gadget over `WIDTH`, `RATE`
This commit is contained in:
commit
01ec8dca1d
|
@ -40,7 +40,7 @@ use gadget::{
|
||||||
chip::{EccChip, EccConfig},
|
chip::{EccChip, EccConfig},
|
||||||
FixedPoint, FixedPointBaseField, FixedPointShort, NonIdentityPoint, Point,
|
FixedPoint, FixedPointBaseField, FixedPointShort, NonIdentityPoint, Point,
|
||||||
},
|
},
|
||||||
poseidon::{Hash as PoseidonHash, Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig},
|
poseidon::{Hash as PoseidonHash, Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig},
|
||||||
sinsemilla::{
|
sinsemilla::{
|
||||||
chip::{SinsemillaChip, SinsemillaConfig, SinsemillaHashDomains},
|
chip::{SinsemillaChip, SinsemillaConfig, SinsemillaHashDomains},
|
||||||
commit_ivk::CommitIvkConfig,
|
commit_ivk::CommitIvkConfig,
|
||||||
|
@ -82,7 +82,7 @@ pub struct Config {
|
||||||
q_add: Selector,
|
q_add: Selector,
|
||||||
advices: [Column<Advice>; 10],
|
advices: [Column<Advice>; 10],
|
||||||
ecc_config: EccConfig,
|
ecc_config: EccConfig,
|
||||||
poseidon_config: PoseidonConfig<pallas::Base>,
|
poseidon_config: PoseidonConfig<pallas::Base, 3, 2>,
|
||||||
merkle_config_1: MerkleConfig,
|
merkle_config_1: MerkleConfig,
|
||||||
merkle_config_2: MerkleConfig,
|
merkle_config_2: MerkleConfig,
|
||||||
sinsemilla_config_1: SinsemillaConfig,
|
sinsemilla_config_1: SinsemillaConfig,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use pasta_curves::pallas;
|
use pasta_curves::pallas;
|
||||||
|
|
||||||
use ecc::chip::EccChip;
|
use ecc::chip::EccChip;
|
||||||
use poseidon::Pow5T3Chip as PoseidonChip;
|
use poseidon::Pow5Chip as PoseidonChip;
|
||||||
use sinsemilla::{chip::SinsemillaChip, merkle::chip::MerkleChip};
|
use sinsemilla::{chip::SinsemillaChip, merkle::chip::MerkleChip};
|
||||||
|
|
||||||
pub(crate) mod ecc;
|
pub(crate) mod ecc;
|
||||||
|
@ -30,7 +30,7 @@ impl super::Config {
|
||||||
MerkleChip::construct(self.merkle_config_2.clone())
|
MerkleChip::construct(self.merkle_config_2.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn poseidon_chip(&self) -> PoseidonChip<pallas::Base> {
|
pub(super) fn poseidon_chip(&self) -> PoseidonChip<pallas::Base, 3, 2> {
|
||||||
PoseidonChip::construct(self.poseidon_config.clone())
|
PoseidonChip::construct(self.poseidon_config.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ use halo2::{
|
||||||
plonk::Error,
|
plonk::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod pow5t3;
|
mod pow5;
|
||||||
pub use pow5t3::{Pow5T3Chip, Pow5T3Config, StateWord};
|
pub use pow5::{Pow5Chip, Pow5Config, StateWord};
|
||||||
|
|
||||||
use crate::circuit::gadget::utilities::CellValue;
|
use crate::circuit::gadget::utilities::CellValue;
|
||||||
use crate::primitives::poseidon::{ConstantLength, Domain, Spec, Sponge, SpongeState, State};
|
use crate::primitives::poseidon::{ConstantLength, Domain, Spec, Sponge, SpongeState, State};
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::convert::TryInto;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use halo2::{
|
use halo2::{
|
||||||
|
@ -11,11 +12,9 @@ use super::{PoseidonDuplexInstructions, PoseidonInstructions};
|
||||||
use crate::circuit::gadget::utilities::{CellValue, Var};
|
use crate::circuit::gadget::utilities::{CellValue, Var};
|
||||||
use crate::primitives::poseidon::{Domain, Mds, Spec, SpongeState, State};
|
use crate::primitives::poseidon::{Domain, Mds, Spec, SpongeState, State};
|
||||||
|
|
||||||
const WIDTH: usize = 3;
|
/// Configuration for a [`Pow5Chip`].
|
||||||
|
|
||||||
/// Configuration for an [`Pow5T3Chip`].
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Pow5T3Config<F: FieldExt> {
|
pub struct Pow5Config<F: FieldExt, const WIDTH: usize, const RATE: usize> {
|
||||||
pub(in crate::circuit) state: [Column<Advice>; WIDTH],
|
pub(in crate::circuit) state: [Column<Advice>; WIDTH],
|
||||||
partial_sbox: Column<Advice>,
|
partial_sbox: Column<Advice>,
|
||||||
rc_a: [Column<Fixed>; WIDTH],
|
rc_a: [Column<Fixed>; WIDTH],
|
||||||
|
@ -32,13 +31,16 @@ pub struct Pow5T3Config<F: FieldExt> {
|
||||||
m_inv: Mds<F, WIDTH>,
|
m_inv: Mds<F, WIDTH>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Poseidon chip using an $x^5$ S-Box, with a width of 3, suitable for a 2:1 reduction.
|
/// 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.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Pow5T3Chip<F: FieldExt> {
|
pub struct Pow5Chip<F: FieldExt, const WIDTH: usize, const RATE: usize> {
|
||||||
config: Pow5T3Config<F>,
|
config: Pow5Config<F, WIDTH, RATE>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: FieldExt> Pow5T3Chip<F> {
|
impl<F: FieldExt, const WIDTH: usize, const RATE: usize> Pow5Chip<F, WIDTH, RATE> {
|
||||||
/// Configures this chip for use in a circuit.
|
/// Configures this chip for use in a circuit.
|
||||||
///
|
///
|
||||||
/// # Side-effects
|
/// # Side-effects
|
||||||
|
@ -48,14 +50,15 @@ impl<F: FieldExt> Pow5T3Chip<F> {
|
||||||
// TODO: Does the rate need to be hard-coded here, or only the width? It probably
|
// 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
|
// needs to be known wherever we implement the hashing gadget, but it isn't strictly
|
||||||
// necessary for the permutation.
|
// necessary for the permutation.
|
||||||
pub fn configure<S: Spec<F, WIDTH, 2>>(
|
pub fn configure<S: Spec<F, WIDTH, RATE>>(
|
||||||
meta: &mut ConstraintSystem<F>,
|
meta: &mut ConstraintSystem<F>,
|
||||||
spec: S,
|
spec: S,
|
||||||
state: [Column<Advice>; WIDTH],
|
state: [Column<Advice>; WIDTH],
|
||||||
partial_sbox: Column<Advice>,
|
partial_sbox: Column<Advice>,
|
||||||
rc_a: [Column<Fixed>; WIDTH],
|
rc_a: [Column<Fixed>; WIDTH],
|
||||||
rc_b: [Column<Fixed>; WIDTH],
|
rc_b: [Column<Fixed>; WIDTH],
|
||||||
) -> Pow5T3Config<F> {
|
) -> Pow5Config<F, WIDTH, RATE> {
|
||||||
|
assert_eq!(RATE, WIDTH - 1);
|
||||||
// Generate constants for the Poseidon permutation.
|
// Generate constants for the Poseidon permutation.
|
||||||
// This gadget requires R_F and R_P to be even.
|
// This gadget requires R_F and R_P to be even.
|
||||||
assert!(S::full_rounds() & 1 == 0);
|
assert!(S::full_rounds() & 1 == 0);
|
||||||
|
@ -87,123 +90,100 @@ impl<F: FieldExt> Pow5T3Chip<F> {
|
||||||
};
|
};
|
||||||
|
|
||||||
meta.create_gate("full round", |meta| {
|
meta.create_gate("full round", |meta| {
|
||||||
let cur_0 = meta.query_advice(state[0], Rotation::cur());
|
|
||||||
let cur_1 = meta.query_advice(state[1], Rotation::cur());
|
|
||||||
let cur_2 = meta.query_advice(state[2], Rotation::cur());
|
|
||||||
let next = [
|
|
||||||
meta.query_advice(state[0], Rotation::next()),
|
|
||||||
meta.query_advice(state[1], Rotation::next()),
|
|
||||||
meta.query_advice(state[2], Rotation::next()),
|
|
||||||
];
|
|
||||||
|
|
||||||
let rc_0 = meta.query_fixed(rc_a[0], Rotation::cur());
|
|
||||||
let rc_1 = meta.query_fixed(rc_a[1], Rotation::cur());
|
|
||||||
let rc_2 = meta.query_fixed(rc_a[2], Rotation::cur());
|
|
||||||
|
|
||||||
let s_full = meta.query_selector(s_full);
|
let s_full = meta.query_selector(s_full);
|
||||||
|
|
||||||
let full_round = |next_idx: usize| {
|
(0..WIDTH)
|
||||||
s_full.clone()
|
.map(|next_idx| {
|
||||||
* (pow_5(cur_0.clone() + rc_0.clone()) * m_reg[next_idx][0]
|
let state_next = meta.query_advice(state[next_idx], Rotation::next());
|
||||||
+ pow_5(cur_1.clone() + rc_1.clone()) * m_reg[next_idx][1]
|
let expr = (0..WIDTH)
|
||||||
+ pow_5(cur_2.clone() + rc_2.clone()) * m_reg[next_idx][2]
|
.map(|idx| {
|
||||||
- next[next_idx].clone())
|
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]
|
||||||
vec![
|
})
|
||||||
("state[0]", full_round(0)),
|
.reduce(|acc, term| acc + term)
|
||||||
("state[1]", full_round(1)),
|
.expect("WIDTH > 0");
|
||||||
("state[2]", full_round(2)),
|
s_full.clone() * (expr - state_next)
|
||||||
]
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
});
|
});
|
||||||
|
|
||||||
meta.create_gate("partial rounds", |meta| {
|
meta.create_gate("partial rounds", |meta| {
|
||||||
let cur_0 = meta.query_advice(state[0], Rotation::cur());
|
let cur_0 = meta.query_advice(state[0], Rotation::cur());
|
||||||
let cur_1 = meta.query_advice(state[1], Rotation::cur());
|
|
||||||
let cur_2 = meta.query_advice(state[2], Rotation::cur());
|
|
||||||
let mid_0 = meta.query_advice(partial_sbox, Rotation::cur());
|
let mid_0 = meta.query_advice(partial_sbox, Rotation::cur());
|
||||||
let next_0 = meta.query_advice(state[0], Rotation::next());
|
|
||||||
let next_1 = meta.query_advice(state[1], Rotation::next());
|
|
||||||
let next_2 = meta.query_advice(state[2], Rotation::next());
|
|
||||||
|
|
||||||
let rc_a0 = meta.query_fixed(rc_a[0], Rotation::cur());
|
let rc_a0 = meta.query_fixed(rc_a[0], Rotation::cur());
|
||||||
let rc_a1 = meta.query_fixed(rc_a[1], Rotation::cur());
|
|
||||||
let rc_a2 = meta.query_fixed(rc_a[2], Rotation::cur());
|
|
||||||
let rc_b0 = meta.query_fixed(rc_b[0], Rotation::cur());
|
let rc_b0 = meta.query_fixed(rc_b[0], Rotation::cur());
|
||||||
let rc_b1 = meta.query_fixed(rc_b[1], Rotation::cur());
|
|
||||||
let rc_b2 = meta.query_fixed(rc_b[2], Rotation::cur());
|
|
||||||
|
|
||||||
let s_partial = meta.query_selector(s_partial);
|
let s_partial = meta.query_selector(s_partial);
|
||||||
|
|
||||||
let partial_round_linear = |idx: usize, rc_b: Expression<F>| {
|
use halo2::plonk::VirtualCells;
|
||||||
s_partial.clone()
|
let mid = |idx: usize, meta: &mut VirtualCells<F>| {
|
||||||
* (mid_0.clone() * m_reg[idx][0]
|
let mid = mid_0.clone() * m_reg[idx][0];
|
||||||
+ (cur_1.clone() + rc_a1.clone()) * m_reg[idx][1]
|
(1..WIDTH).fold(mid, |acc, cur_idx| {
|
||||||
+ (cur_2.clone() + rc_a2.clone()) * m_reg[idx][2]
|
let cur = meta.query_advice(state[cur_idx], Rotation::cur());
|
||||||
+ rc_b
|
let rc_a = meta.query_fixed(rc_a[cur_idx], Rotation::cur());
|
||||||
- (next_0.clone() * m_inv[idx][0]
|
acc + (cur + rc_a) * m_reg[idx][cur_idx]
|
||||||
+ next_1.clone() * m_inv[idx][1]
|
})
|
||||||
+ next_2.clone() * m_inv[idx][2]))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
vec![
|
let next = |idx: usize, meta: &mut VirtualCells<F>| {
|
||||||
(
|
(0..WIDTH)
|
||||||
"state[0] round a",
|
.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")
|
||||||
|
};
|
||||||
|
|
||||||
|
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(
|
||||||
s_partial.clone() * (pow_5(cur_0 + rc_a0) - mid_0.clone()),
|
s_partial.clone() * (pow_5(cur_0 + rc_a0) - mid_0.clone()),
|
||||||
),
|
))
|
||||||
(
|
// state[0] round b
|
||||||
"state[0] round b",
|
.chain(Some(
|
||||||
s_partial.clone()
|
s_partial.clone() * (pow_5(mid(0, meta) + rc_b0) - next(0, meta)),
|
||||||
* (pow_5(
|
))
|
||||||
mid_0.clone() * m_reg[0][0]
|
.chain((1..WIDTH).map(|idx| partial_round_linear(idx, meta)))
|
||||||
+ (cur_1.clone() + rc_a1.clone()) * m_reg[0][1]
|
.collect::<Vec<_>>()
|
||||||
+ (cur_2.clone() + rc_a2.clone()) * m_reg[0][2]
|
|
||||||
+ rc_b0,
|
|
||||||
) - (next_0.clone() * m_inv[0][0]
|
|
||||||
+ next_1.clone() * m_inv[0][1]
|
|
||||||
+ next_2.clone() * m_inv[0][2])),
|
|
||||||
),
|
|
||||||
("state[1]", partial_round_linear(1, rc_b1)),
|
|
||||||
("state[2]", partial_round_linear(2, rc_b2)),
|
|
||||||
]
|
|
||||||
});
|
});
|
||||||
|
|
||||||
meta.create_gate("pad-and-add", |meta| {
|
meta.create_gate("pad-and-add", |meta| {
|
||||||
let initial_state_0 = meta.query_advice(state[0], Rotation::prev());
|
let initial_state_rate = meta.query_advice(state[RATE], Rotation::prev());
|
||||||
let initial_state_1 = meta.query_advice(state[1], Rotation::prev());
|
let output_state_rate = meta.query_advice(state[RATE], Rotation::next());
|
||||||
let initial_state_2 = meta.query_advice(state[2], Rotation::prev());
|
|
||||||
let input_0 = meta.query_advice(state[0], Rotation::cur());
|
|
||||||
let input_1 = meta.query_advice(state[1], Rotation::cur());
|
|
||||||
let output_state_0 = meta.query_advice(state[0], Rotation::next());
|
|
||||||
let output_state_1 = meta.query_advice(state[1], Rotation::next());
|
|
||||||
let output_state_2 = meta.query_advice(state[2], Rotation::next());
|
|
||||||
|
|
||||||
let s_pad_and_add = meta.query_selector(s_pad_and_add);
|
let s_pad_and_add = meta.query_selector(s_pad_and_add);
|
||||||
|
|
||||||
let pad_and_add = |initial_state, input, output_state| {
|
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());
|
||||||
|
|
||||||
// We pad the input by storing the required padding in fixed columns and
|
// We pad the input by storing the required padding in fixed columns and
|
||||||
// then constraining the corresponding input columns to be equal to it.
|
// then constraining the corresponding input columns to be equal to it.
|
||||||
s_pad_and_add.clone() * (initial_state + input - output_state)
|
s_pad_and_add.clone() * (initial_state + input - output_state)
|
||||||
};
|
};
|
||||||
|
|
||||||
vec![
|
(0..RATE)
|
||||||
(
|
.map(pad_and_add)
|
||||||
"state[0]",
|
|
||||||
pad_and_add(initial_state_0, input_0, output_state_0),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"state[1]",
|
|
||||||
pad_and_add(initial_state_1, input_1, output_state_1),
|
|
||||||
),
|
|
||||||
// The capacity element is never altered by the input.
|
// The capacity element is never altered by the input.
|
||||||
(
|
.chain(Some(
|
||||||
"state[2]",
|
s_pad_and_add.clone() * (initial_state_rate - output_state_rate),
|
||||||
s_pad_and_add * (initial_state_2 - output_state_2),
|
))
|
||||||
),
|
.collect::<Vec<_>>()
|
||||||
]
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Pow5T3Config {
|
Pow5Config {
|
||||||
state,
|
state,
|
||||||
partial_sbox,
|
partial_sbox,
|
||||||
rc_a,
|
rc_a,
|
||||||
|
@ -220,13 +200,13 @@ impl<F: FieldExt> Pow5T3Chip<F> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn construct(config: Pow5T3Config<F>) -> Self {
|
pub fn construct(config: Pow5Config<F, WIDTH, RATE>) -> Self {
|
||||||
Pow5T3Chip { config }
|
Pow5Chip { config }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: FieldExt> Chip<F> for Pow5T3Chip<F> {
|
impl<F: FieldExt, const WIDTH: usize, const RATE: usize> Chip<F> for Pow5Chip<F, WIDTH, RATE> {
|
||||||
type Config = Pow5T3Config<F>;
|
type Config = Pow5Config<F, WIDTH, RATE>;
|
||||||
type Loaded = ();
|
type Loaded = ();
|
||||||
|
|
||||||
fn config(&self) -> &Self::Config {
|
fn config(&self) -> &Self::Config {
|
||||||
|
@ -238,7 +218,9 @@ impl<F: FieldExt> Chip<F> for Pow5T3Chip<F> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: FieldExt, S: Spec<F, WIDTH, 2>> PoseidonInstructions<F, S, WIDTH, 2> for Pow5T3Chip<F> {
|
impl<F: FieldExt, S: Spec<F, WIDTH, RATE>, const WIDTH: usize, const RATE: usize>
|
||||||
|
PoseidonInstructions<F, S, WIDTH, RATE> for Pow5Chip<F, WIDTH, RATE>
|
||||||
|
{
|
||||||
type Word = StateWord<F>;
|
type Word = StateWord<F>;
|
||||||
|
|
||||||
fn permute(
|
fn permute(
|
||||||
|
@ -252,7 +234,7 @@ impl<F: FieldExt, S: Spec<F, WIDTH, 2>> PoseidonInstructions<F, S, WIDTH, 2> for
|
||||||
|| "permute state",
|
|| "permute state",
|
||||||
|mut region| {
|
|mut region| {
|
||||||
// Load the initial state into this region.
|
// Load the initial state into this region.
|
||||||
let state = Pow5T3State::load(&mut region, config, initial_state)?;
|
let state = Pow5State::load(&mut region, config, initial_state)?;
|
||||||
|
|
||||||
let state = (0..config.half_full_rounds).fold(Ok(state), |res, r| {
|
let state = (0..config.half_full_rounds).fold(Ok(state), |res, r| {
|
||||||
res.and_then(|state| state.full_round(&mut region, config, r, r))
|
res.and_then(|state| state.full_round(&mut region, config, r, r))
|
||||||
|
@ -286,46 +268,52 @@ impl<F: FieldExt, S: Spec<F, WIDTH, 2>> PoseidonInstructions<F, S, WIDTH, 2> for
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: FieldExt, S: Spec<F, WIDTH, 2>> PoseidonDuplexInstructions<F, S, WIDTH, 2>
|
impl<F: FieldExt, S: Spec<F, WIDTH, RATE>, const WIDTH: usize, const RATE: usize>
|
||||||
for Pow5T3Chip<F>
|
PoseidonDuplexInstructions<F, S, WIDTH, RATE> for Pow5Chip<F, WIDTH, RATE>
|
||||||
{
|
{
|
||||||
fn initial_state(
|
fn initial_state(
|
||||||
&self,
|
&self,
|
||||||
layouter: &mut impl Layouter<F>,
|
layouter: &mut impl Layouter<F>,
|
||||||
domain: &impl Domain<F, WIDTH, 2>,
|
domain: &impl Domain<F, WIDTH, RATE>,
|
||||||
) -> Result<State<Self::Word, WIDTH>, Error> {
|
) -> Result<State<Self::Word, WIDTH>, Error> {
|
||||||
let config = self.config();
|
let config = self.config();
|
||||||
layouter.assign_region(
|
let state = layouter.assign_region(
|
||||||
|| format!("initial state for domain {:?}", domain),
|
|| format!("initial state for domain {:?}", domain),
|
||||||
|mut region| {
|
|mut region| {
|
||||||
let mut load_state_word = |i: usize, value: F| {
|
let mut state = Vec::with_capacity(WIDTH);
|
||||||
|
let mut load_state_word = |i: usize, value: F| -> Result<_, Error> {
|
||||||
let var = region.assign_advice_from_constant(
|
let var = region.assign_advice_from_constant(
|
||||||
|| format!("state_{}", i),
|
|| format!("state_{}", i),
|
||||||
config.state[i],
|
config.state[i],
|
||||||
0,
|
0,
|
||||||
value,
|
value,
|
||||||
)?;
|
)?;
|
||||||
Ok(StateWord {
|
state.push(StateWord {
|
||||||
var,
|
var,
|
||||||
value: Some(value),
|
value: Some(value),
|
||||||
})
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok([
|
for i in 0..RATE {
|
||||||
load_state_word(0, F::zero())?,
|
load_state_word(i, F::zero())?;
|
||||||
load_state_word(1, F::zero())?,
|
}
|
||||||
load_state_word(2, domain.initial_capacity_element())?,
|
load_state_word(RATE, domain.initial_capacity_element())?;
|
||||||
])
|
|
||||||
|
Ok(state)
|
||||||
},
|
},
|
||||||
)
|
)?;
|
||||||
|
|
||||||
|
Ok(state.try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pad_and_add(
|
fn pad_and_add(
|
||||||
&self,
|
&self,
|
||||||
layouter: &mut impl Layouter<F>,
|
layouter: &mut impl Layouter<F>,
|
||||||
domain: &impl Domain<F, WIDTH, 2>,
|
domain: &impl Domain<F, WIDTH, RATE>,
|
||||||
initial_state: &State<Self::Word, WIDTH>,
|
initial_state: &State<Self::Word, WIDTH>,
|
||||||
input: &SpongeState<Self::Word, 2>,
|
input: &SpongeState<Self::Word, RATE>,
|
||||||
) -> Result<State<Self::Word, WIDTH>, Error> {
|
) -> Result<State<Self::Word, WIDTH>, Error> {
|
||||||
let config = self.config();
|
let config = self.config();
|
||||||
layouter.assign_region(
|
layouter.assign_region(
|
||||||
|
@ -334,7 +322,7 @@ impl<F: FieldExt, S: Spec<F, WIDTH, 2>> PoseidonDuplexInstructions<F, S, WIDTH,
|
||||||
config.s_pad_and_add.enable(&mut region, 1)?;
|
config.s_pad_and_add.enable(&mut region, 1)?;
|
||||||
|
|
||||||
// Load the initial state into this region.
|
// Load the initial state into this region.
|
||||||
let mut load_state_word = |i: usize| {
|
let load_state_word = |i: usize| {
|
||||||
let value = initial_state[i].value;
|
let value = initial_state[i].value;
|
||||||
let var = region.assign_advice(
|
let var = region.assign_advice(
|
||||||
|| format!("load state_{}", i),
|
|| format!("load state_{}", i),
|
||||||
|
@ -345,16 +333,14 @@ impl<F: FieldExt, S: Spec<F, WIDTH, 2>> PoseidonDuplexInstructions<F, S, WIDTH,
|
||||||
region.constrain_equal(initial_state[i].var, var)?;
|
region.constrain_equal(initial_state[i].var, var)?;
|
||||||
Ok(StateWord { var, value })
|
Ok(StateWord { var, value })
|
||||||
};
|
};
|
||||||
let initial_state = [
|
let initial_state: Result<Vec<_>, Error> =
|
||||||
load_state_word(0)?,
|
(0..WIDTH).map(load_state_word).collect();
|
||||||
load_state_word(1)?,
|
let initial_state = initial_state?;
|
||||||
load_state_word(2)?,
|
|
||||||
];
|
|
||||||
|
|
||||||
let padding_values = domain.padding();
|
let padding_values = domain.padding();
|
||||||
|
|
||||||
// Load the input and padding into this region.
|
// Load the input and padding into this region.
|
||||||
let mut load_input_word = |i: usize| {
|
let load_input_word = |i: usize| {
|
||||||
let (constraint_var, value) = match (input[i], padding_values[i]) {
|
let (constraint_var, value) = match (input[i], padding_values[i]) {
|
||||||
(Some(word), None) => (word.var, word.value),
|
(Some(word), None) => (word.var, word.value),
|
||||||
(None, Some(padding_value)) => {
|
(None, Some(padding_value)) => {
|
||||||
|
@ -378,10 +364,11 @@ impl<F: FieldExt, S: Spec<F, WIDTH, 2>> PoseidonDuplexInstructions<F, S, WIDTH,
|
||||||
|
|
||||||
Ok(StateWord { var, value })
|
Ok(StateWord { var, value })
|
||||||
};
|
};
|
||||||
let input = [load_input_word(0)?, load_input_word(1)?];
|
let input: Result<Vec<_>, Error> = (0..RATE).map(load_input_word).collect();
|
||||||
|
let input = input?;
|
||||||
|
|
||||||
// Constrain the output.
|
// Constrain the output.
|
||||||
let mut constrain_output_word = |i: usize| {
|
let constrain_output_word = |i: usize| {
|
||||||
let value = initial_state[i].value.and_then(|initial_word| {
|
let value = initial_state[i].value.and_then(|initial_word| {
|
||||||
input
|
input
|
||||||
.get(i)
|
.get(i)
|
||||||
|
@ -399,17 +386,19 @@ impl<F: FieldExt, S: Spec<F, WIDTH, 2>> PoseidonDuplexInstructions<F, S, WIDTH,
|
||||||
Ok(StateWord { var, value })
|
Ok(StateWord { var, value })
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok([
|
let output: Result<Vec<_>, Error> = (0..WIDTH).map(constrain_output_word).collect();
|
||||||
constrain_output_word(0)?,
|
output.map(|output| output.try_into().unwrap())
|
||||||
constrain_output_word(1)?,
|
|
||||||
constrain_output_word(2)?,
|
|
||||||
])
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_output(state: &State<Self::Word, WIDTH>) -> SpongeState<Self::Word, 2> {
|
fn get_output(state: &State<Self::Word, WIDTH>) -> SpongeState<Self::Word, RATE> {
|
||||||
[Some(state[0]), Some(state[1])]
|
state[..RATE]
|
||||||
|
.iter()
|
||||||
|
.map(|word| Some(*word))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,83 +427,73 @@ impl<F: FieldExt> From<CellValue<F>> for StateWord<F> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Pow5T3State<F: FieldExt>([StateWord<F>; WIDTH]);
|
struct Pow5State<F: FieldExt, const WIDTH: usize>([StateWord<F>; WIDTH]);
|
||||||
|
|
||||||
impl<F: FieldExt> Pow5T3State<F> {
|
impl<F: FieldExt, const WIDTH: usize> Pow5State<F, WIDTH> {
|
||||||
fn full_round(
|
fn full_round<const RATE: usize>(
|
||||||
self,
|
self,
|
||||||
region: &mut Region<F>,
|
region: &mut Region<F>,
|
||||||
config: &Pow5T3Config<F>,
|
config: &Pow5Config<F, WIDTH, RATE>,
|
||||||
round: usize,
|
round: usize,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
Self::round(region, config, round, offset, config.s_full, |_| {
|
Self::round(region, config, round, offset, config.s_full, |_| {
|
||||||
let q_0 = self.0[0]
|
let q = self
|
||||||
.value
|
.0
|
||||||
.map(|v| v + config.round_constants[round][0]);
|
.iter()
|
||||||
let q_1 = self.0[1]
|
.enumerate()
|
||||||
.value
|
.map(|(idx, word)| word.value.map(|v| v + config.round_constants[round][idx]));
|
||||||
.map(|v| v + config.round_constants[round][1]);
|
let r: Option<Vec<F>> = q.map(|q| q.map(|q| q.pow(&config.alpha))).collect();
|
||||||
let q_2 = self.0[2]
|
|
||||||
.value
|
|
||||||
.map(|v| v + config.round_constants[round][2]);
|
|
||||||
|
|
||||||
let r_0 = q_0.map(|v| v.pow(&config.alpha));
|
|
||||||
let r_1 = q_1.map(|v| v.pow(&config.alpha));
|
|
||||||
let r_2 = q_2.map(|v| v.pow(&config.alpha));
|
|
||||||
|
|
||||||
let m = &config.m_reg;
|
let m = &config.m_reg;
|
||||||
let r = r_0.and_then(|r_0| r_1.and_then(|r_1| r_2.map(|r_2| [r_0, r_1, r_2])));
|
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((
|
Ok((round + 1, state.collect::<Vec<_>>().try_into().unwrap()))
|
||||||
round + 1,
|
|
||||||
[
|
|
||||||
r.map(|r| m[0][0] * r[0] + m[0][1] * r[1] + m[0][2] * r[2]),
|
|
||||||
r.map(|r| m[1][0] * r[0] + m[1][1] * r[1] + m[1][2] * r[2]),
|
|
||||||
r.map(|r| m[2][0] * r[0] + m[2][1] * r[1] + m[2][2] * r[2]),
|
|
||||||
],
|
|
||||||
))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn partial_round(
|
fn partial_round<const RATE: usize>(
|
||||||
self,
|
self,
|
||||||
region: &mut Region<F>,
|
region: &mut Region<F>,
|
||||||
config: &Pow5T3Config<F>,
|
config: &Pow5Config<F, WIDTH, RATE>,
|
||||||
round: usize,
|
round: usize,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
Self::round(region, config, round, offset, config.s_partial, |region| {
|
Self::round(region, config, round, offset, config.s_partial, |region| {
|
||||||
let m = &config.m_reg;
|
let m = &config.m_reg;
|
||||||
|
let p: Option<Vec<_>> = self.0.iter().map(|word| word.value).collect();
|
||||||
|
|
||||||
let p = self.0[0].value.and_then(|p_0| {
|
let r: Option<Vec<_>> = p.map(|p| {
|
||||||
self.0[1]
|
let r_0 = (p[0] + config.round_constants[round][0]).pow(&config.alpha);
|
||||||
.value
|
let r_i = p[1..]
|
||||||
.and_then(|p_1| self.0[2].value.map(|p_2| [p_0, p_1, p_2]))
|
.iter()
|
||||||
});
|
.enumerate()
|
||||||
|
.map(|(i, p_i)| *p_i + config.round_constants[round][i + 1]);
|
||||||
let r = p.map(|p| {
|
std::iter::empty().chain(Some(r_0)).chain(r_i).collect()
|
||||||
[
|
|
||||||
(p[0] + config.round_constants[round][0]).pow(&config.alpha),
|
|
||||||
p[1] + config.round_constants[round][1],
|
|
||||||
p[2] + config.round_constants[round][2],
|
|
||||||
]
|
|
||||||
});
|
});
|
||||||
|
|
||||||
region.assign_advice(
|
region.assign_advice(
|
||||||
|| format!("round_{} partial_sbox", round),
|
|| format!("round_{} partial_sbox", round),
|
||||||
config.partial_sbox,
|
config.partial_sbox,
|
||||||
offset,
|
offset,
|
||||||
|| r.map(|r| r[0]).ok_or(Error::SynthesisError),
|
|| r.as_ref().map(|r| r[0]).ok_or(Error::SynthesisError),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let p_mid = r.map(|r| {
|
let p_mid: Option<Vec<_>> = m
|
||||||
[
|
.iter()
|
||||||
m[0][0] * r[0] + m[0][1] * r[1] + m[0][2] * r[2],
|
.map(|m_i| {
|
||||||
m[1][0] * r[0] + m[1][1] * r[1] + m[1][2] * r[2],
|
r.as_ref().map(|r| {
|
||||||
m[2][0] * r[0] + m[2][1] * r[1] + m[2][2] * r[2],
|
m_i.iter()
|
||||||
]
|
.zip(r.iter())
|
||||||
});
|
.fold(F::zero(), |acc, (m_ij, r_j)| acc + *m_ij * r_j)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Load the second round constants.
|
// Load the second round constants.
|
||||||
let mut load_round_constant = |i: usize| {
|
let mut load_round_constant = |i: usize| {
|
||||||
|
@ -529,31 +508,36 @@ impl<F: FieldExt> Pow5T3State<F> {
|
||||||
load_round_constant(i)?;
|
load_round_constant(i)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let r_mid = p_mid.map(|p| {
|
let r_mid: Option<Vec<_>> = p_mid.map(|p| {
|
||||||
[
|
let r_0 = (p[0] + config.round_constants[round + 1][0]).pow(&config.alpha);
|
||||||
(p[0] + config.round_constants[round + 1][0]).pow(&config.alpha),
|
let r_i = p[1..]
|
||||||
p[1] + config.round_constants[round + 1][1],
|
.iter()
|
||||||
p[2] + config.round_constants[round + 1][2],
|
.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()
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok((
|
let state: Vec<Option<_>> = m
|
||||||
round + 2,
|
.iter()
|
||||||
[
|
.map(|m_i| {
|
||||||
r_mid.map(|r| m[0][0] * r[0] + m[0][1] * r[1] + m[0][2] * r[2]),
|
r_mid.as_ref().map(|r| {
|
||||||
r_mid.map(|r| m[1][0] * r[0] + m[1][1] * r[1] + m[1][2] * r[2]),
|
m_i.iter()
|
||||||
r_mid.map(|r| m[2][0] * r[0] + m[2][1] * r[1] + m[2][2] * r[2]),
|
.zip(r.iter())
|
||||||
],
|
.fold(F::zero(), |acc, (m_ij, r_j)| acc + *m_ij * r_j)
|
||||||
))
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok((round + 2, state.try_into().unwrap()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(
|
fn load<const RATE: usize>(
|
||||||
region: &mut Region<F>,
|
region: &mut Region<F>,
|
||||||
config: &Pow5T3Config<F>,
|
config: &Pow5Config<F, WIDTH, RATE>,
|
||||||
initial_state: &State<StateWord<F>, WIDTH>,
|
initial_state: &State<StateWord<F>, WIDTH>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let mut load_state_word = |i: usize| {
|
let load_state_word = |i: usize| {
|
||||||
let value = initial_state[i].value;
|
let value = initial_state[i].value;
|
||||||
let var = region.assign_advice(
|
let var = region.assign_advice(
|
||||||
|| format!("load state_{}", i),
|
|| format!("load state_{}", i),
|
||||||
|
@ -565,16 +549,13 @@ impl<F: FieldExt> Pow5T3State<F> {
|
||||||
Ok(StateWord { var, value })
|
Ok(StateWord { var, value })
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Pow5T3State([
|
let state: Result<Vec<_>, _> = (0..WIDTH).map(load_state_word).collect();
|
||||||
load_state_word(0)?,
|
state.map(|state| Pow5State(state.try_into().unwrap()))
|
||||||
load_state_word(1)?,
|
|
||||||
load_state_word(2)?,
|
|
||||||
]))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn round(
|
fn round<const RATE: usize>(
|
||||||
region: &mut Region<F>,
|
region: &mut Region<F>,
|
||||||
config: &Pow5T3Config<F>,
|
config: &Pow5Config<F, WIDTH, RATE>,
|
||||||
round: usize,
|
round: usize,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
round_gate: Selector,
|
round_gate: Selector,
|
||||||
|
@ -599,7 +580,7 @@ impl<F: FieldExt> Pow5T3State<F> {
|
||||||
// Compute the next round's state.
|
// Compute the next round's state.
|
||||||
let (next_round, next_state) = round_fn(region)?;
|
let (next_round, next_state) = round_fn(region)?;
|
||||||
|
|
||||||
let mut next_state_word = |i: usize| {
|
let next_state_word = |i: usize| {
|
||||||
let value = next_state[i];
|
let value = next_state[i];
|
||||||
let var = region.assign_advice(
|
let var = region.assign_advice(
|
||||||
|| format!("round_{} state_{}", next_round, i),
|
|| format!("round_{} state_{}", next_round, i),
|
||||||
|
@ -610,11 +591,8 @@ impl<F: FieldExt> Pow5T3State<F> {
|
||||||
Ok(StateWord { var, value })
|
Ok(StateWord { var, value })
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Pow5T3State([
|
let next_state: Result<Vec<_>, _> = (0..WIDTH).map(next_state_word).collect();
|
||||||
next_state_word(0)?,
|
next_state.map(|next_state| Pow5State(next_state.try_into().unwrap()))
|
||||||
next_state_word(1)?,
|
|
||||||
next_state_word(2)?,
|
|
||||||
]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -630,7 +608,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
use pasta_curves::pallas;
|
use pasta_curves::pallas;
|
||||||
|
|
||||||
use super::{PoseidonInstructions, Pow5T3Chip, Pow5T3Config, StateWord, WIDTH};
|
use super::{PoseidonInstructions, Pow5Chip, Pow5Config, StateWord};
|
||||||
use crate::{
|
use crate::{
|
||||||
circuit::gadget::{
|
circuit::gadget::{
|
||||||
poseidon::Hash,
|
poseidon::Hash,
|
||||||
|
@ -639,17 +617,20 @@ mod tests {
|
||||||
primitives::poseidon::{self, ConstantLength, P128Pow5T3 as OrchardNullifier, Spec},
|
primitives::poseidon::{self, ConstantLength, P128Pow5T3 as OrchardNullifier, Spec},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const WIDTH: usize = 3;
|
||||||
|
const RATE: usize = 2;
|
||||||
|
|
||||||
struct PermuteCircuit {}
|
struct PermuteCircuit {}
|
||||||
|
|
||||||
impl Circuit<Fp> for PermuteCircuit {
|
impl Circuit<Fp> for PermuteCircuit {
|
||||||
type Config = Pow5T3Config<Fp>;
|
type Config = Pow5Config<Fp, WIDTH, RATE>;
|
||||||
type FloorPlanner = SimpleFloorPlanner;
|
type FloorPlanner = SimpleFloorPlanner;
|
||||||
|
|
||||||
fn without_witnesses(&self) -> Self {
|
fn without_witnesses(&self) -> Self {
|
||||||
PermuteCircuit {}
|
PermuteCircuit {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure(meta: &mut ConstraintSystem<Fp>) -> Pow5T3Config<Fp> {
|
fn configure(meta: &mut ConstraintSystem<Fp>) -> Pow5Config<Fp, WIDTH, RATE> {
|
||||||
let state = [
|
let state = [
|
||||||
meta.advice_column(),
|
meta.advice_column(),
|
||||||
meta.advice_column(),
|
meta.advice_column(),
|
||||||
|
@ -668,12 +649,12 @@ mod tests {
|
||||||
meta.fixed_column(),
|
meta.fixed_column(),
|
||||||
];
|
];
|
||||||
|
|
||||||
Pow5T3Chip::configure(meta, OrchardNullifier, state, partial_sbox, rc_a, rc_b)
|
Pow5Chip::configure(meta, OrchardNullifier, state, partial_sbox, rc_a, rc_b)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn synthesize(
|
fn synthesize(
|
||||||
&self,
|
&self,
|
||||||
config: Pow5T3Config<Fp>,
|
config: Pow5Config<Fp, WIDTH, RATE>,
|
||||||
mut layouter: impl Layouter<Fp>,
|
mut layouter: impl Layouter<Fp>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let initial_state = layouter.assign_region(
|
let initial_state = layouter.assign_region(
|
||||||
|
@ -694,8 +675,8 @@ mod tests {
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let chip = Pow5T3Chip::construct(config.clone());
|
let chip = Pow5Chip::construct(config.clone());
|
||||||
let final_state = <Pow5T3Chip<_> as PoseidonInstructions<
|
let final_state = <Pow5Chip<_, WIDTH, RATE> as PoseidonInstructions<
|
||||||
Fp,
|
Fp,
|
||||||
OrchardNullifier,
|
OrchardNullifier,
|
||||||
WIDTH,
|
WIDTH,
|
||||||
|
@ -705,7 +686,7 @@ mod tests {
|
||||||
// For the purpose of this test, compute the real final state inline.
|
// For the purpose of this test, compute the real final state inline.
|
||||||
let mut expected_final_state = [Fp::zero(), Fp::one(), Fp::from_u64(2)];
|
let mut expected_final_state = [Fp::zero(), Fp::one(), Fp::from_u64(2)];
|
||||||
let (round_constants, mds, _) = OrchardNullifier.constants();
|
let (round_constants, mds, _) = OrchardNullifier.constants();
|
||||||
poseidon::permute::<_, OrchardNullifier, WIDTH, 2>(
|
poseidon::permute::<_, OrchardNullifier, WIDTH, RATE>(
|
||||||
&mut expected_final_state,
|
&mut expected_final_state,
|
||||||
&mds,
|
&mds,
|
||||||
&round_constants,
|
&round_constants,
|
||||||
|
@ -749,14 +730,14 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Circuit<Fp> for HashCircuit {
|
impl Circuit<Fp> for HashCircuit {
|
||||||
type Config = Pow5T3Config<Fp>;
|
type Config = Pow5Config<Fp, WIDTH, RATE>;
|
||||||
type FloorPlanner = SimpleFloorPlanner;
|
type FloorPlanner = SimpleFloorPlanner;
|
||||||
|
|
||||||
fn without_witnesses(&self) -> Self {
|
fn without_witnesses(&self) -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure(meta: &mut ConstraintSystem<Fp>) -> Pow5T3Config<Fp> {
|
fn configure(meta: &mut ConstraintSystem<Fp>) -> Pow5Config<Fp, WIDTH, RATE> {
|
||||||
let state = [
|
let state = [
|
||||||
meta.advice_column(),
|
meta.advice_column(),
|
||||||
meta.advice_column(),
|
meta.advice_column(),
|
||||||
|
@ -777,15 +758,15 @@ mod tests {
|
||||||
|
|
||||||
meta.enable_constant(rc_b[0]);
|
meta.enable_constant(rc_b[0]);
|
||||||
|
|
||||||
Pow5T3Chip::configure(meta, OrchardNullifier, state, partial_sbox, rc_a, rc_b)
|
Pow5Chip::configure(meta, OrchardNullifier, state, partial_sbox, rc_a, rc_b)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn synthesize(
|
fn synthesize(
|
||||||
&self,
|
&self,
|
||||||
config: Pow5T3Config<Fp>,
|
config: Pow5Config<Fp, WIDTH, RATE>,
|
||||||
mut layouter: impl Layouter<Fp>,
|
mut layouter: impl Layouter<Fp>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let chip = Pow5T3Chip::construct(config.clone());
|
let chip = Pow5Chip::construct(config.clone());
|
||||||
|
|
||||||
let message = layouter.assign_region(
|
let message = layouter.assign_region(
|
||||||
|| "load message",
|
|| "load message",
|
Loading…
Reference in New Issue