Merge pull request #407 from mir-protocol/challenger_outer_hash

Use the outer hash in the challenger
This commit is contained in:
wborgeaud 2022-01-02 10:24:29 +01:00 committed by GitHub
commit a452da523b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 223 additions and 161 deletions

View File

@ -135,7 +135,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
pub(crate) fn open_plonk( pub(crate) fn open_plonk(
commitments: &[&Self; 4], commitments: &[&Self; 4],
zeta: F::Extension, zeta: F::Extension,
challenger: &mut Challenger<F, C::InnerHasher>, challenger: &mut Challenger<F, C::Hasher>,
common_data: &CommonCircuitData<F, C, D>, common_data: &CommonCircuitData<F, C, D>,
timing: &mut TimingTree, timing: &mut TimingTree,
) -> (FriProof<F, C::Hasher, D>, OpeningSet<F, D>) { ) -> (FriProof<F, C::Hasher, D>, OpeningSet<F, D>) {

View File

@ -21,7 +21,7 @@ pub fn fri_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const
lde_polynomial_coeffs: PolynomialCoeffs<F::Extension>, lde_polynomial_coeffs: PolynomialCoeffs<F::Extension>,
// Evaluation of the polynomial on the large domain. // Evaluation of the polynomial on the large domain.
lde_polynomial_values: PolynomialValues<F::Extension>, lde_polynomial_values: PolynomialValues<F::Extension>,
challenger: &mut Challenger<F, C::InnerHasher>, challenger: &mut Challenger<F, C::Hasher>,
common_data: &CommonCircuitData<F, C, D>, common_data: &CommonCircuitData<F, C, D>,
timing: &mut TimingTree, timing: &mut TimingTree,
) -> FriProof<F, C::Hasher, D> { ) -> FriProof<F, C::Hasher, D> {
@ -63,7 +63,7 @@ pub fn fri_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const
fn fri_committed_trees<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>( fn fri_committed_trees<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
mut coeffs: PolynomialCoeffs<F::Extension>, mut coeffs: PolynomialCoeffs<F::Extension>,
mut values: PolynomialValues<F::Extension>, mut values: PolynomialValues<F::Extension>,
challenger: &mut Challenger<F, C::InnerHasher>, challenger: &mut Challenger<F, C::Hasher>,
common_data: &CommonCircuitData<F, C, D>, common_data: &CommonCircuitData<F, C, D>,
) -> ( ) -> (
Vec<MerkleTree<F, C::Hasher>>, Vec<MerkleTree<F, C::Hasher>>,
@ -140,7 +140,7 @@ fn fri_prover_query_rounds<
>( >(
initial_merkle_trees: &[&MerkleTree<F, C::Hasher>], initial_merkle_trees: &[&MerkleTree<F, C::Hasher>],
trees: &[MerkleTree<F, C::Hasher>], trees: &[MerkleTree<F, C::Hasher>],
challenger: &mut Challenger<F, C::InnerHasher>, challenger: &mut Challenger<F, C::Hasher>,
n: usize, n: usize,
common_data: &CommonCircuitData<F, C, D>, common_data: &CommonCircuitData<F, C, D>,
) -> Vec<FriQueryRound<F, C::Hasher, D>> { ) -> Vec<FriQueryRound<F, C::Hasher, D>> {
@ -156,7 +156,7 @@ fn fri_prover_query_round<
>( >(
initial_merkle_trees: &[&MerkleTree<F, C::Hasher>], initial_merkle_trees: &[&MerkleTree<F, C::Hasher>],
trees: &[MerkleTree<F, C::Hasher>], trees: &[MerkleTree<F, C::Hasher>],
challenger: &mut Challenger<F, C::InnerHasher>, challenger: &mut Challenger<F, C::Hasher>,
n: usize, n: usize,
common_data: &CommonCircuitData<F, C, D>, common_data: &CommonCircuitData<F, C, D>,
) -> FriQueryRound<F, C::Hasher, D> { ) -> FriQueryRound<F, C::Hasher, D> {

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, PlonkyPermutation, 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,61 @@ impl GMiMC<12> for GoldilocksField {
const ROUND_CONSTANTS: [u64; NUM_ROUNDS] = GOLDILOCKS_ROUND_CONSTANTS; const ROUND_CONSTANTS: [u64; NUM_ROUNDS] = GOLDILOCKS_ROUND_CONSTANTS;
} }
pub struct GMiMCPermutation;
impl<F: RichField> PlonkyPermutation<F> for GMiMCPermutation {
fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH] {
F::gmimc_permute(input)
}
}
#[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

@ -97,19 +97,6 @@ pub trait PlonkyPermutation<F: RichField> {
fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH]; fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH];
} }
pub struct PoseidonPermutation;
impl<F: RichField> PlonkyPermutation<F> for PoseidonPermutation {
fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH] {
F::poseidon(input)
}
}
pub struct GMiMCPermutation;
impl<F: RichField> PlonkyPermutation<F> for GMiMCPermutation {
fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH] {
F::gmimc_permute(input)
}
}
/// If `pad` is enabled, the message is padded using the pad10*1 rule. In general this is required /// 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 /// for the hash to be secure, but it can safely be disabled in certain cases, like if the input
/// length is fixed. /// length is fixed.

View File

@ -0,0 +1,81 @@
use std::mem::size_of;
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 pseudo-permutation (not necessarily one-to-one) used in the challenger.
/// A state `input: [F; 12]` is sent to the field representation of `H(input) || H(H(input)) || H(H(H(input)))`
/// where `H` is the Keccak-256 hash.
pub struct KeccakPermutation;
impl<F: RichField> PlonkyPermutation<F> for KeccakPermutation {
fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH] {
// Use rejection sampling so that if one of the `u64` values in the output is larger than
// the field order, we increment the nonce and start again.
'rejection_sampling: for nonce in 0u64.. {
// Fill a byte array with the little-endian representation of the field array.
let mut buffer = [0u8; (SPONGE_WIDTH + 1) * size_of::<u64>()];
for i in 0..SPONGE_WIDTH {
buffer[i * size_of::<u64>()..(i + 1) * size_of::<u64>()]
.copy_from_slice(&input[i].to_canonical_u64().to_le_bytes());
}
// Add the nonce at the end of the buffer.
buffer[SPONGE_WIDTH * size_of::<u64>()..].copy_from_slice(&nonce.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 {
let perm_u64 = u64::from_le_bytes(
permutated_input_bytes[i * size_of::<u64>()..(i + 1) * size_of::<u64>()]
.try_into()
.unwrap(),
);
if perm_u64 >= F::ORDER {
// If a value is larger than the field order, we break and start again with a new nonce.
continue 'rejection_sampling;
} else {
permutated_input[i] = F::from_canonical_u64(perm_u64);
}
}
return permutated_input;
}
panic!("Improbable.")
}
}
/// 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, PlonkyPermutation, 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,62 @@ pub trait Poseidon: PrimeField {
} }
} }
pub struct PoseidonPermutation;
impl<F: RichField> PlonkyPermutation<F> for PoseidonPermutation {
fn permute(input: [F; SPONGE_WIDTH]) -> [F; SPONGE_WIDTH] {
F::poseidon(input)
}
}
/// 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

@ -15,7 +15,7 @@ use crate::plonk::proof::{OpeningSet, OpeningSetTarget};
/// Observes prover messages, and generates challenges by hashing the transcript, a la Fiat-Shamir. /// Observes prover messages, and generates challenges by hashing the transcript, a la Fiat-Shamir.
#[derive(Clone)] #[derive(Clone)]
pub struct Challenger<F: RichField, H: AlgebraicHasher<F>> { pub struct Challenger<F: RichField, H: Hasher<F>> {
sponge_state: [F; SPONGE_WIDTH], sponge_state: [F; SPONGE_WIDTH],
input_buffer: Vec<F>, input_buffer: Vec<F>,
output_buffer: Vec<F>, output_buffer: Vec<F>,
@ -30,7 +30,7 @@ pub struct Challenger<F: RichField, H: AlgebraicHasher<F>> {
/// design, but it can be viewed as a duplex sponge whose inputs are sometimes zero (when we perform /// design, but it can be viewed as a duplex sponge whose inputs are sometimes zero (when we perform
/// multiple squeezes) and whose outputs are sometimes ignored (when we perform multiple /// multiple squeezes) and whose outputs are sometimes ignored (when we perform multiple
/// absorptions). Thus the security properties of a duplex sponge still apply to our design. /// absorptions). Thus the security properties of a duplex sponge still apply to our design.
impl<F: RichField, H: AlgebraicHasher<F>> Challenger<F, H> { impl<F: RichField, H: Hasher<F>> Challenger<F, H> {
pub fn new() -> Challenger<F, H> { pub fn new() -> Challenger<F, H> {
Challenger { Challenger {
sponge_state: [F::ZERO; SPONGE_WIDTH], sponge_state: [F::ZERO; SPONGE_WIDTH],

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
@ -34,6 +29,9 @@ pub trait Hasher<F: RichField>: Sized + Clone + Debug + Eq + PartialEq {
const HASH_SIZE: usize; const HASH_SIZE: usize;
type Hash: GenericHashOut<F>; type Hash: GenericHashOut<F>;
/// Permutation used in the sponge construction.
type Permutation: PlonkyPermutation<F>;
fn hash(input: Vec<F>, pad: bool) -> Self::Hash; fn hash(input: Vec<F>, pad: bool) -> Self::Hash;
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash; fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash;
} }
@ -43,8 +41,6 @@ pub trait AlgebraicHasher<F: RichField>: Hasher<F, Hash = HashOut<F>> {
// TODO: Adding a `const WIDTH: usize` here yields a compiler error down the line. // TODO: Adding a `const WIDTH: usize` here yields a compiler error down the line.
// Maybe try again in a while. // Maybe try again in a while.
/// Permutation used in the sponge construction.
type Permutation: PlonkyPermutation<F>;
/// Circuit to conditionally swap two chunks of the inputs (useful in verifying Merkle proofs), /// Circuit to conditionally swap two chunks of the inputs (useful in verifying Merkle proofs),
/// then apply the permutation. /// then apply the permutation.
fn permute_swapped<const D: usize>( fn permute_swapped<const D: usize>(
@ -56,131 +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>;
fn hash(input: Vec<F>, pad: bool) -> Self::Hash {
hash_n_to_hash::<F, <Self as AlgebraicHasher<F>>::Permutation>(input, pad)
}
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash {
compress::<F, <Self as AlgebraicHasher<F>>::Permutation>(left, right)
}
}
impl<F: RichField> AlgebraicHasher<F> for PoseidonHash {
type Permutation = PoseidonPermutation;
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>;
fn hash(input: Vec<F>, pad: bool) -> Self::Hash {
hash_n_to_hash::<F, <Self as AlgebraicHasher<F>>::Permutation>(input, pad)
}
fn two_to_one(left: Self::Hash, right: Self::Hash) -> Self::Hash {
compress::<F, <Self as AlgebraicHasher<F>>::Permutation>(left, right)
}
}
impl<F: RichField> AlgebraicHasher<F> for GMiMCHash {
type Permutation = GMiMCPermutation;
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 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>;
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

View File

@ -32,7 +32,7 @@ fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, cons
let num_fri_queries = config.fri_config.num_query_rounds; let num_fri_queries = config.fri_config.num_query_rounds;
let lde_size = common_data.lde_size(); let lde_size = common_data.lde_size();
let mut challenger = Challenger::<F, C::InnerHasher>::new(); let mut challenger = Challenger::<F, C::Hasher>::new();
// Observe the instance. // Observe the instance.
challenger.observe_hash::<C::Hasher>(common_data.circuit_digest); challenger.observe_hash::<C::Hasher>(common_data.circuit_digest);

View File

@ -79,7 +79,7 @@ pub(crate) fn prove<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, co
) )
); );
let mut challenger = Challenger::new(); let mut challenger = Challenger::<F, C::Hasher>::new();
// Observe the instance. // Observe the instance.
challenger.observe_hash::<C::Hasher>(common_data.circuit_digest); challenger.observe_hash::<C::Hasher>(common_data.circuit_digest);