mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-10 09:43:09 +00:00
Add compressed FRI proof type using a HashMap
This commit is contained in:
parent
3f22663296
commit
a97b9a7112
@ -3,7 +3,7 @@ use rayon::prelude::*;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::fft::FftRootTable;
|
||||
use crate::field::field_types::{Field, RichField};
|
||||
use crate::fri::proof::FriProof;
|
||||
use crate::fri::proof::DecompressedFriProof;
|
||||
use crate::fri::prover::fri_proof;
|
||||
use crate::hash::merkle_tree::MerkleTree;
|
||||
use crate::iop::challenger::Challenger;
|
||||
@ -128,7 +128,7 @@ impl<F: RichField> PolynomialBatchCommitment<F> {
|
||||
challenger: &mut Challenger<F>,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
timing: &mut TimingTree,
|
||||
) -> (FriProof<F, D>, OpeningSet<F, D>)
|
||||
) -> (DecompressedFriProof<F, D>, OpeningSet<F, D>)
|
||||
where
|
||||
F: RichField + Extendable<D>,
|
||||
{
|
||||
|
||||
131
src/fri/proof.rs
131
src/fri/proof.rs
@ -1,3 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use itertools::izip;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -77,9 +79,24 @@ pub struct FriQueryRoundTarget<const D: usize> {
|
||||
pub steps: Vec<FriQueryStepTarget<D>>,
|
||||
}
|
||||
|
||||
/// Compressed proofs for FRI query rounds.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||
#[serde(bound = "")]
|
||||
pub struct FriProof<F: Extendable<D>, const D: usize> {
|
||||
pub struct CompressedFriQueryRounds<F: Extendable<D>, const D: usize> {
|
||||
pub initial_trees_proofs: HashMap<usize, FriInitialTreeProof<F>>,
|
||||
pub steps: Vec<HashMap<usize, FriQueryStep<F, D>>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||
#[serde(bound = "")]
|
||||
pub enum FriProof<F: Extendable<D>, const D: usize> {
|
||||
Decompressed(DecompressedFriProof<F, D>),
|
||||
Compressed(CompressedFriProof<F, D>),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||
#[serde(bound = "")]
|
||||
pub struct DecompressedFriProof<F: Extendable<D>, const D: usize> {
|
||||
/// A Merkle cap for each reduced polynomial in the commit phase.
|
||||
pub commit_phase_merkle_caps: Vec<MerkleCap<F>>,
|
||||
/// Query rounds proofs
|
||||
@ -88,10 +105,9 @@ pub struct FriProof<F: Extendable<D>, const D: usize> {
|
||||
pub final_poly: PolynomialCoeffs<F::Extension>,
|
||||
/// Witness showing that the prover did PoW.
|
||||
pub pow_witness: F,
|
||||
/// Flag set to true if path compression has been applied to the proof's Merkle proofs.
|
||||
pub is_compressed: bool,
|
||||
}
|
||||
|
||||
/// Corresponds to `DecompressedFriProof`.
|
||||
pub struct FriProofTarget<const D: usize> {
|
||||
pub commit_phase_merkle_caps: Vec<MerkleCapTarget>,
|
||||
pub query_round_proofs: Vec<FriQueryRoundTarget<D>>,
|
||||
@ -99,15 +115,30 @@ pub struct FriProofTarget<const D: usize> {
|
||||
pub pow_witness: Target,
|
||||
}
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> FriProof<F, D> {
|
||||
/// Compress all the Merkle paths in the FRI proof.
|
||||
pub fn compress(self, indices: &[usize], common_data: &CommonCircuitData<F, D>) -> Self {
|
||||
if self.is_compressed {
|
||||
panic!("Proof is already compressed.");
|
||||
}
|
||||
let FriProof {
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||
#[serde(bound = "")]
|
||||
pub struct CompressedFriProof<F: Extendable<D>, const D: usize> {
|
||||
/// A Merkle cap for each reduced polynomial in the commit phase.
|
||||
pub commit_phase_merkle_caps: Vec<MerkleCap<F>>,
|
||||
/// Query rounds proofs
|
||||
pub query_round_proofs: CompressedFriQueryRounds<F, D>,
|
||||
/// The final polynomial in coefficient form.
|
||||
pub final_poly: PolynomialCoeffs<F::Extension>,
|
||||
/// Witness showing that the prover did PoW.
|
||||
pub pow_witness: F,
|
||||
}
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> DecompressedFriProof<F, D> {
|
||||
/// Compress all the Merkle paths in the FRI proof and remove duplicate indices.
|
||||
pub fn compress(
|
||||
self,
|
||||
indices: &[usize],
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
with_indices: bool,
|
||||
) -> CompressedFriProof<F, D> {
|
||||
let DecompressedFriProof {
|
||||
commit_phase_merkle_caps,
|
||||
mut query_round_proofs,
|
||||
query_round_proofs,
|
||||
final_poly,
|
||||
pow_witness,
|
||||
..
|
||||
@ -157,9 +188,14 @@ impl<F: RichField + Extendable<D>, const D: usize> FriProof<F, D> {
|
||||
.map(|(is, ps)| compress_merkle_proofs(cap_height, is, &ps))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut compressed_query_proofs = CompressedFriQueryRounds {
|
||||
initial_trees_proofs: HashMap::new(),
|
||||
steps: vec![HashMap::new(); num_reductions],
|
||||
};
|
||||
|
||||
// Replace the query round proofs with the compressed versions.
|
||||
for (i, qrp) in query_round_proofs.iter_mut().enumerate() {
|
||||
qrp.initial_trees_proof = FriInitialTreeProof {
|
||||
for (i, (mut index, qrp)) in indices.iter().cloned().zip(&query_round_proofs).enumerate() {
|
||||
let initial_proof = FriInitialTreeProof {
|
||||
evals_proofs: (0..num_initial_trees)
|
||||
.map(|j| {
|
||||
(
|
||||
@ -169,31 +205,38 @@ impl<F: RichField + Extendable<D>, const D: usize> FriProof<F, D> {
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
qrp.steps = (0..num_reductions)
|
||||
.map(|j| FriQueryStep {
|
||||
compressed_query_proofs
|
||||
.initial_trees_proofs
|
||||
.insert(index, initial_proof);
|
||||
for j in 0..num_reductions {
|
||||
index >>= reduction_arity_bits[i];
|
||||
let query_step = FriQueryStep {
|
||||
evals: steps_evals[j][i].clone(),
|
||||
merkle_proof: steps_proofs[j][i].clone(),
|
||||
})
|
||||
.collect();
|
||||
};
|
||||
compressed_query_proofs.steps[j].insert(index, query_step);
|
||||
}
|
||||
}
|
||||
|
||||
FriProof {
|
||||
CompressedFriProof {
|
||||
commit_phase_merkle_caps,
|
||||
query_round_proofs,
|
||||
query_round_proofs: compressed_query_proofs,
|
||||
final_poly,
|
||||
pow_witness,
|
||||
is_compressed: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Decompress all the Merkle paths in the FRI proof.
|
||||
pub fn decompress(self, indices: &[usize], common_data: &CommonCircuitData<F, D>) -> Self {
|
||||
if !self.is_compressed {
|
||||
panic!("Proof is not compressed.");
|
||||
}
|
||||
let FriProof {
|
||||
impl<F: RichField + Extendable<D>, const D: usize> CompressedFriProof<F, D> {
|
||||
/// Decompress all the Merkle paths in the FRI proof and add duplicate indices.
|
||||
pub fn decompress(
|
||||
self,
|
||||
indices: &[usize],
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> DecompressedFriProof<F, D> {
|
||||
let CompressedFriProof {
|
||||
commit_phase_merkle_caps,
|
||||
mut query_round_proofs,
|
||||
query_round_proofs,
|
||||
final_poly,
|
||||
pow_witness,
|
||||
..
|
||||
@ -201,7 +244,13 @@ impl<F: RichField + Extendable<D>, const D: usize> FriProof<F, D> {
|
||||
let cap_height = common_data.config.cap_height;
|
||||
let reduction_arity_bits = &common_data.config.fri_config.reduction_arity_bits;
|
||||
let num_reductions = reduction_arity_bits.len();
|
||||
let num_initial_trees = query_round_proofs[0].initial_trees_proof.evals_proofs.len();
|
||||
let num_initial_trees = query_round_proofs
|
||||
.initial_trees_proofs
|
||||
.values()
|
||||
.next()
|
||||
.unwrap()
|
||||
.evals_proofs
|
||||
.len();
|
||||
|
||||
// "Transpose" the query round proofs, so that information for each Merkle tree is collected together.
|
||||
let mut initial_trees_indices = vec![vec![]; num_initial_trees];
|
||||
@ -219,11 +268,8 @@ impl<F: RichField + Extendable<D>, const D: usize> FriProof<F, D> {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (mut index, qrp) in indices.iter().cloned().zip(&query_round_proofs) {
|
||||
let FriQueryRound {
|
||||
initial_trees_proof,
|
||||
steps,
|
||||
} = qrp.clone();
|
||||
for mut index in indices.iter().cloned() {
|
||||
let initial_trees_proof = query_round_proofs.initial_trees_proofs[&index].clone();
|
||||
for (i, (leaves_data, proof)) in
|
||||
initial_trees_proof.evals_proofs.into_iter().enumerate()
|
||||
{
|
||||
@ -231,8 +277,9 @@ impl<F: RichField + Extendable<D>, const D: usize> FriProof<F, D> {
|
||||
initial_trees_leaves[i].push(leaves_data);
|
||||
initial_trees_proofs[i].push(proof);
|
||||
}
|
||||
for (i, query_step) in steps.into_iter().enumerate() {
|
||||
for i in 0..num_reductions {
|
||||
index >>= reduction_arity_bits[i];
|
||||
let query_step = query_round_proofs.steps[i][&index].clone();
|
||||
steps_indices[i].push(index);
|
||||
steps_evals[i].push(flatten(&query_step.evals));
|
||||
steps_proofs[i].push(query_step.merkle_proof);
|
||||
@ -251,9 +298,10 @@ impl<F: RichField + Extendable<D>, const D: usize> FriProof<F, D> {
|
||||
.map(|(ls, is, ps, h)| decompress_merkle_proofs(ls, is, &ps, h, cap_height))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut decompressed_query_proofs = Vec::with_capacity(num_reductions);
|
||||
// Replace the query round proofs with the decompressed versions.
|
||||
for (i, qrp) in query_round_proofs.iter_mut().enumerate() {
|
||||
qrp.initial_trees_proof = FriInitialTreeProof {
|
||||
for i in 0..num_reductions {
|
||||
let initial_trees_proof = FriInitialTreeProof {
|
||||
evals_proofs: (0..num_initial_trees)
|
||||
.map(|j| {
|
||||
(
|
||||
@ -263,20 +311,23 @@ impl<F: RichField + Extendable<D>, const D: usize> FriProof<F, D> {
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
qrp.steps = (0..num_reductions)
|
||||
let steps = (0..num_reductions)
|
||||
.map(|j| FriQueryStep {
|
||||
evals: unflatten(&steps_evals[j][i]),
|
||||
merkle_proof: steps_proofs[j][i].clone(),
|
||||
})
|
||||
.collect();
|
||||
decompressed_query_proofs.push(FriQueryRound {
|
||||
initial_trees_proof,
|
||||
steps,
|
||||
})
|
||||
}
|
||||
|
||||
FriProof {
|
||||
DecompressedFriProof {
|
||||
commit_phase_merkle_caps,
|
||||
query_round_proofs,
|
||||
query_round_proofs: decompressed_query_proofs,
|
||||
final_poly,
|
||||
pow_witness,
|
||||
is_compressed: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ use rayon::prelude::*;
|
||||
|
||||
use crate::field::extension_field::{flatten, unflatten, Extendable};
|
||||
use crate::field::field_types::RichField;
|
||||
use crate::fri::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep};
|
||||
use crate::fri::proof::{DecompressedFriProof, FriInitialTreeProof, FriQueryRound, FriQueryStep};
|
||||
use crate::fri::FriConfig;
|
||||
use crate::hash::hash_types::HashOut;
|
||||
use crate::hash::hashing::hash_n_to_1;
|
||||
@ -25,7 +25,7 @@ pub fn fri_proof<F: RichField + Extendable<D>, const D: usize>(
|
||||
challenger: &mut Challenger<F>,
|
||||
config: &CircuitConfig,
|
||||
timing: &mut TimingTree,
|
||||
) -> FriProof<F, D> {
|
||||
) -> DecompressedFriProof<F, D> {
|
||||
let n = lde_polynomial_values.values.len();
|
||||
assert_eq!(lde_polynomial_coeffs.coeffs.len(), n);
|
||||
|
||||
@ -58,12 +58,11 @@ pub fn fri_proof<F: RichField + Extendable<D>, const D: usize>(
|
||||
&config.fri_config,
|
||||
);
|
||||
|
||||
FriProof {
|
||||
DecompressedFriProof {
|
||||
commit_phase_merkle_caps: trees.iter().map(|t| t.cap.clone()).collect(),
|
||||
query_round_proofs,
|
||||
final_poly: final_coeffs,
|
||||
pow_witness,
|
||||
is_compressed: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ use anyhow::{ensure, Result};
|
||||
use crate::field::extension_field::{flatten, Extendable, FieldExtension};
|
||||
use crate::field::field_types::{Field, RichField};
|
||||
use crate::field::interpolation::{barycentric_weights, interpolate, interpolate2};
|
||||
use crate::fri::proof::{FriInitialTreeProof, FriProof, FriQueryRound};
|
||||
use crate::fri::proof::{DecompressedFriProof, FriInitialTreeProof, FriQueryRound};
|
||||
use crate::fri::FriConfig;
|
||||
use crate::hash::merkle_proofs::verify_merkle_proof;
|
||||
use crate::hash::merkle_tree::MerkleCap;
|
||||
@ -60,7 +60,7 @@ pub(crate) fn verify_fri_proof<F: RichField + Extendable<D>, const D: usize>(
|
||||
os: &OpeningSet<F, D>,
|
||||
challenges: &ProofChallenges<F, D>,
|
||||
initial_merkle_caps: &[MerkleCap<F>],
|
||||
proof: &FriProof<F, D>,
|
||||
proof: &DecompressedFriProof<F, D>,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> Result<()> {
|
||||
let config = &common_data.config;
|
||||
@ -220,7 +220,7 @@ fn fri_verifier_query_round<F: RichField + Extendable<D>, const D: usize>(
|
||||
challenges: &ProofChallenges<F, D>,
|
||||
precomputed_reduced_evals: PrecomputedReducedEvals<F, D>,
|
||||
initial_merkle_caps: &[MerkleCap<F>],
|
||||
proof: &FriProof<F, D>,
|
||||
proof: &DecompressedFriProof<F, D>,
|
||||
mut x_index: usize,
|
||||
n: usize,
|
||||
round_proof: &FriQueryRound<F, D>,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field_types::RichField;
|
||||
use crate::fri::proof::FriProof;
|
||||
use crate::hash::hashing::hash_n_to_1;
|
||||
use crate::iop::challenger::Challenger;
|
||||
use crate::plonk::circuit_data::CommonCircuitData;
|
||||
@ -44,18 +45,24 @@ impl<F: RichField + Extendable<D>, const D: usize> ProofWithPublicInputs<F, D> {
|
||||
let fri_alpha = challenger.get_extension_challenge();
|
||||
|
||||
// Recover the random betas used in the FRI reductions.
|
||||
let fri_betas = self
|
||||
.proof
|
||||
.opening_proof
|
||||
.commit_phase_merkle_caps
|
||||
.iter()
|
||||
.map(|cap| {
|
||||
challenger.observe_cap(cap);
|
||||
challenger.get_extension_challenge()
|
||||
})
|
||||
.collect();
|
||||
let fri_betas = match &self.proof.opening_proof {
|
||||
FriProof::Decompressed(p) => &p.commit_phase_merkle_caps,
|
||||
FriProof::Compressed(p) => &p.commit_phase_merkle_caps,
|
||||
}
|
||||
.iter()
|
||||
.map(|cap| {
|
||||
challenger.observe_cap(cap);
|
||||
challenger.get_extension_challenge()
|
||||
})
|
||||
.collect();
|
||||
|
||||
challenger.observe_extension_elements(&self.proof.opening_proof.final_poly.coeffs);
|
||||
challenger.observe_extension_elements(
|
||||
&match &self.proof.opening_proof {
|
||||
FriProof::Decompressed(p) => &p.final_poly,
|
||||
FriProof::Compressed(p) => &p.final_poly,
|
||||
}
|
||||
.coeffs,
|
||||
);
|
||||
|
||||
let fri_pow_response = hash_n_to_1(
|
||||
challenger
|
||||
@ -63,7 +70,10 @@ impl<F: RichField + Extendable<D>, const D: usize> ProofWithPublicInputs<F, D> {
|
||||
.elements
|
||||
.iter()
|
||||
.copied()
|
||||
.chain(Some(self.proof.opening_proof.pow_witness))
|
||||
.chain(Some(match &self.proof.opening_proof {
|
||||
FriProof::Decompressed(p) => p.pow_witness,
|
||||
FriProof::Compressed(p) => p.pow_witness,
|
||||
}))
|
||||
.collect(),
|
||||
false,
|
||||
);
|
||||
|
||||
@ -5,7 +5,7 @@ use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field_types::RichField;
|
||||
use crate::fri::commitment::PolynomialBatchCommitment;
|
||||
use crate::fri::proof::{FriProof, FriProofTarget};
|
||||
use crate::fri::proof::{DecompressedFriProof, FriProof, FriProofTarget};
|
||||
use crate::hash::hash_types::{HashOut, MerkleCapTarget};
|
||||
use crate::hash::hashing::hash_n_to_hash;
|
||||
use crate::hash::merkle_tree::MerkleCap;
|
||||
@ -38,18 +38,24 @@ pub struct ProofTarget<const D: usize> {
|
||||
impl<F: RichField + Extendable<D>, const D: usize> Proof<F, D> {
|
||||
/// Returns `true` iff the opening proof is compressed.
|
||||
pub fn is_compressed(&self) -> bool {
|
||||
self.opening_proof.is_compressed
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Compress the opening proof.
|
||||
pub fn compress(mut self, indices: &[usize], common_data: &CommonCircuitData<F, D>) -> Self {
|
||||
self.opening_proof = self.opening_proof.compress(&indices, common_data);
|
||||
self.opening_proof = FriProof::Compressed(match self.opening_proof {
|
||||
FriProof::Decompressed(p) => p.compress(indices, common_data, true),
|
||||
FriProof::Compressed(p) => p,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Decompress the opening proof.
|
||||
pub fn decompress(mut self, indices: &[usize], common_data: &CommonCircuitData<F, D>) -> Self {
|
||||
self.opening_proof = self.opening_proof.decompress(&indices, common_data);
|
||||
self.opening_proof = FriProof::Decompressed(match self.opening_proof {
|
||||
FriProof::Decompressed(p) => p,
|
||||
FriProof::Compressed(p) => p.decompress(indices, common_data),
|
||||
});
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -64,7 +70,7 @@ pub struct ProofWithPublicInputs<F: RichField + Extendable<D>, const D: usize> {
|
||||
impl<F: RichField + Extendable<D>, const D: usize> ProofWithPublicInputs<F, D> {
|
||||
/// Returns `true` iff the opening proof is compressed.
|
||||
pub fn is_compressed(&self) -> bool {
|
||||
self.proof.is_compressed()
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Compress the opening proof.
|
||||
|
||||
@ -5,6 +5,7 @@ use rayon::prelude::*;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field_types::RichField;
|
||||
use crate::fri::commitment::PolynomialBatchCommitment;
|
||||
use crate::fri::proof::FriProof;
|
||||
use crate::hash::hash_types::HashOut;
|
||||
use crate::hash::hashing::hash_n_to_hash;
|
||||
use crate::iop::challenger::Challenger;
|
||||
@ -202,7 +203,7 @@ pub(crate) fn prove<F: RichField + Extendable<D>, const D: usize>(
|
||||
plonk_zs_partial_products_cap: zs_partial_products_commitment.merkle_tree.cap,
|
||||
quotient_polys_cap: quotient_polys_commitment.merkle_tree.cap,
|
||||
openings,
|
||||
opening_proof,
|
||||
opening_proof: FriProof::Decompressed(opening_proof),
|
||||
};
|
||||
Ok(ProofWithPublicInputs {
|
||||
proof,
|
||||
|
||||
@ -2,10 +2,11 @@ use anyhow::{ensure, Result};
|
||||
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field_types::{Field, RichField};
|
||||
use crate::fri::proof::FriProof;
|
||||
use crate::fri::verifier::verify_fri_proof;
|
||||
use crate::plonk::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData};
|
||||
use crate::plonk::plonk_common::reduce_with_powers;
|
||||
use crate::plonk::proof::ProofWithPublicInputs;
|
||||
use crate::plonk::proof::{Proof, ProofWithPublicInputs};
|
||||
use crate::plonk::vanishing_poly::eval_vanishing_poly;
|
||||
use crate::plonk::vars::EvaluationVars;
|
||||
|
||||
@ -76,11 +77,20 @@ pub(crate) fn verify<F: RichField + Extendable<D>, const D: usize>(
|
||||
proof.quotient_polys_cap,
|
||||
];
|
||||
|
||||
let Proof {
|
||||
openings,
|
||||
opening_proof,
|
||||
..
|
||||
} = proof;
|
||||
let opening_proof = match opening_proof {
|
||||
FriProof::Decompressed(p) => p,
|
||||
FriProof::Compressed(p) => p.decompress(&challenges.fri_query_indices, common_data),
|
||||
};
|
||||
verify_fri_proof(
|
||||
&proof.openings,
|
||||
&openings,
|
||||
&challenges,
|
||||
merkle_caps,
|
||||
&proof.opening_proof,
|
||||
&opening_proof,
|
||||
common_data,
|
||||
)?;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user