From 9346cef4a6adf531e545f8d40cd8c3db8ff88144 Mon Sep 17 00:00:00 2001 From: Stanislav Lyakhov Date: Wed, 3 Jan 2024 11:40:04 -0800 Subject: [PATCH 1/6] Add `Chip` implementation to endoscaling An implementer of `EndoscaleInstructions` should also implement `Chip`. We enforce this in the trait and provide an implementation for a new type, `EndoscaleChip`. --- halo2_gadgets/benches/endoscale.rs | 24 +++---- halo2_gadgets/src/endoscale.rs | 4 +- halo2_gadgets/src/endoscale/chip.rs | 103 ++++++++++++++++++++-------- 3 files changed, 87 insertions(+), 44 deletions(-) diff --git a/halo2_gadgets/benches/endoscale.rs b/halo2_gadgets/benches/endoscale.rs index df2dbe69d..0e4a1e90a 100644 --- a/halo2_gadgets/benches/endoscale.rs +++ b/halo2_gadgets/benches/endoscale.rs @@ -2,7 +2,7 @@ mod utilities; use halo2_gadgets::ecc::chip::NonIdentityEccPoint; use halo2_gadgets::endoscale::{ - chip::{CurveEndoscale, EndoscaleConfig}, + chip::{CurveEndoscale, EndoscaleChip, EndoscaleConfig}, EndoscaleInstructions, }; @@ -82,16 +82,14 @@ where ) -> Result<(), Error> { config.0.alg_2().table.load(&mut layouter)?; + let chip = EndoscaleChip::construct(config.0); + let bitstring = - config - .0 - .witness_bitstring(&mut layouter, &self.bitstring.transpose_array(), true)?; + chip.witness_bitstring(&mut layouter, &self.bitstring.transpose_array(), true)?; // Alg 1 (fixed base) let g_lagrange = ParamsIPA::::new(11).g_lagrange()[0]; - config - .0 - .endoscale_fixed_base(&mut layouter, bitstring.clone(), vec![g_lagrange])?; + chip.endoscale_fixed_base(&mut layouter, bitstring.clone(), vec![g_lagrange])?; // Alg 1 (variable base) let g_lagrange = layouter.assign_region( @@ -116,9 +114,7 @@ where )) }, )?; - config - .0 - .endoscale_var_base(&mut layouter, bitstring, vec![g_lagrange])?; + chip.endoscale_var_base(&mut layouter, bitstring, vec![g_lagrange])?; Ok(()) } @@ -183,14 +179,16 @@ where ) -> Result<(), Error> { config.alg_2().table.load(&mut layouter)?; + let chip = EndoscaleChip::construct(config); + let bitstring = - config.witness_bitstring(&mut layouter, &self.bitstring.transpose_array(), false)?; + chip.witness_bitstring(&mut layouter, &self.bitstring.transpose_array(), false)?; // Alg 2 with lookup - config.compute_endoscalar(&mut layouter, &bitstring[0])?; + chip.compute_endoscalar(&mut layouter, &bitstring[0])?; // Constrain bitstring - config.constrain_bitstring(&mut layouter, &bitstring[0], self.pub_input_rows.to_vec())?; + chip.constrain_bitstring(&mut layouter, &bitstring[0], self.pub_input_rows.to_vec())?; Ok(()) } diff --git a/halo2_gadgets/src/endoscale.rs b/halo2_gadgets/src/endoscale.rs index 0d1f37c98..265fe5662 100644 --- a/halo2_gadgets/src/endoscale.rs +++ b/halo2_gadgets/src/endoscale.rs @@ -1,7 +1,7 @@ //! Gadget for endoscaling. use ff::PrimeFieldBits; use halo2_proofs::{ - circuit::{AssignedCell, Layouter, Value}, + circuit::{AssignedCell, Chip, Layouter, Value}, plonk::{Assigned, Error}, }; use halo2curves::CurveAffine; @@ -11,7 +11,7 @@ use std::fmt::Debug; pub mod chip; /// Instructions to map bitstrings to and from endoscalars. -pub trait EndoscaleInstructions +pub trait EndoscaleInstructions: Chip + Debug where C::Base: PrimeFieldBits, { diff --git a/halo2_gadgets/src/endoscale/chip.rs b/halo2_gadgets/src/endoscale/chip.rs index 8455ca888..92a7710f9 100644 --- a/halo2_gadgets/src/endoscale/chip.rs +++ b/halo2_gadgets/src/endoscale/chip.rs @@ -5,7 +5,7 @@ use super::EndoscaleInstructions; use ff::PrimeFieldBits; use halo2_proofs::{ arithmetic::CurveAffine, - circuit::{AssignedCell, Layouter, Value}, + circuit::{AssignedCell, Chip, Layouter, Value}, plonk::{Advice, Assigned, Column, ConstraintSystem, Error, Instance}, }; use halo2curves::{pasta, pluto_eris}; @@ -54,6 +54,44 @@ where alg_2: Alg2Config, } +/// Chip implementing [`EndoscaleInstructions`] +#[derive(Debug)] +pub struct EndoscaleChip +where + C::Base: PrimeFieldBits, +{ + config: EndoscaleConfig, +} +impl EndoscaleChip +where + C::Base: PrimeFieldBits, +{ + /// Construct chip from inner config + pub fn construct(config: EndoscaleConfig) -> Self { + Self { config } + } + + /// Configure chip + pub fn configure( + meta: &mut ConstraintSystem, + // Advice columns not shared across alg_1 and alg_2 + advices: [Column; 8], + // Running sum column shared across alg_1 and alg_2 + running_sum: Column, + endoscalars: Column, + ) -> EndoscaleConfig { + EndoscaleConfig::configure(meta, advices, running_sum, endoscalars) + } + + fn alg_1(&self) -> &Alg1Config { + self.config.alg_1() + } + + fn alg_2(&self) -> &Alg2Config { + self.config.alg_2() + } +} + impl EndoscaleConfig where @@ -130,8 +168,25 @@ impl CurveEndoscale for pluto_eris::ErisAffine { const MAX_BITSTRING_LENGTH: usize = 442; } +impl Chip + for EndoscaleChip +where + C::Base: PrimeFieldBits, +{ + type Config = EndoscaleConfig; + type Loaded = (); + + fn config(&self) -> &Self::Config { + &self.config + } + + fn loaded(&self) -> &Self::Loaded { + &() + } +} + impl EndoscaleInstructions - for EndoscaleConfig + for EndoscaleChip where C::Base: PrimeFieldBits, { @@ -152,11 +207,11 @@ where bits.chunks(Self::MAX_BITSTRING_LENGTH) .map(|bits| { if for_base { - self.alg_1 + self.alg_1() .witness_bitstring(layouter.namespace(|| "alg 1"), bits) .map(Bitstring::Pair) } else { - self.alg_2 + self.alg_2() .witness_bitstring(layouter.namespace(|| "alg 2"), bits) .map(Bitstring::KBit) } @@ -171,7 +226,7 @@ where ) -> Result { // NB: This outputs a `Bitstring::Pair` variant compatible with Alg 1 only. // There is currently no equivalent method for Alg 2. - self.alg_1 + self.alg_1() .convert_to_bitstring( layouter.namespace(|| "alg 1: convert challenge to bitstring"), challenge, @@ -247,7 +302,7 @@ where ) -> Result<(), Error> { match bitstring { Bitstring::KBit(bitstring) => { - self.alg_2 + self.alg_2() .constrain_bitstring(layouter, bitstring, pub_input_rows) } // Constraining the bitstring only makes sense when the input is the `Bitstring::Kbit` @@ -260,7 +315,7 @@ where #[cfg(test)] mod tests { - use super::{EndoscaleConfig, EndoscaleInstructions}; + use super::{EndoscaleChip, EndoscaleConfig, EndoscaleInstructions}; use crate::ecc::chip::NonIdentityEccPoint; use crate::endoscale::chip::CurveEndoscale; use crate::utilities::cost_model::circuit_to_csv; @@ -343,17 +398,14 @@ mod tests { ) -> Result<(), Error> { config.0.alg_2().table.load(&mut layouter)?; - let bitstring = config.0.witness_bitstring( - &mut layouter, - &self.bitstring.transpose_array(), - true, - )?; + let chip = EndoscaleChip::construct(config.0); + + let bitstring = + chip.witness_bitstring(&mut layouter, &self.bitstring.transpose_array(), true)?; // Alg 1 (fixed base) let g_lagrange = ParamsIPA::::new(11).g_lagrange()[0]; - config - .0 - .endoscale_fixed_base(&mut layouter, bitstring.clone(), vec![g_lagrange])?; + chip.endoscale_fixed_base(&mut layouter, bitstring.clone(), vec![g_lagrange])?; // Alg 1 (variable base) let g_lagrange = layouter.assign_region( @@ -378,9 +430,7 @@ mod tests { )) }, )?; - config - .0 - .endoscale_var_base(&mut layouter, bitstring, vec![g_lagrange])?; + chip.endoscale_var_base(&mut layouter, bitstring, vec![g_lagrange])?; Ok(()) } @@ -446,21 +496,16 @@ mod tests { ) -> Result<(), Error> { config.alg_2().table.load(&mut layouter)?; - let bitstring = config.witness_bitstring( - &mut layouter, - &self.bitstring.transpose_array(), - false, - )?; + let chip = EndoscaleChip::construct(config); + + let bitstring = + chip.witness_bitstring(&mut layouter, &self.bitstring.transpose_array(), false)?; // Alg 2 with lookup - config.compute_endoscalar(&mut layouter, &bitstring[0])?; + chip.compute_endoscalar(&mut layouter, &bitstring[0])?; // Constrain bitstring - config.constrain_bitstring( - &mut layouter, - &bitstring[0], - self.pub_input_rows.to_vec(), - )?; + chip.constrain_bitstring(&mut layouter, &bitstring[0], self.pub_input_rows.to_vec())?; Ok(()) } From 0ae50dfa95c7007fccdf7b9752647e399ca82af3 Mon Sep 17 00:00:00 2001 From: Stanislav Lyakhov Date: Wed, 24 Jan 2024 21:03:19 -0800 Subject: [PATCH 2/6] Update `endoscaling_demo` to use `EndoscaleChip` Since `EndoscaleInstructions` is now implemented for `EndoscaleChip` instead of `EndoscaleConfig`, we need to update the example. We construct the chip from the config and only then use it to showcase endoscaling functionality. --- halo2_gadgets/examples/endoscaling_demo.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/halo2_gadgets/examples/endoscaling_demo.rs b/halo2_gadgets/examples/endoscaling_demo.rs index 5ae47e898..32e871ecb 100644 --- a/halo2_gadgets/examples/endoscaling_demo.rs +++ b/halo2_gadgets/examples/endoscaling_demo.rs @@ -20,7 +20,10 @@ use ff::Field; use group::{prime::PrimeCurveAffine, Curve}; use halo2_gadgets::{ ecc::chip::NonIdentityEccPoint, - endoscale::{chip::EndoscaleConfig, EndoscaleInstructions}, + endoscale::{ + chip::{EndoscaleChip, EndoscaleConfig}, + EndoscaleInstructions, + }, poseidon::{Pow5Chip, Pow5Config}, transcript::{chip::TranscriptChipP128Pow5T3, TranscriptInstructions}, }; @@ -404,16 +407,18 @@ impl Circuit for ChallengeMultiplicationCircuit { let challenge = chip.squeeze_challenge(layouter.namespace(|| "squeeze challenge"))?; layouter.constrain_instance(challenge.cell(), config.outputs, 0)?; - // Use the endoscale config to convert that challenge into a bitstring that can be used + // All endoscaling-related functionality is implemented by `EndoscaleChip` + // We can construct it directly from the corresponding config + let endoscale_chip = EndoscaleChip::construct(config.endoscale_config.clone()); + + // Use the endoscale chip to convert that challenge into a bitstring that can be used // with algorithm 1. - let bitstring = config - .endoscale_config - .convert_to_bitstring(&mut layouter, &challenge)?; + let bitstring = endoscale_chip.convert_to_bitstring(&mut layouter, &challenge)?; // Endoscale two points with algorithm 1. We used `var_base` because the "base" point -- our - // `known_point` -- isn't known to both parties. - let scaled_points: [_; 2] = config - .endoscale_config + // `known_point` -- isn't known to both parties. You use `endoscale_fixed_base` when the + // base point is a fixed or instance value. + let scaled_points: [_; 2] = endoscale_chip .endoscale_var_base( &mut layouter, vec![bitstring.clone(), bitstring], From 5b737a7a2e16a3f935ab7a9ae381a974af7b5f21 Mon Sep 17 00:00:00 2001 From: Stanislav Lyakhov Date: Fri, 26 Jan 2024 14:08:26 -0800 Subject: [PATCH 3/6] Leave out `configure` function from `EndoscaleConfig` Since we now have `EndoscaleChip`, we no longer need to configure through `EndoscaleConfig` and can do it directly. --- halo2_gadgets/src/endoscale/chip.rs | 65 ++++++++++++----------------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/halo2_gadgets/src/endoscale/chip.rs b/halo2_gadgets/src/endoscale/chip.rs index 92a7710f9..12b68cc61 100644 --- a/halo2_gadgets/src/endoscale/chip.rs +++ b/halo2_gadgets/src/endoscale/chip.rs @@ -71,7 +71,9 @@ where Self { config } } - /// Configure chip + /// Instantiates configuration used by [`EndoscaleChip`]. + /// The chip itself can be instantiated from [`EndoscaleConfig`] + /// using [`EndoscaleChip::construct`] pub fn configure( meta: &mut ConstraintSystem, // Advice columns not shared across alg_1 and alg_2 @@ -80,42 +82,6 @@ where running_sum: Column, endoscalars: Column, ) -> EndoscaleConfig { - EndoscaleConfig::configure(meta, advices, running_sum, endoscalars) - } - - fn alg_1(&self) -> &Alg1Config { - self.config.alg_1() - } - - fn alg_2(&self) -> &Alg2Config { - self.config.alg_2() - } -} - -impl - EndoscaleConfig -where - C::Base: PrimeFieldBits, -{ - /// Get the config for algorithm 1. - pub fn alg_1(&self) -> &Alg1Config { - &self.alg_1 - } - - /// Get the config for algorithm 2 . - pub fn alg_2(&self) -> &Alg2Config { - &self.alg_2 - } - - /// TODO: docs - pub fn configure( - meta: &mut ConstraintSystem, - // Advice columns not shared across alg_1 and alg_2 - advices: [Column; 8], - // Running sum column shared across alg_1 and alg_2 - running_sum: Column, - endoscalars: Column, - ) -> Self { let running_sum_pairs = { let q_pairs = meta.selector(); RunningSumConfig::configure(meta, q_pairs, running_sum) @@ -141,8 +107,31 @@ where advices[1], running_sum_chunks, ); + EndoscaleConfig { alg_1, alg_2 } + } + + fn alg_1(&self) -> &Alg1Config { + self.config.alg_1() + } + + fn alg_2(&self) -> &Alg2Config { + self.config.alg_2() + } +} + +impl + EndoscaleConfig +where + C::Base: PrimeFieldBits, +{ + /// Get the config for algorithm 1. + pub fn alg_1(&self) -> &Alg1Config { + &self.alg_1 + } - Self { alg_1, alg_2 } + /// Get the config for algorithm 2 . + pub fn alg_2(&self) -> &Alg2Config { + &self.alg_2 } } From f1607f7dbaa914f3f40f5a6beb0d35f10caaa90f Mon Sep 17 00:00:00 2001 From: Stanislav Lyakhov Date: Fri, 26 Jan 2024 14:12:16 -0800 Subject: [PATCH 4/6] Configure using `EndoscaleChip` instead of `EndoscaleConfig` Modify benches, tests, and examples to use the new enodscaling configuration API. --- halo2_gadgets/benches/endoscale.rs | 4 ++-- halo2_gadgets/examples/endoscaling_demo.rs | 2 +- halo2_gadgets/src/endoscale/chip.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/halo2_gadgets/benches/endoscale.rs b/halo2_gadgets/benches/endoscale.rs index 0e4a1e90a..9ed267f8f 100644 --- a/halo2_gadgets/benches/endoscale.rs +++ b/halo2_gadgets/benches/endoscale.rs @@ -70,7 +70,7 @@ where let endoscalars = meta.instance_column(); ( - EndoscaleConfig::configure(meta, advices, running_sum, endoscalars), + EndoscaleChip::configure(meta, advices, running_sum, endoscalars), running_sum, ) } @@ -169,7 +169,7 @@ where let running_sum = meta.advice_column(); let endoscalars = meta.instance_column(); - EndoscaleConfig::configure(meta, advices, running_sum, endoscalars) + EndoscaleChip::configure(meta, advices, running_sum, endoscalars) } fn synthesize( diff --git a/halo2_gadgets/examples/endoscaling_demo.rs b/halo2_gadgets/examples/endoscaling_demo.rs index 32e871ecb..bf56ed795 100644 --- a/halo2_gadgets/examples/endoscaling_demo.rs +++ b/halo2_gadgets/examples/endoscaling_demo.rs @@ -315,7 +315,7 @@ impl Circuit for ChallengeMultiplicationCircuit { ); // Set up config for endoscaling - let endoscale_config = EndoscaleConfig::configure( + let endoscale_config = EndoscaleChip::configure( meta, advices[0..8] .try_into() diff --git a/halo2_gadgets/src/endoscale/chip.rs b/halo2_gadgets/src/endoscale/chip.rs index 12b68cc61..17c768fdd 100644 --- a/halo2_gadgets/src/endoscale/chip.rs +++ b/halo2_gadgets/src/endoscale/chip.rs @@ -375,7 +375,7 @@ mod tests { let endoscalars = meta.instance_column(); ( - EndoscaleConfig::configure(meta, advices, running_sum, endoscalars), + EndoscaleChip::configure(meta, advices, running_sum, endoscalars), running_sum, ) } @@ -475,7 +475,7 @@ mod tests { let running_sum = meta.advice_column(); let endoscalars = meta.instance_column(); - EndoscaleConfig::configure(meta, advices, running_sum, endoscalars) + EndoscaleChip::configure(meta, advices, running_sum, endoscalars) } fn synthesize( From 4f6fd3fbf848f084084c367f829727a3437c7fb1 Mon Sep 17 00:00:00 2001 From: Stanislav Lyakhov Date: Fri, 26 Jan 2024 12:34:28 -0800 Subject: [PATCH 5/6] Create public functionality in `EndoscaleChip` to load internal tables Algorithm 2 of `EndoscaleChip` requires loading two lookup tables before usage. We add a convenient interface to load those directly through `EndoscaleChip` and document this functionality. --- halo2_gadgets/src/endoscale/chip.rs | 14 +++++++++++++- halo2_gadgets/src/endoscale/chip/alg_2.rs | 4 ++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/halo2_gadgets/src/endoscale/chip.rs b/halo2_gadgets/src/endoscale/chip.rs index 17c768fdd..921dc202e 100644 --- a/halo2_gadgets/src/endoscale/chip.rs +++ b/halo2_gadgets/src/endoscale/chip.rs @@ -62,7 +62,8 @@ where { config: EndoscaleConfig, } -impl EndoscaleChip + +impl EndoscaleChip where C::Base: PrimeFieldBits, { @@ -110,6 +111,17 @@ where EndoscaleConfig { alg_1, alg_2 } } + /// Setup table used for Algorithm 2 of endoscaling. + /// + /// Loads values [0..2^K) into one column and the endoscalars corresponding + /// to the K-bit bitstring encoding of each value into another column. + pub fn load( + &self, + layouter: &mut impl Layouter, + ) -> Result<>::Loaded, Error> { + self.alg_2().table.load(layouter) + } + fn alg_1(&self) -> &Alg1Config { self.config.alg_1() } diff --git a/halo2_gadgets/src/endoscale/chip/alg_2.rs b/halo2_gadgets/src/endoscale/chip/alg_2.rs index a5b651100..bada8153f 100644 --- a/halo2_gadgets/src/endoscale/chip/alg_2.rs +++ b/halo2_gadgets/src/endoscale/chip/alg_2.rs @@ -51,6 +51,10 @@ impl, const K: usize> TableConfig { } } + /// Fills the columns of [`TableConfig`] + /// + /// Loads values [0..2^K) into one column and the endoscalars corresponding + /// to the K-bit bitstring encoding of each value into another column. pub fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { layouter.assign_table( || "endoscalar_map", From f75446c4b861c83c220ea98d150fc8812543d4ad Mon Sep 17 00:00:00 2001 From: Stanislav Lyakhov Date: Fri, 26 Jan 2024 12:58:36 -0800 Subject: [PATCH 6/6] Use new `EndoscaleChip` API for table loading Tables can now be loaded directly from the chip without accessing the internal configs. --- halo2_gadgets/benches/endoscale.rs | 6 ++---- halo2_gadgets/src/endoscale/chip.rs | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/halo2_gadgets/benches/endoscale.rs b/halo2_gadgets/benches/endoscale.rs index 9ed267f8f..3a05e0b57 100644 --- a/halo2_gadgets/benches/endoscale.rs +++ b/halo2_gadgets/benches/endoscale.rs @@ -80,9 +80,8 @@ where config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - config.0.alg_2().table.load(&mut layouter)?; - let chip = EndoscaleChip::construct(config.0); + chip.load(&mut layouter)?; let bitstring = chip.witness_bitstring(&mut layouter, &self.bitstring.transpose_array(), true)?; @@ -177,9 +176,8 @@ where config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - config.alg_2().table.load(&mut layouter)?; - let chip = EndoscaleChip::construct(config); + chip.load(&mut layouter)?; let bitstring = chip.witness_bitstring(&mut layouter, &self.bitstring.transpose_array(), false)?; diff --git a/halo2_gadgets/src/endoscale/chip.rs b/halo2_gadgets/src/endoscale/chip.rs index 921dc202e..fac2d1762 100644 --- a/halo2_gadgets/src/endoscale/chip.rs +++ b/halo2_gadgets/src/endoscale/chip.rs @@ -397,9 +397,8 @@ mod tests { config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - config.0.alg_2().table.load(&mut layouter)?; - let chip = EndoscaleChip::construct(config.0); + chip.load(&mut layouter)?; let bitstring = chip.witness_bitstring(&mut layouter, &self.bitstring.transpose_array(), true)?; @@ -495,9 +494,8 @@ mod tests { config: Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { - config.alg_2().table.load(&mut layouter)?; - let chip = EndoscaleChip::construct(config); + chip.load(&mut layouter)?; let bitstring = chip.witness_bitstring(&mut layouter, &self.bitstring.transpose_array(), false)?;