From 05c929284edfeb456165692e233ae8c8d003a55e Mon Sep 17 00:00:00 2001 From: guorong009 Date: Mon, 29 Apr 2024 14:50:43 +0800 Subject: [PATCH 01/30] fix: correct the logic of convert selectors to fixed column --- halo2_frontend/src/circuit.rs | 26 ++++++++++---------------- halo2_proofs/src/plonk.rs | 6 ++---- halo2_proofs/src/plonk/prover.rs | 3 +-- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/halo2_frontend/src/circuit.rs b/halo2_frontend/src/circuit.rs index 233a1cb032..99c4b1c616 100644 --- a/halo2_frontend/src/circuit.rs +++ b/halo2_frontend/src/circuit.rs @@ -38,13 +38,8 @@ pub use table_layouter::{SimpleTableLayouter, TableLayouter}; /// The `ConstraintSystem` can be converted to `ConstraintSystemMid` to be used to interface /// with the backend. pub fn compile_circuit_cs>( - compress_selectors: bool, #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, -) -> ( - ConcreteCircuit::Config, - ConstraintSystem, - SelectorsToFixed, -) { +) -> (ConcreteCircuit::Config, ConstraintSystem) { let mut cs = ConstraintSystem::default(); #[cfg(feature = "circuit-params")] let config = ConcreteCircuit::configure_with_params(&mut cs, params); @@ -52,13 +47,7 @@ pub fn compile_circuit_cs>( let config = ConcreteCircuit::configure(&mut cs); let cs = cs; - let (cs, selectors_to_fixed) = if compress_selectors { - cs.selectors_to_fixed_compressed() - } else { - cs.selectors_to_fixed_direct() - }; - - (config, cs, selectors_to_fixed) + (config, cs) } /// Compile a circuit. Runs configure and synthesize on the circuit in order to materialize the @@ -81,9 +70,7 @@ pub fn compile_circuit>( > { let n = 2usize.pow(k); - // After this, the ConstraintSystem should not have any selectors: `verify` does not need them, and `keygen_pk` regenerates `cs` from scratch anyways. - let (config, cs, selectors_to_fixed) = compile_circuit_cs::<_, ConcreteCircuit>( - compress_selectors, + let (config, cs) = compile_circuit_cs::<_, ConcreteCircuit>( #[cfg(feature = "circuit-params")] circuit.params(), ); @@ -108,6 +95,13 @@ pub fn compile_circuit>( cs.constants.clone(), )?; + // After this, the ConstraintSystem should not have any selectors: `verify` does not need them, and `keygen_pk` regenerates `cs` from scratch anyways. + let (cs, selectors_to_fixed) = if compress_selectors { + cs.selectors_to_fixed_compressed() + } else { + cs.selectors_to_fixed_direct() + }; + let mut fixed = batch_invert_assigned(assembly.fixed); let selector_polys = selectors_to_fixed.convert(assembly.selectors); fixed.extend(selector_polys); diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index dff3c7790d..2234ebc6e3 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -50,8 +50,7 @@ pub fn vk_read, { - let (_, cs, _) = compile_circuit_cs::<_, ConcreteCircuit>( - compress_selectors, + let (_, cs) = compile_circuit_cs::<_, ConcreteCircuit>( #[cfg(feature = "circuit-params")] params, ); @@ -80,8 +79,7 @@ pub fn pk_read, { - let (_, cs, _) = compile_circuit_cs::<_, ConcreteCircuit>( - compress_selectors, + let (_, cs) = compile_circuit_cs::<_, ConcreteCircuit>( #[cfg(feature = "circuit-params")] params, ); diff --git a/halo2_proofs/src/plonk/prover.rs b/halo2_proofs/src/plonk/prover.rs index 0470cd4749..77abcbc2a6 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -102,8 +102,7 @@ where if circuits.len() != instances.len() { return Err(Error::Backend(ErrorBack::InvalidInstances)); } - let (config, cs, _) = compile_circuit_cs::<_, ConcreteCircuit>( - compress_selectors, + let (config, cs) = compile_circuit_cs::<_, ConcreteCircuit>( #[cfg(feature = "circuit-params")] circuits[0].params(), ); From a002524603d782ca9f702c5569414581ac8047e7 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Mon, 29 Apr 2024 14:57:14 +0800 Subject: [PATCH 02/30] refactor: remove the "create_proof_custom_with_engine" --- halo2_frontend/src/circuit.rs | 1 - halo2_proofs/examples/serialization.rs | 1 - halo2_proofs/src/plonk.rs | 4 +- halo2_proofs/src/plonk/prover.rs | 86 ++++++++------------------ 4 files changed, 27 insertions(+), 65 deletions(-) diff --git a/halo2_frontend/src/circuit.rs b/halo2_frontend/src/circuit.rs index 99c4b1c616..234f5d67a0 100644 --- a/halo2_frontend/src/circuit.rs +++ b/halo2_frontend/src/circuit.rs @@ -45,7 +45,6 @@ pub fn compile_circuit_cs>( let config = ConcreteCircuit::configure_with_params(&mut cs, params); #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut cs); - let cs = cs; (config, cs) } diff --git a/halo2_proofs/examples/serialization.rs b/halo2_proofs/examples/serialization.rs index 4a83392e37..88de4a3f29 100644 --- a/halo2_proofs/examples/serialization.rs +++ b/halo2_proofs/examples/serialization.rs @@ -147,7 +147,6 @@ fn main() { let pk = pk_read::( &mut reader, SerdeFormat::RawBytes, - compress_selectors, #[cfg(feature = "circuit-params")] circuit.params(), ) diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index 2234ebc6e3..fd4b0c80ab 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -14,7 +14,7 @@ mod verifier { pub use keygen::{keygen_pk, keygen_pk_custom, keygen_vk, keygen_vk_custom}; -pub use prover::{create_proof, create_proof_custom_with_engine, create_proof_with_engine}; +pub use prover::{create_proof, create_proof_with_engine}; pub use verifier::verify_proof; pub use error::Error; @@ -44,7 +44,6 @@ use std::io; pub fn vk_read>( reader: &mut R, format: SerdeFormat, - compress_selectors: bool, #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, ) -> io::Result> where @@ -73,7 +72,6 @@ where pub fn pk_read>( reader: &mut R, format: SerdeFormat, - compress_selectors: bool, #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, ) -> io::Result> where diff --git a/halo2_proofs/src/plonk/prover.rs b/halo2_proofs/src/plonk/prover.rs index 77abcbc2a6..4bb8d4ad11 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -37,9 +37,31 @@ pub fn create_proof_with_engine< where Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, { - create_proof_custom_with_engine::( - engine, params, pk, true, circuits, instances, rng, transcript, - ) + if circuits.len() != instances.len() { + return Err(Error::Backend(ErrorBack::InvalidInstances)); + } + let (config, cs) = compile_circuit_cs::<_, ConcreteCircuit>( + #[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()?) } /// This creates a proof for the provided `circuit` when given the public @@ -71,61 +93,6 @@ 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>( - #[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::{ @@ -244,11 +211,10 @@ fn test_create_proof_custom() { let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); let engine = PlonkEngineConfig::build_default(); - create_proof_custom_with_engine::, ProverSHPLONK<_>, _, _, _, _, _>( + create_proof_with_engine::, ProverSHPLONK<_>, _, _, _, _, _>( engine, ¶ms, &pk, - compress_selectors, &[MyCircuit, MyCircuit], &[&[], &[]], OsRng, From 015229769315ebc31aad79ddd07f26833666e5c7 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Mon, 29 Apr 2024 15:02:19 +0800 Subject: [PATCH 03/30] chore: clippy --- halo2_frontend/src/circuit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/halo2_frontend/src/circuit.rs b/halo2_frontend/src/circuit.rs index 234f5d67a0..b2bde48431 100644 --- a/halo2_frontend/src/circuit.rs +++ b/halo2_frontend/src/circuit.rs @@ -5,7 +5,7 @@ use crate::plonk::{ permutation, sealed::{self, SealedPhase}, Advice, Assignment, Circuit, ConstraintSystem, FirstPhase, Fixed, FloorPlanner, Instance, - SecondPhase, SelectorsToFixed, ThirdPhase, + SecondPhase, ThirdPhase, }; use halo2_middleware::circuit::{Any, CompiledCircuit, Preprocessing}; use halo2_middleware::ff::{BatchInvert, Field}; From 5627fce75ad93d31067047173f647e3e8464a7b3 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Tue, 30 Apr 2024 11:28:58 +0800 Subject: [PATCH 04/30] chore: add ci for running examples --- .github/scripts/run-examples.sh | 22 ++++++++++++++++++++ .github/workflows/ci.yml | 37 +++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 .github/scripts/run-examples.sh diff --git a/.github/scripts/run-examples.sh b/.github/scripts/run-examples.sh new file mode 100644 index 0000000000..b5d9ee5187 --- /dev/null +++ b/.github/scripts/run-examples.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# Get the list of examples from "examples" dir & Cargo.toml +EXAMPLES_WITH_FEATURES=$(awk '/^\[\[example\]\]/ { getline; name=$3; name=substr(name, 2, length(name)-2); getline; if ($1 == "required-features") { features=$NF; gsub(/["\[\]]/, "", features); print name "#" features } }' ./halo2_proofs/Cargo.toml) +EXAMPLES_WITHOUT_FEATURES=$(ls ./halo2_proofs/examples/*.rs | xargs -n1 basename -s .rs) + +# Remove examples with features listed in Cargo.toml from examples without features +EXAMPLES_WITHOUT_FEATURES=$(echo "$EXAMPLES_WITHOUT_FEATURES" | grep -vFx "$(echo "$EXAMPLES_WITH_FEATURES" | cut -d '#' -f 1)") + +# Combine examples with and without features +EXAMPLES=$(echo "$EXAMPLES_WITH_FEATURES $EXAMPLES_WITHOUT_FEATURES" | tr ' ' '\n' | sort -u | tr '\n' ' ') + +# Run the examples +for example in ${{ steps.get-examples.outputs.examples }}; do + if [[ "$example" == *"#"* ]]; then + name=$(echo $example | cut -d '#' -f 1) + features=$(echo $example | cut -d '#' -f 2) + cargo run --package halo2_proofs --example $name --features $features + else + cargo run --package halo2_proofs --example $example + fi +done diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 186d72fcd0..e77f59d487 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,43 @@ jobs: command: test args: --verbose --release --workspace --no-default-features --features "${{ matrix.features }}" + examples: + name: Run the examples + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + # - name: Get example targets and features + # id: get-examples + # run: | + # # Get the list of examples from "examples" dir & Cargo.toml + # EXAMPLES_WITH_FEATURES=$(awk '/^\[\[example\]\]/ { getline; name=$3; name=substr(name, 2, length(name)-2); getline; if ($1 == "required-features") { features=$NF; gsub(/["\[\]]/, "", features); print name "#" features } }' ./halo2_proofs/Cargo.toml) + # EXAMPLES_WITHOUT_FEATURES=$(ls ./halo2_proofs/examples/*.rs | xargs -n1 basename -s .rs) + + # # Remove examples with features listed in Cargo.toml from examples without features + # EXAMPLES_WITHOUT_FEATURES=$(echo "$EXAMPLES_WITHOUT_FEATURES" | grep -vFx "$(echo "$EXAMPLES_WITH_FEATURES" | cut -d '#' -f 1)") + + # # Combine examples with and without features + # EXAMPLES=$(echo "$EXAMPLES_WITH_FEATURES $EXAMPLES_WITHOUT_FEATURES" | tr ' ' '\n' | sort -u | tr '\n' ' ') + + # echo "::set-output name=examples::$EXAMPLES" + + # - name: Run examples + # run: | + # for example in ${{ steps.get-examples.outputs.examples }}; do + # if [[ "$example" == *"#"* ]]; then + # name=$(echo $example | cut -d '#' -f 1) + # features=$(echo $example | cut -d '#' -f 2) + # cargo run --package halo2_proofs --example $name --features $features + # else + # cargo run --package halo2_proofs --example $example + # fi + # done + - name: Run examples + run: | + .github/scripts/run-examples.sh + build: name: Build target ${{ matrix.target }} runs-on: ubuntu-latest From 41e4da277206eb69502944f61fca465d7e0b3829 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Tue, 30 Apr 2024 13:20:55 +0800 Subject: [PATCH 05/30] chore: fix the ci --- .github/scripts/run-examples.sh | 16 ++++++++-------- .github/workflows/ci.yml | 26 -------------------------- 2 files changed, 8 insertions(+), 34 deletions(-) mode change 100644 => 100755 .github/scripts/run-examples.sh diff --git a/.github/scripts/run-examples.sh b/.github/scripts/run-examples.sh old mode 100644 new mode 100755 index b5d9ee5187..be176ad3f6 --- a/.github/scripts/run-examples.sh +++ b/.github/scripts/run-examples.sh @@ -11,12 +11,12 @@ EXAMPLES_WITHOUT_FEATURES=$(echo "$EXAMPLES_WITHOUT_FEATURES" | grep -vFx "$(ech EXAMPLES=$(echo "$EXAMPLES_WITH_FEATURES $EXAMPLES_WITHOUT_FEATURES" | tr ' ' '\n' | sort -u | tr '\n' ' ') # Run the examples -for example in ${{ steps.get-examples.outputs.examples }}; do - if [[ "$example" == *"#"* ]]; then - name=$(echo $example | cut -d '#' -f 1) - features=$(echo $example | cut -d '#' -f 2) - cargo run --package halo2_proofs --example $name --features $features - else - cargo run --package halo2_proofs --example $example - fi +for example in $EXAMPLES; do +if [[ "$example" == *"#"* ]]; then + name=$(echo $example | cut -d '#' -f 1) + features=$(echo $example | cut -d '#' -f 2) + cargo run --package halo2_proofs --example $name --features $features +else + cargo run --package halo2_proofs --example $example +fi done diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e77f59d487..558785df5a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,32 +39,6 @@ jobs: steps: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 - # - name: Get example targets and features - # id: get-examples - # run: | - # # Get the list of examples from "examples" dir & Cargo.toml - # EXAMPLES_WITH_FEATURES=$(awk '/^\[\[example\]\]/ { getline; name=$3; name=substr(name, 2, length(name)-2); getline; if ($1 == "required-features") { features=$NF; gsub(/["\[\]]/, "", features); print name "#" features } }' ./halo2_proofs/Cargo.toml) - # EXAMPLES_WITHOUT_FEATURES=$(ls ./halo2_proofs/examples/*.rs | xargs -n1 basename -s .rs) - - # # Remove examples with features listed in Cargo.toml from examples without features - # EXAMPLES_WITHOUT_FEATURES=$(echo "$EXAMPLES_WITHOUT_FEATURES" | grep -vFx "$(echo "$EXAMPLES_WITH_FEATURES" | cut -d '#' -f 1)") - - # # Combine examples with and without features - # EXAMPLES=$(echo "$EXAMPLES_WITH_FEATURES $EXAMPLES_WITHOUT_FEATURES" | tr ' ' '\n' | sort -u | tr '\n' ' ') - - # echo "::set-output name=examples::$EXAMPLES" - - # - name: Run examples - # run: | - # for example in ${{ steps.get-examples.outputs.examples }}; do - # if [[ "$example" == *"#"* ]]; then - # name=$(echo $example | cut -d '#' -f 1) - # features=$(echo $example | cut -d '#' -f 2) - # cargo run --package halo2_proofs --example $name --features $features - # else - # cargo run --package halo2_proofs --example $example - # fi - # done - name: Run examples run: | .github/scripts/run-examples.sh From 8f38f795ae7cc936b12a774b553cb4da732cd073 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Tue, 30 Apr 2024 14:22:52 +0800 Subject: [PATCH 06/30] chore: clean the ci check script --- .github/scripts/run-examples.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/scripts/run-examples.sh b/.github/scripts/run-examples.sh index be176ad3f6..c96c68e1d0 100755 --- a/.github/scripts/run-examples.sh +++ b/.github/scripts/run-examples.sh @@ -12,11 +12,11 @@ EXAMPLES=$(echo "$EXAMPLES_WITH_FEATURES $EXAMPLES_WITHOUT_FEATURES" | tr ' ' '\ # Run the examples for example in $EXAMPLES; do -if [[ "$example" == *"#"* ]]; then - name=$(echo $example | cut -d '#' -f 1) - features=$(echo $example | cut -d '#' -f 2) - cargo run --package halo2_proofs --example $name --features $features -else - cargo run --package halo2_proofs --example $example -fi + if [ "$(echo "$example" | grep '#')" ]; then + name=$(echo $example | cut -d '#' -f 1) + features=$(echo $example | cut -d '#' -f 2) + cargo run --package halo2_proofs --example $name --features $features + else + cargo run --package halo2_proofs --example $example + fi done From 47157ead2a7ac5fd6a36896a8c8fc31447128f8c Mon Sep 17 00:00:00 2001 From: guorong009 Date: Tue, 30 Apr 2024 23:53:01 +0800 Subject: [PATCH 07/30] fix: roll back wrong updates --- halo2_frontend/src/circuit.rs | 29 +++++---- halo2_proofs/examples/serialization.rs | 1 + halo2_proofs/src/plonk.rs | 10 ++- halo2_proofs/src/plonk/prover.rs | 87 ++++++++++++++++++-------- 4 files changed, 87 insertions(+), 40 deletions(-) diff --git a/halo2_frontend/src/circuit.rs b/halo2_frontend/src/circuit.rs index b2bde48431..74847ae3e7 100644 --- a/halo2_frontend/src/circuit.rs +++ b/halo2_frontend/src/circuit.rs @@ -1,6 +1,6 @@ //! Traits and structs for implementing circuit components. -use crate::plonk; +use crate::plonk::{self, SelectorsToFixed}; use crate::plonk::{ permutation, sealed::{self, SealedPhase}, @@ -38,15 +38,27 @@ pub use table_layouter::{SimpleTableLayouter, TableLayouter}; /// The `ConstraintSystem` can be converted to `ConstraintSystemMid` to be used to interface /// with the backend. pub fn compile_circuit_cs>( + compress_selectors: bool, #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, -) -> (ConcreteCircuit::Config, ConstraintSystem) { +) -> ( + ConcreteCircuit::Config, + ConstraintSystem, + SelectorsToFixed, +) { let mut cs = ConstraintSystem::default(); #[cfg(feature = "circuit-params")] let config = ConcreteCircuit::configure_with_params(&mut cs, params); #[cfg(not(feature = "circuit-params"))] let config = ConcreteCircuit::configure(&mut cs); + let cs = cs; + + let (cs, selectors_to_fixed) = if compress_selectors { + cs.selectors_to_fixed_compressed() + } else { + cs.selectors_to_fixed_direct() + }; - (config, cs) + (config, cs, selectors_to_fixed) } /// Compile a circuit. Runs configure and synthesize on the circuit in order to materialize the @@ -69,7 +81,9 @@ pub fn compile_circuit>( > { let n = 2usize.pow(k); - let (config, cs) = compile_circuit_cs::<_, ConcreteCircuit>( + // After this, the ConstraintSystem should not have any selectors: `verify` does not need them, and `keygen_pk` regenerates `cs` from scratch anyways. + let (config, cs, selectors_to_fixed) = compile_circuit_cs::<_, ConcreteCircuit>( + compress_selectors, #[cfg(feature = "circuit-params")] circuit.params(), ); @@ -94,13 +108,6 @@ pub fn compile_circuit>( cs.constants.clone(), )?; - // After this, the ConstraintSystem should not have any selectors: `verify` does not need them, and `keygen_pk` regenerates `cs` from scratch anyways. - let (cs, selectors_to_fixed) = if compress_selectors { - cs.selectors_to_fixed_compressed() - } else { - cs.selectors_to_fixed_direct() - }; - let mut fixed = batch_invert_assigned(assembly.fixed); let selector_polys = selectors_to_fixed.convert(assembly.selectors); fixed.extend(selector_polys); diff --git a/halo2_proofs/examples/serialization.rs b/halo2_proofs/examples/serialization.rs index 88de4a3f29..4a83392e37 100644 --- a/halo2_proofs/examples/serialization.rs +++ b/halo2_proofs/examples/serialization.rs @@ -147,6 +147,7 @@ fn main() { let pk = pk_read::( &mut reader, SerdeFormat::RawBytes, + compress_selectors, #[cfg(feature = "circuit-params")] circuit.params(), ) diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index fd4b0c80ab..dff3c7790d 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -14,7 +14,7 @@ mod verifier { 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; @@ -44,12 +44,14 @@ use std::io; pub fn vk_read>( reader: &mut R, format: SerdeFormat, + compress_selectors: bool, #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, ) -> io::Result> where C::Scalar: SerdePrimeField + FromUniformBytes<64>, { - let (_, cs) = compile_circuit_cs::<_, ConcreteCircuit>( + let (_, cs, _) = compile_circuit_cs::<_, ConcreteCircuit>( + compress_selectors, #[cfg(feature = "circuit-params")] params, ); @@ -72,12 +74,14 @@ where pub fn pk_read>( reader: &mut R, format: SerdeFormat, + compress_selectors: bool, #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, ) -> io::Result> where C::Scalar: SerdePrimeField + FromUniformBytes<64>, { - let (_, cs) = compile_circuit_cs::<_, ConcreteCircuit>( + let (_, cs, _) = compile_circuit_cs::<_, ConcreteCircuit>( + compress_selectors, #[cfg(feature = "circuit-params")] params, ); diff --git a/halo2_proofs/src/plonk/prover.rs b/halo2_proofs/src/plonk/prover.rs index 4bb8d4ad11..0470cd4749 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -37,31 +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>( - #[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 @@ -93,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::{ @@ -211,10 +245,11 @@ fn test_create_proof_custom() { let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); let engine = PlonkEngineConfig::build_default(); - create_proof_with_engine::, ProverSHPLONK<_>, _, _, _, _, _>( + create_proof_custom_with_engine::, ProverSHPLONK<_>, _, _, _, _, _>( engine, ¶ms, &pk, + compress_selectors, &[MyCircuit, MyCircuit], &[&[], &[]], OsRng, From 151112e61ff14b08af18ad3f45950f9ed2e14607 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Wed, 1 May 2024 00:01:26 +0800 Subject: [PATCH 08/30] fix!: correct the "compile_circuit" process --- halo2_frontend/src/circuit.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/halo2_frontend/src/circuit.rs b/halo2_frontend/src/circuit.rs index 74847ae3e7..c44d135be9 100644 --- a/halo2_frontend/src/circuit.rs +++ b/halo2_frontend/src/circuit.rs @@ -108,8 +108,12 @@ pub fn compile_circuit>( cs.constants.clone(), )?; + // Consider that "cs.num_fixed_columns" already increased by + // "selector_polys"(number of `fixed` columns converted from `selector`s) + // in "compile_circuit_cs" step let mut fixed = batch_invert_assigned(assembly.fixed); let selector_polys = selectors_to_fixed.convert(assembly.selectors); + fixed.truncate(cs.num_fixed_columns - selector_polys.len()); fixed.extend(selector_polys); // sort the "copies" for deterministic ordering From 61d2d05b2674f9db3568490b24f056c2e2950dc7 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Wed, 1 May 2024 00:06:26 +0800 Subject: [PATCH 09/30] chore: update import --- halo2_frontend/src/circuit.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/halo2_frontend/src/circuit.rs b/halo2_frontend/src/circuit.rs index c44d135be9..ab817cfef5 100644 --- a/halo2_frontend/src/circuit.rs +++ b/halo2_frontend/src/circuit.rs @@ -1,11 +1,11 @@ //! Traits and structs for implementing circuit components. -use crate::plonk::{self, SelectorsToFixed}; +use crate::plonk; use crate::plonk::{ permutation, sealed::{self, SealedPhase}, Advice, Assignment, Circuit, ConstraintSystem, FirstPhase, Fixed, FloorPlanner, Instance, - SecondPhase, ThirdPhase, + SecondPhase, SelectorsToFixed, ThirdPhase, }; use halo2_middleware::circuit::{Any, CompiledCircuit, Preprocessing}; use halo2_middleware::ff::{BatchInvert, Field}; From 0469a7b717e81b4aff488ac2a6edbda61590bed5 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Wed, 1 May 2024 00:14:54 +0800 Subject: [PATCH 10/30] chore: fmt --- halo2_frontend/src/circuit.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/halo2_frontend/src/circuit.rs b/halo2_frontend/src/circuit.rs index ab817cfef5..cba243330f 100644 --- a/halo2_frontend/src/circuit.rs +++ b/halo2_frontend/src/circuit.rs @@ -108,8 +108,8 @@ pub fn compile_circuit>( cs.constants.clone(), )?; - // Consider that "cs.num_fixed_columns" already increased by - // "selector_polys"(number of `fixed` columns converted from `selector`s) + // Consider that "cs.num_fixed_columns" already increased by + // "selector_polys"(number of `fixed` columns converted from `selector`s) // in "compile_circuit_cs" step let mut fixed = batch_invert_assigned(assembly.fixed); let selector_polys = selectors_to_fixed.convert(assembly.selectors); From ba65fd85b183d3ad889583250fe4d234c3f2fcfb Mon Sep 17 00:00:00 2001 From: guorong009 Date: Mon, 6 May 2024 18:15:31 +0800 Subject: [PATCH 11/30] feat: roll back "compress_selectors" logic --- halo2_frontend/src/circuit.rs | 31 ++--- halo2_frontend/src/dev.rs | 3 +- halo2_frontend/src/dev/cost.rs | 2 +- .../src/plonk/circuit/constraint_system.rs | 115 +++++++----------- halo2_proofs/examples/serialization.rs | 9 ++ halo2_proofs/src/plonk.rs | 6 +- halo2_proofs/src/plonk/prover.rs | 3 +- 7 files changed, 72 insertions(+), 97 deletions(-) diff --git a/halo2_frontend/src/circuit.rs b/halo2_frontend/src/circuit.rs index cba243330f..f33bbed5be 100644 --- a/halo2_frontend/src/circuit.rs +++ b/halo2_frontend/src/circuit.rs @@ -5,7 +5,7 @@ use crate::plonk::{ permutation, sealed::{self, SealedPhase}, Advice, Assignment, Circuit, ConstraintSystem, FirstPhase, Fixed, FloorPlanner, Instance, - SecondPhase, SelectorsToFixed, ThirdPhase, + SecondPhase, ThirdPhase, }; use halo2_middleware::circuit::{Any, CompiledCircuit, Preprocessing}; use halo2_middleware::ff::{BatchInvert, Field}; @@ -38,12 +38,10 @@ pub use table_layouter::{SimpleTableLayouter, TableLayouter}; /// The `ConstraintSystem` can be converted to `ConstraintSystemMid` to be used to interface /// with the backend. pub fn compile_circuit_cs>( - compress_selectors: bool, #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, ) -> ( ConcreteCircuit::Config, ConstraintSystem, - SelectorsToFixed, ) { let mut cs = ConstraintSystem::default(); #[cfg(feature = "circuit-params")] @@ -52,13 +50,7 @@ pub fn compile_circuit_cs>( let config = ConcreteCircuit::configure(&mut cs); let cs = cs; - let (cs, selectors_to_fixed) = if compress_selectors { - cs.selectors_to_fixed_compressed() - } else { - cs.selectors_to_fixed_direct() - }; - - (config, cs, selectors_to_fixed) + (config, cs) } /// Compile a circuit. Runs configure and synthesize on the circuit in order to materialize the @@ -82,8 +74,7 @@ pub fn compile_circuit>( let n = 2usize.pow(k); // After this, the ConstraintSystem should not have any selectors: `verify` does not need them, and `keygen_pk` regenerates `cs` from scratch anyways. - let (config, cs, selectors_to_fixed) = compile_circuit_cs::<_, ConcreteCircuit>( - compress_selectors, + let (config, cs) = compile_circuit_cs::<_, ConcreteCircuit>( #[cfg(feature = "circuit-params")] circuit.params(), ); @@ -108,13 +99,17 @@ pub fn compile_circuit>( cs.constants.clone(), )?; - // Consider that "cs.num_fixed_columns" already increased by - // "selector_polys"(number of `fixed` columns converted from `selector`s) - // in "compile_circuit_cs" step let mut fixed = batch_invert_assigned(assembly.fixed); - let selector_polys = selectors_to_fixed.convert(assembly.selectors); - fixed.truncate(cs.num_fixed_columns - selector_polys.len()); - fixed.extend(selector_polys); + println!("before: {}", fixed.len()); + // fixed.truncate(cs.num_fixed_columns - selector_polys.len()); + let (cs, selectors_to_fixed) = if compress_selectors { + cs.compress_selectors(assembly.selectors) + } else { + cs.directly_convert_selectors_to_fixed(assembly.selectors) + }; + // let selector_polys = selectors_to_fixed.convert(assembly.selectors); + fixed.extend(selectors_to_fixed); + println!("after: {}", fixed.len()); // sort the "copies" for deterministic ordering #[cfg(feature = "thread-safe-region")] diff --git a/halo2_frontend/src/dev.rs b/halo2_frontend/src/dev.rs index 787a6dfe98..f6cc12ce82 100644 --- a/halo2_frontend/src/dev.rs +++ b/halo2_frontend/src/dev.rs @@ -710,8 +710,7 @@ impl + Ord> MockProver { )?; } - let (cs, selectors_to_fixed) = prover.cs.selectors_to_fixed_compressed(); - let selector_polys = selectors_to_fixed.convert(prover.selectors.clone()); + let (cs, selector_polys) = prover.cs.compress_selectors(prover.selectors.clone()); prover.cs = cs; prover.fixed.extend(selector_polys.into_iter().map(|poly| { let mut v = vec![CellValue::Unassigned; n]; diff --git a/halo2_frontend/src/dev/cost.rs b/halo2_frontend/src/dev/cost.rs index 7d1b70ac1c..870a7e6008 100644 --- a/halo2_frontend/src/dev/cost.rs +++ b/halo2_frontend/src/dev/cost.rs @@ -282,7 +282,7 @@ impl> CircuitCost= cs.minimum_rows()); diff --git a/halo2_frontend/src/plonk/circuit/constraint_system.rs b/halo2_frontend/src/plonk/circuit/constraint_system.rs index d6685c1f09..46dc3c4d1c 100644 --- a/halo2_frontend/src/plonk/circuit/constraint_system.rs +++ b/halo2_frontend/src/plonk/circuit/constraint_system.rs @@ -638,19 +638,23 @@ impl ConstraintSystem { }); } - /// Transform this `ConstraintSystem` into an equivalent one that replaces the selector columns - /// by fixed columns applying compression where possible. + /// This will compress selectors together depending on their provided + /// assignments. This `ConstraintSystem` will then be modified to add new + /// fixed columns (representing the actual selectors) and will return the + /// polynomials for those columns. Finally, an internal map is updated to + /// find which fixed column corresponds with a given `Selector`. /// - /// Panics if called twice. - pub fn selectors_to_fixed_compressed(mut self) -> (Self, SelectorsToFixed) { - if self.selectors_to_fixed { - panic!("the selectors have already been transformed to fixed columns"); - } + /// Do not call this twice. Yes, this should be a builder pattern instead. + pub fn compress_selectors(mut self, selectors: Vec>) -> (Self, Vec>) { + // The number of provided selector assignments must be the number we + // counted for this constraint system. + assert_eq!(selectors.len(), self.num_selectors); + // Compute the maximal degree of every selector. We only consider the // expressions in gates, as lookup arguments cannot support simple // selectors. Selectors that are complex or do not appear in any gates // will have degree zero. - let mut degrees = vec![0; self.num_selectors]; + let mut degrees = vec![0; selectors.len()]; for expr in self.gates.iter().flat_map(|gate| gate.polys.iter()) { if let Some(selector) = expr.extract_simple_selector() { degrees[selector.0] = max(degrees[selector.0], expr.degree()); @@ -660,22 +664,20 @@ impl ConstraintSystem { // We will not increase the degree of the constraint system, so we limit // ourselves to the largest existing degree constraint. let max_degree = self.degree(); - let selectors_to_fixed = SelectorsToFixed { - compress: true, - num_selectors: self.num_selectors, - max_degree, - degrees: degrees.clone(), - }; let mut new_columns = vec![]; - let (_, selector_assignment) = compress_selectors::process( - (0..self.num_selectors) + let (polys, selector_assignment) = compress_selectors::process( + selectors + .into_iter() .zip(degrees) - .map(|(i, max_degree)| compress_selectors::SelectorDescription { - selector: i, - activations: vec![], - max_degree, - }) + .enumerate() + .map( + |(i, (activations, max_degree))| compress_selectors::SelectorDescription { + selector: i, + activations, + max_degree, + }, + ) .collect(), max_degree, || { @@ -705,68 +707,41 @@ impl ConstraintSystem { .map(|a| a.unwrap()) .collect::>(); self.replace_selectors_with_fixed(&selector_replacements); - self.selectors_to_fixed = true; - - (self, selectors_to_fixed) - } - - /// This will compress selectors together depending on their provided - /// assignments. This `ConstraintSystem` will then be modified to add new - /// fixed columns (representing the actual selectors) and will return the - /// polynomials for those columns. Finally, an internal map is updated to - /// find which fixed column corresponds with a given `Selector`. - /// - /// Do not call this twice. Yes, this should be a builder pattern instead. - #[deprecated(note = "Use `selectors_to_fixed_compressed` instead")] - pub fn compress_selectors(self, selectors: Vec>) -> (Self, Vec>) { - let (cs, selectors_to_fixed) = self.selectors_to_fixed_compressed(); - let fixed_polys = selectors_to_fixed.convert(selectors); - (cs, fixed_polys) + (self, polys) } - /// Transform this `ConstraintSystem` into an equivalent one that replaces the selector columns - /// by fixed columns with a direct mapping. - /// - /// Panics if called twice. - pub fn selectors_to_fixed_direct(mut self) -> (Self, SelectorsToFixed) { - if self.selectors_to_fixed { - panic!("the selectors have already been transformed to fixed columns"); - } - let selectors_to_fixed = SelectorsToFixed { - compress: false, - num_selectors: self.num_selectors, - max_degree: 0, - degrees: vec![], - }; + /// Does not combine selectors and directly replaces them everywhere with fixed columns. + pub fn directly_convert_selectors_to_fixed( + mut self, + selectors: Vec>, + ) -> (Self, Vec>) { + // The number of provided selector assignments must be the number we + // counted for this constraint system. + assert_eq!(selectors.len(), self.num_selectors); - let selector_replacements: Vec<_> = (0..self.num_selectors) - .map(|_| { + let (polys, selector_replacements): (Vec<_>, Vec<_>) = selectors + .into_iter() + .map(|selector| { + let poly = selector + .iter() + .map(|b| if *b { F::ONE } else { F::ZERO }) + .collect::>(); let column = self.fixed_column(); let rotation = Rotation::cur(); - Expression::Fixed(FixedQuery { + let expr = Expression::Fixed(FixedQuery { index: Some(self.query_fixed_index(column, rotation)), column_index: column.index, rotation, - }) + }); + (poly, expr) }) - .collect(); + .unzip(); self.replace_selectors_with_fixed(&selector_replacements); - self.selectors_to_fixed = true; - (self, selectors_to_fixed) - } - - /// Does not combine selectors and directly replaces them everywhere with fixed columns. - #[deprecated(note = "Use `selectors_to_fixed_direct` instead")] - pub fn directly_convert_selectors_to_fixed( - self, - selectors: Vec>, - ) -> (Self, Vec>) { - let (cs, selectors_to_fixed) = self.selectors_to_fixed_direct(); - let fixed_polys = selectors_to_fixed.convert(selectors); + self.num_selectors = 0; - (cs, fixed_polys) + (self, polys) } fn replace_selectors_with_fixed(&mut self, selector_replacements: &[Expression]) { diff --git a/halo2_proofs/examples/serialization.rs b/halo2_proofs/examples/serialization.rs index 4a83392e37..64cae2d55f 100644 --- a/halo2_proofs/examples/serialization.rs +++ b/halo2_proofs/examples/serialization.rs @@ -48,6 +48,15 @@ impl StandardPlonkConfig { [a, b, c].map(|column| meta.enable_equality(column)); + // // TEST for "compress_selectors" logic correctness + // let s = meta.selector(); + // meta.create_gate("selector gate", |meta| { + // let s = meta.query_selector(s); + // let a = meta.query_advice(a, Rotation::cur()); + // let b = meta.query_advice(b, Rotation::cur()); + // vec![s * (a - b)] + // }); + meta.create_gate( "q_a·a + q_b·b + q_c·c + q_ab·a·b + constant + instance = 0", |meta| { diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index dff3c7790d..2234ebc6e3 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -50,8 +50,7 @@ pub fn vk_read, { - let (_, cs, _) = compile_circuit_cs::<_, ConcreteCircuit>( - compress_selectors, + let (_, cs) = compile_circuit_cs::<_, ConcreteCircuit>( #[cfg(feature = "circuit-params")] params, ); @@ -80,8 +79,7 @@ pub fn pk_read, { - let (_, cs, _) = compile_circuit_cs::<_, ConcreteCircuit>( - compress_selectors, + let (_, cs) = compile_circuit_cs::<_, ConcreteCircuit>( #[cfg(feature = "circuit-params")] params, ); diff --git a/halo2_proofs/src/plonk/prover.rs b/halo2_proofs/src/plonk/prover.rs index 0470cd4749..77abcbc2a6 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -102,8 +102,7 @@ where if circuits.len() != instances.len() { return Err(Error::Backend(ErrorBack::InvalidInstances)); } - let (config, cs, _) = compile_circuit_cs::<_, ConcreteCircuit>( - compress_selectors, + let (config, cs) = compile_circuit_cs::<_, ConcreteCircuit>( #[cfg(feature = "circuit-params")] circuits[0].params(), ); From 0125d5f3bfa654946c14a365883fd8542d453679 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Mon, 6 May 2024 18:18:38 +0800 Subject: [PATCH 12/30] chore: update the .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index f2af733bf1..123de7b254 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ Cargo.lock .vscode **/*.html .DS_Store + +layout.png +serialization-test.pk From 2034660c09c55d8a0e9b0c276d37df1bd5c43b44 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Mon, 6 May 2024 18:18:52 +0800 Subject: [PATCH 13/30] fix: update the "layout.rs" --- halo2_frontend/src/dev/graph/layout.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/halo2_frontend/src/dev/graph/layout.rs b/halo2_frontend/src/dev/graph/layout.rs index 3520a2cc4a..cb04e4d9e6 100644 --- a/halo2_frontend/src/dev/graph/layout.rs +++ b/halo2_frontend/src/dev/graph/layout.rs @@ -104,8 +104,7 @@ impl CircuitLayout { cs.constants.clone(), ) .unwrap(); - let (cs, selectors_to_fixed) = cs.selectors_to_fixed_compressed(); - let selector_polys = selectors_to_fixed.convert::(layout.selectors); + let (cs, selector_polys) = cs.compress_selectors(layout.selectors); let non_selector_fixed_columns = cs.num_fixed_columns - selector_polys.len(); // Figure out what order to render the columns in. From 09f63a21bcfce2fcf47e2222c06845a08add7478 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Mon, 6 May 2024 18:25:20 +0800 Subject: [PATCH 14/30] feat: remove the "compress_selectors" from several utils --- halo2_proofs/examples/serialization.rs | 1 - halo2_proofs/src/plonk.rs | 4 +- halo2_proofs/src/plonk/prover.rs | 86 ++++++++------------------ 3 files changed, 27 insertions(+), 64 deletions(-) diff --git a/halo2_proofs/examples/serialization.rs b/halo2_proofs/examples/serialization.rs index 64cae2d55f..a4e9f5c929 100644 --- a/halo2_proofs/examples/serialization.rs +++ b/halo2_proofs/examples/serialization.rs @@ -156,7 +156,6 @@ fn main() { let pk = pk_read::( &mut reader, SerdeFormat::RawBytes, - compress_selectors, #[cfg(feature = "circuit-params")] circuit.params(), ) diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index 2234ebc6e3..fd4b0c80ab 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -14,7 +14,7 @@ mod verifier { pub use keygen::{keygen_pk, keygen_pk_custom, keygen_vk, keygen_vk_custom}; -pub use prover::{create_proof, create_proof_custom_with_engine, create_proof_with_engine}; +pub use prover::{create_proof, create_proof_with_engine}; pub use verifier::verify_proof; pub use error::Error; @@ -44,7 +44,6 @@ use std::io; pub fn vk_read>( reader: &mut R, format: SerdeFormat, - compress_selectors: bool, #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, ) -> io::Result> where @@ -73,7 +72,6 @@ where pub fn pk_read>( reader: &mut R, format: SerdeFormat, - compress_selectors: bool, #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, ) -> io::Result> where diff --git a/halo2_proofs/src/plonk/prover.rs b/halo2_proofs/src/plonk/prover.rs index 77abcbc2a6..4bb8d4ad11 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -37,9 +37,31 @@ pub fn create_proof_with_engine< where Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, { - create_proof_custom_with_engine::( - engine, params, pk, true, circuits, instances, rng, transcript, - ) + if circuits.len() != instances.len() { + return Err(Error::Backend(ErrorBack::InvalidInstances)); + } + let (config, cs) = compile_circuit_cs::<_, ConcreteCircuit>( + #[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()?) } /// This creates a proof for the provided `circuit` when given the public @@ -71,61 +93,6 @@ 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>( - #[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::{ @@ -244,11 +211,10 @@ fn test_create_proof_custom() { let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); let engine = PlonkEngineConfig::build_default(); - create_proof_custom_with_engine::, ProverSHPLONK<_>, _, _, _, _, _>( + create_proof_with_engine::, ProverSHPLONK<_>, _, _, _, _, _>( engine, ¶ms, &pk, - compress_selectors, &[MyCircuit, MyCircuit], &[&[], &[]], OsRng, From ef7573d8a220e94a7dafb2f5301319754f9aa7e3 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Mon, 6 May 2024 18:26:20 +0800 Subject: [PATCH 15/30] fix: remove the "selectors_to_fixed" field from "ConstraintSystem" --- halo2_frontend/src/plonk/circuit/constraint_system.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/halo2_frontend/src/plonk/circuit/constraint_system.rs b/halo2_frontend/src/plonk/circuit/constraint_system.rs index 46dc3c4d1c..45010209ec 100644 --- a/halo2_frontend/src/plonk/circuit/constraint_system.rs +++ b/halo2_frontend/src/plonk/circuit/constraint_system.rs @@ -276,9 +276,6 @@ pub struct ConstraintSystem { /// fixed column that they were compressed into. This is just used by dev /// tooling right now. pub(crate) selector_map: Vec>, - /// Status boolean indicating wether the selectors have been already transformed to fixed columns - /// or not. - selectors_to_fixed: bool, pub(crate) gates: Vec>, pub(crate) advice_queries: Vec<(Column, Rotation)>, @@ -371,7 +368,6 @@ impl Default for ConstraintSystem { advice_column_phase: Vec::new(), challenge_phase: Vec::new(), selector_map: vec![], - selectors_to_fixed: false, gates: vec![], fixed_queries: Vec::new(), advice_queries: Vec::new(), From 16887543494111614154fdd9891213cdb30c1837 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Mon, 6 May 2024 18:34:05 +0800 Subject: [PATCH 16/30] feat: remove the "compress_selectors" option & default to "true" --- halo2_frontend/src/circuit.rs | 11 +---- halo2_proofs/examples/serialization.rs | 5 +- halo2_proofs/src/plonk.rs | 2 +- halo2_proofs/src/plonk/keygen.rs | 48 +------------------- halo2_proofs/src/plonk/prover.rs | 7 ++- halo2_proofs/tests/frontend_backend_split.rs | 2 +- 6 files changed, 11 insertions(+), 64 deletions(-) diff --git a/halo2_frontend/src/circuit.rs b/halo2_frontend/src/circuit.rs index f33bbed5be..30bfd1e483 100644 --- a/halo2_frontend/src/circuit.rs +++ b/halo2_frontend/src/circuit.rs @@ -62,7 +62,6 @@ pub fn compile_circuit_cs>( pub fn compile_circuit>( k: u32, circuit: &ConcreteCircuit, - compress_selectors: bool, ) -> Result< ( CompiledCircuit, @@ -101,14 +100,8 @@ pub fn compile_circuit>( let mut fixed = batch_invert_assigned(assembly.fixed); println!("before: {}", fixed.len()); - // fixed.truncate(cs.num_fixed_columns - selector_polys.len()); - let (cs, selectors_to_fixed) = if compress_selectors { - cs.compress_selectors(assembly.selectors) - } else { - cs.directly_convert_selectors_to_fixed(assembly.selectors) - }; - // let selector_polys = selectors_to_fixed.convert(assembly.selectors); - fixed.extend(selectors_to_fixed); + let (cs, selector_polys) = cs.compress_selectors(assembly.selectors); + fixed.extend(selector_polys); println!("after: {}", fixed.len()); // sort the "copies" for deterministic ordering diff --git a/halo2_proofs/examples/serialization.rs b/halo2_proofs/examples/serialization.rs index a4e9f5c929..51af6f44f5 100644 --- a/halo2_proofs/examples/serialization.rs +++ b/halo2_proofs/examples/serialization.rs @@ -7,7 +7,7 @@ use ff::Field; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, plonk::{ - create_proof, keygen_pk, keygen_vk_custom, pk_read, verify_proof, Advice, Circuit, Column, + create_proof, keygen_pk, keygen_vk, pk_read, verify_proof, Advice, Circuit, Column, ConstraintSystem, ErrorFront, Fixed, Instance, }, poly::{ @@ -141,8 +141,7 @@ fn main() { let k = 4; let circuit = StandardPlonk(Fr::random(OsRng)); let params = ParamsKZG::::setup(k, OsRng); - let compress_selectors = true; - let vk = keygen_vk_custom(¶ms, &circuit, compress_selectors).expect("vk should not fail"); + let vk = keygen_vk(¶ms, &circuit).expect("vk should not fail"); let pk = keygen_pk(¶ms, vk, &circuit).expect("pk should not fail"); let f = File::create("serialization-test.pk").unwrap(); diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index fd4b0c80ab..594cf6853b 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -12,7 +12,7 @@ mod verifier { pub use halo2_backend::plonk::verifier::verify_proof; } -pub use keygen::{keygen_pk, keygen_pk_custom, keygen_vk, keygen_vk_custom}; +pub use keygen::{keygen_pk, keygen_vk}; pub use prover::{create_proof, create_proof_with_engine}; pub use verifier::verify_proof; diff --git a/halo2_proofs/src/plonk/keygen.rs b/halo2_proofs/src/plonk/keygen.rs index 1895e36f8c..e45899b829 100644 --- a/halo2_proofs/src/plonk/keygen.rs +++ b/halo2_proofs/src/plonk/keygen.rs @@ -24,29 +24,7 @@ where ConcreteCircuit: Circuit, C::Scalar: FromUniformBytes<64>, { - keygen_vk_custom(params, circuit, true) -} - -/// 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, - compress_selectors: bool, -) -> Result, Error> -where - C: CurveAffine, - P: Params<'params, C>, - ConcreteCircuit: Circuit, - C::Scalar: FromUniformBytes<64>, -{ - let (compiled_circuit, _, _) = compile_circuit(params.k(), circuit, compress_selectors)?; + let (compiled_circuit, _, _) = compile_circuit(params.k(), circuit)?; Ok(backend_keygen_vk(params, &compiled_circuit)?) } @@ -66,28 +44,6 @@ where P: Params<'params, C>, ConcreteCircuit: Circuit, { - 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)?; + let (compiled_circuit, _, _) = compile_circuit(params.k(), circuit)?; 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 4bb8d4ad11..e7ff0a0a07 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -167,7 +167,7 @@ fn test_create_proof() { fn test_create_proof_custom() { use crate::{ circuit::SimpleFloorPlanner, - plonk::{keygen_pk_custom, keygen_vk_custom, ConstraintSystem, ErrorFront}, + plonk::{keygen_vk, keygen_pk, ConstraintSystem, ErrorFront}, poly::kzg::{ commitment::{KZGCommitmentScheme, ParamsKZG}, multiopen::ProverSHPLONK, @@ -203,10 +203,9 @@ fn test_create_proof_custom() { } let params: ParamsKZG = ParamsKZG::setup(3, OsRng); - let compress_selectors = true; - let vk = keygen_vk_custom(¶ms, &MyCircuit, compress_selectors) + let vk = keygen_vk(¶ms, &MyCircuit) .expect("keygen_vk_custom should not fail"); - let pk = keygen_pk_custom(¶ms, vk, &MyCircuit, compress_selectors) + let pk = keygen_pk(¶ms, vk, &MyCircuit) .expect("keygen_pk_custom should not fail"); let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); let engine = PlonkEngineConfig::build_default(); diff --git a/halo2_proofs/tests/frontend_backend_split.rs b/halo2_proofs/tests/frontend_backend_split.rs index 0c58069340..8fa69c54ec 100644 --- a/halo2_proofs/tests/frontend_backend_split.rs +++ b/halo2_proofs/tests/frontend_backend_split.rs @@ -575,7 +575,7 @@ fn test_mycircuit_full_split() { .build(); let k = K; let circuit: MyCircuit = MyCircuit::new(k, 42); - let (compiled_circuit, config, cs) = compile_circuit(k, &circuit, false).unwrap(); + let (compiled_circuit, config, cs) = compile_circuit(k, &circuit).unwrap(); // Setup let mut rng = BlockRng::new(OneNg {}); From 0fb9792cb0b2aa0b34892210218558928e2006de Mon Sep 17 00:00:00 2001 From: guorong009 Date: Mon, 6 May 2024 21:56:12 +0800 Subject: [PATCH 17/30] feat: update the "vk_read" & "pk_read" --- halo2_frontend/src/circuit.rs | 33 +++++--------------------- halo2_proofs/examples/serialization.rs | 25 ++++++++----------- halo2_proofs/src/plonk.rs | 20 +++++++--------- halo2_proofs/src/plonk/prover.rs | 16 +++++-------- 4 files changed, 31 insertions(+), 63 deletions(-) diff --git a/halo2_frontend/src/circuit.rs b/halo2_frontend/src/circuit.rs index 30bfd1e483..ca958075fe 100644 --- a/halo2_frontend/src/circuit.rs +++ b/halo2_frontend/src/circuit.rs @@ -31,28 +31,6 @@ pub mod layouter; pub use table_layouter::{SimpleTableLayouter, TableLayouter}; -/// Compile a circuit, only generating the `Config` and the `ConstraintSystem` related information, -/// skipping all preprocessing data. -/// The `ConcreteCircuit::Config`, `ConstraintSystem` and `SelectorsToFixed` are outputs for the -/// frontend itself, which will be used for witness generation and fixed column assignment. -/// The `ConstraintSystem` can be converted to `ConstraintSystemMid` to be used to interface -/// with the backend. -pub fn compile_circuit_cs>( - #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, -) -> ( - ConcreteCircuit::Config, - ConstraintSystem, -) { - let mut cs = ConstraintSystem::default(); - #[cfg(feature = "circuit-params")] - let config = ConcreteCircuit::configure_with_params(&mut cs, params); - #[cfg(not(feature = "circuit-params"))] - let config = ConcreteCircuit::configure(&mut cs); - let cs = cs; - - (config, cs) -} - /// Compile a circuit. Runs configure and synthesize on the circuit in order to materialize the /// circuit into its columns and the column configuration; as well as doing the fixed column and /// copy constraints assignments. The output of this function can then be used for the key @@ -72,11 +50,12 @@ pub fn compile_circuit>( > { let n = 2usize.pow(k); - // After this, the ConstraintSystem should not have any selectors: `verify` does not need them, and `keygen_pk` regenerates `cs` from scratch anyways. - let (config, cs) = compile_circuit_cs::<_, ConcreteCircuit>( - #[cfg(feature = "circuit-params")] - circuit.params(), - ); + 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); if n < cs.minimum_rows() { return Err(Error::not_enough_rows_available(k)); diff --git a/halo2_proofs/examples/serialization.rs b/halo2_proofs/examples/serialization.rs index 51af6f44f5..fc668ff94f 100644 --- a/halo2_proofs/examples/serialization.rs +++ b/halo2_proofs/examples/serialization.rs @@ -48,14 +48,14 @@ impl StandardPlonkConfig { [a, b, c].map(|column| meta.enable_equality(column)); - // // TEST for "compress_selectors" logic correctness - // let s = meta.selector(); - // meta.create_gate("selector gate", |meta| { - // let s = meta.query_selector(s); - // let a = meta.query_advice(a, Rotation::cur()); - // let b = meta.query_advice(b, Rotation::cur()); - // vec![s * (a - b)] - // }); + // TEST for "compress_selectors" logic correctness + let s = meta.selector(); + meta.create_gate("selector gate", |meta| { + let s = meta.query_selector(s); + let a = meta.query_advice(a, Rotation::cur()); + let b = meta.query_advice(b, Rotation::cur()); + vec![s * (a - b)] + }); meta.create_gate( "q_a·a + q_b·b + q_c·c + q_ab·a·b + constant + instance = 0", @@ -152,13 +152,8 @@ fn main() { let f = File::open("serialization-test.pk").unwrap(); let mut reader = BufReader::new(f); #[allow(clippy::unit_arg)] - let pk = pk_read::( - &mut reader, - SerdeFormat::RawBytes, - #[cfg(feature = "circuit-params")] - circuit.params(), - ) - .unwrap(); + let pk = pk_read::(&mut reader, SerdeFormat::RawBytes, k, &circuit) + .unwrap(); std::fs::remove_file("serialization-test.pk").unwrap(); diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index 594cf6853b..c5c8df0164 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -12,6 +12,7 @@ mod verifier { pub use halo2_backend::plonk::verifier::verify_proof; } +use halo2_frontend::circuit::compile_circuit; pub use keygen::{keygen_pk, keygen_vk}; pub use prover::{create_proof, create_proof_with_engine}; @@ -28,7 +29,6 @@ pub use halo2_middleware::circuit::{Any, ConstraintSystemMid}; use group::ff::FromUniformBytes; use halo2_backend::helpers::{SerdeCurveAffine, SerdeFormat, SerdePrimeField}; -use halo2_frontend::circuit::compile_circuit_cs; use std::io; /// Reads a verification key from a buffer. @@ -44,15 +44,14 @@ use std::io; pub fn vk_read>( reader: &mut R, format: SerdeFormat, - #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, + k: u32, + circuit: &ConcreteCircuit, ) -> io::Result> where C::Scalar: SerdePrimeField + FromUniformBytes<64>, { - let (_, cs) = compile_circuit_cs::<_, ConcreteCircuit>( - #[cfg(feature = "circuit-params")] - params, - ); + let (_, _, cs) = compile_circuit(k, circuit) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; let cs_mid: ConstraintSystemMid<_> = cs.into(); VerifyingKey::read(reader, format, cs_mid.into()) } @@ -72,15 +71,14 @@ where pub fn pk_read>( reader: &mut R, format: SerdeFormat, - #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params, + k: u32, + circuit: &ConcreteCircuit, ) -> io::Result> where C::Scalar: SerdePrimeField + FromUniformBytes<64>, { - let (_, cs) = compile_circuit_cs::<_, ConcreteCircuit>( - #[cfg(feature = "circuit-params")] - params, - ); + let (_, _, cs) = compile_circuit(k, circuit) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; let cs_mid: ConstraintSystemMid<_> = cs.into(); ProvingKey::read(reader, format, cs_mid.into()) } diff --git a/halo2_proofs/src/plonk/prover.rs b/halo2_proofs/src/plonk/prover.rs index e7ff0a0a07..c3d39e44ca 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -2,7 +2,7 @@ use crate::plonk::{Error, ErrorBack}; use crate::poly::commitment::{self, CommitmentScheme, Params}; use crate::transcript::{EncodedChallenge, TranscriptWrite}; use halo2_backend::plonk::{prover::Prover, ProvingKey}; -use halo2_frontend::circuit::{compile_circuit_cs, WitnessCalculator}; +use halo2_frontend::circuit::{compile_circuit, WitnessCalculator}; use halo2_frontend::plonk::Circuit; use halo2_middleware::ff::{FromUniformBytes, WithSmallOrderMulGroup}; use halo2_middleware::zal::{ @@ -40,10 +40,8 @@ where if circuits.len() != instances.len() { return Err(Error::Backend(ErrorBack::InvalidInstances)); } - let (config, cs) = compile_circuit_cs::<_, ConcreteCircuit>( - #[cfg(feature = "circuit-params")] - circuits[0].params(), - ); + + let (_, config, cs) = compile_circuit::<_, ConcreteCircuit>(params.k(), &circuits[0])?; let mut witness_calcs: Vec<_> = circuits .iter() .enumerate() @@ -167,7 +165,7 @@ fn test_create_proof() { fn test_create_proof_custom() { use crate::{ circuit::SimpleFloorPlanner, - plonk::{keygen_vk, keygen_pk, ConstraintSystem, ErrorFront}, + plonk::{keygen_pk, keygen_vk, ConstraintSystem, ErrorFront}, poly::kzg::{ commitment::{KZGCommitmentScheme, ParamsKZG}, multiopen::ProverSHPLONK, @@ -203,10 +201,8 @@ fn test_create_proof_custom() { } let params: ParamsKZG = ParamsKZG::setup(3, OsRng); - let vk = keygen_vk(¶ms, &MyCircuit) - .expect("keygen_vk_custom should not fail"); - let pk = keygen_pk(¶ms, vk, &MyCircuit) - .expect("keygen_pk_custom should not fail"); + let vk = keygen_vk(¶ms, &MyCircuit).expect("keygen_vk_custom should not fail"); + let pk = keygen_pk(¶ms, vk, &MyCircuit).expect("keygen_pk_custom should not fail"); let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); let engine = PlonkEngineConfig::build_default(); From 33b6dbdcf6b124891160c88ffa455f0fb5b8b89b Mon Sep 17 00:00:00 2001 From: guorong009 Date: Mon, 6 May 2024 22:12:26 +0800 Subject: [PATCH 18/30] chore: remove leftovers --- halo2_frontend/src/circuit.rs | 2 - .../src/plonk/circuit/constraint_system.rs | 82 ------------------- 2 files changed, 84 deletions(-) diff --git a/halo2_frontend/src/circuit.rs b/halo2_frontend/src/circuit.rs index ca958075fe..1dd25a0bc9 100644 --- a/halo2_frontend/src/circuit.rs +++ b/halo2_frontend/src/circuit.rs @@ -78,10 +78,8 @@ pub fn compile_circuit>( )?; let mut fixed = batch_invert_assigned(assembly.fixed); - println!("before: {}", fixed.len()); let (cs, selector_polys) = cs.compress_selectors(assembly.selectors); fixed.extend(selector_polys); - println!("after: {}", fixed.len()); // sort the "copies" for deterministic ordering #[cfg(feature = "thread-safe-region")] diff --git a/halo2_frontend/src/plonk/circuit/constraint_system.rs b/halo2_frontend/src/plonk/circuit/constraint_system.rs index 45010209ec..7ac2aaafa5 100644 --- a/halo2_frontend/src/plonk/circuit/constraint_system.rs +++ b/halo2_frontend/src/plonk/circuit/constraint_system.rs @@ -307,55 +307,6 @@ pub struct ConstraintSystem { pub(crate) minimum_degree: Option, } -/// Helper struct with the parameters required to convert selector assignments into fixed column -/// assignments. -pub struct SelectorsToFixed { - compress: bool, - num_selectors: usize, - max_degree: usize, - degrees: Vec, -} - -impl SelectorsToFixed { - /// Convert selector assignments into fixed column assignments based on the parameters in - /// `SelectorsToFixed`. - pub fn convert(&self, selectors: Vec>) -> Vec> { - // The number of provided selector assignments must be the number we - // counted for this constraint system. - assert_eq!(selectors.len(), self.num_selectors); - - if self.compress { - let (polys, _) = compress_selectors::process( - selectors - .into_iter() - .zip(self.degrees.iter()) - .enumerate() - .map( - |(i, (activations, max_degree))| compress_selectors::SelectorDescription { - selector: i, - activations, - max_degree: *max_degree, - }, - ) - .collect(), - self.max_degree, - || Expression::Constant(F::ZERO), - ); - polys - } else { - selectors - .into_iter() - .map(|selector| { - selector - .iter() - .map(|b| if *b { F::ONE } else { F::ZERO }) - .collect::>() - }) - .collect() - } - } -} - impl Default for ConstraintSystem { fn default() -> ConstraintSystem { ConstraintSystem { @@ -707,39 +658,6 @@ impl ConstraintSystem { (self, polys) } - /// Does not combine selectors and directly replaces them everywhere with fixed columns. - pub fn directly_convert_selectors_to_fixed( - mut self, - selectors: Vec>, - ) -> (Self, Vec>) { - // The number of provided selector assignments must be the number we - // counted for this constraint system. - assert_eq!(selectors.len(), self.num_selectors); - - let (polys, selector_replacements): (Vec<_>, Vec<_>) = selectors - .into_iter() - .map(|selector| { - let poly = selector - .iter() - .map(|b| if *b { F::ONE } else { F::ZERO }) - .collect::>(); - let column = self.fixed_column(); - let rotation = Rotation::cur(); - let expr = Expression::Fixed(FixedQuery { - index: Some(self.query_fixed_index(column, rotation)), - column_index: column.index, - rotation, - }); - (poly, expr) - }) - .unzip(); - - self.replace_selectors_with_fixed(&selector_replacements); - self.num_selectors = 0; - - (self, polys) - } - fn replace_selectors_with_fixed(&mut self, selector_replacements: &[Expression]) { fn replace_selectors( expr: &mut Expression, From bbc773b80ae01181678ead168f579ef0ab7cee0d Mon Sep 17 00:00:00 2001 From: guorong009 Date: Mon, 6 May 2024 23:24:19 +0800 Subject: [PATCH 19/30] fix: re-enable the "compress_selectors" option --- halo2_frontend/src/circuit.rs | 14 +++- .../src/plonk/circuit/constraint_system.rs | 33 ++++++++ halo2_proofs/examples/serialization.rs | 16 ++-- halo2_proofs/src/plonk.rs | 10 ++- halo2_proofs/src/plonk/keygen.rs | 48 ++++++++++- halo2_proofs/src/plonk/prover.rs | 80 +++++++++++++------ halo2_proofs/tests/frontend_backend_split.rs | 2 +- 7 files changed, 165 insertions(+), 38 deletions(-) diff --git a/halo2_frontend/src/circuit.rs b/halo2_frontend/src/circuit.rs index 1dd25a0bc9..157127dc02 100644 --- a/halo2_frontend/src/circuit.rs +++ b/halo2_frontend/src/circuit.rs @@ -40,6 +40,7 @@ pub use table_layouter::{SimpleTableLayouter, TableLayouter}; pub fn compile_circuit>( k: u32, circuit: &ConcreteCircuit, + compress_selectors: bool, ) -> Result< ( CompiledCircuit, @@ -49,17 +50,17 @@ pub fn compile_circuit>( Error, > { let n = 2usize.pow(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() { return Err(Error::not_enough_rows_available(k)); } + let mut assembly = plonk::keygen::Assembly { k, fixed: vec![vec![F::ZERO.into(); n]; cs.num_fixed_columns], @@ -78,7 +79,14 @@ pub fn compile_circuit>( )?; let mut fixed = batch_invert_assigned(assembly.fixed); - let (cs, selector_polys) = cs.compress_selectors(assembly.selectors); + let (cs, selector_polys) = if compress_selectors { + cs.compress_selectors(assembly.selectors) + } else { + // After this, the ConstraintSystem should not have any selectors: `verify` does not need them, and `keygen_pk` regenerates `cs` from scratch anyways. + let selectors = std::mem::take(&mut assembly.selectors); + cs.directly_convert_selectors_to_fixed(selectors) + }; + fixed.extend(selector_polys); // sort the "copies" for deterministic ordering diff --git a/halo2_frontend/src/plonk/circuit/constraint_system.rs b/halo2_frontend/src/plonk/circuit/constraint_system.rs index 7ac2aaafa5..8794f38578 100644 --- a/halo2_frontend/src/plonk/circuit/constraint_system.rs +++ b/halo2_frontend/src/plonk/circuit/constraint_system.rs @@ -658,6 +658,39 @@ impl ConstraintSystem { (self, polys) } + /// Does not combine selectors and directly replaces them everywhere with fixed columns. + pub fn directly_convert_selectors_to_fixed( + mut self, + selectors: Vec>, + ) -> (Self, Vec>) { + // The number of provided selector assignments must be the number we + // counted for this constraint system. + assert_eq!(selectors.len(), self.num_selectors); + + let (polys, selector_replacements): (Vec<_>, Vec<_>) = selectors + .into_iter() + .map(|selector| { + let poly = selector + .iter() + .map(|b| if *b { F::ONE } else { F::ZERO }) + .collect::>(); + let column = self.fixed_column(); + let rotation = Rotation::cur(); + let expr = Expression::Fixed(FixedQuery { + index: Some(self.query_fixed_index(column, rotation)), + column_index: column.index, + rotation, + }); + (poly, expr) + }) + .unzip(); + + self.replace_selectors_with_fixed(&selector_replacements); + self.num_selectors = 0; + + (self, polys) + } + fn replace_selectors_with_fixed(&mut self, selector_replacements: &[Expression]) { fn replace_selectors( expr: &mut Expression, diff --git a/halo2_proofs/examples/serialization.rs b/halo2_proofs/examples/serialization.rs index fc668ff94f..b2433428c3 100644 --- a/halo2_proofs/examples/serialization.rs +++ b/halo2_proofs/examples/serialization.rs @@ -7,8 +7,7 @@ use ff::Field; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, plonk::{ - create_proof, keygen_pk, keygen_vk, pk_read, verify_proof, Advice, Circuit, Column, - ConstraintSystem, ErrorFront, Fixed, Instance, + create_proof, keygen_pk, keygen_vk_custom, pk_read, verify_proof, Advice, Circuit, Column, ConstraintSystem, ErrorFront, Fixed, Instance }, poly::{ kzg::{ @@ -141,7 +140,8 @@ fn main() { let k = 4; let circuit = StandardPlonk(Fr::random(OsRng)); let params = ParamsKZG::::setup(k, OsRng); - let vk = keygen_vk(¶ms, &circuit).expect("vk should not fail"); + let compress_selectors = true; + let vk = keygen_vk_custom(¶ms, &circuit, compress_selectors).expect("vk should not fail"); let pk = keygen_pk(¶ms, vk, &circuit).expect("pk should not fail"); let f = File::create("serialization-test.pk").unwrap(); @@ -152,8 +152,14 @@ fn main() { let f = File::open("serialization-test.pk").unwrap(); let mut reader = BufReader::new(f); #[allow(clippy::unit_arg)] - let pk = pk_read::(&mut reader, SerdeFormat::RawBytes, k, &circuit) - .unwrap(); + let pk = pk_read::( + &mut reader, + SerdeFormat::RawBytes, + k, + &circuit, + compress_selectors, + ) + .unwrap(); std::fs::remove_file("serialization-test.pk").unwrap(); diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index c5c8df0164..2924c54ce4 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -13,9 +13,9 @@ mod verifier { } use halo2_frontend::circuit::compile_circuit; -pub use keygen::{keygen_pk, keygen_vk}; +pub use keygen::{keygen_pk, keygen_vk_custom, keygen_vk}; -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; @@ -46,11 +46,12 @@ pub fn vk_read io::Result> where C::Scalar: SerdePrimeField + FromUniformBytes<64>, { - let (_, _, cs) = compile_circuit(k, circuit) + let (_, _, cs) = compile_circuit(k, circuit, compress_selectors) .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; let cs_mid: ConstraintSystemMid<_> = cs.into(); VerifyingKey::read(reader, format, cs_mid.into()) @@ -73,11 +74,12 @@ pub fn pk_read io::Result> where C::Scalar: SerdePrimeField + FromUniformBytes<64>, { - let (_, _, cs) = compile_circuit(k, circuit) + let (_, _, cs) = compile_circuit(k, circuit, compress_selectors) .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; let cs_mid: ConstraintSystemMid<_> = cs.into(); ProvingKey::read(reader, format, cs_mid.into()) diff --git a/halo2_proofs/src/plonk/keygen.rs b/halo2_proofs/src/plonk/keygen.rs index e45899b829..1895e36f8c 100644 --- a/halo2_proofs/src/plonk/keygen.rs +++ b/halo2_proofs/src/plonk/keygen.rs @@ -24,7 +24,29 @@ where ConcreteCircuit: Circuit, C::Scalar: FromUniformBytes<64>, { - let (compiled_circuit, _, _) = compile_circuit(params.k(), circuit)?; + keygen_vk_custom(params, circuit, true) +} + +/// 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, + compress_selectors: bool, +) -> Result, Error> +where + C: CurveAffine, + P: Params<'params, C>, + ConcreteCircuit: Circuit, + C::Scalar: FromUniformBytes<64>, +{ + let (compiled_circuit, _, _) = compile_circuit(params.k(), circuit, compress_selectors)?; Ok(backend_keygen_vk(params, &compiled_circuit)?) } @@ -44,6 +66,28 @@ where P: Params<'params, C>, ConcreteCircuit: Circuit, { - let (compiled_circuit, _, _) = compile_circuit(params.k(), circuit)?; + 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 c3d39e44ca..c8b03210e5 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -37,29 +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::<_, ConcreteCircuit>(params.k(), &circuits[0])?; - 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 @@ -91,6 +71,60 @@ 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::<_, ConcreteCircuit>(params.k(), &circuits[0], compress_selectors)?; + 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::{ diff --git a/halo2_proofs/tests/frontend_backend_split.rs b/halo2_proofs/tests/frontend_backend_split.rs index 8fa69c54ec..0c58069340 100644 --- a/halo2_proofs/tests/frontend_backend_split.rs +++ b/halo2_proofs/tests/frontend_backend_split.rs @@ -575,7 +575,7 @@ fn test_mycircuit_full_split() { .build(); let k = K; let circuit: MyCircuit = MyCircuit::new(k, 42); - let (compiled_circuit, config, cs) = compile_circuit(k, &circuit).unwrap(); + let (compiled_circuit, config, cs) = compile_circuit(k, &circuit, false).unwrap(); // Setup let mut rng = BlockRng::new(OneNg {}); From f614ddd99f1fc8dc49770c8bf378368c7dcf0722 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Mon, 6 May 2024 23:34:58 +0800 Subject: [PATCH 20/30] chore: roll back some needless update --- halo2_proofs/examples/serialization.rs | 3 ++- halo2_proofs/src/plonk.rs | 2 +- halo2_proofs/src/plonk/prover.rs | 12 ++++++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/halo2_proofs/examples/serialization.rs b/halo2_proofs/examples/serialization.rs index b2433428c3..ffaf90972e 100644 --- a/halo2_proofs/examples/serialization.rs +++ b/halo2_proofs/examples/serialization.rs @@ -7,7 +7,8 @@ use ff::Field; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, plonk::{ - create_proof, keygen_pk, keygen_vk_custom, pk_read, verify_proof, Advice, Circuit, Column, ConstraintSystem, ErrorFront, Fixed, Instance + create_proof, keygen_pk, keygen_vk_custom, pk_read, verify_proof, Advice, Circuit, Column, + ConstraintSystem, ErrorFront, Fixed, Instance, }, poly::{ kzg::{ diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index 2924c54ce4..23c5eb0511 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -13,7 +13,7 @@ mod verifier { } use halo2_frontend::circuit::compile_circuit; -pub use keygen::{keygen_pk, keygen_vk_custom, keygen_vk}; +pub use keygen::{keygen_pk, keygen_pk_custom, keygen_vk, keygen_vk_custom}; pub use prover::{create_proof, create_proof_custom_with_engine, create_proof_with_engine}; pub use verifier::verify_proof; diff --git a/halo2_proofs/src/plonk/prover.rs b/halo2_proofs/src/plonk/prover.rs index c8b03210e5..460f9630fa 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -199,7 +199,7 @@ fn test_create_proof() { fn test_create_proof_custom() { use crate::{ circuit::SimpleFloorPlanner, - plonk::{keygen_pk, keygen_vk, ConstraintSystem, ErrorFront}, + plonk::{keygen_pk_custom, keygen_vk_custom, ConstraintSystem, ErrorFront}, poly::kzg::{ commitment::{KZGCommitmentScheme, ParamsKZG}, multiopen::ProverSHPLONK, @@ -235,15 +235,19 @@ fn test_create_proof_custom() { } let params: ParamsKZG = ParamsKZG::setup(3, OsRng); - let vk = keygen_vk(¶ms, &MyCircuit).expect("keygen_vk_custom should not fail"); - let pk = keygen_pk(¶ms, vk, &MyCircuit).expect("keygen_pk_custom should not fail"); + 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_with_engine::, ProverSHPLONK<_>, _, _, _, _, _>( + create_proof_custom_with_engine::, ProverSHPLONK<_>, _, _, _, _, _>( engine, ¶ms, &pk, + compress_selectors, &[MyCircuit, MyCircuit], &[&[], &[]], OsRng, From 2ef0805369b8205af7f29cd26d6d914cd1ad2eea Mon Sep 17 00:00:00 2001 From: guorong009 Date: Wed, 8 May 2024 13:19:08 +0800 Subject: [PATCH 21/30] test: add integration test for "compress_selectors" --- halo2_proofs/examples/serialization.rs | 9 - halo2_proofs/tests/compress_selectors.rs | 115 +++++ halo2_proofs/tests/frontend_backend_split.rs | 463 +------------------ halo2_proofs/tests/utils.rs | 462 ++++++++++++++++++ 4 files changed, 583 insertions(+), 466 deletions(-) create mode 100644 halo2_proofs/tests/compress_selectors.rs create mode 100644 halo2_proofs/tests/utils.rs diff --git a/halo2_proofs/examples/serialization.rs b/halo2_proofs/examples/serialization.rs index ffaf90972e..824ddc2a7b 100644 --- a/halo2_proofs/examples/serialization.rs +++ b/halo2_proofs/examples/serialization.rs @@ -48,15 +48,6 @@ impl StandardPlonkConfig { [a, b, c].map(|column| meta.enable_equality(column)); - // TEST for "compress_selectors" logic correctness - let s = meta.selector(); - meta.create_gate("selector gate", |meta| { - let s = meta.query_selector(s); - let a = meta.query_advice(a, Rotation::cur()); - let b = meta.query_advice(b, Rotation::cur()); - vec![s * (a - b)] - }); - meta.create_gate( "q_a·a + q_b·b + q_c·c + q_ab·a·b + constant + instance = 0", |meta| { diff --git a/halo2_proofs/tests/compress_selectors.rs b/halo2_proofs/tests/compress_selectors.rs new file mode 100644 index 0000000000..b0ea4ccc5a --- /dev/null +++ b/halo2_proofs/tests/compress_selectors.rs @@ -0,0 +1,115 @@ +// Import the utility module from utils.rs +#[path = "utils.rs"] +mod utils; +use utils::{MyCircuit, OneNg}; + +use halo2_backend::transcript::{ + Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, +}; +use halo2_middleware::zal::impls::{H2cEngine, PlonkEngineConfig}; +use halo2_proofs::plonk::{ + create_proof_custom_with_engine, keygen_pk_custom, keygen_vk_custom, verify_proof, Error, +}; +use halo2_proofs::poly::commitment::ParamsProver; +use halo2_proofs::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; +use halo2_proofs::poly::kzg::multiopen::{ProverSHPLONK, VerifierSHPLONK}; +use halo2_proofs::poly::kzg::strategy::SingleStrategy; +use halo2curves::bn256::{Bn256, Fr, G1Affine}; +use rand_core::block::BlockRng; + +const K: u32 = 6; +const WIDTH_FACTOR: usize = 1; + +fn test_mycircuit( + vk_keygen_compress_selectors: bool, + pk_keygen_compress_selectors: bool, + proofgen_compress_selectors: bool, +) -> Result<(), Error> { + let engine = PlonkEngineConfig::new() + .set_curve::() + .set_msm(H2cEngine::new()) + .build(); + let k = K; + let circuit: MyCircuit = MyCircuit::new(k, 42); + + // Setup + let mut rng = BlockRng::new(OneNg {}); + let params = ParamsKZG::::setup(k, &mut rng); + let verifier_params = params.verifier_params(); + let vk = keygen_vk_custom(¶ms, &circuit, vk_keygen_compress_selectors) + .expect("keygen_vk should not fail"); + let pk = keygen_pk_custom(¶ms, vk.clone(), &circuit, pk_keygen_compress_selectors) + .expect("keygen_pk should not fail"); + + // Proving + let instances = circuit.instances(); + let instances_slice: &[&[Fr]] = &(instances + .iter() + .map(|instance| instance.as_slice()) + .collect::>()); + + let mut transcript = Blake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]); + create_proof_custom_with_engine::< + KZGCommitmentScheme, + ProverSHPLONK<'_, Bn256>, + _, + _, + _, + _, + _, + >( + engine, + ¶ms, + &pk, + proofgen_compress_selectors, + &[circuit], + &[instances_slice], + &mut rng, + &mut transcript, + )?; + let proof = transcript.finalize(); + + // Verify + let mut verifier_transcript = + Blake2bRead::<_, G1Affine, Challenge255<_>>::init(proof.as_slice()); + let strategy = SingleStrategy::new(verifier_params); + + verify_proof::, VerifierSHPLONK<'_, Bn256>, _, _, _>( + ¶ms, + &vk, + strategy, + &[instances_slice], + &mut verifier_transcript, + ) + .map_err(|e| Error::Backend(e)) +} + +#[test] +fn test_mycircuit_compress_selectors() { + match test_mycircuit(true, true, true) { + Ok(_) => println!("Success!"), + Err(_) => panic!("Should succeed!"), + } + match test_mycircuit(false, false, false) { + Ok(_) => println!("Success!"), + Err(_) => panic!("Should succeed!"), + } + + match test_mycircuit(false, true, true) { + Ok(_) => panic!("Should fail!"), + Err(_) => println!("Success!"), + } + match test_mycircuit(true, false, true) { + Ok(_) => panic!("Should fail!"), + Err(_) => println!("Success!"), + } + + match test_mycircuit(false, false, true) { + Ok(_) => panic!("Should fail!"), + Err(_) => println!("Success!"), + } + match test_mycircuit(true, true, false) { + Ok(_) => panic!("Should fail!"), + Err(_) => println!("Success!"), + } +} diff --git a/halo2_proofs/tests/frontend_backend_split.rs b/halo2_proofs/tests/frontend_backend_split.rs index 0c58069340..5d73548bf9 100644 --- a/halo2_proofs/tests/frontend_backend_split.rs +++ b/halo2_proofs/tests/frontend_backend_split.rs @@ -5,6 +5,11 @@ #[global_allocator] static ALLOC: dhat::Alloc = dhat::Alloc; +// Import the utility module from utils.rs +#[path = "utils.rs"] +mod utils; +use utils::{MyCircuit, OneNg}; + use halo2_backend::{ plonk::{ keygen::{keygen_pk, keygen_vk}, @@ -17,476 +22,20 @@ use halo2_backend::{ }; use halo2_frontend::{ circuit::{ - compile_circuit, AssignedCell, Layouter, Region, SimpleFloorPlanner, Value, + compile_circuit, WitnessCalculator, }, dev::MockProver, - plonk::{ - circuit::{Challenge, Column}, - Advice, Circuit, ConstraintSystem, Error as ErrorFront, Expression, FirstPhase, Fixed, - Instance, SecondPhase, Selector, - }, }; -use halo2_middleware::{ff::Field, poly::Rotation}; use halo2_proofs::poly::commitment::ParamsProver; use std::collections::HashMap; -#[derive(Clone)] -struct MyCircuitConfig { - // A gate that uses selector, fixed, advice, has addition, multiplication and rotation - // s_gate[0] * (a[0] + b[0] * c[0] * d[0] - a[1]) - s_gate: Selector, - a: Column, - b: Column, - c: Column, - d: Column, - - // Copy constraints between columns (a, b) and (a, d) - - // A dynamic lookup: s_lookup * [1, a[0], b[0]] in s_ltable * [1, d[0], c[0]] - s_lookup: Column, - s_ltable: Column, - - // A shuffle: s_shufle * [1, a[0]] shuffle_of s_stable * [1, b[0]] - s_shuffle: Column, - s_stable: Column, - - // A FirstPhase challenge and SecondPhase column. We define the following gates: - // s_rlc * (a[0] + challenge * b[0] - e[0]) - // s_rlc * (c[0] + challenge * d[0] - e[0]) - s_rlc: Selector, - e: Column, - challenge: Challenge, - - // Instance with a gate: s_instance * (a[0] - instance[0]) - s_instance: Selector, - instance: Column, -} - -impl MyCircuitConfig { - #[allow(clippy::type_complexity)] - fn assign_gate>( - &self, - region: &mut Region<'_, F>, - offset: &mut usize, - a_assigned: Option>, - abcd: [u64; 4], - ) -> Result<(AssignedCell, [AssignedCell; 4]), ErrorFront> { - let [a, b, c, d] = abcd; - self.s_gate.enable(region, *offset)?; - let a_assigned = if let Some(a_assigned) = a_assigned { - a_assigned - } else { - region.assign_advice(|| "", self.a, *offset, || Value::known(F::from(a)))? - }; - let a = a_assigned.value(); - let [b, c, d] = [b, c, d].map(|v| Value::known(F::from(v))); - let b_assigned = region.assign_advice(|| "", self.b, *offset, || b)?; - let c_assigned = region.assign_advice(|| "", self.c, *offset, || c)?; - let d_assigned = region.assign_fixed(|| "", self.d, *offset, || d)?; - *offset += 1; - // let res = a + b * c * d; - let res = a - .zip(b.zip(c.zip(d))) - .map(|(a, (b, (c, d)))| *a + b * c * d); - let res_assigned = region.assign_advice(|| "", self.a, *offset, || res)?; - Ok(( - res_assigned, - [a_assigned, b_assigned, c_assigned, d_assigned], - )) - } -} - -#[derive(Clone)] -struct MyCircuit { - k: u32, - input: u64, - _marker: std::marker::PhantomData, -} - -impl, const WIDTH_FACTOR: usize> MyCircuit { - fn new(k: u32, input: u64) -> Self { - Self { - k, - input, - _marker: std::marker::PhantomData {}, - } - } - - fn instance(&self) -> Vec { - let mut instance = Vec::new(); - let res = F::from(self.input); - instance.push(res); - let (b, c, d) = (3, 4, 1); - let res = res + F::from(b) * F::from(c) * F::from(d); - instance.push(res); - let (b, c, d) = (6, 7, 1); - let res = res + F::from(b) * F::from(c) * F::from(d); - instance.push(res); - let (b, c, d) = (8, 9, 1); - let res = res + F::from(b) * F::from(c) * F::from(d); - instance.push(res); - instance.push(F::from(2)); - instance.push(F::from(2)); - instance - } - - fn instances(&self) -> Vec> { - let instance = self.instance(); - (0..WIDTH_FACTOR).map(|_| instance.clone()).collect() - } - - fn configure_single(meta: &mut ConstraintSystem, id: usize) -> MyCircuitConfig { - let s_gate = meta.selector(); - let a = meta.advice_column(); - let b = meta.advice_column(); - let c = meta.advice_column(); - let d = meta.fixed_column(); - - meta.enable_equality(a); - meta.enable_equality(b); - meta.enable_equality(d); - - let s_lookup = meta.fixed_column(); - let s_ltable = meta.fixed_column(); - - let s_shuffle = meta.fixed_column(); - let s_stable = meta.fixed_column(); - - let s_rlc = meta.selector(); - let e = meta.advice_column_in(SecondPhase); - let challenge = meta.challenge_usable_after(FirstPhase); - - let s_instance = meta.selector(); - let instance = meta.instance_column(); - meta.enable_equality(instance); - - let one = Expression::Constant(F::ONE); - - meta.create_gate(format!("gate_a.{id}"), |meta| { - let s_gate = meta.query_selector(s_gate); - let b = meta.query_advice(b, Rotation::cur()); - let a1 = meta.query_advice(a, Rotation::next()); - let a0 = meta.query_advice(a, Rotation::cur()); - let c = meta.query_advice(c, Rotation::cur()); - let d = meta.query_fixed(d, Rotation::cur()); - - vec![s_gate * (a0 + b * c * d - a1)] - }); - - meta.lookup_any(format!("lookup.{id}"), |meta| { - let s_lookup = meta.query_fixed(s_lookup, Rotation::cur()); - let s_ltable = meta.query_fixed(s_ltable, Rotation::cur()); - let a = meta.query_advice(a, Rotation::cur()); - let b = meta.query_advice(b, Rotation::cur()); - let c = meta.query_advice(c, Rotation::cur()); - let d = meta.query_fixed(d, Rotation::cur()); - let lhs = [one.clone(), a, b].map(|c| c * s_lookup.clone()); - let rhs = [one.clone(), d, c].map(|c| c * s_ltable.clone()); - lhs.into_iter().zip(rhs).collect() - }); - - meta.shuffle(format!("shuffle.{id}"), |meta| { - let s_shuffle = meta.query_fixed(s_shuffle, Rotation::cur()); - let s_stable = meta.query_fixed(s_stable, Rotation::cur()); - let a = meta.query_advice(a, Rotation::cur()); - let b = meta.query_advice(b, Rotation::cur()); - let lhs = [one.clone(), a].map(|c| c * s_shuffle.clone()); - let rhs = [one.clone(), b].map(|c| c * s_stable.clone()); - lhs.into_iter().zip(rhs).collect() - }); - - meta.create_gate(format!("gate_rlc.{id}"), |meta| { - let s_rlc = meta.query_selector(s_rlc); - let a = meta.query_advice(a, Rotation::cur()); - let b = meta.query_advice(b, Rotation::cur()); - let c = meta.query_advice(c, Rotation::cur()); - let d = meta.query_fixed(d, Rotation::cur()); - let e = meta.query_advice(e, Rotation::cur()); - let challenge = meta.query_challenge(challenge); - - vec![ - s_rlc.clone() * (a + challenge.clone() * b - e.clone()), - s_rlc * (c + challenge * d - e), - ] - }); - - MyCircuitConfig { - s_gate, - a, - b, - c, - d, - s_lookup, - s_ltable, - s_rlc, - e, - challenge, - s_shuffle, - s_stable, - s_instance, - instance, - } - } - - fn synthesize_unit( - &self, - config: &MyCircuitConfig, - layouter: &mut impl Layouter, - id: usize, - unit_id: usize, - ) -> Result<(usize, Vec>), ErrorFront> { - let challenge = layouter.get_challenge(config.challenge); - let (rows, instance_copy) = layouter.assign_region( - || format!("unit.{id}-{unit_id}"), - |mut region| { - // Column annotations - region.name_column(|| format!("a.{id}"), config.a); - region.name_column(|| format!("b.{id}"), config.b); - region.name_column(|| format!("c.{id}"), config.c); - region.name_column(|| format!("d.{id}"), config.d); - region.name_column(|| format!("e.{id}"), config.e); - region.name_column(|| format!("instance.{id}"), config.instance); - region.name_column(|| format!("s_lookup.{id}"), config.s_lookup); - region.name_column(|| format!("s_ltable.{id}"), config.s_ltable); - region.name_column(|| format!("s_shuffle.{id}"), config.s_shuffle); - region.name_column(|| format!("s_stable.{id}"), config.s_stable); - - let mut offset = 0; - let mut instance_copy = Vec::new(); - // First "a" value comes from instance - config.s_instance.enable(&mut region, offset).expect("todo"); - let res = region - .assign_advice_from_instance(|| "", config.instance, 0, config.a, offset) - .expect("todo"); - // Enable the gate on a few consecutive rows with rotations - let (res, _) = config - .assign_gate(&mut region, &mut offset, Some(res), [0, 3, 4, 1]) - .expect("todo"); - instance_copy.push(res.clone()); - let (res, _) = config - .assign_gate(&mut region, &mut offset, Some(res), [0, 6, 7, 1]) - .expect("todo"); - instance_copy.push(res.clone()); - let (res, _) = config - .assign_gate(&mut region, &mut offset, Some(res), [0, 8, 9, 1]) - .expect("todo"); - instance_copy.push(res.clone()); - let (res, _) = config - .assign_gate( - &mut region, - &mut offset, - Some(res), - [0, 0xffffffff, 0xdeadbeef, 1], - ) - .expect("todo"); - let _ = config - .assign_gate( - &mut region, - &mut offset, - Some(res), - [0, 0xabad1d3a, 0x12345678, 0x42424242], - ) - .expect("todo"); - offset += 1; - - // Enable the gate on non-consecutive rows with advice-advice copy constraints enabled - let (_, abcd1) = config - .assign_gate(&mut region, &mut offset, None, [5, 2, 1, 1]) - .expect("todo"); - offset += 1; - let (_, abcd2) = config - .assign_gate(&mut region, &mut offset, None, [2, 3, 1, 1]) - .expect("todo"); - offset += 1; - let (_, abcd3) = config - .assign_gate(&mut region, &mut offset, None, [4, 2, 1, 1]) - .expect("todo"); - offset += 1; - region - .constrain_equal(abcd1[1].cell(), abcd2[0].cell()) - .expect("todo"); - region - .constrain_equal(abcd2[0].cell(), abcd3[1].cell()) - .expect("todo"); - instance_copy.push(abcd1[1].clone()); - instance_copy.push(abcd2[0].clone()); - - // Enable the gate on non-consecutive rows with advice-fixed copy constraints enabled - let (_, abcd1) = config - .assign_gate(&mut region, &mut offset, None, [5, 9, 1, 9]) - .expect("todo"); - offset += 1; - let (_, abcd2) = config - .assign_gate(&mut region, &mut offset, None, [2, 9, 1, 1]) - .expect("todo"); - offset += 1; - let (_, abcd3) = config - .assign_gate(&mut region, &mut offset, None, [9, 2, 1, 1]) - .expect("todo"); - offset += 1; - region - .constrain_equal(abcd1[1].cell(), abcd1[3].cell()) - .expect("todo"); - region - .constrain_equal(abcd2[1].cell(), abcd1[3].cell()) - .expect("todo"); - region - .constrain_equal(abcd3[0].cell(), abcd1[3].cell()) - .expect("todo"); - - // Enable a dynamic lookup (powers of two) - let table: Vec<_> = (0u64..=10).map(|exp| (exp, 2u64.pow(exp as u32))).collect(); - let lookups = [(2, 4), (2, 4), (10, 1024), (0, 1), (2, 4)]; - for (table_row, lookup_row) in table - .iter() - .zip(lookups.iter().chain(std::iter::repeat(&(0, 1)))) - { - region - .assign_fixed(|| "", config.s_lookup, offset, || Value::known(F::ONE)) - .expect("todo"); - region - .assign_fixed(|| "", config.s_ltable, offset, || Value::known(F::ONE)) - .expect("todo"); - let lookup_row0 = Value::known(F::from(lookup_row.0)); - let lookup_row1 = Value::known(F::from(lookup_row.1)); - region - .assign_advice(|| "", config.a, offset, || lookup_row0) - .expect("todo"); - region - .assign_advice(|| "", config.b, offset, || lookup_row1) - .expect("todo"); - let table_row0 = Value::known(F::from(table_row.0)); - let table_row1 = Value::known(F::from(table_row.1)); - region - .assign_fixed(|| "", config.d, offset, || table_row0) - .expect("todo"); - region - .assign_advice(|| "", config.c, offset, || table_row1) - .expect("todo"); - offset += 1; - } - - // Enable RLC gate 3 times - for abcd in [[3, 5, 3, 5], [8, 9, 8, 9], [111, 222, 111, 222]] { - config.s_rlc.enable(&mut region, offset)?; - let (_, _) = config - .assign_gate(&mut region, &mut offset, None, abcd) - .expect("todo"); - let rlc = challenge.map(|ch| { - let [a, b, ..] = abcd; - F::from(a) + ch * F::from(b) - }); - region - .assign_advice(|| "", config.e, offset - 1, || rlc) - .expect("todo"); - offset += 1; - } - - // Enable a dynamic shuffle (sequence from 0 to 15) - let table: Vec<_> = (0u64..16).collect(); - let shuffle = [0u64, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15]; - assert_eq!(table.len(), shuffle.len()); - - for (table_row, shuffle_row) in table.iter().zip(shuffle.iter()) { - region - .assign_fixed(|| "", config.s_shuffle, offset, || Value::known(F::ONE)) - .expect("todo"); - region - .assign_fixed(|| "", config.s_stable, offset, || Value::known(F::ONE)) - .expect("todo"); - let shuffle_row0 = Value::known(F::from(*shuffle_row)); - region - .assign_advice(|| "", config.a, offset, || shuffle_row0) - .expect("todo"); - let table_row0 = Value::known(F::from(*table_row)); - region - .assign_advice(|| "", config.b, offset, || table_row0) - .expect("todo"); - offset += 1; - } - - Ok((offset, instance_copy)) - }, - )?; - - Ok((rows, instance_copy)) - } -} - -impl, const WIDTH_FACTOR: usize> Circuit for MyCircuit { - type Config = Vec; - type FloorPlanner = SimpleFloorPlanner; - #[cfg(feature = "circuit-params")] - type Params = (); - - fn without_witnesses(&self) -> Self { - self.clone() - } - - fn configure(meta: &mut ConstraintSystem) -> Vec { - assert!(WIDTH_FACTOR > 0); - (0..WIDTH_FACTOR) - .map(|id| Self::configure_single(meta, id)) - .collect() - } - - fn synthesize( - &self, - config: Vec, - mut layouter: impl Layouter, - ) -> Result<(), ErrorFront> { - // - 2 queries from first gate - // - 3 for permutation argument - // - 1 for multipoen - // - 1 for the last row of grand product poly to check that the product result is 1 - // - 1 for off-by-one errors - let unusable_rows = 2 + 3 + 1 + 1 + 1; - let max_rows = 2usize.pow(self.k) - unusable_rows; - for (id, config) in config.iter().enumerate() { - let mut total_rows = 0; - let mut unit_id = 0; - loop { - let (rows, instance_copy) = self - .synthesize_unit(config, &mut layouter, id, unit_id) - .expect("todo"); - if total_rows == 0 { - for (i, instance) in instance_copy.iter().enumerate() { - layouter.constrain_instance(instance.cell(), config.instance, 1 + i)?; - } - } - total_rows += rows; - if total_rows + rows > max_rows { - break; - } - unit_id += 1; - } - assert!(total_rows <= max_rows); - } - Ok(()) - } -} use halo2_proofs::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; use halo2_proofs::poly::kzg::multiopen::{ProverSHPLONK, VerifierSHPLONK}; use halo2_proofs::poly::kzg::strategy::SingleStrategy; use halo2curves::bn256::{Bn256, Fr, G1Affine}; use rand_core::block::BlockRng; -use rand_core::block::BlockRngCore; - -// One number generator, that can be used as a deterministic Rng, outputing fixed values. -struct OneNg {} - -impl BlockRngCore for OneNg { - type Item = u32; - type Results = [u32; 16]; - - fn generate(&mut self, results: &mut Self::Results) { - for elem in results.iter_mut() { - *elem = 1; - } - } -} #[test] fn test_mycircuit_mock() { diff --git a/halo2_proofs/tests/utils.rs b/halo2_proofs/tests/utils.rs new file mode 100644 index 0000000000..c70a756cf1 --- /dev/null +++ b/halo2_proofs/tests/utils.rs @@ -0,0 +1,462 @@ +use halo2_frontend::{ + circuit::{ + AssignedCell, Layouter, Region, SimpleFloorPlanner, Value, + }, + plonk::{ + circuit::{Challenge, Column}, + Advice, Circuit, ConstraintSystem, Error as ErrorFront, Expression, FirstPhase, Fixed, + Instance, SecondPhase, Selector, + }, +}; +use halo2_middleware::{ff::Field, poly::Rotation}; +use rand_core::block::BlockRngCore; + +#[derive(Clone)] +pub struct MyCircuitConfig { + // A gate that uses selector, fixed, advice, has addition, multiplication and rotation + // s_gate[0] * (a[0] + b[0] * c[0] * d[0] - a[1]) + s_gate: Selector, + a: Column, + b: Column, + c: Column, + d: Column, + + // Copy constraints between columns (a, b) and (a, d) + + // A dynamic lookup: s_lookup * [1, a[0], b[0]] in s_ltable * [1, d[0], c[0]] + s_lookup: Column, + s_ltable: Column, + + // A shuffle: s_shufle * [1, a[0]] shuffle_of s_stable * [1, b[0]] + s_shuffle: Column, + s_stable: Column, + + // A FirstPhase challenge and SecondPhase column. We define the following gates: + // s_rlc * (a[0] + challenge * b[0] - e[0]) + // s_rlc * (c[0] + challenge * d[0] - e[0]) + s_rlc: Selector, + e: Column, + challenge: Challenge, + + // Instance with a gate: s_instance * (a[0] - instance[0]) + s_instance: Selector, + instance: Column, +} + +impl MyCircuitConfig { + #[allow(clippy::type_complexity)] + fn assign_gate>( + &self, + region: &mut Region<'_, F>, + offset: &mut usize, + a_assigned: Option>, + abcd: [u64; 4], + ) -> Result<(AssignedCell, [AssignedCell; 4]), ErrorFront> { + let [a, b, c, d] = abcd; + self.s_gate.enable(region, *offset)?; + let a_assigned = if let Some(a_assigned) = a_assigned { + a_assigned + } else { + region.assign_advice(|| "", self.a, *offset, || Value::known(F::from(a)))? + }; + let a = a_assigned.value(); + let [b, c, d] = [b, c, d].map(|v| Value::known(F::from(v))); + let b_assigned = region.assign_advice(|| "", self.b, *offset, || b)?; + let c_assigned = region.assign_advice(|| "", self.c, *offset, || c)?; + let d_assigned = region.assign_fixed(|| "", self.d, *offset, || d)?; + *offset += 1; + // let res = a + b * c * d; + let res = a + .zip(b.zip(c.zip(d))) + .map(|(a, (b, (c, d)))| *a + b * c * d); + let res_assigned = region.assign_advice(|| "", self.a, *offset, || res)?; + Ok(( + res_assigned, + [a_assigned, b_assigned, c_assigned, d_assigned], + )) + } +} + +#[derive(Clone)] +pub struct MyCircuit { + k: u32, + input: u64, + _marker: std::marker::PhantomData, +} + +impl, const WIDTH_FACTOR: usize> MyCircuit { + pub fn new(k: u32, input: u64) -> Self { + Self { + k, + input, + _marker: std::marker::PhantomData {}, + } + } + + fn instance(&self) -> Vec { + let mut instance = Vec::new(); + let res = F::from(self.input); + instance.push(res); + let (b, c, d) = (3, 4, 1); + let res = res + F::from(b) * F::from(c) * F::from(d); + instance.push(res); + let (b, c, d) = (6, 7, 1); + let res = res + F::from(b) * F::from(c) * F::from(d); + instance.push(res); + let (b, c, d) = (8, 9, 1); + let res = res + F::from(b) * F::from(c) * F::from(d); + instance.push(res); + instance.push(F::from(2)); + instance.push(F::from(2)); + instance + } + + pub fn instances(&self) -> Vec> { + let instance = self.instance(); + (0..WIDTH_FACTOR).map(|_| instance.clone()).collect() + } + + fn configure_single(meta: &mut ConstraintSystem, id: usize) -> MyCircuitConfig { + let s_gate = meta.selector(); + let a = meta.advice_column(); + let b = meta.advice_column(); + let c = meta.advice_column(); + let d = meta.fixed_column(); + + meta.enable_equality(a); + meta.enable_equality(b); + meta.enable_equality(d); + + let s_lookup = meta.fixed_column(); + let s_ltable = meta.fixed_column(); + + let s_shuffle = meta.fixed_column(); + let s_stable = meta.fixed_column(); + + let s_rlc = meta.selector(); + let e = meta.advice_column_in(SecondPhase); + let challenge = meta.challenge_usable_after(FirstPhase); + + let s_instance = meta.selector(); + let instance = meta.instance_column(); + meta.enable_equality(instance); + + let one = Expression::Constant(F::ONE); + + meta.create_gate(format!("gate_a.{id}"), |meta| { + let s_gate = meta.query_selector(s_gate); + let b = meta.query_advice(b, Rotation::cur()); + let a1 = meta.query_advice(a, Rotation::next()); + let a0 = meta.query_advice(a, Rotation::cur()); + let c = meta.query_advice(c, Rotation::cur()); + let d = meta.query_fixed(d, Rotation::cur()); + + vec![s_gate * (a0 + b * c * d - a1)] + }); + + meta.lookup_any(format!("lookup.{id}"), |meta| { + let s_lookup = meta.query_fixed(s_lookup, Rotation::cur()); + let s_ltable = meta.query_fixed(s_ltable, Rotation::cur()); + let a = meta.query_advice(a, Rotation::cur()); + let b = meta.query_advice(b, Rotation::cur()); + let c = meta.query_advice(c, Rotation::cur()); + let d = meta.query_fixed(d, Rotation::cur()); + let lhs = [one.clone(), a, b].map(|c| c * s_lookup.clone()); + let rhs = [one.clone(), d, c].map(|c| c * s_ltable.clone()); + lhs.into_iter().zip(rhs).collect() + }); + + meta.shuffle(format!("shuffle.{id}"), |meta| { + let s_shuffle = meta.query_fixed(s_shuffle, Rotation::cur()); + let s_stable = meta.query_fixed(s_stable, Rotation::cur()); + let a = meta.query_advice(a, Rotation::cur()); + let b = meta.query_advice(b, Rotation::cur()); + let lhs = [one.clone(), a].map(|c| c * s_shuffle.clone()); + let rhs = [one.clone(), b].map(|c| c * s_stable.clone()); + lhs.into_iter().zip(rhs).collect() + }); + + meta.create_gate(format!("gate_rlc.{id}"), |meta| { + let s_rlc = meta.query_selector(s_rlc); + let a = meta.query_advice(a, Rotation::cur()); + let b = meta.query_advice(b, Rotation::cur()); + let c = meta.query_advice(c, Rotation::cur()); + let d = meta.query_fixed(d, Rotation::cur()); + let e = meta.query_advice(e, Rotation::cur()); + let challenge = meta.query_challenge(challenge); + + vec![ + s_rlc.clone() * (a + challenge.clone() * b - e.clone()), + s_rlc * (c + challenge * d - e), + ] + }); + + MyCircuitConfig { + s_gate, + a, + b, + c, + d, + s_lookup, + s_ltable, + s_rlc, + e, + challenge, + s_shuffle, + s_stable, + s_instance, + instance, + } + } + + fn synthesize_unit( + &self, + config: &MyCircuitConfig, + layouter: &mut impl Layouter, + id: usize, + unit_id: usize, + ) -> Result<(usize, Vec>), ErrorFront> { + let challenge = layouter.get_challenge(config.challenge); + let (rows, instance_copy) = layouter.assign_region( + || format!("unit.{id}-{unit_id}"), + |mut region| { + // Column annotations + region.name_column(|| format!("a.{id}"), config.a); + region.name_column(|| format!("b.{id}"), config.b); + region.name_column(|| format!("c.{id}"), config.c); + region.name_column(|| format!("d.{id}"), config.d); + region.name_column(|| format!("e.{id}"), config.e); + region.name_column(|| format!("instance.{id}"), config.instance); + region.name_column(|| format!("s_lookup.{id}"), config.s_lookup); + region.name_column(|| format!("s_ltable.{id}"), config.s_ltable); + region.name_column(|| format!("s_shuffle.{id}"), config.s_shuffle); + region.name_column(|| format!("s_stable.{id}"), config.s_stable); + + let mut offset = 0; + let mut instance_copy = Vec::new(); + // First "a" value comes from instance + config.s_instance.enable(&mut region, offset).expect("todo"); + let res = region + .assign_advice_from_instance(|| "", config.instance, 0, config.a, offset) + .expect("todo"); + // Enable the gate on a few consecutive rows with rotations + let (res, _) = config + .assign_gate(&mut region, &mut offset, Some(res), [0, 3, 4, 1]) + .expect("todo"); + instance_copy.push(res.clone()); + let (res, _) = config + .assign_gate(&mut region, &mut offset, Some(res), [0, 6, 7, 1]) + .expect("todo"); + instance_copy.push(res.clone()); + let (res, _) = config + .assign_gate(&mut region, &mut offset, Some(res), [0, 8, 9, 1]) + .expect("todo"); + instance_copy.push(res.clone()); + let (res, _) = config + .assign_gate( + &mut region, + &mut offset, + Some(res), + [0, 0xffffffff, 0xdeadbeef, 1], + ) + .expect("todo"); + let _ = config + .assign_gate( + &mut region, + &mut offset, + Some(res), + [0, 0xabad1d3a, 0x12345678, 0x42424242], + ) + .expect("todo"); + offset += 1; + + // Enable the gate on non-consecutive rows with advice-advice copy constraints enabled + let (_, abcd1) = config + .assign_gate(&mut region, &mut offset, None, [5, 2, 1, 1]) + .expect("todo"); + offset += 1; + let (_, abcd2) = config + .assign_gate(&mut region, &mut offset, None, [2, 3, 1, 1]) + .expect("todo"); + offset += 1; + let (_, abcd3) = config + .assign_gate(&mut region, &mut offset, None, [4, 2, 1, 1]) + .expect("todo"); + offset += 1; + region + .constrain_equal(abcd1[1].cell(), abcd2[0].cell()) + .expect("todo"); + region + .constrain_equal(abcd2[0].cell(), abcd3[1].cell()) + .expect("todo"); + instance_copy.push(abcd1[1].clone()); + instance_copy.push(abcd2[0].clone()); + + // Enable the gate on non-consecutive rows with advice-fixed copy constraints enabled + let (_, abcd1) = config + .assign_gate(&mut region, &mut offset, None, [5, 9, 1, 9]) + .expect("todo"); + offset += 1; + let (_, abcd2) = config + .assign_gate(&mut region, &mut offset, None, [2, 9, 1, 1]) + .expect("todo"); + offset += 1; + let (_, abcd3) = config + .assign_gate(&mut region, &mut offset, None, [9, 2, 1, 1]) + .expect("todo"); + offset += 1; + region + .constrain_equal(abcd1[1].cell(), abcd1[3].cell()) + .expect("todo"); + region + .constrain_equal(abcd2[1].cell(), abcd1[3].cell()) + .expect("todo"); + region + .constrain_equal(abcd3[0].cell(), abcd1[3].cell()) + .expect("todo"); + + // Enable a dynamic lookup (powers of two) + let table: Vec<_> = (0u64..=10).map(|exp| (exp, 2u64.pow(exp as u32))).collect(); + let lookups = [(2, 4), (2, 4), (10, 1024), (0, 1), (2, 4)]; + for (table_row, lookup_row) in table + .iter() + .zip(lookups.iter().chain(std::iter::repeat(&(0, 1)))) + { + region + .assign_fixed(|| "", config.s_lookup, offset, || Value::known(F::ONE)) + .expect("todo"); + region + .assign_fixed(|| "", config.s_ltable, offset, || Value::known(F::ONE)) + .expect("todo"); + let lookup_row0 = Value::known(F::from(lookup_row.0)); + let lookup_row1 = Value::known(F::from(lookup_row.1)); + region + .assign_advice(|| "", config.a, offset, || lookup_row0) + .expect("todo"); + region + .assign_advice(|| "", config.b, offset, || lookup_row1) + .expect("todo"); + let table_row0 = Value::known(F::from(table_row.0)); + let table_row1 = Value::known(F::from(table_row.1)); + region + .assign_fixed(|| "", config.d, offset, || table_row0) + .expect("todo"); + region + .assign_advice(|| "", config.c, offset, || table_row1) + .expect("todo"); + offset += 1; + } + + // Enable RLC gate 3 times + for abcd in [[3, 5, 3, 5], [8, 9, 8, 9], [111, 222, 111, 222]] { + config.s_rlc.enable(&mut region, offset)?; + let (_, _) = config + .assign_gate(&mut region, &mut offset, None, abcd) + .expect("todo"); + let rlc = challenge.map(|ch| { + let [a, b, ..] = abcd; + F::from(a) + ch * F::from(b) + }); + region + .assign_advice(|| "", config.e, offset - 1, || rlc) + .expect("todo"); + offset += 1; + } + + // Enable a dynamic shuffle (sequence from 0 to 15) + let table: Vec<_> = (0u64..16).collect(); + let shuffle = [0u64, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15]; + assert_eq!(table.len(), shuffle.len()); + + for (table_row, shuffle_row) in table.iter().zip(shuffle.iter()) { + region + .assign_fixed(|| "", config.s_shuffle, offset, || Value::known(F::ONE)) + .expect("todo"); + region + .assign_fixed(|| "", config.s_stable, offset, || Value::known(F::ONE)) + .expect("todo"); + let shuffle_row0 = Value::known(F::from(*shuffle_row)); + region + .assign_advice(|| "", config.a, offset, || shuffle_row0) + .expect("todo"); + let table_row0 = Value::known(F::from(*table_row)); + region + .assign_advice(|| "", config.b, offset, || table_row0) + .expect("todo"); + offset += 1; + } + + Ok((offset, instance_copy)) + }, + )?; + + Ok((rows, instance_copy)) + } +} + +impl, const WIDTH_FACTOR: usize> Circuit for MyCircuit { + type Config = Vec; + type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); + + fn without_witnesses(&self) -> Self { + self.clone() + } + + fn configure(meta: &mut ConstraintSystem) -> Vec { + assert!(WIDTH_FACTOR > 0); + (0..WIDTH_FACTOR) + .map(|id| Self::configure_single(meta, id)) + .collect() + } + + fn synthesize( + &self, + config: Vec, + mut layouter: impl Layouter, + ) -> Result<(), ErrorFront> { + // - 2 queries from first gate + // - 3 for permutation argument + // - 1 for multipoen + // - 1 for the last row of grand product poly to check that the product result is 1 + // - 1 for off-by-one errors + let unusable_rows = 2 + 3 + 1 + 1 + 1; + let max_rows = 2usize.pow(self.k) - unusable_rows; + for (id, config) in config.iter().enumerate() { + let mut total_rows = 0; + let mut unit_id = 0; + loop { + let (rows, instance_copy) = self + .synthesize_unit(config, &mut layouter, id, unit_id) + .expect("todo"); + if total_rows == 0 { + for (i, instance) in instance_copy.iter().enumerate() { + layouter.constrain_instance(instance.cell(), config.instance, 1 + i)?; + } + } + total_rows += rows; + if total_rows + rows > max_rows { + break; + } + unit_id += 1; + } + assert!(total_rows <= max_rows); + } + Ok(()) + } +} + +// One number generator, that can be used as a deterministic Rng, outputing fixed values. +pub struct OneNg {} + +impl BlockRngCore for OneNg { + type Item = u32; + type Results = [u32; 16]; + + fn generate(&mut self, results: &mut Self::Results) { + for elem in results.iter_mut() { + *elem = 1; + } + } +} From 6ad6b4b054d4656c3270af86d9e216dafff60eb0 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Wed, 8 May 2024 14:08:16 +0800 Subject: [PATCH 22/30] chore: fmt + clippy --- halo2_proofs/tests/frontend_backend_split.rs | 6 +----- halo2_proofs/tests/utils.rs | 4 +--- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/halo2_proofs/tests/frontend_backend_split.rs b/halo2_proofs/tests/frontend_backend_split.rs index 5d73548bf9..a130dee8b1 100644 --- a/halo2_proofs/tests/frontend_backend_split.rs +++ b/halo2_proofs/tests/frontend_backend_split.rs @@ -21,16 +21,12 @@ use halo2_backend::{ }, }; use halo2_frontend::{ - circuit::{ - compile_circuit, - WitnessCalculator, - }, + circuit::{compile_circuit, WitnessCalculator}, dev::MockProver, }; use halo2_proofs::poly::commitment::ParamsProver; use std::collections::HashMap; - use halo2_proofs::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; use halo2_proofs::poly::kzg::multiopen::{ProverSHPLONK, VerifierSHPLONK}; use halo2_proofs::poly::kzg::strategy::SingleStrategy; diff --git a/halo2_proofs/tests/utils.rs b/halo2_proofs/tests/utils.rs index c70a756cf1..880602ae67 100644 --- a/halo2_proofs/tests/utils.rs +++ b/halo2_proofs/tests/utils.rs @@ -1,7 +1,5 @@ use halo2_frontend::{ - circuit::{ - AssignedCell, Layouter, Region, SimpleFloorPlanner, Value, - }, + circuit::{AssignedCell, Layouter, Region, SimpleFloorPlanner, Value}, plonk::{ circuit::{Challenge, Column}, Advice, Circuit, ConstraintSystem, Error as ErrorFront, Expression, FirstPhase, Fixed, From df25fee9f07068b69d0d19ae5d67af3f2e183be0 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Wed, 8 May 2024 23:21:29 +0800 Subject: [PATCH 23/30] test: use the custom circuit for integration test --- halo2_proofs/tests/compress_selectors.rs | 400 ++++++++++++++-- halo2_proofs/tests/frontend_backend_split.rs | 467 ++++++++++++++++++- halo2_proofs/tests/utils.rs | 460 ------------------ 3 files changed, 825 insertions(+), 502 deletions(-) delete mode 100644 halo2_proofs/tests/utils.rs diff --git a/halo2_proofs/tests/compress_selectors.rs b/halo2_proofs/tests/compress_selectors.rs index b0ea4ccc5a..c68dec1106 100644 --- a/halo2_proofs/tests/compress_selectors.rs +++ b/halo2_proofs/tests/compress_selectors.rs @@ -1,14 +1,20 @@ -// Import the utility module from utils.rs -#[path = "utils.rs"] -mod utils; -use utils::{MyCircuit, OneNg}; +#![allow(non_snake_case)] + +use std::marker::PhantomData; + +use ff::PrimeField; +use halo2_frontend::plonk::Error; +use halo2_proofs::circuit::{Cell, Layouter, SimpleFloorPlanner, Value}; +use halo2_proofs::poly::Rotation; use halo2_backend::transcript::{ Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, }; use halo2_middleware::zal::impls::{H2cEngine, PlonkEngineConfig}; +use halo2_proofs::arithmetic::Field; use halo2_proofs::plonk::{ - create_proof_custom_with_engine, keygen_pk_custom, keygen_vk_custom, verify_proof, Error, + create_proof_custom_with_engine, keygen_pk_custom, keygen_vk_custom, verify_proof, Advice, + Assigned, Circuit, Column, ConstraintSystem, Instance, Selector, }; use halo2_proofs::poly::commitment::ParamsProver; use halo2_proofs::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; @@ -16,21 +22,338 @@ use halo2_proofs::poly::kzg::multiopen::{ProverSHPLONK, VerifierSHPLONK}; use halo2_proofs::poly::kzg::strategy::SingleStrategy; use halo2curves::bn256::{Bn256, Fr, G1Affine}; use rand_core::block::BlockRng; +use rand_core::block::BlockRngCore; + +// One number generator, that can be used as a deterministic Rng, outputing fixed values. +pub struct OneNg {} + +impl BlockRngCore for OneNg { + type Item = u32; + type Results = [u32; 16]; + + fn generate(&mut self, results: &mut Self::Results) { + for elem in results.iter_mut() { + *elem = 1; + } + } +} + +#[derive(Debug, Clone)] +struct MyCircuitConfig { + l: Column, + r: Column, + o: Column, + + s_add: Selector, + s_mul: Selector, + s_cubed: Selector, + + PI: Column, +} + +#[derive(Debug)] +struct MyCircuitChip { + config: MyCircuitConfig, + marker: PhantomData, +} + +trait MyCircuitComposer { + fn raw_multiply( + &self, + layouter: &mut impl Layouter, + f: FM, + ) -> Result<(Cell, Cell, Cell), Error> + where + FM: FnMut() -> Value<(Assigned, Assigned, Assigned)>; + + fn raw_add( + &self, + layouter: &mut impl Layouter, + f: FM, + ) -> Result<(Cell, Cell, Cell), Error> + where + FM: FnMut() -> Value<(Assigned, Assigned, Assigned)>; + + fn copy(&self, layouter: &mut impl Layouter, a: Cell, b: Cell) -> Result<(), Error>; + + fn expose_public( + &self, + layouter: &mut impl Layouter, + cell: Cell, + row: usize, + ) -> Result<(), Error>; + + fn cube(&self, layouter: &mut impl Layouter, f: FM) -> Result<(Cell, Cell), Error> + where + FM: FnMut() -> Value<(Assigned, Assigned)>; +} + +impl MyCircuitChip { + fn construct(config: MyCircuitConfig) -> Self { + Self { + config, + marker: PhantomData, + } + } + + fn configure(meta: &mut ConstraintSystem) -> MyCircuitConfig { + let l = meta.advice_column(); + let r = meta.advice_column(); + let o = meta.advice_column(); + + let s_add = meta.selector(); + let s_mul = meta.selector(); + let s_cubed = meta.selector(); + + let PI = meta.instance_column(); + + meta.enable_equality(l); + meta.enable_equality(r); + meta.enable_equality(o); + + meta.enable_equality(PI); + + meta.create_gate("add", |meta| { + let l = meta.query_advice(l, Rotation::cur()); + let r = meta.query_advice(r, Rotation::cur()); + let o = meta.query_advice(o, Rotation::cur()); + + let s_add = meta.query_selector(s_add); + + vec![s_add * (l + r - o)] + }); + + meta.create_gate("mul", |meta| { + let l = meta.query_advice(l, Rotation::cur()); + let r = meta.query_advice(r, Rotation::cur()); + let o = meta.query_advice(o, Rotation::cur()); + + let s_mul = meta.query_selector(s_mul); + + vec![s_mul * (l * r - o)] + }); + + // NOTE: This gate is placement for "compress_selectors" logic testing. Not really used. + meta.create_gate("cubed", |meta| { + let l = meta.query_advice(l, Rotation::cur()); + let o = meta.query_advice(o, Rotation::cur()); + + let s_cubed = meta.query_selector(s_cubed); + + vec![s_cubed * (l.clone() * l.clone() * l - o)] + }); + + MyCircuitConfig { + l, + r, + o, + s_add, + s_mul, + s_cubed, + PI, + } + } +} + +impl MyCircuitComposer for MyCircuitChip { + fn raw_multiply( + &self, + layouter: &mut impl Layouter, + mut f: FM, + ) -> Result<(Cell, Cell, Cell), Error> + where + FM: FnMut() -> Value<(Assigned, Assigned, Assigned)>, + { + let mut values = None; + layouter.assign_region( + || "multiply", + |mut region| { + let lhs = region.assign_advice( + || "lhs", + self.config.l, + 0, + || { + values = Some(f()); + values.unwrap().map(|x| x.0) + }, + )?; + let rhs = region.assign_advice( + || "rhs", + self.config.r, + 0, + || values.unwrap().map(|x| x.1), + )?; + let out = region.assign_advice( + || "out", + self.config.o, + 0, + || values.unwrap().map(|x| x.2), + )?; + + region.enable_selector(|| "mul", &self.config.s_mul, 0)?; + + Ok((lhs.cell(), rhs.cell(), out.cell())) + }, + ) + } + + fn raw_add( + &self, + layouter: &mut impl Layouter, + mut f: FM, + ) -> Result<(Cell, Cell, Cell), Error> + where + FM: FnMut() -> Value<(Assigned, Assigned, Assigned)>, + { + let mut values = None; + layouter.assign_region( + || "add", + |mut region| { + let lhs = region.assign_advice( + || "lhs", + self.config.l, + 0, + || { + values = Some(f()); + values.unwrap().map(|x| x.0) + }, + )?; + let rhs = region.assign_advice( + || "rhs", + self.config.r, + 0, + || values.unwrap().map(|x| x.1), + )?; + let out = region.assign_advice( + || "out", + self.config.o, + 0, + || values.unwrap().map(|x| x.2), + )?; + + region.enable_selector(|| "add", &self.config.s_add, 0)?; -const K: u32 = 6; -const WIDTH_FACTOR: usize = 1; + Ok((lhs.cell(), rhs.cell(), out.cell())) + }, + ) + } + + fn copy(&self, layouter: &mut impl Layouter, a: Cell, b: Cell) -> Result<(), Error> { + layouter.assign_region(|| "copy values", |mut region| region.constrain_equal(a, b)) + } + + fn expose_public( + &self, + layouter: &mut impl Layouter, + cell: Cell, + row: usize, + ) -> Result<(), Error> { + layouter.constrain_instance(cell, self.config.PI, row) + } + + fn cube(&self, layouter: &mut impl Layouter, mut f: FM) -> Result<(Cell, Cell), Error> + where + FM: FnMut() -> Value<(Assigned, Assigned)>, + { + let mut values = None; + layouter.assign_region( + || "cube", + |mut region| { + let lhs = region.assign_advice( + || "lhs", + self.config.l, + 0, + || { + values = Some(f()); + values.unwrap().map(|x| x.0) + }, + )?; + let out = region.assign_advice( + || "out", + self.config.o, + 0, + || values.unwrap().map(|x| x.1), + )?; + + region.enable_selector(|| "cube", &self.config.s_cubed, 0)?; + + Ok((lhs.cell(), out.cell())) + }, + ) + } +} + +#[derive(Debug, Clone, Default)] +struct MyCircuitCircuit { + x: Value, + y: Value, + constant: F, +} + +impl Circuit for MyCircuitCircuit { + type Config = MyCircuitConfig; + + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + MyCircuitChip::configure(meta) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let cs = MyCircuitChip::construct(config); + + let x: Value> = self.x.into(); + let y: Value> = self.y.into(); + let consty = Assigned::from(self.constant); + + let (a0, b0, c0) = cs.raw_multiply(&mut layouter, || x.map(|x| (x, x, x * x)))?; + cs.copy(&mut layouter, a0, b0)?; + + let (a1, b1, c1) = cs.raw_multiply(&mut layouter, || y.map(|y| (y, y, y * y)))?; + cs.copy(&mut layouter, a1, b1)?; + + let (a2, b2, c2) = cs.raw_add(&mut layouter, || { + x.zip(y).map(|(x, y)| (x * x, y * y, x * x + y * y)) + })?; + cs.copy(&mut layouter, a2, c0)?; + cs.copy(&mut layouter, b2, c1)?; + + let (a3, b3, c3) = cs.raw_add(&mut layouter, || { + x.zip(y) + .map(|(x, y)| (x * x + y * y, consty, x * x + y * y + consty)) + })?; + cs.copy(&mut layouter, a3, c2)?; + cs.expose_public(&mut layouter, b3, 0)?; + + cs.expose_public(&mut layouter, c3, 1)?; + + Ok(()) + } +} fn test_mycircuit( vk_keygen_compress_selectors: bool, pk_keygen_compress_selectors: bool, proofgen_compress_selectors: bool, -) -> Result<(), Error> { +) -> Result<(), halo2_proofs::plonk::Error> { let engine = PlonkEngineConfig::new() .set_curve::() .set_msm(H2cEngine::new()) .build(); - let k = K; - let circuit: MyCircuit = MyCircuit::new(k, 42); + let k = 4; + let circuit: MyCircuitCircuit = MyCircuitCircuit { + x: Value::known(Fr::one()), + y: Value::known(Fr::one()), + constant: Fr::one(), + }; // Setup let mut rng = BlockRng::new(OneNg {}); @@ -42,7 +365,7 @@ fn test_mycircuit( .expect("keygen_pk should not fail"); // Proving - let instances = circuit.instances(); + let instances = vec![vec![Fr::one(), Fr::from_u128(3)]]; let instances_slice: &[&[Fr]] = &(instances .iter() .map(|instance| instance.as_slice()) @@ -81,35 +404,40 @@ fn test_mycircuit( &[instances_slice], &mut verifier_transcript, ) - .map_err(|e| Error::Backend(e)) + .map_err(|e| halo2_proofs::plonk::Error::Backend(e)) } #[test] fn test_mycircuit_compress_selectors() { - match test_mycircuit(true, true, true) { - Ok(_) => println!("Success!"), - Err(_) => panic!("Should succeed!"), - } - match test_mycircuit(false, false, false) { - Ok(_) => println!("Success!"), - Err(_) => panic!("Should succeed!"), - } + assert!(test_mycircuit(true, true, true).is_ok()); + assert!(test_mycircuit(false, false, false).is_ok()); +} - match test_mycircuit(false, true, true) { - Ok(_) => panic!("Should fail!"), - Err(_) => println!("Success!"), - } - match test_mycircuit(true, false, true) { - Ok(_) => panic!("Should fail!"), - Err(_) => println!("Success!"), - } +// TODO: +// This panic comes from that there exists many ".unwrap()" +// in proof generation/verification process. +// Remove ".unwrap()" & apply the error handling. +#[should_panic] +#[test] +fn test_compress_selectors_3() { + assert!(test_mycircuit(false, true, true).is_err()); +} - match test_mycircuit(false, false, true) { - Ok(_) => panic!("Should fail!"), - Err(_) => println!("Success!"), - } - match test_mycircuit(true, true, false) { - Ok(_) => panic!("Should fail!"), - Err(_) => println!("Success!"), - } +#[test] +fn test_compress_selectors_4() { + assert!(test_mycircuit(true, false, true).is_err()); +} + +// ditto +#[should_panic] +#[test] +fn test_compress_selectors_5() { + assert!(test_mycircuit(false, false, true).is_err()); +} + +// ditto +#[should_panic] +#[test] +fn test_compress_selectors_6() { + assert!(test_mycircuit(true, true, false).is_err()); } diff --git a/halo2_proofs/tests/frontend_backend_split.rs b/halo2_proofs/tests/frontend_backend_split.rs index a130dee8b1..0c58069340 100644 --- a/halo2_proofs/tests/frontend_backend_split.rs +++ b/halo2_proofs/tests/frontend_backend_split.rs @@ -5,11 +5,6 @@ #[global_allocator] static ALLOC: dhat::Alloc = dhat::Alloc; -// Import the utility module from utils.rs -#[path = "utils.rs"] -mod utils; -use utils::{MyCircuit, OneNg}; - use halo2_backend::{ plonk::{ keygen::{keygen_pk, keygen_vk}, @@ -21,17 +16,477 @@ use halo2_backend::{ }, }; use halo2_frontend::{ - circuit::{compile_circuit, WitnessCalculator}, + circuit::{ + compile_circuit, AssignedCell, Layouter, Region, SimpleFloorPlanner, Value, + WitnessCalculator, + }, dev::MockProver, + plonk::{ + circuit::{Challenge, Column}, + Advice, Circuit, ConstraintSystem, Error as ErrorFront, Expression, FirstPhase, Fixed, + Instance, SecondPhase, Selector, + }, }; +use halo2_middleware::{ff::Field, poly::Rotation}; use halo2_proofs::poly::commitment::ParamsProver; use std::collections::HashMap; +#[derive(Clone)] +struct MyCircuitConfig { + // A gate that uses selector, fixed, advice, has addition, multiplication and rotation + // s_gate[0] * (a[0] + b[0] * c[0] * d[0] - a[1]) + s_gate: Selector, + a: Column, + b: Column, + c: Column, + d: Column, + + // Copy constraints between columns (a, b) and (a, d) + + // A dynamic lookup: s_lookup * [1, a[0], b[0]] in s_ltable * [1, d[0], c[0]] + s_lookup: Column, + s_ltable: Column, + + // A shuffle: s_shufle * [1, a[0]] shuffle_of s_stable * [1, b[0]] + s_shuffle: Column, + s_stable: Column, + + // A FirstPhase challenge and SecondPhase column. We define the following gates: + // s_rlc * (a[0] + challenge * b[0] - e[0]) + // s_rlc * (c[0] + challenge * d[0] - e[0]) + s_rlc: Selector, + e: Column, + challenge: Challenge, + + // Instance with a gate: s_instance * (a[0] - instance[0]) + s_instance: Selector, + instance: Column, +} + +impl MyCircuitConfig { + #[allow(clippy::type_complexity)] + fn assign_gate>( + &self, + region: &mut Region<'_, F>, + offset: &mut usize, + a_assigned: Option>, + abcd: [u64; 4], + ) -> Result<(AssignedCell, [AssignedCell; 4]), ErrorFront> { + let [a, b, c, d] = abcd; + self.s_gate.enable(region, *offset)?; + let a_assigned = if let Some(a_assigned) = a_assigned { + a_assigned + } else { + region.assign_advice(|| "", self.a, *offset, || Value::known(F::from(a)))? + }; + let a = a_assigned.value(); + let [b, c, d] = [b, c, d].map(|v| Value::known(F::from(v))); + let b_assigned = region.assign_advice(|| "", self.b, *offset, || b)?; + let c_assigned = region.assign_advice(|| "", self.c, *offset, || c)?; + let d_assigned = region.assign_fixed(|| "", self.d, *offset, || d)?; + *offset += 1; + // let res = a + b * c * d; + let res = a + .zip(b.zip(c.zip(d))) + .map(|(a, (b, (c, d)))| *a + b * c * d); + let res_assigned = region.assign_advice(|| "", self.a, *offset, || res)?; + Ok(( + res_assigned, + [a_assigned, b_assigned, c_assigned, d_assigned], + )) + } +} + +#[derive(Clone)] +struct MyCircuit { + k: u32, + input: u64, + _marker: std::marker::PhantomData, +} + +impl, const WIDTH_FACTOR: usize> MyCircuit { + fn new(k: u32, input: u64) -> Self { + Self { + k, + input, + _marker: std::marker::PhantomData {}, + } + } + + fn instance(&self) -> Vec { + let mut instance = Vec::new(); + let res = F::from(self.input); + instance.push(res); + let (b, c, d) = (3, 4, 1); + let res = res + F::from(b) * F::from(c) * F::from(d); + instance.push(res); + let (b, c, d) = (6, 7, 1); + let res = res + F::from(b) * F::from(c) * F::from(d); + instance.push(res); + let (b, c, d) = (8, 9, 1); + let res = res + F::from(b) * F::from(c) * F::from(d); + instance.push(res); + instance.push(F::from(2)); + instance.push(F::from(2)); + instance + } + + fn instances(&self) -> Vec> { + let instance = self.instance(); + (0..WIDTH_FACTOR).map(|_| instance.clone()).collect() + } + + fn configure_single(meta: &mut ConstraintSystem, id: usize) -> MyCircuitConfig { + let s_gate = meta.selector(); + let a = meta.advice_column(); + let b = meta.advice_column(); + let c = meta.advice_column(); + let d = meta.fixed_column(); + + meta.enable_equality(a); + meta.enable_equality(b); + meta.enable_equality(d); + + let s_lookup = meta.fixed_column(); + let s_ltable = meta.fixed_column(); + + let s_shuffle = meta.fixed_column(); + let s_stable = meta.fixed_column(); + + let s_rlc = meta.selector(); + let e = meta.advice_column_in(SecondPhase); + let challenge = meta.challenge_usable_after(FirstPhase); + + let s_instance = meta.selector(); + let instance = meta.instance_column(); + meta.enable_equality(instance); + + let one = Expression::Constant(F::ONE); + + meta.create_gate(format!("gate_a.{id}"), |meta| { + let s_gate = meta.query_selector(s_gate); + let b = meta.query_advice(b, Rotation::cur()); + let a1 = meta.query_advice(a, Rotation::next()); + let a0 = meta.query_advice(a, Rotation::cur()); + let c = meta.query_advice(c, Rotation::cur()); + let d = meta.query_fixed(d, Rotation::cur()); + + vec![s_gate * (a0 + b * c * d - a1)] + }); + + meta.lookup_any(format!("lookup.{id}"), |meta| { + let s_lookup = meta.query_fixed(s_lookup, Rotation::cur()); + let s_ltable = meta.query_fixed(s_ltable, Rotation::cur()); + let a = meta.query_advice(a, Rotation::cur()); + let b = meta.query_advice(b, Rotation::cur()); + let c = meta.query_advice(c, Rotation::cur()); + let d = meta.query_fixed(d, Rotation::cur()); + let lhs = [one.clone(), a, b].map(|c| c * s_lookup.clone()); + let rhs = [one.clone(), d, c].map(|c| c * s_ltable.clone()); + lhs.into_iter().zip(rhs).collect() + }); + + meta.shuffle(format!("shuffle.{id}"), |meta| { + let s_shuffle = meta.query_fixed(s_shuffle, Rotation::cur()); + let s_stable = meta.query_fixed(s_stable, Rotation::cur()); + let a = meta.query_advice(a, Rotation::cur()); + let b = meta.query_advice(b, Rotation::cur()); + let lhs = [one.clone(), a].map(|c| c * s_shuffle.clone()); + let rhs = [one.clone(), b].map(|c| c * s_stable.clone()); + lhs.into_iter().zip(rhs).collect() + }); + + meta.create_gate(format!("gate_rlc.{id}"), |meta| { + let s_rlc = meta.query_selector(s_rlc); + let a = meta.query_advice(a, Rotation::cur()); + let b = meta.query_advice(b, Rotation::cur()); + let c = meta.query_advice(c, Rotation::cur()); + let d = meta.query_fixed(d, Rotation::cur()); + let e = meta.query_advice(e, Rotation::cur()); + let challenge = meta.query_challenge(challenge); + + vec![ + s_rlc.clone() * (a + challenge.clone() * b - e.clone()), + s_rlc * (c + challenge * d - e), + ] + }); + + MyCircuitConfig { + s_gate, + a, + b, + c, + d, + s_lookup, + s_ltable, + s_rlc, + e, + challenge, + s_shuffle, + s_stable, + s_instance, + instance, + } + } + + fn synthesize_unit( + &self, + config: &MyCircuitConfig, + layouter: &mut impl Layouter, + id: usize, + unit_id: usize, + ) -> Result<(usize, Vec>), ErrorFront> { + let challenge = layouter.get_challenge(config.challenge); + let (rows, instance_copy) = layouter.assign_region( + || format!("unit.{id}-{unit_id}"), + |mut region| { + // Column annotations + region.name_column(|| format!("a.{id}"), config.a); + region.name_column(|| format!("b.{id}"), config.b); + region.name_column(|| format!("c.{id}"), config.c); + region.name_column(|| format!("d.{id}"), config.d); + region.name_column(|| format!("e.{id}"), config.e); + region.name_column(|| format!("instance.{id}"), config.instance); + region.name_column(|| format!("s_lookup.{id}"), config.s_lookup); + region.name_column(|| format!("s_ltable.{id}"), config.s_ltable); + region.name_column(|| format!("s_shuffle.{id}"), config.s_shuffle); + region.name_column(|| format!("s_stable.{id}"), config.s_stable); + + let mut offset = 0; + let mut instance_copy = Vec::new(); + // First "a" value comes from instance + config.s_instance.enable(&mut region, offset).expect("todo"); + let res = region + .assign_advice_from_instance(|| "", config.instance, 0, config.a, offset) + .expect("todo"); + // Enable the gate on a few consecutive rows with rotations + let (res, _) = config + .assign_gate(&mut region, &mut offset, Some(res), [0, 3, 4, 1]) + .expect("todo"); + instance_copy.push(res.clone()); + let (res, _) = config + .assign_gate(&mut region, &mut offset, Some(res), [0, 6, 7, 1]) + .expect("todo"); + instance_copy.push(res.clone()); + let (res, _) = config + .assign_gate(&mut region, &mut offset, Some(res), [0, 8, 9, 1]) + .expect("todo"); + instance_copy.push(res.clone()); + let (res, _) = config + .assign_gate( + &mut region, + &mut offset, + Some(res), + [0, 0xffffffff, 0xdeadbeef, 1], + ) + .expect("todo"); + let _ = config + .assign_gate( + &mut region, + &mut offset, + Some(res), + [0, 0xabad1d3a, 0x12345678, 0x42424242], + ) + .expect("todo"); + offset += 1; + + // Enable the gate on non-consecutive rows with advice-advice copy constraints enabled + let (_, abcd1) = config + .assign_gate(&mut region, &mut offset, None, [5, 2, 1, 1]) + .expect("todo"); + offset += 1; + let (_, abcd2) = config + .assign_gate(&mut region, &mut offset, None, [2, 3, 1, 1]) + .expect("todo"); + offset += 1; + let (_, abcd3) = config + .assign_gate(&mut region, &mut offset, None, [4, 2, 1, 1]) + .expect("todo"); + offset += 1; + region + .constrain_equal(abcd1[1].cell(), abcd2[0].cell()) + .expect("todo"); + region + .constrain_equal(abcd2[0].cell(), abcd3[1].cell()) + .expect("todo"); + instance_copy.push(abcd1[1].clone()); + instance_copy.push(abcd2[0].clone()); + + // Enable the gate on non-consecutive rows with advice-fixed copy constraints enabled + let (_, abcd1) = config + .assign_gate(&mut region, &mut offset, None, [5, 9, 1, 9]) + .expect("todo"); + offset += 1; + let (_, abcd2) = config + .assign_gate(&mut region, &mut offset, None, [2, 9, 1, 1]) + .expect("todo"); + offset += 1; + let (_, abcd3) = config + .assign_gate(&mut region, &mut offset, None, [9, 2, 1, 1]) + .expect("todo"); + offset += 1; + region + .constrain_equal(abcd1[1].cell(), abcd1[3].cell()) + .expect("todo"); + region + .constrain_equal(abcd2[1].cell(), abcd1[3].cell()) + .expect("todo"); + region + .constrain_equal(abcd3[0].cell(), abcd1[3].cell()) + .expect("todo"); + + // Enable a dynamic lookup (powers of two) + let table: Vec<_> = (0u64..=10).map(|exp| (exp, 2u64.pow(exp as u32))).collect(); + let lookups = [(2, 4), (2, 4), (10, 1024), (0, 1), (2, 4)]; + for (table_row, lookup_row) in table + .iter() + .zip(lookups.iter().chain(std::iter::repeat(&(0, 1)))) + { + region + .assign_fixed(|| "", config.s_lookup, offset, || Value::known(F::ONE)) + .expect("todo"); + region + .assign_fixed(|| "", config.s_ltable, offset, || Value::known(F::ONE)) + .expect("todo"); + let lookup_row0 = Value::known(F::from(lookup_row.0)); + let lookup_row1 = Value::known(F::from(lookup_row.1)); + region + .assign_advice(|| "", config.a, offset, || lookup_row0) + .expect("todo"); + region + .assign_advice(|| "", config.b, offset, || lookup_row1) + .expect("todo"); + let table_row0 = Value::known(F::from(table_row.0)); + let table_row1 = Value::known(F::from(table_row.1)); + region + .assign_fixed(|| "", config.d, offset, || table_row0) + .expect("todo"); + region + .assign_advice(|| "", config.c, offset, || table_row1) + .expect("todo"); + offset += 1; + } + + // Enable RLC gate 3 times + for abcd in [[3, 5, 3, 5], [8, 9, 8, 9], [111, 222, 111, 222]] { + config.s_rlc.enable(&mut region, offset)?; + let (_, _) = config + .assign_gate(&mut region, &mut offset, None, abcd) + .expect("todo"); + let rlc = challenge.map(|ch| { + let [a, b, ..] = abcd; + F::from(a) + ch * F::from(b) + }); + region + .assign_advice(|| "", config.e, offset - 1, || rlc) + .expect("todo"); + offset += 1; + } + + // Enable a dynamic shuffle (sequence from 0 to 15) + let table: Vec<_> = (0u64..16).collect(); + let shuffle = [0u64, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15]; + assert_eq!(table.len(), shuffle.len()); + + for (table_row, shuffle_row) in table.iter().zip(shuffle.iter()) { + region + .assign_fixed(|| "", config.s_shuffle, offset, || Value::known(F::ONE)) + .expect("todo"); + region + .assign_fixed(|| "", config.s_stable, offset, || Value::known(F::ONE)) + .expect("todo"); + let shuffle_row0 = Value::known(F::from(*shuffle_row)); + region + .assign_advice(|| "", config.a, offset, || shuffle_row0) + .expect("todo"); + let table_row0 = Value::known(F::from(*table_row)); + region + .assign_advice(|| "", config.b, offset, || table_row0) + .expect("todo"); + offset += 1; + } + + Ok((offset, instance_copy)) + }, + )?; + + Ok((rows, instance_copy)) + } +} + +impl, const WIDTH_FACTOR: usize> Circuit for MyCircuit { + type Config = Vec; + type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); + + fn without_witnesses(&self) -> Self { + self.clone() + } + + fn configure(meta: &mut ConstraintSystem) -> Vec { + assert!(WIDTH_FACTOR > 0); + (0..WIDTH_FACTOR) + .map(|id| Self::configure_single(meta, id)) + .collect() + } + + fn synthesize( + &self, + config: Vec, + mut layouter: impl Layouter, + ) -> Result<(), ErrorFront> { + // - 2 queries from first gate + // - 3 for permutation argument + // - 1 for multipoen + // - 1 for the last row of grand product poly to check that the product result is 1 + // - 1 for off-by-one errors + let unusable_rows = 2 + 3 + 1 + 1 + 1; + let max_rows = 2usize.pow(self.k) - unusable_rows; + for (id, config) in config.iter().enumerate() { + let mut total_rows = 0; + let mut unit_id = 0; + loop { + let (rows, instance_copy) = self + .synthesize_unit(config, &mut layouter, id, unit_id) + .expect("todo"); + if total_rows == 0 { + for (i, instance) in instance_copy.iter().enumerate() { + layouter.constrain_instance(instance.cell(), config.instance, 1 + i)?; + } + } + total_rows += rows; + if total_rows + rows > max_rows { + break; + } + unit_id += 1; + } + assert!(total_rows <= max_rows); + } + Ok(()) + } +} + use halo2_proofs::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; use halo2_proofs::poly::kzg::multiopen::{ProverSHPLONK, VerifierSHPLONK}; use halo2_proofs::poly::kzg::strategy::SingleStrategy; use halo2curves::bn256::{Bn256, Fr, G1Affine}; use rand_core::block::BlockRng; +use rand_core::block::BlockRngCore; + +// One number generator, that can be used as a deterministic Rng, outputing fixed values. +struct OneNg {} + +impl BlockRngCore for OneNg { + type Item = u32; + type Results = [u32; 16]; + + fn generate(&mut self, results: &mut Self::Results) { + for elem in results.iter_mut() { + *elem = 1; + } + } +} #[test] fn test_mycircuit_mock() { diff --git a/halo2_proofs/tests/utils.rs b/halo2_proofs/tests/utils.rs deleted file mode 100644 index 880602ae67..0000000000 --- a/halo2_proofs/tests/utils.rs +++ /dev/null @@ -1,460 +0,0 @@ -use halo2_frontend::{ - circuit::{AssignedCell, Layouter, Region, SimpleFloorPlanner, Value}, - plonk::{ - circuit::{Challenge, Column}, - Advice, Circuit, ConstraintSystem, Error as ErrorFront, Expression, FirstPhase, Fixed, - Instance, SecondPhase, Selector, - }, -}; -use halo2_middleware::{ff::Field, poly::Rotation}; -use rand_core::block::BlockRngCore; - -#[derive(Clone)] -pub struct MyCircuitConfig { - // A gate that uses selector, fixed, advice, has addition, multiplication and rotation - // s_gate[0] * (a[0] + b[0] * c[0] * d[0] - a[1]) - s_gate: Selector, - a: Column, - b: Column, - c: Column, - d: Column, - - // Copy constraints between columns (a, b) and (a, d) - - // A dynamic lookup: s_lookup * [1, a[0], b[0]] in s_ltable * [1, d[0], c[0]] - s_lookup: Column, - s_ltable: Column, - - // A shuffle: s_shufle * [1, a[0]] shuffle_of s_stable * [1, b[0]] - s_shuffle: Column, - s_stable: Column, - - // A FirstPhase challenge and SecondPhase column. We define the following gates: - // s_rlc * (a[0] + challenge * b[0] - e[0]) - // s_rlc * (c[0] + challenge * d[0] - e[0]) - s_rlc: Selector, - e: Column, - challenge: Challenge, - - // Instance with a gate: s_instance * (a[0] - instance[0]) - s_instance: Selector, - instance: Column, -} - -impl MyCircuitConfig { - #[allow(clippy::type_complexity)] - fn assign_gate>( - &self, - region: &mut Region<'_, F>, - offset: &mut usize, - a_assigned: Option>, - abcd: [u64; 4], - ) -> Result<(AssignedCell, [AssignedCell; 4]), ErrorFront> { - let [a, b, c, d] = abcd; - self.s_gate.enable(region, *offset)?; - let a_assigned = if let Some(a_assigned) = a_assigned { - a_assigned - } else { - region.assign_advice(|| "", self.a, *offset, || Value::known(F::from(a)))? - }; - let a = a_assigned.value(); - let [b, c, d] = [b, c, d].map(|v| Value::known(F::from(v))); - let b_assigned = region.assign_advice(|| "", self.b, *offset, || b)?; - let c_assigned = region.assign_advice(|| "", self.c, *offset, || c)?; - let d_assigned = region.assign_fixed(|| "", self.d, *offset, || d)?; - *offset += 1; - // let res = a + b * c * d; - let res = a - .zip(b.zip(c.zip(d))) - .map(|(a, (b, (c, d)))| *a + b * c * d); - let res_assigned = region.assign_advice(|| "", self.a, *offset, || res)?; - Ok(( - res_assigned, - [a_assigned, b_assigned, c_assigned, d_assigned], - )) - } -} - -#[derive(Clone)] -pub struct MyCircuit { - k: u32, - input: u64, - _marker: std::marker::PhantomData, -} - -impl, const WIDTH_FACTOR: usize> MyCircuit { - pub fn new(k: u32, input: u64) -> Self { - Self { - k, - input, - _marker: std::marker::PhantomData {}, - } - } - - fn instance(&self) -> Vec { - let mut instance = Vec::new(); - let res = F::from(self.input); - instance.push(res); - let (b, c, d) = (3, 4, 1); - let res = res + F::from(b) * F::from(c) * F::from(d); - instance.push(res); - let (b, c, d) = (6, 7, 1); - let res = res + F::from(b) * F::from(c) * F::from(d); - instance.push(res); - let (b, c, d) = (8, 9, 1); - let res = res + F::from(b) * F::from(c) * F::from(d); - instance.push(res); - instance.push(F::from(2)); - instance.push(F::from(2)); - instance - } - - pub fn instances(&self) -> Vec> { - let instance = self.instance(); - (0..WIDTH_FACTOR).map(|_| instance.clone()).collect() - } - - fn configure_single(meta: &mut ConstraintSystem, id: usize) -> MyCircuitConfig { - let s_gate = meta.selector(); - let a = meta.advice_column(); - let b = meta.advice_column(); - let c = meta.advice_column(); - let d = meta.fixed_column(); - - meta.enable_equality(a); - meta.enable_equality(b); - meta.enable_equality(d); - - let s_lookup = meta.fixed_column(); - let s_ltable = meta.fixed_column(); - - let s_shuffle = meta.fixed_column(); - let s_stable = meta.fixed_column(); - - let s_rlc = meta.selector(); - let e = meta.advice_column_in(SecondPhase); - let challenge = meta.challenge_usable_after(FirstPhase); - - let s_instance = meta.selector(); - let instance = meta.instance_column(); - meta.enable_equality(instance); - - let one = Expression::Constant(F::ONE); - - meta.create_gate(format!("gate_a.{id}"), |meta| { - let s_gate = meta.query_selector(s_gate); - let b = meta.query_advice(b, Rotation::cur()); - let a1 = meta.query_advice(a, Rotation::next()); - let a0 = meta.query_advice(a, Rotation::cur()); - let c = meta.query_advice(c, Rotation::cur()); - let d = meta.query_fixed(d, Rotation::cur()); - - vec![s_gate * (a0 + b * c * d - a1)] - }); - - meta.lookup_any(format!("lookup.{id}"), |meta| { - let s_lookup = meta.query_fixed(s_lookup, Rotation::cur()); - let s_ltable = meta.query_fixed(s_ltable, Rotation::cur()); - let a = meta.query_advice(a, Rotation::cur()); - let b = meta.query_advice(b, Rotation::cur()); - let c = meta.query_advice(c, Rotation::cur()); - let d = meta.query_fixed(d, Rotation::cur()); - let lhs = [one.clone(), a, b].map(|c| c * s_lookup.clone()); - let rhs = [one.clone(), d, c].map(|c| c * s_ltable.clone()); - lhs.into_iter().zip(rhs).collect() - }); - - meta.shuffle(format!("shuffle.{id}"), |meta| { - let s_shuffle = meta.query_fixed(s_shuffle, Rotation::cur()); - let s_stable = meta.query_fixed(s_stable, Rotation::cur()); - let a = meta.query_advice(a, Rotation::cur()); - let b = meta.query_advice(b, Rotation::cur()); - let lhs = [one.clone(), a].map(|c| c * s_shuffle.clone()); - let rhs = [one.clone(), b].map(|c| c * s_stable.clone()); - lhs.into_iter().zip(rhs).collect() - }); - - meta.create_gate(format!("gate_rlc.{id}"), |meta| { - let s_rlc = meta.query_selector(s_rlc); - let a = meta.query_advice(a, Rotation::cur()); - let b = meta.query_advice(b, Rotation::cur()); - let c = meta.query_advice(c, Rotation::cur()); - let d = meta.query_fixed(d, Rotation::cur()); - let e = meta.query_advice(e, Rotation::cur()); - let challenge = meta.query_challenge(challenge); - - vec![ - s_rlc.clone() * (a + challenge.clone() * b - e.clone()), - s_rlc * (c + challenge * d - e), - ] - }); - - MyCircuitConfig { - s_gate, - a, - b, - c, - d, - s_lookup, - s_ltable, - s_rlc, - e, - challenge, - s_shuffle, - s_stable, - s_instance, - instance, - } - } - - fn synthesize_unit( - &self, - config: &MyCircuitConfig, - layouter: &mut impl Layouter, - id: usize, - unit_id: usize, - ) -> Result<(usize, Vec>), ErrorFront> { - let challenge = layouter.get_challenge(config.challenge); - let (rows, instance_copy) = layouter.assign_region( - || format!("unit.{id}-{unit_id}"), - |mut region| { - // Column annotations - region.name_column(|| format!("a.{id}"), config.a); - region.name_column(|| format!("b.{id}"), config.b); - region.name_column(|| format!("c.{id}"), config.c); - region.name_column(|| format!("d.{id}"), config.d); - region.name_column(|| format!("e.{id}"), config.e); - region.name_column(|| format!("instance.{id}"), config.instance); - region.name_column(|| format!("s_lookup.{id}"), config.s_lookup); - region.name_column(|| format!("s_ltable.{id}"), config.s_ltable); - region.name_column(|| format!("s_shuffle.{id}"), config.s_shuffle); - region.name_column(|| format!("s_stable.{id}"), config.s_stable); - - let mut offset = 0; - let mut instance_copy = Vec::new(); - // First "a" value comes from instance - config.s_instance.enable(&mut region, offset).expect("todo"); - let res = region - .assign_advice_from_instance(|| "", config.instance, 0, config.a, offset) - .expect("todo"); - // Enable the gate on a few consecutive rows with rotations - let (res, _) = config - .assign_gate(&mut region, &mut offset, Some(res), [0, 3, 4, 1]) - .expect("todo"); - instance_copy.push(res.clone()); - let (res, _) = config - .assign_gate(&mut region, &mut offset, Some(res), [0, 6, 7, 1]) - .expect("todo"); - instance_copy.push(res.clone()); - let (res, _) = config - .assign_gate(&mut region, &mut offset, Some(res), [0, 8, 9, 1]) - .expect("todo"); - instance_copy.push(res.clone()); - let (res, _) = config - .assign_gate( - &mut region, - &mut offset, - Some(res), - [0, 0xffffffff, 0xdeadbeef, 1], - ) - .expect("todo"); - let _ = config - .assign_gate( - &mut region, - &mut offset, - Some(res), - [0, 0xabad1d3a, 0x12345678, 0x42424242], - ) - .expect("todo"); - offset += 1; - - // Enable the gate on non-consecutive rows with advice-advice copy constraints enabled - let (_, abcd1) = config - .assign_gate(&mut region, &mut offset, None, [5, 2, 1, 1]) - .expect("todo"); - offset += 1; - let (_, abcd2) = config - .assign_gate(&mut region, &mut offset, None, [2, 3, 1, 1]) - .expect("todo"); - offset += 1; - let (_, abcd3) = config - .assign_gate(&mut region, &mut offset, None, [4, 2, 1, 1]) - .expect("todo"); - offset += 1; - region - .constrain_equal(abcd1[1].cell(), abcd2[0].cell()) - .expect("todo"); - region - .constrain_equal(abcd2[0].cell(), abcd3[1].cell()) - .expect("todo"); - instance_copy.push(abcd1[1].clone()); - instance_copy.push(abcd2[0].clone()); - - // Enable the gate on non-consecutive rows with advice-fixed copy constraints enabled - let (_, abcd1) = config - .assign_gate(&mut region, &mut offset, None, [5, 9, 1, 9]) - .expect("todo"); - offset += 1; - let (_, abcd2) = config - .assign_gate(&mut region, &mut offset, None, [2, 9, 1, 1]) - .expect("todo"); - offset += 1; - let (_, abcd3) = config - .assign_gate(&mut region, &mut offset, None, [9, 2, 1, 1]) - .expect("todo"); - offset += 1; - region - .constrain_equal(abcd1[1].cell(), abcd1[3].cell()) - .expect("todo"); - region - .constrain_equal(abcd2[1].cell(), abcd1[3].cell()) - .expect("todo"); - region - .constrain_equal(abcd3[0].cell(), abcd1[3].cell()) - .expect("todo"); - - // Enable a dynamic lookup (powers of two) - let table: Vec<_> = (0u64..=10).map(|exp| (exp, 2u64.pow(exp as u32))).collect(); - let lookups = [(2, 4), (2, 4), (10, 1024), (0, 1), (2, 4)]; - for (table_row, lookup_row) in table - .iter() - .zip(lookups.iter().chain(std::iter::repeat(&(0, 1)))) - { - region - .assign_fixed(|| "", config.s_lookup, offset, || Value::known(F::ONE)) - .expect("todo"); - region - .assign_fixed(|| "", config.s_ltable, offset, || Value::known(F::ONE)) - .expect("todo"); - let lookup_row0 = Value::known(F::from(lookup_row.0)); - let lookup_row1 = Value::known(F::from(lookup_row.1)); - region - .assign_advice(|| "", config.a, offset, || lookup_row0) - .expect("todo"); - region - .assign_advice(|| "", config.b, offset, || lookup_row1) - .expect("todo"); - let table_row0 = Value::known(F::from(table_row.0)); - let table_row1 = Value::known(F::from(table_row.1)); - region - .assign_fixed(|| "", config.d, offset, || table_row0) - .expect("todo"); - region - .assign_advice(|| "", config.c, offset, || table_row1) - .expect("todo"); - offset += 1; - } - - // Enable RLC gate 3 times - for abcd in [[3, 5, 3, 5], [8, 9, 8, 9], [111, 222, 111, 222]] { - config.s_rlc.enable(&mut region, offset)?; - let (_, _) = config - .assign_gate(&mut region, &mut offset, None, abcd) - .expect("todo"); - let rlc = challenge.map(|ch| { - let [a, b, ..] = abcd; - F::from(a) + ch * F::from(b) - }); - region - .assign_advice(|| "", config.e, offset - 1, || rlc) - .expect("todo"); - offset += 1; - } - - // Enable a dynamic shuffle (sequence from 0 to 15) - let table: Vec<_> = (0u64..16).collect(); - let shuffle = [0u64, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15]; - assert_eq!(table.len(), shuffle.len()); - - for (table_row, shuffle_row) in table.iter().zip(shuffle.iter()) { - region - .assign_fixed(|| "", config.s_shuffle, offset, || Value::known(F::ONE)) - .expect("todo"); - region - .assign_fixed(|| "", config.s_stable, offset, || Value::known(F::ONE)) - .expect("todo"); - let shuffle_row0 = Value::known(F::from(*shuffle_row)); - region - .assign_advice(|| "", config.a, offset, || shuffle_row0) - .expect("todo"); - let table_row0 = Value::known(F::from(*table_row)); - region - .assign_advice(|| "", config.b, offset, || table_row0) - .expect("todo"); - offset += 1; - } - - Ok((offset, instance_copy)) - }, - )?; - - Ok((rows, instance_copy)) - } -} - -impl, const WIDTH_FACTOR: usize> Circuit for MyCircuit { - type Config = Vec; - type FloorPlanner = SimpleFloorPlanner; - #[cfg(feature = "circuit-params")] - type Params = (); - - fn without_witnesses(&self) -> Self { - self.clone() - } - - fn configure(meta: &mut ConstraintSystem) -> Vec { - assert!(WIDTH_FACTOR > 0); - (0..WIDTH_FACTOR) - .map(|id| Self::configure_single(meta, id)) - .collect() - } - - fn synthesize( - &self, - config: Vec, - mut layouter: impl Layouter, - ) -> Result<(), ErrorFront> { - // - 2 queries from first gate - // - 3 for permutation argument - // - 1 for multipoen - // - 1 for the last row of grand product poly to check that the product result is 1 - // - 1 for off-by-one errors - let unusable_rows = 2 + 3 + 1 + 1 + 1; - let max_rows = 2usize.pow(self.k) - unusable_rows; - for (id, config) in config.iter().enumerate() { - let mut total_rows = 0; - let mut unit_id = 0; - loop { - let (rows, instance_copy) = self - .synthesize_unit(config, &mut layouter, id, unit_id) - .expect("todo"); - if total_rows == 0 { - for (i, instance) in instance_copy.iter().enumerate() { - layouter.constrain_instance(instance.cell(), config.instance, 1 + i)?; - } - } - total_rows += rows; - if total_rows + rows > max_rows { - break; - } - unit_id += 1; - } - assert!(total_rows <= max_rows); - } - Ok(()) - } -} - -// One number generator, that can be used as a deterministic Rng, outputing fixed values. -pub struct OneNg {} - -impl BlockRngCore for OneNg { - type Item = u32; - type Results = [u32; 16]; - - fn generate(&mut self, results: &mut Self::Results) { - for elem in results.iter_mut() { - *elem = 1; - } - } -} From deed874c8976f750bf91b3dff1b797782be162f1 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Wed, 8 May 2024 23:34:32 +0800 Subject: [PATCH 24/30] chore: fix clippy --- halo2_proofs/tests/compress_selectors.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/halo2_proofs/tests/compress_selectors.rs b/halo2_proofs/tests/compress_selectors.rs index c68dec1106..6b0c8d2c71 100644 --- a/halo2_proofs/tests/compress_selectors.rs +++ b/halo2_proofs/tests/compress_selectors.rs @@ -292,8 +292,9 @@ struct MyCircuitCircuit { impl Circuit for MyCircuitCircuit { type Config = MyCircuitConfig; - type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() From 2bcb970d2c71239254fb7288c1aea503b23ef57e Mon Sep 17 00:00:00 2001 From: guorong009 Date: Wed, 8 May 2024 23:41:04 +0800 Subject: [PATCH 25/30] chore: fix error mapping --- halo2_proofs/tests/compress_selectors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/halo2_proofs/tests/compress_selectors.rs b/halo2_proofs/tests/compress_selectors.rs index 6b0c8d2c71..ee2f22814e 100644 --- a/halo2_proofs/tests/compress_selectors.rs +++ b/halo2_proofs/tests/compress_selectors.rs @@ -405,7 +405,7 @@ fn test_mycircuit( &[instances_slice], &mut verifier_transcript, ) - .map_err(|e| halo2_proofs::plonk::Error::Backend(e)) + .map_err(halo2_proofs::plonk::Error::Backend) } #[test] From 379491e077653f0282a485aff30cafbd406a1e23 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Thu, 9 May 2024 00:05:21 +0800 Subject: [PATCH 26/30] chore: some renamings + some comments --- halo2_proofs/tests/compress_selectors.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/halo2_proofs/tests/compress_selectors.rs b/halo2_proofs/tests/compress_selectors.rs index ee2f22814e..9cca602c62 100644 --- a/halo2_proofs/tests/compress_selectors.rs +++ b/halo2_proofs/tests/compress_selectors.rs @@ -409,8 +409,11 @@ fn test_mycircuit( } #[test] -fn test_mycircuit_compress_selectors() { +fn test_success() { + // keygen & proof generation both WITH compress assert!(test_mycircuit(true, true, true).is_ok()); + + // keygen & proof generation both WITHOUT compress assert!(test_mycircuit(false, false, false).is_ok()); } @@ -420,25 +423,29 @@ fn test_mycircuit_compress_selectors() { // Remove ".unwrap()" & apply the error handling. #[should_panic] #[test] -fn test_compress_selectors_3() { +fn test_failure_1() { + // vk_keygen WITH compress vs pk_keygen WITHOUT compress assert!(test_mycircuit(false, true, true).is_err()); } #[test] -fn test_compress_selectors_4() { +fn test_failure_2() { + // vk_keygen WITHOUT compress vs pk_keygen WITH compress assert!(test_mycircuit(true, false, true).is_err()); } // ditto #[should_panic] #[test] -fn test_compress_selectors_5() { +fn test_failure_3() { + // keygen WITHOUT compress vs proof_gen WITH compress assert!(test_mycircuit(false, false, true).is_err()); } // ditto #[should_panic] #[test] -fn test_compress_selectors_6() { +fn test_failure_4() { + // keygen WITH compress vs proof_gen WITHOUT compress assert!(test_mycircuit(true, true, false).is_err()); } From b8aa3246d10654a58d711bab9ad36c44bcc627b4 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Thu, 9 May 2024 12:04:19 +0800 Subject: [PATCH 27/30] fix: remove wrong attr & comments --- halo2_proofs/tests/compress_selectors.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/halo2_proofs/tests/compress_selectors.rs b/halo2_proofs/tests/compress_selectors.rs index 9cca602c62..bb3992f74e 100644 --- a/halo2_proofs/tests/compress_selectors.rs +++ b/halo2_proofs/tests/compress_selectors.rs @@ -417,10 +417,6 @@ fn test_success() { assert!(test_mycircuit(false, false, false).is_ok()); } -// TODO: -// This panic comes from that there exists many ".unwrap()" -// in proof generation/verification process. -// Remove ".unwrap()" & apply the error handling. #[should_panic] #[test] fn test_failure_1() { @@ -434,16 +430,12 @@ fn test_failure_2() { assert!(test_mycircuit(true, false, true).is_err()); } -// ditto -#[should_panic] #[test] fn test_failure_3() { // keygen WITHOUT compress vs proof_gen WITH compress assert!(test_mycircuit(false, false, true).is_err()); } -// ditto -#[should_panic] #[test] fn test_failure_4() { // keygen WITH compress vs proof_gen WITHOUT compress From 3190c579ae3fd3ddc90bb9fddf1de903dc604563 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Thu, 9 May 2024 22:31:06 +0800 Subject: [PATCH 28/30] fix!: remove the "create_proof_custom_with_engine" --- halo2_frontend/src/circuit.rs | 1 + halo2_proofs/src/plonk.rs | 2 +- halo2_proofs/src/plonk/prover.rs | 93 ++++++++---------------- halo2_proofs/tests/compress_selectors.rs | 46 ++++-------- 4 files changed, 47 insertions(+), 95 deletions(-) diff --git a/halo2_frontend/src/circuit.rs b/halo2_frontend/src/circuit.rs index 157127dc02..d50e64d99c 100644 --- a/halo2_frontend/src/circuit.rs +++ b/halo2_frontend/src/circuit.rs @@ -50,6 +50,7 @@ pub fn compile_circuit>( Error, > { let n = 2usize.pow(k); + let mut cs = ConstraintSystem::default(); #[cfg(feature = "circuit-params")] let config = ConcreteCircuit::configure_with_params(&mut cs, circuit.params()); diff --git a/halo2_proofs/src/plonk.rs b/halo2_proofs/src/plonk.rs index 23c5eb0511..41a2feb2d9 100644 --- a/halo2_proofs/src/plonk.rs +++ b/halo2_proofs/src/plonk.rs @@ -15,7 +15,7 @@ mod verifier { use halo2_frontend::circuit::compile_circuit; pub use keygen::{keygen_pk, keygen_pk_custom, keygen_vk, keygen_vk_custom}; -pub use prover::{create_proof, create_proof_custom_with_engine, create_proof_with_engine}; +pub use prover::{create_proof, create_proof_with_engine}; pub use verifier::verify_proof; pub use error::Error; diff --git a/halo2_proofs/src/plonk/prover.rs b/halo2_proofs/src/plonk/prover.rs index 460f9630fa..133df4da40 100644 --- a/halo2_proofs/src/plonk/prover.rs +++ b/halo2_proofs/src/plonk/prover.rs @@ -2,8 +2,8 @@ use crate::plonk::{Error, ErrorBack}; use crate::poly::commitment::{self, CommitmentScheme, Params}; use crate::transcript::{EncodedChallenge, TranscriptWrite}; use halo2_backend::plonk::{prover::Prover, ProvingKey}; -use halo2_frontend::circuit::{compile_circuit, WitnessCalculator}; -use halo2_frontend::plonk::Circuit; +use halo2_frontend::circuit::WitnessCalculator; +use halo2_frontend::plonk::{Circuit, ConstraintSystem}; use halo2_middleware::ff::{FromUniformBytes, WithSmallOrderMulGroup}; use halo2_middleware::zal::{ impls::{PlonkEngine, PlonkEngineConfig}, @@ -37,9 +37,35 @@ pub fn create_proof_with_engine< where Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>, { - create_proof_custom_with_engine::( - engine, params, pk, true, circuits, instances, rng, transcript, - ) + if circuits.len() != instances.len() { + return Err(Error::Backend(ErrorBack::InvalidInstances)); + } + + let mut cs = ConstraintSystem::default(); + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut cs, circuits[0].params()); + #[cfg(not(feature = "circuit-params"))] + let config = ConcreteCircuit::configure(&mut cs); + let cs = cs; + + 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()?) } /// This creates a proof for the provided `circuit` when given the public @@ -71,60 +97,6 @@ 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::<_, ConcreteCircuit>(params.k(), &circuits[0], compress_selectors)?; - 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::{ @@ -243,11 +215,10 @@ fn test_create_proof_custom() { let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); let engine = PlonkEngineConfig::build_default(); - create_proof_custom_with_engine::, ProverSHPLONK<_>, _, _, _, _, _>( + create_proof_with_engine::, ProverSHPLONK<_>, _, _, _, _, _>( engine, ¶ms, &pk, - compress_selectors, &[MyCircuit, MyCircuit], &[&[], &[]], OsRng, diff --git a/halo2_proofs/tests/compress_selectors.rs b/halo2_proofs/tests/compress_selectors.rs index bb3992f74e..2c246e972b 100644 --- a/halo2_proofs/tests/compress_selectors.rs +++ b/halo2_proofs/tests/compress_selectors.rs @@ -13,8 +13,8 @@ use halo2_backend::transcript::{ use halo2_middleware::zal::impls::{H2cEngine, PlonkEngineConfig}; use halo2_proofs::arithmetic::Field; use halo2_proofs::plonk::{ - create_proof_custom_with_engine, keygen_pk_custom, keygen_vk_custom, verify_proof, Advice, - Assigned, Circuit, Column, ConstraintSystem, Instance, Selector, + create_proof_with_engine, keygen_pk_custom, keygen_vk_custom, verify_proof, Advice, Assigned, + Circuit, Column, ConstraintSystem, Instance, Selector, }; use halo2_proofs::poly::commitment::ParamsProver; use halo2_proofs::poly::kzg::commitment::{KZGCommitmentScheme, ParamsKZG}; @@ -343,7 +343,6 @@ impl Circuit for MyCircuitCircuit { fn test_mycircuit( vk_keygen_compress_selectors: bool, pk_keygen_compress_selectors: bool, - proofgen_compress_selectors: bool, ) -> Result<(), halo2_proofs::plonk::Error> { let engine = PlonkEngineConfig::new() .set_curve::() @@ -373,19 +372,10 @@ fn test_mycircuit( .collect::>()); let mut transcript = Blake2bWrite::<_, G1Affine, Challenge255<_>>::init(vec![]); - create_proof_custom_with_engine::< - KZGCommitmentScheme, - ProverSHPLONK<'_, Bn256>, - _, - _, - _, - _, - _, - >( + create_proof_with_engine::, ProverSHPLONK<'_, Bn256>, _, _, _, _, _>( engine, ¶ms, &pk, - proofgen_compress_selectors, &[circuit], &[instances_slice], &mut rng, @@ -410,34 +400,24 @@ fn test_mycircuit( #[test] fn test_success() { - // keygen & proof generation both WITH compress - assert!(test_mycircuit(true, true, true).is_ok()); + // vk & pk keygen both WITH compress + assert!(test_mycircuit(true, true).is_ok()); - // keygen & proof generation both WITHOUT compress - assert!(test_mycircuit(false, false, false).is_ok()); + // vk & pk keygen both WITHOUT compress + assert!(test_mycircuit(false, false).is_ok()); } #[should_panic] #[test] fn test_failure_1() { - // vk_keygen WITH compress vs pk_keygen WITHOUT compress - assert!(test_mycircuit(false, true, true).is_err()); + // vk keygen WITH compress + // pk keygen WITHOUT compress + assert!(test_mycircuit(false, true).is_err()); } #[test] fn test_failure_2() { - // vk_keygen WITHOUT compress vs pk_keygen WITH compress - assert!(test_mycircuit(true, false, true).is_err()); -} - -#[test] -fn test_failure_3() { - // keygen WITHOUT compress vs proof_gen WITH compress - assert!(test_mycircuit(false, false, true).is_err()); -} - -#[test] -fn test_failure_4() { - // keygen WITH compress vs proof_gen WITHOUT compress - assert!(test_mycircuit(true, true, false).is_err()); + // vk keygen WITHOUT compress + // pk keygen WITH compress + assert!(test_mycircuit(true, false).is_err()); } From 46d1a852abea56e66db192e4bd2c8eb3ca6de047 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Fri, 10 May 2024 11:49:16 +0800 Subject: [PATCH 29/30] chore: some housekeeping --- halo2_proofs/tests/compress_selectors.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/halo2_proofs/tests/compress_selectors.rs b/halo2_proofs/tests/compress_selectors.rs index 2c246e972b..6df7616ed5 100644 --- a/halo2_proofs/tests/compress_selectors.rs +++ b/halo2_proofs/tests/compress_selectors.rs @@ -284,13 +284,13 @@ impl MyCircuitComposer for MyCircuitChip { } #[derive(Debug, Clone, Default)] -struct MyCircuitCircuit { +struct MyCircuit { x: Value, y: Value, constant: F, } -impl Circuit for MyCircuitCircuit { +impl Circuit for MyCircuit { type Config = MyCircuitConfig; type FloorPlanner = SimpleFloorPlanner; #[cfg(feature = "circuit-params")] @@ -349,7 +349,7 @@ fn test_mycircuit( .set_msm(H2cEngine::new()) .build(); let k = 4; - let circuit: MyCircuitCircuit = MyCircuitCircuit { + let circuit: MyCircuit = MyCircuit { x: Value::known(Fr::one()), y: Value::known(Fr::one()), constant: Fr::one(), @@ -359,10 +359,8 @@ fn test_mycircuit( let mut rng = BlockRng::new(OneNg {}); let params = ParamsKZG::::setup(k, &mut rng); let verifier_params = params.verifier_params(); - let vk = keygen_vk_custom(¶ms, &circuit, vk_keygen_compress_selectors) - .expect("keygen_vk should not fail"); - let pk = keygen_pk_custom(¶ms, vk.clone(), &circuit, pk_keygen_compress_selectors) - .expect("keygen_pk should not fail"); + let vk = keygen_vk_custom(¶ms, &circuit, vk_keygen_compress_selectors)?; + let pk = keygen_pk_custom(¶ms, vk.clone(), &circuit, pk_keygen_compress_selectors)?; // Proving let instances = vec![vec![Fr::one(), Fr::from_u128(3)]]; From 95c6bac5dea5c859e3b18f3137dbc7d5f790dab3 Mon Sep 17 00:00:00 2001 From: guorong009 Date: Fri, 10 May 2024 20:42:29 +0800 Subject: [PATCH 30/30] chore: add detailed comments --- halo2_proofs/tests/compress_selectors.rs | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/halo2_proofs/tests/compress_selectors.rs b/halo2_proofs/tests/compress_selectors.rs index 6df7616ed5..47a394aa09 100644 --- a/halo2_proofs/tests/compress_selectors.rs +++ b/halo2_proofs/tests/compress_selectors.rs @@ -396,6 +396,43 @@ fn test_mycircuit( .map_err(halo2_proofs::plonk::Error::Backend) } +/* + +How the `compress_selectors` works in `MyCircuit` under the hood: + +# compress = false + + selector `s_add` -> fixed `s_add` + - 1 when `s_add` enabled, 0 otherwise + + selector `s_mul` -> fixed `s_mul` + - 1 when `s_mul` enabled, 0 otherwise + + selector `s_cubed` -> fixed `s_cubed` + - 1 when `s_cubed` enabled, 0 otherwise + + Selector queries in expressions become the corresponding fixed column queries + at rotation 0. + + +# compress = true + + selector `s_add`, `s_mul` -> fixed `s_add_mul` + - 0 when `s_add` disabled and `s_mul` disabled + - 1 when only `s_add` enabled + - 2 when only `s_mul` enabled + + selector `s_cubed` -> fixed `s_cubed` + - 1 when `s_cubed` enabled, 0 otherwise + - NOTE: `s_cubed` is not compressed to avoid growing the max degree which is 3 + + Selector query for `s_add` becomes (`s_add_mul`)*(2 - `s_add_mul`) + Selector query for `s_mul` becomes (`s_add_mul`)*(1 - `s_add_mul`) + Selector query for `s_cubed` becomes the corresponding fixed column query + at rotation 0. + +*/ + #[test] fn test_success() { // vk & pk keygen both WITH compress