diff --git a/src/fri/prover.rs b/src/fri/prover.rs index d0fc1f0c..ec58fc14 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -45,7 +45,7 @@ pub fn fri_proof, const D: usize>( let current_hash = challenger.get_hash(); let pow_witness = timed!( timing, - "find for proof-of-work witness", + "find proof-of-work witness", fri_proof_of_work(current_hash, &common_data.config.fri_config) ); diff --git a/src/plonk/circuit_data.rs b/src/plonk/circuit_data.rs index 9cb7ba61..f1257860 100644 --- a/src/plonk/circuit_data.rs +++ b/src/plonk/circuit_data.rs @@ -60,6 +60,24 @@ impl CircuitConfig { self.num_wires - self.num_routed_wires } + /// A typical recursion config, without zero-knowledge, targeting ~100 bit security. + pub(crate) fn standard_recursion_config() -> Self { + Self { + num_wires: 143, + num_routed_wires: 64, + security_bits: 128, + rate_bits: 3, + num_challenges: 3, + zero_knowledge: false, + cap_height: 3, + fri_config: FriConfig { + proof_of_work_bits: 16, + reduction_strategy: FriReductionStrategy::ConstantArityBits(3, 5), + num_query_rounds: 28, + }, + } + } + #[cfg(test)] pub(crate) fn large_config() -> Self { Self { diff --git a/src/plonk/recursive_verifier.rs b/src/plonk/recursive_verifier.rs index 7e95bc6d..d649848c 100644 --- a/src/plonk/recursive_verifier.rs +++ b/src/plonk/recursive_verifier.rs @@ -127,7 +127,6 @@ mod tests { use log::info; use super::*; - use crate::field::field_types::Field; use crate::field::goldilocks_field::GoldilocksField; use crate::fri::proof::{ FriInitialTreeProofTarget, FriProofTarget, FriQueryRoundTarget, FriQueryStepTarget, @@ -137,11 +136,11 @@ mod tests { use crate::gadgets::polynomial::PolynomialCoeffsExtTarget; use crate::hash::merkle_proofs::MerkleProofTarget; use crate::iop::witness::{PartialWitness, Witness}; + use crate::plonk::circuit_data::VerifierOnlyCircuitData; use crate::plonk::proof::{ CompressedProofWithPublicInputs, OpeningSetTarget, Proof, ProofTarget, ProofWithPublicInputs, }; - use crate::plonk::verifier::verify; use crate::util::log2_strict; // Construct a `FriQueryRoundTarget` with the same dimensions as the ones in `proof`. @@ -362,145 +361,156 @@ mod tests { #[test] #[ignore] fn test_recursive_verifier() -> Result<()> { - env_logger::init(); + init_logger(); type F = GoldilocksField; const D: usize = 4; - let config = CircuitConfig { - num_wires: 143, - num_routed_wires: 33, - security_bits: 128, - rate_bits: 3, - num_challenges: 3, - zero_knowledge: false, - cap_height: 2, - fri_config: FriConfig { - proof_of_work_bits: 15, - reduction_strategy: FriReductionStrategy::ConstantArityBits(3, 5), - num_query_rounds: 27, - }, - }; - let (proof_with_pis, vd, cd) = { - let mut builder = CircuitBuilder::::new(config.clone()); - let _two = builder.two(); - let _two = builder.hash_n_to_hash(vec![_two], true).elements[0]; - for _ in 0..10000 { - let _two = builder.mul(_two, _two); - } - let data = builder.build(); - ( - data.prove(PartialWitness::new())?, - data.verifier_only, - data.common, - ) - }; - verify(proof_with_pis.clone(), &vd, &cd)?; + let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config.clone()); - let mut pw = PartialWitness::new(); - let pt = proof_to_proof_target(&proof_with_pis, &mut builder); - set_proof_target(&proof_with_pis, &pt, &mut pw); + let (proof, vd, cd) = dummy_proof::(&config, 8_000)?; + let (proof, _vd, cd) = recursive_proof(proof, vd, cd, &config, &config, true)?; + test_serialization(&proof, &cd)?; - let inner_data = VerifierCircuitTarget { - constants_sigmas_cap: builder.add_virtual_cap(config.cap_height), - }; - pw.set_cap_target(&inner_data.constants_sigmas_cap, &vd.constants_sigmas_cap); - - builder.add_recursive_verifier(pt, &config, &inner_data, &cd); - - builder.print_gate_counts(0); - let data = builder.build(); - let recursive_proof = data.prove(pw)?; - - verify(recursive_proof, &data.verifier_only, &data.common) + Ok(()) } #[test] #[ignore] fn test_recursive_recursive_verifier() -> Result<()> { - env_logger::init(); + init_logger(); type F = GoldilocksField; const D: usize = 4; - let config = CircuitConfig { - num_wires: 143, - num_routed_wires: 64, - security_bits: 128, - rate_bits: 3, - num_challenges: 3, - zero_knowledge: false, - cap_height: 3, + + let config = CircuitConfig::standard_recursion_config(); + + let (proof, vd, cd) = dummy_proof::(&config, 8_000)?; + let (proof, vd, cd) = recursive_proof(proof, vd, cd, &config, &config, false)?; + let (proof, _vd, cd) = recursive_proof(proof, vd, cd, &config, &config, true)?; + + test_serialization(&proof, &cd)?; + + Ok(()) + } + + /// Creates a chain of recursive proofs where the last proof is made as small as reasonably + /// possible, using a high rate, high PoW bits, etc. + #[test] + #[ignore] + fn test_size_optimized_recursion() -> Result<()> { + init_logger(); + type F = GoldilocksField; + const D: usize = 4; + + let normal_config = CircuitConfig::standard_recursion_config(); + let final_config = CircuitConfig { + rate_bits: 7, fri_config: FriConfig { - proof_of_work_bits: 15, - reduction_strategy: FriReductionStrategy::ConstantArityBits(3, 5), - num_query_rounds: 27, + proof_of_work_bits: 23, + reduction_strategy: FriReductionStrategy::MinSize, + num_query_rounds: 11, }, - }; - let (proof_with_pis, vd, cd) = { - let (proof_with_pis, vd, cd) = { - let mut builder = CircuitBuilder::::new(config.clone()); - for i in 0..8_000 { - builder.constant(F::from_canonical_u64(i)); - } - let data = builder.build(); - ( - data.prove(PartialWitness::new())?, - data.verifier_only, - data.common, - ) - }; - verify(proof_with_pis.clone(), &vd, &cd)?; - - let mut builder = CircuitBuilder::::new(config.clone()); - let mut pw = PartialWitness::new(); - let pt = proof_to_proof_target(&proof_with_pis, &mut builder); - set_proof_target(&proof_with_pis, &pt, &mut pw); - - let inner_data = VerifierCircuitTarget { - constants_sigmas_cap: builder.add_virtual_cap(config.cap_height), - }; - pw.set_cap_target(&inner_data.constants_sigmas_cap, &vd.constants_sigmas_cap); - - builder.add_recursive_verifier(pt, &config, &inner_data, &cd); - - let data = builder.build(); - let recursive_proof = data.prove(pw)?; - (recursive_proof, data.verifier_only, data.common) + ..normal_config }; - verify(proof_with_pis.clone(), &vd, &cd)?; + let (proof, vd, cd) = dummy_proof::(&normal_config, 8_000)?; + let (proof, vd, cd) = + recursive_proof(proof, vd, cd, &normal_config, &normal_config, false)?; + let (proof, _vd, cd) = recursive_proof(proof, vd, cd, &normal_config, &final_config, true)?; + + test_serialization(&proof, &cd)?; + + Ok(()) + } + + fn dummy_proof, const D: usize>( + config: &CircuitConfig, + num_dummy_gates: u64, + ) -> Result<( + ProofWithPublicInputs, + VerifierOnlyCircuitData, + CommonCircuitData, + )> { + let mut builder = CircuitBuilder::::new(config.clone()); + for i in 0..num_dummy_gates { + builder.constant(F::from_canonical_u64(i)); + } + + let data = builder.build(); + let proof = data.prove(PartialWitness::new())?; + data.verify(proof.clone())?; + + Ok((proof, data.verifier_only, data.common)) + } + + fn recursive_proof, const D: usize>( + inner_proof: ProofWithPublicInputs, + inner_vd: VerifierOnlyCircuitData, + inner_cd: CommonCircuitData, + inner_config: &CircuitConfig, + config: &CircuitConfig, + print_gate_counts: bool, + ) -> Result<( + ProofWithPublicInputs, + VerifierOnlyCircuitData, + CommonCircuitData, + )> { let mut builder = CircuitBuilder::::new(config.clone()); let mut pw = PartialWitness::new(); - let pt = proof_to_proof_target(&proof_with_pis, &mut builder); - set_proof_target(&proof_with_pis, &pt, &mut pw); + let pt = proof_to_proof_target(&inner_proof, &mut builder); + set_proof_target(&inner_proof, &pt, &mut pw); let inner_data = VerifierCircuitTarget { - constants_sigmas_cap: builder.add_virtual_cap(config.cap_height), + constants_sigmas_cap: builder.add_virtual_cap(inner_config.cap_height), }; - pw.set_cap_target(&inner_data.constants_sigmas_cap, &vd.constants_sigmas_cap); + pw.set_cap_target( + &inner_data.constants_sigmas_cap, + &inner_vd.constants_sigmas_cap, + ); - builder.add_recursive_verifier(pt, &config, &inner_data, &cd); + builder.add_recursive_verifier(pt, &inner_config, &inner_data, &inner_cd); + + if print_gate_counts { + builder.print_gate_counts(0); + } - builder.print_gate_counts(0); let data = builder.build(); - let recursive_proof = data.prove(pw)?; - let proof_bytes = recursive_proof.to_bytes()?; + let proof = data.prove(pw)?; + data.verify(proof.clone())?; + + Ok((proof, data.verifier_only, data.common)) + } + + /// Test serialization and print some size info. + fn test_serialization, const D: usize>( + proof: &ProofWithPublicInputs, + cd: &CommonCircuitData, + ) -> Result<()> { + let proof_bytes = 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 proof_from_bytes = ProofWithPublicInputs::from_bytes(proof_bytes, &cd)?; + assert_eq!(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 - .clone() - .decompress(&data.common)?; - assert_eq!(recursive_proof, decompressed_compressed_proof); - info!("{:.4} to compress proof", now.elapsed().as_secs_f64()); - let compressed_proof_bytes = compressed_recursive_proof.to_bytes()?; + let compressed_proof = proof.clone().compress(&cd)?; + let decompressed_compressed_proof = compressed_proof.clone().decompress(&cd)?; + info!("{:.4}s to compress proof", now.elapsed().as_secs_f64()); + assert_eq!(proof, &decompressed_compressed_proof); + + let compressed_proof_bytes = compressed_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) + CompressedProofWithPublicInputs::from_bytes(compressed_proof_bytes, &cd)?; + assert_eq!(compressed_proof, compressed_proof_from_bytes); + + Ok(()) + } + + fn init_logger() { + let _ = env_logger::builder() + .format_timestamp(None) + .is_test(true) + .try_init(); } }