diff --git a/poseidon-parameters/src/arc_matrix.rs b/poseidon-parameters/src/arc_matrix.rs index ed659de..15dd152 100644 --- a/poseidon-parameters/src/arc_matrix.rs +++ b/poseidon-parameters/src/arc_matrix.rs @@ -16,6 +16,10 @@ impl pub fn transpose(&self) -> ArcMatrix { ArcMatrix(self.0.transpose()) } + + pub fn inner_elements(&self) -> [Fq; N_ELEMENTS] { + self.0.elements + } } impl MatrixOperations diff --git a/poseidon-parameters/src/matrix.rs b/poseidon-parameters/src/matrix.rs index 2664fd5..17977c4 100644 --- a/poseidon-parameters/src/matrix.rs +++ b/poseidon-parameters/src/matrix.rs @@ -88,7 +88,6 @@ impl MatrixOp } } -#[allow(dead_code)] /// Multiply two `Matrix` pub fn mat_mul< const LHS_N_ROWS: usize, diff --git a/poseidon-parameters/src/v1.rs b/poseidon-parameters/src/v1.rs index fd99ac8..907ec03 100644 --- a/poseidon-parameters/src/v1.rs +++ b/poseidon-parameters/src/v1.rs @@ -1,10 +1,7 @@ pub use crate::alpha::Alpha; pub use crate::round_numbers::RoundNumbers; -pub use crate::{matrix::Matrix, matrix::SquareMatrix}; - -// pub use crate::matrix_ops::mat_mul; - +pub use crate::matrix::{mat_mul, square_mat_mul, Matrix, SquareMatrix}; pub use crate::{ arc_matrix::ArcMatrix, arc_matrix::OptimizedArcMatrix, matrix_ops::MatrixOperations, matrix_ops::SquareMatrixOperations, mds_matrix::MdsMatrix, mds_matrix::OptimizedMdsMatrices, @@ -48,7 +45,13 @@ pub struct PoseidonParameters< /// Optimized round constants. pub optimized_arc: OptimizedArcMatrix, - // TODO: - // /// Optimized MDS matrices. - //pub optimized_mds: OptimizedMdsMatrices, + + /// Optimized MDS matrices. + pub optimized_mds: OptimizedMdsMatrices< + NUM_ROUND_ROWS, + STATE_SIZE, + STATE_SIZE_MINUS_1, + NUM_MDS_ELEMENTS, + NUM_STATE_SIZE_MINUS_1_ELEMENTS, + >, } diff --git a/poseidon-paramgen/Cargo.toml b/poseidon-paramgen/Cargo.toml index d0458c8..18e7ba8 100644 --- a/poseidon-paramgen/Cargo.toml +++ b/poseidon-paramgen/Cargo.toml @@ -3,7 +3,7 @@ name = "poseidon-paramgen" version = "0.4.0" edition = "2018" authors = ["Penumbra ", "redshiftzero "] -description = "A crate for generating Poseidon parameters" +description = "A crate for generating Poseidon parameters for decaf377" license = "MIT OR Apache-2.0" repository = "https://github.com/penumbra-zone/poseidon377" @@ -20,9 +20,7 @@ rand_core = { version = "0.6.3", default-features = false, features = ["getrando poseidon-parameters = { path = "../poseidon-parameters", default-features = false, version = "1.0" } [dev-dependencies] -ark-bn254 = "0.4" -ark-ed-on-bls12-377 = "0.4" -ark-ed-on-bls12-381 = "0.4" +decaf377 = "0.8" [features] default = ["std"] diff --git a/poseidon-paramgen/src/v1.rs b/poseidon-paramgen/src/v1.rs index e5d3ab2..b4d62ce 100644 --- a/poseidon-paramgen/src/v1.rs +++ b/poseidon-paramgen/src/v1.rs @@ -30,7 +30,6 @@ pub fn generate( PoseidonParameters:: { M: input.M, - t: input.t, alpha, rounds, mds, diff --git a/poseidon-paramgen/src/v2.rs b/poseidon-paramgen/src/v2.rs index 9b76109..15dfbd4 100644 --- a/poseidon-paramgen/src/v2.rs +++ b/poseidon-paramgen/src/v2.rs @@ -33,7 +33,6 @@ pub fn generate( if t < 4 { PoseidonParameters:: { M: input.M, - t: input.t, alpha, rounds, arc, @@ -44,7 +43,6 @@ pub fn generate( let m_e = external::generate(t); PoseidonParameters:: { M: input.M, - t: input.t, alpha, rounds, arc, diff --git a/poseidon-paramgen/src/v2/external.rs b/poseidon-paramgen/src/v2/external.rs index c301221..ec1fec5 100644 --- a/poseidon-paramgen/src/v2/external.rs +++ b/poseidon-paramgen/src/v2/external.rs @@ -71,7 +71,7 @@ pub fn generate(t: usize) -> SquareMatrix { #[cfg(test)] mod tests { - use ark_ed_on_bls12_377::Fq; + use decaf377::Fq; use super::*; diff --git a/poseidon-permutation/Cargo.toml b/poseidon-permutation/Cargo.toml index 76a0089..32974b7 100644 --- a/poseidon-permutation/Cargo.toml +++ b/poseidon-permutation/Cargo.toml @@ -3,13 +3,14 @@ name = "poseidon-permutation" version = "1.0.0" edition = "2018" authors = ["Penumbra ", "redshiftzero "] -description = "An instantiation of the Poseidon permutation" +description = "An instantiation of the Poseidon permutation for decaf377" license = "MIT OR Apache-2.0" repository = "https://github.com/penumbra-zone/poseidon377" [dependencies] -ark-ff = { version = "0.4", default-features = false } -ark-std = { version = "^0.4.0", default-features = false } +decaf377 = { version="0.8", default-features = false } +ark-ff = { version = "0.4", default-features = false, optional=true } +ark-std = { version = "^0.4.0", default-features = false, optional=true } ark-r1cs-std = {version = "0.4", default-features = false, optional=true } ark-relations = { version="0.4", default-features = false, optional=true } @@ -17,7 +18,7 @@ poseidon-parameters = { path = "../poseidon-parameters", default-features = fals [features] default = ["std"] -r1cs = ["ark-r1cs-std", "ark-relations"] +r1cs = ["std", "ark-ff/std", "ark-r1cs-std", "ark-relations", "decaf377/r1cs"] std = [ "ark-ff/std", "ark-std/std", diff --git a/poseidon-permutation/src/permutation.rs b/poseidon-permutation/src/permutation.rs index fad249f..c44e055 100644 --- a/poseidon-permutation/src/permutation.rs +++ b/poseidon-permutation/src/permutation.rs @@ -1,40 +1,83 @@ #![allow(non_snake_case)] -use ark_ff::PrimeField; -use ark_std::{vec, vec::Vec}; +use decaf377::Fq; use poseidon_parameters::v1::{Alpha, MatrixOperations, PoseidonParameters}; +//temp +use poseidon_parameters::StuffThatNeedsToGoInDecaf377; + /// Represents a generic instance of `Poseidon`. /// /// Intended for generic fixed-width hashing. -pub struct Instance<'a, F: PrimeField> { +pub struct Instance< + 'a, + const STATE_SIZE: usize, + const STATE_SIZE_MINUS_1: usize, + const NUM_MDS_ELEMENTS: usize, + const NUM_STATE_SIZE_MINUS_1_ELEMENTS: usize, + const NUM_ROUND_ROWS: usize, + const NUM_ROUND_COLS: usize, + const NUM_ROUND_ELEMENTS: usize, +> { /// Parameters for this instance of Poseidon. - parameters: &'a PoseidonParameters, + parameters: &'a PoseidonParameters< + STATE_SIZE, + STATE_SIZE_MINUS_1, + NUM_MDS_ELEMENTS, + NUM_STATE_SIZE_MINUS_1_ELEMENTS, + NUM_ROUND_ROWS, + NUM_ROUND_COLS, + NUM_ROUND_ELEMENTS, + >, /// Inner state. - state_words: Vec, + state_words: [Fq; STATE_SIZE], } -impl<'a, F: PrimeField> Instance<'a, F> { - /// Instantiate a new hash function over GF(p) given `Parameters`. - pub fn new(parameters: &'a PoseidonParameters) -> Self { - let t = parameters.t; +impl< + 'a, + const STATE_SIZE: usize, + const STATE_SIZE_MINUS_1: usize, + const NUM_MDS_ELEMENTS: usize, + const NUM_STATE_SIZE_MINUS_1_ELEMENTS: usize, + const NUM_ROUND_ROWS: usize, + const NUM_ROUND_COLS: usize, + const NUM_ROUND_ELEMENTS: usize, + > + Instance< + 'a, + STATE_SIZE, + STATE_SIZE_MINUS_1, + NUM_MDS_ELEMENTS, + NUM_STATE_SIZE_MINUS_1_ELEMENTS, + NUM_ROUND_ROWS, + NUM_ROUND_COLS, + NUM_ROUND_ELEMENTS, + > +{ + /// Instantiate a new hash function over Fq given `Parameters`. + pub fn new( + parameters: &'a PoseidonParameters< + STATE_SIZE, + STATE_SIZE_MINUS_1, + NUM_MDS_ELEMENTS, + NUM_STATE_SIZE_MINUS_1_ELEMENTS, + NUM_ROUND_ROWS, + NUM_ROUND_COLS, + NUM_ROUND_ELEMENTS, + >, + ) -> Self { Self { parameters, - state_words: vec![F::zero(); t], + state_words: [Fq::zero(); STATE_SIZE], } } /// Fixed width hash from n:1. Outputs a F given `t` input words. - pub fn n_to_1_fixed_hash(&mut self, input_words: Vec) -> F { - // Check input words are `t` elements long - if input_words.len() != self.parameters.t { - panic!("err: input words must be t elements long") - } - + pub fn n_to_1_fixed_hash(&mut self, input_words: &[Fq; STATE_SIZE]) -> Fq { // Set internal state words. - for (i, input_word) in input_words.into_iter().enumerate() { - self.state_words[i] = input_word + for (i, input_word) in input_words.iter().enumerate() { + self.state_words[i] = *input_word } // Apply Poseidon permutation. @@ -45,8 +88,8 @@ impl<'a, F: PrimeField> Instance<'a, F> { } /// Print out internal state. - pub fn output_words(&self) -> Vec { - self.state_words.clone() + pub fn output_words(&self) -> [Fq; STATE_SIZE] { + self.state_words } /// Permutes the internal state. @@ -59,7 +102,7 @@ impl<'a, F: PrimeField> Instance<'a, F> { // First chunk of full rounds for r in 0..R_f { // Apply `AddRoundConstants` layer - for i in 0..self.parameters.t { + for i in 0..STATE_SIZE { self.state_words[i] += self.parameters.optimized_arc.0.get_element(r, i); } self.full_sub_words(); @@ -69,7 +112,7 @@ impl<'a, F: PrimeField> Instance<'a, F> { // Partial rounds // First part of `AddRoundConstants` layer - for i in 0..self.parameters.t { + for i in 0..STATE_SIZE { self.state_words[i] += self .parameters .optimized_arc @@ -99,7 +142,7 @@ impl<'a, F: PrimeField> Instance<'a, F> { // Final full rounds for _ in 0..R_f { // Apply `AddRoundConstants` layer - for i in 0..self.parameters.t { + for i in 0..STATE_SIZE { self.state_words[i] += self .parameters .optimized_arc @@ -113,15 +156,10 @@ impl<'a, F: PrimeField> Instance<'a, F> { } /// Fixed width hash from n:1. Outputs a F given `t` input words. Unoptimized. - pub fn unoptimized_n_to_1_fixed_hash(&mut self, input_words: Vec) -> F { - // Check input words are `t` elements long - if input_words.len() != self.parameters.t { - panic!("err: input words must be t elements long") - } - + pub fn unoptimized_n_to_1_fixed_hash(&mut self, input_words: [Fq; STATE_SIZE]) -> Fq { // Set internal state words. - for (i, input_word) in input_words.into_iter().enumerate() { - self.state_words[i] = input_word + for (i, input_word) in input_words.iter().enumerate() { + self.state_words[i] = *input_word } // Apply Poseidon permutation. @@ -139,13 +177,12 @@ impl<'a, F: PrimeField> Instance<'a, F> { let R_f = self.parameters.rounds.full() / 2; let R_P = self.parameters.rounds.partial(); let mut round_constants_counter = 0; - let t = self.parameters.t; - let round_constants = self.parameters.arc.elements().clone(); + let round_constants = self.parameters.arc.elements(); // First full rounds for _ in 0..R_f { // Apply `AddRoundConstants` layer - for i in 0..t { + for i in 0..STATE_SIZE { self.state_words[i] += round_constants[round_constants_counter]; round_constants_counter += 1; } @@ -156,7 +193,7 @@ impl<'a, F: PrimeField> Instance<'a, F> { // Partial rounds for _ in 0..R_P { // Apply `AddRoundConstants` layer - for i in 0..t { + for i in 0..STATE_SIZE { self.state_words[i] += round_constants[round_constants_counter]; round_constants_counter += 1; } @@ -167,7 +204,7 @@ impl<'a, F: PrimeField> Instance<'a, F> { // Final full rounds for _ in 0..R_f { // Apply `AddRoundConstants` layer - for i in 0..t { + for i in 0..STATE_SIZE { self.state_words[i] += round_constants[round_constants_counter]; round_constants_counter += 1; } @@ -180,7 +217,7 @@ impl<'a, F: PrimeField> Instance<'a, F> { fn partial_sub_words(&mut self) { match self.parameters.alpha { Alpha::Exponent(exp) => self.state_words[0] = (self.state_words[0]).pow([exp as u64]), - Alpha::Inverse => self.state_words[0] = F::one() / self.state_words[0], + Alpha::Inverse => self.state_words[0] = Fq::one() / self.state_words[0], } } @@ -188,61 +225,59 @@ impl<'a, F: PrimeField> Instance<'a, F> { fn full_sub_words(&mut self) { match self.parameters.alpha { Alpha::Exponent(exp) => { - self.state_words = self - .state_words - .iter() - .map(|x| x.pow([exp as u64])) - .collect() + for i in 0..STATE_SIZE { + self.state_words[i] = self.state_words[i].pow([exp as u64]); + } } Alpha::Inverse => { - self.state_words = self.state_words.iter().map(|x| F::one() / x).collect() + for i in 0..STATE_SIZE { + self.state_words[i] = Fq::one() / self.state_words[i]; + } } } } /// Applies the `MixLayer` using the M_i matrix. fn mix_layer_mi(&mut self) { - self.state_words = self - .parameters - .optimized_mds - .M_i - .iter_rows() - .map(|row| { - row.iter() - .zip(&self.state_words) - .map(|(x, y)| *x * *y) - .sum() - }) - .collect(); + let mut new_state_words = [Fq::zero(); STATE_SIZE]; + for (i, row) in self.parameters.optimized_mds.M_i.iter_rows().enumerate() { + let sum = row + .iter() + .zip(&self.state_words) + .map(|(x, y)| *x * *y) + .sum(); + new_state_words[i] = sum; + } + self.state_words = new_state_words; } /// Applies the `MixLayer` using the MDS matrix. fn mix_layer_mds(&mut self) { - self.state_words = self - .parameters - .mds - .0 - .0 - .iter_rows() - .map(|row| { - row.iter() - .zip(&self.state_words) - .map(|(x, y)| *x * *y) - .sum() - }) - .collect(); + let mut new_state_words = [Fq::zero(); STATE_SIZE]; + + for (i, row) in self.parameters.mds.0 .0.iter_rows().enumerate() { + let sum = row + .iter() + .zip(&self.state_words) + .map(|(x, y)| *x * *y) + .sum(); + new_state_words[i] = sum; + } + self.state_words = new_state_words; } /// This is `cheap_matrix_mul` in the Sage spec fn sparse_mat_mul(&mut self, round_number: usize) { // mul_row = [(state_words[0] * v[i]) for i in range(0, t-1)] // add_row = [(mul_row[i] + state_words[i+1]) for i in range(0, t-1)] - let add_row: Vec = self.parameters.optimized_mds.v_collection[round_number] + let mut add_row = [Fq::zero(); STATE_SIZE_MINUS_1]; + for (i, x) in self.parameters.optimized_mds.v_collection[round_number] .elements .iter() .enumerate() - .map(|(i, x)| *x * self.state_words[0] + self.state_words[i + 1]) - .collect(); + { + add_row[i] = *x * self.state_words[0] + self.state_words[i + 1]; + } // column_1 = [M_0_0] + w_hat // state_words_new[0] = sum([column_1[i] * state_words[i] for i in range(0, t)]) @@ -251,10 +286,10 @@ impl<'a, F: PrimeField> Instance<'a, F> { + self.parameters.optimized_mds.w_hat_collection[round_number] .elements .iter() - .zip(self.state_words[1..self.parameters.t].iter()) + .zip(self.state_words[1..STATE_SIZE].iter()) .map(|(x, y)| *x * *y) - .sum::(); + .sum::(); - self.state_words[1..self.parameters.t].copy_from_slice(&add_row[..(self.parameters.t - 1)]); + self.state_words[1..STATE_SIZE].copy_from_slice(&add_row[..(STATE_SIZE - 1)]); } } diff --git a/poseidon-permutation/src/r1cs.rs b/poseidon-permutation/src/r1cs.rs index d09e3aa..97873be 100644 --- a/poseidon-permutation/src/r1cs.rs +++ b/poseidon-permutation/src/r1cs.rs @@ -1,44 +1,78 @@ #![allow(non_snake_case)] -use ark_ff::PrimeField; use ark_std::vec::Vec; use ark_r1cs_std::{fields::fp::FpVar, prelude::*}; use ark_relations::r1cs::ConstraintSystemRef; +use decaf377::Fq; use poseidon_parameters::v1::{Alpha, MatrixOperations, PoseidonParameters}; /// Represents a Poseidon permutation instance. -pub struct InstanceVar { +pub struct InstanceVar< + const STATE_SIZE: usize, + const STATE_SIZE_MINUS_1: usize, + const NUM_MDS_ELEMENTS: usize, + const NUM_STATE_SIZE_MINUS_1_ELEMENTS: usize, + const NUM_ROUND_ROWS: usize, + const NUM_ROUND_COLS: usize, + const NUM_ROUND_ELEMENTS: usize, +> { /// Parameters for this instance of Poseidon. - pub parameters: PoseidonParameters, + pub parameters: PoseidonParameters< + STATE_SIZE, + STATE_SIZE_MINUS_1, + NUM_MDS_ELEMENTS, + NUM_STATE_SIZE_MINUS_1_ELEMENTS, + NUM_ROUND_ROWS, + NUM_ROUND_COLS, + NUM_ROUND_ELEMENTS, + >, /// Constraint system - pub cs: ConstraintSystemRef, + pub cs: ConstraintSystemRef, /// Current state - pub state_words: Vec>, + pub state_words: Vec>, } -impl InstanceVar -where - F: PrimeField, +impl< + const STATE_SIZE: usize, + const STATE_SIZE_MINUS_1: usize, + const NUM_MDS_ELEMENTS: usize, + const NUM_STATE_SIZE_MINUS_1_ELEMENTS: usize, + const NUM_ROUND_ROWS: usize, + const NUM_ROUND_COLS: usize, + const NUM_ROUND_ELEMENTS: usize, + > + InstanceVar< + STATE_SIZE, + STATE_SIZE_MINUS_1, + NUM_MDS_ELEMENTS, + NUM_STATE_SIZE_MINUS_1_ELEMENTS, + NUM_ROUND_ROWS, + NUM_ROUND_COLS, + NUM_ROUND_ELEMENTS, + > { - /// Fixed width hash from n:1. Outputs a F given `t` input words. + /// Fixed width hash from n:1. Outputs a Fq given `t` input words. pub fn n_to_1_fixed_hash( - parameters: PoseidonParameters, - cs: ConstraintSystemRef, - input_words: Vec>, - ) -> FpVar { - // Check input words are `t` elements long - if input_words.len() != parameters.t { - panic!("err: input words must be t elements long") - } - + parameters: PoseidonParameters< + STATE_SIZE, + STATE_SIZE_MINUS_1, + NUM_MDS_ELEMENTS, + NUM_STATE_SIZE_MINUS_1_ELEMENTS, + NUM_ROUND_ROWS, + NUM_ROUND_COLS, + NUM_ROUND_ELEMENTS, + >, + cs: ConstraintSystemRef, + input_words: [FpVar; STATE_SIZE], + ) -> FpVar { // t = rate + capacity let mut instance = InstanceVar { parameters, cs, - state_words: input_words, + state_words: input_words.to_vec(), }; // Apply Poseidon permutation. @@ -53,13 +87,12 @@ where let R_f = self.parameters.rounds.full() / 2; let R_P = self.parameters.rounds.partial(); let mut round_constants_counter = 0; - let t = self.parameters.t; - let round_constants = self.parameters.arc.elements().clone(); + let round_constants: [Fq; NUM_ROUND_ELEMENTS] = self.parameters.arc.inner_elements(); // First full rounds for _ in 0..R_f { // Apply `AddRoundConstants` layer - for i in 0..t { + for i in 0..STATE_SIZE { self.state_words[i] += round_constants[round_constants_counter]; round_constants_counter += 1; } @@ -70,7 +103,7 @@ where // Partial rounds for _ in 0..R_P { // Apply `AddRoundConstants` layer - for i in 0..t { + for i in 0..STATE_SIZE { self.state_words[i] += round_constants[round_constants_counter]; round_constants_counter += 1; } @@ -81,7 +114,7 @@ where // Final full rounds for _ in 0..R_f { // Apply `AddRoundConstants` layer - for i in 0..t { + for i in 0..STATE_SIZE { self.state_words[i] += round_constants[round_constants_counter]; round_constants_counter += 1; } @@ -106,7 +139,7 @@ where fn full_sub_words(&mut self) { match self.parameters.alpha { Alpha::Exponent(exp) => { - for i in 0..self.parameters.t { + for i in 0..STATE_SIZE { self.state_words[i] = (self.state_words[i]) .pow_by_constant([exp as u64]) .expect("can compute pow"); @@ -127,11 +160,11 @@ where .0 .iter_rows() .map(|row| { - let temp_vec: Vec> = row + let temp_vec: Vec> = row .iter() .zip(&self.state_words) .map(|(x, y)| { - FpVar::::new_constant(self.cs.clone(), x).expect("can create constant") + FpVar::::new_constant(self.cs.clone(), x).expect("can create constant") * y }) .collect();