diff --git a/src/hash.rs b/src/hash.rs new file mode 100644 index 00000000..67e43950 --- /dev/null +++ b/src/hash.rs @@ -0,0 +1,58 @@ +//! Concrete instantiation of a hash function. + +use std::convert::TryInto; + +use crate::field::field::Field; +use crate::gmimc::{gmimc_compress, gmimc_permute_array}; +use crate::proof::Hash; + +const RATE: usize = 8; +const CAPACITY: usize = 4; +const WIDTH: usize = RATE + CAPACITY; + +const GMIMC_ROUNDS: usize = 101; +const GMIMC_CONSTANTS: [u64; GMIMC_ROUNDS] = [11875528958976719239, 6107683892976199900, 7756999550758271958, 14819109722912164804, 9716579428412441110, 13627117528901194436, 16260683900833506663, 5942251937084147420, 3340009544523273897, 5103423085715007461, 17051583366444092101, 11122892258227244197, 16564300648907092407, 978667924592675864, 17676416205210517593, 1938246372790494499, 8857737698008340728, 1616088456497468086, 15961521580811621978, 17427220057097673602, 14693961562064090188, 694121596646283736, 554241305747273747, 5783347729647881086, 14933083198980931734, 2600898787591841337, 9178797321043036456, 18068112389665928586, 14493389459750307626, 1650694762687203587, 12538946551586403559, 10144328970401184255, 4215161528137084719, 17559540991336287827, 1632269449854444901, 986434918028205468, 14921385763379308253, 4345141219277982730, 2645897826751167170, 9815223670029373528, 7687983869685434132, 13956100321958014639, 519639453142393369, 15617837024229225911, 1557446238053329052, 8130006133842942201, 864716631341688017, 2860289738131495304, 16723700803638270299, 8363528906277648001, 13196016034228493087, 2514677332206134618, 15626342185220554936, 466271571343554681, 17490024028988898434, 6454235936129380878, 15187752952940298536, 18043495619660620405, 17118101079533798167, 13420382916440963101, 535472393366793763, 1071152303676936161, 6351382326603870931, 12029593435043638097, 9983185196487342247, 414304527840226604, 1578977347398530191, 13594880016528059526, 13219707576179925776, 6596253305527634647, 17708788597914990288, 7005038999589109658, 10171979740390484633, 1791376803510914239, 2405996319967739434, 12383033218117026776, 17648019043455213923, 6600216741450137683, 5359884112225925883, 1501497388400572107, 11860887439428904719, 64080876483307031, 11909038931518362287, 14166132102057826906, 14172584203466994499, 593515702472765471, 3423583343794830614, 10041710997716717966, 13434212189787960052, 9943803922749087030, 3216887087479209126, 17385898166602921353, 617799950397934255, 9245115057096506938, 13290383521064450731, 10193883853810413351, 14648839921475785656, 14635698366607946133, 9134302981480720532, 10045888297267997632, 10752096344939765738]; + +/// 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(mut inputs: Vec, num_outputs: usize, pad: bool) -> Vec { + if pad { + inputs.push(F::ZERO); + while (inputs.len() + 1) % WIDTH != 0 { + inputs.push(F::ONE); + } + inputs.push(F::ZERO); + } + + let mut state = [F::ZERO; WIDTH]; + + // Absorb all input chunks. + for input_chunk in inputs.chunks(WIDTH - 1) { + for i in 0..input_chunk.len() { + state[i] = state[i] + input_chunk[i]; + } + state = gmimc_permute_array(state, GMIMC_CONSTANTS); + } + + // Squeeze until we have the desired number of outputs. + let mut outputs = Vec::new(); + loop { + for i in 0..(WIDTH - 1) { + outputs.push(state[i]); + if outputs.len() == num_outputs { + return outputs; + } + } + state = gmimc_permute_array(state, GMIMC_CONSTANTS); + } +} + +pub fn hash_n_to_hash(inputs: Vec, pad: bool) -> Hash { + let elements = hash_n_to_m(inputs, 4, pad).try_into().unwrap(); + Hash { elements } +} + +pub fn hash_n_to_1(inputs: Vec, pad: bool) -> F { + hash_n_to_m(inputs, 1, pad)[0] +} diff --git a/src/main.rs b/src/main.rs index 125ed1d9..4f61c262 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,6 +30,7 @@ mod util; mod verifier; mod wire; mod witness; +mod hash; // 112 wire polys, 3 Z polys, 4 parts of quotient poly. const PROVER_POLYS: usize = 113 + 3 + 4; diff --git a/src/proof.rs b/src/proof.rs index 2b6e4acf..59283651 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -2,7 +2,7 @@ use crate::field::field::Field; use crate::target::Target; pub struct Hash { - elements: Vec, + pub(crate) elements: Vec, } pub struct HashTarget { diff --git a/src/prover.rs b/src/prover.rs index fdad16f3..081908a3 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -3,9 +3,10 @@ use crate::field::fft::{fft, ifft}; use crate::field::field::Field; use crate::generator::generate_partial_witness; use crate::proof::{Proof2, Hash}; -use crate::util::log2_ceil; +use crate::util::{log2_ceil, transpose}; use crate::wire::Wire; use crate::witness::PartialWitness; +use crate::hash::{hash_n_to_hash, hash_n_to_m}; pub(crate) fn prove( prover_data: &ProverOnlyCircuitData, @@ -23,10 +24,10 @@ pub(crate) fn prove( let wire_ldes = (0..num_wires) .map(|i| compute_wire_lde(i, &witness, common_data.degree, lde_size)) .collect::>(); - let wires_root = merkle_root_batch(wire_ldes); + let wires_root = merkle_root(wire_ldes); let z_ldes = todo!(); - let plonk_z_root = merkle_root_batch(z_ldes); + let plonk_z_root = merkle_root(z_ldes); let plonk_t_root = todo!(); @@ -40,11 +41,18 @@ pub(crate) fn prove( } } -fn merkle_root(vec: Vec) -> Hash { - todo!() -} - -fn merkle_root_batch(vecs: Vec>) -> Hash { +/// Given `n` vectors, each of length `l`, constructs a Merkle tree with `l` leaves, where each leaf +/// is a hash obtained by hashing a "leaf set" consisting of `n` elements. If `n <= 4`, this hashing +/// is skipped, as there is no need to compress leaf data. +fn merkle_root(vecs: Vec>) -> Hash { + let n = vecs.len(); + let mut vecs_t = transpose(&vecs); + let l = vecs_t.len(); + if n > 4 { + vecs_t = vecs_t.into_iter() + .map(|leaf_set| hash_n_to_hash(leaf_set, false).elements) + .collect(); + } todo!() } @@ -55,7 +63,11 @@ fn compute_wire_lde( lde_size: usize, ) -> Vec { let wire = (0..degree) - .map(|gate| witness.get_wire(Wire { gate, input })) + // Some gates do not use all wires, and we do not require that generators populate unused + // wires, so some wire values will not be set. We can set these to any value; here we + // arbitrary pick zero. Ideally we would verify that no constraints operate on these unset + // wires, but that isn't trivial. + .map(|gate| witness.try_get_wire(Wire { gate, input }).unwrap_or(F::ZERO)) .collect(); let mut coeffs = ifft(wire); for _ in 0..(lde_size - degree) { diff --git a/src/witness.rs b/src/witness.rs index 982d98d3..5f6e803b 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -38,6 +38,10 @@ impl PartialWitness { self.get_target(Target::Wire(wire)) } + pub fn try_get_wire(&self, wire: Wire) -> Option { + self.try_get_target(Target::Wire(wire)) + } + pub fn contains(&self, target: Target) -> bool { self.target_values.contains_key(&target) }