diff --git a/halo2_backend/src/plonk.rs b/halo2_backend/src/plonk.rs index e386371fac..770c1130f0 100644 --- a/halo2_backend/src/plonk.rs +++ b/halo2_backend/src/plonk.rs @@ -52,9 +52,6 @@ pub struct VerifyingKey { cs_degree: usize, /// The representative of this `VerifyingKey` in transcripts. transcript_repr: C::Scalar, - /// Legacy field that indicates wether the circuit was compiled with compressed selectors or - /// not using the legacy API. - pub compress_selectors: Option, } // Current version of the VK @@ -187,7 +184,6 @@ impl VerifyingKey { cs_degree, // Temporary, this is not pinned. transcript_repr: C::Scalar::ZERO, - compress_selectors: None, }; let mut hasher = Blake2bParams::new() diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index 8fb2a01e9c..5c475d699b 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -12,9 +12,9 @@ mod verifier { pub use halo2_backend::plonk::verifier::verify_proof; } -pub use keygen::{keygen_pk, keygen_vk, keygen_vk_custom}; +pub use keygen::{keygen_pk, keygen_pk_custom, keygen_vk, keygen_vk_custom}; -pub use prover::{create_proof, create_proof_with_engine}; +pub use prover::{create_proof, create_proof_custom_with_engine, create_proof_with_engine}; pub use verifier::verify_proof; pub use error::Error; diff --git a/halo2_proofs/src/plonk/keygen.rs b/halo2_proofs/src/plonk/keygen.rs index a110aa73c2..1895e36f8c 100644 --- a/halo2_proofs/src/plonk/keygen.rs +++ b/halo2_proofs/src/plonk/keygen.rs @@ -9,7 +9,11 @@ use halo2_frontend::plonk::Circuit; use halo2_middleware::ff::FromUniformBytes; /// Generate a `VerifyingKey` from an instance of `Circuit`. -/// By default, selector compression is turned **off**. +/// By default, selector compression is turned **ON**. +/// +/// **NOTE**: This `keygen_vk` is legacy one, assuming that `compress_selector: true`. +/// Hence, it is HIGHLY recommended to pair this util with `keygen_pk`. +/// In addition, when using this for key generation, user MUST use `compress_selectors: true`. pub fn keygen_vk<'params, C, P, ConcreteCircuit>( params: &P, circuit: &ConcreteCircuit, @@ -26,6 +30,11 @@ where /// Generate a `VerifyingKey` from an instance of `Circuit`. /// /// The selector compression optimization is turned on only if `compress_selectors` is `true`. +/// +/// **NOTE**: This `keygen_vk_custom` MUST share the same `compress_selectors` with +/// `ProvingKey` generation process. +/// Otherwise, the user could get unmatching pk/vk pair. +/// Hence, it is HIGHLY recommended to pair this util with `keygen_pk_custom`. pub fn keygen_vk_custom<'params, C, P, ConcreteCircuit>( params: &P, circuit: &ConcreteCircuit, @@ -38,12 +47,15 @@ where C::Scalar: FromUniformBytes<64>, { let (compiled_circuit, _, _) = compile_circuit(params.k(), circuit, compress_selectors)?; - let mut vk = backend_keygen_vk(params, &compiled_circuit)?; - vk.compress_selectors = Some(compress_selectors); - Ok(vk) + Ok(backend_keygen_vk(params, &compiled_circuit)?) } /// Generate a `ProvingKey` from a `VerifyingKey` and an instance of `Circuit`. +/// By default, selector compression is turned **ON**. +/// +/// **NOTE**: This `keygen_pk` is legacy one, assuming that `compress_selector: true`. +/// Hence, it is HIGHLY recommended to pair this util with `keygen_vk`. +/// In addition, when using this for key generation, user MUST use `compress_selectors: true`. pub fn keygen_pk<'params, C, P, ConcreteCircuit>( params: &P, vk: VerifyingKey, @@ -54,10 +66,28 @@ where P: Params<'params, C>, ConcreteCircuit: Circuit, { - let (compiled_circuit, _, _) = compile_circuit( - params.k(), - circuit, - vk.compress_selectors.unwrap_or_default(), - )?; + keygen_pk_custom(params, vk, circuit, true) +} + +/// Generate a `ProvingKey` from an instance of `Circuit`. +/// +/// The selector compression optimization is turned on only if `compress_selectors` is `true`. +/// +/// **NOTE**: This `keygen_pk_custom` MUST share the same `compress_selectors` with +/// `VerifyingKey` generation process. +/// Otherwise, the user could get unmatching pk/vk pair. +/// Hence, it is HIGHLY recommended to pair this util with `keygen_vk_custom`. +pub fn keygen_pk_custom<'params, C, P, ConcreteCircuit>( + params: &P, + vk: VerifyingKey, + circuit: &ConcreteCircuit, + compress_selectors: bool, +) -> Result, Error> +where + C: CurveAffine, + P: Params<'params, C>, + ConcreteCircuit: Circuit, +{ + let (compiled_circuit, _, _) = compile_circuit(params.k(), circuit, compress_selectors)?; Ok(backend_keygen_pk(params, vk, &compiled_circuit)?) } diff --git a/halo2_proofs/src/plonk/prover.rs b/halo2_proofs/src/plonk/prover.rs index 4713186529..0470cd4749 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -37,32 +37,9 @@ pub fn create_proof_with_engine< where Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, { - if circuits.len() != instances.len() { - return Err(Error::Backend(ErrorBack::InvalidInstances)); - } - let (config, cs, _) = compile_circuit_cs::<_, ConcreteCircuit>( - pk.get_vk().compress_selectors.unwrap_or_default(), - #[cfg(feature = "circuit-params")] - circuits[0].params(), - ); - let mut witness_calcs: Vec<_> = circuits - .iter() - .enumerate() - .map(|(i, circuit)| WitnessCalculator::new(params.k(), circuit, &config, &cs, instances[i])) - .collect(); - let mut prover = Prover::::new_with_engine( - engine, params, pk, instances, rng, transcript, - )?; - let mut challenges = HashMap::new(); - let phases = prover.phases().to_vec(); - for phase in phases.iter() { - let mut witnesses = Vec::with_capacity(circuits.len()); - for witness_calc in witness_calcs.iter_mut() { - witnesses.push(witness_calc.calc(*phase, &challenges)?); - } - challenges = prover.commit_phase(*phase, witnesses).unwrap(); - } - Ok(prover.create_proof()?) + create_proof_custom_with_engine::( + engine, params, pk, true, circuits, instances, rng, transcript, + ) } /// This creates a proof for the provided `circuit` when given the public @@ -94,6 +71,62 @@ where ) } +/// This creates a proof for the provided `circuit` when given the public +/// parameters `params` and the proving key [`ProvingKey`] that was +/// generated previously for the same circuit. The provided `instances` +/// are zero-padded internally. +/// In addition, this needs the `compress_selectors` field. +#[allow(clippy::too_many_arguments)] +pub fn create_proof_custom_with_engine< + 'params, + Scheme: CommitmentScheme, + P: commitment::Prover<'params, Scheme>, + E: EncodedChallenge, + R: RngCore, + T: TranscriptWrite, + ConcreteCircuit: Circuit, + M: MsmAccel, +>( + engine: PlonkEngine, + params: &'params Scheme::ParamsProver, + pk: &ProvingKey, + compress_selectors: bool, + circuits: &[ConcreteCircuit], + instances: &[&[&[Scheme::Scalar]]], + rng: R, + transcript: &mut T, +) -> Result<(), Error> +where + Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, +{ + if circuits.len() != instances.len() { + return Err(Error::Backend(ErrorBack::InvalidInstances)); + } + let (config, cs, _) = compile_circuit_cs::<_, ConcreteCircuit>( + compress_selectors, + #[cfg(feature = "circuit-params")] + circuits[0].params(), + ); + let mut witness_calcs: Vec<_> = circuits + .iter() + .enumerate() + .map(|(i, circuit)| WitnessCalculator::new(params.k(), circuit, &config, &cs, instances[i])) + .collect(); + let mut prover = Prover::::new_with_engine( + engine, params, pk, instances, rng, transcript, + )?; + let mut challenges = HashMap::new(); + let phases = prover.phases().to_vec(); + for phase in phases.iter() { + let mut witnesses = Vec::with_capacity(circuits.len()); + for witness_calc in witness_calcs.iter_mut() { + witnesses.push(witness_calc.calc(*phase, &challenges)?); + } + challenges = prover.commit_phase(*phase, witnesses).unwrap(); + } + Ok(prover.create_proof()?) +} + #[test] fn test_create_proof() { use crate::{ @@ -163,3 +196,64 @@ fn test_create_proof() { ) .expect("proof generation should not fail"); } + +#[test] +fn test_create_proof_custom() { + use crate::{ + circuit::SimpleFloorPlanner, + plonk::{keygen_pk_custom, keygen_vk_custom, ConstraintSystem, ErrorFront}, + poly::kzg::{ + commitment::{KZGCommitmentScheme, ParamsKZG}, + multiopen::ProverSHPLONK, + }, + transcript::{Blake2bWrite, Challenge255, TranscriptWriterBuffer}, + }; + use halo2_middleware::ff::Field; + use halo2curves::bn256::Bn256; + use rand_core::OsRng; + + #[derive(Clone, Copy)] + struct MyCircuit; + + impl Circuit for MyCircuit { + type Config = (); + type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); + + fn without_witnesses(&self) -> Self { + *self + } + + fn configure(_meta: &mut ConstraintSystem) -> Self::Config {} + + fn synthesize( + &self, + _config: Self::Config, + _layouter: impl crate::circuit::Layouter, + ) -> Result<(), ErrorFront> { + Ok(()) + } + } + + let params: ParamsKZG = ParamsKZG::setup(3, OsRng); + let compress_selectors = true; + let vk = keygen_vk_custom(¶ms, &MyCircuit, compress_selectors) + .expect("keygen_vk_custom should not fail"); + let pk = keygen_pk_custom(¶ms, vk, &MyCircuit, compress_selectors) + .expect("keygen_pk_custom should not fail"); + let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + let engine = PlonkEngineConfig::build_default(); + + create_proof_custom_with_engine::, ProverSHPLONK<_>, _, _, _, _, _>( + engine, + ¶ms, + &pk, + compress_selectors, + &[MyCircuit, MyCircuit], + &[&[], &[]], + OsRng, + &mut transcript, + ) + .expect("proof generation should not fail"); +}