diff --git a/Cargo.toml b/Cargo.toml index d12d0b32..2b38917e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,10 @@ harness = false name = "circuit" harness = false +[[bench]] +name = "poseidon" +harness = false + [profile.release] debug = true diff --git a/benches/poseidon.rs b/benches/poseidon.rs new file mode 100644 index 00000000..4f40d9a6 --- /dev/null +++ b/benches/poseidon.rs @@ -0,0 +1,258 @@ +use ff::Field; +use halo2::{ + circuit::{Layouter, SimpleFloorPlanner}, + pasta::Fp, + plonk::{ + create_proof, keygen_pk, keygen_vk, verify_proof, Advice, Circuit, Column, + ConstraintSystem, Error, + }, + poly::commitment::Params, + transcript::{Blake2bRead, Blake2bWrite, Challenge255}, +}; +use pasta_curves::{pallas, vesta}; + +use orchard::{ + circuit::gadget::{ + poseidon::{Hash, Pow5Chip, Pow5Config}, + utilities::{CellValue, Var}, + }, + primitives::poseidon::{self, ConstantLength, Spec}, +}; +use std::convert::TryInto; +use std::marker::PhantomData; + +use criterion::{criterion_group, criterion_main, Criterion}; +use rand::rngs::OsRng; + +#[derive(Clone, Copy)] +struct HashCircuit +where + S: Spec + Clone + Copy, +{ + message: Option<[Fp; L]>, + // For the purpose of this test, witness the result. + // TODO: Move this into an instance column. + output: Option, + _spec: PhantomData, +} + +#[derive(Debug, Clone)] +struct MyConfig { + input: [Column; L], + poseidon_config: Pow5Config, +} + +impl Circuit + for HashCircuit +where + S: Spec + Copy + Clone, +{ + type Config = MyConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self { + message: None, + output: None, + _spec: PhantomData, + } + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let state = (0..WIDTH).map(|_| meta.advice_column()).collect::>(); + let partial_sbox = meta.advice_column(); + + let rc_a = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); + let rc_b = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); + + meta.enable_constant(rc_b[0]); + + Self::Config { + input: state[..RATE].try_into().unwrap(), + poseidon_config: Pow5Chip::configure::( + meta, + state.try_into().unwrap(), + partial_sbox, + rc_a.try_into().unwrap(), + rc_b.try_into().unwrap(), + ), + } + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let chip = Pow5Chip::construct(config.poseidon_config.clone()); + + let message = layouter.assign_region( + || "load message", + |mut region| { + let message_word = |i: usize| { + let value = self.message.map(|message_vals| message_vals[i]); + let cell = region.assign_advice( + || format!("load message_{}", i), + config.input[i], + 0, + || value.ok_or(Error::Synthesis), + )?; + Ok(CellValue::new(cell, value)) + }; + + let message: Result, Error> = (0..L).map(message_word).collect(); + Ok(message?.try_into().unwrap()) + }, + )?; + + let hasher = Hash::<_, _, S, _, WIDTH, RATE>::init( + chip, + layouter.namespace(|| "init"), + ConstantLength::, + )?; + let output = hasher.hash(layouter.namespace(|| "hash"), message)?; + + layouter.assign_region( + || "constrain output", + |mut region| { + let expected_var = region.assign_advice( + || "load output", + config.input[0], + 0, + || self.output.ok_or(Error::Synthesis), + )?; + region.constrain_equal(output.cell(), expected_var) + }, + ) + } +} + +#[derive(Debug, Clone, Copy)] +struct MySpec; + +impl Spec for MySpec<3, 2> { + fn full_rounds() -> usize { + 8 + } + + fn partial_rounds() -> usize { + 56 + } + + fn sbox(val: Fp) -> Fp { + val.pow_vartime(&[5]) + } + + fn secure_mds() -> usize { + 0 + } +} + +impl Spec for MySpec<9, 8> { + fn full_rounds() -> usize { + 8 + } + + fn partial_rounds() -> usize { + 56 + } + + fn sbox(val: Fp) -> Fp { + val.pow_vartime(&[5]) + } + + fn secure_mds() -> usize { + 0 + } +} + +impl Spec for MySpec<12, 11> { + fn full_rounds() -> usize { + 8 + } + + fn partial_rounds() -> usize { + 56 + } + + fn sbox(val: Fp) -> Fp { + val.pow_vartime(&[5]) + } + + fn secure_mds() -> usize { + 0 + } +} + +const K: u32 = 6; + +fn bench_poseidon( + name: &str, + c: &mut Criterion, +) where + S: Spec + Copy + Clone, +{ + // Initialize the polynomial commitment parameters + let params: Params = Params::new(K); + + let empty_circuit = HashCircuit:: { + message: None, + output: None, + _spec: PhantomData, + }; + + // Initialize the proving key + let vk = keygen_vk(¶ms, &empty_circuit).expect("keygen_vk should not fail"); + let pk = keygen_pk(¶ms, vk, &empty_circuit).expect("keygen_pk should not fail"); + + let prover_name = name.to_string() + "-prover"; + let verifier_name = name.to_string() + "-verifier"; + + let rng = OsRng; + let message = (0..L) + .map(|_| pallas::Base::random(rng)) + .collect::>() + .try_into() + .unwrap(); + let output = poseidon::Hash::<_, S, _, WIDTH, RATE>::init(ConstantLength::).hash(message); + + let circuit = HashCircuit:: { + message: Some(message), + output: Some(output), + _spec: PhantomData, + }; + + c.bench_function(&prover_name, |b| { + b.iter(|| { + // Create a proof + let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + create_proof(¶ms, &pk, &[circuit], &[&[]], &mut transcript) + .expect("proof generation should not fail") + }) + }); + + // Create a proof + let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + create_proof(¶ms, &pk, &[circuit], &[&[]], &mut transcript) + .expect("proof generation should not fail"); + let proof = transcript.finalize(); + + c.bench_function(&verifier_name, |b| { + b.iter(|| { + let msm = params.empty_msm(); + let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); + let guard = verify_proof(¶ms, pk.get_vk(), msm, &[&[]], &mut transcript).unwrap(); + let msm = guard.clone().use_challenges(); + assert!(msm.eval()); + }); + }); +} + +fn criterion_benchmark(c: &mut Criterion) { + bench_poseidon::, 3, 2, 2>("WIDTH = 3, RATE = 2", c); + bench_poseidon::, 9, 8, 8>("WIDTH = 9, RATE = 8", c); + bench_poseidon::, 12, 11, 11>("WIDTH = 12, RATE = 11", c); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/benches/primitives.rs b/benches/primitives.rs index df6747b5..4d141c69 100644 --- a/benches/primitives.rs +++ b/benches/primitives.rs @@ -21,7 +21,7 @@ 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::init(P128Pow5T3, ConstantLength).hash(message)) + b.iter(|| poseidon::Hash::<_, P128Pow5T3, _, 3, 2>::init(ConstantLength).hash(message)) }); } diff --git a/src/circuit.rs b/src/circuit.rs index f1b741dd..c383b7ef 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -57,7 +57,7 @@ use std::convert::TryInto; use self::gadget::utilities::lookup_range_check::LookupRangeCheckConfig; -pub(crate) mod gadget; +pub mod gadget; /// Size of the Orchard circuit. const K: u32 = 11; @@ -239,9 +239,8 @@ impl plonk::Circuit for Circuit { let ecc_config = EccChip::configure(meta, advices, lagrange_coeffs, range_check.clone()); // Configuration for the Poseidon hash. - let poseidon_config = PoseidonChip::configure( + let poseidon_config = PoseidonChip::configure::( meta, - poseidon::P128Pow5T3, // We place the state columns after the partial_sbox column so that the // pad-and-add region can be layed out more efficiently. advices[6..9].try_into().unwrap(), diff --git a/src/circuit/gadget.rs b/src/circuit/gadget.rs index b0d8b247..4bf51c93 100644 --- a/src/circuit/gadget.rs +++ b/src/circuit/gadget.rs @@ -1,3 +1,5 @@ +//! Gadgets used in the Orchard circuit. + use pasta_curves::pallas; use ecc::chip::EccChip; @@ -5,9 +7,9 @@ use poseidon::Pow5Chip as PoseidonChip; use sinsemilla::{chip::SinsemillaChip, merkle::chip::MerkleChip}; pub(crate) mod ecc; -pub(crate) mod poseidon; +pub mod poseidon; pub(crate) mod sinsemilla; -pub(crate) mod utilities; +pub mod utilities; impl super::Config { pub(super) fn ecc_chip(&self) -> EccChip { diff --git a/src/circuit/gadget/poseidon.rs b/src/circuit/gadget/poseidon.rs index 8a891ccf..96e5917d 100644 --- a/src/circuit/gadget/poseidon.rs +++ b/src/circuit/gadget/poseidon.rs @@ -80,11 +80,13 @@ impl< const RATE: usize, > Word { - pub(crate) fn inner(&self) -> PoseidonChip::Word { + /// The word contained in this gadget. + pub fn inner(&self) -> PoseidonChip::Word { self.inner } - pub(crate) fn from_inner(inner: PoseidonChip::Word) -> Self { + /// Construct a [`Word`] gadget from the inner word. + pub fn from_inner(inner: PoseidonChip::Word) -> Self { Self { inner } } } @@ -109,6 +111,7 @@ fn poseidon_duplex< } /// A Poseidon duplex sponge. +#[derive(Debug)] pub struct Duplex< F: FieldExt, PoseidonChip: PoseidonDuplexInstructions, @@ -210,6 +213,7 @@ impl< } /// A Poseidon hash function, built around a duplex sponge. +#[derive(Debug)] pub struct Hash< F: FieldExt, PoseidonChip: PoseidonDuplexInstructions, diff --git a/src/circuit/gadget/poseidon/pow5.rs b/src/circuit/gadget/poseidon/pow5.rs index d84fb392..f6829791 100644 --- a/src/circuit/gadget/poseidon/pow5.rs +++ b/src/circuit/gadget/poseidon/pow5.rs @@ -52,7 +52,6 @@ impl Pow5Chip>( meta: &mut ConstraintSystem, - spec: S, state: [Column; WIDTH], partial_sbox: Column, rc_a: [Column; WIDTH], @@ -65,7 +64,7 @@ impl Pow5Chip Pow5Chip) -> Self { Pow5Chip { config } } @@ -402,18 +402,13 @@ impl, const WIDTH: usize, const RATE: usize } } +/// A word in the Poseidon state. #[derive(Clone, Copy, Debug)] pub struct StateWord { var: Cell, value: Option, } -impl StateWord { - pub fn new(var: Cell, value: Option) -> Self { - Self { var, value } - } -} - impl From> for CellValue { fn from(state_word: StateWord) -> CellValue { CellValue::new(state_word.var, state_word.value) @@ -426,6 +421,20 @@ impl From> for StateWord { } } +impl Var for StateWord { + fn new(var: Cell, value: Option) -> Self { + Self { var, value } + } + + fn cell(&self) -> Cell { + self.var + } + + fn value(&self) -> Option { + self.value + } +} + #[derive(Debug)] struct Pow5State([StateWord; WIDTH]); @@ -616,40 +625,37 @@ mod tests { }, primitives::poseidon::{self, ConstantLength, P128Pow5T3 as OrchardNullifier, Spec}, }; + use std::convert::TryInto; + use std::marker::PhantomData; - const WIDTH: usize = 3; - const RATE: usize = 2; + struct PermuteCircuit, const WIDTH: usize, const RATE: usize>( + PhantomData, + ); - struct PermuteCircuit {} - - impl Circuit for PermuteCircuit { + impl, const WIDTH: usize, const RATE: usize> Circuit + for PermuteCircuit + { type Config = Pow5Config; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { - PermuteCircuit {} + PermuteCircuit::(PhantomData) } fn configure(meta: &mut ConstraintSystem) -> Pow5Config { - let state = [ - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - ]; + let state = (0..WIDTH).map(|_| meta.advice_column()).collect::>(); let partial_sbox = meta.advice_column(); - let rc_a = [ - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - ]; - let rc_b = [ - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - ]; + let rc_a = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); + let rc_b = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); - Pow5Chip::configure(meta, OrchardNullifier, state, partial_sbox, rc_a, rc_b) + Pow5Chip::configure::( + meta, + state.try_into().unwrap(), + partial_sbox, + rc_a.try_into().unwrap(), + rc_b.try_into().unwrap(), + ) } fn synthesize( @@ -660,7 +666,7 @@ mod tests { let initial_state = layouter.assign_region( || "prepare initial state", |mut region| { - let mut state_word = |i: usize| -> Result<_, Error> { + let state_word = |i: usize| { let value = Some(Fp::from(i as u64)); let var = region.assign_advice( || format!("load state_{}", i), @@ -671,22 +677,27 @@ mod tests { Ok(StateWord { var, value }) }; - Ok([state_word(0)?, state_word(1)?, state_word(2)?]) + let state: Result, Error> = (0..WIDTH).map(state_word).collect(); + Ok(state?.try_into().unwrap()) }, )?; let chip = Pow5Chip::construct(config.clone()); let final_state = as PoseidonInstructions< Fp, - OrchardNullifier, + S, WIDTH, - 2, + RATE, >>::permute(&chip, &mut layouter, &initial_state)?; // 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 (round_constants, mds, _) = OrchardNullifier.constants(); - poseidon::permute::<_, OrchardNullifier, WIDTH, RATE>( + let mut expected_final_state = (0..WIDTH) + .map(|idx| Fp::from_u64(idx as u64)) + .collect::>() + .try_into() + .unwrap(); + let (round_constants, mds, _) = S::constants(); + poseidon::permute::<_, S, WIDTH, RATE>( &mut expected_final_state, &mds, &round_constants, @@ -705,9 +716,11 @@ mod tests { region.constrain_equal(final_state[i].var, var) }; - final_state_word(0)?; - final_state_word(1)?; - final_state_word(2) + for i in 0..(WIDTH) { + final_state_word(i)?; + } + + Ok(()) }, ) } @@ -716,49 +729,54 @@ mod tests { #[test] fn poseidon_permute() { let k = 6; - let circuit = PermuteCircuit {}; + let circuit = PermuteCircuit::(PhantomData); let prover = MockProver::run(k, &circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())) } - #[derive(Default)] - struct HashCircuit { - message: Option<[Fp; 2]>, + struct HashCircuit< + S: Spec, + const WIDTH: usize, + const RATE: usize, + const L: usize, + > { + message: Option<[Fp; L]>, // For the purpose of this test, witness the result. // TODO: Move this into an instance column. output: Option, + _spec: PhantomData, } - impl Circuit for HashCircuit { + impl, const WIDTH: usize, const RATE: usize, const L: usize> + Circuit for HashCircuit + { type Config = Pow5Config; type FloorPlanner = SimpleFloorPlanner; fn without_witnesses(&self) -> Self { - Self::default() + Self { + message: None, + output: None, + _spec: PhantomData, + } } fn configure(meta: &mut ConstraintSystem) -> Pow5Config { - let state = [ - meta.advice_column(), - meta.advice_column(), - meta.advice_column(), - ]; + let state = (0..WIDTH).map(|_| meta.advice_column()).collect::>(); let partial_sbox = meta.advice_column(); - let rc_a = [ - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - ]; - let rc_b = [ - meta.fixed_column(), - meta.fixed_column(), - meta.fixed_column(), - ]; + let rc_a = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); + let rc_b = (0..WIDTH).map(|_| meta.fixed_column()).collect::>(); meta.enable_constant(rc_b[0]); - Pow5Chip::configure(meta, OrchardNullifier, state, partial_sbox, rc_a, rc_b) + Pow5Chip::configure::( + meta, + state.try_into().unwrap(), + partial_sbox, + rc_a.try_into().unwrap(), + rc_b.try_into().unwrap(), + ) } fn synthesize( @@ -771,7 +789,7 @@ mod tests { let message = layouter.assign_region( || "load message", |mut region| { - let mut message_word = |i: usize| -> Result<_, Error> { + let message_word = |i: usize| { let value = self.message.map(|message_vals| message_vals[i]); let cell = region.assign_advice( || format!("load message_{}", i), @@ -782,14 +800,15 @@ mod tests { Ok(CellValue::new(cell, value)) }; - Ok([message_word(0)?, message_word(1)?]) + let message: Result, Error> = (0..L).map(message_word).collect(); + Ok(message?.try_into().unwrap()) }, )?; - let hasher = Hash::<_, _, OrchardNullifier, _, WIDTH, 2>::init( + let hasher = Hash::<_, _, S, _, WIDTH, RATE>::init( chip, layouter.namespace(|| "init"), - ConstantLength::<2>, + ConstantLength::, )?; let output = hasher.hash(layouter.namespace(|| "hash"), message)?; @@ -811,12 +830,14 @@ mod tests { #[test] fn poseidon_hash() { let message = [Fp::rand(), Fp::rand()]; - let output = poseidon::Hash::init(OrchardNullifier, ConstantLength::<2>).hash(message); + let output = + poseidon::Hash::<_, OrchardNullifier, _, 3, 2>::init(ConstantLength::<2>).hash(message); let k = 6; - let circuit = HashCircuit { + let circuit = HashCircuit:: { message: Some(message), output: Some(output), + _spec: PhantomData, }; let prover = MockProver::run(k, &circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())) @@ -829,12 +850,14 @@ mod tests { pallas::Base::from_repr(tv.input[0]).unwrap(), pallas::Base::from_repr(tv.input[1]).unwrap(), ]; - let output = poseidon::Hash::init(OrchardNullifier, ConstantLength).hash(message); + let output = + poseidon::Hash::<_, OrchardNullifier, _, 3, 2>::init(ConstantLength).hash(message); let k = 6; - let circuit = HashCircuit { + let circuit = HashCircuit:: { message: Some(message), output: Some(output), + _spec: PhantomData, }; let prover = MockProver::run(k, &circuit, vec![]).unwrap(); assert_eq!(prover.verify(), Ok(())); @@ -852,9 +875,10 @@ mod tests { .titled("Poseidon Chip Layout", ("sans-serif", 60)) .unwrap(); - let circuit = HashCircuit { + let circuit = HashCircuit:: { message: None, output: None, + _spec: PhantomData, }; halo2::dev::CircuitLayout::default() .render(6, &circuit, &root) diff --git a/src/circuit/gadget/utilities.rs b/src/circuit/gadget/utilities.rs index c4dca83d..53661b28 100644 --- a/src/circuit/gadget/utilities.rs +++ b/src/circuit/gadget/utilities.rs @@ -1,3 +1,5 @@ +//! Utility gadgets. + use ff::PrimeFieldBits; use halo2::{ circuit::{Cell, Layouter, Region}, @@ -17,9 +19,15 @@ pub struct CellValue { value: Option, } +/// Trait for a variable in the circuit. pub trait Var: Copy + Clone + std::fmt::Debug { + /// Construct a new variable. fn new(cell: Cell, value: Option) -> Self; + + /// The cell at which this variable was allocated. fn cell(&self) -> Cell; + + /// The value allocated to this variable. fn value(&self) -> Option; } @@ -37,9 +45,12 @@ impl Var for CellValue { } } +/// Trait for utilities used across circuits. pub trait UtilitiesInstructions { + /// Variable in the circuit. type Var: Var; + /// Load a variable. fn load_private( &self, mut layouter: impl Layouter, @@ -88,7 +99,7 @@ where Ok(CellValue::new(cell, copy.value)) } -pub fn transpose_option_array( +pub(crate) fn transpose_option_array( option_array: Option<[T; LEN]>, ) -> [Option; LEN] { let mut ret = [None; LEN]; diff --git a/src/primitives/poseidon.rs b/src/primitives/poseidon.rs index f2892d1b..8078fac2 100644 --- a/src/primitives/poseidon.rs +++ b/src/primitives/poseidon.rs @@ -30,7 +30,7 @@ pub(crate) type SpongeState = [Option; RATE]; pub(crate) type Mds = [[F; T]; T]; /// A specification for a Poseidon permutation. -pub trait Spec { +pub trait Spec: fmt::Debug { /// The number of full rounds for this specification. /// /// This must be an even number. @@ -47,10 +47,10 @@ pub trait Spec { /// /// This is used by the default implementation of [`Spec::constants`]. If you are /// hard-coding the constants, you may leave this unimplemented. - fn secure_mds(&self) -> usize; + fn secure_mds() -> usize; /// Generates `(round_constants, mds, mds^-1)` corresponding to this specification. - fn constants(&self) -> (Vec<[F; T]>, Mds, Mds) { + fn constants() -> (Vec<[F; T]>, Mds, Mds) { let r_f = Self::full_rounds(); let r_p = Self::partial_rounds(); @@ -69,7 +69,7 @@ pub trait Spec { }) .collect(); - let (mds, mds_inv) = mds::generate_mds::(&mut grain, self.secure_mds()); + let (mds, mds_inv) = mds::generate_mds::(&mut grain, Self::secure_mds()); (round_constants, mds, mds_inv) } @@ -141,6 +141,7 @@ fn poseidon_duplex, const T: usize, const RATE: output } +#[derive(Debug)] pub(crate) enum Sponge { Absorbing(SpongeState), Squeezing(SpongeState), @@ -167,11 +168,10 @@ pub(crate) struct Duplex, const T: usize, const impl, const T: usize, const RATE: usize> Duplex { /// Constructs a new duplex sponge for the given Poseidon specification. pub(crate) fn new( - spec: S, initial_capacity_element: F, pad_and_add: Box, &SpongeState)>, ) -> Self { - let (round_constants, mds_matrix, _) = spec.constants(); + let (round_constants, mds_matrix, _) = S::constants(); let input = [None; RATE]; let mut state = [F::zero(); T]; @@ -336,13 +336,9 @@ impl< > Hash { /// Initializes a new hasher. - pub fn init(spec: S, domain: D) -> Self { + pub fn init(domain: D) -> Self { Hash { - duplex: Duplex::new( - spec, - domain.initial_capacity_element(), - domain.pad_and_add(), - ), + duplex: Duplex::new(domain.initial_capacity_element(), domain.pad_and_add()), domain, } } @@ -371,9 +367,9 @@ mod tests { fn orchard_spec_equivalence() { let message = [pallas::Base::from_u64(6), pallas::Base::from_u64(42)]; - let (round_constants, mds, _) = OrchardNullifier.constants(); + let (round_constants, mds, _) = OrchardNullifier::constants(); - let hasher = Hash::init(OrchardNullifier, ConstantLength); + let hasher = Hash::<_, OrchardNullifier, _, 3, 2>::init(ConstantLength); let result = hasher.hash(message); // The result should be equivalent to just directly applying the permutation and diff --git a/src/primitives/poseidon/p128pow5t3.rs b/src/primitives/poseidon/p128pow5t3.rs index 31c7217c..c33a9ca6 100644 --- a/src/primitives/poseidon/p128pow5t3.rs +++ b/src/primitives/poseidon/p128pow5t3.rs @@ -25,11 +25,11 @@ impl Spec for P128Pow5T3 { val.pow_vartime(&[5]) } - fn secure_mds(&self) -> usize { + fn secure_mds() -> usize { unimplemented!() } - fn constants(&self) -> (Vec<[Fp; 3]>, Mds, Mds) { + fn constants() -> (Vec<[Fp; 3]>, Mds, Mds) { ( super::fp::ROUND_CONSTANTS[..].to_vec(), super::fp::MDS, @@ -51,11 +51,11 @@ impl Spec for P128Pow5T3 { val.pow_vartime(&[5]) } - fn secure_mds(&self) -> usize { + fn secure_mds() -> usize { unimplemented!() } - fn constants(&self) -> (Vec<[Fq; 3]>, Mds, Mds) { + fn constants() -> (Vec<[Fq; 3]>, Mds, Mds) { ( super::fq::ROUND_CONSTANTS[..].to_vec(), super::fq::MDS, @@ -80,21 +80,15 @@ mod tests { /// The same Poseidon specification as poseidon::P128Pow5T3, but constructed /// such that its constants will be generated at runtime. #[derive(Debug)] - pub struct P128Pow5T3Gen { - secure_mds: usize, - _field: PhantomData, - } + pub struct P128Pow5T3Gen(PhantomData); - impl P128Pow5T3Gen { - pub fn new(secure_mds: usize) -> Self { - P128Pow5T3Gen { - secure_mds, - _field: PhantomData::default(), - } + impl P128Pow5T3Gen { + pub fn new() -> Self { + P128Pow5T3Gen(PhantomData::default()) } } - impl Spec for P128Pow5T3Gen { + impl Spec for P128Pow5T3Gen { fn full_rounds() -> usize { 8 } @@ -107,8 +101,8 @@ mod tests { val.pow_vartime(&[5]) } - fn secure_mds(&self) -> usize { - self.secure_mds + fn secure_mds() -> usize { + SECURE_MDS } } @@ -119,8 +113,7 @@ mod tests { expected_mds: [[F; 3]; 3], expected_mds_inv: [[F; 3]; 3], ) { - let poseidon = P128Pow5T3Gen::::new(0); - let (round_constants, mds, mds_inv) = poseidon.constants(); + let (round_constants, mds, mds_inv) = P128Pow5T3Gen::::constants(); for (actual, expected) in round_constants .iter() @@ -196,7 +189,7 @@ mod tests { ]), ]; - permute::, 3, 2>(&mut input, &fp::MDS, &fp::ROUND_CONSTANTS); + permute::, 3, 2>(&mut input, &fp::MDS, &fp::ROUND_CONSTANTS); assert_eq!(input, expected_output); } @@ -247,7 +240,7 @@ mod tests { ]), ]; - permute::, 3, 2>(&mut input, &fq::MDS, &fq::ROUND_CONSTANTS); + permute::, 3, 2>(&mut input, &fq::MDS, &fq::ROUND_CONSTANTS); assert_eq!(input, expected_output); } } @@ -255,7 +248,7 @@ mod tests { #[test] fn permute_test_vectors() { { - let (round_constants, mds, _) = super::P128Pow5T3.constants(); + let (round_constants, mds, _) = super::P128Pow5T3::constants(); for tv in crate::primitives::poseidon::test_vectors::fp::permute() { let mut state = [ @@ -273,7 +266,7 @@ mod tests { } { - let (round_constants, mds, _) = super::P128Pow5T3.constants(); + let (round_constants, mds, _) = super::P128Pow5T3::constants(); for tv in crate::primitives::poseidon::test_vectors::fq::permute() { let mut state = [ @@ -299,7 +292,7 @@ mod tests { Fp::from_repr(tv.input[1]).unwrap(), ]; - let result = Hash::init(super::P128Pow5T3, ConstantLength).hash(message); + let result = Hash::<_, super::P128Pow5T3, _, 3, 2>::init(ConstantLength).hash(message); assert_eq!(result.to_repr(), tv.output); } @@ -310,7 +303,7 @@ mod tests { Fq::from_repr(tv.input[1]).unwrap(), ]; - let result = Hash::init(super::P128Pow5T3, ConstantLength).hash(message); + let result = Hash::<_, super::P128Pow5T3, _, 3, 2>::init(ConstantLength).hash(message); assert_eq!(result.to_repr(), tv.output); } diff --git a/src/spec.rs b/src/spec.rs index ddd394c6..7d1cd698 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -212,7 +212,8 @@ 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::init(poseidon::P128Pow5T3, poseidon::ConstantLength).hash([nk, rho]) + poseidon::Hash::<_, poseidon::P128Pow5T3, _, 3, 2>::init(poseidon::ConstantLength) + .hash([nk, rho]) } /// Defined in [Zcash Protocol Spec ยง 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement].