2022-11-02 19:59:12 -07:00
|
|
|
use alloc::vec::Vec;
|
|
|
|
|
|
2022-01-18 12:51:04 -08:00
|
|
|
use itertools::Itertools;
|
2022-07-21 17:02:03 -04:00
|
|
|
use maybe_rayon::*;
|
2022-06-27 07:18:21 -07:00
|
|
|
use plonky2_field::extension::{flatten, unflatten, Extendable};
|
2021-12-28 11:51:13 -08:00
|
|
|
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};
|
2022-01-02 11:26:26 -08:00
|
|
|
use crate::fri::{FriConfig, FriParams};
|
2021-12-28 11:51:13 -08:00
|
|
|
use crate::hash::hash_types::{HashOut, RichField};
|
2021-07-29 22:00:29 -07:00
|
|
|
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};
|
2021-07-29 22:00:29 -07:00
|
|
|
use crate::plonk::plonk_common::reduce_with_powers;
|
2021-07-15 07:40:41 -07:00
|
|
|
use crate::timed;
|
2021-08-02 10:38:09 -07:00
|
|
|
use crate::util::timing::TimingTree;
|
2021-05-05 18:23:59 +02:00
|
|
|
|
|
|
|
|
/// Builds a FRI proof.
|
2021-12-28 11:51:13 -08:00
|
|
|
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>,
|
2022-01-02 11:26:26 -08:00
|
|
|
fri_params: &FriParams,
|
2021-08-02 10:38:09 -07:00
|
|
|
timing: &mut TimingTree,
|
2022-11-02 17:50:31 -07:00
|
|
|
) -> 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",
|
2022-01-02 11:26:26 -08:00
|
|
|
fri_committed_trees::<F, C, D>(
|
2021-08-02 15:49:06 -07:00
|
|
|
lde_polynomial_coeffs,
|
|
|
|
|
lde_polynomial_values,
|
|
|
|
|
challenger,
|
2022-01-02 11:26:26 -08:00
|
|
|
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!(
|
2021-08-02 10:38:09 -07:00
|
|
|
timing,
|
Refactor recursion tests (#285)
* Refactor recursion tests
E.g. the main part of `test_recursive_recursive_verifier` is now
```rust
let (proof, vd, cd) = dummy_proof::<F, D>(&config, 8_000)?;
let (proof, vd, cd) = recursive_proof(proof, vd, cd, &config, &config, false)?;
let (proof, _vd, cd) = recursive_proof(proof, vd, cd, &config, &config, true)?;
```
Also adds a new `test_size_optimized_recursion` to see how small we can make the final proof in a recursion chain. The final proof is ~74kb (depending on compression luck) and takes ~20s to prove on my M1 (depending on PoW luck).
* Refactor serialization
* Don't log timestamps
2021-10-05 08:36:24 -07:00
|
|
|
"find proof-of-work witness",
|
2022-01-02 11:26:26 -08:00
|
|
|
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
|
Automatically select FRI reduction arities (#282)
* Automatically select FRI reduction arities
This way when a proof's degree changes, we won't need to manually update the `FriConfig`s of any recursive proofs on top of it.
For now I've added two methods of selecting arities. The first, `ConstantArityBits`, just applies a fixed reduciton arity until the degree has shrunk below a certain threshold. The second, `MinSize`, searches for the sequence of arities that minimizes proof size.
Note that this optimization is approximate -- e.g. it doesn't account for the effect of compression, and doesn't count some minor contributions to proof size, like the Merkle roots from the commit phase. It also assumes we're not using Merkle caps in serialized proofs, and that we're inferring one of the evaluations, even though we haven't made those changes yet.
I think we should generally use `ConstantArityBits` for proofs that we will recurse on, since using a single arity tends to be more recursion-friendly. We could use `MinSize` for generating final bridge proofs, since we won't do further recursion on top of those.
* Fix tests
* Feedback
2021-10-04 13:52:05 -07:00
|
|
|
let query_round_proofs =
|
2022-01-02 11:26:26 -08:00
|
|
|
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,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 17:50:31 -07:00
|
|
|
type FriCommitedTrees<F, C, const D: usize> = (
|
|
|
|
|
Vec<MerkleTree<F, <C as GenericConfig<D>>::Hasher>>,
|
|
|
|
|
PolynomialCoeffs<<F as Extendable<D>>::Extension>,
|
|
|
|
|
);
|
|
|
|
|
|
2021-12-28 11:51:13 -08:00
|
|
|
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>,
|
2022-01-02 11:26:26 -08:00
|
|
|
fri_params: &FriParams,
|
2022-11-02 17:50:31 -07:00
|
|
|
) -> FriCommitedTrees<F, C, D> {
|
2021-05-05 18:23:59 +02:00
|
|
|
let mut trees = Vec::new();
|
|
|
|
|
|
|
|
|
|
let mut shift = F::MULTIPLICATIVE_GROUP_GENERATOR;
|
2022-01-23 23:27:26 -08:00
|
|
|
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);
|
2021-08-12 07:27:33 -07:00
|
|
|
let chunked_values = values
|
|
|
|
|
.values
|
|
|
|
|
.par_chunks(arity)
|
|
|
|
|
.map(|chunk: &[F::Extension]| flatten(chunk))
|
|
|
|
|
.collect();
|
2022-01-02 11:26:26 -08:00
|
|
|
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<_>>(),
|
|
|
|
|
);
|
2021-09-05 10:27:11 -07:00
|
|
|
shift = shift.exp_u64(arity as u64);
|
2021-07-21 08:26:41 -07:00
|
|
|
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.
|
2022-01-02 11:26:26 -08:00
|
|
|
coeffs
|
|
|
|
|
.coeffs
|
|
|
|
|
.truncate(coeffs.len() >> fri_params.config.rate_bits);
|
2021-08-14 10:01:10 -07:00
|
|
|
|
2021-05-18 15:22:06 +02:00
|
|
|
challenger.observe_extension_elements(&coeffs.coeffs);
|
2021-05-05 18:23:59 +02:00
|
|
|
(trees, coeffs)
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-28 11:51:13 -08:00
|
|
|
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 {
|
2021-08-18 00:53:20 -07:00
|
|
|
(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| {
|
2022-02-04 13:08:57 -08:00
|
|
|
C::InnerHasher::hash_no_pad(
|
2022-01-18 12:51:04 -08:00
|
|
|
¤t_hash
|
2021-05-05 18:23:59 +02:00
|
|
|
.elements
|
|
|
|
|
.iter()
|
|
|
|
|
.copied()
|
|
|
|
|
.chain(Some(F::from_canonical_u64(i)))
|
2022-01-18 12:51:04 -08:00
|
|
|
.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
|
|
|
}
|
|
|
|
|
|
2021-12-28 11:51:13 -08: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,
|
2022-01-02 11:26:26 -08:00
|
|
|
fri_params: &FriParams,
|
2021-11-05 10:56:23 +01:00
|
|
|
) -> Vec<FriQueryRound<F, C::Hasher, D>> {
|
2022-09-04 22:46:16 -07:00
|
|
|
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)
|
2022-01-02 11:26:26 -08:00
|
|
|
})
|
2021-05-05 18:23:59 +02:00
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-28 11:51:13 -08:00
|
|
|
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>],
|
2022-09-04 22:46:16 -07:00
|
|
|
mut x_index: usize,
|
2022-01-02 11:26:26 -08:00
|
|
|
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() {
|
2022-01-02 11:26:26 -08:00
|
|
|
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,
|
|
|
|
|
}
|
|
|
|
|
}
|