Skip to content

Commit 52cd668

Browse files
authored
Merge pull request privacy-ethereum#178 from input-output-hk/dev-feature/gl-133-plonk-gate
Add generic arithmetic gate / Plonk gadget # Project Context This is a partial resolution of privacy-ethereum#58: that issue is concerned with creating a generic arithmetic gadget and using it where possible. Here we create the gadget, but only use it in a few places. Using it everywhere that makes sense will be a later PR. The corresponding Galois internal issue is [Galois#133](https://gitlab-ext.galois.com/iog-midnight/halo2/-/issues/133). # Issue Description See privacy-ethereum#58. This PR includes a generic arithmetic / Plonk gadget that provides an API that allows user to implement circuits without needing custom gates, or to directly work with layouters or regions. This is not a good match for unrolled loops (e.g. variable base scalar mul), where a custom gate gives a more efficient encoding. But for gadgets that are not used in a loop, the API provided here both simplifies the programming task and eases the review process. To demonstrate the API, this PR includes refactoring of three existing gadgets to eliminate all their custom gates: the power-of-2 range check, the generic range check, and the $k$-high-low decomposition. # Notes for Reviewers This PR includes several different implementations of the internals, i.e. of the custom gates. I've left these intermediate states in the Git history so that reviewers can see that trying to do something closer to what's in the original Plonk paper doesn't work as well as what we end up with. The main reasons for the mismatch with the original Plonk paper are: 1) vanilla Plonk selectors take values over the whole field, but Halo 2 selectors are binary; 2) Halo 2 has an optimization called "selector combining" that combines multiple binary selectors into a single non-binary selector behind the scenes, but for this optimization to trigger, the selectors must be used in a restricted way. See the commit messages along the way for details; I've attempted to keep the commits small and focused, with descriptive messages. # Update Via review feedback, we ended up with an implementation that is closer to the original Plonk paper, by using fixed columns to implement the selectors, instead of using the actual selector API. According to cost modeling, this is slightly more expensive than the implementation using simple selectors (via the standard the Halo2 selector API), where the selector combining optimization reduces them all to a single selector behind the scenes, but the difference is negligible.
2 parents 29b3798 + fb33783 commit 52cd668

File tree

62 files changed

+1046
-1000
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1046
-1000
lines changed

book/src/SUMMARY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
- [Fields](design/implementation/fields.md)
2828
- [Selector combining](design/implementation/selector-combining.md)
2929
- [Gadgets](design/gadgets.md)
30+
- [PLONK gate / generic arithmetic ops](design/gadgets/plonk-gate.md)
31+
- [Decomposition](design/gadgets/decomposition.md)
3032
- [Elliptic curve cryptography (ECC) Chip](design/gadgets/ecc-chip.md)
3133
- [Witnessing points](design/gadgets/ecc-chip/witnessing-points.md)
3234
- [Point Doubling](design/gadgets/ecc-chip/doubling.md)
@@ -35,7 +37,6 @@
3537
- [Variable-base scalar multiplication](design/gadgets/ecc-chip/var-base-scalar-mul.md)
3638
- [Sinsemilla](design/gadgets/sinsemilla.md)
3739
- [MerkleCRH](design/gadgets/sinsemilla/merkle-crh.md)
38-
- [Decomposition](design/gadgets/decomposition.md)
3940
- [SHA-256](design/gadgets/sha256.md)
4041
- [16-bit table chip](design/gadgets/sha256/table16.md)
4142
- [Double-and-add](design/gadgets/double-and-add.md)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# PLONK Gate / Generic Arithmetic Gadget
2+
3+
Halo 2 supports custom gates — see a description of the [Halo 2 arithmetization](/concepts/arithmetization.html) for more details — but in many cases, the constraints a gadget implementer needs can be built up from a few basic primitives, including arithmetic, equality, and constraining a value to equal some constant, without a need to resort to custom gates. In the vanilla PLONK, all of these basic operations are provided by the "PLONK gate", described in [Section 6 of the original PLONK paper](https://eprint.iacr.org/2019/953); indeed, that's all vanilla PLONK provides. The motivation for custom gates is to allow for more efficient constraint construction, in particular avoiding copy constraints, but the benefits mostly appear only when the custom gate is used many times in a loop, e.g. in [variable base scalar multiplication](/design/gadgets/ecc-chip/var-base-scalar-mul.html).
4+
5+
The point of `halo2_gadgets::utilities::plonk_gate` module is to provide a set of basic primitives that are similar to what a vanilla PLONK gate would provide, so that users can avoid creating custom gates when they just want to create simple constraints outside of a loop. For loops or large sets of repetitive constraints, using custom gates to get a more efficient representation should still be considered.
6+
7+
The following operations are provided, where in the case of arithmetic ops, the output cell is constrained to be in the appropriate relationship to the input cells:
8+
- Addition of cells
9+
- Subtraction of cells
10+
- Multiplication of cells
11+
- Equality between cells
12+
- Equality between a cell and the constant 0
13+
- Witnessing a value into a cell (no relationship between the input value and output cell is enforced)
14+
- Witnessing a constant into a cell
15+
16+
See the `halo2_gadgets::utilities::plonk_gate` module documentation for full details, including details of the implementation, which is rather different from the vanilla PLONK gate internally.

halo2_gadgets/benches/ecc_circuits/scalar_mul.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use halo2_gadgets::{
22
ecc::chip::{configs, EccPoint, EccScalarVarFullWidth, NonIdentityEccPoint},
3-
utilities::{high_low_decomp, lookup_range_check::LookupRangeCheckConfig, pow2_range_check},
3+
utilities::{
4+
high_low_decomp, lookup_range_check::LookupRangeCheckConfig, plonk_gate, pow2_range_check,
5+
},
46
};
57
use halo2_proofs::{
68
circuit::{Layouter, SimpleFloorPlanner, Value},
@@ -62,12 +64,14 @@ impl Circuit<pallas::Base> for ScalarMulCircuitSmall {
6264

6365
let fixed = meta.fixed_column();
6466
meta.enable_constant(fixed);
67+
let fixeds = [fixed, meta.fixed_column(), meta.fixed_column()];
6568

6669
let lookup_table = meta.lookup_table_column();
6770
let range_check = LookupRangeCheckConfig::configure(meta, advices[9], lookup_table);
68-
let pow2_config = pow2_range_check::Config::configure(meta, advices[9], range_check);
69-
let high_low_config =
70-
high_low_decomp::Config::configure(meta, pow2_config, advices[0], advices[1]);
71+
let plonk_config =
72+
plonk_gate::Config::configure(meta, advices[0..3].try_into().unwrap(), fixeds);
73+
let pow2_config = pow2_range_check::Config::configure(range_check, plonk_config);
74+
let high_low_config = high_low_decomp::Config::configure(plonk_config, pow2_config);
7175
let mul = configs::MulVarBaseConfig::configure(
7276
meta,
7377
add_config,

halo2_gadgets/benches/endoscale.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ where
6060
fn configure(meta: &mut ConstraintSystem<C::Base>) -> Self::Config {
6161
let constants = meta.fixed_column();
6262
meta.enable_constant(constants);
63-
63+
let fixeds = [constants, meta.fixed_column(), meta.fixed_column()];
6464
let advices = (0..8)
6565
.map(|_| meta.advice_column())
6666
.collect::<Vec<_>>()
@@ -70,7 +70,7 @@ where
7070
let endoscalars = meta.instance_column();
7171

7272
(
73-
EndoscaleChip::configure(meta, advices, running_sum, endoscalars),
73+
EndoscaleChip::configure(meta, advices, fixeds, running_sum, endoscalars),
7474
running_sum,
7575
)
7676
}
@@ -159,7 +159,7 @@ where
159159
fn configure(meta: &mut ConstraintSystem<C::Base>) -> Self::Config {
160160
let constants = meta.fixed_column();
161161
meta.enable_constant(constants);
162-
162+
let fixeds = [constants, meta.fixed_column(), meta.fixed_column()];
163163
let advices = (0..8)
164164
.map(|_| meta.advice_column())
165165
.collect::<Vec<_>>()
@@ -168,7 +168,7 @@ where
168168
let running_sum = meta.advice_column();
169169
let endoscalars = meta.instance_column();
170170

171-
EndoscaleChip::configure(meta, advices, running_sum, endoscalars)
171+
EndoscaleChip::configure(meta, advices, fixeds, running_sum, endoscalars)
172172
}
173173

174174
fn synthesize(

halo2_gadgets/benches/ipa_recursive_commitment.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use halo2_gadgets::{
4040
recursive_chip::DefaultRecursiveChip as RecursiveChip,
4141
recursive_circuits::ipa,
4242
transcript::{TranscriptInstructions, TranscriptReadInstructions},
43+
utilities::plonk_gate,
4344
};
4445

4546
use ecc_circuits::fixed_points::TestFixedBases;
@@ -138,8 +139,13 @@ where
138139
.unwrap();
139140
let instance = [meta.instance_column()];
140141
let lookup = meta.lookup_table_column();
142+
let plonk_config = plonk_gate::Config::configure(
143+
meta,
144+
advice[0..3].try_into().unwrap(),
145+
fixed[0..3].try_into().unwrap(),
146+
);
141147

142-
RecursiveChip::configure(meta, advice, fixed, instance, lookup)
148+
RecursiveChip::configure(meta, advice, fixed, instance, plonk_config, lookup)
143149
}
144150

145151
fn synthesize(

halo2_gadgets/benches/plonk.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use halo2_gadgets::ecc::chip::{BaseFieldElem, FixedPoint, FullScalar, ShortScala
66
use halo2_gadgets::ecc::FixedPoints;
77
use halo2_gadgets::recursive_chip::DefaultRecursiveChip as RecursiveChip;
88
use halo2_gadgets::recursive_circuits::verify_plonk_proof;
9+
use halo2_gadgets::utilities::plonk_gate;
910
use halo2_proofs::circuit::{Cell, Chip, Layouter, SimpleFloorPlanner, Value};
1011
use halo2_proofs::poly::{commitment::ParamsProver, Rotation};
1112
use halo2_proofs::poseidon::duplex::DuplexDomain;
@@ -383,8 +384,13 @@ fn criterion_benchmark(c: &mut Criterion) {
383384
.unwrap();
384385
let instance = [meta.instance_column()];
385386
let lookup = meta.lookup_table_column();
387+
let plonk_config = plonk_gate::Config::configure(
388+
meta,
389+
advice[0..3].try_into().unwrap(),
390+
fixed[0..3].try_into().unwrap(),
391+
);
386392

387-
RecursiveChip::configure(meta, advice, fixed, instance, lookup)
393+
RecursiveChip::configure(meta, advice, fixed, instance, plonk_config, lookup)
388394
}
389395

390396
fn synthesize(

halo2_gadgets/examples/endoscaling_demo.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ impl Circuit<pallas::Base> for ChallengeMultiplicationCircuit {
319319
advices[0..8]
320320
.try_into()
321321
.expect("won't fail because length is hardcoded"),
322+
fixeds[0..3].try_into().unwrap(),
322323
advices[8],
323324
instance,
324325
);
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
max_deg,rows,table_rows,advice_columns,lookups,permutations,column_queries,point_sets,proof_size
2-
6,56,1024,2,1,3,19,4,2336
2+
5,21,0,3,0,4,16,2,1600
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
name,degree,uses
2+
Plonk universal gate,4,10
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
max_deg,rows,table_rows,advice_columns,lookups,permutations,column_queries,point_sets,proof_size
2+
5,201,0,3,0,4,16,2,1792

0 commit comments

Comments
 (0)