plonky2/plonky2/src/fri/prover.rs

194 lines
6.3 KiB
Rust
Raw Normal View History

use alloc::vec::Vec;
use itertools::Itertools;
2022-07-21 17:02:03 -04:00
use maybe_rayon::*;
use plonky2_field::extension::{flatten, unflatten, Extendable};
use plonky2_field::polynomial::{PolynomialCoeffs, PolynomialValues};
use plonky2_util::reverse_index_bits_in_place;
2021-07-15 07:40:41 -07:00
2021-09-30 06:56:32 +02:00
use crate::fri::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep};
use crate::fri::{FriConfig, FriParams};
use crate::hash::hash_types::{HashOut, RichField};
use crate::hash::merkle_tree::MerkleTree;
use crate::iop::challenger::Challenger;
2021-11-05 10:56:23 +01:00
use crate::plonk::config::{GenericConfig, Hasher};
use crate::plonk::plonk_common::reduce_with_powers;
2021-07-15 07:40:41 -07:00
use crate::timed;
use crate::util::timing::TimingTree;
2021-05-05 18:23:59 +02:00
/// Builds a FRI proof.
pub fn fri_proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
2021-11-05 10:56:23 +01:00
initial_merkle_trees: &[&MerkleTree<F, C::Hasher>],
2021-05-05 18:23:59 +02:00
// Coefficients of the polynomial on which the LDT is performed. Only the first `1/rate` coefficients are non-zero.
2021-07-21 08:26:56 -07:00
lde_polynomial_coeffs: PolynomialCoeffs<F::Extension>,
2021-05-05 18:23:59 +02:00
// Evaluation of the polynomial on the large domain.
2021-07-21 08:26:56 -07:00
lde_polynomial_values: PolynomialValues<F::Extension>,
2021-12-29 16:41:43 +01:00
challenger: &mut Challenger<F, C::Hasher>,
fri_params: &FriParams,
timing: &mut TimingTree,
) -> FriProof<F, C::Hasher, D> {
2022-01-08 23:44:12 -08:00
let n = lde_polynomial_values.len();
assert_eq!(lde_polynomial_coeffs.len(), n);
2021-05-05 18:23:59 +02:00
// Commit phase
2021-08-02 15:49:06 -07:00
let (trees, final_coeffs) = timed!(
timing,
"fold codewords in the commitment phase",
fri_committed_trees::<F, C, D>(
2021-08-02 15:49:06 -07:00
lde_polynomial_coeffs,
lde_polynomial_values,
challenger,
fri_params,
2021-08-02 15:49:06 -07:00
)
2021-05-05 18:23:59 +02:00
);
// PoW phase
2021-11-05 12:02:33 +01:00
let current_hash = challenger.get_hash();
2021-07-15 07:40:41 -07:00
let pow_witness = timed!(
timing,
"find proof-of-work witness",
fri_proof_of_work::<F, C, D>(current_hash, &fri_params.config)
2021-07-15 07:40:41 -07:00
);
2021-05-05 18:23:59 +02:00
// Query phase
let query_round_proofs =
fri_prover_query_rounds::<F, C, D>(initial_merkle_trees, &trees, challenger, n, fri_params);
2021-05-05 18:23:59 +02:00
2021-09-30 06:56:32 +02:00
FriProof {
2021-08-10 15:53:27 +02:00
commit_phase_merkle_caps: trees.iter().map(|t| t.cap.clone()).collect(),
2021-05-05 18:23:59 +02:00
query_round_proofs,
final_poly: final_coeffs,
pow_witness,
}
}
type FriCommitedTrees<F, C, const D: usize> = (
Vec<MerkleTree<F, <C as GenericConfig<D>>::Hasher>>,
PolynomialCoeffs<<F as Extendable<D>>::Extension>,
);
fn fri_committed_trees<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
2021-07-21 08:26:56 -07:00
mut coeffs: PolynomialCoeffs<F::Extension>,
mut values: PolynomialValues<F::Extension>,
2021-12-29 16:41:43 +01:00
challenger: &mut Challenger<F, C::Hasher>,
fri_params: &FriParams,
) -> FriCommitedTrees<F, C, D> {
2021-05-05 18:23:59 +02:00
let mut trees = Vec::new();
let mut shift = F::MULTIPLICATIVE_GROUP_GENERATOR;
for arity_bits in &fri_params.reduction_arity_bits {
let arity = 1 << arity_bits;
2021-05-05 18:23:59 +02:00
reverse_index_bits_in_place(&mut values.values);
let chunked_values = values
.values
.par_chunks(arity)
.map(|chunk: &[F::Extension]| flatten(chunk))
.collect();
let tree = MerkleTree::<F, C::Hasher>::new(chunked_values, fri_params.config.cap_height);
2021-05-05 18:23:59 +02:00
2021-08-10 15:53:27 +02:00
challenger.observe_cap(&tree.cap);
2021-05-05 18:23:59 +02:00
trees.push(tree);
2021-11-05 12:02:33 +01:00
let beta = challenger.get_extension_challenge::<D>();
2021-05-05 18:23:59 +02:00
// P(x) = sum_{i<r} x^i * P_i(x^r) becomes sum_{i<r} beta^i * P_i(x).
coeffs = PolynomialCoeffs::new(
coeffs
.coeffs
2021-08-03 07:39:36 -07:00
.par_chunks_exact(arity)
2021-05-05 18:23:59 +02:00
.map(|chunk| reduce_with_powers(chunk, beta))
.collect::<Vec<_>>(),
);
shift = shift.exp_u64(arity as u64);
values = coeffs.coset_fft(shift.into())
2021-05-05 18:23:59 +02:00
}
2021-08-15 23:45:38 -07:00
// The coefficients being removed here should always be zero.
coeffs
.coeffs
.truncate(coeffs.len() >> fri_params.config.rate_bits);
2021-05-18 15:22:06 +02:00
challenger.observe_extension_elements(&coeffs.coeffs);
2021-05-05 18:23:59 +02:00
(trees, coeffs)
}
fn fri_proof_of_work<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
2021-11-05 10:56:23 +01:00
current_hash: HashOut<F>,
config: &FriConfig,
) -> F {
(0..=F::NEG_ONE.to_canonical_u64())
2022-07-21 17:01:21 -04:00
.into_par_iter()
2021-07-15 07:40:41 -07:00
.find_any(|&i| {
C::InnerHasher::hash_no_pad(
&current_hash
2021-05-05 18:23:59 +02:00
.elements
.iter()
.copied()
.chain(Some(F::from_canonical_u64(i)))
.collect_vec(),
2021-05-05 18:23:59 +02:00
)
2021-11-05 10:56:23 +01:00
.elements[0]
.to_canonical_u64()
.leading_zeros()
2021-07-21 13:05:32 -07:00
>= config.proof_of_work_bits + (64 - F::order().bits()) as u32
2021-05-05 18:23:59 +02:00
})
.map(F::from_canonical_u64)
2021-07-15 07:40:41 -07:00
.expect("Proof of work failed. This is highly unlikely!")
2021-05-05 18:23:59 +02:00
}
fn fri_prover_query_rounds<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
2021-11-05 10:56:23 +01:00
initial_merkle_trees: &[&MerkleTree<F, C::Hasher>],
trees: &[MerkleTree<F, C::Hasher>],
2021-12-29 16:41:43 +01:00
challenger: &mut Challenger<F, C::Hasher>,
2021-05-05 18:23:59 +02:00
n: usize,
fri_params: &FriParams,
2021-11-05 10:56:23 +01:00
) -> Vec<FriQueryRound<F, C::Hasher, D>> {
challenger
.get_n_challenges(fri_params.config.num_query_rounds)
.into_par_iter()
.map(|rand| {
let x_index = rand.to_canonical_u64() as usize % n;
fri_prover_query_round::<F, C, D>(initial_merkle_trees, trees, x_index, fri_params)
})
2021-05-05 18:23:59 +02:00
.collect()
}
fn fri_prover_query_round<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
2021-11-05 10:56:23 +01:00
initial_merkle_trees: &[&MerkleTree<F, C::Hasher>],
trees: &[MerkleTree<F, C::Hasher>],
mut x_index: usize,
fri_params: &FriParams,
2021-11-05 10:56:23 +01:00
) -> FriQueryRound<F, C::Hasher, D> {
2021-05-05 18:23:59 +02:00
let mut query_steps = Vec::new();
let initial_proof = initial_merkle_trees
.iter()
.map(|t| (t.get(x_index).to_vec(), t.prove(x_index)))
.collect::<Vec<_>>();
for (i, tree) in trees.iter().enumerate() {
let arity_bits = fri_params.reduction_arity_bits[i];
2021-08-10 15:53:27 +02:00
let evals = unflatten(tree.get(x_index >> arity_bits));
2021-05-05 18:23:59 +02:00
let merkle_proof = tree.prove(x_index >> arity_bits);
query_steps.push(FriQueryStep {
evals,
merkle_proof,
});
x_index >>= arity_bits;
}
FriQueryRound {
initial_trees_proof: FriInitialTreeProof {
evals_proofs: initial_proof,
},
steps: query_steps,
}
}