1
1
//! Circuit to verify multiple ECDSA secp256k1 signatures.
2
- // This module uses two different types of chip configurations
3
- // - halo2-ecc's ecdsa chip, which is used
4
- // - to prove the correctness of secp signatures
5
- // - to compute the RLC in circuit
6
- // - halo2wrong's main gate chip: this is used for keccak lookup table
7
- //
8
2
//
3
+ // This module uses halo2-ecc's ecdsa chip
4
+ // - to prove the correctness of secp signatures
5
+ // - to compute the RLC in circuit
6
+ // - to perform keccak lookup table
9
7
//
10
8
// Naming notes:
11
9
// - *_be: Big-Endian bytes
@@ -34,12 +32,17 @@ use halo2_ecc::{
34
32
FieldChip ,
35
33
} ,
36
34
} ;
35
+ #[ cfg( feature = "onephase" ) ]
36
+ use halo2_proofs:: plonk:: FirstPhase ;
37
+ #[ cfg( not( feature = "onephase" ) ) ]
38
+ use halo2_proofs:: plonk:: SecondPhase ;
37
39
use halo2_proofs:: {
38
40
circuit:: { Cell , Layouter , Value } ,
39
41
halo2curves:: secp256k1:: { Fp , Fq , Secp256k1Affine } ,
40
- plonk:: { ConstraintSystem , Error , Selector } ,
42
+ plonk:: { Advice , Column , ConstraintSystem , Error , Fixed , Selector } ,
41
43
poly:: Rotation ,
42
44
} ;
45
+
43
46
use itertools:: Itertools ;
44
47
use keccak256:: plain:: Keccak ;
45
48
use log:: error;
@@ -138,45 +141,43 @@ impl<F: Field> Default for SignVerifyChip<F> {
138
141
/// SignVerify Configuration
139
142
#[ derive( Debug , Clone ) ]
140
143
pub ( crate ) struct SignVerifyConfig < F : Field > {
141
- // ECDSA
144
+ /// ECDSA
142
145
ecdsa_config : FpChip < F > ,
143
- // main_gate_config: MainGateConfig,
144
- // Keccak
146
+ /// A fixed column to store F::one
147
+ // FIXME: it is pretty wasteful to allocate a new fixed column just
148
+ // to store a constant value F::one.
149
+ fixed_column : Column < Fixed > ,
150
+ /// An advice column to store RLC witnesses
151
+ rlc_column : Column < Advice > ,
152
+ /// selector for keccak lookup table
145
153
q_keccak : Selector ,
146
154
keccak_table : KeccakTable ,
147
155
}
148
156
149
157
impl < F : Field > SignVerifyConfig < F > {
150
158
pub ( crate ) fn new ( meta : & mut ConstraintSystem < F > , keccak_table : KeccakTable ) -> Self {
159
+ #[ cfg( feature = "onephase" ) ]
160
+ let num_advice = [ calc_required_advices ( MAX_NUM_SIG ) ] ;
161
+ #[ cfg( not( feature = "onephase" ) ) ]
162
+ // need an additional phase 2 column/basic gate to hold the witnesses during RLC
163
+ // computations
164
+ let num_advice = [ calc_required_advices ( MAX_NUM_SIG ) , 1 ] ;
165
+
166
+ #[ cfg( feature = "onephase" ) ]
167
+ log:: info!( "configuring ECDSA chip with single phase" ) ;
168
+ #[ cfg( not( feature = "onephase" ) ) ]
169
+ log:: info!( "configuring ECDSA chip with multiple phases" ) ;
170
+
151
171
// halo2-ecc's ECDSA config
152
172
//
153
- // Create a new FpConfig chip for the following parameters
154
- // {"strategy":"Simple","degree":14,"num_advice":36,"num_lookup_advice":6,"
155
- // num_fixed":1," lookup_bits":13,"limb_bits":88,"num_limbs":3}
156
- //
157
173
// - num_advice: 36
158
- // - num_lookup_advice: 6
174
+ // - num_lookup_advice: 17
159
175
// - num_fixed: 1
160
176
// - lookup_bits: 13
161
177
// - limb_bits: 88
162
178
// - num_limbs: 3
163
179
//
164
180
// TODO: make those parameters tunable from a config file
165
-
166
- #[ cfg( feature = "onephase" ) ]
167
- // need one extra column to host the lookup cells and RLC cells
168
- let num_advice = [ calc_required_advices ( MAX_NUM_SIG ) + 1 ] ;
169
- #[ cfg( not( feature = "onephase" ) ) ]
170
- // need two phase 2 columns:
171
- // - one to hold the witnesses for RLC computations
172
- // - one to hold the actual results of RLCs, i.e., rlc_column
173
- let num_advice = [ calc_required_advices ( MAX_NUM_SIG ) , 2 ] ;
174
-
175
- #[ cfg( feature = "onephase" ) ]
176
- log:: info!( "configuring ECDSA chip with single phase" ) ;
177
- #[ cfg( not( feature = "onephase" ) ) ]
178
- log:: info!( "configuring ECDSA chip with multiple phases" ) ;
179
-
180
181
let ecdsa_config = FpConfig :: configure (
181
182
meta,
182
183
FpStrategy :: Simple ,
@@ -191,14 +192,18 @@ impl<F: Field> SignVerifyConfig<F> {
191
192
TOTAL_NUM_ROWS , // maximum k of the chip
192
193
) ;
193
194
194
- // halo2wrong's main gate config
195
+ // we are not really using this instance column
195
196
let instance = meta. instance_column ( ) ;
196
197
meta. enable_equality ( instance) ;
197
-
198
- #[ cfg( not( feature = "onephase" ) ) ]
199
- let rlc_column = ecdsa_config. range . gate . basic_gates [ 1 ] . last ( ) . unwrap ( ) . value ;
198
+ // we will need one fixed column to check if ecdsa is valid
199
+ let fixed_column = meta. fixed_column ( ) ;
200
+ meta. enable_equality ( fixed_column) ;
201
+ // we need one phase 2 column to store RLC results
200
202
#[ cfg( feature = "onephase" ) ]
201
- let rlc_column = ecdsa_config. range . gate . basic_gates [ 0 ] . last ( ) . unwrap ( ) . value ;
203
+ let rlc_column = meta. advice_column_in ( FirstPhase ) ;
204
+ #[ cfg( not( feature = "onephase" ) ) ]
205
+ let rlc_column = meta. advice_column_in ( SecondPhase ) ;
206
+ meta. enable_equality ( rlc_column) ;
202
207
203
208
// Ref. spec SignVerifyChip 1. Verify that keccak(pub_key_bytes) = pub_key_hash
204
209
// by keccak table lookup, where pub_key_bytes is built from the pub_key
@@ -210,7 +215,7 @@ impl<F: Field> SignVerifyConfig<F> {
210
215
// msg_hash and signature which is not constrained to match msg_hash_rlc nor
211
216
// the address.
212
217
// Layout:
213
- // | q_keccak | rlc |
218
+ // | q_keccak | rlc |
214
219
// | -------- | --------------- |
215
220
// | 1 | is_address_zero |
216
221
// | | pk_rlc |
@@ -241,6 +246,8 @@ impl<F: Field> SignVerifyConfig<F> {
241
246
ecdsa_config,
242
247
keccak_table,
243
248
q_keccak,
249
+ fixed_column,
250
+ rlc_column,
244
251
}
245
252
}
246
253
}
@@ -366,7 +373,6 @@ impl<F: Field> SignVerifyChip<F> {
366
373
// build ecc chip from Fp chip
367
374
let ecc_chip = EccChip :: < F , FpChip < F > > :: construct ( ecdsa_chip. clone ( ) ) ;
368
375
// build Fq chip from Fp chip
369
- // TODO: pass the parameters
370
376
let fq_chip = FqChip :: construct ( ecdsa_chip. range . clone ( ) , 88 , 3 , modulus :: < Fq > ( ) ) ;
371
377
372
378
log:: trace!( "r: {:?}" , sig_r) ;
@@ -425,38 +431,29 @@ impl<F: Field> SignVerifyChip<F> {
425
431
// | | pk_hash_rlc |
426
432
config. q_keccak . enable ( & mut ctx. region , * offset) ?;
427
433
428
- // this is a phase 2 column if "onephase" is not enabled
429
- #[ cfg( not( feature = "onephase" ) ) ]
430
- let rlc_column = config. ecdsa_config . range . gate . basic_gates [ 1 ]
431
- . last ( )
432
- . unwrap ( )
433
- . value ;
434
- #[ cfg( feature = "onephase" ) ]
435
- let rlc_column = config. ecdsa_config . range . gate . basic_gates [ 0 ]
436
- . last ( )
437
- . unwrap ( )
438
- . value ;
439
-
440
434
// is_address_zero
441
435
let tmp_cell = ctx. region . assign_advice (
442
436
|| "is_address_zero" ,
443
- rlc_column,
437
+ config . rlc_column ,
444
438
* offset,
445
439
|| is_address_zero. value ,
446
440
) ?;
447
441
ctx. region
448
442
. constrain_equal ( is_address_zero. cell , tmp_cell. cell ( ) ) ?;
449
443
450
444
// pk_rlc
451
- let tmp_cell =
452
- ctx. region
453
- . assign_advice ( || "pk_rlc" , rlc_column, * offset + 1 , || pk_rlc. value ) ?;
445
+ let tmp_cell = ctx. region . assign_advice (
446
+ || "pk_rlc" ,
447
+ config. rlc_column ,
448
+ * offset + 1 ,
449
+ || pk_rlc. value ,
450
+ ) ?;
454
451
ctx. region . constrain_equal ( pk_rlc. cell , tmp_cell. cell ( ) ) ?;
455
452
456
453
// pk_hash_rlc
457
454
let tmp_cell = ctx. region . assign_advice (
458
455
|| "pk_hash_rlc" ,
459
- rlc_column,
456
+ config . rlc_column ,
460
457
* offset + 2 ,
461
458
|| pk_hash_rlc. value ,
462
459
) ?;
@@ -719,12 +716,12 @@ impl<F: Field> SignVerifyChip<F> {
719
716
let mut first_pass = SKIP_FIRST_PASS ;
720
717
let ecdsa_chip = & config. ecdsa_config ;
721
718
722
- let ( deferred_keccak_check , assigned_sig_verifs) = layouter. assign_region (
719
+ let assigned_sig_verifs = layouter. assign_region (
723
720
|| "ecdsa chip verification" ,
724
721
|region| {
725
722
if first_pass {
726
723
first_pass = false ;
727
- return Ok ( ( vec ! [ ] , vec ! [ ] ) ) ;
724
+ return Ok ( vec ! [ ] ) ;
728
725
}
729
726
730
727
let mut ctx = ecdsa_chip. new_context ( region) ;
@@ -784,42 +781,17 @@ impl<F: Field> SignVerifyChip<F> {
784
781
& e. sig_is_valid ,
785
782
) ?;
786
783
assigned_sig_verifs. push ( assigned_sig_verif) ;
787
- deferred_keccak_check. push ( [
788
- AssignedValueNoTimer :: from ( to_be_keccak_checked[ 0 ] . clone ( ) ) ,
789
- AssignedValueNoTimer :: from ( to_be_keccak_checked[ 1 ] . clone ( ) ) ,
790
- AssignedValueNoTimer :: from ( to_be_keccak_checked[ 2 ] . clone ( ) ) ,
791
- ] ) ;
792
- }
793
-
794
- // IMPORTANT: this assigns all constants to the fixed columns
795
- // IMPORTANT: this copies cells to the lookup advice column to perform range
796
- // check lookups
797
- // This is not optional.
798
- let lookup_cells = ecdsa_chip. finalize ( & mut ctx) ;
799
- log:: info!( "total number of lookup cells: {}" , lookup_cells) ;
800
-
801
- for sig_verif in assigned_sig_verifs. iter ( ) {
802
- config. ecdsa_config . range . gate . assert_equal (
803
- & mut ctx,
804
- QuantumCell :: Existing ( & sig_verif. sig_is_valid . clone ( ) . into ( ) ) ,
805
- QuantumCell :: Constant ( F :: one ( ) ) ,
784
+ deferred_keccak_check. push (
785
+ to_be_keccak_checked
806
786
) ;
807
787
}
808
- ctx. print_stats ( & [ "Range" ] ) ;
809
- Ok ( ( deferred_keccak_check, assigned_sig_verifs) )
810
- } ,
811
- ) ?;
812
788
813
- layouter. assign_region (
814
- || "keccak lookup" ,
815
- |region| {
816
- let mut ctx = ecdsa_chip. new_context ( region) ;
789
+ // ================================================
790
+ // step 4: deferred keccak checks
791
+ // ================================================
817
792
let mut offset = 0 ;
818
793
for e in deferred_keccak_check. iter ( ) {
819
794
let [ is_address_zero, pk_rlc, pk_hash_rlc] = e;
820
- let is_address_zero = AssignedValue :: from ( is_address_zero) ;
821
- let pk_rlc = AssignedValue :: from ( pk_rlc) ;
822
- let pk_hash_rlc = AssignedValue :: from ( pk_hash_rlc) ;
823
795
self . enable_keccak_lookup (
824
796
config,
825
797
& mut ctx,
@@ -829,7 +801,16 @@ impl<F: Field> SignVerifyChip<F> {
829
801
& pk_hash_rlc,
830
802
) ?;
831
803
}
832
- Ok ( ( ) )
804
+
805
+ // IMPORTANT: this assigns all constants to the fixed columns
806
+ // IMPORTANT: this copies cells to the lookup advice column to perform range
807
+ // check lookups
808
+ // This is not optional.
809
+ let lookup_cells = ecdsa_chip. finalize ( & mut ctx) ;
810
+ log:: info!( "total number of lookup cells: {}" , lookup_cells) ;
811
+
812
+ ctx. print_stats ( & [ "Range" ] ) ;
813
+ Ok ( assigned_sig_verifs)
833
814
} ,
834
815
) ?;
835
816
@@ -944,24 +925,29 @@ impl<F: Field> SignVerifyChip<F> {
944
925
945
926
pub ( crate ) fn assert_sig_is_valid (
946
927
& self ,
947
- _config : & SignVerifyConfig < F > ,
928
+ config : & SignVerifyConfig < F > ,
948
929
layouter : & mut impl Layouter < F > ,
949
930
sig_verifs : & [ AssignedSignatureVerify < F > ] ,
950
931
) -> Result < ( ) , Error > {
951
- // let flex_gate_chip = &config.ecdsa_config.range.gate;
952
- for ( i, s) in sig_verifs. iter ( ) . enumerate ( ) {
953
- log:: trace!(
954
- "checking {}-th signature is valid: {:?}" ,
955
- i,
956
- s. sig_is_valid. value
957
- ) ;
958
- }
959
-
960
932
layouter. assign_region (
961
933
|| "ecdsa chip verification" ,
962
- |_region| {
963
- // not doing anything as the validity is already been checked
964
- // within assign() function
934
+ |mut region| {
935
+ let one = region. assign_fixed (
936
+ || "one" ,
937
+ config. fixed_column ,
938
+ 0 ,
939
+ || Value :: known ( F :: one ( ) ) ,
940
+ ) ?;
941
+
942
+ for ( i, sig_verif) in sig_verifs. iter ( ) . enumerate ( ) {
943
+ log:: trace!(
944
+ "checking {}-th signature is valid: {:?}" ,
945
+ i,
946
+ sig_verif. sig_is_valid. value
947
+ ) ;
948
+
949
+ region. constrain_equal ( sig_verif. sig_is_valid . cell , one. cell ( ) ) ?;
950
+ }
965
951
966
952
Ok ( ( ) )
967
953
} ,
0 commit comments