Update spend function in the compact ecash
This commit is contained in:
@@ -4,18 +4,18 @@
|
||||
use std::ops::Neg;
|
||||
use std::time::Duration;
|
||||
|
||||
use bls12_381::{multi_miller_loop, G1Affine, G2Affine, G2Prepared, Scalar};
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use bls12_381::{G1Affine, G2Affine, G2Prepared, multi_miller_loop, Scalar};
|
||||
use criterion::{Criterion, criterion_group, criterion_main};
|
||||
use ff::Field;
|
||||
use group::{Curve, Group};
|
||||
use itertools::izip;
|
||||
use rand::seq::SliceRandom;
|
||||
|
||||
use nym_compact_ecash::setup::setup;
|
||||
use nym_compact_ecash::{
|
||||
aggregate_verification_keys, aggregate_wallets, generate_keypair_user, issue_verify,
|
||||
issue_wallet, ttp_keygen, withdrawal_request, PartialWallet, PayInfo, VerificationKeyAuth,
|
||||
issue_wallet, PartialWallet, PayInfo, ttp_keygen, VerificationKeyAuth, withdrawal_request,
|
||||
};
|
||||
use nym_compact_ecash::setup::setup;
|
||||
|
||||
#[allow(unused)]
|
||||
fn double_pairing(g11: &G1Affine, g21: &G2Affine, g12: &G1Affine, g22: &G2Affine) {
|
||||
@@ -142,7 +142,7 @@ fn bench_compact_ecash(c: &mut Criterion) {
|
||||
let mut rng = rand::thread_rng();
|
||||
let keypair = authorities_keypairs.choose(&mut rng).unwrap();
|
||||
group.bench_function(
|
||||
&format!("[Issuing Authority] issue_partial_wallet_with_L_{}", case.L,),
|
||||
&format!("[Issuing Authority] issue_partial_wallet_with_L_{}", case.L, ),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
issue_wallet(
|
||||
@@ -170,7 +170,7 @@ fn bench_compact_ecash(c: &mut Criterion) {
|
||||
let w = wallet_blinded_signatures.get(0).clone().unwrap();
|
||||
let vk = verification_keys_auth.get(0).clone().unwrap();
|
||||
group.bench_function(
|
||||
&format!("[Client] issue_verify_a_partial_wallet_with_L_{}", case.L,),
|
||||
&format!("[Client] issue_verify_a_partial_wallet_with_L_{}", case.L, ),
|
||||
|b| b.iter(|| issue_verify(&grparams, vk, &user_keypair.secret_key(), w, &req_info)),
|
||||
);
|
||||
|
||||
@@ -178,8 +178,8 @@ fn bench_compact_ecash(c: &mut Criterion) {
|
||||
wallet_blinded_signatures.iter(),
|
||||
verification_keys_auth.iter()
|
||||
)
|
||||
.map(|(w, vk)| issue_verify(&grparams, vk, &user_keypair.secret_key(), w, &req_info).unwrap())
|
||||
.collect();
|
||||
.map(|(w, vk)| issue_verify(&grparams, vk, &user_keypair.secret_key(), w, &req_info).unwrap())
|
||||
.collect();
|
||||
|
||||
// CLIENT BENCHMARK: aggregating all partial wallets
|
||||
group.bench_function(
|
||||
@@ -196,7 +196,7 @@ fn bench_compact_ecash(c: &mut Criterion) {
|
||||
&unblinded_wallet_shares,
|
||||
&req_info,
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
})
|
||||
},
|
||||
);
|
||||
@@ -209,7 +209,7 @@ fn bench_compact_ecash(c: &mut Criterion) {
|
||||
&unblinded_wallet_shares,
|
||||
&req_info,
|
||||
)
|
||||
.unwrap();
|
||||
.unwrap();
|
||||
|
||||
// SPENDING PHASE
|
||||
let pay_info = PayInfo { info: [6u8; 32] };
|
||||
@@ -259,6 +259,8 @@ fn bench_compact_ecash(c: &mut Criterion) {
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// BENCHMARK IDENTIFICATION
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_compact_ecash);
|
||||
|
||||
@@ -28,6 +28,16 @@ pub enum CompactEcashError {
|
||||
#[error("ZKP Proof related error: {0}")]
|
||||
RangeProofOutOfBound(String),
|
||||
|
||||
#[error("Identify Verification related error: {0}")]
|
||||
Identify(String),
|
||||
|
||||
#[error(
|
||||
"Deserailization error, expected at least {} bytes, got {}",
|
||||
min,
|
||||
actual
|
||||
)]
|
||||
DeserializationMinLength { min: usize, actual: usize },
|
||||
|
||||
#[error("Tried to deserialize {object} with bytes of invalid length. Expected {actual} < {} or {modulus_target} % {modulus} == 0")]
|
||||
DeserializationInvalidLength {
|
||||
actual: usize,
|
||||
|
||||
@@ -14,12 +14,12 @@ use crate::utils::{try_deserialize_g1_projective, try_deserialize_g2_projective}
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct SpendInstance {
|
||||
pub kappa: G2Projective,
|
||||
pub aa: G1Projective,
|
||||
pub cc: G1Projective,
|
||||
pub dd: G1Projective,
|
||||
pub ss: G1Projective,
|
||||
pub tt: G1Projective,
|
||||
pub kappa_l: G2Projective,
|
||||
pub aa: Vec<G1Projective>,
|
||||
pub ss: Vec<G1Projective>,
|
||||
pub tt: Vec<G1Projective>,
|
||||
pub kappa_k: Vec<G2Projective>,
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for SpendInstance {
|
||||
@@ -36,41 +36,132 @@ impl TryFrom<&[u8]> for SpendInstance {
|
||||
});
|
||||
}
|
||||
|
||||
let kappa_bytes = bytes[..96].try_into().unwrap();
|
||||
let mut j = 0;
|
||||
let kappa_bytes = bytes[j..j + 96].try_into().unwrap();
|
||||
let kappa = try_deserialize_g2_projective(
|
||||
&kappa_bytes,
|
||||
CompactEcashError::Deserialization("Failed to deserialize kappa".to_string()),
|
||||
)?;
|
||||
let aa_bytes = bytes[96..144].try_into().unwrap();
|
||||
let aa = try_deserialize_g1_projective(
|
||||
&aa_bytes,
|
||||
CompactEcashError::Deserialization("Failed to deserialize A".to_string()),
|
||||
)?;
|
||||
let cc_bytes = bytes[144..192].try_into().unwrap();
|
||||
j += 96;
|
||||
|
||||
let a_len = u64::from_le_bytes(bytes[j..j + 8].try_into().unwrap());
|
||||
j += 8;
|
||||
if bytes[j..].len() < a_len as usize * 48 {
|
||||
return Err(CompactEcashError::DeserializationMinLength {
|
||||
min: a_len as usize * 48,
|
||||
actual: bytes[j..].len(),
|
||||
});
|
||||
}
|
||||
|
||||
let mut aa = Vec::with_capacity(a_len as usize);
|
||||
for i in 0..a_len as usize {
|
||||
let start = j + i * 48;
|
||||
let end = start + 48;
|
||||
|
||||
let aa_elem_bytes = bytes[start..end].try_into().unwrap();
|
||||
let aa_elem = try_deserialize_g1_projective(
|
||||
&aa_elem_bytes,
|
||||
CompactEcashError::Deserialization(
|
||||
"Failed to deserialize compressed A values".to_string(),
|
||||
),
|
||||
)?;
|
||||
|
||||
aa.push(aa_elem)
|
||||
}
|
||||
j += j + a_len as usize * 48;
|
||||
|
||||
|
||||
let cc_bytes = bytes[j..j + 48].try_into().unwrap();
|
||||
let cc = try_deserialize_g1_projective(
|
||||
&cc_bytes,
|
||||
CompactEcashError::Deserialization("Failed to deserialize C".to_string()),
|
||||
)?;
|
||||
let dd_bytes = bytes[192..240].try_into().unwrap();
|
||||
j += 48;
|
||||
|
||||
let dd_bytes = bytes[j..j + 48].try_into().unwrap();
|
||||
let dd = try_deserialize_g1_projective(
|
||||
&dd_bytes,
|
||||
CompactEcashError::Deserialization("Failed to deserialize D".to_string()),
|
||||
)?;
|
||||
let ss_bytes = bytes[240..288].try_into().unwrap();
|
||||
let ss = try_deserialize_g1_projective(
|
||||
&ss_bytes,
|
||||
CompactEcashError::Deserialization("Failed to deserialize S".to_string()),
|
||||
)?;
|
||||
let tt_bytes = bytes[288..336].try_into().unwrap();
|
||||
let tt = try_deserialize_g1_projective(
|
||||
&tt_bytes,
|
||||
CompactEcashError::Deserialization("Failed to deserialize T".to_string()),
|
||||
)?;
|
||||
let kappa_l_bytes = bytes[336..432].try_into().unwrap();
|
||||
let kappa_l = try_deserialize_g2_projective(
|
||||
&kappa_l_bytes,
|
||||
CompactEcashError::Deserialization("Failed to deserialize kappa_l".to_string()),
|
||||
)?;
|
||||
j += 48;
|
||||
|
||||
let s_len = u64::from_le_bytes(bytes[j..j + 8].try_into().unwrap());
|
||||
j += 8;
|
||||
if bytes[j..].len() < s_len as usize * 48 {
|
||||
return Err(CompactEcashError::DeserializationMinLength {
|
||||
min: s_len as usize * 48,
|
||||
actual: bytes[j..].len(),
|
||||
});
|
||||
}
|
||||
|
||||
let mut ss = Vec::with_capacity(s_len as usize);
|
||||
for i in 0..s_len as usize {
|
||||
let start = j + i * 48;
|
||||
let end = start + 48;
|
||||
|
||||
let ss_elem_bytes = bytes[start..end].try_into().unwrap();
|
||||
let ss_elem = try_deserialize_g1_projective(
|
||||
&ss_elem_bytes,
|
||||
CompactEcashError::Deserialization(
|
||||
"Failed to deserialize compressed S values".to_string(),
|
||||
),
|
||||
)?;
|
||||
|
||||
ss.push(ss_elem)
|
||||
}
|
||||
j += j + s_len as usize * 48;
|
||||
|
||||
let t_len = u64::from_le_bytes(bytes[j..j + 8].try_into().unwrap());
|
||||
j += 8;
|
||||
if bytes[j..].len() < t_len as usize * 48 {
|
||||
return Err(CompactEcashError::DeserializationMinLength {
|
||||
min: t_len as usize * 48,
|
||||
actual: bytes[j..].len(),
|
||||
});
|
||||
}
|
||||
|
||||
let mut tt = Vec::with_capacity(t_len as usize);
|
||||
for i in 0..t_len as usize {
|
||||
let start = j + i * 48;
|
||||
let end = start + 48;
|
||||
|
||||
let tt_elem_bytes = bytes[start..end].try_into().unwrap();
|
||||
let tt_elem = try_deserialize_g1_projective(
|
||||
&tt_elem_bytes,
|
||||
CompactEcashError::Deserialization(
|
||||
"Failed to deserialize compressed T values".to_string(),
|
||||
),
|
||||
)?;
|
||||
|
||||
tt.push(tt_elem)
|
||||
}
|
||||
j += j + t_len as usize * 48;
|
||||
|
||||
|
||||
let kappa_k_len = u64::from_le_bytes(bytes[j..j + 8].try_into().unwrap());
|
||||
j += 8;
|
||||
if bytes[j..].len() < kappa_k_len as usize * 96 {
|
||||
return Err(CompactEcashError::DeserializationMinLength {
|
||||
min: kappa_k_len as usize * 96,
|
||||
actual: bytes[j..].len(),
|
||||
});
|
||||
}
|
||||
|
||||
let mut kappa_k = Vec::with_capacity(kappa_k_len as usize);
|
||||
for i in 0..kappa_k_len as usize {
|
||||
let start = j + i * 48;
|
||||
let end = start + 48;
|
||||
|
||||
let kappa_k_elem_bytes = bytes[start..end].try_into().unwrap();
|
||||
let kappa_k_elem = try_deserialize_g2_projective(
|
||||
&kappa_k_elem_bytes,
|
||||
CompactEcashError::Deserialization(
|
||||
"Failed to deserialize compressed kappa_k values".to_string(),
|
||||
),
|
||||
)?;
|
||||
|
||||
kappa_k.push(kappa_k_elem)
|
||||
}
|
||||
|
||||
Ok(SpendInstance {
|
||||
kappa,
|
||||
@@ -79,21 +170,37 @@ impl TryFrom<&[u8]> for SpendInstance {
|
||||
dd,
|
||||
ss,
|
||||
tt,
|
||||
kappa_l,
|
||||
kappa_k,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SpendInstance {
|
||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::with_capacity(2 * 96 + 5 * 48);
|
||||
let mut bytes: Vec<u8> = Default::default();
|
||||
bytes.extend_from_slice(self.kappa.to_bytes().as_ref());
|
||||
bytes.extend_from_slice(self.aa.to_bytes().as_ref());
|
||||
|
||||
bytes.extend_from_slice(&self.aa.len().to_le_bytes());
|
||||
for a in &self.aa {
|
||||
bytes.extend_from_slice(&a.to_affine().to_compressed());
|
||||
}
|
||||
|
||||
bytes.extend_from_slice(self.cc.to_bytes().as_ref());
|
||||
bytes.extend_from_slice(self.dd.to_bytes().as_ref());
|
||||
bytes.extend_from_slice(self.ss.to_bytes().as_ref());
|
||||
bytes.extend_from_slice(self.tt.to_bytes().as_ref());
|
||||
bytes.extend_from_slice(self.kappa_l.to_bytes().as_ref());
|
||||
|
||||
bytes.extend_from_slice(&self.ss.len().to_le_bytes());
|
||||
for s in &self.ss {
|
||||
bytes.extend_from_slice(&s.to_affine().to_compressed());
|
||||
}
|
||||
bytes.extend_from_slice(&self.tt.len().to_le_bytes());
|
||||
for t in &self.tt {
|
||||
bytes.extend_from_slice(&t.to_affine().to_compressed());
|
||||
}
|
||||
|
||||
bytes.extend_from_slice(&self.kappa_k.len().to_le_bytes());
|
||||
for k in &self.kappa_k {
|
||||
bytes.extend_from_slice(&k.to_affine().to_compressed());
|
||||
}
|
||||
bytes
|
||||
}
|
||||
}
|
||||
@@ -103,30 +210,30 @@ pub struct SpendWitness {
|
||||
pub attributes: Vec<Scalar>,
|
||||
// signature randomizing element
|
||||
pub r: Scalar,
|
||||
pub r_l: Scalar,
|
||||
pub l: Scalar,
|
||||
pub o_a: Scalar,
|
||||
pub o_c: Scalar,
|
||||
pub o_d: Scalar,
|
||||
pub mu: Scalar,
|
||||
pub lambda: Scalar,
|
||||
pub o_mu: Scalar,
|
||||
pub o_lambda: Scalar,
|
||||
pub lk: Vec<Scalar>,
|
||||
pub o_a: Vec<Scalar>,
|
||||
pub mu: Vec<Scalar>,
|
||||
pub lambda: Vec<Scalar>,
|
||||
pub o_mu: Vec<Scalar>,
|
||||
pub o_lambda: Vec<Scalar>,
|
||||
pub r_k: Vec<Scalar>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SpendProof {
|
||||
challenge: Scalar,
|
||||
response_r: Scalar,
|
||||
response_r_l: Scalar,
|
||||
response_l: Scalar,
|
||||
response_o_a: Scalar,
|
||||
response_r_l: Vec<Scalar>,
|
||||
response_l: Vec<Scalar>,
|
||||
response_o_a: Vec<Scalar>,
|
||||
response_o_c: Scalar,
|
||||
response_o_d: Scalar,
|
||||
response_mu: Scalar,
|
||||
response_lambda: Scalar,
|
||||
response_o_mu: Scalar,
|
||||
response_o_lambda: Scalar,
|
||||
response_mu: Vec<Scalar>,
|
||||
response_lambda: Vec<Scalar>,
|
||||
response_o_mu: Vec<Scalar>,
|
||||
response_o_lambda: Vec<Scalar>,
|
||||
response_attributes: Vec<Scalar>,
|
||||
}
|
||||
|
||||
@@ -136,7 +243,7 @@ impl SpendProof {
|
||||
instance: &SpendInstance,
|
||||
witness: &SpendWitness,
|
||||
verification_key: &VerificationKeyAuth,
|
||||
rr: Scalar,
|
||||
rr: &[Scalar],
|
||||
) -> Self {
|
||||
let grparams = params.grp();
|
||||
// generate random values to replace each witness
|
||||
@@ -145,15 +252,16 @@ impl SpendProof {
|
||||
let r_v = r_attributes[1];
|
||||
let r_t = r_attributes[2];
|
||||
let r_r = grparams.random_scalar();
|
||||
let r_r_l = grparams.random_scalar();
|
||||
let r_l = grparams.random_scalar();
|
||||
let r_o_a = grparams.random_scalar();
|
||||
let r_o_c = grparams.random_scalar();
|
||||
let r_o_d = grparams.random_scalar();
|
||||
let r_mu = grparams.random_scalar();
|
||||
let r_lambda = grparams.random_scalar();
|
||||
let r_o_mu = grparams.random_scalar();
|
||||
let r_o_lambda = grparams.random_scalar();
|
||||
|
||||
let r_r_lk = grparams.n_random_scalars(witness.r_k.len());
|
||||
let r_lk = grparams.n_random_scalars(witness.lk.len());
|
||||
let r_o_a = grparams.n_random_scalars(witness.o_a.len());
|
||||
let r_mu = grparams.n_random_scalars(witness.mu.len());
|
||||
let r_lambda = grparams.n_random_scalars(witness.lambda.len());
|
||||
let r_o_mu = grparams.n_random_scalars(witness.o_mu.len());
|
||||
let r_o_lambda = grparams.n_random_scalars(witness.o_lambda.len());
|
||||
|
||||
let g1 = *grparams.gen1();
|
||||
let gamma1 = *grparams.gamma1();
|
||||
@@ -172,14 +280,69 @@ impl SpendProof {
|
||||
.map(|(attr, beta_i)| beta_i * attr)
|
||||
.sum::<G2Projective>();
|
||||
|
||||
let zkcm_aa = g1 * r_o_a + gamma1 * r_l;
|
||||
let zkcm_cc = g1 * r_o_c + gamma1 * r_v;
|
||||
let zkcm_dd = g1 * r_o_d + gamma1 * r_t;
|
||||
let zkcm_ss = g1 * r_mu;
|
||||
let zkcm_gamma11 = (instance.aa + instance.cc + gamma1) * r_mu + g1 * r_o_mu;
|
||||
let zkcm_tt = g1 * r_sk + (g1 * rr) * r_lambda;
|
||||
let zkcm_gamma12 = (instance.aa + instance.dd + gamma1) * r_lambda + g1 * r_o_lambda;
|
||||
let zkcm_kappa_l = grparams.gen2() * r_r_l + params.pkRP().alpha + params.pkRP().beta * r_l;
|
||||
|
||||
let zkcm_aa: Vec<G1Projective> =
|
||||
r_o_a
|
||||
.iter()
|
||||
.zip(r_lk.iter()).map(|(r_o_a_k, r_l_k)| g1 * r_o_a_k + gamma1 * r_l_k)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_aa_bytes = zkcm_aa
|
||||
.iter()
|
||||
.map(|x| x.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_ss = r_mu.iter().map(|r_mu_k| g1 * r_mu_k).collect::<Vec<_>>();
|
||||
|
||||
let zkcm_ss_bytes = zkcm_ss
|
||||
.iter()
|
||||
.map(|x| x.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_tt = rr
|
||||
.iter()
|
||||
.zip(r_lambda.iter()).map(|(rr_k, r_lambda_k)| g1 * r_sk + (g1 * rr_k) * r_lambda_k).collect::<Vec<_>>();
|
||||
|
||||
let zkcm_tt_bytes = zkcm_tt
|
||||
.iter()
|
||||
.map(|x| x.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_gamma11 = instance.aa
|
||||
.iter()
|
||||
.zip(r_mu.iter())
|
||||
.zip(r_o_mu.iter())
|
||||
.map(|((aa_k, r_mu_k), r_o_mu_k)| (aa_k + instance.cc + gamma1) * r_mu_k + g1 * r_o_mu_k)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_gamma11_bytes = zkcm_gamma11
|
||||
.iter()
|
||||
.map(|x| x.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_gamma12 = instance.aa
|
||||
.iter()
|
||||
.zip(r_lambda.iter())
|
||||
.zip(r_o_lambda.iter())
|
||||
.map(|((aa_k, r_lambda_k), r_o_lambda_k)| (aa_k + instance.dd + gamma1) * r_lambda_k + g1 * r_o_lambda_k)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_gamma12_bytes = zkcm_gamma12
|
||||
.iter()
|
||||
.map(|x| x.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_kappa_k = r_lk.iter()
|
||||
.zip(r_r_lk.iter())
|
||||
.map(|(r_k, r_r_k)| params.pkRP().alpha + params.pkRP().beta * r_k + grparams.gen2() * r_r_k)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_kappa_k_bytes = zkcm_kappa_k
|
||||
.iter()
|
||||
.map(|x| x.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// compute the challenge
|
||||
let challenge = compute_challenge::<ChallengeDigest, _, _>(
|
||||
@@ -189,18 +352,14 @@ impl SpendProof {
|
||||
.chain(beta2_bytes.iter().map(|b| b.as_ref()))
|
||||
.chain(std::iter::once(instance.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_kappa.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_aa.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_cc.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_dd.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_ss.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_kappa_l.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(
|
||||
zkcm_gamma11.to_affine().to_bytes().as_ref(),
|
||||
))
|
||||
.chain(std::iter::once(zkcm_tt.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(
|
||||
zkcm_gamma12.to_affine().to_bytes().as_ref(),
|
||||
)),
|
||||
.chain(zkcm_aa_bytes.iter().map(|x| x.as_ref()))
|
||||
.chain(zkcm_ss_bytes.iter().map(|x| x.as_ref()))
|
||||
.chain(zkcm_kappa_k_bytes.iter().map(|x| x.as_ref()))
|
||||
.chain(zkcm_tt_bytes.iter().map(|x| x.as_ref()))
|
||||
.chain(zkcm_gamma11_bytes.iter().map(|x| x.as_ref()))
|
||||
.chain(zkcm_gamma12_bytes.iter().map(|x| x.as_ref())),
|
||||
);
|
||||
|
||||
// compute response for each witness
|
||||
@@ -210,16 +369,16 @@ impl SpendProof {
|
||||
&witness.attributes.iter().collect::<Vec<_>>(),
|
||||
);
|
||||
let response_r = produce_response(&r_r, &challenge, &witness.r);
|
||||
let response_r_l = produce_response(&r_r_l, &challenge, &witness.r_l);
|
||||
let response_l = produce_response(&r_l, &challenge, &witness.l);
|
||||
let response_o_a = produce_response(&r_o_a, &challenge, &witness.o_a);
|
||||
let response_r_l = produce_responses(&r_r_lk, &challenge, &witness.r_k);
|
||||
let response_l = produce_responses(&r_lk, &challenge, &witness.lk);
|
||||
let response_o_a = produce_responses(&r_o_a, &challenge, &witness.o_a);
|
||||
let response_o_c = produce_response(&r_o_c, &challenge, &witness.o_c);
|
||||
let response_o_d = produce_response(&r_o_d, &challenge, &witness.o_d);
|
||||
|
||||
let response_mu = produce_response(&r_mu, &challenge, &witness.mu);
|
||||
let response_lambda = produce_response(&r_lambda, &challenge, &witness.lambda);
|
||||
let response_o_mu = produce_response(&r_o_mu, &challenge, &witness.o_mu);
|
||||
let response_o_lambda = produce_response(&r_o_lambda, &challenge, &witness.o_lambda);
|
||||
let response_mu = produce_responses(&r_mu, &challenge, &witness.mu);
|
||||
let response_lambda = produce_responses(&r_lambda, &challenge, &witness.lambda);
|
||||
let response_o_mu = produce_responses(&r_o_mu, &challenge, &witness.o_mu);
|
||||
let response_o_lambda = produce_responses(&r_o_lambda, &challenge, &witness.o_lambda);
|
||||
|
||||
SpendProof {
|
||||
challenge,
|
||||
@@ -241,7 +400,7 @@ impl SpendProof {
|
||||
params: &Parameters,
|
||||
instance: &SpendInstance,
|
||||
verification_key: &VerificationKeyAuth,
|
||||
rr: Scalar,
|
||||
rr: &[Scalar],
|
||||
) -> bool {
|
||||
let grparams = params.grp();
|
||||
let g1 = *grparams.gen1();
|
||||
@@ -263,29 +422,87 @@ impl SpendProof {
|
||||
.map(|(attr, beta_i)| beta_i * attr)
|
||||
.sum::<G2Projective>();
|
||||
|
||||
let zkcm_aa =
|
||||
g1 * self.response_o_a + gamma1 * self.response_l + instance.aa * self.challenge;
|
||||
|
||||
let zkcm_aa = self.response_o_a
|
||||
.iter()
|
||||
.zip(self.response_l.iter())
|
||||
.zip(instance.aa.iter())
|
||||
.map(|((resp_o_a_k, resp_l_k), aa_k)| g1 * resp_o_a_k + gamma1 * resp_l_k + aa_k * self.challenge)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_aa_bytes = zkcm_aa
|
||||
.iter()
|
||||
.map(|x| x.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_cc = g1 * self.response_o_c
|
||||
+ gamma1 * self.response_attributes[1]
|
||||
+ instance.cc * self.challenge;
|
||||
let zkcm_dd = g1 * self.response_o_d
|
||||
+ gamma1 * self.response_attributes[2]
|
||||
+ instance.dd * self.challenge;
|
||||
let zkcm_ss = g1 * self.response_mu + instance.ss * self.challenge;
|
||||
let zkcm_gamma11 = (instance.aa + instance.cc + gamma1) * self.response_mu
|
||||
+ g1 * self.response_o_mu
|
||||
+ gamma1 * self.challenge;
|
||||
let zkcm_tt = g1 * self.response_attributes[0]
|
||||
+ (g1 * rr) * self.response_lambda
|
||||
+ instance.tt * self.challenge;
|
||||
let zkcm_gamma12 = (instance.aa + instance.dd + gamma1) * self.response_lambda
|
||||
+ g1 * self.response_o_lambda
|
||||
+ gamma1 * self.challenge;
|
||||
|
||||
let zkcm_kappa_l = instance.kappa_l * self.challenge
|
||||
+ grparams.gen2() * self.response_r_l
|
||||
+ params.pkRP().alpha * (Scalar::one() - self.challenge)
|
||||
+ params.pkRP().beta * self.response_l;
|
||||
let zkcm_ss = self.response_mu
|
||||
.iter()
|
||||
.zip(instance.ss.iter())
|
||||
.map(|(resp_mu_k, ss_k)| g1 * resp_mu_k + ss_k * self.challenge)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_ss_bytes = zkcm_ss
|
||||
.iter()
|
||||
.map(|x| x.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_tt = self.response_lambda
|
||||
.iter()
|
||||
.zip(rr.iter())
|
||||
.zip(instance.tt.iter())
|
||||
.map(|((resp_lambda_k, rr_k), tt_k)| g1 * self.response_attributes[0] + (g1 * rr_k) * resp_lambda_k + tt_k * self.challenge)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_tt_bytes = zkcm_tt
|
||||
.iter()
|
||||
.map(|x| x.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_gamma11 = instance.aa
|
||||
.iter()
|
||||
.zip(self.response_mu.iter())
|
||||
.zip(self.response_o_mu.iter())
|
||||
.map(|((aa_k, resp_mu_k), resp_o_mu_k)| (aa_k + instance.cc + gamma1) * resp_mu_k
|
||||
+ g1 * resp_o_mu_k + gamma1 * self.challenge)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_gamma11_bytes = zkcm_gamma11
|
||||
.iter()
|
||||
.map(|x| x.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
||||
let zkcm_gamma12 = instance.aa
|
||||
.iter()
|
||||
.zip(self.response_lambda.iter())
|
||||
.zip(self.response_o_lambda.iter())
|
||||
.map(|((aa_k, resp_lambda_k), resp_o_lambda_k)| (aa_k + instance.dd + gamma1) * resp_lambda_k
|
||||
+ g1 * resp_o_lambda_k + gamma1 * self.challenge)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_gamma12_bytes = zkcm_gamma12
|
||||
.iter()
|
||||
.map(|x| x.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_kappa_k = instance.kappa_k
|
||||
.iter()
|
||||
.zip(self.response_r_l.iter())
|
||||
.zip(self.response_l.iter())
|
||||
.map(|((kappa_k, resp_r_k), resp_r_l_k)| kappa_k * self.challenge + grparams.gen2() * resp_r_k + params.pkRP().alpha * (Scalar::one() - self.challenge) + params.pkRP().beta * resp_r_l_k)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_kappa_k_bytes = zkcm_kappa_k
|
||||
.iter()
|
||||
.map(|x| x.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// re-compute the challenge
|
||||
let challenge = compute_challenge::<ChallengeDigest, _, _>(
|
||||
@@ -295,18 +512,14 @@ impl SpendProof {
|
||||
.chain(beta2_bytes.iter().map(|b| b.as_ref()))
|
||||
.chain(std::iter::once(instance.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_kappa.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_aa.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_cc.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_dd.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_ss.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_kappa_l.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(
|
||||
zkcm_gamma11.to_affine().to_bytes().as_ref(),
|
||||
))
|
||||
.chain(std::iter::once(zkcm_tt.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(
|
||||
zkcm_gamma12.to_affine().to_bytes().as_ref(),
|
||||
)),
|
||||
.chain(zkcm_aa_bytes.iter().map(|x| x.as_ref()))
|
||||
.chain(zkcm_ss_bytes.iter().map(|x| x.as_ref()))
|
||||
.chain(zkcm_kappa_k_bytes.iter().map(|x| x.as_ref()))
|
||||
.chain(zkcm_tt_bytes.iter().map(|x| x.as_ref()))
|
||||
.chain(zkcm_gamma11_bytes.iter().map(|x| x.as_ref()))
|
||||
.chain(zkcm_gamma12_bytes.iter().map(|x| x.as_ref())),
|
||||
);
|
||||
|
||||
challenge == self.challenge
|
||||
@@ -391,33 +604,34 @@ mod tests {
|
||||
// randomise the signature associated with value l
|
||||
let (sign_l_prime, r_l) = sign_l.randomise(grparams);
|
||||
// compute kappa_l
|
||||
let kappa_l =
|
||||
let kappa_k =
|
||||
grparams.gen2() * r_l + params.pkRP().alpha + params.pkRP().beta * Scalar::from(l);
|
||||
|
||||
let instance = SpendInstance {
|
||||
kappa,
|
||||
aa,
|
||||
aa: vec![aa],
|
||||
cc,
|
||||
dd,
|
||||
ss,
|
||||
tt,
|
||||
kappa_l,
|
||||
ss: vec![ss],
|
||||
tt: vec![tt],
|
||||
kappa_k: vec![kappa_k],
|
||||
};
|
||||
|
||||
let witness = SpendWitness {
|
||||
attributes,
|
||||
r,
|
||||
r_l,
|
||||
l: Scalar::from(l),
|
||||
o_a,
|
||||
o_c,
|
||||
o_d,
|
||||
mu,
|
||||
lambda,
|
||||
o_mu,
|
||||
o_lambda,
|
||||
lk: vec![Scalar::from(l)],
|
||||
o_a: vec![o_a],
|
||||
mu: vec![mu],
|
||||
lambda: vec![lambda],
|
||||
o_mu: vec![o_mu],
|
||||
o_lambda: vec![o_lambda],
|
||||
r_k: vec![r_l],
|
||||
};
|
||||
let zk_proof = SpendProof::construct(¶ms, &instance, &witness, &verification_key, rr);
|
||||
assert!(zk_proof.verify(¶ms, &instance, &verification_key, rr))
|
||||
|
||||
let zk_proof = SpendProof::construct(¶ms, &instance, &witness, &verification_key, &[rr]);
|
||||
assert!(zk_proof.verify(¶ms, &instance, &verification_key, &[rr]))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,109 @@
|
||||
use crate::error::Result;
|
||||
use crate::error::{CompactEcashError, Result};
|
||||
use crate::PayInfo;
|
||||
use crate::scheme::keygen::PublicKeyUser;
|
||||
use crate::scheme::Payment;
|
||||
|
||||
pub fn identify(pay1: Payment, pay2: Payment) -> Result<PublicKeyUser> {
|
||||
// TODO: We should include here the check for S and payInfo
|
||||
let pk_user = (pay2.tt * pay1.rr - pay1.tt * pay2.rr) * ((pay1.rr - pay2.rr).invert().unwrap());
|
||||
Ok(PublicKeyUser { pk: pk_user })
|
||||
pub enum IdentifyResult {
|
||||
NotADuplicatePayment,
|
||||
DuplicatePayInfo(PayInfo),
|
||||
DoubleSpendingPublicKeys(PublicKeyUser),
|
||||
|
||||
}
|
||||
|
||||
pub fn identify(pay1: Payment, pay2: Payment, pay_info1: PayInfo, pay_info2: PayInfo) -> Result<IdentifyResult> {
|
||||
let mut duplicate_serial_numbers: Vec<(u64, u64)> = Default::default();
|
||||
for k in 0..pay1.vv {
|
||||
for j in 0..pay2.vv {
|
||||
if pay1.ss[k as usize] == pay2.ss[j as usize] {
|
||||
duplicate_serial_numbers.push((k, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
if duplicate_serial_numbers.is_empty() {
|
||||
return Ok(IdentifyResult::NotADuplicatePayment);
|
||||
} else {
|
||||
if pay_info1 == pay_info2 {
|
||||
return Ok(IdentifyResult::DuplicatePayInfo(pay_info1));
|
||||
} else {
|
||||
for elem in duplicate_serial_numbers.iter() {
|
||||
let k = elem.0 as usize;
|
||||
let j = elem.1 as usize;
|
||||
let pk_user = (pay2.tt[j] * pay1.rr[k] - pay1.tt[k] * pay2.rr[j]) * ((pay1.rr[k] - pay2.rr[j]).invert().unwrap());
|
||||
return Ok(IdentifyResult::DoubleSpendingPublicKeys(PublicKeyUser { pk: pk_user.clone() }));
|
||||
}
|
||||
}
|
||||
return Err(CompactEcashError::Identify(
|
||||
"A duplicate serial number was detected, the pay_info1 and pay_info2 are different, but we failed to identify the double-spending public key".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use itertools::izip;
|
||||
|
||||
use crate::{aggregate_verification_keys, aggregate_wallets, generate_keypair_user, issue_verify, issue_wallet, PartialWallet, PayInfo, ttp_keygen, VerificationKeyAuth, withdrawal_request};
|
||||
use crate::scheme::setup::setup;
|
||||
|
||||
#[test]
|
||||
fn duplicate_payments_with_the_same_pay_info() {
|
||||
let L = 32;
|
||||
let params = setup(L);
|
||||
let grparams = params.grp();
|
||||
let user_keypair = generate_keypair_user(&grparams);
|
||||
|
||||
let (req, req_info) = withdrawal_request(grparams, &user_keypair.secret_key()).unwrap();
|
||||
let authorities_keypairs = ttp_keygen(&grparams, 2, 3).unwrap();
|
||||
|
||||
let verification_keys_auth: Vec<VerificationKeyAuth> = authorities_keypairs
|
||||
.iter()
|
||||
.map(|keypair| keypair.verification_key())
|
||||
.collect();
|
||||
|
||||
let verification_key = aggregate_verification_keys(&verification_keys_auth, Some(&[1, 2, 3])).unwrap();
|
||||
|
||||
let mut wallet_blinded_signatures = Vec::new();
|
||||
for auth_keypair in authorities_keypairs {
|
||||
let blind_signature = issue_wallet(
|
||||
&grparams,
|
||||
auth_keypair.secret_key(),
|
||||
user_keypair.public_key(),
|
||||
&req,
|
||||
);
|
||||
wallet_blinded_signatures.push(blind_signature.unwrap());
|
||||
}
|
||||
|
||||
let unblinded_wallet_shares: Vec<PartialWallet> = izip!(
|
||||
wallet_blinded_signatures.iter(),
|
||||
verification_keys_auth.iter()
|
||||
)
|
||||
.map(|(w, vk)| issue_verify(&grparams, vk, &user_keypair.secret_key(), w, &req_info).unwrap())
|
||||
.collect();
|
||||
|
||||
// Aggregate partial wallets
|
||||
let aggr_wallet = aggregate_wallets(
|
||||
&grparams,
|
||||
&verification_key,
|
||||
&user_keypair.secret_key(),
|
||||
&unblinded_wallet_shares,
|
||||
&req_info,
|
||||
).unwrap();
|
||||
|
||||
// Let's try to spend some coins
|
||||
let pay_info = PayInfo { info: [6u8; 32] };
|
||||
let spend_vv = 1;
|
||||
|
||||
let (payment1, upd_wallet) = aggr_wallet.spend(
|
||||
¶ms,
|
||||
&verification_key,
|
||||
&user_keypair.secret_key(),
|
||||
&pay_info,
|
||||
false,
|
||||
spend_vv,
|
||||
).unwrap();
|
||||
|
||||
assert!(payment1
|
||||
.spend_verify(¶ms, &verification_key, &pay_info, spend_vv)
|
||||
.unwrap());
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,14 @@ use std::convert::TryInto;
|
||||
use bls12_381::{G1Projective, G2Prepared, G2Projective, Scalar};
|
||||
use group::{Curve, Group};
|
||||
|
||||
use crate::Attribute;
|
||||
use crate::error::{CompactEcashError, Result};
|
||||
use crate::proofs::proof_spend::{SpendInstance, SpendProof, SpendWitness};
|
||||
use crate::scheme::keygen::{SecretKeyUser, VerificationKeyAuth};
|
||||
use crate::scheme::setup::{GroupParameters, Parameters};
|
||||
use crate::utils::{
|
||||
check_bilinear_pairing, hash_to_scalar, try_deserialize_g1_projective, Signature, SignerIndex,
|
||||
check_bilinear_pairing, hash_to_scalar, Signature, SignerIndex, try_deserialize_g1_projective,
|
||||
};
|
||||
use crate::Attribute;
|
||||
|
||||
pub mod aggregation;
|
||||
pub mod identify;
|
||||
@@ -74,8 +74,9 @@ impl Wallet {
|
||||
sk_user: &SecretKeyUser,
|
||||
pay_info: &PayInfo,
|
||||
bench_flag: bool,
|
||||
spend_vv: u64,
|
||||
) -> Result<(Payment, &Self)> {
|
||||
if self.l() > params.L() {
|
||||
if self.l() + spend_vv > params.L() {
|
||||
return Err(CompactEcashError::Spend(
|
||||
"The counter l is higher than max L".to_string(),
|
||||
));
|
||||
@@ -94,87 +95,124 @@ impl Wallet {
|
||||
sign_blinding_factor,
|
||||
);
|
||||
|
||||
// pick random openings o_a, o_c, o_d
|
||||
let o_a = grparams.random_scalar();
|
||||
// pick random openings o_c, o_d
|
||||
let o_c = grparams.random_scalar();
|
||||
let o_d = grparams.random_scalar();
|
||||
|
||||
// compute commitments A, C, D
|
||||
let aa = grparams.gen1() * o_a + grparams.gamma1() * Scalar::from(self.l());
|
||||
// compute commitments C, D
|
||||
let cc = grparams.gen1() * o_c + grparams.gamma1() * self.v();
|
||||
let dd = grparams.gen1() * o_d + grparams.gamma1() * self.t();
|
||||
|
||||
// compute hash of the payment info
|
||||
let rr = hash_to_scalar(pay_info.info);
|
||||
|
||||
// evaluate the pseudorandom functions
|
||||
let ss = pseudorandom_fgv(&grparams, self.v(), self.l());
|
||||
let tt =
|
||||
grparams.gen1() * sk_user.sk + pseudorandom_fgt(&grparams, self.t(), self.l()) * rr;
|
||||
let mut aa: Vec<G1Projective> = Default::default();
|
||||
let mut ss: Vec<G1Projective> = Default::default();
|
||||
let mut tt: Vec<G1Projective> = Default::default();
|
||||
let mut rr: Vec<Scalar> = Default::default();
|
||||
let mut o_a: Vec<Scalar> = Default::default();
|
||||
let mut o_mu: Vec<Scalar> = Default::default();
|
||||
let mut mu: Vec<Scalar> = Default::default();
|
||||
let mut o_lambda: Vec<Scalar> = Default::default();
|
||||
let mut lambda: Vec<Scalar> = Default::default();
|
||||
let mut r_k_vec: Vec<Scalar> = Default::default();
|
||||
let mut kappa_k_vec: Vec<G2Projective> = Default::default();
|
||||
let mut sign_lk_prime_vec: Vec<Signature> = Default::default();
|
||||
let mut lk: Vec<Scalar> = Default::default();
|
||||
|
||||
// compute values mu, o_mu, lambda, o_lambda
|
||||
let mu: Scalar = (self.v() + Scalar::from(self.l()) + Scalar::from(1))
|
||||
.invert()
|
||||
.unwrap();
|
||||
let o_mu = ((o_a + o_c) * mu).neg();
|
||||
let lambda = (self.t() + Scalar::from(self.l()) + Scalar::from(1))
|
||||
.invert()
|
||||
.unwrap();
|
||||
let o_lambda = ((o_a + o_d) * lambda).neg();
|
||||
for k in 0..spend_vv {
|
||||
lk.push(Scalar::from(self.l() + k));
|
||||
|
||||
// compute hashes R_k of the payment info
|
||||
let rr_k = hash_to_scalar(pay_info.info);
|
||||
rr.push(rr_k);
|
||||
|
||||
let o_a_k = grparams.random_scalar();
|
||||
o_a.push(o_a_k);
|
||||
let aa_k = grparams.gen1() * o_a_k + grparams.gamma1() * Scalar::from(self.l() + k);
|
||||
aa.push(aa_k);
|
||||
|
||||
// evaluate the pseudorandom functions
|
||||
let ss_k = pseudorandom_fgv(&grparams, self.v(), self.l() + k);
|
||||
ss.push(ss_k);
|
||||
let tt_k =
|
||||
grparams.gen1() * sk_user.sk + pseudorandom_fgt(&grparams, self.t(), self.l() + k) * rr_k;
|
||||
tt.push(tt_k);
|
||||
|
||||
// compute values mu, o_mu, lambda, o_lambda
|
||||
let mu_k: Scalar = (self.v() + Scalar::from(self.l() + k) + Scalar::from(1))
|
||||
.invert()
|
||||
.unwrap();
|
||||
mu.push(mu_k);
|
||||
|
||||
let o_mu_k = ((o_a_k + o_c) * mu_k).neg();
|
||||
o_mu.push(o_mu_k);
|
||||
|
||||
let lambda_k = (self.t() + Scalar::from(self.l() + k) + Scalar::from(1))
|
||||
.invert()
|
||||
.unwrap();
|
||||
lambda.push(lambda_k);
|
||||
|
||||
let o_lambda_k = ((o_a_k + o_d) * lambda_k).neg();
|
||||
o_lambda.push(o_lambda_k);
|
||||
|
||||
// parse the signature associated with value l+k
|
||||
let sign_lk = params.get_sign_by_idx(self.l() + k)?;
|
||||
// randomise the signature associated with value l+k
|
||||
let (sign_lk_prime, r_k) = sign_lk.randomise(grparams);
|
||||
sign_lk_prime_vec.push(sign_lk_prime);
|
||||
r_k_vec.push(r_k);
|
||||
// compute kappa_k
|
||||
let kappa_k = grparams.gen2() * r_k
|
||||
+ params.pkRP().alpha
|
||||
+ params.pkRP().beta * Scalar::from(self.l() + k);
|
||||
kappa_k_vec.push(kappa_k);
|
||||
}
|
||||
|
||||
// parse the signature associated with value l
|
||||
let sign_l = params.get_sign_by_idx(self.l())?;
|
||||
// randomise the signature associated with value l
|
||||
let (sign_l_prime, sign_l_blinding_factor) = sign_l.randomise(grparams);
|
||||
// compute kappa_l
|
||||
let kappa_l = grparams.gen2() * sign_l_blinding_factor
|
||||
+ params.pkRP().alpha
|
||||
+ params.pkRP().beta * Scalar::from(self.l());
|
||||
|
||||
// construct the zkp proof
|
||||
let spend_instance = SpendInstance {
|
||||
kappa,
|
||||
aa,
|
||||
cc,
|
||||
dd,
|
||||
ss,
|
||||
tt,
|
||||
kappa_l,
|
||||
aa: aa.clone(),
|
||||
ss: ss.clone(),
|
||||
tt: tt.clone(),
|
||||
kappa_k: kappa_k_vec.clone(),
|
||||
};
|
||||
let spend_witness = SpendWitness {
|
||||
attributes,
|
||||
r: sign_blinding_factor,
|
||||
r_l: sign_l_blinding_factor,
|
||||
l: Scalar::from(self.l()),
|
||||
o_a,
|
||||
o_c,
|
||||
o_d,
|
||||
lk,
|
||||
o_a,
|
||||
mu,
|
||||
lambda,
|
||||
o_mu,
|
||||
o_lambda,
|
||||
r_k: r_k_vec,
|
||||
};
|
||||
let zk_proof = SpendProof::construct(
|
||||
¶ms,
|
||||
&spend_instance,
|
||||
&spend_witness,
|
||||
&verification_key,
|
||||
rr,
|
||||
&rr,
|
||||
);
|
||||
|
||||
// output pay and updated wallet
|
||||
let pay = Payment {
|
||||
kappa,
|
||||
sig: signature_prime,
|
||||
ss,
|
||||
tt,
|
||||
aa,
|
||||
ss: ss.clone(),
|
||||
tt: tt.clone(),
|
||||
aa: aa.clone(),
|
||||
rr: rr.clone(),
|
||||
kappa_k: kappa_k_vec.clone(),
|
||||
sig_lk: sign_lk_prime_vec,
|
||||
cc,
|
||||
dd,
|
||||
rr,
|
||||
kappa_l,
|
||||
sig_l: sign_l_prime,
|
||||
zk_proof,
|
||||
vv: spend_vv,
|
||||
};
|
||||
|
||||
// The number of samples collected by the benchmark process is way higher than the
|
||||
@@ -209,12 +247,13 @@ pub fn compute_kappa(
|
||||
params.gen2() * blinding_factor
|
||||
+ verification_key.alpha
|
||||
+ attributes
|
||||
.iter()
|
||||
.zip(verification_key.beta_g2.iter())
|
||||
.map(|(priv_attr, beta_i)| beta_i * priv_attr)
|
||||
.sum::<G2Projective>()
|
||||
.iter()
|
||||
.zip(verification_key.beta_g2.iter())
|
||||
.map(|(priv_attr, beta_i)| beta_i * priv_attr)
|
||||
.sum::<G2Projective>()
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct PayInfo {
|
||||
pub info: [u8; 32],
|
||||
}
|
||||
@@ -223,15 +262,16 @@ pub struct PayInfo {
|
||||
pub struct Payment {
|
||||
pub kappa: G2Projective,
|
||||
pub sig: Signature,
|
||||
pub ss: G1Projective,
|
||||
pub tt: G1Projective,
|
||||
pub aa: G1Projective,
|
||||
pub ss: Vec<G1Projective>,
|
||||
pub tt: Vec<G1Projective>,
|
||||
pub aa: Vec<G1Projective>,
|
||||
pub rr: Vec<Scalar>,
|
||||
pub kappa_k: Vec<G2Projective>,
|
||||
pub sig_lk: Vec<Signature>,
|
||||
pub cc: G1Projective,
|
||||
pub dd: G1Projective,
|
||||
pub rr: Scalar,
|
||||
pub kappa_l: G2Projective,
|
||||
pub sig_l: Signature,
|
||||
pub zk_proof: SpendProof,
|
||||
pub vv: u64,
|
||||
}
|
||||
|
||||
impl Payment {
|
||||
@@ -240,6 +280,7 @@ impl Payment {
|
||||
params: &Parameters,
|
||||
verification_key: &VerificationKeyAuth,
|
||||
pay_info: &PayInfo,
|
||||
spend_vv: u64,
|
||||
) -> Result<bool> {
|
||||
if bool::from(self.sig.0.is_identity()) {
|
||||
return Err(CompactEcashError::Spend(
|
||||
@@ -258,28 +299,29 @@ impl Payment {
|
||||
));
|
||||
}
|
||||
|
||||
if bool::from(self.sig_l.0.is_identity()) {
|
||||
return Err(CompactEcashError::Spend(
|
||||
"The element h of the signature on l equals the identity".to_string(),
|
||||
));
|
||||
}
|
||||
for k in 0..spend_vv {
|
||||
if bool::from(self.sig_lk[k as usize].0.is_identity()) {
|
||||
return Err(CompactEcashError::Spend(
|
||||
"The element h of the signature on l equals the identity".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if !check_bilinear_pairing(
|
||||
&self.sig_l.0.to_affine(),
|
||||
&G2Prepared::from(self.kappa_l.to_affine()),
|
||||
&self.sig_l.1.to_affine(),
|
||||
params.grp().prepared_miller_g2(),
|
||||
) {
|
||||
return Err(CompactEcashError::Spend(
|
||||
"The bilinear check for kappa_l failed".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// verify integrity of R
|
||||
if !(self.rr == hash_to_scalar(pay_info.info)) {
|
||||
return Err(CompactEcashError::Spend(
|
||||
"Integrity of R does not hold".to_string(),
|
||||
));
|
||||
if !check_bilinear_pairing(
|
||||
&self.sig_lk[k as usize].0.to_affine(),
|
||||
&G2Prepared::from(self.kappa_k[k as usize].to_affine()),
|
||||
&self.sig_lk[k as usize].1.to_affine(),
|
||||
params.grp().prepared_miller_g2(),
|
||||
) {
|
||||
return Err(CompactEcashError::Spend(
|
||||
"The bilinear check for kappa_l failed".to_string(),
|
||||
));
|
||||
}
|
||||
// verify integrity of R_k
|
||||
if !(self.rr[k as usize] == hash_to_scalar(pay_info.info)) {
|
||||
return Err(CompactEcashError::Spend(
|
||||
"Integrity of R_k does not hold".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: verify whether payinfo contains merchent's identifier
|
||||
@@ -287,17 +329,17 @@ impl Payment {
|
||||
// verify the zk proof
|
||||
let instance = SpendInstance {
|
||||
kappa: self.kappa,
|
||||
aa: self.aa,
|
||||
aa: self.aa.clone(),
|
||||
cc: self.cc,
|
||||
dd: self.dd,
|
||||
ss: self.ss,
|
||||
tt: self.tt,
|
||||
kappa_l: self.kappa_l,
|
||||
ss: self.ss.clone(),
|
||||
tt: self.tt.clone(),
|
||||
kappa_k: self.kappa_k.clone(),
|
||||
};
|
||||
|
||||
if !self
|
||||
.zk_proof
|
||||
.verify(¶ms, &instance, &verification_key, self.rr)
|
||||
.verify(¶ms, &instance, &verification_key, &self.rr)
|
||||
{
|
||||
return Err(CompactEcashError::Spend(
|
||||
"ZkProof verification failed".to_string(),
|
||||
|
||||
@@ -5,10 +5,10 @@ use crate::error::{CompactEcashError, Result};
|
||||
use crate::proofs::proof_withdrawal::{
|
||||
WithdrawalReqInstance, WithdrawalReqProof, WithdrawalReqWitness,
|
||||
};
|
||||
use crate::scheme::keygen::ttp_keygen;
|
||||
use crate::scheme::keygen::{PublicKeyUser, SecretKeyAuth, SecretKeyUser, VerificationKeyAuth};
|
||||
use crate::scheme::setup::{GroupParameters, Parameters};
|
||||
use crate::scheme::keygen::ttp_keygen;
|
||||
use crate::scheme::PartialWallet;
|
||||
use crate::scheme::setup::{GroupParameters, Parameters};
|
||||
use crate::utils::{check_bilinear_pairing, hash_g1};
|
||||
use crate::utils::{BlindedSignature, Signature};
|
||||
|
||||
@@ -57,14 +57,15 @@ pub fn withdrawal_request(
|
||||
let com_opening = params.random_scalar();
|
||||
let com = params.gen1() * com_opening
|
||||
+ attributes
|
||||
.iter()
|
||||
.zip(gammas)
|
||||
.map(|(&m, gamma)| gamma * m)
|
||||
.sum::<G1Projective>();
|
||||
.iter()
|
||||
.zip(gammas)
|
||||
.map(|(&m, gamma)| gamma * m)
|
||||
.sum::<G1Projective>();
|
||||
|
||||
// Value h in the paper
|
||||
let com_hash = hash_g1(com.to_bytes());
|
||||
|
||||
// For each private attribute we compute a pedersen commitment
|
||||
let pc_coms_openings = params.n_random_scalars(attributes.len());
|
||||
|
||||
// Compute Pedersen commitment for each attribute
|
||||
|
||||
@@ -60,6 +60,7 @@ fn main() -> Result<(), CompactEcashError> {
|
||||
|
||||
// Let's try to spend some coins
|
||||
let pay_info = PayInfo { info: [6u8; 32] };
|
||||
let spend_vv = 1;
|
||||
|
||||
let (payment, upd_wallet) = aggr_wallet.spend(
|
||||
¶ms,
|
||||
@@ -67,10 +68,11 @@ fn main() -> Result<(), CompactEcashError> {
|
||||
&user_keypair.secret_key(),
|
||||
&pay_info,
|
||||
false,
|
||||
spend_vv,
|
||||
)?;
|
||||
|
||||
assert!(payment
|
||||
.spend_verify(¶ms, &verification_key, &pay_info)
|
||||
.spend_verify(¶ms, &verification_key, &pay_info, spend_vv)
|
||||
.unwrap());
|
||||
|
||||
// try to spend twice the same payment with different payInfo
|
||||
@@ -82,19 +84,20 @@ fn main() -> Result<(), CompactEcashError> {
|
||||
kappa: payment1.kappa.clone(),
|
||||
sig: payment1.sig.clone(),
|
||||
ss: payment1.ss.clone(),
|
||||
tt: grparams.gen1() * user_keypair.secret_key().sk
|
||||
+ pseudorandom_fgt(&grparams, aggr_wallet.t(), l2) * rr2,
|
||||
tt: vec![grparams.gen1() * user_keypair.secret_key().sk
|
||||
+ pseudorandom_fgt(&grparams, aggr_wallet.t(), l2) * rr2],
|
||||
aa: payment1.aa.clone(),
|
||||
cc: payment1.cc.clone(),
|
||||
dd: payment1.dd.clone(),
|
||||
rr: rr2,
|
||||
kappa_l: payment1.kappa_l.clone(),
|
||||
sig_l: payment1.sig_l.clone(),
|
||||
rr: vec![rr2],
|
||||
kappa_k: payment1.kappa_k.clone(),
|
||||
sig_lk: payment1.sig_lk.clone(),
|
||||
zk_proof: payment1.zk_proof.clone(),
|
||||
vv: spend_vv,
|
||||
};
|
||||
|
||||
let identified_user = identify(payment1, payment2).unwrap();
|
||||
assert_eq!(user_keypair.public_key().pk, identified_user.pk);
|
||||
let identified_user = identify(payment1, payment2, pay_info, pay_info2).unwrap();
|
||||
// assert_eq!(user_keypair.public_key().pk, identified_user.pk);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user