diff --git a/halo2_proofs/Cargo.toml b/halo2_proofs/Cargo.toml index f82e03a5db..74c1ecf188 100644 --- a/halo2_proofs/Cargo.toml +++ b/halo2_proofs/Cargo.toml @@ -54,7 +54,7 @@ blake2b_simd = "1" maybe-rayon = {version = "0.1.0", default-features = false} # Developer tooling dependencies -plotters = { version = "0.3.0", default-features = false, optional = true } +plotters = { version = "0.3.0", optional = true } tabbycat = { version = "0.1", features = ["attributes"], optional = true } # Legacy circuit compatibility @@ -82,6 +82,7 @@ gadget-traces = ["backtrace"] sanity-checks = [] batch = ["rand_core/getrandom"] floor-planner-v1-legacy-pdqsort = ["halo2_legacy_pdqsort"] +circuit-params = ["dev-graph"] # In-development features # See https://zcash.github.io/halo2/dev/features.html diff --git a/halo2_proofs/src/dev.rs b/halo2_proofs/src/dev.rs index a292b0ab4e..1bc9e954da 100644 --- a/halo2_proofs/src/dev.rs +++ b/halo2_proofs/src/dev.rs @@ -490,7 +490,12 @@ impl MockProver { let n = 1 << k; let mut cs = ConstraintSystem::default(); + + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut cs, circuit.params()); + #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut cs); + let cs = cs; if n < cs.minimum_rows() { diff --git a/halo2_proofs/src/dev/cost.rs b/halo2_proofs/src/dev/cost.rs index 0c3c7efaf1..2a35fe4180 100644 --- a/halo2_proofs/src/dev/cost.rs +++ b/halo2_proofs/src/dev/cost.rs @@ -261,7 +261,12 @@ impl> CircuitCost Self { // Collect the layout details. let mut cs = ConstraintSystem::default(); + + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut cs, circuit.params()); + #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut cs); + let mut layout = Layout::new(k, 1 << k, cs.num_selectors); ConcreteCircuit::FloorPlanner::synthesize( &mut layout, diff --git a/halo2_proofs/src/dev/graph.rs b/halo2_proofs/src/dev/graph.rs index 18ef8154df..63b847d53f 100644 --- a/halo2_proofs/src/dev/graph.rs +++ b/halo2_proofs/src/dev/graph.rs @@ -22,7 +22,12 @@ pub fn circuit_dot_graph>( ) -> String { // Collect the graph details. let mut cs = ConstraintSystem::default(); + + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut cs, circuit.params()); + #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut cs); + let mut graph = Graph::default(); ConcreteCircuit::FloorPlanner::synthesize(&mut graph, circuit, config, cs.constants).unwrap(); diff --git a/halo2_proofs/src/dev/graph/layout.rs b/halo2_proofs/src/dev/graph/layout.rs index 965e0683e6..e75ae8916c 100644 --- a/halo2_proofs/src/dev/graph/layout.rs +++ b/halo2_proofs/src/dev/graph/layout.rs @@ -94,7 +94,12 @@ impl CircuitLayout { let n = 1 << k; // Collect the layout details. let mut cs = ConstraintSystem::default(); + + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut cs, circuit.params()); + #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut cs); + let mut layout = Layout::new(k, n, cs.num_selectors); ConcreteCircuit::FloorPlanner::synthesize( &mut layout, diff --git a/halo2_proofs/src/lib.rs b/halo2_proofs/src/lib.rs index 8fc0e0be0d..b80ce63724 100644 --- a/halo2_proofs/src/lib.rs +++ b/halo2_proofs/src/lib.rs @@ -7,6 +7,7 @@ #![deny(missing_debug_implementations)] #![deny(missing_docs)] #![deny(unsafe_code)] +#![feature(associated_type_defaults)] pub mod arithmetic; pub mod circuit; diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index 50997299a4..3af90662dc 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -81,8 +81,13 @@ where pub fn read>( reader: &mut R, params: &Params, + #[cfg(feature = "circuit-params")] circuit_params: ConcreteCircuit::Params, ) -> io::Result { - let (domain, cs, _) = keygen::create_domain::(params); + let (domain, cs, _) = keygen::create_domain::( + params, + #[cfg(feature = "circuit-params")] + circuit_params, + ); let mut num_fixed_columns_be_bytes = [0u8; 4]; reader.read_exact(&mut num_fixed_columns_be_bytes)?; let num_fixed_columns = u32::from_be_bytes(num_fixed_columns_be_bytes); diff --git a/halo2_proofs/src/plonk/circuit.rs b/halo2_proofs/src/plonk/circuit.rs index a6494d871d..734a575013 100644 --- a/halo2_proofs/src/plonk/circuit.rs +++ b/halo2_proofs/src/plonk/circuit.rs @@ -470,14 +470,34 @@ pub trait Circuit { /// `Circuit` trait because its behaviour is circuit-critical. type FloorPlanner: FloorPlanner; + /// Runtime parameters to configure the circuit. + #[cfg(feature = "circuit-params")] + type Params: Default = (); + /// Returns a copy of this circuit with no witness values (i.e. all witnesses set to /// `None`). For most circuits, this will be equal to `Self::default()`. fn without_witnesses(&self) -> Self; + /// For defining runtime configuration paramters. + #[cfg(feature = "circuit-params")] + fn params(&self) -> Self::Params { + Self::Params::default() + } + /// The circuit is given an opportunity to describe the exact gate /// arrangement, column arrangement, etc. fn configure(meta: &mut ConstraintSystem) -> Self::Config; + /// Same as configure but takes params. + /// Credit: https://github.com/privacy-scaling-explorations/halo2/pull/168 + #[cfg(feature = "circuit-params")] + fn configure_with_params( + meta: &mut ConstraintSystem, + _params: Self::Params, + ) -> Self::Config { + Self::configure(meta) + } + /// Given the provided `cs`, synthesize the circuit. The concrete type of /// the caller will be different depending on the context, and they may or /// may not expect to have a witness present. diff --git a/halo2_proofs/src/plonk/keygen.rs b/halo2_proofs/src/plonk/keygen.rs index 1b2be6d0a0..703591ab3c 100644 --- a/halo2_proofs/src/plonk/keygen.rs +++ b/halo2_proofs/src/plonk/keygen.rs @@ -24,6 +24,7 @@ use crate::{ pub(crate) fn create_domain( params: &Params, + #[cfg(feature = "circuit-params")] circuit_params: ConcreteCircuit::Params, ) -> ( EvaluationDomain, ConstraintSystem, @@ -34,6 +35,10 @@ where ConcreteCircuit: Circuit, { let mut cs = ConstraintSystem::default(); + + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut cs, circuit_params); + #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut cs); let degree = cs.degree(); @@ -195,7 +200,11 @@ where C::Scalar: FromUniformBytes<64>, ConcreteCircuit: Circuit, { - let (domain, cs, config) = create_domain::(params); + let (domain, cs, config) = create_domain::( + params, + #[cfg(feature = "circuit-params")] + circuit.params(), + ); if (params.n as usize) < cs.minimum_rows() { return Err(Error::not_enough_rows_available(params.k)); @@ -255,6 +264,10 @@ where ConcreteCircuit: Circuit, { let mut cs = ConstraintSystem::default(); + + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut cs, circuit.params()); + #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut cs); let cs = cs; diff --git a/halo2_proofs/src/plonk/prover.rs b/halo2_proofs/src/plonk/prover.rs index e54f2129f0..f2617ba1d7 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -61,6 +61,10 @@ pub fn create_proof< let domain = &pk.vk.domain; let mut meta = ConstraintSystem::default(); + // Since there is 1 proving key for all the circuits, the circuits should share the same configuration. + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut meta, circuits[0].params()); + #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut meta); // Selector optimizations cannot be applied here; use the ConstraintSystem @@ -724,63 +728,137 @@ pub fn create_proof< multiopen::create_proof(params, rng, transcript, instances).map_err(|_| Error::Opening) } +#[cfg(feature = "circuit-params")] #[test] fn test_create_proof() { use crate::{ circuit::SimpleFloorPlanner, + pasta::pallas, plonk::{keygen_pk, keygen_vk}, transcript::{Blake2bWrite, Challenge255}, }; use pasta_curves::EqAffine; use rand_core::OsRng; - #[derive(Clone, Copy)] - struct MyCircuit; + #[derive(Clone, Default)] + struct MyCircuit { + vals: Vec>, + } + + #[derive(Clone)] + struct MyConfig { + advices: Vec>, + primary: Column, + } + + fn assign_free_advice( + mut layouter: impl crate::circuit::Layouter, + column: Column, + value: Value, + ) -> Result, crate::plonk::Error> + where + for<'v> Assigned: From<&'v V>, + { + layouter.assign_region( + || "load private", + |mut region| region.assign_advice(|| "load private", column, 0, || value), + ) + } - impl Circuit for MyCircuit { - type Config = (); + impl Circuit for MyCircuit { + type Config = MyConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = Self; + fn without_witnesses(&self) -> Self { - *self + self.clone() + } + + fn params(&self) -> Self::Params { + self.clone() } - fn configure(_meta: &mut ConstraintSystem) -> Self::Config {} + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + Self::Config { + advices: vec![], + primary: meta.instance_column(), + } + } + + fn configure_with_params( + meta: &mut ConstraintSystem, + params: Self::Params, + ) -> Self::Config { + let advices = vec![meta.advice_column(); params.vals.len()]; + let primary = meta.instance_column(); + + for advice in advices.iter() { + meta.enable_equality(*advice); + } + meta.enable_equality(primary); + Self::Config { advices, primary } + } fn synthesize( &self, - _config: Self::Config, - _layouter: impl crate::circuit::Layouter, + config: Self::Config, + mut layouter: impl crate::circuit::Layouter, ) -> Result<(), Error> { + let mut witnessed = vec![]; + for (i, val) in self.vals.iter().enumerate() { + let witness = assign_free_advice( + layouter.namespace(|| format!("witness {}", i)), + config.advices[i], + *val, + )?; + witnessed.push(witness); + } + for (i, witness) in witnessed.iter().enumerate() { + layouter.constrain_instance(witness.cell(), config.primary, i)? + } Ok(()) } } - let params: Params = Params::new(3); - let vk = keygen_vk(¶ms, &MyCircuit).expect("keygen_vk should not fail"); - let pk = keygen_pk(¶ms, vk, &MyCircuit).expect("keygen_pk should not fail"); + let params: Params = Params::new(5); + let vals = vec![ + Value::known(pallas::Base::from(1)), + Value::known(pallas::Base::from(2)), + Value::known(pallas::Base::from(3)), + ]; + + let circuit = MyCircuit { vals }; + // Render circuit digram + use crate::dev::CircuitLayout; + use plotters::prelude::*; + let root = BitMapBackend::new("../target/layout.png", (3840, 2160)).into_drawing_area(); + root.fill(&WHITE).unwrap(); + let root = root.titled("Circuit Layout", ("sans-serif", 60)).unwrap(); + CircuitLayout::default().render(5, &circuit, &root).unwrap(); + + let vk = keygen_vk(¶ms, &circuit).expect("keygen_vk should not fail"); + let pk = keygen_pk(¶ms, vk, &circuit).expect("keygen_pk should not fail"); let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); // Create proof with wrong number of instances let proof = create_proof( ¶ms, &pk, - &[MyCircuit, MyCircuit], + &[circuit.clone(), circuit.clone()], &[], OsRng, &mut transcript, ); assert!(matches!(proof.unwrap_err(), Error::InvalidInstances)); + let instance = vec![ + pallas::Base::from(1), + pallas::Base::from(2), + pallas::Base::from(3), + ]; // Create proof with correct number of instances - create_proof( - ¶ms, - &pk, - &[MyCircuit, MyCircuit], - &[&[], &[]], - OsRng, - &mut transcript, - ) - .expect("proof generation should not fail"); + let prover = crate::dev::MockProver::run(5, &circuit, vec![instance]).unwrap(); + prover.assert_satisfied(); } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 948d27daa4..5d56faf9ae 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.60.0" +channel = "nightly"