From a0a42e4bef17d8c88f03645c1ac5850a2394729e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 29 Dec 2021 16:51:46 +0100 Subject: [PATCH] Move hashes to their specific files --- plonky2/src/hash/gmimc.rs | 56 ++++++++++++ plonky2/src/hash/keccak.rs | 65 +++++++++++++ plonky2/src/hash/mod.rs | 1 + plonky2/src/hash/poseidon.rs | 56 +++++++++++- plonky2/src/plonk/config.rs | 171 +---------------------------------- 5 files changed, 181 insertions(+), 168 deletions(-) create mode 100644 plonky2/src/hash/keccak.rs diff --git a/plonky2/src/hash/gmimc.rs b/plonky2/src/hash/gmimc.rs index d5c0fb81..ffe31fbd 100644 --- a/plonky2/src/hash/gmimc.rs +++ b/plonky2/src/hash/gmimc.rs @@ -1,7 +1,15 @@ +use plonky2_field::extension_field::Extendable; use plonky2_field::field_types::Field; use plonky2_field::goldilocks_field::GoldilocksField; 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 trait GMiMC: Field @@ -85,6 +93,54 @@ impl GMiMC<12> for GoldilocksField { const ROUND_CONSTANTS: [u64; NUM_ROUNDS] = GOLDILOCKS_ROUND_CONSTANTS; } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct GMiMCHash; +impl Hasher for GMiMCHash { + const HASH_SIZE: usize = 4 * 8; + type Hash = HashOut; + type Permutation = GMiMCPermutation; + + fn hash(input: Vec, pad: bool) -> Self::Hash { + hash_n_to_hash::(input, pad) + } + + fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash { + compress::(left, right) + } +} + +impl AlgebraicHasher for GMiMCHash { + fn permute_swapped( + inputs: [Target; SPONGE_WIDTH], + swap: BoolTarget, + builder: &mut CircuitBuilder, + ) -> [Target; SPONGE_WIDTH] + where + F: RichField + Extendable, + { + let gate_type = GMiMCGate::::new(); + let gate = builder.add_gate(gate_type, vec![]); + + let swap_wire = GMiMCGate::::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::::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::::wire_output(i))) + .collect::>() + .try_into() + .unwrap() + } +} + #[cfg(test)] mod tests { use plonky2_field::goldilocks_field::GoldilocksField; diff --git a/plonky2/src/hash/keccak.rs b/plonky2/src/hash/keccak.rs new file mode 100644 index 00000000..23166954 --- /dev/null +++ b/plonky2/src/hash/keccak.rs @@ -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 PlonkyPermutation 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::()]; + for i in 0..SPONGE_WIDTH { + buffer[i * std::mem::size_of::()..(i + 1) * std::mem::size_of::()] + .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::()..(i + 1) * std::mem::size_of::()] + .try_into() + .unwrap(), + )); + } + permutated_input + } +} + +/// Keccak-256 hash function. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct KeccakHash; +impl Hasher for KeccakHash { + const HASH_SIZE: usize = N; + type Hash = BytesHash; + type Permutation = KeccakPermutation; + + fn hash(input: Vec, _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) + } +} diff --git a/plonky2/src/hash/mod.rs b/plonky2/src/hash/mod.rs index d6846de8..5a8ccb3f 100644 --- a/plonky2/src/hash/mod.rs +++ b/plonky2/src/hash/mod.rs @@ -2,6 +2,7 @@ mod arch; pub mod gmimc; pub mod hash_types; pub mod hashing; +pub mod keccak; pub mod merkle_proofs; pub mod merkle_tree; pub mod path_compression; diff --git a/plonky2/src/hash/poseidon.rs b/plonky2/src/hash/poseidon.rs index a224fa55..bacb966b 100644 --- a/plonky2/src/hash/poseidon.rs +++ b/plonky2/src/hash/poseidon.rs @@ -6,11 +6,14 @@ use plonky2_field::field_types::{Field, PrimeField}; use unroll::unroll_for_loops; use crate::gates::gate::Gate; +use crate::gates::poseidon::PoseidonGate; use crate::gates::poseidon_mds::PoseidonMdsGate; -use crate::hash::hash_types::RichField; -use crate::hash::hashing::SPONGE_WIDTH; +use crate::hash::hash_types::{HashOut, RichField}; +use crate::hash::hashing::{compress, hash_n_to_hash, PoseidonPermutation, SPONGE_WIDTH}; use crate::iop::ext_target::ExtensionTarget; +use crate::iop::target::{BoolTarget, Target}; use crate::plonk::circuit_builder::CircuitBuilder; +use crate::plonk::config::{AlgebraicHasher, Hasher}; // 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 @@ -615,6 +618,55 @@ pub trait Poseidon: PrimeField { } } +/// Poseidon hash function. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct PoseidonHash; +impl Hasher for PoseidonHash { + const HASH_SIZE: usize = 4 * 8; + type Hash = HashOut; + type Permutation = PoseidonPermutation; + + fn hash(input: Vec, pad: bool) -> Self::Hash { + hash_n_to_hash::(input, pad) + } + + fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash { + compress::(left, right) + } +} + +impl AlgebraicHasher for PoseidonHash { + fn permute_swapped( + inputs: [Target; SPONGE_WIDTH], + swap: BoolTarget, + builder: &mut CircuitBuilder, + ) -> [Target; SPONGE_WIDTH] + where + F: RichField + Extendable, + { + let gate_type = PoseidonGate::::new(); + let gate = builder.add_gate(gate_type, vec![]); + + let swap_wire = PoseidonGate::::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::::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::::wire_output(i))) + .collect::>() + .try_into() + .unwrap() + } +} + #[cfg(test)] pub(crate) mod test_helpers { use plonky2_field::field_types::Field; diff --git a/plonky2/src/plonk/config.rs b/plonky2/src/plonk/config.rs index 3c204a16..34f92f58 100644 --- a/plonky2/src/plonk/config.rs +++ b/plonky2/src/plonk/config.rs @@ -1,23 +1,18 @@ -use std::convert::TryInto; use std::fmt::Debug; -use keccak_hash::keccak; use plonky2_field::extension_field::quadratic::QuadraticExtension; use plonky2_field::extension_field::{Extendable, FieldExtension}; use plonky2_field::goldilocks_field::GoldilocksField; use serde::{de::DeserializeOwned, Serialize}; -use crate::gates::gmimc::GMiMCGate; -use crate::gates::poseidon::PoseidonGate; +use crate::hash::gmimc::GMiMCHash; +use crate::hash::hash_types::HashOut; use crate::hash::hash_types::RichField; -use crate::hash::hash_types::{BytesHash, HashOut}; -use crate::hash::hashing::{ - compress, hash_n_to_hash, GMiMCPermutation, PlonkyPermutation, PoseidonPermutation, - SPONGE_WIDTH, -}; +use crate::hash::hashing::{PlonkyPermutation, SPONGE_WIDTH}; +use crate::hash::keccak::KeccakHash; +use crate::hash::poseidon::PoseidonHash; use crate::iop::target::{BoolTarget, Target}; use crate::plonk::circuit_builder::CircuitBuilder; -use crate::util::serialization::Buffer; pub trait GenericHashOut: Copy + Clone + Debug + Eq + PartialEq + Send + Sync + Serialize + DeserializeOwned @@ -57,162 +52,6 @@ pub trait AlgebraicHasher: Hasher> { F: RichField + Extendable; } -/// Poseidon hash function. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct PoseidonHash; -impl Hasher for PoseidonHash { - const HASH_SIZE: usize = 4 * 8; - type Hash = HashOut; - type Permutation = PoseidonPermutation; - - fn hash(input: Vec, pad: bool) -> Self::Hash { - hash_n_to_hash::(input, pad) - } - - fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash { - compress::(left, right) - } -} - -impl AlgebraicHasher for PoseidonHash { - fn permute_swapped( - inputs: [Target; SPONGE_WIDTH], - swap: BoolTarget, - builder: &mut CircuitBuilder, - ) -> [Target; SPONGE_WIDTH] - where - F: RichField + Extendable, - { - let gate_type = PoseidonGate::::new(); - let gate = builder.add_gate(gate_type, vec![]); - - let swap_wire = PoseidonGate::::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::::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::::wire_output(i))) - .collect::>() - .try_into() - .unwrap() - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct GMiMCHash; -impl Hasher for GMiMCHash { - const HASH_SIZE: usize = 4 * 8; - type Hash = HashOut; - type Permutation = GMiMCPermutation; - - fn hash(input: Vec, pad: bool) -> Self::Hash { - hash_n_to_hash::(input, pad) - } - - fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash { - compress::(left, right) - } -} - -impl AlgebraicHasher for GMiMCHash { - fn permute_swapped( - inputs: [Target; SPONGE_WIDTH], - swap: BoolTarget, - builder: &mut CircuitBuilder, - ) -> [Target; SPONGE_WIDTH] - where - F: RichField + Extendable, - { - let gate_type = GMiMCGate::::new(); - let gate = builder.add_gate(gate_type, vec![]); - - let swap_wire = GMiMCGate::::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::::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::::wire_output(i))) - .collect::>() - .try_into() - .unwrap() - } -} - -/// Keccak-256 permutation used in the challenger. -pub struct KeccakPermutation; -impl PlonkyPermutation 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::()]; - for i in 0..SPONGE_WIDTH { - buffer[i * std::mem::size_of::()..(i + 1) * std::mem::size_of::()] - .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::()..(i + 1) * std::mem::size_of::()] - .try_into() - .unwrap(), - )); - } - permutated_input - } -} - -/// Keccak-256 hash function. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct KeccakHash; -impl Hasher for KeccakHash { - const HASH_SIZE: usize = N; - type Hash = BytesHash; - type Permutation = KeccakPermutation; - - fn hash(input: Vec, _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. pub trait GenericConfig: Debug + Clone + Sync + Sized + Send + Eq + PartialEq