From a97b9a71126f8b06b61dfcee1cb16f6c452afd9a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 29 Sep 2021 21:01:15 +0200 Subject: [PATCH 01/17] Add compressed FRI proof type using a HashMap --- src/fri/commitment.rs | 4 +- src/fri/proof.rs | 131 +++++++++++++++++++++++++----------- src/fri/prover.rs | 7 +- src/fri/verifier.rs | 6 +- src/plonk/get_challenges.rs | 34 ++++++---- src/plonk/proof.rs | 16 +++-- src/plonk/prover.rs | 3 +- src/plonk/verifier.rs | 16 ++++- 8 files changed, 147 insertions(+), 70 deletions(-) diff --git a/src/fri/commitment.rs b/src/fri/commitment.rs index c704c33c..738d7b8f 100644 --- a/src/fri/commitment.rs +++ b/src/fri/commitment.rs @@ -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 PolynomialBatchCommitment { challenger: &mut Challenger, common_data: &CommonCircuitData, timing: &mut TimingTree, - ) -> (FriProof, OpeningSet) + ) -> (DecompressedFriProof, OpeningSet) where F: RichField + Extendable, { diff --git a/src/fri/proof.rs b/src/fri/proof.rs index 5fc6a9a8..8b6f98fe 100644 --- a/src/fri/proof.rs +++ b/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 { pub steps: Vec>, } +/// Compressed proofs for FRI query rounds. #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] #[serde(bound = "")] -pub struct FriProof, const D: usize> { +pub struct CompressedFriQueryRounds, const D: usize> { + pub initial_trees_proofs: HashMap>, + pub steps: Vec>>, +} + +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[serde(bound = "")] +pub enum FriProof, const D: usize> { + Decompressed(DecompressedFriProof), + Compressed(CompressedFriProof), +} + +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[serde(bound = "")] +pub struct DecompressedFriProof, const D: usize> { /// A Merkle cap for each reduced polynomial in the commit phase. pub commit_phase_merkle_caps: Vec>, /// Query rounds proofs @@ -88,10 +105,9 @@ pub struct FriProof, const D: usize> { pub final_poly: PolynomialCoeffs, /// 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 { pub commit_phase_merkle_caps: Vec, pub query_round_proofs: Vec>, @@ -99,15 +115,30 @@ pub struct FriProofTarget { pub pow_witness: Target, } -impl, const D: usize> FriProof { - /// Compress all the Merkle paths in the FRI proof. - pub fn compress(self, indices: &[usize], common_data: &CommonCircuitData) -> Self { - if self.is_compressed { - panic!("Proof is already compressed."); - } - let FriProof { +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[serde(bound = "")] +pub struct CompressedFriProof, const D: usize> { + /// A Merkle cap for each reduced polynomial in the commit phase. + pub commit_phase_merkle_caps: Vec>, + /// Query rounds proofs + pub query_round_proofs: CompressedFriQueryRounds, + /// The final polynomial in coefficient form. + pub final_poly: PolynomialCoeffs, + /// Witness showing that the prover did PoW. + pub pow_witness: F, +} + +impl, const D: usize> DecompressedFriProof { + /// Compress all the Merkle paths in the FRI proof and remove duplicate indices. + pub fn compress( + self, + indices: &[usize], + common_data: &CommonCircuitData, + with_indices: bool, + ) -> CompressedFriProof { + let DecompressedFriProof { commit_phase_merkle_caps, - mut query_round_proofs, + query_round_proofs, final_poly, pow_witness, .. @@ -157,9 +188,14 @@ impl, const D: usize> FriProof { .map(|(is, ps)| compress_merkle_proofs(cap_height, is, &ps)) .collect::>(); + 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, const D: usize> FriProof { }) .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) -> Self { - if !self.is_compressed { - panic!("Proof is not compressed."); - } - let FriProof { +impl, const D: usize> CompressedFriProof { + /// Decompress all the Merkle paths in the FRI proof and add duplicate indices. + pub fn decompress( + self, + indices: &[usize], + common_data: &CommonCircuitData, + ) -> DecompressedFriProof { + let CompressedFriProof { commit_phase_merkle_caps, - mut query_round_proofs, + query_round_proofs, final_poly, pow_witness, .. @@ -201,7 +244,13 @@ impl, const D: usize> FriProof { 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, const D: usize> FriProof { }) .collect::>(); - 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, const D: usize> FriProof { 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, const D: usize> FriProof { .map(|(ls, is, ps, h)| decompress_merkle_proofs(ls, is, &ps, h, cap_height)) .collect::>(); + 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, const D: usize> FriProof { }) .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, } } } diff --git a/src/fri/prover.rs b/src/fri/prover.rs index a2534e3f..7bbff557 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -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, const D: usize>( challenger: &mut Challenger, config: &CircuitConfig, timing: &mut TimingTree, -) -> FriProof { +) -> DecompressedFriProof { let n = lde_polynomial_values.values.len(); assert_eq!(lde_polynomial_coeffs.coeffs.len(), n); @@ -58,12 +58,11 @@ pub fn fri_proof, 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, } } diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 6662830a..914dd34e 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -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, const D: usize>( os: &OpeningSet, challenges: &ProofChallenges, initial_merkle_caps: &[MerkleCap], - proof: &FriProof, + proof: &DecompressedFriProof, common_data: &CommonCircuitData, ) -> Result<()> { let config = &common_data.config; @@ -220,7 +220,7 @@ fn fri_verifier_query_round, const D: usize>( challenges: &ProofChallenges, precomputed_reduced_evals: PrecomputedReducedEvals, initial_merkle_caps: &[MerkleCap], - proof: &FriProof, + proof: &DecompressedFriProof, mut x_index: usize, n: usize, round_proof: &FriQueryRound, diff --git a/src/plonk/get_challenges.rs b/src/plonk/get_challenges.rs index d63c2550..5ff8bccb 100644 --- a/src/plonk/get_challenges.rs +++ b/src/plonk/get_challenges.rs @@ -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, const D: usize> ProofWithPublicInputs { 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, const D: usize> ProofWithPublicInputs { .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, ); diff --git a/src/plonk/proof.rs b/src/plonk/proof.rs index cf8bfc74..57377741 100644 --- a/src/plonk/proof.rs +++ b/src/plonk/proof.rs @@ -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 { impl, const D: usize> Proof { /// 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) -> 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) -> 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, const D: usize> { impl, const D: usize> ProofWithPublicInputs { /// Returns `true` iff the opening proof is compressed. pub fn is_compressed(&self) -> bool { - self.proof.is_compressed() + todo!() } /// Compress the opening proof. diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 19852cff..76267820 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -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, 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, diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 27038b0d..6753cfeb 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -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, 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, )?; From f92ce1a80c5466deed21561fc79acaa20d897295 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 30 Sep 2021 06:56:32 +0200 Subject: [PATCH 02/17] Add CompressedProof type --- src/fri/commitment.rs | 4 +- src/fri/proof.rs | 21 ++---- src/fri/prover.rs | 6 +- src/fri/verifier.rs | 6 +- src/plonk/get_challenges.rs | 117 +++++++++++++++++++++++++------- src/plonk/proof.rs | 128 ++++++++++++++++++++++++++---------- src/plonk/prover.rs | 2 +- src/plonk/verifier.rs | 18 +---- 8 files changed, 205 insertions(+), 97 deletions(-) diff --git a/src/fri/commitment.rs b/src/fri/commitment.rs index 738d7b8f..c704c33c 100644 --- a/src/fri/commitment.rs +++ b/src/fri/commitment.rs @@ -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 PolynomialBatchCommitment { challenger: &mut Challenger, common_data: &CommonCircuitData, timing: &mut TimingTree, - ) -> (DecompressedFriProof, OpeningSet) + ) -> (FriProof, OpeningSet) where F: RichField + Extendable, { diff --git a/src/fri/proof.rs b/src/fri/proof.rs index 8b6f98fe..b6402733 100644 --- a/src/fri/proof.rs +++ b/src/fri/proof.rs @@ -89,14 +89,7 @@ pub struct CompressedFriQueryRounds, const D: usize> { #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] #[serde(bound = "")] -pub enum FriProof, const D: usize> { - Decompressed(DecompressedFriProof), - Compressed(CompressedFriProof), -} - -#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] -#[serde(bound = "")] -pub struct DecompressedFriProof, const D: usize> { +pub struct FriProof, const D: usize> { /// A Merkle cap for each reduced polynomial in the commit phase. pub commit_phase_merkle_caps: Vec>, /// Query rounds proofs @@ -107,7 +100,6 @@ pub struct DecompressedFriProof, const D: usize> { pub pow_witness: F, } -/// Corresponds to `DecompressedFriProof`. pub struct FriProofTarget { pub commit_phase_merkle_caps: Vec, pub query_round_proofs: Vec>, @@ -128,15 +120,14 @@ pub struct CompressedFriProof, const D: usize> { pub pow_witness: F, } -impl, const D: usize> DecompressedFriProof { +impl, const D: usize> FriProof { /// Compress all the Merkle paths in the FRI proof and remove duplicate indices. pub fn compress( self, indices: &[usize], common_data: &CommonCircuitData, - with_indices: bool, ) -> CompressedFriProof { - let DecompressedFriProof { + let FriProof { commit_phase_merkle_caps, query_round_proofs, final_poly, @@ -209,7 +200,7 @@ impl, const D: usize> DecompressedFriProof { .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, const D: usize> CompressedFriProof { self, indices: &[usize], common_data: &CommonCircuitData, - ) -> DecompressedFriProof { + ) -> FriProof { let CompressedFriProof { commit_phase_merkle_caps, query_round_proofs, @@ -323,7 +314,7 @@ impl, const D: usize> CompressedFriProof { }) } - DecompressedFriProof { + FriProof { commit_phase_merkle_caps, query_round_proofs: decompressed_query_proofs, final_poly, diff --git a/src/fri/prover.rs b/src/fri/prover.rs index 7bbff557..a0d71d98 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -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, const D: usize>( challenger: &mut Challenger, config: &CircuitConfig, timing: &mut TimingTree, -) -> DecompressedFriProof { +) -> FriProof { let n = lde_polynomial_values.values.len(); assert_eq!(lde_polynomial_coeffs.coeffs.len(), n); @@ -58,7 +58,7 @@ pub fn fri_proof, 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, diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 914dd34e..6662830a 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -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, const D: usize>( os: &OpeningSet, challenges: &ProofChallenges, initial_merkle_caps: &[MerkleCap], - proof: &DecompressedFriProof, + proof: &FriProof, common_data: &CommonCircuitData, ) -> Result<()> { let config = &common_data.config; @@ -220,7 +220,7 @@ fn fri_verifier_query_round, const D: usize>( challenges: &ProofChallenges, precomputed_reduced_evals: PrecomputedReducedEvals, initial_merkle_caps: &[MerkleCap], - proof: &DecompressedFriProof, + proof: &FriProof, mut x_index: usize, n: usize, round_proof: &FriQueryRound, diff --git a/src/plonk/get_challenges.rs b/src/plonk/get_challenges.rs index 5ff8bccb..f1593b46 100644 --- a/src/plonk/get_challenges.rs +++ b/src/plonk/get_challenges.rs @@ -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, const D: usize> ProofWithPublicInputs { pub(crate) fn fri_query_indices( @@ -45,24 +47,18 @@ impl, const D: usize> ProofWithPublicInputs { 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, const D: usize> ProofWithPublicInputs { .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, const D: usize> CompressedProofWithPublicInputs { + pub(crate) fn fri_query_indices( + &self, + common_data: &CommonCircuitData, + ) -> anyhow::Result> { + Ok(self.get_challenges(common_data)?.fri_query_indices) + } + + pub(crate) fn get_challenges( + &self, + common_data: &CommonCircuitData, + ) -> anyhow::Result> { + 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, ); diff --git a/src/plonk/proof.rs b/src/plonk/proof.rs index 57377741..3442d5fa 100644 --- a/src/plonk/proof.rs +++ b/src/plonk/proof.rs @@ -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 { } impl, const D: usize> Proof { - /// 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, + ) -> CompressedProof { + 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) -> 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) -> 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, const D: usize> { } impl, const D: usize> ProofWithPublicInputs { - /// Returns `true` iff the opening proof is compressed. - pub fn is_compressed(&self) -> bool { - todo!() + pub fn compress( + mut self, + common_data: &CommonCircuitData, + ) -> anyhow::Result> { + 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) -> anyhow::Result { - 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 { + hash_n_to_hash(self.public_inputs.clone(), true) } +} - /// Decompress the opening proof. - pub fn decompress(mut self, common_data: &CommonCircuitData) -> anyhow::Result { +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[serde(bound = "")] +pub struct CompressedProof, const D: usize> { + /// Merkle cap of LDEs of wire values. + pub wires_cap: MerkleCap, + /// Merkle cap of LDEs of Z, in the context of Plonk's permutation argument. + pub plonk_zs_partial_products_cap: MerkleCap, + /// Merkle cap of LDEs of the quotient polynomial components. + pub quotient_polys_cap: MerkleCap, + /// Purported values of each polynomial at the challenge point. + pub openings: OpeningSet, + /// A batch FRI argument for all openings. + pub opening_proof: CompressedFriProof, +} + +impl, const D: usize> CompressedProof { + /// Decompress the proof. + pub fn decompress( + self, + indices: &[usize], + common_data: &CommonCircuitData, + ) -> Proof { + 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, const D: usize> { + pub proof: CompressedProof, + pub public_inputs: Vec, +} + +impl, const D: usize> CompressedProofWithPublicInputs { + pub fn decompress( + mut self, + common_data: &CommonCircuitData, + ) -> anyhow::Result> { 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 { @@ -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) } } diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 76267820..ebf9e140 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -203,7 +203,7 @@ pub(crate) fn prove, 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, diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 6753cfeb..590275cd 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -15,11 +15,6 @@ pub(crate) fn verify, const D: usize>( verifier_data: &VerifierOnlyCircuitData, common_data: &CommonCircuitData, ) -> 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, 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, )?; From 94375cdf684b7760997998722139945fa5c5a868 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 30 Sep 2021 08:02:01 +0200 Subject: [PATCH 03/17] Minor --- src/plonk/proof.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plonk/proof.rs b/src/plonk/proof.rs index 3442d5fa..41c5501b 100644 --- a/src/plonk/proof.rs +++ b/src/plonk/proof.rs @@ -275,6 +275,5 @@ mod tests { assert_eq!(proof, decompressed_compressed_proof); verify(proof, &data.verifier_only, &data.common) - // verify(compressed_proof, &data.verifier_only, &data.common) } } From dd68971609766aab28162a602b182f3579dfe4c2 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 30 Sep 2021 18:58:36 +0200 Subject: [PATCH 04/17] Cleaning --- src/fri/proof.rs | 11 ++++++----- src/plonk/proof.rs | 2 +- src/plonk/prover.rs | 1 - src/plonk/verifier.rs | 3 +-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/fri/proof.rs b/src/fri/proof.rs index b6402733..4d31c66e 100644 --- a/src/fri/proof.rs +++ b/src/fri/proof.rs @@ -79,11 +79,13 @@ pub struct FriQueryRoundTarget { pub steps: Vec>, } -/// Compressed proofs for FRI query rounds. +/// Compressed proof of the FRI query rounds. #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] #[serde(bound = "")] pub struct CompressedFriQueryRounds, const D: usize> { + /// Map from initial indices `i` to the `FriInitialProof` for the `i`th leaf. pub initial_trees_proofs: HashMap>, + /// For each FRI query step, a map from initial indices `i` to the `FriInitialProof` for the `i`th leaf. pub steps: Vec>>, } @@ -112,7 +114,7 @@ pub struct FriProofTarget { pub struct CompressedFriProof, const D: usize> { /// A Merkle cap for each reduced polynomial in the commit phase. pub commit_phase_merkle_caps: Vec>, - /// Query rounds proofs + /// Compressed query rounds proof. pub query_round_proofs: CompressedFriQueryRounds, /// The final polynomial in coefficient form. pub final_poly: PolynomialCoeffs, @@ -219,7 +221,7 @@ impl, const D: usize> FriProof { } impl, const D: usize> CompressedFriProof { - /// Decompress all the Merkle paths in the FRI proof and add duplicate indices. + /// Decompress all the Merkle paths in the FRI proof and reinsert duplicate indices. pub fn decompress( self, indices: &[usize], @@ -259,7 +261,7 @@ impl, const D: usize> CompressedFriProof { }) .collect::>(); - for mut index in indices.iter().cloned() { + for mut index in indices.iter().copied() { 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() @@ -290,7 +292,6 @@ impl, const D: usize> CompressedFriProof { .collect::>(); let mut decompressed_query_proofs = Vec::with_capacity(num_reductions); - // Replace the query round proofs with the decompressed versions. for i in 0..num_reductions { let initial_trees_proof = FriInitialTreeProof { evals_proofs: (0..num_initial_trees) diff --git a/src/plonk/proof.rs b/src/plonk/proof.rs index 41c5501b..ee38938b 100644 --- a/src/plonk/proof.rs +++ b/src/plonk/proof.rs @@ -96,7 +96,7 @@ pub struct CompressedProof, const D: usize> { pub quotient_polys_cap: MerkleCap, /// Purported values of each polynomial at the challenge point. pub openings: OpeningSet, - /// A batch FRI argument for all openings. + /// A compressed batch FRI argument for all openings. pub opening_proof: CompressedFriProof, } diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index ebf9e140..19852cff 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -5,7 +5,6 @@ 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; diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 590275cd..2dbf7989 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -2,11 +2,10 @@ 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::{Proof, ProofWithPublicInputs}; +use crate::plonk::proof::ProofWithPublicInputs; use crate::plonk::vanishing_poly::eval_vanishing_poly; use crate::plonk::vars::EvaluationVars; From 28eca4bfdd5f536b22a6ca81d3b42cfa0285b242 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 30 Sep 2021 19:09:27 +0200 Subject: [PATCH 05/17] Clippy --- src/fri/proof.rs | 4 ++-- src/plonk/proof.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/fri/proof.rs b/src/fri/proof.rs index 4d31c66e..44cef3dc 100644 --- a/src/fri/proof.rs +++ b/src/fri/proof.rs @@ -187,7 +187,7 @@ impl, const D: usize> FriProof { }; // Replace the query round proofs with the compressed versions. - for (i, (mut index, qrp)) in indices.iter().cloned().zip(&query_round_proofs).enumerate() { + for (i, mut index) in indices.iter().copied().enumerate() { let initial_proof = FriInitialTreeProof { evals_proofs: (0..num_initial_trees) .map(|j| { @@ -285,7 +285,7 @@ impl, const D: usize> CompressedFriProof { &initial_trees_indices, initial_trees_proofs ) - .map(|(ls, is, ps)| decompress_merkle_proofs(&ls, is, &ps, height, cap_height)) + .map(|(ls, is, ps)| decompress_merkle_proofs(ls, is, &ps, height, cap_height)) .collect::>(); let steps_proofs = izip!(&steps_evals, &steps_indices, steps_proofs, heights) .map(|(ls, is, ps, h)| decompress_merkle_proofs(ls, is, &ps, h, cap_height)) diff --git a/src/plonk/proof.rs b/src/plonk/proof.rs index ee38938b..c7d0b0ff 100644 --- a/src/plonk/proof.rs +++ b/src/plonk/proof.rs @@ -69,7 +69,7 @@ pub struct ProofWithPublicInputs, const D: usize> { impl, const D: usize> ProofWithPublicInputs { pub fn compress( - mut self, + self, common_data: &CommonCircuitData, ) -> anyhow::Result> { let indices = self.fri_query_indices(common_data)?; @@ -134,7 +134,7 @@ pub struct CompressedProofWithPublicInputs, const D impl, const D: usize> CompressedProofWithPublicInputs { pub fn decompress( - mut self, + self, common_data: &CommonCircuitData, ) -> anyhow::Result> { let indices = self.fri_query_indices(common_data)?; @@ -271,7 +271,7 @@ mod tests { // Verify that `decompress ∘ compress = identity`. let compressed_proof = proof.clone().compress(&data.common)?; - let decompressed_compressed_proof = compressed_proof.clone().decompress(&data.common)?; + let decompressed_compressed_proof = compressed_proof.decompress(&data.common)?; assert_eq!(proof, decompressed_compressed_proof); verify(proof, &data.verifier_only, &data.common) From 8f212d31ef3e3d8180db1811a2c1025a55cfb6ed Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 1 Oct 2021 14:59:16 +0200 Subject: [PATCH 06/17] Working custom (de)serializer --- src/gadgets/arithmetic_extension.rs | 10 +- src/plonk/recursive_verifier.rs | 8 + src/util/mod.rs | 1 + src/util/serialization.rs | 357 ++++++++++++++++++++++++++++ 4 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 src/util/serialization.rs diff --git a/src/gadgets/arithmetic_extension.rs b/src/gadgets/arithmetic_extension.rs index af838ab6..de608be5 100644 --- a/src/gadgets/arithmetic_extension.rs +++ b/src/gadgets/arithmetic_extension.rs @@ -510,6 +510,7 @@ mod tests { use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::verifier::verify; + use crate::util::serialization::Buffer; #[test] fn test_mul_many() -> Result<()> { @@ -583,7 +584,7 @@ mod tests { let config = CircuitConfig::large_config(); let pw = PartialWitness::new(); - let mut builder = CircuitBuilder::::new(config); + let mut builder = CircuitBuilder::::new(config.clone()); let x = FF::rand_vec(4); let y = FF::rand_vec(4); @@ -602,6 +603,13 @@ mod tests { let data = builder.build(); let proof = data.prove(pw)?; + let mut buffer = Buffer::new(Vec::new()); + buffer.write_proof(&proof.proof).unwrap(); + dbg!(buffer.len()); + let mut buffer = Buffer::new(buffer.bytes()); + let oproof = buffer.read_proof(&data.common, &config).unwrap(); + assert_eq!(proof.proof, oproof); + verify(proof, &data.verifier_only, &data.common) } } diff --git a/src/plonk/recursive_verifier.rs b/src/plonk/recursive_verifier.rs index 75158d67..7babb26f 100644 --- a/src/plonk/recursive_verifier.rs +++ b/src/plonk/recursive_verifier.rs @@ -139,6 +139,7 @@ mod tests { use crate::plonk::proof::{OpeningSetTarget, Proof, ProofTarget, ProofWithPublicInputs}; use crate::plonk::verifier::verify; use crate::util::log2_strict; + use crate::util::serialization::Buffer; // Construct a `FriQueryRoundTarget` with the same dimensions as the ones in `proof`. fn get_fri_query_round, const D: usize>( @@ -480,6 +481,13 @@ mod tests { builder.print_gate_counts(0); let data = builder.build(); let recursive_proof = data.prove(pw)?; + let mut buffer = Buffer::new(Vec::new()); + buffer.write_proof(&recursive_proof.proof).unwrap(); + dbg!(recursive_proof.public_inputs.len()); + dbg!(buffer.len()); + let mut buffer = Buffer::new(buffer.bytes()); + let proof = buffer.read_proof(&data.common, &config).unwrap(); + assert_eq!(recursive_proof.proof, proof); let now = std::time::Instant::now(); let compressed_recursive_proof = recursive_proof.clone().compress(&data.common)?; info!("{:.4} to compress proof", now.elapsed().as_secs_f64()); diff --git a/src/util/mod.rs b/src/util/mod.rs index daa6716b..d8e05a18 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -6,6 +6,7 @@ pub(crate) mod context_tree; pub(crate) mod marking; pub(crate) mod partial_products; pub mod reducing; +pub mod serialization; pub(crate) mod timing; pub(crate) fn bits_u64(n: u64) -> usize { diff --git a/src/util/serialization.rs b/src/util/serialization.rs new file mode 100644 index 00000000..5a32becb --- /dev/null +++ b/src/util/serialization.rs @@ -0,0 +1,357 @@ +use std::convert::TryInto; +use std::fmt; +use std::io::Cursor; +use std::io::{Error, ErrorKind, Read, Result, Write}; + +use crate::field::crandall_field::CrandallField; +use crate::field::extension_field::quartic::QuarticExtension; +use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::field::field_types::{Field, PrimeField, RichField}; +use crate::fri::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep}; +use crate::hash::hash_types::HashOut; +use crate::hash::merkle_proofs::MerkleProof; +use crate::hash::merkle_tree::{MerkleCap, MerkleTree}; +use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData}; +use crate::plonk::proof::{OpeningSet, Proof}; +use crate::polynomial::polynomial::PolynomialCoeffs; + +#[derive(Debug)] +pub struct Buffer(Cursor>); + +impl Buffer { + pub fn new(buffer: Vec) -> Self { + Self(Cursor::new(buffer)) + } + + pub fn len(&self) -> usize { + self.0.get_ref().len() + } + + pub fn bytes(self) -> Vec { + self.0.into_inner() + } + + pub fn write_u8(&mut self, x: u8) -> Result<()> { + self.0.write_all(&[x]) + } + pub fn read_u8(&mut self) -> Result { + let mut buf = [0; 1]; + self.0.read_exact(&mut buf)?; + Ok(buf[0]) + } + + pub fn write_field(&mut self, x: F) -> Result<()> { + self.0.write_all(&x.to_canonical_u64().to_le_bytes()) + } + pub fn read_field(&mut self) -> Result { + let mut buf = [0; std::mem::size_of::()]; + self.0.read_exact(&mut buf)?; + Ok(F::from_canonical_u64(u64::from_le_bytes( + buf.try_into().unwrap(), + ))) + } + + pub fn write_field_ext, const D: usize>( + &mut self, + x: F::Extension, + ) -> Result<()> { + for &a in &x.to_basefield_array() { + self.write_field(a)?; + } + Ok(()) + } + pub fn read_field_ext, const D: usize>(&mut self) -> Result { + let mut arr = [F::ZERO; D]; + for a in arr.iter_mut() { + *a = self.read_field()?; + } + Ok(>::from_basefield_array( + arr, + )) + } + + pub fn write_hash(&mut self, h: HashOut) -> Result<()> { + for &a in &h.elements { + self.write_field(a)?; + } + Ok(()) + } + pub fn read_hash(&mut self) -> Result> { + let mut elements = [F::ZERO; 4]; + for a in elements.iter_mut() { + *a = self.read_field()?; + } + Ok(HashOut { elements }) + } + + pub fn write_merkle_cap(&mut self, cap: &MerkleCap) -> Result<()> { + for &a in &cap.0 { + self.write_hash(a)?; + } + Ok(()) + } + pub fn read_merkle_cap(&mut self, cap_height: usize) -> Result> { + let cap_length = 1 << cap_height; + Ok(MerkleCap( + (0..cap_length) + .map(|_| self.read_hash()) + .collect::>>()?, + )) + } + + pub fn write_field_vec(&mut self, v: &[F]) -> Result<()> { + for &a in v { + self.write_field(a)?; + } + Ok(()) + } + pub fn read_field_vec(&mut self, length: usize) -> Result> { + Ok((0..length) + .map(|_| self.read_field()) + .collect::>>()?) + } + + pub fn write_field_ext_vec, const D: usize>( + &mut self, + v: &[F::Extension], + ) -> Result<()> { + for &a in v { + self.write_field_ext::(a)?; + } + Ok(()) + } + pub fn read_field_ext_vec, const D: usize>( + &mut self, + length: usize, + ) -> Result> { + Ok((0..length) + .map(|_| self.read_field_ext::()) + .collect::>>()?) + } + + pub fn write_opening_set, const D: usize>( + &mut self, + os: &OpeningSet, + ) -> Result<()> { + self.write_field_ext_vec::(&os.constants)?; + self.write_field_ext_vec::(&os.plonk_sigmas)?; + self.write_field_ext_vec::(&os.wires)?; + self.write_field_ext_vec::(&os.plonk_zs)?; + self.write_field_ext_vec::(&os.plonk_zs_right)?; + self.write_field_ext_vec::(&os.partial_products)?; + self.write_field_ext_vec::(&os.quotient_polys) + } + pub fn read_opening_set, const D: usize>( + &mut self, + common_data: &CommonCircuitData, + config: &CircuitConfig, + ) -> Result> { + let constants = self.read_field_ext_vec::(common_data.num_constants)?; + let plonk_sigmas = self.read_field_ext_vec::(config.num_routed_wires)?; + let wires = self.read_field_ext_vec::(config.num_wires)?; + let plonk_zs = self.read_field_ext_vec::(config.num_challenges)?; + let plonk_zs_right = self.read_field_ext_vec::(config.num_challenges)?; + let partial_products = self.read_field_ext_vec::( + common_data.num_partial_products.0 * config.num_challenges, + )?; + let quotient_polys = self.read_field_ext_vec::( + common_data.quotient_degree_factor * config.num_challenges, + )?; + Ok(OpeningSet { + constants, + plonk_sigmas, + wires, + plonk_zs, + plonk_zs_right, + partial_products, + quotient_polys, + }) + } + + pub fn write_merkle_proof(&mut self, p: &MerkleProof) -> Result<()> { + let length = p.siblings.len(); + self.write_u8( + length + .try_into() + .expect("Merkle proof length must fit in u8."), + ); + for &h in &p.siblings { + self.write_hash(h)?; + } + Ok(()) + } + pub fn read_merkle_proof(&mut self) -> Result> { + let length = self.read_u8()?; + Ok(MerkleProof { + siblings: (0..length) + .map(|_| self.read_hash()) + .collect::>>()?, + }) + } + + pub fn write_fri_initial_proof( + &mut self, + fitp: &FriInitialTreeProof, + ) -> Result<()> { + for (v, p) in &fitp.evals_proofs { + self.write_field_vec(v)?; + self.write_merkle_proof(p)?; + } + Ok(()) + } + pub fn read_fri_initial_proof, const D: usize>( + &mut self, + common_data: &CommonCircuitData, + config: &CircuitConfig, + ) -> Result> { + let mut evals_proofs = Vec::with_capacity(4); + + let constants_sigmas_v = + self.read_field_vec(common_data.num_constants + config.num_routed_wires)?; + let constants_sigmas_p = self.read_merkle_proof()?; + evals_proofs.push((constants_sigmas_v, constants_sigmas_p)); + + let wires_v = self.read_field_vec(config.num_wires)?; + let wires_p = self.read_merkle_proof()?; + evals_proofs.push((wires_v, wires_p)); + + let zs_partial_v = + self.read_field_vec(config.num_challenges * (1 + common_data.num_partial_products.0))?; + let zs_partial_p = self.read_merkle_proof()?; + evals_proofs.push((zs_partial_v, zs_partial_p)); + + let quotient_v = + self.read_field_vec(config.num_challenges * common_data.quotient_degree_factor)?; + let quotient_p = self.read_merkle_proof()?; + evals_proofs.push((quotient_v, quotient_p)); + + Ok(FriInitialTreeProof { evals_proofs }) + } + + pub fn write_fri_query_steps, const D: usize>( + &mut self, + fqss: &[FriQueryStep], + ) -> Result<()> { + for fqs in fqss { + self.write_field_ext_vec::(&fqs.evals)?; + self.write_merkle_proof(&fqs.merkle_proof)?; + } + Ok(()) + } + pub fn read_fri_query_steps, const D: usize>( + &mut self, + config: &CircuitConfig, + ) -> Result>> { + let mut fqss = Vec::with_capacity(config.fri_config.reduction_arity_bits.len()); + for &arity_bits in &config.fri_config.reduction_arity_bits { + let evals = self.read_field_ext_vec::(1 << arity_bits)?; + let merkle_proof = self.read_merkle_proof()?; + fqss.push(FriQueryStep { + evals, + merkle_proof, + }) + } + Ok(fqss) + } + + pub fn write_fri_query_rounds, const D: usize>( + &mut self, + fqrs: &[FriQueryRound], + ) -> Result<()> { + for fqr in fqrs { + self.write_fri_initial_proof(&fqr.initial_trees_proof)?; + self.write_fri_query_steps(&fqr.steps)?; + } + Ok(()) + } + pub fn read_fri_query_rounds, const D: usize>( + &mut self, + common_data: &CommonCircuitData, + config: &CircuitConfig, + ) -> Result>> { + let mut fqrs = Vec::with_capacity(config.fri_config.num_query_rounds); + for i in 0..config.fri_config.num_query_rounds { + let initial_trees_proof = self.read_fri_initial_proof(common_data, config)?; + let steps = self.read_fri_query_steps(config)?; + fqrs.push(FriQueryRound { + initial_trees_proof, + steps, + }) + } + Ok(fqrs) + } + + pub fn write_fri_proof, const D: usize>( + &mut self, + fp: &FriProof, + ) -> Result<()> { + for cap in &fp.commit_phase_merkle_caps { + self.write_merkle_cap(cap)?; + } + self.write_fri_query_rounds(&fp.query_round_proofs)?; + self.write_field_ext_vec::(&fp.final_poly.coeffs)?; + self.write_field(fp.pow_witness) + } + pub fn read_fri_proof, const D: usize>( + &mut self, + common_data: &CommonCircuitData, + config: &CircuitConfig, + ) -> Result> { + let commit_phase_merkle_caps = (0..config.fri_config.reduction_arity_bits.len()) + .map(|_| self.read_merkle_cap(config.cap_height)) + .collect::>>()?; + let query_round_proofs = self.read_fri_query_rounds(common_data, config)?; + let final_poly = PolynomialCoeffs::new(self.read_field_ext_vec::( + 1 << (common_data.degree_bits + - config.fri_config.reduction_arity_bits.iter().sum::()), + )?); + let pow_witness = self.read_field()?; + Ok(FriProof { + commit_phase_merkle_caps, + query_round_proofs, + final_poly, + pow_witness, + }) + } + + pub fn write_proof, const D: usize>( + &mut self, + proof: &Proof, + ) -> Result<()> { + self.write_merkle_cap(&proof.wires_cap)?; + self.write_merkle_cap(&proof.plonk_zs_partial_products_cap)?; + self.write_merkle_cap(&proof.quotient_polys_cap)?; + self.write_opening_set(&proof.openings)?; + self.write_fri_proof(&proof.opening_proof) + } + pub fn read_proof, const D: usize>( + &mut self, + common_data: &CommonCircuitData, + config: &CircuitConfig, + ) -> Result> { + let wires_cap = self.read_merkle_cap(config.cap_height)?; + let plonk_zs_partial_products_cap = self.read_merkle_cap(config.cap_height)?; + let quotient_polys_cap = self.read_merkle_cap(config.cap_height)?; + let openings = self.read_opening_set(common_data, config)?; + let opening_proof = self.read_fri_proof(common_data, config)?; + + Ok(Proof { + wires_cap, + plonk_zs_partial_products_cap, + quotient_polys_cap, + openings, + opening_proof, + }) + } +} + +#[test] +fn yo() { + type F = CrandallField; + type FF = QuarticExtension; + let mut buffer = Buffer::new(Vec::new()); + let x = FF::rand(); + buffer.write_field_ext::(x).unwrap(); + let mut buffer = Buffer::new(buffer.0.into_inner()); + let y: FF = buffer.read_field_ext::().unwrap(); +} From fb585064a9d311b4cfddf7b7723fbe0367b3419b Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 1 Oct 2021 16:54:14 +0200 Subject: [PATCH 07/17] (De)Serializer for CompressedProof --- src/fri/proof.rs | 3 + src/plonk/proof.rs | 35 +++- src/plonk/recursive_verifier.rs | 27 +-- src/util/serialization.rs | 300 ++++++++++++++++++++++++++------ 4 files changed, 301 insertions(+), 64 deletions(-) diff --git a/src/fri/proof.rs b/src/fri/proof.rs index 44cef3dc..f0d13ebe 100644 --- a/src/fri/proof.rs +++ b/src/fri/proof.rs @@ -83,6 +83,8 @@ pub struct FriQueryRoundTarget { #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] #[serde(bound = "")] pub struct CompressedFriQueryRounds, const D: usize> { + /// Query indices. + pub indices: Vec, /// Map from initial indices `i` to the `FriInitialProof` for the `i`th leaf. pub initial_trees_proofs: HashMap>, /// For each FRI query step, a map from initial indices `i` to the `FriInitialProof` for the `i`th leaf. @@ -182,6 +184,7 @@ impl, const D: usize> FriProof { .collect::>(); let mut compressed_query_proofs = CompressedFriQueryRounds { + indices: indices.to_vec(), initial_trees_proofs: HashMap::new(), steps: vec![HashMap::new(); num_reductions], }; diff --git a/src/plonk/proof.rs b/src/plonk/proof.rs index c7d0b0ff..4c9bfc65 100644 --- a/src/plonk/proof.rs +++ b/src/plonk/proof.rs @@ -10,7 +10,8 @@ use crate::hash::hash_types::{HashOut, MerkleCapTarget}; use crate::hash::hashing::hash_n_to_hash; use crate::hash::merkle_tree::MerkleCap; use crate::iop::target::Target; -use crate::plonk::circuit_data::CommonCircuitData; +use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData}; +use crate::util::serialization::Buffer; #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] #[serde(bound = "")] @@ -83,6 +84,22 @@ impl, const D: usize> ProofWithPublicInputs { pub(crate) fn get_public_inputs_hash(&self) -> HashOut { hash_n_to_hash(self.public_inputs.clone(), true) } + + pub fn to_bytes(&self) -> anyhow::Result> { + let mut buffer = Buffer::new(Vec::new()); + buffer.write_proof_with_public_inputs(&self)?; + Ok(buffer.bytes()) + } + + pub fn from_bytes( + bytes: Vec, + common_data: &CommonCircuitData, + config: &CircuitConfig, + ) -> anyhow::Result { + let mut buffer = Buffer::new(bytes); + let proof = buffer.read_proof_with_public_inputs(common_data, config)?; + Ok(proof) + } } #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] @@ -148,6 +165,22 @@ impl, const D: usize> CompressedProofWithPublicInpu pub(crate) fn get_public_inputs_hash(&self) -> HashOut { hash_n_to_hash(self.public_inputs.clone(), true) } + + pub fn to_bytes(&self) -> anyhow::Result> { + let mut buffer = Buffer::new(Vec::new()); + buffer.write_compressed_proof_with_public_inputs(&self)?; + Ok(buffer.bytes()) + } + + pub fn from_bytes( + bytes: Vec, + common_data: &CommonCircuitData, + config: &CircuitConfig, + ) -> anyhow::Result { + let mut buffer = Buffer::new(bytes); + let proof = buffer.read_compressed_proof_with_public_inputs(common_data, config)?; + Ok(proof) + } } pub(crate) struct ProofChallenges, const D: usize> { diff --git a/src/plonk/recursive_verifier.rs b/src/plonk/recursive_verifier.rs index 7babb26f..a2351700 100644 --- a/src/plonk/recursive_verifier.rs +++ b/src/plonk/recursive_verifier.rs @@ -136,7 +136,10 @@ mod tests { use crate::gadgets::polynomial::PolynomialCoeffsExtTarget; use crate::hash::merkle_proofs::MerkleProofTarget; use crate::iop::witness::{PartialWitness, Witness}; - use crate::plonk::proof::{OpeningSetTarget, Proof, ProofTarget, ProofWithPublicInputs}; + use crate::plonk::proof::{ + CompressedProofWithPublicInputs, OpeningSetTarget, Proof, ProofTarget, + ProofWithPublicInputs, + }; use crate::plonk::verifier::verify; use crate::util::log2_strict; use crate::util::serialization::Buffer; @@ -481,23 +484,25 @@ mod tests { builder.print_gate_counts(0); let data = builder.build(); let recursive_proof = data.prove(pw)?; - let mut buffer = Buffer::new(Vec::new()); - buffer.write_proof(&recursive_proof.proof).unwrap(); - dbg!(recursive_proof.public_inputs.len()); - dbg!(buffer.len()); - let mut buffer = Buffer::new(buffer.bytes()); - let proof = buffer.read_proof(&data.common, &config).unwrap(); - assert_eq!(recursive_proof.proof, proof); + let proof_bytes = recursive_proof.to_bytes()?; + info!("Proof length: {} bytes", proof_bytes.len()); + let proof_from_bytes = + ProofWithPublicInputs::from_bytes(proof_bytes, &data.common, &config)?; + assert_eq!(recursive_proof, proof_from_bytes); let now = std::time::Instant::now(); let compressed_recursive_proof = recursive_proof.clone().compress(&data.common)?; info!("{:.4} to compress proof", now.elapsed().as_secs_f64()); - let proof_bytes = serde_cbor::to_vec(&recursive_proof).unwrap(); - info!("Proof length: {} bytes", proof_bytes.len()); - let compressed_proof_bytes = serde_cbor::to_vec(&compressed_recursive_proof).unwrap(); + let compressed_proof_bytes = compressed_recursive_proof.to_bytes()?; info!( "Compressed proof length: {} bytes", compressed_proof_bytes.len() ); + let compressed_proof_from_bytes = CompressedProofWithPublicInputs::from_bytes( + compressed_proof_bytes, + &data.common, + &config, + )?; + assert_eq!(compressed_recursive_proof, compressed_proof_from_bytes); verify(recursive_proof, &data.verifier_only, &data.common) } } diff --git a/src/util/serialization.rs b/src/util/serialization.rs index 5a32becb..2c6a1d68 100644 --- a/src/util/serialization.rs +++ b/src/util/serialization.rs @@ -1,18 +1,25 @@ +use std::collections::HashMap; use std::convert::TryInto; use std::fmt; use std::io::Cursor; use std::io::{Error, ErrorKind, Read, Result, Write}; +use std::iter::FromIterator; use crate::field::crandall_field::CrandallField; use crate::field::extension_field::quartic::QuarticExtension; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field_types::{Field, PrimeField, RichField}; -use crate::fri::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep}; +use crate::fri::proof::{ + CompressedFriProof, CompressedFriQueryRounds, FriInitialTreeProof, FriProof, FriQueryRound, + FriQueryStep, +}; use crate::hash::hash_types::HashOut; use crate::hash::merkle_proofs::MerkleProof; use crate::hash::merkle_tree::{MerkleCap, MerkleTree}; use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData}; -use crate::plonk::proof::{OpeningSet, Proof}; +use crate::plonk::proof::{ + CompressedProof, CompressedProofWithPublicInputs, OpeningSet, Proof, ProofWithPublicInputs, +}; use crate::polynomial::polynomial::PolynomialCoeffs; #[derive(Debug)] @@ -31,19 +38,28 @@ impl Buffer { self.0.into_inner() } - pub fn write_u8(&mut self, x: u8) -> Result<()> { + fn write_u8(&mut self, x: u8) -> Result<()> { self.0.write_all(&[x]) } - pub fn read_u8(&mut self) -> Result { - let mut buf = [0; 1]; + fn read_u8(&mut self) -> Result { + let mut buf = [0; std::mem::size_of::()]; self.0.read_exact(&mut buf)?; Ok(buf[0]) } - pub fn write_field(&mut self, x: F) -> Result<()> { + fn write_u32(&mut self, x: u32) -> Result<()> { + self.0.write_all(&x.to_le_bytes()) + } + fn read_u32(&mut self) -> Result { + let mut buf = [0; std::mem::size_of::()]; + self.0.read_exact(&mut buf)?; + Ok(u32::from_le_bytes(buf)) + } + + fn write_field(&mut self, x: F) -> Result<()> { self.0.write_all(&x.to_canonical_u64().to_le_bytes()) } - pub fn read_field(&mut self) -> Result { + fn read_field(&mut self) -> Result { let mut buf = [0; std::mem::size_of::()]; self.0.read_exact(&mut buf)?; Ok(F::from_canonical_u64(u64::from_le_bytes( @@ -51,16 +67,13 @@ impl Buffer { ))) } - pub fn write_field_ext, const D: usize>( - &mut self, - x: F::Extension, - ) -> Result<()> { + fn write_field_ext, const D: usize>(&mut self, x: F::Extension) -> Result<()> { for &a in &x.to_basefield_array() { self.write_field(a)?; } Ok(()) } - pub fn read_field_ext, const D: usize>(&mut self) -> Result { + fn read_field_ext, const D: usize>(&mut self) -> Result { let mut arr = [F::ZERO; D]; for a in arr.iter_mut() { *a = self.read_field()?; @@ -70,13 +83,13 @@ impl Buffer { )) } - pub fn write_hash(&mut self, h: HashOut) -> Result<()> { + fn write_hash(&mut self, h: HashOut) -> Result<()> { for &a in &h.elements { self.write_field(a)?; } Ok(()) } - pub fn read_hash(&mut self) -> Result> { + fn read_hash(&mut self) -> Result> { let mut elements = [F::ZERO; 4]; for a in elements.iter_mut() { *a = self.read_field()?; @@ -84,13 +97,13 @@ impl Buffer { Ok(HashOut { elements }) } - pub fn write_merkle_cap(&mut self, cap: &MerkleCap) -> Result<()> { + fn write_merkle_cap(&mut self, cap: &MerkleCap) -> Result<()> { for &a in &cap.0 { self.write_hash(a)?; } Ok(()) } - pub fn read_merkle_cap(&mut self, cap_height: usize) -> Result> { + fn read_merkle_cap(&mut self, cap_height: usize) -> Result> { let cap_length = 1 << cap_height; Ok(MerkleCap( (0..cap_length) @@ -99,19 +112,19 @@ impl Buffer { )) } - pub fn write_field_vec(&mut self, v: &[F]) -> Result<()> { + fn write_field_vec(&mut self, v: &[F]) -> Result<()> { for &a in v { self.write_field(a)?; } Ok(()) } - pub fn read_field_vec(&mut self, length: usize) -> Result> { + fn read_field_vec(&mut self, length: usize) -> Result> { Ok((0..length) .map(|_| self.read_field()) .collect::>>()?) } - pub fn write_field_ext_vec, const D: usize>( + fn write_field_ext_vec, const D: usize>( &mut self, v: &[F::Extension], ) -> Result<()> { @@ -120,7 +133,7 @@ impl Buffer { } Ok(()) } - pub fn read_field_ext_vec, const D: usize>( + fn read_field_ext_vec, const D: usize>( &mut self, length: usize, ) -> Result> { @@ -129,7 +142,7 @@ impl Buffer { .collect::>>()?) } - pub fn write_opening_set, const D: usize>( + fn write_opening_set, const D: usize>( &mut self, os: &OpeningSet, ) -> Result<()> { @@ -141,7 +154,7 @@ impl Buffer { self.write_field_ext_vec::(&os.partial_products)?; self.write_field_ext_vec::(&os.quotient_polys) } - pub fn read_opening_set, const D: usize>( + fn read_opening_set, const D: usize>( &mut self, common_data: &CommonCircuitData, config: &CircuitConfig, @@ -168,7 +181,7 @@ impl Buffer { }) } - pub fn write_merkle_proof(&mut self, p: &MerkleProof) -> Result<()> { + fn write_merkle_proof(&mut self, p: &MerkleProof) -> Result<()> { let length = p.siblings.len(); self.write_u8( length @@ -180,7 +193,7 @@ impl Buffer { } Ok(()) } - pub fn read_merkle_proof(&mut self) -> Result> { + fn read_merkle_proof(&mut self) -> Result> { let length = self.read_u8()?; Ok(MerkleProof { siblings: (0..length) @@ -189,7 +202,7 @@ impl Buffer { }) } - pub fn write_fri_initial_proof( + fn write_fri_initial_proof( &mut self, fitp: &FriInitialTreeProof, ) -> Result<()> { @@ -199,7 +212,7 @@ impl Buffer { } Ok(()) } - pub fn read_fri_initial_proof, const D: usize>( + fn read_fri_initial_proof, const D: usize>( &mut self, common_data: &CommonCircuitData, config: &CircuitConfig, @@ -228,43 +241,38 @@ impl Buffer { Ok(FriInitialTreeProof { evals_proofs }) } - pub fn write_fri_query_steps, const D: usize>( + fn write_fri_query_step, const D: usize>( &mut self, - fqss: &[FriQueryStep], + fqs: &FriQueryStep, ) -> Result<()> { - for fqs in fqss { - self.write_field_ext_vec::(&fqs.evals)?; - self.write_merkle_proof(&fqs.merkle_proof)?; - } - Ok(()) + self.write_field_ext_vec::(&fqs.evals)?; + self.write_merkle_proof(&fqs.merkle_proof) } - pub fn read_fri_query_steps, const D: usize>( + fn read_fri_query_step, const D: usize>( &mut self, - config: &CircuitConfig, - ) -> Result>> { - let mut fqss = Vec::with_capacity(config.fri_config.reduction_arity_bits.len()); - for &arity_bits in &config.fri_config.reduction_arity_bits { - let evals = self.read_field_ext_vec::(1 << arity_bits)?; - let merkle_proof = self.read_merkle_proof()?; - fqss.push(FriQueryStep { - evals, - merkle_proof, - }) - } - Ok(fqss) + arity: usize, + ) -> Result> { + let evals = self.read_field_ext_vec::(arity)?; + let merkle_proof = self.read_merkle_proof()?; + Ok(FriQueryStep { + evals, + merkle_proof, + }) } - pub fn write_fri_query_rounds, const D: usize>( + fn write_fri_query_rounds, const D: usize>( &mut self, fqrs: &[FriQueryRound], ) -> Result<()> { for fqr in fqrs { self.write_fri_initial_proof(&fqr.initial_trees_proof)?; - self.write_fri_query_steps(&fqr.steps)?; + for fqs in &fqr.steps { + self.write_fri_query_step(fqs)?; + } } Ok(()) } - pub fn read_fri_query_rounds, const D: usize>( + fn read_fri_query_rounds, const D: usize>( &mut self, common_data: &CommonCircuitData, config: &CircuitConfig, @@ -272,7 +280,12 @@ impl Buffer { let mut fqrs = Vec::with_capacity(config.fri_config.num_query_rounds); for i in 0..config.fri_config.num_query_rounds { let initial_trees_proof = self.read_fri_initial_proof(common_data, config)?; - let steps = self.read_fri_query_steps(config)?; + let steps = config + .fri_config + .reduction_arity_bits + .iter() + .map(|&ar| self.read_fri_query_step(1 << ar)) + .collect::>()?; fqrs.push(FriQueryRound { initial_trees_proof, steps, @@ -281,7 +294,7 @@ impl Buffer { Ok(fqrs) } - pub fn write_fri_proof, const D: usize>( + fn write_fri_proof, const D: usize>( &mut self, fp: &FriProof, ) -> Result<()> { @@ -292,7 +305,7 @@ impl Buffer { self.write_field_ext_vec::(&fp.final_poly.coeffs)?; self.write_field(fp.pow_witness) } - pub fn read_fri_proof, const D: usize>( + fn read_fri_proof, const D: usize>( &mut self, common_data: &CommonCircuitData, config: &CircuitConfig, @@ -343,6 +356,189 @@ impl Buffer { opening_proof, }) } + + pub fn write_proof_with_public_inputs, const D: usize>( + &mut self, + proof_with_pis: &ProofWithPublicInputs, + ) -> Result<()> { + let ProofWithPublicInputs { + proof, + public_inputs, + } = proof_with_pis; + self.write_proof(proof)?; + self.write_field_vec(public_inputs) + } + pub fn read_proof_with_public_inputs, const D: usize>( + &mut self, + common_data: &CommonCircuitData, + config: &CircuitConfig, + ) -> Result> { + let proof = self.read_proof(common_data, config)?; + let public_inputs = self.read_field_vec(self.len() - self.0.position() as usize)?; + + Ok(ProofWithPublicInputs { + proof, + public_inputs, + }) + } + + fn write_compressed_fri_query_rounds, const D: usize>( + &mut self, + cfqrs: &CompressedFriQueryRounds, + ) -> Result<()> { + for &i in &cfqrs.indices { + self.write_u32(i as u32)?; + } + + let mut initial_trees_proofs = cfqrs.initial_trees_proofs.iter().collect::>(); + initial_trees_proofs.sort_by_key(|&x| x.0); + for (_, itp) in initial_trees_proofs { + self.write_fri_initial_proof(itp)?; + } + for h in &cfqrs.steps { + let mut fri_query_steps = h.iter().collect::>(); + fri_query_steps.sort_by_key(|&x| x.0); + for (_, fqs) in fri_query_steps { + self.write_fri_query_step(fqs)?; + } + } + Ok(()) + } + fn read_compressed_fri_query_rounds, const D: usize>( + &mut self, + common_data: &CommonCircuitData, + config: &CircuitConfig, + ) -> Result> { + let original_indices = (0..config.fri_config.num_query_rounds) + .map(|_| self.read_u32().map(|i| i as usize)) + .collect::>>()?; + let mut indices = original_indices.clone(); + indices.sort(); + indices.dedup(); + let mut pairs = Vec::new(); + for &i in &indices { + pairs.push((i, self.read_fri_initial_proof(common_data, config)?)); + } + let initial_trees_proofs = HashMap::from_iter(pairs); + + let mut steps = Vec::with_capacity(config.fri_config.reduction_arity_bits.len()); + for &a in &config.fri_config.reduction_arity_bits { + indices.iter_mut().for_each(|x| { + *x >>= a; + }); + indices.dedup(); + let query_steps = (0..indices.len()) + .map(|_| self.read_fri_query_step(1 << a)) + .collect::>>()?; + steps.push( + indices + .iter() + .copied() + .zip(query_steps) + .collect::>(), + ); + } + + Ok(CompressedFriQueryRounds { + indices: original_indices, + initial_trees_proofs, + steps, + }) + } + + fn write_compressed_fri_proof, const D: usize>( + &mut self, + fp: &CompressedFriProof, + ) -> Result<()> { + for cap in &fp.commit_phase_merkle_caps { + self.write_merkle_cap(cap)?; + } + self.write_compressed_fri_query_rounds(&fp.query_round_proofs)?; + self.write_field_ext_vec::(&fp.final_poly.coeffs)?; + self.write_field(fp.pow_witness) + } + fn read_compressed_fri_proof, const D: usize>( + &mut self, + common_data: &CommonCircuitData, + config: &CircuitConfig, + ) -> Result> { + let commit_phase_merkle_caps = (0..config.fri_config.reduction_arity_bits.len()) + .map(|_| self.read_merkle_cap(config.cap_height)) + .collect::>>()?; + let query_round_proofs = self.read_compressed_fri_query_rounds(common_data, config)?; + let final_poly = PolynomialCoeffs::new(self.read_field_ext_vec::( + 1 << (common_data.degree_bits + - config.fri_config.reduction_arity_bits.iter().sum::()), + )?); + let pow_witness = self.read_field()?; + Ok(CompressedFriProof { + commit_phase_merkle_caps, + query_round_proofs, + final_poly, + pow_witness, + }) + } + + pub fn write_compressed_proof, const D: usize>( + &mut self, + proof: &CompressedProof, + ) -> Result<()> { + self.write_merkle_cap(&proof.wires_cap)?; + self.write_merkle_cap(&proof.plonk_zs_partial_products_cap)?; + self.write_merkle_cap(&proof.quotient_polys_cap)?; + self.write_opening_set(&proof.openings)?; + self.write_compressed_fri_proof(&proof.opening_proof) + } + pub fn read_compressed_proof, const D: usize>( + &mut self, + common_data: &CommonCircuitData, + config: &CircuitConfig, + ) -> Result> { + let wires_cap = self.read_merkle_cap(config.cap_height)?; + let plonk_zs_partial_products_cap = self.read_merkle_cap(config.cap_height)?; + let quotient_polys_cap = self.read_merkle_cap(config.cap_height)?; + let openings = self.read_opening_set(common_data, config)?; + let opening_proof = self.read_compressed_fri_proof(common_data, config)?; + + Ok(CompressedProof { + wires_cap, + plonk_zs_partial_products_cap, + quotient_polys_cap, + openings, + opening_proof, + }) + } + + pub fn write_compressed_proof_with_public_inputs< + F: RichField + Extendable, + const D: usize, + >( + &mut self, + proof_with_pis: &CompressedProofWithPublicInputs, + ) -> Result<()> { + let CompressedProofWithPublicInputs { + proof, + public_inputs, + } = proof_with_pis; + self.write_compressed_proof(proof)?; + self.write_field_vec(public_inputs) + } + pub fn read_compressed_proof_with_public_inputs< + F: RichField + Extendable, + const D: usize, + >( + &mut self, + common_data: &CommonCircuitData, + config: &CircuitConfig, + ) -> Result> { + let proof = self.read_compressed_proof(common_data, config)?; + let public_inputs = self.read_field_vec(self.len() - self.0.position() as usize)?; + + Ok(CompressedProofWithPublicInputs { + proof, + public_inputs, + }) + } } #[test] From 5dcb85e08515ba946fc3755304e258c83256ef7b Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 1 Oct 2021 16:58:55 +0200 Subject: [PATCH 08/17] Minor --- src/gadgets/arithmetic_extension.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/gadgets/arithmetic_extension.rs b/src/gadgets/arithmetic_extension.rs index de608be5..af838ab6 100644 --- a/src/gadgets/arithmetic_extension.rs +++ b/src/gadgets/arithmetic_extension.rs @@ -510,7 +510,6 @@ mod tests { use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::verifier::verify; - use crate::util::serialization::Buffer; #[test] fn test_mul_many() -> Result<()> { @@ -584,7 +583,7 @@ mod tests { let config = CircuitConfig::large_config(); let pw = PartialWitness::new(); - let mut builder = CircuitBuilder::::new(config.clone()); + let mut builder = CircuitBuilder::::new(config); let x = FF::rand_vec(4); let y = FF::rand_vec(4); @@ -603,13 +602,6 @@ mod tests { let data = builder.build(); let proof = data.prove(pw)?; - let mut buffer = Buffer::new(Vec::new()); - buffer.write_proof(&proof.proof).unwrap(); - dbg!(buffer.len()); - let mut buffer = Buffer::new(buffer.bytes()); - let oproof = buffer.read_proof(&data.common, &config).unwrap(); - assert_eq!(proof.proof, oproof); - verify(proof, &data.verifier_only, &data.common) } } From c2765960923c3b5020bb14418e21cbe4e8ae99e8 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 1 Oct 2021 17:05:55 +0200 Subject: [PATCH 09/17] Remove useless test --- src/util/serialization.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/util/serialization.rs b/src/util/serialization.rs index 2c6a1d68..d83839d9 100644 --- a/src/util/serialization.rs +++ b/src/util/serialization.rs @@ -540,14 +540,3 @@ impl Buffer { }) } } - -#[test] -fn yo() { - type F = CrandallField; - type FF = QuarticExtension; - let mut buffer = Buffer::new(Vec::new()); - let x = FF::rand(); - buffer.write_field_ext::(x).unwrap(); - let mut buffer = Buffer::new(buffer.0.into_inner()); - let y: FF = buffer.read_field_ext::().unwrap(); -} From 17ed6a2b040ad299588c32a3e820261b6d380ba3 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 1 Oct 2021 17:12:46 +0200 Subject: [PATCH 10/17] Clippy --- src/plonk/proof.rs | 4 ++-- src/util/serialization.rs | 23 ++++++++++------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/plonk/proof.rs b/src/plonk/proof.rs index 4c9bfc65..8ee20bc7 100644 --- a/src/plonk/proof.rs +++ b/src/plonk/proof.rs @@ -87,7 +87,7 @@ impl, const D: usize> ProofWithPublicInputs { pub fn to_bytes(&self) -> anyhow::Result> { let mut buffer = Buffer::new(Vec::new()); - buffer.write_proof_with_public_inputs(&self)?; + buffer.write_proof_with_public_inputs(self)?; Ok(buffer.bytes()) } @@ -168,7 +168,7 @@ impl, const D: usize> CompressedProofWithPublicInpu pub fn to_bytes(&self) -> anyhow::Result> { let mut buffer = Buffer::new(Vec::new()); - buffer.write_compressed_proof_with_public_inputs(&self)?; + buffer.write_compressed_proof_with_public_inputs(self)?; Ok(buffer.bytes()) } diff --git a/src/util/serialization.rs b/src/util/serialization.rs index d83839d9..bfc0473e 100644 --- a/src/util/serialization.rs +++ b/src/util/serialization.rs @@ -1,21 +1,18 @@ use std::collections::HashMap; use std::convert::TryInto; -use std::fmt; use std::io::Cursor; -use std::io::{Error, ErrorKind, Read, Result, Write}; +use std::io::{Read, Result, Write}; use std::iter::FromIterator; -use crate::field::crandall_field::CrandallField; -use crate::field::extension_field::quartic::QuarticExtension; use crate::field::extension_field::{Extendable, FieldExtension}; -use crate::field::field_types::{Field, PrimeField, RichField}; +use crate::field::field_types::{PrimeField, RichField}; use crate::fri::proof::{ CompressedFriProof, CompressedFriQueryRounds, FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep, }; use crate::hash::hash_types::HashOut; use crate::hash::merkle_proofs::MerkleProof; -use crate::hash::merkle_tree::{MerkleCap, MerkleTree}; +use crate::hash::merkle_tree::MerkleCap; use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData}; use crate::plonk::proof::{ CompressedProof, CompressedProofWithPublicInputs, OpeningSet, Proof, ProofWithPublicInputs, @@ -119,9 +116,9 @@ impl Buffer { Ok(()) } fn read_field_vec(&mut self, length: usize) -> Result> { - Ok((0..length) + (0..length) .map(|_| self.read_field()) - .collect::>>()?) + .collect::>>() } fn write_field_ext_vec, const D: usize>( @@ -137,9 +134,9 @@ impl Buffer { &mut self, length: usize, ) -> Result> { - Ok((0..length) + (0..length) .map(|_| self.read_field_ext::()) - .collect::>>()?) + .collect::>>() } fn write_opening_set, const D: usize>( @@ -187,7 +184,7 @@ impl Buffer { length .try_into() .expect("Merkle proof length must fit in u8."), - ); + )?; for &h in &p.siblings { self.write_hash(h)?; } @@ -278,7 +275,7 @@ impl Buffer { config: &CircuitConfig, ) -> Result>> { let mut fqrs = Vec::with_capacity(config.fri_config.num_query_rounds); - for i in 0..config.fri_config.num_query_rounds { + for _ in 0..config.fri_config.num_query_rounds { let initial_trees_proof = self.read_fri_initial_proof(common_data, config)?; let steps = config .fri_config @@ -413,7 +410,7 @@ impl Buffer { .map(|_| self.read_u32().map(|i| i as usize)) .collect::>>()?; let mut indices = original_indices.clone(); - indices.sort(); + indices.sort_unstable(); indices.dedup(); let mut pairs = Vec::new(); for &i in &indices { From 76d3f488bd80543da0050bcd5af8ceaef7226d9c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Sat, 2 Oct 2021 09:53:31 +0200 Subject: [PATCH 11/17] Fixes --- src/fri/proof.rs | 29 ++++++++++++++++++++++------- src/plonk/proof.rs | 3 ++- src/plonk/recursive_verifier.rs | 4 ++++ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/fri/proof.rs b/src/fri/proof.rs index 44cef3dc..b4ba8efb 100644 --- a/src/fri/proof.rs +++ b/src/fri/proof.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use itertools::izip; use serde::{Deserialize, Serialize}; @@ -85,7 +85,7 @@ pub struct FriQueryRoundTarget { pub struct CompressedFriQueryRounds, const D: usize> { /// Map from initial indices `i` to the `FriInitialProof` for the `i`th leaf. pub initial_trees_proofs: HashMap>, - /// For each FRI query step, a map from initial indices `i` to the `FriInitialProof` for the `i`th leaf. + /// For each FRI query step, a map from indices `i` to the `FriQueryStep` for the `i`th leaf. pub steps: Vec>>, } @@ -200,14 +200,17 @@ impl, const D: usize> FriProof { }; compressed_query_proofs .initial_trees_proofs - .insert(index, initial_proof); + .entry(index) + .or_insert(initial_proof); for j in 0..num_reductions { index >>= reduction_arity_bits[j]; let query_step = FriQueryStep { evals: steps_evals[j][i].clone(), merkle_proof: steps_proofs[j][i].clone(), }; - compressed_query_proofs.steps[j].insert(index, query_step); + compressed_query_proofs.steps[j] + .entry(index) + .or_insert(query_step); } } @@ -261,8 +264,17 @@ impl, const D: usize> CompressedFriProof { }) .collect::>(); + let mut seen_indices = vec![HashSet::new(); num_reductions + 1]; for mut index in indices.iter().copied() { - let initial_trees_proof = query_round_proofs.initial_trees_proofs[&index].clone(); + let mut initial_trees_proof = query_round_proofs.initial_trees_proofs[&index].clone(); + if !seen_indices[0].insert(index) { + initial_trees_proof + .evals_proofs + .iter_mut() + .for_each(|(_, p)| { + p.siblings = vec![]; + }); + } for (i, (leaves_data, proof)) in initial_trees_proof.evals_proofs.into_iter().enumerate() { @@ -272,7 +284,10 @@ impl, const D: usize> CompressedFriProof { } for i in 0..num_reductions { index >>= reduction_arity_bits[i]; - let query_step = query_round_proofs.steps[i][&index].clone(); + let mut query_step = query_round_proofs.steps[i][&index].clone(); + if !seen_indices[1 + i].insert(index) { + query_step.merkle_proof.siblings = vec![]; + } steps_indices[i].push(index); steps_evals[i].push(flatten(&query_step.evals)); steps_proofs[i].push(query_step.merkle_proof); @@ -292,7 +307,7 @@ impl, const D: usize> CompressedFriProof { .collect::>(); let mut decompressed_query_proofs = Vec::with_capacity(num_reductions); - for i in 0..num_reductions { + for i in 0..indices.len() { let initial_trees_proof = FriInitialTreeProof { evals_proofs: (0..num_initial_trees) .map(|j| { diff --git a/src/plonk/proof.rs b/src/plonk/proof.rs index c7d0b0ff..9510687a 100644 --- a/src/plonk/proof.rs +++ b/src/plonk/proof.rs @@ -252,7 +252,8 @@ mod tests { type F = CrandallField; const D: usize = 4; - let config = CircuitConfig::large_config(); + let mut config = CircuitConfig::large_config(); + config.fri_config.num_query_rounds = 50; let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); diff --git a/src/plonk/recursive_verifier.rs b/src/plonk/recursive_verifier.rs index 75158d67..15dd7830 100644 --- a/src/plonk/recursive_verifier.rs +++ b/src/plonk/recursive_verifier.rs @@ -482,6 +482,10 @@ mod tests { let recursive_proof = data.prove(pw)?; let now = std::time::Instant::now(); let compressed_recursive_proof = recursive_proof.clone().compress(&data.common)?; + let decompressed_compressed_proof = compressed_recursive_proof + .clone() + .decompress(&data.common)?; + assert_eq!(recursive_proof, decompressed_compressed_proof); info!("{:.4} to compress proof", now.elapsed().as_secs_f64()); let proof_bytes = serde_cbor::to_vec(&recursive_proof).unwrap(); info!("Proof length: {} bytes", proof_bytes.len()); From 0839ed8793a5b3de6364745c302e01a8b7182066 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Sat, 2 Oct 2021 09:55:38 +0200 Subject: [PATCH 12/17] Unused import --- src/plonk/get_challenges.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plonk/get_challenges.rs b/src/plonk/get_challenges.rs index f1593b46..34034260 100644 --- a/src/plonk/get_challenges.rs +++ b/src/plonk/get_challenges.rs @@ -1,6 +1,5 @@ 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; From 3859ca20905142c599e48baa066ea3bad240d6f2 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Sat, 2 Oct 2021 10:46:02 +0200 Subject: [PATCH 13/17] PR comments --- src/fri/mod.rs | 6 ++++ src/fri/recursive_verifier.rs | 7 ++--- src/fri/verifier.rs | 6 ++-- src/plonk/circuit_builder.rs | 7 +---- src/plonk/circuit_data.rs | 4 +++ src/plonk/proof.rs | 8 ++--- src/plonk/recursive_verifier.rs | 10 ++---- src/util/serialization.rs | 56 +++++++++++++++------------------ 8 files changed, 49 insertions(+), 55 deletions(-) diff --git a/src/fri/mod.rs b/src/fri/mod.rs index 716260a3..5e8936fa 100644 --- a/src/fri/mod.rs +++ b/src/fri/mod.rs @@ -17,3 +17,9 @@ pub struct FriConfig { /// Number of query rounds to perform. pub num_query_rounds: usize, } + +impl FriConfig { + pub(crate) fn total_arities(&self) -> usize { + self.reduction_arity_bits.iter().sum() + } +} diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 2c0d009f..142d1e24 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -81,15 +81,14 @@ impl, const D: usize> CircuitBuilder { common_data: &CommonCircuitData, ) { let config = &common_data.config; - let total_arities = config.fri_config.reduction_arity_bits.iter().sum::(); debug_assert_eq!( - common_data.degree_bits, - log2_strict(proof.final_poly.len()) + total_arities, + common_data.final_poly_len(), + proof.final_poly.len(), "Final polynomial has wrong degree." ); // Size of the LDE domain. - let n = proof.final_poly.len() << (total_arities + config.rate_bits); + let n = 1 << (common_data.degree_bits + config.rate_bits); challenger.observe_opening_set(os); diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 6662830a..efbd733b 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -64,14 +64,14 @@ pub(crate) fn verify_fri_proof, const D: usize>( common_data: &CommonCircuitData, ) -> Result<()> { let config = &common_data.config; - let total_arities = config.fri_config.reduction_arity_bits.iter().sum::(); + let total_arities = config.fri_config.total_arities(); ensure!( - common_data.degree_bits == log2_strict(proof.final_poly.len()) + total_arities, + common_data.final_poly_len() == proof.final_poly.len(), "Final polynomial has wrong degree." ); // Size of the LDE domain. - let n = proof.final_poly.len() << (total_arities + config.rate_bits); + let n = 1 << (common_data.degree_bits + config.rate_bits); // Check PoW. fri_verify_proof_of_work(challenges.fri_pow_response, &config.fri_config)?; diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index 035a6d90..18f84681 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -579,12 +579,7 @@ impl, const D: usize> CircuitBuilder { info!("Degree after blinding & padding: {}", degree); let degree_bits = log2_strict(degree); assert!( - self.config - .fri_config - .reduction_arity_bits - .iter() - .sum::() - <= degree_bits, + self.config.fri_config.total_arities() <= degree_bits, "FRI total reduction arity is too large." ); diff --git a/src/plonk/circuit_data.rs b/src/plonk/circuit_data.rs index 644ab370..f8bb0b6a 100644 --- a/src/plonk/circuit_data.rs +++ b/src/plonk/circuit_data.rs @@ -252,6 +252,10 @@ impl, const D: usize> CommonCircuitData { pub fn partial_products_range(&self) -> RangeFrom { self.config.num_challenges.. } + + pub fn final_poly_len(&self) -> usize { + 1 << (self.degree_bits - self.config.fri_config.total_arities()) + } } /// The `Target` version of `VerifierCircuitData`, for use inside recursive circuits. Note that this diff --git a/src/plonk/proof.rs b/src/plonk/proof.rs index 8ee20bc7..d26e5fb1 100644 --- a/src/plonk/proof.rs +++ b/src/plonk/proof.rs @@ -10,7 +10,7 @@ use crate::hash::hash_types::{HashOut, MerkleCapTarget}; use crate::hash::hashing::hash_n_to_hash; use crate::hash::merkle_tree::MerkleCap; use crate::iop::target::Target; -use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData}; +use crate::plonk::circuit_data::CommonCircuitData; use crate::util::serialization::Buffer; #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] @@ -94,10 +94,9 @@ impl, const D: usize> ProofWithPublicInputs { pub fn from_bytes( bytes: Vec, common_data: &CommonCircuitData, - config: &CircuitConfig, ) -> anyhow::Result { let mut buffer = Buffer::new(bytes); - let proof = buffer.read_proof_with_public_inputs(common_data, config)?; + let proof = buffer.read_proof_with_public_inputs(common_data)?; Ok(proof) } } @@ -175,10 +174,9 @@ impl, const D: usize> CompressedProofWithPublicInpu pub fn from_bytes( bytes: Vec, common_data: &CommonCircuitData, - config: &CircuitConfig, ) -> anyhow::Result { let mut buffer = Buffer::new(bytes); - let proof = buffer.read_compressed_proof_with_public_inputs(common_data, config)?; + let proof = buffer.read_compressed_proof_with_public_inputs(common_data)?; Ok(proof) } } diff --git a/src/plonk/recursive_verifier.rs b/src/plonk/recursive_verifier.rs index a2351700..5a485495 100644 --- a/src/plonk/recursive_verifier.rs +++ b/src/plonk/recursive_verifier.rs @@ -486,8 +486,7 @@ mod tests { let recursive_proof = data.prove(pw)?; let proof_bytes = recursive_proof.to_bytes()?; info!("Proof length: {} bytes", proof_bytes.len()); - let proof_from_bytes = - ProofWithPublicInputs::from_bytes(proof_bytes, &data.common, &config)?; + let proof_from_bytes = ProofWithPublicInputs::from_bytes(proof_bytes, &data.common)?; assert_eq!(recursive_proof, proof_from_bytes); let now = std::time::Instant::now(); let compressed_recursive_proof = recursive_proof.clone().compress(&data.common)?; @@ -497,11 +496,8 @@ mod tests { "Compressed proof length: {} bytes", compressed_proof_bytes.len() ); - let compressed_proof_from_bytes = CompressedProofWithPublicInputs::from_bytes( - compressed_proof_bytes, - &data.common, - &config, - )?; + let compressed_proof_from_bytes = + CompressedProofWithPublicInputs::from_bytes(compressed_proof_bytes, &data.common)?; assert_eq!(compressed_recursive_proof, compressed_proof_from_bytes); verify(recursive_proof, &data.verifier_only, &data.common) } diff --git a/src/util/serialization.rs b/src/util/serialization.rs index bfc0473e..b3737363 100644 --- a/src/util/serialization.rs +++ b/src/util/serialization.rs @@ -13,7 +13,7 @@ use crate::fri::proof::{ use crate::hash::hash_types::HashOut; use crate::hash::merkle_proofs::MerkleProof; use crate::hash::merkle_tree::MerkleCap; -use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData}; +use crate::plonk::circuit_data::CommonCircuitData; use crate::plonk::proof::{ CompressedProof, CompressedProofWithPublicInputs, OpeningSet, Proof, ProofWithPublicInputs, }; @@ -154,8 +154,8 @@ impl Buffer { fn read_opening_set, const D: usize>( &mut self, common_data: &CommonCircuitData, - config: &CircuitConfig, ) -> Result> { + let config = &common_data.config; let constants = self.read_field_ext_vec::(common_data.num_constants)?; let plonk_sigmas = self.read_field_ext_vec::(config.num_routed_wires)?; let wires = self.read_field_ext_vec::(config.num_wires)?; @@ -212,8 +212,8 @@ impl Buffer { fn read_fri_initial_proof, const D: usize>( &mut self, common_data: &CommonCircuitData, - config: &CircuitConfig, ) -> Result> { + let config = &common_data.config; let mut evals_proofs = Vec::with_capacity(4); let constants_sigmas_v = @@ -272,11 +272,11 @@ impl Buffer { fn read_fri_query_rounds, const D: usize>( &mut self, common_data: &CommonCircuitData, - config: &CircuitConfig, ) -> Result>> { + let config = &common_data.config; let mut fqrs = Vec::with_capacity(config.fri_config.num_query_rounds); for _ in 0..config.fri_config.num_query_rounds { - let initial_trees_proof = self.read_fri_initial_proof(common_data, config)?; + let initial_trees_proof = self.read_fri_initial_proof(common_data)?; let steps = config .fri_config .reduction_arity_bits @@ -305,16 +305,14 @@ impl Buffer { fn read_fri_proof, const D: usize>( &mut self, common_data: &CommonCircuitData, - config: &CircuitConfig, ) -> Result> { + let config = &common_data.config; let commit_phase_merkle_caps = (0..config.fri_config.reduction_arity_bits.len()) .map(|_| self.read_merkle_cap(config.cap_height)) .collect::>>()?; - let query_round_proofs = self.read_fri_query_rounds(common_data, config)?; - let final_poly = PolynomialCoeffs::new(self.read_field_ext_vec::( - 1 << (common_data.degree_bits - - config.fri_config.reduction_arity_bits.iter().sum::()), - )?); + let query_round_proofs = self.read_fri_query_rounds(common_data)?; + let final_poly = + PolynomialCoeffs::new(self.read_field_ext_vec::(common_data.final_poly_len())?); let pow_witness = self.read_field()?; Ok(FriProof { commit_phase_merkle_caps, @@ -337,13 +335,13 @@ impl Buffer { pub fn read_proof, const D: usize>( &mut self, common_data: &CommonCircuitData, - config: &CircuitConfig, ) -> Result> { + let config = &common_data.config; let wires_cap = self.read_merkle_cap(config.cap_height)?; let plonk_zs_partial_products_cap = self.read_merkle_cap(config.cap_height)?; let quotient_polys_cap = self.read_merkle_cap(config.cap_height)?; - let openings = self.read_opening_set(common_data, config)?; - let opening_proof = self.read_fri_proof(common_data, config)?; + let openings = self.read_opening_set(common_data)?; + let opening_proof = self.read_fri_proof(common_data)?; Ok(Proof { wires_cap, @@ -368,10 +366,11 @@ impl Buffer { pub fn read_proof_with_public_inputs, const D: usize>( &mut self, common_data: &CommonCircuitData, - config: &CircuitConfig, ) -> Result> { - let proof = self.read_proof(common_data, config)?; - let public_inputs = self.read_field_vec(self.len() - self.0.position() as usize)?; + let proof = self.read_proof(common_data)?; + let public_inputs = self.read_field_vec( + (self.len() - self.0.position() as usize) / std::mem::size_of::(), + )?; Ok(ProofWithPublicInputs { proof, @@ -404,8 +403,8 @@ impl Buffer { fn read_compressed_fri_query_rounds, const D: usize>( &mut self, common_data: &CommonCircuitData, - config: &CircuitConfig, ) -> Result> { + let config = &common_data.config; let original_indices = (0..config.fri_config.num_query_rounds) .map(|_| self.read_u32().map(|i| i as usize)) .collect::>>()?; @@ -414,7 +413,7 @@ impl Buffer { indices.dedup(); let mut pairs = Vec::new(); for &i in &indices { - pairs.push((i, self.read_fri_initial_proof(common_data, config)?)); + pairs.push((i, self.read_fri_initial_proof(common_data)?)); } let initial_trees_proofs = HashMap::from_iter(pairs); @@ -457,16 +456,14 @@ impl Buffer { fn read_compressed_fri_proof, const D: usize>( &mut self, common_data: &CommonCircuitData, - config: &CircuitConfig, ) -> Result> { + let config = &common_data.config; let commit_phase_merkle_caps = (0..config.fri_config.reduction_arity_bits.len()) .map(|_| self.read_merkle_cap(config.cap_height)) .collect::>>()?; - let query_round_proofs = self.read_compressed_fri_query_rounds(common_data, config)?; - let final_poly = PolynomialCoeffs::new(self.read_field_ext_vec::( - 1 << (common_data.degree_bits - - config.fri_config.reduction_arity_bits.iter().sum::()), - )?); + let query_round_proofs = self.read_compressed_fri_query_rounds(common_data)?; + let final_poly = + PolynomialCoeffs::new(self.read_field_ext_vec::(common_data.final_poly_len())?); let pow_witness = self.read_field()?; Ok(CompressedFriProof { commit_phase_merkle_caps, @@ -489,13 +486,13 @@ impl Buffer { pub fn read_compressed_proof, const D: usize>( &mut self, common_data: &CommonCircuitData, - config: &CircuitConfig, ) -> Result> { + let config = &common_data.config; let wires_cap = self.read_merkle_cap(config.cap_height)?; let plonk_zs_partial_products_cap = self.read_merkle_cap(config.cap_height)?; let quotient_polys_cap = self.read_merkle_cap(config.cap_height)?; - let openings = self.read_opening_set(common_data, config)?; - let opening_proof = self.read_compressed_fri_proof(common_data, config)?; + let openings = self.read_opening_set(common_data)?; + let opening_proof = self.read_compressed_fri_proof(common_data)?; Ok(CompressedProof { wires_cap, @@ -526,9 +523,8 @@ impl Buffer { >( &mut self, common_data: &CommonCircuitData, - config: &CircuitConfig, ) -> Result> { - let proof = self.read_compressed_proof(common_data, config)?; + let proof = self.read_compressed_proof(common_data)?; let public_inputs = self.read_field_vec(self.len() - self.0.position() as usize)?; Ok(CompressedProofWithPublicInputs { From fbefaa4768f16d57466381bf34d80c398a6b614f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Sat, 2 Oct 2021 14:01:08 +0200 Subject: [PATCH 14/17] Unused import --- src/plonk/recursive_verifier.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plonk/recursive_verifier.rs b/src/plonk/recursive_verifier.rs index 3855ca3a..33bcbb67 100644 --- a/src/plonk/recursive_verifier.rs +++ b/src/plonk/recursive_verifier.rs @@ -142,7 +142,6 @@ mod tests { }; use crate::plonk::verifier::verify; use crate::util::log2_strict; - use crate::util::serialization::Buffer; // Construct a `FriQueryRoundTarget` with the same dimensions as the ones in `proof`. fn get_fri_query_round, const D: usize>( From bbeb54c910fed00c2ea1c80ae4073b39d77d903a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 4 Oct 2021 10:03:00 +0200 Subject: [PATCH 15/17] Remove pruning --- src/fri/proof.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/fri/proof.rs b/src/fri/proof.rs index b4ba8efb..e99a5e2b 100644 --- a/src/fri/proof.rs +++ b/src/fri/proof.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use itertools::izip; use serde::{Deserialize, Serialize}; @@ -264,17 +264,8 @@ impl, const D: usize> CompressedFriProof { }) .collect::>(); - let mut seen_indices = vec![HashSet::new(); num_reductions + 1]; for mut index in indices.iter().copied() { let mut initial_trees_proof = query_round_proofs.initial_trees_proofs[&index].clone(); - if !seen_indices[0].insert(index) { - initial_trees_proof - .evals_proofs - .iter_mut() - .for_each(|(_, p)| { - p.siblings = vec![]; - }); - } for (i, (leaves_data, proof)) in initial_trees_proof.evals_proofs.into_iter().enumerate() { @@ -285,9 +276,6 @@ impl, const D: usize> CompressedFriProof { for i in 0..num_reductions { index >>= reduction_arity_bits[i]; let mut query_step = query_round_proofs.steps[i][&index].clone(); - if !seen_indices[1 + i].insert(index) { - query_step.merkle_proof.siblings = vec![]; - } steps_indices[i].push(index); steps_evals[i].push(flatten(&query_step.evals)); steps_proofs[i].push(query_step.merkle_proof); From b4614991f7a720d3df6404e4bd59d35d95c86359 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 4 Oct 2021 10:09:17 +0200 Subject: [PATCH 16/17] Useless `mut` --- src/fri/proof.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fri/proof.rs b/src/fri/proof.rs index e99a5e2b..1761744d 100644 --- a/src/fri/proof.rs +++ b/src/fri/proof.rs @@ -265,7 +265,7 @@ impl, const D: usize> CompressedFriProof { .collect::>(); for mut index in indices.iter().copied() { - let mut initial_trees_proof = query_round_proofs.initial_trees_proofs[&index].clone(); + 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() { @@ -275,7 +275,7 @@ impl, const D: usize> CompressedFriProof { } for i in 0..num_reductions { index >>= reduction_arity_bits[i]; - let mut query_step = query_round_proofs.steps[i][&index].clone(); + 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); From bce3256c96ce41861f487ccf09d6e02f67ddd1e0 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 4 Oct 2021 10:21:35 +0200 Subject: [PATCH 17/17] PR feedback --- src/fri/recursive_verifier.rs | 2 +- src/fri/verifier.rs | 3 +-- src/util/serialization.rs | 4 +++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 142d1e24..78d5f007 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -88,7 +88,7 @@ impl, const D: usize> CircuitBuilder { ); // Size of the LDE domain. - let n = 1 << (common_data.degree_bits + config.rate_bits); + let n = common_data.lde_size(); challenger.observe_opening_set(os); diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index efbd733b..f9c1d998 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -64,14 +64,13 @@ pub(crate) fn verify_fri_proof, const D: usize>( common_data: &CommonCircuitData, ) -> Result<()> { let config = &common_data.config; - let total_arities = config.fri_config.total_arities(); ensure!( common_data.final_poly_len() == proof.final_poly.len(), "Final polynomial has wrong degree." ); // Size of the LDE domain. - let n = 1 << (common_data.degree_bits + config.rate_bits); + let n = common_data.lde_size(); // Check PoW. fri_verify_proof_of_work(challenges.fri_pow_response, &config.fri_config)?; diff --git a/src/util/serialization.rs b/src/util/serialization.rs index b3737363..21b12c11 100644 --- a/src/util/serialization.rs +++ b/src/util/serialization.rs @@ -525,7 +525,9 @@ impl Buffer { common_data: &CommonCircuitData, ) -> Result> { let proof = self.read_compressed_proof(common_data)?; - let public_inputs = self.read_field_vec(self.len() - self.0.position() as usize)?; + let public_inputs = self.read_field_vec( + (self.len() - self.0.position() as usize) / std::mem::size_of::(), + )?; Ok(CompressedProofWithPublicInputs { proof,