Add verification of the payments inside identify; speed up the identify checks
This commit is contained in:
Generated
+10
@@ -7719,18 +7719,28 @@ dependencies = [
|
||||
name = "nym_compact_ecash"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
<<<<<<< HEAD
|
||||
"bls12_381 0.5.0",
|
||||
<<<<<<< HEAD
|
||||
"bs58 0.4.0",
|
||||
"criterion 0.3.6",
|
||||
=======
|
||||
=======
|
||||
"bls12_381 0.6.0",
|
||||
>>>>>>> 0b58e0ae2 (Small updates and code cleaning)
|
||||
"bs58",
|
||||
"criterion",
|
||||
>>>>>>> b6a43787b (Add bilinear equations into the zk proof; the last eq passes the tests)
|
||||
"digest 0.9.0",
|
||||
<<<<<<< HEAD
|
||||
"ff 0.10.1",
|
||||
"group 0.10.0",
|
||||
"itertools 0.10.5",
|
||||
=======
|
||||
"ff 0.11.0",
|
||||
"group 0.11.0",
|
||||
"itertools",
|
||||
>>>>>>> 0b58e0ae2 (Small updates and code cleaning)
|
||||
"rand 0.8.5",
|
||||
"sha2 0.9.9",
|
||||
"thiserror",
|
||||
|
||||
@@ -7,7 +7,8 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] }
|
||||
#bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] }
|
||||
bls12_381 = { path = "/Users/ania/Documents/Git/andrew_bls12_381", default-features = false, features = ["alloc", "pairings", "experimental", "zeroize"] }
|
||||
itertools = "0.10"
|
||||
digest = "0.9"
|
||||
rand = "0.8"
|
||||
@@ -19,11 +20,11 @@ bs58 = "0.4.0"
|
||||
criterion = { version = "0.3", features = ["html_reports"] }
|
||||
|
||||
[dependencies.ff]
|
||||
version = "0.10"
|
||||
version = "0.11"
|
||||
default-features = false
|
||||
|
||||
[dependencies.group]
|
||||
version = "0.10"
|
||||
version = "0.11"
|
||||
default-features = false
|
||||
|
||||
[[bench]]
|
||||
|
||||
@@ -156,7 +156,7 @@ fn bench_compact_ecash(c: &mut Criterion) {
|
||||
keypair.secret_key(),
|
||||
user_keypair.public_key(),
|
||||
&req,
|
||||
)
|
||||
).unwrap()
|
||||
})
|
||||
},
|
||||
);
|
||||
@@ -219,28 +219,27 @@ fn bench_compact_ecash(c: &mut Criterion) {
|
||||
|
||||
// SPENDING PHASE
|
||||
let pay_info = PayInfo { info: [6u8; 32] };
|
||||
let spend_vv =
|
||||
// CLIENT BENCHMARK: spend a single coin from the wallet
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Client] spend_a_single_coin_L_{}_threshold_{}",
|
||||
case.L, case.threshold_p,
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
aggr_wallet
|
||||
.spend(
|
||||
¶ms,
|
||||
&verification_key,
|
||||
&user_keypair.secret_key(),
|
||||
&pay_info,
|
||||
true,
|
||||
case.spend_vv,
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
},
|
||||
);
|
||||
// CLIENT BENCHMARK: spend a single coin from the wallet
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Client] spend_a_single_coin_L_{}_threshold_{}",
|
||||
case.L, case.threshold_p,
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
aggr_wallet
|
||||
.spend(
|
||||
¶ms,
|
||||
&verification_key,
|
||||
&user_keypair.secret_key(),
|
||||
&pay_info,
|
||||
true,
|
||||
case.spend_vv,
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let (payment, upd_wallet) = aggr_wallet
|
||||
.spend(
|
||||
@@ -262,7 +261,7 @@ fn bench_compact_ecash(c: &mut Criterion) {
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
payment
|
||||
.spend_verify(¶ms, &verification_key, &pay_info, case.spend_vv)
|
||||
.spend_verify(¶ms, &verification_key, &pay_info)
|
||||
.unwrap()
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use bls12_381::G1Projective;
|
||||
use group::Curve;
|
||||
|
||||
use crate::{PayInfo, VerificationKeyAuth};
|
||||
use crate::error::{CompactEcashError, Result};
|
||||
use crate::PayInfo;
|
||||
use crate::scheme::keygen::PublicKeyUser;
|
||||
use crate::scheme::Payment;
|
||||
use crate::scheme::setup::Parameters;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum IdentifyResult {
|
||||
@@ -10,43 +16,44 @@ pub enum IdentifyResult {
|
||||
DoubleSpendingPublicKeys(PublicKeyUser),
|
||||
}
|
||||
|
||||
pub fn identify(public_keys_u: &[PublicKeyUser], 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));
|
||||
|
||||
pub fn identify(params: &Parameters, public_keys_u: &[PublicKeyUser], verification_key: &VerificationKeyAuth, payment1: Payment, payment2: Payment, pay_info1: PayInfo, pay_info2: PayInfo) -> Result<IdentifyResult> {
|
||||
// verify first the validity of both payments
|
||||
assert!(payment1.spend_verify(¶ms, &verification_key, &pay_info1).unwrap());
|
||||
assert!(payment2.spend_verify(¶ms, &verification_key, &pay_info2).unwrap());
|
||||
|
||||
let mut k = 0;
|
||||
let mut j = 0;
|
||||
'outer: for (id1, pay1_ss) in payment1.ss.iter().enumerate() {
|
||||
'inner: for (id2, pay2_ss) in payment2.ss.iter().enumerate() {
|
||||
if pay1_ss == pay2_ss {
|
||||
k = id1.clone();
|
||||
j = id2.clone();
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 = (pay2.tt[j] * pay1.rr[k] - pay1.tt[k] * pay2.rr[j]) * ((pay1.rr[k] - pay2.rr[j]).invert().unwrap());
|
||||
let pk_user = PublicKeyUser { pk: pk.clone() };
|
||||
if public_keys_u.contains(&pk_user) {
|
||||
return Ok(IdentifyResult::DoubleSpendingPublicKeys(pk_user));
|
||||
} else {
|
||||
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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
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(),
|
||||
));
|
||||
}
|
||||
return if pay_info1 == pay_info2 {
|
||||
Ok(IdentifyResult::DuplicatePayInfo(pay_info1))
|
||||
} else {
|
||||
let pk = (payment2.tt[j] * payment1.rr[k] - payment1.tt[k] * payment2.rr[j]) * ((payment1.rr[k] - payment2.rr[j]).invert().unwrap());
|
||||
let pk_user = PublicKeyUser { pk: pk.clone() };
|
||||
if public_keys_u.contains(&pk_user) {
|
||||
Ok(IdentifyResult::DoubleSpendingPublicKeys(pk_user))
|
||||
} else {
|
||||
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 std::collections::HashSet;
|
||||
|
||||
use group::Curve;
|
||||
use itertools::izip;
|
||||
|
||||
use crate::{aggregate_verification_keys, aggregate_wallets, generate_keypair_user, issue_verify, issue_wallet, PartialWallet, PayInfo, ttp_keygen, VerificationKeyAuth, withdrawal_request};
|
||||
@@ -112,16 +119,16 @@ mod tests {
|
||||
).unwrap();
|
||||
|
||||
assert!(payment1
|
||||
.spend_verify(¶ms, &verification_key, &pay_info1, spend_vv)
|
||||
.spend_verify(¶ms, &verification_key, &pay_info1)
|
||||
.unwrap());
|
||||
|
||||
let payment2 = payment1.clone();
|
||||
assert!(payment2
|
||||
.spend_verify(¶ms, &verification_key, &pay_info1, spend_vv)
|
||||
.spend_verify(¶ms, &verification_key, &pay_info1)
|
||||
.unwrap());
|
||||
|
||||
let pay_info2 = pay_info1.clone();
|
||||
let identify_result = identify(&[user_keypair.public_key()], payment1, payment2, pay_info1.clone(), pay_info2.clone()).unwrap();
|
||||
let identify_result = identify(¶ms, &[user_keypair.public_key()], &verification_key, payment1, payment2, pay_info1.clone(), pay_info2.clone()).unwrap();
|
||||
assert_eq!(identify_result, IdentifyResult::DuplicatePayInfo(pay_info1.clone()));
|
||||
}
|
||||
|
||||
@@ -183,7 +190,7 @@ mod tests {
|
||||
).unwrap();
|
||||
|
||||
assert!(payment1
|
||||
.spend_verify(¶ms, &verification_key, &pay_info1, spend_vv)
|
||||
.spend_verify(¶ms, &verification_key, &pay_info1)
|
||||
.unwrap());
|
||||
|
||||
|
||||
@@ -198,10 +205,10 @@ mod tests {
|
||||
).unwrap();
|
||||
|
||||
assert!(payment2
|
||||
.spend_verify(¶ms, &verification_key, &pay_info2, spend_vv)
|
||||
.spend_verify(¶ms, &verification_key, &pay_info2)
|
||||
.unwrap());
|
||||
|
||||
let identify_result = identify(&[user_keypair.public_key()], payment1, payment2, pay_info1.clone(), pay_info2.clone()).unwrap();
|
||||
let identify_result = identify(¶ms, &[user_keypair.public_key()], &verification_key, payment1, payment2, pay_info1.clone(), pay_info2.clone()).unwrap();
|
||||
assert_eq!(identify_result, IdentifyResult::NotADuplicatePayment);
|
||||
}
|
||||
|
||||
@@ -212,6 +219,17 @@ mod tests {
|
||||
let grp = params.grp();
|
||||
let user_keypair = generate_keypair_user(&grp);
|
||||
|
||||
// GENERATE KEYS FOR OTHER USERS
|
||||
let mut public_keys: Vec<PublicKeyUser> = Default::default();
|
||||
for i in 0..50 {
|
||||
let sk = grp.random_scalar();
|
||||
let sk_user = SecretKeyUser { sk };
|
||||
let pk_user = sk_user.public_key(&grp);
|
||||
public_keys.push(pk_user.clone());
|
||||
}
|
||||
public_keys.push(user_keypair.public_key().clone());
|
||||
|
||||
|
||||
let (req, req_info) = withdrawal_request(grp, &user_keypair.secret_key()).unwrap();
|
||||
let authorities_keypairs = ttp_keygen(&grp, 2, 3).unwrap();
|
||||
|
||||
@@ -253,7 +271,7 @@ mod tests {
|
||||
let pay_info1 = PayInfo { info: [6u8; 32] };
|
||||
let spend_vv = 1;
|
||||
|
||||
let (payment1, upd_wallet) = aggr_wallet.spend(
|
||||
let (payment1, _) = aggr_wallet.spend(
|
||||
¶ms,
|
||||
&verification_key,
|
||||
&user_keypair.secret_key(),
|
||||
@@ -263,7 +281,7 @@ mod tests {
|
||||
).unwrap();
|
||||
|
||||
assert!(payment1
|
||||
.spend_verify(¶ms, &verification_key, &pay_info1, spend_vv)
|
||||
.spend_verify(¶ms, &verification_key, &pay_info1)
|
||||
.unwrap());
|
||||
|
||||
// let's reverse the spending counter in the wallet to create a double spending payment
|
||||
@@ -271,7 +289,6 @@ mod tests {
|
||||
aggr_wallet.l.set(current_l - 1);
|
||||
|
||||
let pay_info2 = PayInfo { info: [7u8; 32] };
|
||||
let spend_vv = 1;
|
||||
|
||||
let (payment2, _) = aggr_wallet.spend(
|
||||
¶ms,
|
||||
@@ -283,21 +300,10 @@ mod tests {
|
||||
).unwrap();
|
||||
|
||||
assert!(payment2
|
||||
.spend_verify(¶ms, &verification_key, &pay_info2, spend_vv)
|
||||
.spend_verify(¶ms, &verification_key, &pay_info2)
|
||||
.unwrap());
|
||||
|
||||
// GENERATE KEYS FOR OTHER USERS
|
||||
let mut public_keys: Vec<PublicKeyUser> = Default::default();
|
||||
for i in 0..50 {
|
||||
let sk = grp.random_scalar();
|
||||
let sk_user = SecretKeyUser { sk };
|
||||
let pk_user = sk_user.public_key(&grp);
|
||||
public_keys.push(pk_user);
|
||||
}
|
||||
public_keys.push(user_keypair.public_key());
|
||||
|
||||
|
||||
let identify_result = identify(&public_keys, payment1, payment2, pay_info1.clone(), pay_info2.clone()).unwrap();
|
||||
let identify_result = identify(¶ms, &public_keys, &verification_key, payment1, payment2, pay_info1.clone(), pay_info2.clone()).unwrap();
|
||||
assert_eq!(identify_result, IdentifyResult::DoubleSpendingPublicKeys(user_keypair.public_key()));
|
||||
}
|
||||
|
||||
@@ -308,6 +314,16 @@ mod tests {
|
||||
let grp = params.grp();
|
||||
let user_keypair = generate_keypair_user(&grp);
|
||||
|
||||
// GENERATE KEYS FOR OTHER USERS
|
||||
let mut public_keys: Vec<PublicKeyUser> = Default::default();
|
||||
for i in 0..50 {
|
||||
let sk = grp.random_scalar();
|
||||
let sk_user = SecretKeyUser { sk };
|
||||
let pk_user = sk_user.public_key(&grp);
|
||||
public_keys.push(pk_user.clone());
|
||||
}
|
||||
public_keys.push(user_keypair.public_key().clone());
|
||||
|
||||
let (req, req_info) = withdrawal_request(grp, &user_keypair.secret_key()).unwrap();
|
||||
let authorities_keypairs = ttp_keygen(&grp, 2, 3).unwrap();
|
||||
|
||||
@@ -349,7 +365,7 @@ mod tests {
|
||||
let pay_info1 = PayInfo { info: [6u8; 32] };
|
||||
let spend_vv = 10;
|
||||
|
||||
let (payment1, upd_wallet) = aggr_wallet.spend(
|
||||
let (payment1, _) = aggr_wallet.spend(
|
||||
¶ms,
|
||||
&verification_key,
|
||||
&user_keypair.secret_key(),
|
||||
@@ -359,7 +375,7 @@ mod tests {
|
||||
).unwrap();
|
||||
|
||||
assert!(payment1
|
||||
.spend_verify(¶ms, &verification_key, &pay_info1, spend_vv)
|
||||
.spend_verify(¶ms, &verification_key, &pay_info1)
|
||||
.unwrap());
|
||||
|
||||
// let's reverse the spending counter in the wallet to create a double spending payment
|
||||
@@ -376,17 +392,8 @@ mod tests {
|
||||
spend_vv,
|
||||
).unwrap();
|
||||
|
||||
// GENERATE KEYS FOR OTHER USERS
|
||||
let mut public_keys: Vec<PublicKeyUser> = Default::default();
|
||||
for i in 0..50 {
|
||||
let sk = grp.random_scalar();
|
||||
let sk_user = SecretKeyUser { sk };
|
||||
let pk_user = sk_user.public_key(&grp);
|
||||
public_keys.push(pk_user);
|
||||
}
|
||||
public_keys.push(user_keypair.public_key());
|
||||
|
||||
let identify_result = identify(&public_keys, payment1, payment2, pay_info1.clone(), pay_info2.clone()).unwrap();
|
||||
let identify_result = identify(¶ms, &public_keys, &verification_key, payment1, payment2, pay_info1.clone(), pay_info2.clone()).unwrap();
|
||||
assert_eq!(identify_result, IdentifyResult::DoubleSpendingPublicKeys(user_keypair.public_key()));
|
||||
}
|
||||
}
|
||||
@@ -332,7 +332,7 @@ impl SecretKeyUser {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
pub struct PublicKeyUser {
|
||||
pub(crate) pk: G1Projective,
|
||||
}
|
||||
|
||||
@@ -281,7 +281,6 @@ 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(
|
||||
@@ -300,7 +299,7 @@ impl Payment {
|
||||
));
|
||||
}
|
||||
|
||||
for k in 0..spend_vv {
|
||||
for k in 0..self.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(),
|
||||
|
||||
@@ -72,7 +72,7 @@ fn main() -> Result<(), CompactEcashError> {
|
||||
)?;
|
||||
|
||||
assert!(payment
|
||||
.spend_verify(¶ms, &verification_key, &pay_info, spend_vv)
|
||||
.spend_verify(¶ms, &verification_key, &pay_info)
|
||||
.unwrap());
|
||||
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ use core::ops::Mul;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::ops::Neg;
|
||||
|
||||
use bls12_381::hash_to_curve::{ExpandMsgXmd, HashToCurve, HashToField};
|
||||
use bls12_381::{
|
||||
multi_miller_loop, G1Affine, G1Projective, G2Affine, G2Prepared, G2Projective, Scalar,
|
||||
G1Affine, G1Projective, G2Affine, G2Prepared, G2Projective, multi_miller_loop, Scalar,
|
||||
};
|
||||
use bls12_381::hash_to_curve::{ExpandMsgXmd, HashToCurve, HashToField};
|
||||
use ff::Field;
|
||||
use group::{Curve, Group};
|
||||
|
||||
@@ -36,7 +36,7 @@ impl Polynomial {
|
||||
Scalar::zero()
|
||||
// if x is zero then we can ignore most of the expensive computation and
|
||||
// just return the last term of the polynomial
|
||||
} else if x.is_zero() {
|
||||
} else if x.is_zero().unwrap_u8() == 1 {
|
||||
// we checked that coefficients are not empty so unwrap here is fine
|
||||
*self.coefficients.first().unwrap()
|
||||
} else {
|
||||
@@ -85,9 +85,9 @@ pub(crate) fn perform_lagrangian_interpolation_at_origin<T>(
|
||||
points: &[SignerIndex],
|
||||
values: &[T],
|
||||
) -> Result<T>
|
||||
where
|
||||
T: Sum,
|
||||
for<'a> &'a T: Mul<Scalar, Output = T>,
|
||||
where
|
||||
T: Sum,
|
||||
for<'a> &'a T: Mul<Scalar, Output=T>,
|
||||
{
|
||||
if points.is_empty() || values.is_empty() {
|
||||
return Err(CompactEcashError::Interpolation(
|
||||
|
||||
@@ -25,4 +25,8 @@ default-features = false
|
||||
|
||||
[dependencies.group]
|
||||
version = "0.11"
|
||||
default-features = false
|
||||
default-features = false
|
||||
|
||||
[[bench]]
|
||||
name = "benchmarks"
|
||||
harness = false
|
||||
@@ -0,0 +1,267 @@
|
||||
use std::collections::HashSet;
|
||||
use std::ops::Neg;
|
||||
use std::time::Duration;
|
||||
|
||||
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 rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
|
||||
use nym_offline_divisible_ecash::{aggregate_verification_keys, aggregate_wallets,
|
||||
issue, issue_verify, PartialWallet,
|
||||
PayInfo, PublicKeyUser, SecretKeyUser, ttp_keygen_authorities, ttp_keygen_users, VerificationKeyAuth, withdrawal_request};
|
||||
use nym_offline_divisible_ecash::identification::{identify, IdentifyResult};
|
||||
use nym_offline_divisible_ecash::setup::{GroupParameters, Parameters};
|
||||
|
||||
#[allow(unused)]
|
||||
fn double_pairing(g11: &G1Affine, g21: &G2Affine, g12: &G1Affine, g22: &G2Affine) {
|
||||
let gt1 = bls12_381::pairing(g11, g21);
|
||||
let gt2 = bls12_381::pairing(g12, g22);
|
||||
assert_eq!(gt1, gt2)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn multi_miller_pairing_affine(g11: &G1Affine, g21: &G2Affine, g12: &G1Affine, g22: &G2Affine) {
|
||||
let miller_loop_result = multi_miller_loop(&[
|
||||
(g11, &G2Prepared::from(*g21)),
|
||||
(&g12.neg(), &G2Prepared::from(*g22)),
|
||||
]);
|
||||
assert!(bool::from(
|
||||
miller_loop_result.final_exponentiation().is_identity()
|
||||
))
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn multi_miller_pairing_with_prepared(
|
||||
g11: &G1Affine,
|
||||
g21: &G2Prepared,
|
||||
g12: &G1Affine,
|
||||
g22: &G2Prepared,
|
||||
) {
|
||||
let miller_loop_result = multi_miller_loop(&[(g11, g21), (&g12.neg(), g22)]);
|
||||
assert!(bool::from(
|
||||
miller_loop_result.final_exponentiation().is_identity()
|
||||
))
|
||||
}
|
||||
|
||||
// the case of being able to prepare G2 generator
|
||||
#[allow(unused)]
|
||||
fn multi_miller_pairing_with_semi_prepared(
|
||||
g11: &G1Affine,
|
||||
g21: &G2Affine,
|
||||
g12: &G1Affine,
|
||||
g22: &G2Prepared,
|
||||
) {
|
||||
let miller_loop_result =
|
||||
multi_miller_loop(&[(g11, &G2Prepared::from(*g21)), (&g12.neg(), g22)]);
|
||||
assert!(bool::from(
|
||||
miller_loop_result.final_exponentiation().is_identity()
|
||||
))
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn bench_pairings(c: &mut Criterion) {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let g1 = G1Affine::generator();
|
||||
let g2 = G2Affine::generator();
|
||||
let r = Scalar::random(&mut rng);
|
||||
let s = Scalar::random(&mut rng);
|
||||
|
||||
let g11 = (g1 * r).to_affine();
|
||||
let g21 = (g2 * s).to_affine();
|
||||
let g21_prep = G2Prepared::from(g21);
|
||||
|
||||
let g12 = (g1 * s).to_affine();
|
||||
let g22 = (g2 * r).to_affine();
|
||||
let g22_prep = G2Prepared::from(g22);
|
||||
|
||||
c.bench_function("double pairing", |b| {
|
||||
b.iter(|| double_pairing(&g11, &g21, &g12, &g22))
|
||||
});
|
||||
|
||||
c.bench_function("multi miller in affine", |b| {
|
||||
b.iter(|| multi_miller_pairing_affine(&g11, &g21, &g12, &g22))
|
||||
});
|
||||
|
||||
c.bench_function("multi miller with prepared g2", |b| {
|
||||
b.iter(|| multi_miller_pairing_with_prepared(&g11, &g21_prep, &g12, &g22_prep))
|
||||
});
|
||||
|
||||
c.bench_function("multi miller with semi-prepared g2", |b| {
|
||||
b.iter(|| multi_miller_pairing_with_semi_prepared(&g11, &g21, &g12, &g22_prep))
|
||||
});
|
||||
}
|
||||
|
||||
struct BenchCase {
|
||||
num_authorities: u64,
|
||||
threshold_p: f32,
|
||||
L: u64,
|
||||
spend_vv: u64,
|
||||
case_nr_pub_keys: u64,
|
||||
}
|
||||
|
||||
fn bench_divisible_ecash(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("benchmark-divisible-ecash");
|
||||
group.measurement_time(Duration::from_secs(200));
|
||||
|
||||
let case = BenchCase {
|
||||
num_authorities: 100,
|
||||
threshold_p: 0.7,
|
||||
L: 100,
|
||||
spend_vv: 10,
|
||||
case_nr_pub_keys: 50,
|
||||
};
|
||||
|
||||
// SETUP PHASE
|
||||
let grp = GroupParameters::new().unwrap();
|
||||
let params = Parameters::new(grp.clone());
|
||||
|
||||
// KEY GENERATION FOR THE AUTHORITIES
|
||||
let authorities_keypairs = ttp_keygen_authorities(¶ms, 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();
|
||||
|
||||
// KEY GENERATION FOR THE USER
|
||||
let sk = grp.random_scalar();
|
||||
let sk_user = SecretKeyUser { sk };
|
||||
let pk_user = SecretKeyUser::public_key(&sk_user, &grp);
|
||||
|
||||
// GENERATE KEYS FOR OTHER USERS
|
||||
let mut pk_all_users = HashSet::new();
|
||||
for i in 0..50 {
|
||||
let sk = grp.random_scalar();
|
||||
let sk_user = SecretKeyUser { sk };
|
||||
let pk_user = sk_user.public_key(&grp);
|
||||
pk_all_users.insert(pk_user);
|
||||
}
|
||||
pk_all_users.insert(pk_user.clone());
|
||||
|
||||
// WITHDRAWAL REQUEST
|
||||
let (withdrawal_req, req_info) = withdrawal_request(¶ms, &sk_user).unwrap();
|
||||
|
||||
// CLIENT BENCHMARK: prepare a single withdrawal request
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Client] withdrawal_request_{}_authorities_{}_L_{}_threshold",
|
||||
case.num_authorities, case.L, case.threshold_p,
|
||||
),
|
||||
|b| b.iter(|| withdrawal_request(¶ms, &sk_user).unwrap()),
|
||||
);
|
||||
|
||||
// ISSUE PARTIAL WALLETS
|
||||
// first one meaningful one just for benchmark
|
||||
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, ),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
issue(
|
||||
¶ms,
|
||||
&withdrawal_req,
|
||||
pk_user.clone(),
|
||||
&keypair.secret_key(),
|
||||
).unwrap()
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
let mut partial_wallets = Vec::new();
|
||||
for auth_keypair in authorities_keypairs {
|
||||
let blind_signature = issue(
|
||||
¶ms,
|
||||
&withdrawal_req,
|
||||
pk_user.clone(),
|
||||
&auth_keypair.secret_key(),
|
||||
).unwrap();
|
||||
let partial_wallet = issue_verify(&grp, &auth_keypair.verification_key(), &sk_user, &blind_signature, &req_info).unwrap();
|
||||
partial_wallets.push(partial_wallet);
|
||||
}
|
||||
|
||||
// AGGREGATE WALLET
|
||||
let mut wallet = aggregate_wallets(&grp, &verification_key, &sk_user, &partial_wallets).unwrap();
|
||||
|
||||
// CLIENT BENCHMARK: aggregating all partial wallets
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Client] aggregate_wallets_with_L_{}_threshold_{}",
|
||||
case.L, case.threshold_p,
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
aggregate_wallets(&grp, &verification_key, &sk_user, &partial_wallets)
|
||||
.unwrap()
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
let pay_info = PayInfo { info: [67u8; 32] };
|
||||
let (payment, wallet) = wallet.spend(¶ms, &verification_key, &sk_user, &pay_info, 10, false).unwrap();
|
||||
|
||||
// CLIENT BENCHMARK: spend a single coin from the wallet
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Client] spend_a_single_coin_L_{}_threshold_{}",
|
||||
case.L, case.threshold_p,
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
wallet.spend(¶ms, &verification_key, &sk_user, &pay_info, 10, true)
|
||||
.unwrap()
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// MERCHANT BENCHMARK: verify whether the submitted payment is legit
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Merchant] spend_verify_of_a_single_payment_L_{}_threshold_{}",
|
||||
case.L, case.threshold_p,
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
payment.spend_verify(¶ms, &verification_key, &pay_info)
|
||||
.unwrap()
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// BENCHMARK IDENTIFICATION
|
||||
// Let's generate a double spending payment
|
||||
|
||||
// let's reverse the spending counter in the wallet to create a double spending payment
|
||||
let current_l = wallet.l();
|
||||
wallet.l.set(current_l - 7);
|
||||
|
||||
let pay_info2 = PayInfo { info: [52u8; 32] };
|
||||
let (payment2, wallet) = wallet.spend(¶ms, &verification_key, &sk_user, &pay_info2, 10, false).unwrap();
|
||||
|
||||
// MERCHANT BENCHMARK: identify double spending
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Merchant] identify_L_{}_threshold_{}_spend_vv_{}_pks_{}",
|
||||
case.L, case.threshold_p, case.spend_vv, pk_all_users.len()
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
identify(¶ms, &verification_key, &pk_all_users, payment.clone(), payment2.clone(), pay_info, pay_info2).unwrap()
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
let identify_result = identify(¶ms, &verification_key, &pk_all_users, payment.clone(), payment2.clone(), pay_info, pay_info2).unwrap();
|
||||
assert_eq!(identify_result, IdentifyResult::DoubleSpendingPublicKeys(pk_user));
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_divisible_ecash);
|
||||
criterion_main!(benches);
|
||||
@@ -1,4 +1,21 @@
|
||||
use bls12_381::Scalar;
|
||||
use bls12_381::{G1Projective, G2Prepared, G2Projective, pairing, Scalar};
|
||||
|
||||
pub use scheme::aggregation::aggregate_verification_keys;
|
||||
pub use scheme::aggregation::aggregate_wallets;
|
||||
pub use scheme::identification;
|
||||
pub use scheme::keygen::{PublicKeyUser, SecretKeyUser, VerificationKeyAuth};
|
||||
pub use scheme::keygen::ttp_keygen_authorities;
|
||||
pub use scheme::keygen::ttp_keygen_users;
|
||||
pub use scheme::PartialWallet;
|
||||
pub use scheme::PayInfo;
|
||||
pub use scheme::setup;
|
||||
pub use scheme::withdrawal::issue;
|
||||
pub use scheme::withdrawal::issue_verify;
|
||||
pub use scheme::withdrawal::withdrawal_request;
|
||||
pub use traits::Base58;
|
||||
|
||||
use crate::error::DivisibleEcashError;
|
||||
use crate::traits::Bytable;
|
||||
|
||||
mod error;
|
||||
mod proofs;
|
||||
|
||||
@@ -22,66 +22,63 @@ pub enum IdentifyResult {
|
||||
pub fn identify(
|
||||
params: &Parameters,
|
||||
verification_key: &VerificationKeyAuth,
|
||||
public_keys_u: &[PublicKeyUser],
|
||||
public_keys_u: &HashSet<PublicKeyUser>,
|
||||
payment1: Payment,
|
||||
payment2: Payment,
|
||||
pay_info1: PayInfo,
|
||||
pay_info2: PayInfo) -> Result<IdentifyResult> {
|
||||
// verify first the validaty of both payments
|
||||
// verify first the validity of both payments
|
||||
assert!(payment1.spend_verify(¶ms, &verification_key, &pay_info1).unwrap());
|
||||
assert!(payment2.spend_verify(¶ms, &verification_key, &pay_info2).unwrap());
|
||||
|
||||
let params_a = params.get_params_a();
|
||||
|
||||
// compute the serial numbers for k1 in [0, V1-1]
|
||||
let mut serial_numbers = HashMap::new();
|
||||
|
||||
for k1 in 0..payment1.vv {
|
||||
let sn = pairing(&payment1.phi.1.to_affine(), ¶ms_a.get_ith_delta(k1 as usize).to_affine())
|
||||
+ pairing(&payment1.phi.0.to_affine(), ¶ms_a.get_etas_ith_jth_elem(payment1.vv as usize, k1 as usize).to_affine());
|
||||
serial_numbers.insert(sn, k1);
|
||||
for k in 0..payment1.vv {
|
||||
let sn = pairing(&payment1.phi.1.to_affine(), ¶ms_a.get_ith_delta(k as usize).to_affine())
|
||||
+ pairing(&payment1.phi.0.to_affine(), ¶ms_a.get_etas_ith_jth_elem(payment1.vv as usize, k as usize).to_affine());
|
||||
serial_numbers.insert(sn, k);
|
||||
}
|
||||
|
||||
// compute the serial numbers fo k2 in [0, V2-1]
|
||||
let mut k1 = 0;
|
||||
let mut k2 = 0;
|
||||
let mut duplicate_serial_numbers: Vec<(Gt, u64, u64)> = Default::default();
|
||||
for k2 in 0..payment2.vv {
|
||||
let sn = pairing(&payment2.phi.1.to_affine(), ¶ms_a.get_ith_delta(k2 as usize).to_affine())
|
||||
+ pairing(&payment2.phi.0.to_affine(), ¶ms_a.get_etas_ith_jth_elem(payment2.vv as usize, k2 as usize).to_affine());
|
||||
for j in 0..payment2.vv {
|
||||
let sn = pairing(&payment2.phi.1.to_affine(), ¶ms_a.get_ith_delta(j as usize).to_affine())
|
||||
+ pairing(&payment2.phi.0.to_affine(), ¶ms_a.get_etas_ith_jth_elem(payment2.vv as usize, j as usize).to_affine());
|
||||
if !serial_numbers.contains_key(&sn) {
|
||||
serial_numbers.insert(sn, k2);
|
||||
serial_numbers.insert(sn, j);
|
||||
} else {
|
||||
let k1 = *serial_numbers.get(&sn).unwrap() as u64;
|
||||
duplicate_serial_numbers.push((sn, k1, k2));
|
||||
k1 = *serial_numbers.get(&sn).unwrap() as u64;
|
||||
k2 = j.clone();
|
||||
break;
|
||||
}
|
||||
return Ok(IdentifyResult::NotADuplicatePayment);
|
||||
}
|
||||
|
||||
if duplicate_serial_numbers.is_empty() {
|
||||
Ok(IdentifyResult::NotADuplicatePayment)
|
||||
if pay_info1 == pay_info2 {
|
||||
Ok(IdentifyResult::DuplicatePayInfo(pay_info1))
|
||||
} else {
|
||||
if pay_info1.info == pay_info2.info {
|
||||
Ok(IdentifyResult::DuplicatePayInfo(pay_info1))
|
||||
} else {
|
||||
for elem in duplicate_serial_numbers.iter() {
|
||||
let k1 = elem.1;
|
||||
let k2 = elem.2;
|
||||
let delta_k1 = params_a.get_ith_delta(k1 as usize);
|
||||
let delta_k2 = params_a.get_ith_delta(k2 as usize);
|
||||
let tt1 = pairing(&payment1.varphi.1.to_affine(), &delta_k1.to_affine())
|
||||
+ pairing(&payment1.varphi.0.to_affine(), ¶ms_a.get_etas_ith_jth_elem(payment1.vv as usize, k1 as usize).to_affine());
|
||||
let tt2 = pairing(&payment2.varphi.1.to_affine(), &delta_k2.to_affine())
|
||||
+ pairing(&payment2.varphi.0.to_affine(), ¶ms_a.get_etas_ith_jth_elem(payment2.vv as usize, k2 as usize).to_affine());
|
||||
let delta_k1 = params_a.get_ith_delta(k1 as usize);
|
||||
let delta_k2 = params_a.get_ith_delta(k2 as usize);
|
||||
let tt1 = pairing(&payment1.varphi.1.to_affine(), &delta_k1.to_affine())
|
||||
+ pairing(&payment1.varphi.0.to_affine(), ¶ms_a.get_etas_ith_jth_elem(payment1.vv as usize, k1 as usize).to_affine());
|
||||
let tt2 = pairing(&payment2.varphi.1.to_affine(), &delta_k2.to_affine())
|
||||
+ pairing(&payment2.varphi.0.to_affine(), ¶ms_a.get_etas_ith_jth_elem(payment2.vv as usize, k2 as usize).to_affine());
|
||||
|
||||
|
||||
for pk_u in public_keys_u.iter() {
|
||||
let pg_pku_deltas = pairing(&pk_u.pk.to_affine(), &(delta_k1 * payment1.rr + delta_k2 * payment2.rr.neg()).to_affine());
|
||||
if tt1 - tt2 == pg_pku_deltas {
|
||||
return Ok(IdentifyResult::DoubleSpendingPublicKeys(pk_u.clone()));
|
||||
}
|
||||
}
|
||||
for pk_u in public_keys_u.iter() {
|
||||
let pg_pku_deltas = pairing(&pk_u.pk.to_affine(), &(delta_k1 * payment1.rr + delta_k2 * payment2.rr.neg()).to_affine());
|
||||
if tt1 - tt2 == pg_pku_deltas {
|
||||
return Ok(IdentifyResult::DoubleSpendingPublicKeys(pk_u.clone()));
|
||||
}
|
||||
return Err(DivisibleEcashError::Identify(
|
||||
"A duplicate serial number was detected, the payinfo1 and payinfo2 are different, but we failed to identify the double-spending public key".to_string(),
|
||||
));
|
||||
}
|
||||
return Err(DivisibleEcashError::Identify(
|
||||
"A duplicate serial number was detected, the payinfo1 and payinfo2 are different, but we failed to identify the double-spending public key".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +100,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn duplicate_payments_with_the_same_pay_info() {
|
||||
let rng = thread_rng();
|
||||
let grp = GroupParameters::new().unwrap();
|
||||
let params = Parameters::new(grp.clone());
|
||||
let params_u = params.get_params_u();
|
||||
@@ -150,7 +146,7 @@ mod tests {
|
||||
let mut wallet1 = aggregate_wallets(&grp, &verification_key, &sk_user1, &partial_wallets1).unwrap();
|
||||
|
||||
let pay_info1 = PayInfo { info: [67u8; 32] };
|
||||
let (payment1, wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info1, 10).unwrap();
|
||||
let (payment1, wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info1, 10, false).unwrap();
|
||||
|
||||
// SPEND VERIFICATION for USER1
|
||||
assert!(payment1.spend_verify(¶ms, &verification_key, &pay_info1).unwrap());
|
||||
@@ -161,7 +157,8 @@ mod tests {
|
||||
|
||||
let pay_info2 = pay_info1.clone();
|
||||
|
||||
let identify_result = identify(¶ms, &verification_key, &[pk_user1, pk_user2], payment1, payment2, pay_info1, pay_info2).unwrap();
|
||||
let public_keys = HashSet::from([pk_user1, pk_user2]);
|
||||
let identify_result = identify(¶ms, &verification_key, &public_keys, payment1, payment2, pay_info1, pay_info2).unwrap();
|
||||
assert_eq!(identify_result, IdentifyResult::DuplicatePayInfo(pay_info1));
|
||||
}
|
||||
|
||||
@@ -188,20 +185,16 @@ mod tests {
|
||||
let sk_user1 = SecretKeyUser { sk: sk1 };
|
||||
let pk_user1 = SecretKeyUser::public_key(&sk_user1, &grp);
|
||||
|
||||
// KEY GENERATION FOR THE USER2
|
||||
let sk_user2 = sk_user1.clone();
|
||||
let pk_user2 = pk_user1.clone();
|
||||
|
||||
// GENERATE KEYS FOR OTHER USERS
|
||||
let mut pk_all_users: Vec<PublicKeyUser> = Default::default();
|
||||
let mut pk_all_users = HashSet::new();
|
||||
for i in 0..50 {
|
||||
let sk = grp.random_scalar();
|
||||
let sk_user = SecretKeyUser { sk };
|
||||
let pk_user = sk_user.public_key(&grp);
|
||||
pk_all_users.push(pk_user);
|
||||
pk_all_users.insert(pk_user);
|
||||
}
|
||||
pk_all_users.push(pk_user1.clone());
|
||||
pk_all_users.push(pk_user2.clone());
|
||||
pk_all_users.insert(pk_user1.clone());
|
||||
|
||||
// WITHDRAWAL REQUEST FOR USER1
|
||||
let (withdrawal_req1, req_info1) = withdrawal_request(¶ms, &sk_user1).unwrap();
|
||||
@@ -223,14 +216,14 @@ mod tests {
|
||||
let mut wallet1 = aggregate_wallets(&grp, &verification_key, &sk_user1, &partial_wallets1).unwrap();
|
||||
|
||||
let pay_info1 = PayInfo { info: [67u8; 32] };
|
||||
let (payment1, new_wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info1, 10).unwrap();
|
||||
let (payment1, new_wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info1, 10, false).unwrap();
|
||||
|
||||
// let's reverse the spending counter in the wallet to create a double spending payment
|
||||
let current_l = wallet1.l.get();
|
||||
wallet1.l.set(current_l - 1);
|
||||
|
||||
let pay_info2 = PayInfo { info: [52u8; 32] };
|
||||
let (payment2, wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info2, 10).unwrap();
|
||||
let (payment2, wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info2, 10, false).unwrap();
|
||||
|
||||
|
||||
let identify_result = identify(¶ms, &verification_key, &pk_all_users, payment1, payment2, pay_info1, pay_info2).unwrap();
|
||||
@@ -261,20 +254,15 @@ mod tests {
|
||||
let sk_user1 = SecretKeyUser { sk: sk1 };
|
||||
let pk_user1 = SecretKeyUser::public_key(&sk_user1, &grp);
|
||||
|
||||
// KEY GENERATION FOR THE USER2
|
||||
let sk_user2 = sk_user1.clone();
|
||||
let pk_user2 = pk_user1.clone();
|
||||
|
||||
// GENERATE KEYS FOR OTHER USERS
|
||||
let mut pk_all_users: Vec<PublicKeyUser> = Default::default();
|
||||
let mut public_keys = HashSet::new();
|
||||
for i in 0..50 {
|
||||
let sk = grp.random_scalar();
|
||||
let sk_user = SecretKeyUser { sk };
|
||||
let pk_user = sk_user.public_key(&grp);
|
||||
pk_all_users.push(pk_user);
|
||||
public_keys.insert(pk_user);
|
||||
}
|
||||
pk_all_users.push(pk_user1.clone());
|
||||
pk_all_users.push(pk_user2.clone());
|
||||
public_keys.insert(pk_user1.clone());
|
||||
|
||||
// WITHDRAWAL REQUEST FOR USER1
|
||||
let (withdrawal_req1, req_info1) = withdrawal_request(¶ms, &sk_user1).unwrap();
|
||||
@@ -296,17 +284,17 @@ mod tests {
|
||||
let mut wallet1 = aggregate_wallets(&grp, &verification_key, &sk_user1, &partial_wallets1).unwrap();
|
||||
|
||||
let pay_info1 = PayInfo { info: [67u8; 32] };
|
||||
let (payment1, new_wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info1, 10).unwrap();
|
||||
let (payment1, new_wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info1, 10, false).unwrap();
|
||||
|
||||
// let's reverse the spending counter in the wallet to create a double spending payment
|
||||
let current_l = wallet1.l.get();
|
||||
wallet1.l.set(current_l - 7);
|
||||
|
||||
let pay_info2 = PayInfo { info: [52u8; 32] };
|
||||
let (payment2, wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info2, 10).unwrap();
|
||||
let (payment2, wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info2, 10, false).unwrap();
|
||||
|
||||
|
||||
let identify_result = identify(¶ms, &verification_key, &pk_all_users, payment1, payment2, pay_info1, pay_info2).unwrap();
|
||||
let identify_result = identify(¶ms, &verification_key, &public_keys, payment1, payment2, pay_info1, pay_info2).unwrap();
|
||||
|
||||
assert_eq!(identify_result, IdentifyResult::DoubleSpendingPublicKeys(pk_user1));
|
||||
}
|
||||
@@ -360,7 +348,7 @@ mod tests {
|
||||
let mut wallet1 = aggregate_wallets(&grp, &verification_key, &sk_user1, &partial_wallets1).unwrap();
|
||||
|
||||
let pay_info1 = PayInfo { info: [67u8; 32] };
|
||||
let (payment1, wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info1, 10).unwrap();
|
||||
let (payment1, wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info1, 10, false).unwrap();
|
||||
|
||||
// SPEND VERIFICATION for USER1
|
||||
assert!(payment1.spend_verify(¶ms, &verification_key, &pay_info1).unwrap());
|
||||
@@ -385,12 +373,13 @@ mod tests {
|
||||
let mut wallet2 = aggregate_wallets(&grp, &verification_key, &sk_user2, &partial_wallets2).unwrap();
|
||||
|
||||
let pay_info2 = PayInfo { info: [67u8; 32] };
|
||||
let (payment2, wallet2) = wallet2.spend(¶ms, &verification_key, &sk_user2, &pay_info2, 10).unwrap();
|
||||
let (payment2, wallet2) = wallet2.spend(¶ms, &verification_key, &sk_user2, &pay_info2, 10, false).unwrap();
|
||||
|
||||
// SPEND VERIFICATION for USER2
|
||||
assert!(payment2.spend_verify(¶ms, &verification_key, &pay_info2).unwrap());
|
||||
|
||||
let identify_result = identify(¶ms, &verification_key, &[pk_user1, pk_user2], payment1, payment2, pay_info1, pay_info2).unwrap();
|
||||
let public_keys = HashSet::from([pk_user1, pk_user2]);
|
||||
let identify_result = identify(¶ms, &verification_key, &public_keys, payment1, payment2, pay_info1, pay_info2).unwrap();
|
||||
assert_eq!(identify_result, IdentifyResult::NotADuplicatePayment);
|
||||
}
|
||||
}
|
||||
@@ -340,7 +340,7 @@ impl KeyPairAuth {
|
||||
|
||||
#[derive(Eq, Debug, PartialEq, Clone)]
|
||||
pub struct SecretKeyUser {
|
||||
pub(crate) sk: Scalar,
|
||||
pub sk: Scalar,
|
||||
}
|
||||
|
||||
impl SecretKeyUser {
|
||||
|
||||
@@ -172,7 +172,7 @@ impl PartialWallet {
|
||||
pub struct Wallet {
|
||||
sig: Signature,
|
||||
v: Scalar,
|
||||
l: Cell<u64>,
|
||||
pub l: Cell<u64>,
|
||||
}
|
||||
|
||||
impl Wallet {
|
||||
@@ -189,13 +189,14 @@ impl Wallet {
|
||||
}
|
||||
|
||||
|
||||
pub(crate) fn spend(
|
||||
pub fn spend(
|
||||
&self,
|
||||
params: &Parameters,
|
||||
verification_key: &VerificationKeyAuth,
|
||||
sk_user: &SecretKeyUser,
|
||||
pay_info: &PayInfo,
|
||||
vv: u64,
|
||||
bench_flag: bool,
|
||||
) -> Result<(Payment, &Self)> {
|
||||
if self.l() + vv >= L {
|
||||
return Err(DivisibleEcashError::Spend(
|
||||
@@ -345,8 +346,16 @@ impl Wallet {
|
||||
zk_proof,
|
||||
vv,
|
||||
};
|
||||
|
||||
self.l.set(self.l() + vv);
|
||||
|
||||
// The number of samples collected by the benchmark process is way higher than the
|
||||
// MAX_WALLET_VALUE we ever consider. Thus, we would execute the spending too many times
|
||||
// and the initial condition at the top of this function will crush. Thus, we need a
|
||||
// benchmark flag to signal that we don't want to increase the spending couter but only
|
||||
// care about the function performance.
|
||||
if !bench_flag {
|
||||
let current_l = self.l();
|
||||
self.l.set(current_l + vv);
|
||||
}
|
||||
Ok((pay, self))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ impl GroupParameters {
|
||||
&self._g2_prepared_miller
|
||||
}
|
||||
|
||||
pub(crate) fn random_scalar(&self) -> Scalar {
|
||||
pub fn random_scalar(&self) -> Scalar {
|
||||
// lazily-initialized thread-local random number generator, seeded by the system
|
||||
let mut rng = thread_rng();
|
||||
Scalar::random(&mut rng)
|
||||
|
||||
@@ -78,7 +78,7 @@ pub fn withdrawal_request(params: &Parameters, sk_user: &SecretKeyUser) -> Resul
|
||||
Ok((req, req_info))
|
||||
}
|
||||
|
||||
pub(crate) fn issue(params: &Parameters, req: &WithdrawalRequest, pk_u: PublicKeyUser, sk_a: &SecretKeyAuth) -> Result<BlindedSignature> {
|
||||
pub fn issue(params: &Parameters, req: &WithdrawalRequest, pk_u: PublicKeyUser, sk_a: &SecretKeyAuth) -> Result<BlindedSignature> {
|
||||
let h = hash_g1(req.com.to_bytes());
|
||||
if !(h == req.com_hash) {
|
||||
return Err(DivisibleEcashError::WithdrawalRequestVerification(
|
||||
@@ -110,7 +110,7 @@ pub(crate) fn issue(params: &Parameters, req: &WithdrawalRequest, pk_u: PublicKe
|
||||
Ok(BlindedSignature(h, sig))
|
||||
}
|
||||
|
||||
pub(crate) fn issue_verify(
|
||||
pub fn issue_verify(
|
||||
params: &GroupParameters,
|
||||
vk_auth: &VerificationKeyAuth,
|
||||
sk_user: &SecretKeyUser,
|
||||
|
||||
@@ -13,11 +13,8 @@ use crate::scheme::withdrawal::{issue, issue_verify, withdrawal_request};
|
||||
// and spending.
|
||||
fn main() -> Result<(), DivisibleEcashError> {
|
||||
// SETUP PHASE
|
||||
let rng = thread_rng();
|
||||
let grp = GroupParameters::new().unwrap();
|
||||
let params = Parameters::new(grp.clone());
|
||||
let params_u = params.get_params_u();
|
||||
let params_a = params.get_params_a();
|
||||
|
||||
// KEY GENERATION FOR THE AUTHORITIES
|
||||
let authorities_keypairs = ttp_keygen_authorities(¶ms, 2, 3).unwrap();
|
||||
@@ -54,7 +51,7 @@ fn main() -> Result<(), DivisibleEcashError> {
|
||||
let mut wallet = aggregate_wallets(&grp, &verification_key, &sk_user, &partial_wallets)?;
|
||||
|
||||
let pay_info = PayInfo { info: [67u8; 32] };
|
||||
let (payment, wallet) = wallet.spend(¶ms, &verification_key, &sk_user, &pay_info, 10)?;
|
||||
let (payment, wallet) = wallet.spend(¶ms, &verification_key, &sk_user, &pay_info, 10, false)?;
|
||||
|
||||
// SPEND VERIFICATION
|
||||
assert!(payment.spend_verify(¶ms, &verification_key, &pay_info).unwrap());
|
||||
|
||||
Reference in New Issue
Block a user