Move hashes to their specific files

This commit is contained in:
wborgeaud 2021-12-29 16:51:46 +01:00
parent 314a5845b7
commit a0a42e4bef
5 changed files with 181 additions and 168 deletions

View File

@ -1,7 +1,15 @@
use plonky2_field::extension_field::Extendable;
use plonky2_field::field_types::Field; use plonky2_field::field_types::Field;
use plonky2_field::goldilocks_field::GoldilocksField; use plonky2_field::goldilocks_field::GoldilocksField;
use unroll::unroll_for_loops; use unroll::unroll_for_loops;
use crate::gates::gmimc::GMiMCGate;
use crate::hash::hash_types::{HashOut, RichField};
use crate::hash::hashing::{compress, hash_n_to_hash, GMiMCPermutation, SPONGE_WIDTH};
use crate::iop::target::{BoolTarget, Target};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::config::{AlgebraicHasher, Hasher};
pub(crate) const NUM_ROUNDS: usize = 101; pub(crate) const NUM_ROUNDS: usize = 101;
pub trait GMiMC<const WIDTH: usize>: Field pub trait GMiMC<const WIDTH: usize>: Field
@ -85,6 +93,54 @@ impl GMiMC<12> for GoldilocksField {
const ROUND_CONSTANTS: [u64; NUM_ROUNDS] = GOLDILOCKS_ROUND_CONSTANTS; const ROUND_CONSTANTS: [u64; NUM_ROUNDS] = GOLDILOCKS_ROUND_CONSTANTS;
} }
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct GMiMCHash;
impl<F: RichField> Hasher<F> for GMiMCHash {
const HASH_SIZE: usize = 4 * 8;
type Hash = HashOut<F>;
type Permutation = GMiMCPermutation;
fn hash(input: Vec<F>, pad: bool) -> Self::Hash {
hash_n_to_hash::<F, Self::Permutation>(input, pad)
}
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash {
compress::<F, Self::Permutation>(left, right)
}
}
impl<F: RichField> AlgebraicHasher<F> for GMiMCHash {
fn permute_swapped<const D: usize>(
inputs: [Target; SPONGE_WIDTH],
swap: BoolTarget,
builder: &mut CircuitBuilder<F, D>,
) -> [Target; SPONGE_WIDTH]
where
F: RichField + Extendable<D>,
{
let gate_type = GMiMCGate::<F, D, SPONGE_WIDTH>::new();
let gate = builder.add_gate(gate_type, vec![]);
let swap_wire = GMiMCGate::<F, D, SPONGE_WIDTH>::WIRE_SWAP;
let swap_wire = Target::wire(gate, swap_wire);
builder.connect(swap.target, swap_wire);
// Route input wires.
for i in 0..SPONGE_WIDTH {
let in_wire = GMiMCGate::<F, D, SPONGE_WIDTH>::wire_input(i);
let in_wire = Target::wire(gate, in_wire);
builder.connect(inputs[i], in_wire);
}
// Collect output wires.
(0..SPONGE_WIDTH)
.map(|i| Target::wire(gate, GMiMCGate::<F, D, SPONGE_WIDTH>::wire_output(i)))
.collect::<Vec<_>>()
.try_into()
.unwrap()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use plonky2_field::goldilocks_field::GoldilocksField; use plonky2_field::goldilocks_field::GoldilocksField;

View File

@ -0,0 +1,65 @@
use keccak_hash::keccak;
use crate::hash::hash_types::{BytesHash, RichField};
use crate::hash::hashing::{PlonkyPermutation, SPONGE_WIDTH};
use crate::plonk::config::Hasher;
use crate::util::serialization::Buffer;
/// Keccak-256 permutation used in the challenger.
pub struct KeccakPermutation;
impl<F: RichField> PlonkyPermutation<F> for KeccakPermutation {
fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH] {
// Fill a byte array with the little-endian representation of the field array.
let mut buffer = [0u8; SPONGE_WIDTH * std::mem::size_of::<u64>()];
for i in 0..SPONGE_WIDTH {
buffer[i * std::mem::size_of::<F>()..(i + 1) * std::mem::size_of::<F>()]
.copy_from_slice(&input[i].to_canonical_u64().to_le_bytes());
}
// Concatenate `H(input), H(H(input)), H(H(H(input)))`.
let permutated_input_bytes = {
let mut ans = [0u8; 96];
ans[0..32].copy_from_slice(&keccak(buffer).0);
ans[32..64].copy_from_slice(&keccak(keccak(buffer).0).0);
ans[64..96].copy_from_slice(&keccak(keccak(keccak(buffer).0).0).0);
ans
};
// Write the hashed byte array to a field array.
let mut permutated_input = [F::ZERO; SPONGE_WIDTH];
for i in 0..SPONGE_WIDTH {
permutated_input[i] = F::from_noncanonical_u64(u64::from_le_bytes(
permutated_input_bytes
[i * std::mem::size_of::<F>()..(i + 1) * std::mem::size_of::<F>()]
.try_into()
.unwrap(),
));
}
permutated_input
}
}
/// Keccak-256 hash function.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct KeccakHash<const N: usize>;
impl<F: RichField, const N: usize> Hasher<F> for KeccakHash<N> {
const HASH_SIZE: usize = N;
type Hash = BytesHash<N>;
type Permutation = KeccakPermutation;
fn hash(input: Vec<F>, _pad: bool) -> Self::Hash {
let mut buffer = Buffer::new(Vec::new());
buffer.write_field_vec(&input).unwrap();
let mut arr = [0; N];
let hash_bytes = keccak(buffer.bytes()).0;
arr.copy_from_slice(&hash_bytes[..N]);
BytesHash(arr)
}
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash {
let mut v = vec![0; N * 2];
v[0..N].copy_from_slice(&left.0);
v[N..].copy_from_slice(&right.0);
let mut arr = [0; N];
arr.copy_from_slice(&keccak(v).0[..N]);
BytesHash(arr)
}
}

View File

@ -2,6 +2,7 @@ mod arch;
pub mod gmimc; pub mod gmimc;
pub mod hash_types; pub mod hash_types;
pub mod hashing; pub mod hashing;
pub mod keccak;
pub mod merkle_proofs; pub mod merkle_proofs;
pub mod merkle_tree; pub mod merkle_tree;
pub mod path_compression; pub mod path_compression;

View File

@ -6,11 +6,14 @@ use plonky2_field::field_types::{Field, PrimeField};
use unroll::unroll_for_loops; use unroll::unroll_for_loops;
use crate::gates::gate::Gate; use crate::gates::gate::Gate;
use crate::gates::poseidon::PoseidonGate;
use crate::gates::poseidon_mds::PoseidonMdsGate; use crate::gates::poseidon_mds::PoseidonMdsGate;
use crate::hash::hash_types::RichField; use crate::hash::hash_types::{HashOut, RichField};
use crate::hash::hashing::SPONGE_WIDTH; use crate::hash::hashing::{compress, hash_n_to_hash, PoseidonPermutation, SPONGE_WIDTH};
use crate::iop::ext_target::ExtensionTarget; use crate::iop::ext_target::ExtensionTarget;
use crate::iop::target::{BoolTarget, Target};
use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::config::{AlgebraicHasher, Hasher};
// The number of full rounds and partial rounds is given by the // The number of full rounds and partial rounds is given by the
// calc_round_numbers.py script. They happen to be the same for both // calc_round_numbers.py script. They happen to be the same for both
@ -615,6 +618,55 @@ pub trait Poseidon: PrimeField {
} }
} }
/// Poseidon hash function.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct PoseidonHash;
impl<F: RichField> Hasher<F> for PoseidonHash {
const HASH_SIZE: usize = 4 * 8;
type Hash = HashOut<F>;
type Permutation = PoseidonPermutation;
fn hash(input: Vec<F>, pad: bool) -> Self::Hash {
hash_n_to_hash::<F, Self::Permutation>(input, pad)
}
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash {
compress::<F, Self::Permutation>(left, right)
}
}
impl<F: RichField> AlgebraicHasher<F> for PoseidonHash {
fn permute_swapped<const D: usize>(
inputs: [Target; SPONGE_WIDTH],
swap: BoolTarget,
builder: &mut CircuitBuilder<F, D>,
) -> [Target; SPONGE_WIDTH]
where
F: RichField + Extendable<D>,
{
let gate_type = PoseidonGate::<F, D>::new();
let gate = builder.add_gate(gate_type, vec![]);
let swap_wire = PoseidonGate::<F, D>::WIRE_SWAP;
let swap_wire = Target::wire(gate, swap_wire);
builder.connect(swap.target, swap_wire);
// Route input wires.
for i in 0..SPONGE_WIDTH {
let in_wire = PoseidonGate::<F, D>::wire_input(i);
let in_wire = Target::wire(gate, in_wire);
builder.connect(inputs[i], in_wire);
}
// Collect output wires.
(0..SPONGE_WIDTH)
.map(|i| Target::wire(gate, PoseidonGate::<F, D>::wire_output(i)))
.collect::<Vec<_>>()
.try_into()
.unwrap()
}
}
#[cfg(test)] #[cfg(test)]
pub(crate) mod test_helpers { pub(crate) mod test_helpers {
use plonky2_field::field_types::Field; use plonky2_field::field_types::Field;

View File

@ -1,23 +1,18 @@
use std::convert::TryInto;
use std::fmt::Debug; use std::fmt::Debug;
use keccak_hash::keccak;
use plonky2_field::extension_field::quadratic::QuadraticExtension; use plonky2_field::extension_field::quadratic::QuadraticExtension;
use plonky2_field::extension_field::{Extendable, FieldExtension}; use plonky2_field::extension_field::{Extendable, FieldExtension};
use plonky2_field::goldilocks_field::GoldilocksField; use plonky2_field::goldilocks_field::GoldilocksField;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use crate::gates::gmimc::GMiMCGate; use crate::hash::gmimc::GMiMCHash;
use crate::gates::poseidon::PoseidonGate; use crate::hash::hash_types::HashOut;
use crate::hash::hash_types::RichField; use crate::hash::hash_types::RichField;
use crate::hash::hash_types::{BytesHash, HashOut}; use crate::hash::hashing::{PlonkyPermutation, SPONGE_WIDTH};
use crate::hash::hashing::{ use crate::hash::keccak::KeccakHash;
compress, hash_n_to_hash, GMiMCPermutation, PlonkyPermutation, PoseidonPermutation, use crate::hash::poseidon::PoseidonHash;
SPONGE_WIDTH,
};
use crate::iop::target::{BoolTarget, Target}; use crate::iop::target::{BoolTarget, Target};
use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_builder::CircuitBuilder;
use crate::util::serialization::Buffer;
pub trait GenericHashOut<F: RichField>: pub trait GenericHashOut<F: RichField>:
Copy + Clone + Debug + Eq + PartialEq + Send + Sync + Serialize + DeserializeOwned Copy + Clone + Debug + Eq + PartialEq + Send + Sync + Serialize + DeserializeOwned
@ -57,162 +52,6 @@ pub trait AlgebraicHasher<F: RichField>: Hasher<F, Hash = HashOut<F>> {
F: RichField + Extendable<D>; F: RichField + Extendable<D>;
} }
/// Poseidon hash function.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct PoseidonHash;
impl<F: RichField> Hasher<F> for PoseidonHash {
const HASH_SIZE: usize = 4 * 8;
type Hash = HashOut<F>;
type Permutation = PoseidonPermutation;
fn hash(input: Vec<F>, pad: bool) -> Self::Hash {
hash_n_to_hash::<F, Self::Permutation>(input, pad)
}
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash {
compress::<F, Self::Permutation>(left, right)
}
}
impl<F: RichField> AlgebraicHasher<F> for PoseidonHash {
fn permute_swapped<const D: usize>(
inputs: [Target; SPONGE_WIDTH],
swap: BoolTarget,
builder: &mut CircuitBuilder<F, D>,
) -> [Target; SPONGE_WIDTH]
where
F: RichField + Extendable<D>,
{
let gate_type = PoseidonGate::<F, D>::new();
let gate = builder.add_gate(gate_type, vec![]);
let swap_wire = PoseidonGate::<F, D>::WIRE_SWAP;
let swap_wire = Target::wire(gate, swap_wire);
builder.connect(swap.target, swap_wire);
// Route input wires.
for i in 0..SPONGE_WIDTH {
let in_wire = PoseidonGate::<F, D>::wire_input(i);
let in_wire = Target::wire(gate, in_wire);
builder.connect(inputs[i], in_wire);
}
// Collect output wires.
(0..SPONGE_WIDTH)
.map(|i| Target::wire(gate, PoseidonGate::<F, D>::wire_output(i)))
.collect::<Vec<_>>()
.try_into()
.unwrap()
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct GMiMCHash;
impl<F: RichField> Hasher<F> for GMiMCHash {
const HASH_SIZE: usize = 4 * 8;
type Hash = HashOut<F>;
type Permutation = GMiMCPermutation;
fn hash(input: Vec<F>, pad: bool) -> Self::Hash {
hash_n_to_hash::<F, Self::Permutation>(input, pad)
}
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash {
compress::<F, Self::Permutation>(left, right)
}
}
impl<F: RichField> AlgebraicHasher<F> for GMiMCHash {
fn permute_swapped<const D: usize>(
inputs: [Target; SPONGE_WIDTH],
swap: BoolTarget,
builder: &mut CircuitBuilder<F, D>,
) -> [Target; SPONGE_WIDTH]
where
F: RichField + Extendable<D>,
{
let gate_type = GMiMCGate::<F, D, SPONGE_WIDTH>::new();
let gate = builder.add_gate(gate_type, vec![]);
let swap_wire = GMiMCGate::<F, D, SPONGE_WIDTH>::WIRE_SWAP;
let swap_wire = Target::wire(gate, swap_wire);
builder.connect(swap.target, swap_wire);
// Route input wires.
for i in 0..SPONGE_WIDTH {
let in_wire = GMiMCGate::<F, D, SPONGE_WIDTH>::wire_input(i);
let in_wire = Target::wire(gate, in_wire);
builder.connect(inputs[i], in_wire);
}
// Collect output wires.
(0..SPONGE_WIDTH)
.map(|i| Target::wire(gate, GMiMCGate::<F, D, SPONGE_WIDTH>::wire_output(i)))
.collect::<Vec<_>>()
.try_into()
.unwrap()
}
}
/// Keccak-256 permutation used in the challenger.
pub struct KeccakPermutation;
impl<F: RichField> PlonkyPermutation<F> for KeccakPermutation {
fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH] {
// Fill a byte array with the little-endian representation of the field array.
let mut buffer = [0u8; SPONGE_WIDTH * std::mem::size_of::<u64>()];
for i in 0..SPONGE_WIDTH {
buffer[i * std::mem::size_of::<F>()..(i + 1) * std::mem::size_of::<F>()]
.copy_from_slice(&input[i].to_canonical_u64().to_le_bytes());
}
// Concatenate `H(input), H(H(input)), H(H(H(input)))`.
let permutated_input_bytes = {
let mut ans = [0u8; 96];
ans[0..32].copy_from_slice(&keccak(buffer).0);
ans[32..64].copy_from_slice(&keccak(keccak(buffer).0).0);
ans[64..96].copy_from_slice(&keccak(keccak(keccak(buffer).0).0).0);
ans
};
// Write the hashed byte array to a field array.
let mut permutated_input = [F::ZERO; SPONGE_WIDTH];
for i in 0..SPONGE_WIDTH {
permutated_input[i] = F::from_noncanonical_u64(u64::from_le_bytes(
permutated_input_bytes
[i * std::mem::size_of::<F>()..(i + 1) * std::mem::size_of::<F>()]
.try_into()
.unwrap(),
));
}
permutated_input
}
}
/// Keccak-256 hash function.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct KeccakHash<const N: usize>;
impl<F: RichField, const N: usize> Hasher<F> for KeccakHash<N> {
const HASH_SIZE: usize = N;
type Hash = BytesHash<N>;
type Permutation = KeccakPermutation;
fn hash(input: Vec<F>, _pad: bool) -> Self::Hash {
let mut buffer = Buffer::new(Vec::new());
buffer.write_field_vec(&input).unwrap();
let mut arr = [0; N];
let hash_bytes = keccak(buffer.bytes()).0;
arr.copy_from_slice(&hash_bytes[..N]);
BytesHash(arr)
}
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash {
let mut v = vec![0; N * 2];
v[0..N].copy_from_slice(&left.0);
v[N..].copy_from_slice(&right.0);
let mut arr = [0; N];
arr.copy_from_slice(&keccak(v).0[..N]);
BytesHash(arr)
}
}
/// Generic configuration trait. /// Generic configuration trait.
pub trait GenericConfig<const D: usize>: pub trait GenericConfig<const D: usize>:
Debug + Clone + Sync + Sized + Send + Eq + PartialEq Debug + Clone + Sync + Sized + Send + Eq + PartialEq