@ -168,11 +168,9 @@ where
s : & SignatureVar < C , GC > ,
q : & PublicKeyVar < C , GC > ,
) -> Result < Boolean < ConstraintF < C > > , SynthesisError > {
let s_s = s . s . clone ( ) ;
let sG = parameters
. generator
. scalar_mul_le ( s_ s . to_bits_le ( ) ? . iter ( ) ) ? ;
. scalar_mul_le ( s . s . to_bits_le ( ) ? . iter ( ) ) ? ;
// Note: in a circuit that aggregates multiple verifications, the hashing step could be
// done outside the signature verification, once for all 1 votes and once for all 0 votes,
@ -190,7 +188,63 @@ where
}
}
// example of circuit using BlindSigVerifyGadget
pub struct BlindSigBatchVerifyGadget <
C : ProjectiveCurve ,
GC : CurveVar < C , ConstraintF < C > > ,
const NUM_SIGS : usize ,
> where
for < 'a > & 'a GC : GroupOpsBounds < 'a , C , GC > ,
{
_params : Parameters < C > , // TODO review if needed, maybe delete
_gc : PhantomData < GC > ,
}
impl < C : ProjectiveCurve , GC : CurveVar < C , ConstraintF < C > > , const NUM_SIGS : usize >
BlindSigBatchVerifyGadget < C , GC , NUM_SIGS >
where
C : ProjectiveCurve ,
GC : CurveVar < C , ConstraintF < C > > ,
for < 'a > & 'a GC : GroupOpsBounds < 'a , C , GC > ,
ark_r1cs_std ::groups ::curves ::twisted_edwards ::AffineVar <
EdwardsParameters ,
FpVar < Fp256 < FqParameters > > ,
> : From < GC > ,
< C as ProjectiveCurve > ::BaseField : PrimeField ,
FpVar < < C as ProjectiveCurve > ::BaseField > : Mul < FpVar < Fp256 < FqParameters > > > ,
FpVar < < C as ProjectiveCurve > ::BaseField > : From < FpVar < Fp256 < FqParameters > > > ,
{
fn batch_verify (
parameters : & ParametersVar < C , GC > ,
poseidon_hash : & PoseidonGadget < ConstraintF < C > > ,
m : FpVar < ConstraintF < C > > ,
sigs : & [ SignatureVar < C , GC > ] ,
q : & PublicKeyVar < C , GC > ,
) -> Result < Boolean < ConstraintF < C > > , SynthesisError > {
// Note: in a circuit that aggregates multiple verifications, the hashing step could be
// done outside the signature verification, once for all 1 votes and once for all 0 votes,
// saving lots of constraints
let hm = poseidon_hash . hash ( & [ m ] ) ? ;
#[ allow(clippy::needless_range_loop) ]
for i in 0 . . NUM_SIGS {
let sG = parameters
. generator
. scalar_mul_le ( sigs [ i ] . s . to_bits_le ( ) ? . iter ( ) ) ? ;
let r = EdwardsVar ::from ( sigs [ i ] . r . clone ( ) ) ; // WIP
let rx_fpvar : FpVar < ConstraintF < C > > = r . x . into ( ) ;
// G * s == R + Q * (R.x * H(m))
let Q_rx_hm_0 = q . pub_key . scalar_mul_le ( rx_fpvar . to_bits_le ( ) ? . iter ( ) ) ? ;
let Q_rx_hm = Q_rx_hm_0 . scalar_mul_le ( hm . to_bits_le ( ) ? . iter ( ) ) ? ;
let RHS = sigs [ i ] . r . clone ( ) + Q_rx_hm ;
sG . enforce_equal ( & RHS ) ? ;
}
Ok ( Boolean ::TRUE )
}
}
// example of circuit using BlindSigVerifyGadget to verify a single blind signature
#[ derive(Clone) ]
pub struct BlindSigVerifyCircuit < C : ProjectiveCurve , GC : CurveVar < C , ConstraintF < C > > >
where
@ -255,6 +309,82 @@ where
}
}
// example of circuit using BlindSigVerifyGadget to verify a batch of blind signatures
#[ derive(Clone) ]
pub struct BlindSigBatchVerifyCircuit <
C : ProjectiveCurve ,
GC : CurveVar < C , ConstraintF < C > > ,
const NUM_SIGS : usize ,
> where
< C as ProjectiveCurve > ::BaseField : PrimeField ,
{
_group : PhantomData < * const GC > ,
pub params : Parameters < C > ,
pub poseidon_hash_native : poseidon_native ::Poseidon < ConstraintF < C > > ,
pub signatures : Option < Vec < Signature < C > > > ,
pub pub_key : Option < PublicKey < C > > ,
pub message : Option < ConstraintF < C > > ,
}
impl < C : ProjectiveCurve , GC : CurveVar < C , ConstraintF < C > > , const NUM_SIGS : usize >
ConstraintSynthesizer < ConstraintF < C > > for BlindSigBatchVerifyCircuit < C , GC , NUM_SIGS >
where
C : ProjectiveCurve ,
GC : CurveVar < C , ConstraintF < C > > ,
for < 'a > & 'a GC : GroupOpsBounds < 'a , C , GC > ,
ark_r1cs_std ::groups ::curves ::twisted_edwards ::AffineVar <
EdwardsParameters ,
FpVar < Fp256 < FqParameters > > ,
> : From < GC > ,
< C as ProjectiveCurve > ::BaseField : PrimeField ,
FpVar < < C as ProjectiveCurve > ::BaseField > : Mul < FpVar < Fp256 < FqParameters > > > ,
FpVar < < C as ProjectiveCurve > ::BaseField > : From < FpVar < Fp256 < FqParameters > > > ,
{
#[ tracing::instrument(target = " r1cs " , skip(self, cs)) ]
fn generate_constraints (
self ,
cs : ConstraintSystemRef < ConstraintF < C > > ,
) -> Result < ( ) , SynthesisError > {
let parameters =
ParametersVar ::new_constant ( ark_relations ::ns ! ( cs , "parameters" ) , & self . params ) ? ;
let pub_key =
PublicKeyVar ::< C , GC > ::new_input ( ark_relations ::ns ! ( cs , "public key" ) , | | {
self . pub_key . ok_or ( SynthesisError ::AssignmentMissing )
} ) ? ;
let m = FpVar ::< ConstraintF < C > > ::new_input ( ark_relations ::ns ! ( cs , "message" ) , | | {
self . message . ok_or ( SynthesisError ::AssignmentMissing )
} ) ? ;
let mut signatures : Vec < SignatureVar < C , GC > > = Vec ::new ( ) ;
for i in 0 . . NUM_SIGS {
let signature = self . signatures . as_ref ( ) . and_then ( | s | s . get ( i ) ) ;
let signature =
SignatureVar ::< C , GC > ::new_witness ( ark_relations ::ns ! ( cs , "signature" ) , | | {
signature . ok_or ( SynthesisError ::AssignmentMissing )
} ) ? ;
signatures . push ( signature ) ;
}
#[ allow(clippy::redundant_clone) ]
let poseidon_hash = PoseidonGadget ::< ConstraintF < C > > ::from_native (
& mut cs . clone ( ) ,
self . poseidon_hash_native ,
)
. unwrap ( ) ;
let v = BlindSigBatchVerifyGadget ::< C , GC , NUM_SIGS > ::batch_verify (
& parameters ,
& poseidon_hash ,
m ,
& signatures ,
& pub_key ,
) ? ;
v . enforce_equal ( & Boolean ::TRUE )
}
}
#[ cfg(test) ]
mod test {
use super ::* ;
@ -271,7 +401,7 @@ mod test {
// type Fr = <BabyJubJub as ProjectiveCurve>::ScalarField;
type S = BlindSigScheme < BabyJubJub > ;
fn generate_native_data (
fn generate_single_sig_ native_data (
poseidon_hash : & poseidon ::Poseidon < Fq > ,
) -> (
Parameters < BabyJubJub > ,
@ -292,13 +422,39 @@ mod test {
( params , pk , m , s )
}
fn generate_batch_sig_native_data (
poseidon_hash : & poseidon ::Poseidon < Fq > ,
n : usize ,
) -> (
Parameters < BabyJubJub > ,
PublicKey < BabyJubJub > ,
Fq ,
Vec < Signature < BabyJubJub > > ,
) {
let mut rng = ark_std ::test_rng ( ) ;
let params = S ::setup ( ) ;
let ( pk , sk ) = S ::keygen ( & params , & mut rng ) ;
let m = Fq ::from ( 1234 ) ;
let mut signatures : Vec < Signature < BabyJubJub > > = Vec ::new ( ) ;
for _ in 0 . . n {
let ( k , signer_r ) = S ::new_request_params ( & params , & mut rng ) ;
let ( m_blinded , u ) = S ::blind ( & params , & mut rng , & poseidon_hash , m , signer_r ) . unwrap ( ) ;
let s_blinded = S ::blind_sign ( sk , k , m_blinded ) ;
let s = S ::unblind ( s_blinded , u ) ;
let verified = S ::verify ( & params , & poseidon_hash , m , s . clone ( ) , pk ) ;
assert ! ( verified ) ;
signatures . push ( s ) ;
}
( params , pk , m , signatures )
}
#[ test ]
fn test_verify ( ) {
fn test_single_ verify ( ) {
let poseidon_params = poseidon_setup_params ::< Fq > ( Curve ::Bn254 , 5 , 3 ) ;
let poseidon_hash = poseidon ::Poseidon ::new ( poseidon_params ) ;
// create signature using native-rust lib
let ( params , pk , m , s ) = generate_native_data ( & poseidon_hash ) ;
let ( params , pk , m , s ) = generate_single_sig_ native_data ( & poseidon_hash ) ;
// use the constraint system to verify the signature
type SG = BlindSigVerifyGadget < BabyJubJub , BabyJubJubVar > ;
@ -327,12 +483,12 @@ mod test {
}
#[ test ]
fn test_constraint_system ( ) {
fn test_single_verify_ constraint_system ( ) {
let poseidon_params = poseidon_setup_params ::< Fq > ( Curve ::Bn254 , 5 , 3 ) ;
let poseidon_hash = poseidon ::Poseidon ::new ( poseidon_params ) ;
// create signature using native-rust lib
let ( params , pk , m , s ) = generate_native_data ( & poseidon_hash ) ;
let ( params , pk , m , s ) = generate_single_sig_ native_data ( & poseidon_hash ) ;
// use the constraint system to verify the signature
let circuit = BlindSigVerifyCircuit ::< BabyJubJub , BabyJubJubVar > {
@ -349,4 +505,29 @@ mod test {
assert ! ( is_satisfied ) ;
println ! ( "num_cnstraints={:?}" , cs . num_constraints ( ) ) ;
}
#[ test ]
fn test_batch_verify_constraint_system ( ) {
let poseidon_params = poseidon_setup_params ::< Fq > ( Curve ::Bn254 , 5 , 3 ) ;
let poseidon_hash = poseidon ::Poseidon ::new ( poseidon_params ) ;
// create signatures using native-rust lib
const NUM_SIGS : usize = 5 ;
let ( params , pk , m , sigs ) = generate_batch_sig_native_data ( & poseidon_hash , NUM_SIGS ) ;
// use the constraint system to verify the batch of signatures
let circuit = BlindSigBatchVerifyCircuit ::< BabyJubJub , BabyJubJubVar , NUM_SIGS > {
params ,
poseidon_hash_native : poseidon_hash ,
signatures : Some ( sigs ) ,
pub_key : Some ( pk ) ,
message : Some ( m ) ,
_group : PhantomData ,
} ;
let cs = ConstraintSystem ::< Fq > ::new_ref ( ) ;
circuit . generate_constraints ( cs . clone ( ) ) . unwrap ( ) ;
let is_satisfied = cs . is_satisfied ( ) . unwrap ( ) ;
assert ! ( is_satisfied ) ;
println ! ( "num_cnstraints={:?}" , cs . num_constraints ( ) ) ;
}
}