mirror of https://github.com/zcash/halo2.git
poseidon: Remove `self` parameter from `Domain` trait methods
For almost all the sponge constructions defined in the Poseidon paper, the domain can be defined completely statically. Variable-length hashing requires knowledge of the message length, but that can be provided to the fixed padding function in a subsequent commit, and in any case we can't use variable-length inputs in a circuit.
This commit is contained in:
parent
9f654005c7
commit
bfc65d5985
|
@ -101,10 +101,9 @@ where
|
|||
},
|
||||
)?;
|
||||
|
||||
let hasher = Hash::<_, _, S, _, WIDTH, RATE>::init(
|
||||
let hasher = Hash::<_, _, S, ConstantLength<L>, WIDTH, RATE>::init(
|
||||
chip,
|
||||
layouter.namespace(|| "init"),
|
||||
ConstantLength::<L>,
|
||||
)?;
|
||||
let output = hasher.hash(layouter.namespace(|| "hash"), message)?;
|
||||
|
||||
|
@ -210,7 +209,7 @@ fn bench_poseidon<S, const WIDTH: usize, const RATE: usize, const L: usize>(
|
|||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let output = poseidon::Hash::<_, S, _, WIDTH, RATE>::init(ConstantLength::<L>).hash(message);
|
||||
let output = poseidon::Hash::<_, S, ConstantLength<L>, WIDTH, RATE>::init().hash(message);
|
||||
|
||||
let circuit = HashCircuit::<S, WIDTH, RATE, L> {
|
||||
message: Some(message),
|
||||
|
|
|
@ -21,7 +21,9 @@ fn bench_primitives(c: &mut Criterion) {
|
|||
let message = [pallas::Base::random(rng), pallas::Base::random(rng)];
|
||||
|
||||
group.bench_function("2-to-1", |b| {
|
||||
b.iter(|| poseidon::Hash::<_, P128Pow5T3, _, 3, 2>::init(ConstantLength).hash(message))
|
||||
b.iter(|| {
|
||||
poseidon::Hash::<_, P128Pow5T3, ConstantLength<2>, 3, 2>::init().hash(message)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -482,11 +482,11 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
// hash_old = poseidon_hash(nk, rho_old)
|
||||
let hash_old = {
|
||||
let poseidon_message = [nk.clone(), rho_old.clone()];
|
||||
let poseidon_hasher = PoseidonHash::<_, _, poseidon::P128Pow5T3, _, 3, 2>::init(
|
||||
config.poseidon_chip(),
|
||||
layouter.namespace(|| "Poseidon init"),
|
||||
ConstantLength::<2>,
|
||||
)?;
|
||||
let poseidon_hasher =
|
||||
PoseidonHash::<_, _, poseidon::P128Pow5T3, ConstantLength<2>, 3, 2>::init(
|
||||
config.poseidon_chip(),
|
||||
layouter.namespace(|| "Poseidon init"),
|
||||
)?;
|
||||
poseidon_hasher.hash(
|
||||
layouter.namespace(|| "Poseidon hash (nk, rho_old)"),
|
||||
poseidon_message,
|
||||
|
|
|
@ -39,22 +39,19 @@ pub trait PoseidonInstructions<F: FieldExt, S: Spec<F, T, RATE>, const T: usize,
|
|||
pub trait PoseidonSpongeInstructions<
|
||||
F: FieldExt,
|
||||
S: Spec<F, T, RATE>,
|
||||
D: Domain<F, T, RATE>,
|
||||
const T: usize,
|
||||
const RATE: usize,
|
||||
>: PoseidonInstructions<F, S, T, RATE>
|
||||
{
|
||||
/// Returns the initial empty state for the given domain.
|
||||
fn initial_state(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<F>,
|
||||
domain: &impl Domain<F, T, RATE>,
|
||||
) -> Result<State<Self::Word, T>, Error>;
|
||||
fn initial_state(&self, layouter: &mut impl Layouter<F>)
|
||||
-> Result<State<Self::Word, T>, Error>;
|
||||
|
||||
/// Pads the given input (according to the specified domain) and adds it to the state.
|
||||
fn pad_and_add(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<F>,
|
||||
domain: &impl Domain<F, T, RATE>,
|
||||
initial_state: &State<Self::Word, T>,
|
||||
input: &SpongeRate<Self::Word, RATE>,
|
||||
) -> Result<State<Self::Word, T>, Error>;
|
||||
|
@ -96,7 +93,7 @@ impl<
|
|||
|
||||
fn poseidon_sponge<
|
||||
F: FieldExt,
|
||||
PoseidonChip: PoseidonSpongeInstructions<F, S, T, RATE>,
|
||||
PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
|
||||
S: Spec<F, T, RATE>,
|
||||
D: Domain<F, T, RATE>,
|
||||
const T: usize,
|
||||
|
@ -104,11 +101,10 @@ fn poseidon_sponge<
|
|||
>(
|
||||
chip: &PoseidonChip,
|
||||
mut layouter: impl Layouter<F>,
|
||||
domain: &D,
|
||||
state: &mut State<PoseidonChip::Word, T>,
|
||||
input: &SpongeRate<PoseidonChip::Word, RATE>,
|
||||
) -> Result<SpongeRate<PoseidonChip::Word, RATE>, Error> {
|
||||
*state = chip.pad_and_add(&mut layouter, domain, state, input)?;
|
||||
*state = chip.pad_and_add(&mut layouter, state, input)?;
|
||||
*state = chip.permute(&mut layouter, state)?;
|
||||
Ok(PoseidonChip::get_output(state))
|
||||
}
|
||||
|
@ -117,7 +113,7 @@ fn poseidon_sponge<
|
|||
#[derive(Debug)]
|
||||
pub struct Sponge<
|
||||
F: FieldExt,
|
||||
PoseidonChip: PoseidonSpongeInstructions<F, S, T, RATE>,
|
||||
PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
|
||||
S: Spec<F, T, RATE>,
|
||||
M: SpongeMode,
|
||||
D: Domain<F, T, RATE>,
|
||||
|
@ -127,13 +123,12 @@ pub struct Sponge<
|
|||
chip: PoseidonChip,
|
||||
mode: M,
|
||||
state: State<PoseidonChip::Word, T>,
|
||||
domain: D,
|
||||
_marker: PhantomData<M>,
|
||||
_marker: PhantomData<(M, D)>,
|
||||
}
|
||||
|
||||
impl<
|
||||
F: FieldExt,
|
||||
PoseidonChip: PoseidonSpongeInstructions<F, S, T, RATE>,
|
||||
PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
|
||||
S: Spec<F, T, RATE>,
|
||||
D: Domain<F, T, RATE>,
|
||||
const T: usize,
|
||||
|
@ -141,25 +136,19 @@ impl<
|
|||
> Sponge<F, PoseidonChip, S, Absorbing<PoseidonChip::Word, RATE>, D, T, RATE>
|
||||
{
|
||||
/// Constructs a new duplex sponge for the given Poseidon specification.
|
||||
pub fn new(
|
||||
chip: PoseidonChip,
|
||||
mut layouter: impl Layouter<F>,
|
||||
domain: D,
|
||||
) -> Result<Self, Error> {
|
||||
chip.initial_state(&mut layouter, &domain)
|
||||
.map(|state| Sponge {
|
||||
chip,
|
||||
mode: Absorbing(
|
||||
(0..RATE)
|
||||
.map(|_| None)
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
),
|
||||
state,
|
||||
domain,
|
||||
_marker: PhantomData::default(),
|
||||
})
|
||||
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(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Absorbs an element into the sponge.
|
||||
|
@ -179,7 +168,6 @@ impl<
|
|||
let _ = poseidon_sponge(
|
||||
&self.chip,
|
||||
layouter.namespace(|| "PoseidonSponge"),
|
||||
&self.domain,
|
||||
&mut self.state,
|
||||
&self.mode.0,
|
||||
)?;
|
||||
|
@ -198,7 +186,6 @@ impl<
|
|||
let mode = Squeezing(poseidon_sponge(
|
||||
&self.chip,
|
||||
layouter.namespace(|| "PoseidonSponge"),
|
||||
&self.domain,
|
||||
&mut self.state,
|
||||
&self.mode.0,
|
||||
)?);
|
||||
|
@ -207,7 +194,6 @@ impl<
|
|||
chip: self.chip,
|
||||
mode,
|
||||
state: self.state,
|
||||
domain: self.domain,
|
||||
_marker: PhantomData::default(),
|
||||
})
|
||||
}
|
||||
|
@ -215,7 +201,7 @@ impl<
|
|||
|
||||
impl<
|
||||
F: FieldExt,
|
||||
PoseidonChip: PoseidonSpongeInstructions<F, S, T, RATE>,
|
||||
PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
|
||||
S: Spec<F, T, RATE>,
|
||||
D: Domain<F, T, RATE>,
|
||||
const T: usize,
|
||||
|
@ -235,7 +221,6 @@ impl<
|
|||
self.mode = Squeezing(poseidon_sponge(
|
||||
&self.chip,
|
||||
layouter.namespace(|| "PoseidonSponge"),
|
||||
&self.domain,
|
||||
&mut self.state,
|
||||
&self.mode.0,
|
||||
)?);
|
||||
|
@ -247,7 +232,7 @@ impl<
|
|||
#[derive(Debug)]
|
||||
pub struct Hash<
|
||||
F: FieldExt,
|
||||
PoseidonChip: PoseidonSpongeInstructions<F, S, T, RATE>,
|
||||
PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
|
||||
S: Spec<F, T, RATE>,
|
||||
D: Domain<F, T, RATE>,
|
||||
const T: usize,
|
||||
|
@ -258,7 +243,7 @@ pub struct Hash<
|
|||
|
||||
impl<
|
||||
F: FieldExt,
|
||||
PoseidonChip: PoseidonSpongeInstructions<F, S, T, RATE>,
|
||||
PoseidonChip: PoseidonSpongeInstructions<F, S, D, T, RATE>,
|
||||
S: Spec<F, T, RATE>,
|
||||
D: Domain<F, T, RATE>,
|
||||
const T: usize,
|
||||
|
@ -266,14 +251,14 @@ impl<
|
|||
> Hash<F, PoseidonChip, S, D, T, RATE>
|
||||
{
|
||||
/// Initializes a new hasher.
|
||||
pub fn init(chip: PoseidonChip, layouter: impl Layouter<F>, domain: D) -> Result<Self, Error> {
|
||||
Sponge::new(chip, layouter, domain).map(|sponge| Hash { sponge })
|
||||
pub fn init(chip: PoseidonChip, layouter: impl Layouter<F>) -> Result<Self, Error> {
|
||||
Sponge::new(chip, layouter).map(|sponge| Hash { sponge })
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
F: FieldExt,
|
||||
PoseidonChip: PoseidonSpongeInstructions<F, S, T, RATE>,
|
||||
PoseidonChip: PoseidonSpongeInstructions<F, S, ConstantLength<L>, T, RATE>,
|
||||
S: Spec<F, T, RATE>,
|
||||
const T: usize,
|
||||
const RATE: usize,
|
||||
|
|
|
@ -268,17 +268,21 @@ impl<F: FieldExt, S: Spec<F, WIDTH, RATE>, const WIDTH: usize, const RATE: usize
|
|||
}
|
||||
}
|
||||
|
||||
impl<F: FieldExt, S: Spec<F, WIDTH, RATE>, const WIDTH: usize, const RATE: usize>
|
||||
PoseidonSpongeInstructions<F, S, WIDTH, RATE> for Pow5Chip<F, WIDTH, RATE>
|
||||
impl<
|
||||
F: FieldExt,
|
||||
S: Spec<F, WIDTH, RATE>,
|
||||
D: Domain<F, WIDTH, RATE>,
|
||||
const WIDTH: usize,
|
||||
const RATE: usize,
|
||||
> PoseidonSpongeInstructions<F, S, D, WIDTH, RATE> for Pow5Chip<F, WIDTH, RATE>
|
||||
{
|
||||
fn initial_state(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<F>,
|
||||
domain: &impl Domain<F, WIDTH, RATE>,
|
||||
) -> Result<State<Self::Word, WIDTH>, Error> {
|
||||
let config = self.config();
|
||||
let state = layouter.assign_region(
|
||||
|| format!("initial state for domain {:?}", domain),
|
||||
|| format!("initial state for domain {}", D::name()),
|
||||
|mut region| {
|
||||
let mut state = Vec::with_capacity(WIDTH);
|
||||
let mut load_state_word = |i: usize, value: F| -> Result<_, Error> {
|
||||
|
@ -296,7 +300,7 @@ impl<F: FieldExt, S: Spec<F, WIDTH, RATE>, const WIDTH: usize, const RATE: usize
|
|||
for i in 0..RATE {
|
||||
load_state_word(i, F::zero())?;
|
||||
}
|
||||
load_state_word(RATE, domain.initial_capacity_element())?;
|
||||
load_state_word(RATE, D::initial_capacity_element())?;
|
||||
|
||||
Ok(state)
|
||||
},
|
||||
|
@ -308,13 +312,12 @@ impl<F: FieldExt, S: Spec<F, WIDTH, RATE>, const WIDTH: usize, const RATE: usize
|
|||
fn pad_and_add(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<F>,
|
||||
domain: &impl Domain<F, WIDTH, RATE>,
|
||||
initial_state: &State<Self::Word, WIDTH>,
|
||||
input: &SpongeRate<Self::Word, RATE>,
|
||||
) -> Result<State<Self::Word, WIDTH>, Error> {
|
||||
let config = self.config();
|
||||
layouter.assign_region(
|
||||
|| format!("pad-and-add for domain {:?}", domain),
|
||||
|| format!("pad-and-add for domain {}", D::name()),
|
||||
|mut region| {
|
||||
config.s_pad_and_add.enable(&mut region, 1)?;
|
||||
|
||||
|
@ -334,7 +337,7 @@ impl<F: FieldExt, S: Spec<F, WIDTH, RATE>, const WIDTH: usize, const RATE: usize
|
|||
(0..WIDTH).map(load_state_word).collect();
|
||||
let initial_state = initial_state?;
|
||||
|
||||
let padding_values = domain.padding();
|
||||
let padding_values = D::padding();
|
||||
|
||||
// Load the input and padding into this region.
|
||||
let load_input_word = |i: usize| {
|
||||
|
@ -783,10 +786,9 @@ mod tests {
|
|||
},
|
||||
)?;
|
||||
|
||||
let hasher = Hash::<_, _, S, _, WIDTH, RATE>::init(
|
||||
let hasher = Hash::<_, _, S, ConstantLength<L>, WIDTH, RATE>::init(
|
||||
chip,
|
||||
layouter.namespace(|| "init"),
|
||||
ConstantLength::<L>,
|
||||
)?;
|
||||
let output = hasher.hash(layouter.namespace(|| "hash"), message)?;
|
||||
|
||||
|
@ -809,7 +811,7 @@ mod tests {
|
|||
fn poseidon_hash() {
|
||||
let message = [Fp::rand(), Fp::rand()];
|
||||
let output =
|
||||
poseidon::Hash::<_, OrchardNullifier, _, 3, 2>::init(ConstantLength::<2>).hash(message);
|
||||
poseidon::Hash::<_, OrchardNullifier, ConstantLength<2>, 3, 2>::init().hash(message);
|
||||
|
||||
let k = 6;
|
||||
let circuit = HashCircuit::<OrchardNullifier, 3, 2, 2> {
|
||||
|
@ -828,8 +830,8 @@ mod tests {
|
|||
pallas::Base::from_repr(tv.input[0]).unwrap(),
|
||||
pallas::Base::from_repr(tv.input[1]).unwrap(),
|
||||
];
|
||||
let output =
|
||||
poseidon::Hash::<_, OrchardNullifier, _, 3, 2>::init(ConstantLength).hash(message);
|
||||
let output = poseidon::Hash::<_, OrchardNullifier, ConstantLength<2>, 3, 2>::init()
|
||||
.hash(message);
|
||||
|
||||
let k = 6;
|
||||
let circuit = HashCircuit::<OrchardNullifier, 3, 2, 2> {
|
||||
|
|
|
@ -275,16 +275,19 @@ impl<F: FieldExt, S: Spec<F, T, RATE>, const T: usize, const RATE: usize>
|
|||
}
|
||||
|
||||
/// A domain in which a Poseidon hash function is being used.
|
||||
pub trait Domain<F: FieldExt, const T: usize, const RATE: usize>: Copy + fmt::Debug {
|
||||
pub trait Domain<F: FieldExt, const T: usize, const RATE: usize> {
|
||||
/// The name of this domain, for debug formatting purposes.
|
||||
fn name() -> String;
|
||||
|
||||
/// The initial capacity element, encoding this domain.
|
||||
fn initial_capacity_element(&self) -> F;
|
||||
fn initial_capacity_element() -> F;
|
||||
|
||||
/// The padding that will be added to each state word by [`Domain::pad_and_add`].
|
||||
fn padding(&self) -> SpongeRate<F, RATE>;
|
||||
fn padding() -> SpongeRate<F, RATE>;
|
||||
|
||||
/// Returns a function that will update the given state with the given input to a
|
||||
/// duplex permutation round, applying padding according to this domain specification.
|
||||
fn pad_and_add(&self) -> Box<dyn Fn(&mut State<F, T>, &SpongeRate<F, RATE>)>;
|
||||
fn pad_and_add() -> Box<dyn Fn(&mut State<F, T>, &SpongeRate<F, RATE>)>;
|
||||
}
|
||||
|
||||
/// A Poseidon hash function used with constant input length.
|
||||
|
@ -296,13 +299,17 @@ pub struct ConstantLength<const L: usize>;
|
|||
impl<F: FieldExt, const T: usize, const RATE: usize, const L: usize> Domain<F, T, RATE>
|
||||
for ConstantLength<L>
|
||||
{
|
||||
fn initial_capacity_element(&self) -> F {
|
||||
fn name() -> String {
|
||||
format!("ConstantLength<{}>", L)
|
||||
}
|
||||
|
||||
fn initial_capacity_element() -> F {
|
||||
// Capacity value is $length \cdot 2^64 + (o-1)$ where o is the output length.
|
||||
// We hard-code an output length of 1.
|
||||
F::from_u128((L as u128) << 64)
|
||||
}
|
||||
|
||||
fn padding(&self) -> SpongeRate<F, RATE> {
|
||||
fn padding() -> SpongeRate<F, RATE> {
|
||||
// For constant-input-length hashing, padding consists of the field elements being
|
||||
// zero.
|
||||
let mut padding = [None; RATE];
|
||||
|
@ -312,7 +319,7 @@ impl<F: FieldExt, const T: usize, const RATE: usize, const L: usize> Domain<F, T
|
|||
padding
|
||||
}
|
||||
|
||||
fn pad_and_add(&self) -> Box<dyn Fn(&mut State<F, T>, &SpongeRate<F, RATE>)> {
|
||||
fn pad_and_add() -> Box<dyn Fn(&mut State<F, T>, &SpongeRate<F, RATE>)> {
|
||||
Box::new(|state, input| {
|
||||
// `Iterator::zip` short-circuits when one iterator completes, so this will only
|
||||
// mutate the rate portion of the state.
|
||||
|
@ -336,7 +343,7 @@ pub struct Hash<
|
|||
const RATE: usize,
|
||||
> {
|
||||
sponge: Sponge<F, S, Absorbing<F, RATE>, T, RATE>,
|
||||
domain: D,
|
||||
_domain: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<
|
||||
|
@ -353,7 +360,7 @@ impl<
|
|||
.field("rate", &RATE)
|
||||
.field("R_F", &S::full_rounds())
|
||||
.field("R_P", &S::partial_rounds())
|
||||
.field("domain", &self.domain)
|
||||
.field("domain", &D::name())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
@ -367,10 +374,10 @@ impl<
|
|||
> Hash<F, S, D, T, RATE>
|
||||
{
|
||||
/// Initializes a new hasher.
|
||||
pub fn init(domain: D) -> Self {
|
||||
pub fn init() -> Self {
|
||||
Hash {
|
||||
sponge: Sponge::new(domain.initial_capacity_element(), domain.pad_and_add()),
|
||||
domain,
|
||||
sponge: Sponge::new(D::initial_capacity_element(), D::pad_and_add()),
|
||||
_domain: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -400,7 +407,7 @@ mod tests {
|
|||
|
||||
let (round_constants, mds, _) = OrchardNullifier::constants();
|
||||
|
||||
let hasher = Hash::<_, OrchardNullifier, _, 3, 2>::init(ConstantLength);
|
||||
let hasher = Hash::<_, OrchardNullifier, ConstantLength<2>, 3, 2>::init();
|
||||
let result = hasher.hash(message);
|
||||
|
||||
// The result should be equivalent to just directly applying the permutation and
|
||||
|
|
|
@ -292,7 +292,8 @@ mod tests {
|
|||
Fp::from_repr(tv.input[1]).unwrap(),
|
||||
];
|
||||
|
||||
let result = Hash::<_, super::P128Pow5T3, _, 3, 2>::init(ConstantLength).hash(message);
|
||||
let result =
|
||||
Hash::<_, super::P128Pow5T3, ConstantLength<2>, 3, 2>::init().hash(message);
|
||||
|
||||
assert_eq!(result.to_repr(), tv.output);
|
||||
}
|
||||
|
@ -303,7 +304,8 @@ mod tests {
|
|||
Fq::from_repr(tv.input[1]).unwrap(),
|
||||
];
|
||||
|
||||
let result = Hash::<_, super::P128Pow5T3, _, 3, 2>::init(ConstantLength).hash(message);
|
||||
let result =
|
||||
Hash::<_, super::P128Pow5T3, ConstantLength<2>, 3, 2>::init().hash(message);
|
||||
|
||||
assert_eq!(result.to_repr(), tv.output);
|
||||
}
|
||||
|
|
|
@ -212,7 +212,7 @@ pub(crate) fn diversify_hash(d: &[u8; 11]) -> NonIdentityPallasPoint {
|
|||
///
|
||||
/// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs
|
||||
pub(crate) fn prf_nf(nk: pallas::Base, rho: pallas::Base) -> pallas::Base {
|
||||
poseidon::Hash::<_, poseidon::P128Pow5T3, _, 3, 2>::init(poseidon::ConstantLength)
|
||||
poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
|
||||
.hash([nk, rho])
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue