mirror of
https://github.com/arnaucube/ark-ec-blind-signatures.git
synced 2026-01-12 00:21:29 +01:00
Impl blind sigs batch verifiction r1cs constraints
This commit is contained in:
@@ -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(
|
||||
¶meters,
|
||||
&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(¶ms, &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(¶ms, &mut rng);
|
||||
let (m_blinded, u) = S::blind(¶ms, &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(¶ms, &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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user