use std::convert::TryInto; use std::fmt::Debug; use keccak_hash::keccak; use serde::{de::DeserializeOwned, Serialize}; use crate::field::extension_field::quadratic::QuadraticExtension; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field_types::RichField; use crate::field::goldilocks_field::GoldilocksField; use crate::gates::poseidon::PoseidonGate; use crate::hash::hash_types::{BytesHash, HashOut}; use crate::hash::hashing::{ compress, hash_n_to_hash, PlonkyPermutation, PoseidonPermutation, SPONGE_WIDTH, }; use crate::iop::target::{BoolTarget, Target}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::util::serialization::Buffer; /// Trait for hash functions. pub trait Hasher: Sized + Clone + Debug + Eq + PartialEq { /// Size of `Hash` in bytes. const HASH_SIZE: usize; type Hash: From> + Into> + Into> + Copy + Clone + Debug + Eq + PartialEq + Send + Sync + Serialize + DeserializeOwned; fn hash(input: Vec, pad: bool) -> Self::Hash; fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash; } /// Trait for algebraic hash functions, built from a permutation using the sponge construction. pub trait AlgebraicHasher: Hasher> { // TODO: Adding a `const WIDTH: usize` here yields a compiler error down the line. // Maybe try again in a while. /// Permutation used in the sponge construction. type Permutation: PlonkyPermutation; /// Circuit to conditionally swap two chunks of the inputs (useful in verifying Merkle proofs), /// then apply the permutation. fn permute_swapped( inputs: [Target; SPONGE_WIDTH], swap: BoolTarget, builder: &mut CircuitBuilder, ) -> [Target; SPONGE_WIDTH] where F: 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; fn hash(input: Vec, pad: bool) -> Self::Hash { hash_n_to_hash::>::Permutation>(input, pad) } fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash { compress::>::Permutation>(left, right) } } impl AlgebraicHasher for PoseidonHash { type Permutation = PoseidonPermutation; fn permute_swapped( inputs: [Target; SPONGE_WIDTH], swap: BoolTarget, builder: &mut CircuitBuilder, ) -> [Target; SPONGE_WIDTH] where F: 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() } } // TODO: Remove width from `GMiMCGate` to make this work. // #[derive(Copy, Clone, Debug, Eq, PartialEq)] // pub struct GMiMCHash; // impl Hasher for GMiMCHash { // const HASH_SIZE: usize = 4 * 8; // type Hash = HashOut; // // fn hash(input: Vec, pad: bool) -> Self::Hash { // hash_n_to_hash::>::Permutation>(input, pad) // } // // fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash { // compress::>::Permutation>(left, right) // } // } // // impl AlgebraicHasher for GMiMCHash { // type Permutation = GMiMCPermutation; // // fn permute_swapped( // inputs: [Target; WIDTH], // swap: BoolTarget, // builder: &mut CircuitBuilder, // ) -> [Target; WIDTH] // where // F: 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..W { // 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..W) // .map(|i| Target::wire(gate, input: GMiMCGate::wire_output(i))) // .collect::>() // .try_into() // .unwrap() // } // } /// Keccak hash function. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct KeccakHash; impl Hasher for KeccakHash { const HASH_SIZE: usize = N; type Hash = BytesHash; 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]; arr.copy_from_slice(&keccak(buffer.bytes()).0[..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 { /// Main field. type F: RichField + Extendable; /// Field extension of degree 4 of the main field. type FE: FieldExtension; /// Hash function used for building Merkle trees. type Hasher: Hasher; /// Algebraic hash function used for the challenger and hashing public inputs. type InnerHasher: AlgebraicHasher; } /// Configuration trait for "algebraic" configurations, i.e., those using an algebraic hash function /// in Merkle trees. /// Same as `GenericConfig` trait but with `InnerHasher: AlgebraicHasher`. pub trait AlgebraicConfig: Debug + Clone + Sync + Sized + Send + Eq + PartialEq { type F: RichField + Extendable; type FE: FieldExtension; type Hasher: AlgebraicHasher; type InnerHasher: AlgebraicHasher; } impl, const D: usize> GenericConfig for A { type F = >::F; type FE = >::FE; type Hasher = >::Hasher; type InnerHasher = >::InnerHasher; } /// Configuration using Poseidon over the Goldilocks field. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct PoseidonGoldilocksConfig; impl AlgebraicConfig<2> for PoseidonGoldilocksConfig { type F = GoldilocksField; type FE = QuadraticExtension; type Hasher = PoseidonHash; type InnerHasher = PoseidonHash; } /// Configuration using truncated Keccak over the Goldilocks field. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct KeccakGoldilocksConfig; impl GenericConfig<2> for KeccakGoldilocksConfig { type F = GoldilocksField; type FE = QuadraticExtension; type Hasher = KeccakHash<25>; type InnerHasher = PoseidonHash; }