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/proof.rs b/src/fri/proof.rs index 1761744d..62e836af 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 indices `i` to the `FriQueryStep` 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/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 2c0d009f..78d5f007 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 = common_data.lde_size(); challenger.observe_opening_set(os); diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 6662830a..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.reduction_arity_bits.iter().sum::(); 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 = common_data.lde_size(); // 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 9510687a..4021f3cb 100644 --- a/src/plonk/proof.rs +++ b/src/plonk/proof.rs @@ -11,6 +11,7 @@ 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::util::serialization::Buffer; #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] #[serde(bound = "")] @@ -83,6 +84,21 @@ 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, + ) -> anyhow::Result { + let mut buffer = Buffer::new(bytes); + let proof = buffer.read_proof_with_public_inputs(common_data)?; + Ok(proof) + } } #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] @@ -148,6 +164,21 @@ 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, + ) -> anyhow::Result { + let mut buffer = Buffer::new(bytes); + let proof = buffer.read_compressed_proof_with_public_inputs(common_data)?; + Ok(proof) + } } pub(crate) struct ProofChallenges, const D: usize> { diff --git a/src/plonk/recursive_verifier.rs b/src/plonk/recursive_verifier.rs index 15dd7830..33bcbb67 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; @@ -480,6 +483,10 @@ mod tests { builder.print_gate_counts(0); let data = builder.build(); 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)?; + assert_eq!(recursive_proof, proof_from_bytes); let now = std::time::Instant::now(); let compressed_recursive_proof = recursive_proof.clone().compress(&data.common)?; let decompressed_compressed_proof = compressed_recursive_proof @@ -487,13 +494,14 @@ mod tests { .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()); - 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)?; + assert_eq!(compressed_recursive_proof, compressed_proof_from_bytes); verify(recursive_proof, &data.verifier_only, &data.common) } } 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..21b12c11 --- /dev/null +++ b/src/util/serialization.rs @@ -0,0 +1,537 @@ +use std::collections::HashMap; +use std::convert::TryInto; +use std::io::Cursor; +use std::io::{Read, Result, Write}; +use std::iter::FromIterator; + +use crate::field::extension_field::{Extendable, FieldExtension}; +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; +use crate::plonk::circuit_data::CommonCircuitData; +use crate::plonk::proof::{ + CompressedProof, CompressedProofWithPublicInputs, OpeningSet, Proof, ProofWithPublicInputs, +}; +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() + } + + fn write_u8(&mut self, x: u8) -> Result<()> { + self.0.write_all(&[x]) + } + fn read_u8(&mut self) -> Result { + let mut buf = [0; std::mem::size_of::()]; + self.0.read_exact(&mut buf)?; + Ok(buf[0]) + } + + 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()) + } + 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(), + ))) + } + + 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(()) + } + 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, + )) + } + + fn write_hash(&mut self, h: HashOut) -> Result<()> { + for &a in &h.elements { + self.write_field(a)?; + } + Ok(()) + } + 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 }) + } + + fn write_merkle_cap(&mut self, cap: &MerkleCap) -> Result<()> { + for &a in &cap.0 { + self.write_hash(a)?; + } + Ok(()) + } + 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::>>()?, + )) + } + + fn write_field_vec(&mut self, v: &[F]) -> Result<()> { + for &a in v { + self.write_field(a)?; + } + Ok(()) + } + fn read_field_vec(&mut self, length: usize) -> Result> { + (0..length) + .map(|_| self.read_field()) + .collect::>>() + } + + fn write_field_ext_vec, const D: usize>( + &mut self, + v: &[F::Extension], + ) -> Result<()> { + for &a in v { + self.write_field_ext::(a)?; + } + Ok(()) + } + fn read_field_ext_vec, const D: usize>( + &mut self, + length: usize, + ) -> Result> { + (0..length) + .map(|_| self.read_field_ext::()) + .collect::>>() + } + + 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) + } + fn read_opening_set, const D: usize>( + &mut self, + common_data: &CommonCircuitData, + ) -> 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)?; + 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, + }) + } + + 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(()) + } + fn read_merkle_proof(&mut self) -> Result> { + let length = self.read_u8()?; + Ok(MerkleProof { + siblings: (0..length) + .map(|_| self.read_hash()) + .collect::>>()?, + }) + } + + 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(()) + } + fn read_fri_initial_proof, const D: usize>( + &mut self, + common_data: &CommonCircuitData, + ) -> Result> { + let config = &common_data.config; + 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 }) + } + + fn write_fri_query_step, const D: usize>( + &mut self, + fqs: &FriQueryStep, + ) -> Result<()> { + self.write_field_ext_vec::(&fqs.evals)?; + self.write_merkle_proof(&fqs.merkle_proof) + } + fn read_fri_query_step, const D: usize>( + &mut self, + arity: usize, + ) -> Result> { + let evals = self.read_field_ext_vec::(arity)?; + let merkle_proof = self.read_merkle_proof()?; + Ok(FriQueryStep { + evals, + merkle_proof, + }) + } + + 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)?; + for fqs in &fqr.steps { + self.write_fri_query_step(fqs)?; + } + } + Ok(()) + } + fn read_fri_query_rounds, const D: usize>( + &mut self, + common_data: &CommonCircuitData, + ) -> 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)?; + 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, + }) + } + Ok(fqrs) + } + + 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) + } + fn read_fri_proof, const D: usize>( + &mut self, + common_data: &CommonCircuitData, + ) -> 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)?; + 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, + 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, + ) -> 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)?; + let opening_proof = self.read_fri_proof(common_data)?; + + Ok(Proof { + wires_cap, + plonk_zs_partial_products_cap, + quotient_polys_cap, + openings, + 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, + ) -> Result> { + 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, + 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, + ) -> 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::>>()?; + let mut indices = original_indices.clone(); + indices.sort_unstable(); + indices.dedup(); + let mut pairs = Vec::new(); + for &i in &indices { + pairs.push((i, self.read_fri_initial_proof(common_data)?)); + } + 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, + ) -> 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)?; + 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, + 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, + ) -> 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)?; + let opening_proof = self.read_compressed_fri_proof(common_data)?; + + 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, + ) -> Result> { + let proof = self.read_compressed_proof(common_data)?; + let public_inputs = self.read_field_vec( + (self.len() - self.0.position() as usize) / std::mem::size_of::(), + )?; + + Ok(CompressedProofWithPublicInputs { + proof, + public_inputs, + }) + } +}