Start the spend function and zkproof for spend
This commit is contained in:
@@ -22,6 +22,9 @@ pub enum CompactEcashError {
|
||||
#[error("Issuance Verification related error: {0}")]
|
||||
IssuanceVfy(String),
|
||||
|
||||
#[error("Spend Verification related error: {0}")]
|
||||
Spend(String),
|
||||
|
||||
#[error("Tried to deserialize {object} with bytes of invalid length. Expected {actual} < {} or {modulus_target} % {modulus} == 0")]
|
||||
DeserializationInvalidLength {
|
||||
actual: usize,
|
||||
|
||||
@@ -3,10 +3,9 @@ use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use bls12_381::{G1Affine, G1Projective, Scalar};
|
||||
use digest::generic_array::typenum::Unsigned;
|
||||
use digest::Digest;
|
||||
use digest::generic_array::typenum::Unsigned;
|
||||
use group::GroupEncoding;
|
||||
use itertools::izip;
|
||||
use sha2::Sha256;
|
||||
|
||||
use crate::error::{CompactEcashError, Result};
|
||||
@@ -14,14 +13,17 @@ use crate::scheme::keygen::PublicKeyUser;
|
||||
use crate::scheme::setup::Parameters;
|
||||
use crate::utils::try_deserialize_g1_projective;
|
||||
|
||||
pub mod proof_withdrawal;
|
||||
pub mod proof_spend;
|
||||
|
||||
type ChallengeDigest = Sha256;
|
||||
|
||||
/// Generates a Scalar [or Fp] challenge by hashing a number of elliptic curve points.
|
||||
fn compute_challenge<D, I, B>(iter: I) -> Scalar
|
||||
where
|
||||
D: Digest,
|
||||
I: Iterator<Item = B>,
|
||||
B: AsRef<[u8]>,
|
||||
where
|
||||
D: Digest,
|
||||
I: Iterator<Item=B>,
|
||||
B: AsRef<[u8]>,
|
||||
{
|
||||
let mut h = D::new();
|
||||
for point_representation in iter {
|
||||
@@ -49,8 +51,8 @@ fn produce_response(witness_replacement: &Scalar, challenge: &Scalar, secret: &S
|
||||
|
||||
// note: it's caller's responsibility to ensure witnesses.len() = secrets.len()
|
||||
fn produce_responses<S>(witnesses: &[Scalar], challenge: &Scalar, secrets: &[S]) -> Vec<Scalar>
|
||||
where
|
||||
S: Borrow<Scalar>,
|
||||
where
|
||||
S: Borrow<Scalar>,
|
||||
{
|
||||
debug_assert_eq!(witnesses.len(), secrets.len());
|
||||
|
||||
@@ -61,310 +63,4 @@ where
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
// instance: g, gamma1, gamma2, gamma3, com, h, com1, com2, com3, pkUser
|
||||
pub struct WithdrawalReqInstance {
|
||||
// Joined commitment to all attributes
|
||||
pub com: G1Projective,
|
||||
// Hash of the joined commitment com
|
||||
pub h: G1Projective,
|
||||
// Pedersen commitments to each attribute
|
||||
pub pc_coms: Vec<G1Projective>,
|
||||
// Public key of a user
|
||||
pub pk_user: PublicKeyUser,
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for WithdrawalReqInstance {
|
||||
type Error = CompactEcashError;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<WithdrawalReqInstance> {
|
||||
if bytes.len() < 48 * 4 + 8 || (bytes.len() - 8) % 48 != 0 {
|
||||
return Err(CompactEcashError::DeserializationInvalidLength {
|
||||
actual: bytes.len(),
|
||||
modulus_target: bytes.len() - 8,
|
||||
target: 48 * 4 + 8,
|
||||
modulus: 48,
|
||||
object: "secret key".to_string(),
|
||||
});
|
||||
}
|
||||
let com_bytes: [u8; 48] = bytes[..48].try_into().unwrap();
|
||||
let com = try_deserialize_g1_projective(
|
||||
&com_bytes,
|
||||
CompactEcashError::Deserialization("Failed to deserialize com".to_string()),
|
||||
)?;
|
||||
let h_bytes: [u8; 48] = bytes[48..96].try_into().unwrap();
|
||||
let h = try_deserialize_g1_projective(
|
||||
&h_bytes,
|
||||
CompactEcashError::Deserialization("Failed to deserialize h".to_string()),
|
||||
)?;
|
||||
let pc_coms_len = u64::from_le_bytes(bytes[96..104].try_into().unwrap());
|
||||
let actual_pc_coms_len = (bytes.len() - 152) / 48;
|
||||
if pc_coms_len as usize != actual_pc_coms_len {
|
||||
return Err(CompactEcashError::Deserialization(format!(
|
||||
"Tried to deserialize pedersen commitments with inconsistent pc_coms_len (expected {}, got {})",
|
||||
pc_coms_len, actual_pc_coms_len
|
||||
)));
|
||||
}
|
||||
let mut pc_coms = Vec::new();
|
||||
let mut pc_coms_end: usize = 0;
|
||||
for i in 0..pc_coms_len {
|
||||
let start = (104 + i * 48) as usize;
|
||||
let end = (start + 48) as usize;
|
||||
let pc_i_bytes = bytes[start..end].try_into().unwrap();
|
||||
let pc_i = try_deserialize_g1_projective(
|
||||
&pc_i_bytes,
|
||||
CompactEcashError::Deserialization(
|
||||
"Failed to deserialize pedersen commitment".to_string(),
|
||||
),
|
||||
)?;
|
||||
pc_coms_end = end;
|
||||
pc_coms.push(pc_i);
|
||||
}
|
||||
let pk_bytes = bytes[pc_coms_end..].try_into().unwrap();
|
||||
let pk = try_deserialize_g1_projective(
|
||||
&pk_bytes,
|
||||
CompactEcashError::Deserialization(
|
||||
"Failed to deserialize user's public key".to_string(),
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(WithdrawalReqInstance {
|
||||
com,
|
||||
h,
|
||||
pc_coms,
|
||||
pk_user: PublicKeyUser { pk },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WithdrawalReqInstance {
|
||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||
let pc_coms_len = self.pc_coms.len();
|
||||
let mut bytes = Vec::with_capacity(8 + (pc_coms_len + 3) as usize * 48);
|
||||
bytes.extend_from_slice(self.com.to_bytes().as_ref());
|
||||
bytes.extend_from_slice(self.h.to_bytes().as_ref());
|
||||
bytes.extend_from_slice(&pc_coms_len.to_le_bytes());
|
||||
for pc in self.pc_coms.iter() {
|
||||
bytes.extend_from_slice((pc.to_bytes()).as_ref());
|
||||
}
|
||||
bytes.extend_from_slice(self.pk_user.pk.to_bytes().as_ref());
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<WithdrawalReqInstance> {
|
||||
WithdrawalReqInstance::try_from(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
// witness: m1, m2, m3, o, o1, o2, o3,
|
||||
pub struct WithdrawalReqWitness {
|
||||
pub attributes: Vec<Scalar>,
|
||||
// Opening for the joined commitment com
|
||||
pub com_opening: Scalar,
|
||||
// Openings for the pedersen commitments
|
||||
pub pc_coms_openings: Vec<Scalar>,
|
||||
}
|
||||
|
||||
pub struct WithdrawalReqProof {
|
||||
challenge: Scalar,
|
||||
response_opening: Scalar,
|
||||
response_openings: Vec<Scalar>,
|
||||
response_attributes: Vec<Scalar>,
|
||||
}
|
||||
|
||||
impl WithdrawalReqProof {
|
||||
pub(crate) fn construct(
|
||||
params: &Parameters,
|
||||
instance: &WithdrawalReqInstance,
|
||||
witness: &WithdrawalReqWitness,
|
||||
) -> Self {
|
||||
// generate random values to replace the witnesses
|
||||
let r_com_opening = params.random_scalar();
|
||||
let r_pedcom_openings = params.n_random_scalars(witness.pc_coms_openings.len());
|
||||
let r_attributes = params.n_random_scalars(witness.attributes.len());
|
||||
|
||||
// compute zkp commitments for each instance
|
||||
let zkcm_com = params.gen1() * r_com_opening
|
||||
+ r_attributes
|
||||
.iter()
|
||||
.zip(params.gammas().iter())
|
||||
.map(|(rm_i, gamma_i)| gamma_i * rm_i)
|
||||
.sum::<G1Projective>();
|
||||
|
||||
let zkcm_pedcom = r_pedcom_openings
|
||||
.iter()
|
||||
.zip(r_attributes.iter())
|
||||
.map(|(o_j, m_j)| params.gen1() * o_j + instance.h * m_j)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_user_sk = params.gen1() * r_attributes[0];
|
||||
|
||||
// covert to bytes
|
||||
let gammas_bytes = params
|
||||
.gammas()
|
||||
.iter()
|
||||
.map(|gamma| gamma.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_pedcom_bytes = zkcm_pedcom
|
||||
.iter()
|
||||
.map(|cm| cm.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// compute zkp challenge using g1, gammas, c, h, c1, c2, c3, zk commitments
|
||||
let challenge = compute_challenge::<ChallengeDigest, _, _>(
|
||||
std::iter::once(params.gen1().to_bytes().as_ref())
|
||||
.chain(gammas_bytes.iter().map(|gamma| gamma.as_ref()))
|
||||
.chain(std::iter::once(instance.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_com.to_bytes().as_ref()))
|
||||
.chain(zkcm_pedcom_bytes.iter().map(|c| c.as_ref()))
|
||||
.chain(std::iter::once(zkcm_user_sk.to_bytes().as_ref())),
|
||||
);
|
||||
|
||||
// compute response
|
||||
let response_opening = produce_response(&r_com_opening, &challenge, &witness.com_opening);
|
||||
let response_openings = produce_responses(
|
||||
&r_pedcom_openings,
|
||||
&challenge,
|
||||
&witness.pc_coms_openings.iter().collect::<Vec<_>>(),
|
||||
);
|
||||
let response_attributes = produce_responses(
|
||||
&r_attributes,
|
||||
&challenge,
|
||||
&witness.attributes.iter().collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
WithdrawalReqProof {
|
||||
challenge,
|
||||
response_opening,
|
||||
response_openings,
|
||||
response_attributes,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn verify(&self, params: &Parameters, instance: &WithdrawalReqInstance) -> bool {
|
||||
// recompute zk commitments for each instance
|
||||
let zkcm_com = instance.com * self.challenge
|
||||
+ params.gen1() * self.response_opening
|
||||
+ self
|
||||
.response_attributes
|
||||
.iter()
|
||||
.zip(params.gammas().iter())
|
||||
.map(|(m_i, gamma_i)| gamma_i * m_i)
|
||||
.sum::<G1Projective>();
|
||||
|
||||
let zkcm_pedcom = izip!(
|
||||
instance.pc_coms.iter(),
|
||||
self.response_openings.iter(),
|
||||
self.response_attributes.iter()
|
||||
)
|
||||
.map(|(cm_j, resp_o_j, resp_m_j)| {
|
||||
cm_j * self.challenge + params.gen1() * resp_o_j + instance.h * resp_m_j
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zk_commitment_user_sk =
|
||||
instance.pk_user.pk * self.challenge + params.gen1() * self.response_attributes[0];
|
||||
|
||||
// covert to bytes
|
||||
let gammas_bytes = params
|
||||
.gammas()
|
||||
.iter()
|
||||
.map(|gamma| gamma.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_pedcom_bytes = zkcm_pedcom
|
||||
.iter()
|
||||
.map(|cm| cm.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// recompute zkp challenge
|
||||
let challenge = compute_challenge::<ChallengeDigest, _, _>(
|
||||
std::iter::once(params.gen1().to_bytes().as_ref())
|
||||
.chain(gammas_bytes.iter().map(|hs| hs.as_ref()))
|
||||
.chain(std::iter::once(instance.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_com.to_bytes().as_ref()))
|
||||
.chain(zkcm_pedcom_bytes.iter().map(|c| c.as_ref()))
|
||||
.chain(std::iter::once(zk_commitment_user_sk.to_bytes().as_ref())),
|
||||
);
|
||||
|
||||
challenge == self.challenge
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use group::Group;
|
||||
use rand::thread_rng;
|
||||
|
||||
use crate::utils::hash_g1;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn withdrawal_request_instance_roundtrip() {
|
||||
let mut rng = thread_rng();
|
||||
let params = Parameters::new().unwrap();
|
||||
let instance = WithdrawalReqInstance {
|
||||
com: G1Projective::random(&mut rng),
|
||||
h: G1Projective::random(&mut rng),
|
||||
pc_coms: vec![
|
||||
G1Projective::random(&mut rng),
|
||||
G1Projective::random(&mut rng),
|
||||
G1Projective::random(&mut rng),
|
||||
],
|
||||
pk_user: PublicKeyUser {
|
||||
pk: params.gen1() * params.random_scalar(),
|
||||
},
|
||||
};
|
||||
|
||||
let instance_bytes = instance.to_bytes();
|
||||
let instance_p = WithdrawalReqInstance::from_bytes(&instance_bytes).unwrap();
|
||||
assert_eq!(instance, instance_p)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn withdrawal_proof_construct_and_verify() {
|
||||
let mut rng = thread_rng();
|
||||
let params = Parameters::new().unwrap();
|
||||
let sk = params.random_scalar();
|
||||
let pk_user = PublicKeyUser {
|
||||
pk: params.gen1() * sk,
|
||||
};
|
||||
let v = params.random_scalar();
|
||||
let t = params.random_scalar();
|
||||
let attr = vec![sk, v, t];
|
||||
|
||||
let com_opening = params.random_scalar();
|
||||
let com = params.gen1() * com_opening
|
||||
+ attr
|
||||
.iter()
|
||||
.zip(params.gammas())
|
||||
.map(|(&m, gamma)| gamma * m)
|
||||
.sum::<G1Projective>();
|
||||
let h = hash_g1(com.to_bytes());
|
||||
|
||||
let pc_openings = params.n_random_scalars(attr.len());
|
||||
let pc_coms = pc_openings
|
||||
.iter()
|
||||
.zip(attr.iter())
|
||||
.map(|(o_j, m_j)| params.gen1() * o_j + h * m_j)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let instance = WithdrawalReqInstance {
|
||||
com,
|
||||
h,
|
||||
pc_coms,
|
||||
pk_user,
|
||||
};
|
||||
|
||||
let witness = WithdrawalReqWitness {
|
||||
attributes: attr,
|
||||
com_opening,
|
||||
pc_coms_openings: pc_openings,
|
||||
};
|
||||
let zk_proof = WithdrawalReqProof::construct(¶ms, &instance, &witness);
|
||||
assert!(zk_proof.verify(¶ms, &instance))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
use bls12_381::{G1Projective, G2Projective, Scalar};
|
||||
|
||||
use crate::scheme::keygen::SecretKeyUser;
|
||||
use crate::scheme::setup::Parameters;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct SpendInstance {
|
||||
pub kappa: G2Projective,
|
||||
pub A: G1Projective,
|
||||
pub C: G1Projective,
|
||||
pub D: G1Projective,
|
||||
pub S: G1Projective,
|
||||
pub T: G1Projective,
|
||||
}
|
||||
|
||||
pub struct SpendWitness {
|
||||
// includes skUser, v, t
|
||||
pub attributes: Vec<Scalar>,
|
||||
// signature randomizing element
|
||||
pub r: Scalar,
|
||||
pub l: u64,
|
||||
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 struct SpendProof {}
|
||||
|
||||
impl SpendProof {
|
||||
pub fn construct(params: &Parameters,
|
||||
instance: &SpendInstance,
|
||||
witness: &SpendWitness, ) {}
|
||||
pub fn verify() {}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
use bls12_381::{G1Projective, Scalar};
|
||||
use group::GroupEncoding;
|
||||
use itertools::izip;
|
||||
|
||||
use crate::error::{CompactEcashError, Result};
|
||||
use crate::proofs::{ChallengeDigest, compute_challenge, produce_response, produce_responses};
|
||||
use crate::scheme::keygen::PublicKeyUser;
|
||||
use crate::scheme::setup::Parameters;
|
||||
use crate::utils::try_deserialize_g1_projective;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
// instance: g, gamma1, gamma2, gamma3, com, h, com1, com2, com3, pkUser
|
||||
pub struct WithdrawalReqInstance {
|
||||
// Joined commitment to all attributes
|
||||
pub com: G1Projective,
|
||||
// Hash of the joined commitment com
|
||||
pub h: G1Projective,
|
||||
// Pedersen commitments to each attribute
|
||||
pub pc_coms: Vec<G1Projective>,
|
||||
// Public key of a user
|
||||
pub pk_user: PublicKeyUser,
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for WithdrawalReqInstance {
|
||||
type Error = CompactEcashError;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<WithdrawalReqInstance> {
|
||||
if bytes.len() < 48 * 4 + 8 || (bytes.len() - 8) % 48 != 0 {
|
||||
return Err(CompactEcashError::DeserializationInvalidLength {
|
||||
actual: bytes.len(),
|
||||
modulus_target: bytes.len() - 8,
|
||||
target: 48 * 4 + 8,
|
||||
modulus: 48,
|
||||
object: "secret key".to_string(),
|
||||
});
|
||||
}
|
||||
let com_bytes: [u8; 48] = bytes[..48].try_into().unwrap();
|
||||
let com = try_deserialize_g1_projective(
|
||||
&com_bytes,
|
||||
CompactEcashError::Deserialization("Failed to deserialize com".to_string()),
|
||||
)?;
|
||||
let h_bytes: [u8; 48] = bytes[48..96].try_into().unwrap();
|
||||
let h = try_deserialize_g1_projective(
|
||||
&h_bytes,
|
||||
CompactEcashError::Deserialization("Failed to deserialize h".to_string()),
|
||||
)?;
|
||||
let pc_coms_len = u64::from_le_bytes(bytes[96..104].try_into().unwrap());
|
||||
let actual_pc_coms_len = (bytes.len() - 152) / 48;
|
||||
if pc_coms_len as usize != actual_pc_coms_len {
|
||||
return Err(CompactEcashError::Deserialization(format!(
|
||||
"Tried to deserialize pedersen commitments with inconsistent pc_coms_len (expected {}, got {})",
|
||||
pc_coms_len, actual_pc_coms_len
|
||||
)));
|
||||
}
|
||||
let mut pc_coms = Vec::new();
|
||||
let mut pc_coms_end: usize = 0;
|
||||
for i in 0..pc_coms_len {
|
||||
let start = (104 + i * 48) as usize;
|
||||
let end = (start + 48) as usize;
|
||||
let pc_i_bytes = bytes[start..end].try_into().unwrap();
|
||||
let pc_i = try_deserialize_g1_projective(
|
||||
&pc_i_bytes,
|
||||
CompactEcashError::Deserialization(
|
||||
"Failed to deserialize pedersen commitment".to_string(),
|
||||
),
|
||||
)?;
|
||||
pc_coms_end = end;
|
||||
pc_coms.push(pc_i);
|
||||
}
|
||||
let pk_bytes = bytes[pc_coms_end..].try_into().unwrap();
|
||||
let pk = try_deserialize_g1_projective(
|
||||
&pk_bytes,
|
||||
CompactEcashError::Deserialization(
|
||||
"Failed to deserialize user's public key".to_string(),
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(WithdrawalReqInstance {
|
||||
com,
|
||||
h,
|
||||
pc_coms,
|
||||
pk_user: PublicKeyUser { pk },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WithdrawalReqInstance {
|
||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||
let pc_coms_len = self.pc_coms.len();
|
||||
let mut bytes = Vec::with_capacity(8 + (pc_coms_len + 3) as usize * 48);
|
||||
bytes.extend_from_slice(self.com.to_bytes().as_ref());
|
||||
bytes.extend_from_slice(self.h.to_bytes().as_ref());
|
||||
bytes.extend_from_slice(&pc_coms_len.to_le_bytes());
|
||||
for pc in self.pc_coms.iter() {
|
||||
bytes.extend_from_slice((pc.to_bytes()).as_ref());
|
||||
}
|
||||
bytes.extend_from_slice(self.pk_user.pk.to_bytes().as_ref());
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<WithdrawalReqInstance> {
|
||||
WithdrawalReqInstance::try_from(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
// witness: m1, m2, m3, o, o1, o2, o3,
|
||||
pub struct WithdrawalReqWitness {
|
||||
pub attributes: Vec<Scalar>,
|
||||
// Opening for the joined commitment com
|
||||
pub com_opening: Scalar,
|
||||
// Openings for the pedersen commitments
|
||||
pub pc_coms_openings: Vec<Scalar>,
|
||||
}
|
||||
|
||||
pub struct WithdrawalReqProof {
|
||||
challenge: Scalar,
|
||||
response_opening: Scalar,
|
||||
response_openings: Vec<Scalar>,
|
||||
response_attributes: Vec<Scalar>,
|
||||
}
|
||||
|
||||
impl WithdrawalReqProof {
|
||||
pub(crate) fn construct(
|
||||
params: &Parameters,
|
||||
instance: &WithdrawalReqInstance,
|
||||
witness: &WithdrawalReqWitness,
|
||||
) -> Self {
|
||||
// generate random values to replace the witnesses
|
||||
let r_com_opening = params.random_scalar();
|
||||
let r_pedcom_openings = params.n_random_scalars(witness.pc_coms_openings.len());
|
||||
let r_attributes = params.n_random_scalars(witness.attributes.len());
|
||||
|
||||
// compute zkp commitments for each instance
|
||||
let zkcm_com = params.gen1() * r_com_opening
|
||||
+ r_attributes
|
||||
.iter()
|
||||
.zip(params.gammas().iter())
|
||||
.map(|(rm_i, gamma_i)| gamma_i * rm_i)
|
||||
.sum::<G1Projective>();
|
||||
|
||||
let zkcm_pedcom = r_pedcom_openings
|
||||
.iter()
|
||||
.zip(r_attributes.iter())
|
||||
.map(|(o_j, m_j)| params.gen1() * o_j + instance.h * m_j)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_user_sk = params.gen1() * r_attributes[0];
|
||||
|
||||
// covert to bytes
|
||||
let gammas_bytes = params
|
||||
.gammas()
|
||||
.iter()
|
||||
.map(|gamma| gamma.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_pedcom_bytes = zkcm_pedcom
|
||||
.iter()
|
||||
.map(|cm| cm.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// compute zkp challenge using g1, gammas, c, h, c1, c2, c3, zk commitments
|
||||
let challenge = compute_challenge::<ChallengeDigest, _, _>(
|
||||
std::iter::once(params.gen1().to_bytes().as_ref())
|
||||
.chain(gammas_bytes.iter().map(|gamma| gamma.as_ref()))
|
||||
.chain(std::iter::once(instance.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_com.to_bytes().as_ref()))
|
||||
.chain(zkcm_pedcom_bytes.iter().map(|c| c.as_ref()))
|
||||
.chain(std::iter::once(zkcm_user_sk.to_bytes().as_ref())),
|
||||
);
|
||||
|
||||
// compute response
|
||||
let response_opening = produce_response(&r_com_opening, &challenge, &witness.com_opening);
|
||||
let response_openings = produce_responses(
|
||||
&r_pedcom_openings,
|
||||
&challenge,
|
||||
&witness.pc_coms_openings.iter().collect::<Vec<_>>(),
|
||||
);
|
||||
let response_attributes = produce_responses(
|
||||
&r_attributes,
|
||||
&challenge,
|
||||
&witness.attributes.iter().collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
WithdrawalReqProof {
|
||||
challenge,
|
||||
response_opening,
|
||||
response_openings,
|
||||
response_attributes,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn verify(&self, params: &Parameters, instance: &WithdrawalReqInstance) -> bool {
|
||||
// recompute zk commitments for each instance
|
||||
let zkcm_com = instance.com * self.challenge
|
||||
+ params.gen1() * self.response_opening
|
||||
+ self
|
||||
.response_attributes
|
||||
.iter()
|
||||
.zip(params.gammas().iter())
|
||||
.map(|(m_i, gamma_i)| gamma_i * m_i)
|
||||
.sum::<G1Projective>();
|
||||
|
||||
let zkcm_pedcom = izip!(
|
||||
instance.pc_coms.iter(),
|
||||
self.response_openings.iter(),
|
||||
self.response_attributes.iter()
|
||||
)
|
||||
.map(|(cm_j, resp_o_j, resp_m_j)| {
|
||||
cm_j * self.challenge + params.gen1() * resp_o_j + instance.h * resp_m_j
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zk_commitment_user_sk =
|
||||
instance.pk_user.pk * self.challenge + params.gen1() * self.response_attributes[0];
|
||||
|
||||
// covert to bytes
|
||||
let gammas_bytes = params
|
||||
.gammas()
|
||||
.iter()
|
||||
.map(|gamma| gamma.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let zkcm_pedcom_bytes = zkcm_pedcom
|
||||
.iter()
|
||||
.map(|cm| cm.to_bytes())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// recompute zkp challenge
|
||||
let challenge = compute_challenge::<ChallengeDigest, _, _>(
|
||||
std::iter::once(params.gen1().to_bytes().as_ref())
|
||||
.chain(gammas_bytes.iter().map(|hs| hs.as_ref()))
|
||||
.chain(std::iter::once(instance.to_bytes().as_ref()))
|
||||
.chain(std::iter::once(zkcm_com.to_bytes().as_ref()))
|
||||
.chain(zkcm_pedcom_bytes.iter().map(|c| c.as_ref()))
|
||||
.chain(std::iter::once(zk_commitment_user_sk.to_bytes().as_ref())),
|
||||
);
|
||||
|
||||
challenge == self.challenge
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use group::Group;
|
||||
use rand::thread_rng;
|
||||
|
||||
use crate::utils::hash_g1;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn withdrawal_request_instance_roundtrip() {
|
||||
let mut rng = thread_rng();
|
||||
let params = Parameters::new().unwrap();
|
||||
let instance = WithdrawalReqInstance {
|
||||
com: G1Projective::random(&mut rng),
|
||||
h: G1Projective::random(&mut rng),
|
||||
pc_coms: vec![
|
||||
G1Projective::random(&mut rng),
|
||||
G1Projective::random(&mut rng),
|
||||
G1Projective::random(&mut rng),
|
||||
],
|
||||
pk_user: PublicKeyUser {
|
||||
pk: params.gen1() * params.random_scalar(),
|
||||
},
|
||||
};
|
||||
|
||||
let instance_bytes = instance.to_bytes();
|
||||
let instance_p = WithdrawalReqInstance::from_bytes(&instance_bytes).unwrap();
|
||||
assert_eq!(instance, instance_p)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn withdrawal_proof_construct_and_verify() {
|
||||
let rng = thread_rng();
|
||||
let params = Parameters::new().unwrap();
|
||||
let sk = params.random_scalar();
|
||||
let pk_user = PublicKeyUser {
|
||||
pk: params.gen1() * sk,
|
||||
};
|
||||
let v = params.random_scalar();
|
||||
let t = params.random_scalar();
|
||||
let attr = vec![sk, v, t];
|
||||
|
||||
let com_opening = params.random_scalar();
|
||||
let com = params.gen1() * com_opening
|
||||
+ attr
|
||||
.iter()
|
||||
.zip(params.gammas())
|
||||
.map(|(&m, gamma)| gamma * m)
|
||||
.sum::<G1Projective>();
|
||||
let h = hash_g1(com.to_bytes());
|
||||
|
||||
let pc_openings = params.n_random_scalars(attr.len());
|
||||
let pc_coms = pc_openings
|
||||
.iter()
|
||||
.zip(attr.iter())
|
||||
.map(|(o_j, m_j)| params.gen1() * o_j + h * m_j)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let instance = WithdrawalReqInstance {
|
||||
com,
|
||||
h,
|
||||
pc_coms,
|
||||
pk_user,
|
||||
};
|
||||
|
||||
let witness = WithdrawalReqWitness {
|
||||
attributes: attr,
|
||||
com_opening,
|
||||
pc_coms_openings: pc_openings,
|
||||
};
|
||||
let zk_proof = WithdrawalReqProof::construct(¶ms, &instance, &witness);
|
||||
assert!(zk_proof.verify(¶ms, &instance))
|
||||
}
|
||||
}
|
||||
@@ -144,6 +144,7 @@ impl Wallet {
|
||||
pub fn signature(&self) -> &Signature { &self.sig }
|
||||
pub fn v(&self) -> Scalar { self.v }
|
||||
pub fn t(&self) -> Scalar { self.t }
|
||||
pub fn l(&self) -> u64 { self.l.get() }
|
||||
fn up(&self) {
|
||||
self.l.set(self.l.get() + 1);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::error::Result;
|
||||
use crate::utils::hash_g1;
|
||||
|
||||
const ATTRIBUTES_LEN: usize = 3;
|
||||
const MAX_COIN_VALUE: usize = 32;
|
||||
const MAX_COIN_VALUE: u64 = 32;
|
||||
|
||||
pub struct Parameters {
|
||||
/// Generator of the G1 group
|
||||
@@ -15,9 +15,9 @@ pub struct Parameters {
|
||||
/// Generator of the G2 group
|
||||
g2: G2Affine,
|
||||
/// Additional generators of the G1 group
|
||||
gammas: Vec<G1Affine>,
|
||||
gammas: Vec<G1Projective>,
|
||||
/// Value of wallet
|
||||
L: usize,
|
||||
L: u64,
|
||||
/// Precomputed G2 generator used for the miller loop
|
||||
_g2_prepared_miller: G2Prepared,
|
||||
}
|
||||
@@ -25,7 +25,7 @@ pub struct Parameters {
|
||||
impl Parameters {
|
||||
pub fn new() -> Result<Parameters> {
|
||||
let gammas = (1..=ATTRIBUTES_LEN)
|
||||
.map(|i| hash_g1(format!("gamma{}", i)).to_affine())
|
||||
.map(|i| hash_g1(format!("gamma{}", i)))
|
||||
.collect();
|
||||
Ok(Parameters {
|
||||
g1: G1Affine::generator(),
|
||||
@@ -44,10 +44,18 @@ impl Parameters {
|
||||
&self.g2
|
||||
}
|
||||
|
||||
pub(crate) fn gammas(&self) -> &Vec<G1Affine> {
|
||||
pub(crate) fn gammas(&self) -> &Vec<G1Projective> {
|
||||
&self.gammas
|
||||
}
|
||||
|
||||
pub(crate) fn gamma1(&self) -> Option<&G1Projective> { self.gammas.get(1) }
|
||||
|
||||
pub(crate) fn gamma2(&self) -> Option<&G1Projective> { self.gammas.get(2) }
|
||||
|
||||
pub(crate) fn gamma3(&self) -> Option<&G1Projective> { self.gammas.get(3) }
|
||||
|
||||
pub(crate) fn L(&self) -> u64 { self.L }
|
||||
|
||||
pub fn random_scalar(&self) -> Scalar {
|
||||
// lazily-initialized thread-local random number generator, seeded by the system
|
||||
let mut rng = thread_rng();
|
||||
|
||||
@@ -1,10 +1,106 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use bls12_381::{G1Projective, G2Projective, Scalar};
|
||||
|
||||
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::Parameters;
|
||||
use crate::scheme::Wallet;
|
||||
use crate::utils::hash_to_scalar;
|
||||
|
||||
pub struct PayInfo {}
|
||||
pub struct PayInfo {
|
||||
info: [u8; 32],
|
||||
}
|
||||
|
||||
pub fn spend(wallet: &Wallet, verification_key: &VerificationKeyAuth, skUser: &SecretKeyUser, payInfo: &PayInfo) {
|
||||
//
|
||||
pub fn pseudorandom_fgv(params: &Parameters, v: Scalar, l: u64) -> G1Projective {
|
||||
let pow = (v + Scalar::from(l) + Scalar::from(1)).neg();
|
||||
params.gen1() * pow
|
||||
}
|
||||
|
||||
pub fn pseudorandom_fgt(params: &Parameters, t: Scalar, l: u64) -> G1Projective {
|
||||
let pow = (t + Scalar::from(l) + Scalar::from(1)).neg();
|
||||
params.gen1() * pow
|
||||
}
|
||||
|
||||
pub fn compute_kappa(
|
||||
params: &Parameters,
|
||||
verification_key: &VerificationKeyAuth,
|
||||
attributes: &[Attribute],
|
||||
blinding_factor: Scalar,
|
||||
) -> G2Projective {
|
||||
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>()
|
||||
}
|
||||
|
||||
pub fn spend(params: &Parameters, wallet: &Wallet, verification_key: &VerificationKeyAuth, skUser: &SecretKeyUser, payInfo: &PayInfo) -> Result<()> {
|
||||
if wallet.l() > params.L() {
|
||||
return Err(CompactEcashError::Spend(
|
||||
"The counter l is higher than max L".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// randomize signature in the wallet
|
||||
let (signature_prime, sign_blinding_factor) = wallet.signature().randomise(params);
|
||||
// construct kappa i.e., blinded attributes for show
|
||||
let attributes = vec![skUser.sk, wallet.v(), wallet.t()];
|
||||
let kappa = compute_kappa(¶ms, &verification_key, &attributes, sign_blinding_factor);
|
||||
|
||||
// pick random openings o_a, o_c, o_d
|
||||
let o_a = params.random_scalar();
|
||||
let o_c = params.random_scalar();
|
||||
let o_d = params.random_scalar();
|
||||
|
||||
// compute commitments A, C, D
|
||||
let A = params.gen1() * o_a + params.gamma1().unwrap() * Scalar::from(wallet.l());
|
||||
let C = params.gen1() * o_c + params.gamma1().unwrap() * wallet.v();
|
||||
let D = params.gen1() * o_d + params.gamma1().unwrap() * wallet.t();
|
||||
|
||||
// compute hash of the payment info
|
||||
let R = hash_to_scalar(payInfo.info);
|
||||
|
||||
// evaluate the pseudorandom functions
|
||||
let S = pseudorandom_fgv(¶ms, wallet.v(), wallet.l());
|
||||
let T = params.gen1() * skUser.sk + pseudorandom_fgt(¶ms, wallet.t(), wallet.l()) * R;
|
||||
|
||||
// compute values mu, o_mu, lambda, o_lambda
|
||||
let mu: Scalar = (wallet.v() + Scalar::from(wallet.l()) + Scalar::from(1)).neg();
|
||||
let o_mu = ((o_a + o_c) * mu).neg();
|
||||
let lambda = (wallet.t() + Scalar::from(wallet.l()) + Scalar::from(1)).neg();
|
||||
let o_lambda = ((o_a + o_d) * lambda).neg();
|
||||
|
||||
// construct the zkp proof
|
||||
let spendInstance = SpendInstance {
|
||||
kappa,
|
||||
A,
|
||||
C,
|
||||
D,
|
||||
S,
|
||||
T,
|
||||
};
|
||||
let spendWitness = SpendWitness {
|
||||
attributes,
|
||||
r: sign_blinding_factor,
|
||||
l: wallet.l(),
|
||||
o_a,
|
||||
o_c,
|
||||
o_d,
|
||||
mu,
|
||||
lambda,
|
||||
o_mu,
|
||||
o_lambda,
|
||||
};
|
||||
let zkp = SpendProof::construct(¶ms, &spendInstance, &spendWitness);
|
||||
|
||||
// output pay and updated wallet
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn spend_verify() {}
|
||||
|
||||
@@ -2,7 +2,7 @@ use bls12_381::{G1Projective, G2Prepared, G2Projective, Scalar};
|
||||
use group::{Curve, GroupEncoding};
|
||||
|
||||
use crate::error::{CompactEcashError, Result};
|
||||
use crate::proofs::{WithdrawalReqInstance, WithdrawalReqProof, WithdrawalReqWitness};
|
||||
use crate::proofs::proof_withdrawal::{WithdrawalReqInstance, WithdrawalReqProof, WithdrawalReqWitness};
|
||||
use crate::scheme::{BlindedSignature, PartialWallet, Signature};
|
||||
use crate::scheme::keygen::{PublicKeyUser, SecretKeyAuth, SecretKeyUser, VerificationKeyAuth};
|
||||
use crate::scheme::keygen::ttp_keygen;
|
||||
|
||||
@@ -5,9 +5,10 @@ use core::ops::Neg;
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use bls12_381::{multi_miller_loop, G1Affine, G2Prepared, G2Projective, Scalar};
|
||||
use bls12_381::{G1Affine, G2Prepared, G2Projective, multi_miller_loop, Scalar};
|
||||
use group::{Curve, Group};
|
||||
|
||||
use crate::Attribute;
|
||||
use crate::error::{CoconutError, Result};
|
||||
use crate::proofs::ProofKappaZeta;
|
||||
use crate::scheme::double_use::BlindedSerialNumber;
|
||||
@@ -16,7 +17,6 @@ use crate::scheme::Signature;
|
||||
use crate::scheme::VerificationKey;
|
||||
use crate::traits::{Base58, Bytable};
|
||||
use crate::utils::try_deserialize_g2_projective;
|
||||
use crate::Attribute;
|
||||
|
||||
// TODO NAMING: this whole thing
|
||||
// Theta
|
||||
@@ -136,10 +136,10 @@ pub fn compute_kappa(
|
||||
params.gen2() * blinding_factor
|
||||
+ verification_key.alpha
|
||||
+ private_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>()
|
||||
}
|
||||
|
||||
pub fn compute_zeta(params: &Parameters, serial_number: Attribute) -> G2Projective {
|
||||
@@ -296,11 +296,11 @@ pub fn verify(
|
||||
) -> bool {
|
||||
let kappa = (verification_key.alpha
|
||||
+ public_attributes
|
||||
.iter()
|
||||
.zip(verification_key.beta_g2.iter())
|
||||
.map(|(m_i, b_i)| b_i * m_i)
|
||||
.sum::<G2Projective>())
|
||||
.to_affine();
|
||||
.iter()
|
||||
.zip(verification_key.beta_g2.iter())
|
||||
.map(|(m_i, b_i)| b_i * m_i)
|
||||
.sum::<G2Projective>())
|
||||
.to_affine();
|
||||
|
||||
check_bilinear_pairing(
|
||||
&sig.0.to_affine(),
|
||||
@@ -345,7 +345,7 @@ mod tests {
|
||||
serial_number,
|
||||
binding_number,
|
||||
)
|
||||
.unwrap();
|
||||
.unwrap();
|
||||
|
||||
let bytes = theta.to_bytes();
|
||||
assert_eq!(Theta::try_from(bytes.as_slice()).unwrap(), theta);
|
||||
|
||||
Reference in New Issue
Block a user