Add CompressedProof type

This commit is contained in:
wborgeaud 2021-09-30 06:56:32 +02:00
parent a97b9a7112
commit f92ce1a80c
8 changed files with 205 additions and 97 deletions

View File

@ -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::DecompressedFriProof;
use crate::fri::proof::FriProof;
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,
) -> (DecompressedFriProof<F, D>, OpeningSet<F, D>)
) -> (FriProof<F, D>, OpeningSet<F, D>)
where
F: RichField + Extendable<D>,
{

View File

@ -89,14 +89,7 @@ pub struct CompressedFriQueryRounds<F: Extendable<D>, const D: usize> {
#[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> {
pub struct FriProof<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
@ -107,7 +100,6 @@ pub struct DecompressedFriProof<F: Extendable<D>, const D: usize> {
pub pow_witness: F,
}
/// Corresponds to `DecompressedFriProof`.
pub struct FriProofTarget<const D: usize> {
pub commit_phase_merkle_caps: Vec<MerkleCapTarget>,
pub query_round_proofs: Vec<FriQueryRoundTarget<D>>,
@ -128,15 +120,14 @@ pub struct CompressedFriProof<F: Extendable<D>, const D: usize> {
pub pow_witness: F,
}
impl<F: RichField + Extendable<D>, const D: usize> DecompressedFriProof<F, D> {
impl<F: RichField + Extendable<D>, const D: usize> FriProof<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 {
let FriProof {
commit_phase_merkle_caps,
query_round_proofs,
final_poly,
@ -209,7 +200,7 @@ impl<F: RichField + Extendable<D>, const D: usize> DecompressedFriProof<F, D> {
.initial_trees_proofs
.insert(index, initial_proof);
for j in 0..num_reductions {
index >>= reduction_arity_bits[i];
index >>= reduction_arity_bits[j];
let query_step = FriQueryStep {
evals: steps_evals[j][i].clone(),
merkle_proof: steps_proofs[j][i].clone(),
@ -233,7 +224,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CompressedFriProof<F, D> {
self,
indices: &[usize],
common_data: &CommonCircuitData<F, D>,
) -> DecompressedFriProof<F, D> {
) -> FriProof<F, D> {
let CompressedFriProof {
commit_phase_merkle_caps,
query_round_proofs,
@ -323,7 +314,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CompressedFriProof<F, D> {
})
}
DecompressedFriProof {
FriProof {
commit_phase_merkle_caps,
query_round_proofs: decompressed_query_proofs,
final_poly,

View File

@ -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::{DecompressedFriProof, FriInitialTreeProof, FriQueryRound, FriQueryStep};
use crate::fri::proof::{FriInitialTreeProof, FriProof, 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,
) -> DecompressedFriProof<F, D> {
) -> FriProof<F, D> {
let n = lde_polynomial_values.values.len();
assert_eq!(lde_polynomial_coeffs.coeffs.len(), n);
@ -58,7 +58,7 @@ pub fn fri_proof<F: RichField + Extendable<D>, const D: usize>(
&config.fri_config,
);
DecompressedFriProof {
FriProof {
commit_phase_merkle_caps: trees.iter().map(|t| t.cap.clone()).collect(),
query_round_proofs,
final_poly: final_coeffs,

View File

@ -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::{DecompressedFriProof, FriInitialTreeProof, FriQueryRound};
use crate::fri::proof::{FriInitialTreeProof, FriProof, 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: &DecompressedFriProof<F, D>,
proof: &FriProof<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: &DecompressedFriProof<F, D>,
proof: &FriProof<F, D>,
mut x_index: usize,
n: usize,
round_proof: &FriQueryRound<F, D>,

View File

@ -4,7 +4,9 @@ use crate::fri::proof::FriProof;
use crate::hash::hashing::hash_n_to_1;
use crate::iop::challenger::Challenger;
use crate::plonk::circuit_data::CommonCircuitData;
use crate::plonk::proof::{ProofChallenges, ProofWithPublicInputs};
use crate::plonk::proof::{
CompressedProofWithPublicInputs, ProofChallenges, ProofWithPublicInputs,
};
impl<F: RichField + Extendable<D>, const D: usize> ProofWithPublicInputs<F, D> {
pub(crate) fn fri_query_indices(
@ -45,24 +47,18 @@ 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 = 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();
let fri_betas = self
.proof
.opening_proof
.commit_phase_merkle_caps
.iter()
.map(|cap| {
challenger.observe_cap(cap);
challenger.get_extension_challenge()
})
.collect();
challenger.observe_extension_elements(
&match &self.proof.opening_proof {
FriProof::Decompressed(p) => &p.final_poly,
FriProof::Compressed(p) => &p.final_poly,
}
.coeffs,
);
challenger.observe_extension_elements(&self.proof.opening_proof.final_poly.coeffs);
let fri_pow_response = hash_n_to_1(
challenger
@ -70,10 +66,87 @@ impl<F: RichField + Extendable<D>, const D: usize> ProofWithPublicInputs<F, D> {
.elements
.iter()
.copied()
.chain(Some(match &self.proof.opening_proof {
FriProof::Decompressed(p) => p.pow_witness,
FriProof::Compressed(p) => p.pow_witness,
}))
.chain(Some(self.proof.opening_proof.pow_witness))
.collect(),
false,
);
let fri_query_indices = (0..num_fri_queries)
.map(|_| challenger.get_challenge().to_canonical_u64() as usize % lde_size)
.collect();
Ok(ProofChallenges {
plonk_betas,
plonk_gammas,
plonk_alphas,
plonk_zeta,
fri_alpha,
fri_betas,
fri_pow_response,
fri_query_indices,
})
}
}
impl<F: RichField + Extendable<D>, const D: usize> CompressedProofWithPublicInputs<F, D> {
pub(crate) fn fri_query_indices(
&self,
common_data: &CommonCircuitData<F, D>,
) -> anyhow::Result<Vec<usize>> {
Ok(self.get_challenges(common_data)?.fri_query_indices)
}
pub(crate) fn get_challenges(
&self,
common_data: &CommonCircuitData<F, D>,
) -> anyhow::Result<ProofChallenges<F, D>> {
let config = &common_data.config;
let num_challenges = config.num_challenges;
let num_fri_queries = config.fri_config.num_query_rounds;
let lde_size = common_data.lde_size();
let mut challenger = Challenger::new();
// Observe the instance.
challenger.observe_hash(&common_data.circuit_digest);
challenger.observe_hash(&self.get_public_inputs_hash());
challenger.observe_cap(&self.proof.wires_cap);
let plonk_betas = challenger.get_n_challenges(num_challenges);
let plonk_gammas = challenger.get_n_challenges(num_challenges);
challenger.observe_cap(&self.proof.plonk_zs_partial_products_cap);
let plonk_alphas = challenger.get_n_challenges(num_challenges);
challenger.observe_cap(&self.proof.quotient_polys_cap);
let plonk_zeta = challenger.get_extension_challenge();
challenger.observe_opening_set(&self.proof.openings);
// Scaling factor to combine polynomials.
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();
challenger.observe_extension_elements(&self.proof.opening_proof.final_poly.coeffs);
let fri_pow_response = hash_n_to_1(
challenger
.get_hash()
.elements
.iter()
.copied()
.chain(Some(self.proof.opening_proof.pow_witness))
.collect(),
false,
);

View File

@ -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::{DecompressedFriProof, FriProof, FriProofTarget};
use crate::fri::proof::{CompressedFriProof, FriProof, FriProofTarget};
use crate::hash::hash_types::{HashOut, MerkleCapTarget};
use crate::hash::hashing::hash_n_to_hash;
use crate::hash::merkle_tree::MerkleCap;
@ -36,27 +36,27 @@ 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 {
todo!()
}
/// Compress the proof.
pub fn compress(
self,
indices: &[usize],
common_data: &CommonCircuitData<F, D>,
) -> CompressedProof<F, D> {
let Proof {
wires_cap,
plonk_zs_partial_products_cap,
quotient_polys_cap,
openings,
opening_proof,
} = self;
/// Compress the opening proof.
pub fn compress(mut self, indices: &[usize], common_data: &CommonCircuitData<F, D>) -> Self {
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 = FriProof::Decompressed(match self.opening_proof {
FriProof::Decompressed(p) => p,
FriProof::Compressed(p) => p.decompress(indices, common_data),
});
self
CompressedProof {
wires_cap,
plonk_zs_partial_products_cap,
quotient_polys_cap,
openings,
opening_proof: opening_proof.compress(indices, common_data),
}
}
}
@ -68,23 +68,81 @@ 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 {
todo!()
pub fn compress(
mut self,
common_data: &CommonCircuitData<F, D>,
) -> anyhow::Result<CompressedProofWithPublicInputs<F, D>> {
let indices = self.fri_query_indices(common_data)?;
let compressed_proof = self.proof.compress(&indices, common_data);
Ok(CompressedProofWithPublicInputs {
public_inputs: self.public_inputs,
proof: compressed_proof,
})
}
/// Compress the opening proof.
pub fn compress(mut self, common_data: &CommonCircuitData<F, D>) -> anyhow::Result<Self> {
let indices = self.fri_query_indices(common_data)?;
self.proof = self.proof.compress(&indices, common_data);
Ok(self)
pub(crate) fn get_public_inputs_hash(&self) -> HashOut<F> {
hash_n_to_hash(self.public_inputs.clone(), true)
}
}
/// Decompress the opening proof.
pub fn decompress(mut self, common_data: &CommonCircuitData<F, D>) -> anyhow::Result<Self> {
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
#[serde(bound = "")]
pub struct CompressedProof<F: Extendable<D>, const D: usize> {
/// Merkle cap of LDEs of wire values.
pub wires_cap: MerkleCap<F>,
/// Merkle cap of LDEs of Z, in the context of Plonk's permutation argument.
pub plonk_zs_partial_products_cap: MerkleCap<F>,
/// Merkle cap of LDEs of the quotient polynomial components.
pub quotient_polys_cap: MerkleCap<F>,
/// Purported values of each polynomial at the challenge point.
pub openings: OpeningSet<F, D>,
/// A batch FRI argument for all openings.
pub opening_proof: CompressedFriProof<F, D>,
}
impl<F: RichField + Extendable<D>, const D: usize> CompressedProof<F, D> {
/// Decompress the proof.
pub fn decompress(
self,
indices: &[usize],
common_data: &CommonCircuitData<F, D>,
) -> Proof<F, D> {
let CompressedProof {
wires_cap,
plonk_zs_partial_products_cap,
quotient_polys_cap,
openings,
opening_proof,
} = self;
Proof {
wires_cap,
plonk_zs_partial_products_cap,
quotient_polys_cap,
openings,
opening_proof: opening_proof.decompress(indices, common_data),
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
#[serde(bound = "")]
pub struct CompressedProofWithPublicInputs<F: RichField + Extendable<D>, const D: usize> {
pub proof: CompressedProof<F, D>,
pub public_inputs: Vec<F>,
}
impl<F: RichField + Extendable<D>, const D: usize> CompressedProofWithPublicInputs<F, D> {
pub fn decompress(
mut self,
common_data: &CommonCircuitData<F, D>,
) -> anyhow::Result<ProofWithPublicInputs<F, D>> {
let indices = self.fri_query_indices(common_data)?;
self.proof = self.proof.decompress(&indices, common_data);
Ok(self)
let compressed_proof = self.proof.decompress(&indices, common_data);
Ok(ProofWithPublicInputs {
public_inputs: self.public_inputs,
proof: compressed_proof,
})
}
pub(crate) fn get_public_inputs_hash(&self) -> HashOut<F> {
@ -216,7 +274,7 @@ mod tests {
let decompressed_compressed_proof = compressed_proof.clone().decompress(&data.common)?;
assert_eq!(proof, decompressed_compressed_proof);
verify(proof, &data.verifier_only, &data.common)?;
verify(compressed_proof, &data.verifier_only, &data.common)
verify(proof, &data.verifier_only, &data.common)
// verify(compressed_proof, &data.verifier_only, &data.common)
}
}

View File

@ -203,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: FriProof::Decompressed(opening_proof),
opening_proof,
};
Ok(ProofWithPublicInputs {
proof,

View File

@ -15,11 +15,6 @@ pub(crate) fn verify<F: RichField + Extendable<D>, const D: usize>(
verifier_data: &VerifierOnlyCircuitData<F>,
common_data: &CommonCircuitData<F, D>,
) -> Result<()> {
// Decompress the proof if needed.
if proof_with_pis.is_compressed() {
proof_with_pis = proof_with_pis.decompress(common_data)?;
}
let public_inputs_hash = &proof_with_pis.get_public_inputs_hash();
let challenges = proof_with_pis.get_challenges(common_data)?;
@ -77,20 +72,11 @@ 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(
&openings,
&proof.openings,
&challenges,
merkle_caps,
&opening_proof,
&proof.opening_proof,
common_data,
)?;