diff --git a/src/fri/proof.rs b/src/fri/proof.rs index 9751825f..ccac24eb 100644 --- a/src/fri/proof.rs +++ b/src/fri/proof.rs @@ -1,5 +1,3 @@ -use std::collections::hash_map::Entry::Vacant; - use serde::{Deserialize, Serialize}; use crate::field::extension_field::target::ExtensionTarget; @@ -102,6 +100,7 @@ pub struct FriProofTarget { } impl, const D: usize> FriProof { + /// Compress all the Merkle paths in the FRI proof. pub fn compress(self, common_data: &CommonCircuitData) -> Self { if self.is_compressed { panic!("Proof is already compressed."); @@ -159,6 +158,7 @@ impl, const D: usize> FriProof { .map(|(is, ps)| compress_merkle_proofs(cap_height, is, &ps)) .collect::>(); + // Replace the query round proofs with the decompressed versions. for (i, qrp) in query_round_proofs.iter_mut().enumerate() { qrp.initial_trees_proof = FriInitialTreeProof { evals_proofs: (0..num_initial_trees) @@ -187,6 +187,7 @@ impl, const D: usize> FriProof { } } + /// Decompress all the Merkle paths in the FRI proof. pub fn decompress(self, common_data: &CommonCircuitData) -> Self { if !self.is_compressed { panic!("Proof is not compressed."); @@ -255,6 +256,7 @@ impl, const D: usize> FriProof { .map(|(((ls, is), ps), h)| decompress_merkle_proofs(ls, is, &ps, h, cap_height)) .collect::>(); + // Replace the query round proofs with the decompressed versions. for (i, qrp) in query_round_proofs.iter_mut().enumerate() { qrp.initial_trees_proof = FriInitialTreeProof { evals_proofs: (0..num_initial_trees) diff --git a/src/gadgets/arithmetic_extension.rs b/src/gadgets/arithmetic_extension.rs index 4645e38f..af838ab6 100644 --- a/src/gadgets/arithmetic_extension.rs +++ b/src/gadgets/arithmetic_extension.rs @@ -601,10 +601,6 @@ mod tests { let data = builder.build(); let proof = data.prove(pw)?; - let mut go = proof.proof.opening_proof.clone(); - let goc = go.clone().compress(&data.common); - let gocd = goc.decompress(&data.common); - assert_eq!(go, gocd); verify(proof, &data.verifier_only, &data.common) } diff --git a/src/hash/path_compression.rs b/src/hash/path_compression.rs index 38b8543c..bf1b164b 100644 --- a/src/hash/path_compression.rs +++ b/src/hash/path_compression.rs @@ -50,7 +50,7 @@ pub(crate) fn compress_merkle_proofs( compressed_proofs } -/// Verify a compressed Merkle proof. +/// Decompress compressed Merkle proofs. /// Note: The data and indices must be in the same order as in `compress_merkle_proofs`. pub(crate) fn decompress_merkle_proofs( leaves_data: &[Vec], diff --git a/src/plonk/proof.rs b/src/plonk/proof.rs index 06a7f3ed..cdaa28ed 100644 --- a/src/plonk/proof.rs +++ b/src/plonk/proof.rs @@ -11,7 +11,7 @@ use crate::hash::merkle_tree::MerkleCap; use crate::iop::target::Target; use crate::plonk::circuit_data::CommonCircuitData; -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] #[serde(bound = "")] pub struct Proof, const D: usize> { /// Merkle cap of LDEs of wire values. @@ -35,22 +35,25 @@ pub struct ProofTarget { } impl, const D: usize> Proof { + /// Returns `true` iff the opening proof is compressed. pub fn is_compressed(&self) -> bool { self.opening_proof.is_compressed } + /// Compress the opening proof. pub fn compress(mut self, common_data: &CommonCircuitData) -> Self { self.opening_proof = self.opening_proof.compress(common_data); self } + /// Decompress the opening proof. pub fn decompress(mut self, common_data: &CommonCircuitData) -> Self { self.opening_proof = self.opening_proof.decompress(common_data); self } } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] #[serde(bound = "")] pub struct ProofWithPublicInputs, const D: usize> { pub proof: Proof, @@ -63,22 +66,25 @@ pub struct ProofWithPublicInputsTarget { } impl, const D: usize> ProofWithPublicInputs { + /// Returns `true` iff the opening proof is compressed. pub fn is_compressed(&self) -> bool { self.proof.is_compressed() } + /// Compress the opening proof. pub fn compress(mut self, common_data: &CommonCircuitData) -> Self { self.proof = self.proof.compress(common_data); self } + /// Decompress the opening proof. pub fn decompress(mut self, common_data: &CommonCircuitData) -> Self { self.proof = self.proof.decompress(common_data); self } } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] /// The purported values of each polynomial at a single point. pub struct OpeningSet, const D: usize> { pub constants: Vec, @@ -134,3 +140,49 @@ pub struct OpeningSetTarget { pub partial_products: Vec>, pub quotient_polys: Vec>, } + +#[cfg(test)] +mod tests { + use anyhow::Result; + + use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::algebra::ExtensionAlgebra; + use crate::field::extension_field::quartic::QuarticExtension; + use crate::field::field_types::Field; + use crate::iop::witness::PartialWitness; + use crate::plonk::circuit_builder::CircuitBuilder; + use crate::plonk::circuit_data::CircuitConfig; + use crate::plonk::verifier::verify; + + #[test] + fn test_proof_compression() -> Result<()> { + type F = CrandallField; + type FF = QuarticExtension; + const D: usize = 4; + + let config = CircuitConfig::large_config(); + + let pw = PartialWitness::new(); + let mut builder = CircuitBuilder::::new(config); + + // Build dummy circuit to get a valid proof. + let x = F::rand(); + let y = F::rand(); + let z = x * y; + let xt = builder.constant(x); + let yt = builder.constant(y); + let zt = builder.constant(z); + let comp_zt = builder.mul(xt, yt); + builder.connect(zt, comp_zt); + let data = builder.build(); + let proof = data.prove(pw)?; + + // Verify that `decompress ∘ compress = identity`. + let compressed_proof = proof.clone().compress(&data.common); + 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) + } +}