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]