diff --git a/src/fri.rs b/src/fri.rs index 90cceef6..01813749 100644 --- a/src/fri.rs +++ b/src/fri.rs @@ -1,11 +1,11 @@ use crate::field::fft::fft; use crate::field::field::Field; -use crate::hash::{compress, hash_n_to_hash}; +use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; -use crate::proof::{FriEvaluations, FriMerkleProofs, FriProof, FriQueryRound, Hash}; +use crate::proof::{FriEvaluations, FriMerkleProofs, FriProof, FriQueryRound}; use crate::util::log2_strict; use anyhow::{ensure, Result}; @@ -14,7 +14,7 @@ use anyhow::{ensure, Result}; const EPSILON: f64 = 0.01; struct FriConfig { - proof_of_work_bits: usize, + proof_of_work_bits: u32, /// The arity of each FRI reduction step, expressed (i.e. the log2 of the actual arity). /// For example, `[3, 2, 1]` would describe a FRI reduction tree with 8-to-1 reduction, then @@ -67,17 +67,41 @@ fn fri_proof( let n = polynomial_values.values.len(); assert_eq!(polynomial_coeffs.coeffs.len(), n); + // Commit phase + let (trees, final_coeffs) = + fri_committed_trees(polynomial_coeffs, polynomial_values, challenger, config); + + let current_hash = challenger.get_challenge(); + let pow_witness = fri_proof_of_work(current_hash, config); + + // Query phase + let query_round_proofs = fri_query_rounds(&trees, challenger, n, config); + + FriProof { + commit_phase_merkle_roots: trees.iter().map(|t| t.root).collect(), + // TODO: Fix this + initial_merkle_proofs: vec![], + query_round_proofs, + final_poly: final_coeffs, + pow_witness, + } +} + +fn fri_committed_trees( + polynomial_coeffs: &PolynomialCoeffs, + polynomial_values: &PolynomialValues, + challenger: &mut Challenger, + config: &FriConfig, +) -> (Vec>, PolynomialCoeffs) { let mut trees = vec![MerkleTree::new( polynomial_values.values.iter().map(|&v| vec![v]).collect(), true, )]; - let mut root = trees[0].root; let mut coeffs = polynomial_coeffs.clone(); let mut values; - challenger.observe_hash(&root); + challenger.observe_hash(&trees[0].root); - // Commit phase for _ in 0..config.reduction_count { let beta = challenger.get_challenge(); // P(x) = P_0(x^2) + xP_1(x^2) becomes P_0(x) + beta*P_1(x) @@ -94,8 +118,27 @@ fn fri_proof( challenger.observe_hash(&tree.root); trees.push(tree); } + (trees, coeffs) +} - // Query phase +fn fri_proof_of_work(current_hash: F, config: &FriConfig) -> F { + (0u64..) + .find(|&i| { + hash_n_to_1(vec![current_hash, F::from_canonical_u64(i)], false) + .to_canonical_u64() + .leading_zeros() + >= config.proof_of_work_bits + }) + .map(F::from_canonical_u64) + .expect("Proof of work failed.") +} + +fn fri_query_rounds( + trees: &[MerkleTree], + challenger: &mut Challenger, + n: usize, + config: &FriConfig, +) -> Vec> { let mut query_round_proofs = Vec::new(); for _ in 0..config.num_query_rounds { let mut merkle_proofs = FriMerkleProofs { proofs: Vec::new() }; @@ -131,13 +174,7 @@ fn fri_proof( merkle_proofs, }); } - - FriProof { - commit_phase_merkle_roots: trees.iter().map(|t| t.root).collect(), - initial_merkle_proofs: vec![], - query_round_proofs, - final_poly: coeffs, - } + query_round_proofs } /// Computes P'(x^2) from P_even(x) and P_odd(x), where P' is the FRI reduced polynomial, @@ -167,6 +204,15 @@ fn verify_fri_proof( .collect::>(); challenger.observe_hash(proof.commit_phase_merkle_roots.last().unwrap()); + // Check PoW. + ensure!( + hash_n_to_1(vec![challenger.get_challenge(), proof.pow_witness], false) + .to_canonical_u64() + .leading_zeros() + >= config.proof_of_work_bits, + "Invalid proof of work witness." + ); + // Check that parameters are coherent. ensure!( config.num_query_rounds == proof.query_round_proofs.len(), @@ -228,7 +274,7 @@ fn verify_fri_proof( last_e_x_minus, betas[config.reduction_count - 1], ); - // Final check of FRI. After all the reduction, we check that the final polynomial is equal + // Final check of FRI. After all the reductions, we check that the final polynomial is equal // to the one sent by the prover. ensure!( proof.final_poly.eval(subgroup_x.square()) == purported_eval, @@ -260,7 +306,7 @@ mod tests { let config = FriConfig { reduction_count, num_query_rounds, - proof_of_work_bits: 0, + proof_of_work_bits: 2, reduction_arity_bits: Vec::new(), }; let mut challenger = Challenger::new(); diff --git a/src/merkle_proofs.rs b/src/merkle_proofs.rs index 9fe268bc..edcdee42 100644 --- a/src/merkle_proofs.rs +++ b/src/merkle_proofs.rs @@ -5,7 +5,6 @@ use crate::hash::GMIMC_ROUNDS; use crate::hash::{compress, hash_or_noop}; use crate::proof::{Hash, HashTarget}; use crate::target::Target; -use crate::util::reverse_index_bits; use crate::wire::Wire; use anyhow::{ensure, Result}; diff --git a/src/merkle_tree.rs b/src/merkle_tree.rs index 2bd6711f..f9a6d8f3 100644 --- a/src/merkle_tree.rs +++ b/src/merkle_tree.rs @@ -1,5 +1,5 @@ use crate::field::field::Field; -use crate::hash::{compress, hash_n_to_hash, hash_or_noop}; +use crate::hash::{compress, hash_or_noop}; use crate::merkle_proofs::MerkleProof; use crate::proof::Hash; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; @@ -33,11 +33,11 @@ impl MerkleTree { if l.len() == 1 { break; } - layers.push( - l.chunks(2) - .map(|chunk| compress(chunk[0], chunk[1])) - .collect::>(), - ); + let next_layer = l + .chunks(2) + .map(|chunk| compress(chunk[0], chunk[1])) + .collect::>(); + layers.push(next_layer); } let root = layers.pop().unwrap()[0]; Self { diff --git a/src/proof.rs b/src/proof.rs index 35a64527..9e41bcd9 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -110,6 +110,8 @@ pub struct FriProof { pub query_round_proofs: Vec>, /// The final polynomial in coefficient form. pub final_poly: PolynomialCoeffs, + /// Witness showing that the prover did PoW. + pub pow_witness: F, } /// Represents a single FRI query, i.e. a path through the reduction tree.