Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
116 changes: 116 additions & 0 deletions halo2-base/src/poseidon/hasher/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use std::mem;

use crate::{
gates::GateInstructions,
poseidon::hasher::{spec::OptimizedPoseidonSpec, state::PoseidonState},
AssignedValue, Context, ScalarField,
};

#[cfg(test)]
mod tests;

/// Module for maximum distance separable matrix operations.
pub mod mds;
/// Module for poseidon specification.
pub mod spec;
/// Module for poseidon states.
pub mod state;

/// Poseidon hasher. This is stateful.
pub struct PoseidonHasher<F: ScalarField, const T: usize, const RATE: usize> {
init_state: PoseidonState<F, T, RATE>,
state: PoseidonState<F, T, RATE>,
spec: OptimizedPoseidonSpec<F, T, RATE>,
absorbing: Vec<AssignedValue<F>>,
}

impl<F: ScalarField, const T: usize, const RATE: usize> PoseidonHasher<F, T, RATE> {
/// Create new Poseidon hasher.
pub fn new<const R_F: usize, const R_P: usize, const SECURE_MDS: usize>(
ctx: &mut Context<F>,
) -> Self {
let init_state = PoseidonState::default(ctx);
let state = init_state.clone();
Self {
init_state,
state,
spec: OptimizedPoseidonSpec::new::<R_F, R_P, SECURE_MDS>(),
absorbing: Vec::new(),
}
}

/// Initialize a poseidon hasher from an existing spec.
pub fn from_spec(ctx: &mut Context<F>, spec: OptimizedPoseidonSpec<F, T, RATE>) -> Self {
let init_state = PoseidonState::default(ctx);
Self { spec, state: init_state.clone(), init_state, absorbing: Vec::new() }
}

/// Reset state to default and clear the buffer.
pub fn clear(&mut self) {
self.state = self.init_state.clone();
self.absorbing.clear();
}

/// Store given `elements` into buffer.
pub fn update(&mut self, elements: &[AssignedValue<F>]) {
self.absorbing.extend_from_slice(elements);
}

/// Consume buffer and perform permutation, then output second element of
/// state.
pub fn squeeze(
&mut self,
ctx: &mut Context<F>,
gate: &impl GateInstructions<F>,
) -> AssignedValue<F> {
let input_elements = mem::take(&mut self.absorbing);
let exact = input_elements.len() % RATE == 0;

for chunk in input_elements.chunks(RATE) {
self.permutation(ctx, gate, chunk.to_vec());
}
if exact {
self.permutation(ctx, gate, vec![]);
}

self.state.s[1]
}

fn permutation(
&mut self,
ctx: &mut Context<F>,
gate: &impl GateInstructions<F>,
inputs: Vec<AssignedValue<F>>,
) {
let r_f = self.spec.r_f / 2;
let mds = &self.spec.mds_matrices.mds.0;
let pre_sparse_mds = &self.spec.mds_matrices.pre_sparse_mds.0;
let sparse_matrices = &self.spec.mds_matrices.sparse_matrices;

// First half of the full round
let constants = &self.spec.constants.start;
self.state.absorb_with_pre_constants(ctx, gate, inputs, &constants[0]);
for constants in constants.iter().skip(1).take(r_f - 1) {
self.state.sbox_full(ctx, gate, constants);
self.state.apply_mds(ctx, gate, mds);
}
self.state.sbox_full(ctx, gate, constants.last().unwrap());
self.state.apply_mds(ctx, gate, pre_sparse_mds);

// Partial rounds
let constants = &self.spec.constants.partial;
for (constant, sparse_mds) in constants.iter().zip(sparse_matrices.iter()) {
self.state.sbox_part(ctx, gate, constant);
self.state.apply_sparse_mds(ctx, gate, sparse_mds);
}

// Second half of the full rounds
let constants = &self.spec.constants.end;
for constants in constants.iter() {
self.state.sbox_full(ctx, gate, constants);
self.state.apply_mds(ctx, gate, mds);
}
self.state.sbox_full(ctx, gate, &[F::ZERO; T]);
self.state.apply_mds(ctx, gate, mds);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{poseidon::mds::*, utils::ScalarField};
use crate::{poseidon::hasher::mds::*, utils::ScalarField};

use poseidon_rs::poseidon::primitives::Spec as PoseidonSpec; // trait
use std::marker::PhantomData;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::iter;

use crate::{
gates::GateInstructions,
poseidon::mds::SparseMDSMatrix,
poseidon::hasher::mds::SparseMDSMatrix,
utils::ScalarField,
AssignedValue, Context,
QuantumCell::{Constant, Existing},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{cmp::max, iter::zip};
use crate::{
gates::{builder::GateThreadBuilder, GateChip},
halo2_proofs::halo2curves::bn256::Fr,
poseidon::PoseidonHasherChip,
poseidon::hasher::PoseidonHasher,
utils::ScalarField,
};
use pse_poseidon::Poseidon;
Expand Down Expand Up @@ -31,7 +31,7 @@ fn poseidon_compatiblity_verification<
// constructing native and in-circuit Poseidon sponges
let mut native_sponge = Poseidon::<F, T, RATE>::new(R_F, R_P);
// assuming SECURE_MDS = 0
let mut circuit_sponge = PoseidonHasherChip::<F, T, RATE>::new::<R_F, R_P, 0>(ctx);
let mut circuit_sponge = PoseidonHasher::<F, T, RATE>::new::<R_F, R_P, 0>(ctx);

// preparing to interleave absorptions and squeezings
let n_iterations = max(absorptions.len(), squeezings.len());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fn test_poseidon_against_test_vectors() {
const T: usize = 3;
const RATE: usize = 2;

let mut hasher = PoseidonHasherChip::<Fr, T, RATE>::new::<R_F, R_P, 0>(ctx);
let mut hasher = PoseidonHasher::<Fr, T, RATE>::new::<R_F, R_P, 0>(ctx);

let state = [0u64, 1, 2];
hasher.state =
Expand All @@ -76,7 +76,7 @@ fn test_poseidon_against_test_vectors() {
const T: usize = 5;
const RATE: usize = 4;

let mut hasher = PoseidonHasherChip::<Fr, T, RATE>::new::<R_F, R_P, 0>(ctx);
let mut hasher = PoseidonHasher::<Fr, T, RATE>::new::<R_F, R_P, 0>(ctx);

let state = [0u64, 1, 2, 3, 4];
hasher.state =
Expand Down
118 changes: 2 additions & 116 deletions halo2-base/src/poseidon/mod.rs
Original file line number Diff line number Diff line change
@@ -1,116 +1,2 @@
use std::mem;

use crate::{
gates::GateInstructions,
poseidon::{spec::OptimizedPoseidonSpec, state::PoseidonState},
AssignedValue, Context, ScalarField,
};

#[cfg(test)]
mod tests;

/// Module for maximum distance separable matrix operations.
pub mod mds;
/// Module for poseidon specification.
pub mod spec;
/// Module for poseidon states.
pub mod state;

/// Chip for Poseidon hasher. The chip is stateful.
pub struct PoseidonHasherChip<F: ScalarField, const T: usize, const RATE: usize> {
init_state: PoseidonState<F, T, RATE>,
state: PoseidonState<F, T, RATE>,
spec: OptimizedPoseidonSpec<F, T, RATE>,
absorbing: Vec<AssignedValue<F>>,
}

impl<F: ScalarField, const T: usize, const RATE: usize> PoseidonHasherChip<F, T, RATE> {
/// Create new Poseidon hasher chip.
pub fn new<const R_F: usize, const R_P: usize, const SECURE_MDS: usize>(
ctx: &mut Context<F>,
) -> Self {
let init_state = PoseidonState::default(ctx);
let state = init_state.clone();
Self {
init_state,
state,
spec: OptimizedPoseidonSpec::new::<R_F, R_P, SECURE_MDS>(),
absorbing: Vec::new(),
}
}

/// Initialize a poseidon hasher from an existing spec.
pub fn from_spec(ctx: &mut Context<F>, spec: OptimizedPoseidonSpec<F, T, RATE>) -> Self {
let init_state = PoseidonState::default(ctx);
Self { spec, state: init_state.clone(), init_state, absorbing: Vec::new() }
}

/// Reset state to default and clear the buffer.
pub fn clear(&mut self) {
self.state = self.init_state.clone();
self.absorbing.clear();
}

/// Store given `elements` into buffer.
pub fn update(&mut self, elements: &[AssignedValue<F>]) {
self.absorbing.extend_from_slice(elements);
}

/// Consume buffer and perform permutation, then output second element of
/// state.
pub fn squeeze(
&mut self,
ctx: &mut Context<F>,
gate: &impl GateInstructions<F>,
) -> AssignedValue<F> {
let input_elements = mem::take(&mut self.absorbing);
let exact = input_elements.len() % RATE == 0;

for chunk in input_elements.chunks(RATE) {
self.permutation(ctx, gate, chunk.to_vec());
}
if exact {
self.permutation(ctx, gate, vec![]);
}

self.state.s[1]
}

fn permutation(
&mut self,
ctx: &mut Context<F>,
gate: &impl GateInstructions<F>,
inputs: Vec<AssignedValue<F>>,
) {
let r_f = self.spec.r_f / 2;
let mds = &self.spec.mds_matrices.mds.0;
let pre_sparse_mds = &self.spec.mds_matrices.pre_sparse_mds.0;
let sparse_matrices = &self.spec.mds_matrices.sparse_matrices;

// First half of the full round
let constants = &self.spec.constants.start;
self.state.absorb_with_pre_constants(ctx, gate, inputs, &constants[0]);
for constants in constants.iter().skip(1).take(r_f - 1) {
self.state.sbox_full(ctx, gate, constants);
self.state.apply_mds(ctx, gate, mds);
}
self.state.sbox_full(ctx, gate, constants.last().unwrap());
self.state.apply_mds(ctx, gate, pre_sparse_mds);

// Partial rounds
let constants = &self.spec.constants.partial;
for (constant, sparse_mds) in constants.iter().zip(sparse_matrices.iter()) {
self.state.sbox_part(ctx, gate, constant);
self.state.apply_sparse_mds(ctx, gate, sparse_mds);
}

// Second half of the full rounds
let constants = &self.spec.constants.end;
for constants in constants.iter() {
self.state.sbox_full(ctx, gate, constants);
self.state.apply_mds(ctx, gate, mds);
}
self.state.sbox_full(ctx, gate, &[F::ZERO; T]);
self.state.apply_mds(ctx, gate, mds);
}
}
/// Module for Poseidon hasher
pub mod hasher;