plonky2/src/hash/hashing.rs

144 lines
4.4 KiB
Rust
Raw Normal View History

2021-03-21 19:50:05 -07:00
//! Concrete instantiation of a hash function.
use crate::field::extension_field::Extendable;
use crate::field::field_types::RichField;
2021-09-17 13:15:22 +02:00
use crate::gates::poseidon::PoseidonGate;
use crate::hash::hash_types::{HashOut, HashOutTarget};
use crate::iop::target::Target;
use crate::plonk::circuit_builder::CircuitBuilder;
2021-03-21 19:50:05 -07:00
2021-03-31 21:15:24 -07:00
pub(crate) const SPONGE_RATE: usize = 8;
pub(crate) const SPONGE_CAPACITY: usize = 4;
pub(crate) const SPONGE_WIDTH: usize = SPONGE_RATE + SPONGE_CAPACITY;
2021-03-21 19:50:05 -07:00
2021-09-16 21:18:08 +02:00
pub(crate) const HASH_FAMILY: HashFamily = HashFamily::Poseidon;
pub(crate) enum HashFamily {
GMiMC,
Poseidon,
}
2021-03-21 19:50:05 -07:00
2021-03-25 15:20:14 -07:00
/// Hash the vector if necessary to reduce its length to ~256 bits. If it already fits, this is a
/// no-op.
pub fn hash_or_noop<F: RichField>(inputs: Vec<F>) -> HashOut<F> {
2021-03-25 15:20:14 -07:00
if inputs.len() <= 4 {
HashOut::from_partial(inputs)
2021-03-25 15:20:14 -07:00
} else {
hash_n_to_hash(inputs, false)
}
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn hash_or_noop(&mut self, inputs: Vec<Target>) -> HashOutTarget {
2021-04-12 10:38:07 +02:00
let zero = self.zero();
if inputs.len() <= 4 {
HashOutTarget::from_partial(inputs, zero)
2021-04-12 10:38:07 +02:00
} else {
self.hash_n_to_hash(inputs, false)
}
}
pub fn hash_n_to_hash(&mut self, inputs: Vec<Target>, pad: bool) -> HashOutTarget {
HashOutTarget::from_vec(self.hash_n_to_m(inputs, 4, pad))
2021-04-12 10:38:07 +02:00
}
pub fn hash_n_to_m(
&mut self,
mut inputs: Vec<Target>,
num_outputs: usize,
pad: bool,
) -> Vec<Target> {
let zero = self.zero();
let one = self.one();
if pad {
inputs.push(zero);
while (inputs.len() + 1) % SPONGE_WIDTH != 0 {
inputs.push(one);
}
inputs.push(zero);
}
let mut state = [zero; SPONGE_WIDTH];
// Absorb all input chunks.
for input_chunk in inputs.chunks(SPONGE_RATE) {
// Overwrite the first r elements with the inputs. This differs from a standard sponge,
// where we would xor or add in the inputs. This is a well-known variant, though,
// sometimes called "overwrite mode".
2021-04-23 12:35:19 -07:00
state[..input_chunk.len()].copy_from_slice(input_chunk);
2021-04-12 10:38:07 +02:00
state = self.permute(state);
}
// Squeeze until we have the desired number of outputs.
let mut outputs = Vec::new();
loop {
for i in 0..SPONGE_RATE {
outputs.push(state[i]);
if outputs.len() == num_outputs {
return outputs;
}
}
state = self.permute(state);
}
}
}
2021-03-25 15:20:14 -07:00
/// A one-way compression function which takes two ~256 bit inputs and returns a ~256 bit output.
pub fn compress<F: RichField>(x: HashOut<F>, y: HashOut<F>) -> HashOut<F> {
2021-03-25 15:20:14 -07:00
let mut inputs = Vec::with_capacity(8);
inputs.extend(&x.elements);
inputs.extend(&y.elements);
hash_n_to_hash(inputs, false)
}
2021-03-21 19:50:05 -07:00
/// If `pad` is enabled, the message is padded using the pad10*1 rule. In general this is required
/// for the hash to be secure, but it can safely be disabled in certain cases, like if the input
/// length is fixed.
pub fn hash_n_to_m<F: RichField>(mut inputs: Vec<F>, num_outputs: usize, pad: bool) -> Vec<F> {
2021-03-21 19:50:05 -07:00
if pad {
inputs.push(F::ZERO);
2021-03-31 21:15:24 -07:00
while (inputs.len() + 1) % SPONGE_WIDTH != 0 {
2021-03-21 19:50:05 -07:00
inputs.push(F::ONE);
}
inputs.push(F::ZERO);
}
2021-03-31 21:15:24 -07:00
let mut state = [F::ZERO; SPONGE_WIDTH];
2021-03-21 19:50:05 -07:00
// Absorb all input chunks.
2021-04-12 10:38:07 +02:00
for input_chunk in inputs.chunks(SPONGE_RATE) {
2021-03-21 19:50:05 -07:00
for i in 0..input_chunk.len() {
2021-07-14 21:16:04 +02:00
state[i] = input_chunk[i];
2021-03-21 19:50:05 -07:00
}
state = permute(state);
2021-03-21 19:50:05 -07:00
}
// Squeeze until we have the desired number of outputs.
let mut outputs = Vec::new();
loop {
2021-07-15 07:34:46 -07:00
for &item in state.iter().take(SPONGE_RATE) {
outputs.push(item);
2021-03-21 19:50:05 -07:00
if outputs.len() == num_outputs {
return outputs;
}
}
state = permute(state);
2021-03-21 19:50:05 -07:00
}
}
pub fn hash_n_to_hash<F: RichField>(inputs: Vec<F>, pad: bool) -> HashOut<F> {
HashOut::from_vec(hash_n_to_m(inputs, 4, pad))
2021-03-21 19:50:05 -07:00
}
pub fn hash_n_to_1<F: RichField>(inputs: Vec<F>, pad: bool) -> F {
2021-03-21 19:50:05 -07:00
hash_n_to_m(inputs, 1, pad)[0]
}
pub(crate) fn permute<F: RichField>(inputs: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH] {
match HASH_FAMILY {
HashFamily::GMiMC => F::gmimc_permute(inputs),
HashFamily::Poseidon => F::poseidon(inputs),
}
}