Zk token sdk clean decryption (#23478)
* zk-token-sdk: add decryption for pod elgamal ciphertexts * zk-token-sdk: add decryption for pod elgamal ciphertexts * zk-token-sdk: cargo fmt * zk-token-sdk: minor update to docs * zk-token-sdk: minor * zk-token-sdk: fix bpf build error * zk-token-sdk: more simplifying discrete log * zk-token-sdk: fmt * zk-token-sdk: minor update to doc
This commit is contained in:
parent
09b58e1cfb
commit
d2b23da9ea
Binary file not shown.
Before Width: | Height: | Size: 9.0 MiB After Width: | Height: | Size: 2.1 MiB |
|
@ -6,10 +6,13 @@ use {
|
||||||
std::collections::HashMap,
|
std::collections::HashMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
const TWO15: u32 = 32768;
|
#[allow(dead_code)]
|
||||||
const TWO14: u32 = 16384; // 2^14
|
const TWO15: u64 = 32768;
|
||||||
// const TWO16: u32 = 65536; // 2^16
|
#[allow(dead_code)]
|
||||||
const TWO18: u32 = 262144; // 2^18
|
const TWO14: u64 = 16384; // 2^14
|
||||||
|
const TWO16: u64 = 65536; // 2^16
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const TWO18: u64 = 262144; // 2^18
|
||||||
|
|
||||||
/// Type that captures a discrete log challenge.
|
/// Type that captures a discrete log challenge.
|
||||||
///
|
///
|
||||||
|
@ -23,65 +26,58 @@ pub struct DiscreteLog {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
pub struct DecodeU32Precomputation(HashMap<[u8; 32], u32>);
|
pub struct DecodePrecomputation(HashMap<[u8; 32], u16>);
|
||||||
|
|
||||||
/// Builds a HashMap of 2^18 elements
|
/// Builds a HashMap of 2^16 elements
|
||||||
fn decode_u32_precomputation(generator: RistrettoPoint) -> DecodeU32Precomputation {
|
#[allow(dead_code)]
|
||||||
|
fn decode_u32_precomputation(generator: RistrettoPoint) -> DecodePrecomputation {
|
||||||
let mut hashmap = HashMap::new();
|
let mut hashmap = HashMap::new();
|
||||||
|
|
||||||
let two12_scalar = Scalar::from(TWO14);
|
let two16_scalar = Scalar::from(TWO16);
|
||||||
let identity = RistrettoPoint::identity(); // 0 * G
|
let identity = RistrettoPoint::identity(); // 0 * G
|
||||||
let generator = two12_scalar * generator; // 2^12 * G
|
let generator = two16_scalar * generator; // 2^16 * G
|
||||||
|
|
||||||
// iterator for 2^12*0G , 2^12*1G, 2^12*2G, ...
|
// iterator for 2^12*0G , 2^12*1G, 2^12*2G, ...
|
||||||
let ristretto_iter = RistrettoIterator::new(identity, generator);
|
let ristretto_iter = RistrettoIterator::new(identity, generator);
|
||||||
let mut steps_for_breakpoint = 0;
|
ristretto_iter.zip(0..TWO16).for_each(|(elem, x_hi)| {
|
||||||
ristretto_iter.zip(0..TWO18).for_each(|(elem, x_hi)| {
|
|
||||||
let key = elem.compress().to_bytes();
|
let key = elem.compress().to_bytes();
|
||||||
hashmap.insert(key, x_hi);
|
hashmap.insert(key, x_hi as u16);
|
||||||
|
|
||||||
// unclean way to print status update; will clean up later
|
|
||||||
if x_hi % TWO15 == 0 {
|
|
||||||
println!(" [{:?}/8] completed", steps_for_breakpoint);
|
|
||||||
steps_for_breakpoint += 1;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
println!(" [8/8] completed");
|
|
||||||
|
|
||||||
DecodeU32Precomputation(hashmap)
|
DecodePrecomputation(hashmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
/// Pre-computed HashMap needed for decryption. The HashMap is independent of (works for) any key.
|
/// Pre-computed HashMap needed for decryption. The HashMap is independent of (works for) any key.
|
||||||
pub static ref DECODE_U32_PRECOMPUTATION_FOR_G: DecodeU32Precomputation = {
|
pub static ref DECODE_PRECOMPUTATION_FOR_G: DecodePrecomputation = {
|
||||||
static DECODE_U32_PRECOMPUTATION_FOR_G_BINCODE: &[u8] =
|
static DECODE_PRECOMPUTATION_FOR_G_BINCODE: &[u8] =
|
||||||
include_bytes!("decode_u32_precomputation_for_G.bincode");
|
include_bytes!("decode_u32_precomputation_for_G.bincode");
|
||||||
bincode::deserialize(DECODE_U32_PRECOMPUTATION_FOR_G_BINCODE).unwrap_or_default()
|
bincode::deserialize(DECODE_PRECOMPUTATION_FOR_G_BINCODE).unwrap_or_default()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Solves the discrete log instance using a 18/14 bit offline/online split
|
/// Solves the discrete log instance using a 16/16 bit offline/online split
|
||||||
impl DiscreteLog {
|
impl DiscreteLog {
|
||||||
/// Solves the discrete log problem under the assumption that the solution
|
/// Solves the discrete log problem under the assumption that the solution
|
||||||
/// is a 32-bit number.
|
/// is a 32-bit number.
|
||||||
pub(crate) fn decode_u32(self) -> Option<u32> {
|
pub(crate) fn decode_u32(self) -> Option<u64> {
|
||||||
self.decode_u32_online(&decode_u32_precomputation(self.generator))
|
self.decode_online(&DECODE_PRECOMPUTATION_FOR_G, TWO16)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Solves the discrete log instance using the pre-computed HashMap by enumerating through 2^14
|
pub fn decode_online(self, hashmap: &DecodePrecomputation, solution_bound: u64) -> Option<u64> {
|
||||||
/// possible solutions
|
|
||||||
pub fn decode_u32_online(self, hashmap: &DecodeU32Precomputation) -> Option<u32> {
|
|
||||||
// iterator for 0G, -1G, -2G, ...
|
// iterator for 0G, -1G, -2G, ...
|
||||||
let ristretto_iter = RistrettoIterator::new(self.target, -self.generator);
|
let ristretto_iter = RistrettoIterator::new(self.target, -self.generator);
|
||||||
|
|
||||||
let mut decoded = None;
|
let mut decoded = None;
|
||||||
ristretto_iter.zip(0..TWO14).for_each(|(elem, x_lo)| {
|
ristretto_iter
|
||||||
let key = elem.compress().to_bytes();
|
.zip(0..solution_bound)
|
||||||
if hashmap.0.contains_key(&key) {
|
.for_each(|(elem, x_lo)| {
|
||||||
let x_hi = hashmap.0[&key];
|
let key = elem.compress().to_bytes();
|
||||||
decoded = Some(x_lo + TWO14 * x_hi);
|
if hashmap.0.contains_key(&key) {
|
||||||
}
|
let x_hi = hashmap.0[&key];
|
||||||
});
|
decoded = Some(x_lo + solution_bound * x_hi as u64);
|
||||||
|
}
|
||||||
|
});
|
||||||
decoded
|
decoded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,7 +118,7 @@ mod tests {
|
||||||
fn test_serialize_decode_u32_precomputation_for_G() {
|
fn test_serialize_decode_u32_precomputation_for_G() {
|
||||||
let decode_u32_precomputation_for_G = decode_u32_precomputation(G);
|
let decode_u32_precomputation_for_G = decode_u32_precomputation(G);
|
||||||
|
|
||||||
if decode_u32_precomputation_for_G.0 != DECODE_U32_PRECOMPUTATION_FOR_G.0 {
|
if decode_u32_precomputation_for_G.0 != DECODE_PRECOMPUTATION_FOR_G.0 {
|
||||||
use std::{fs::File, io::Write, path::PathBuf};
|
use std::{fs::File, io::Write, path::PathBuf};
|
||||||
let mut f = File::create(PathBuf::from(
|
let mut f = File::create(PathBuf::from(
|
||||||
"src/encryption/decode_u32_precomputation_for_G.bincode",
|
"src/encryption/decode_u32_precomputation_for_G.bincode",
|
||||||
|
@ -134,14 +130,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Discrete log test for 16/16 split
|
|
||||||
///
|
|
||||||
/// Very informal measurements on my machine:
|
|
||||||
/// - 8 sec for precomputation
|
|
||||||
/// - 3 sec for online computation
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decode_correctness() {
|
fn test_decode_correctness() {
|
||||||
let amount: u32 = 65545;
|
let amount: u64 = 65545;
|
||||||
|
|
||||||
let instance = DiscreteLog {
|
let instance = DiscreteLog {
|
||||||
generator: G,
|
generator: G,
|
||||||
|
@ -154,7 +145,7 @@ mod tests {
|
||||||
let precomputation_secs = start_precomputation.elapsed().as_secs_f64();
|
let precomputation_secs = start_precomputation.elapsed().as_secs_f64();
|
||||||
|
|
||||||
let start_online = Instant::now();
|
let start_online = Instant::now();
|
||||||
let computed_amount = instance.decode_u32_online(&precomputed_hashmap).unwrap();
|
let computed_amount = instance.decode_online(&precomputed_hashmap, TWO16).unwrap();
|
||||||
let online_secs = start_online.elapsed().as_secs_f64();
|
let online_secs = start_online.elapsed().as_secs_f64();
|
||||||
|
|
||||||
assert_eq!(amount, computed_amount);
|
assert_eq!(amount, computed_amount);
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::encryption::{
|
crate::encryption::{
|
||||||
discrete_log::{DecodeU32Precomputation, DiscreteLog},
|
discrete_log::DiscreteLog,
|
||||||
pedersen::{Pedersen, PedersenCommitment, PedersenOpening, G, H},
|
pedersen::{Pedersen, PedersenCommitment, PedersenOpening, G, H},
|
||||||
},
|
},
|
||||||
arrayref::{array_ref, array_refs},
|
arrayref::{array_ref, array_refs},
|
||||||
|
@ -132,22 +132,10 @@ impl ElGamal {
|
||||||
/// On input a secret key and a ciphertext, the function returns the decrypted message
|
/// On input a secret key and a ciphertext, the function returns the decrypted message
|
||||||
/// interpretted as type `u32`.
|
/// interpretted as type `u32`.
|
||||||
#[cfg(not(target_arch = "bpf"))]
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
fn decrypt_u32(secret: &ElGamalSecretKey, ciphertext: &ElGamalCiphertext) -> Option<u32> {
|
fn decrypt_u32(secret: &ElGamalSecretKey, ciphertext: &ElGamalCiphertext) -> Option<u64> {
|
||||||
let discrete_log_instance = Self::decrypt(secret, ciphertext);
|
let discrete_log_instance = Self::decrypt(secret, ciphertext);
|
||||||
discrete_log_instance.decode_u32()
|
discrete_log_instance.decode_u32()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// On input a secret key, a ciphertext, and a pre-computed hashmap, the function returns the
|
|
||||||
/// decrypted message interpretted as type `u32`.
|
|
||||||
#[cfg(not(target_arch = "bpf"))]
|
|
||||||
fn decrypt_u32_online(
|
|
||||||
secret: &ElGamalSecretKey,
|
|
||||||
ciphertext: &ElGamalCiphertext,
|
|
||||||
hashmap: &DecodeU32Precomputation,
|
|
||||||
) -> Option<u32> {
|
|
||||||
let discrete_log_instance = Self::decrypt(secret, ciphertext);
|
|
||||||
discrete_log_instance.decode_u32_online(hashmap)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A (twisted) ElGamal encryption keypair.
|
/// A (twisted) ElGamal encryption keypair.
|
||||||
|
@ -375,20 +363,10 @@ impl ElGamalSecretKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypts a ciphertext using the ElGamal secret key interpretting the message as type `u32`.
|
/// Decrypts a ciphertext using the ElGamal secret key interpretting the message as type `u32`.
|
||||||
pub fn decrypt_u32(&self, ciphertext: &ElGamalCiphertext) -> Option<u32> {
|
pub fn decrypt_u32(&self, ciphertext: &ElGamalCiphertext) -> Option<u64> {
|
||||||
ElGamal::decrypt_u32(self, ciphertext)
|
ElGamal::decrypt_u32(self, ciphertext)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypts a ciphertext using the ElGamal secret key and a pre-computed hashmap. It
|
|
||||||
/// interprets the decrypted message as type `u32`.
|
|
||||||
pub fn decrypt_u32_online(
|
|
||||||
&self,
|
|
||||||
ciphertext: &ElGamalCiphertext,
|
|
||||||
hashmap: &DecodeU32Precomputation,
|
|
||||||
) -> Option<u32> {
|
|
||||||
ElGamal::decrypt_u32_online(self, ciphertext, hashmap)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8; 32] {
|
pub fn as_bytes(&self) -> &[u8; 32] {
|
||||||
self.0.as_bytes()
|
self.0.as_bytes()
|
||||||
}
|
}
|
||||||
|
@ -474,19 +452,9 @@ impl ElGamalCiphertext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypts the ciphertext using an ElGamal secret key interpretting the message as type `u32`.
|
/// Decrypts the ciphertext using an ElGamal secret key interpretting the message as type `u32`.
|
||||||
pub fn decrypt_u32(&self, secret: &ElGamalSecretKey) -> Option<u32> {
|
pub fn decrypt_u32(&self, secret: &ElGamalSecretKey) -> Option<u64> {
|
||||||
ElGamal::decrypt_u32(secret, self)
|
ElGamal::decrypt_u32(secret, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypts the ciphertext using an ElGamal secret key and a pre-computed hashmap. It
|
|
||||||
/// interprets the decrypted message as type `u32`.
|
|
||||||
pub fn decrypt_u32_online(
|
|
||||||
&self,
|
|
||||||
secret: &ElGamalSecretKey,
|
|
||||||
hashmap: &DecodeU32Precomputation,
|
|
||||||
) -> Option<u32> {
|
|
||||||
ElGamal::decrypt_u32_online(secret, self, hashmap)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Add<&'b ElGamalCiphertext> for &'a ElGamalCiphertext {
|
impl<'a, 'b> Add<&'b ElGamalCiphertext> for &'a ElGamalCiphertext {
|
||||||
|
@ -606,7 +574,7 @@ define_mul_variants!(LHS = DecryptHandle, RHS = Scalar, Output = DecryptHandle);
|
||||||
mod tests {
|
mod tests {
|
||||||
use {
|
use {
|
||||||
super::*,
|
super::*,
|
||||||
crate::encryption::{discrete_log::DECODE_U32_PRECOMPUTATION_FOR_G, pedersen::Pedersen},
|
crate::encryption::pedersen::Pedersen,
|
||||||
solana_sdk::{signature::Keypair, signer::null_signer::NullSigner},
|
solana_sdk::{signature::Keypair, signer::null_signer::NullSigner},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -622,12 +590,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(expected_instance, ElGamal::decrypt(&secret, &ciphertext));
|
assert_eq!(expected_instance, ElGamal::decrypt(&secret, &ciphertext));
|
||||||
assert_eq!(
|
assert_eq!(57_u64, secret.decrypt_u32(&ciphertext).unwrap());
|
||||||
57_u32,
|
|
||||||
secret
|
|
||||||
.decrypt_u32_online(&ciphertext, &(*DECODE_U32_PRECOMPUTATION_FOR_G))
|
|
||||||
.unwrap()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -4,7 +4,6 @@ use {
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: clean up errors for encryption
|
|
||||||
#[derive(Error, Clone, Debug, Eq, PartialEq)]
|
#[derive(Error, Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum ProofError {
|
pub enum ProofError {
|
||||||
#[error("proof generation failed")]
|
#[error("proof generation failed")]
|
||||||
|
|
|
@ -6,7 +6,6 @@ use {
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
encryption::{
|
encryption::{
|
||||||
discrete_log::*,
|
|
||||||
elgamal::{
|
elgamal::{
|
||||||
DecryptHandle, ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey, ElGamalSecretKey,
|
DecryptHandle, ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey, ElGamalSecretKey,
|
||||||
},
|
},
|
||||||
|
@ -211,11 +210,11 @@ impl TransferData {
|
||||||
let ciphertext_lo = self.ciphertext_lo(role)?;
|
let ciphertext_lo = self.ciphertext_lo(role)?;
|
||||||
let ciphertext_hi = self.ciphertext_hi(role)?;
|
let ciphertext_hi = self.ciphertext_hi(role)?;
|
||||||
|
|
||||||
let amount_lo = ciphertext_lo.decrypt_u32_online(sk, &DECODE_U32_PRECOMPUTATION_FOR_G);
|
let amount_lo = ciphertext_lo.decrypt_u32(sk);
|
||||||
let amount_hi = ciphertext_hi.decrypt_u32_online(sk, &DECODE_U32_PRECOMPUTATION_FOR_G);
|
let amount_hi = ciphertext_hi.decrypt_u32(sk);
|
||||||
|
|
||||||
if let (Some(amount_lo), Some(amount_hi)) = (amount_lo, amount_hi) {
|
if let (Some(amount_lo), Some(amount_hi)) = (amount_lo, amount_hi) {
|
||||||
Ok((amount_lo as u64) + (TWO_32 * amount_hi as u64))
|
Ok(amount_lo + TWO_32 * amount_hi)
|
||||||
} else {
|
} else {
|
||||||
Err(ProofError::Verification)
|
Err(ProofError::Verification)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ use {
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
encryption::{
|
encryption::{
|
||||||
discrete_log::*,
|
|
||||||
elgamal::{
|
elgamal::{
|
||||||
DecryptHandle, ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey, ElGamalSecretKey,
|
DecryptHandle, ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey, ElGamalSecretKey,
|
||||||
},
|
},
|
||||||
|
@ -33,7 +32,9 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(target_arch = "bpf"))]
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
const MAX_FEE_BASIS_POINTS: u64 = 10000;
|
const MAX_FEE_BASIS_POINTS: u64 = 10_000;
|
||||||
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
|
const ONE_IN_BASIS_POINTS: u128 = MAX_FEE_BASIS_POINTS as u128;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "bpf"))]
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
const TRANSFER_WITH_FEE_SOURCE_AMOUNT_BIT_LENGTH: usize = 64;
|
const TRANSFER_WITH_FEE_SOURCE_AMOUNT_BIT_LENGTH: usize = 64;
|
||||||
|
@ -121,7 +122,8 @@ impl TransferWithFeeData {
|
||||||
|
|
||||||
// calculate and encrypt fee
|
// calculate and encrypt fee
|
||||||
let (fee_amount, delta_fee) =
|
let (fee_amount, delta_fee) =
|
||||||
calculate_fee(transfer_amount, fee_parameters.fee_rate_basis_points);
|
calculate_fee(transfer_amount, fee_parameters.fee_rate_basis_points)
|
||||||
|
.ok_or(ProofError::Generation)?;
|
||||||
|
|
||||||
let below_max = u64::ct_gt(&fee_parameters.maximum_fee, &fee_amount);
|
let below_max = u64::ct_gt(&fee_parameters.maximum_fee, &fee_amount);
|
||||||
let fee_to_encrypt =
|
let fee_to_encrypt =
|
||||||
|
@ -219,11 +221,11 @@ impl TransferWithFeeData {
|
||||||
let ciphertext_lo = self.ciphertext_lo(role)?;
|
let ciphertext_lo = self.ciphertext_lo(role)?;
|
||||||
let ciphertext_hi = self.ciphertext_hi(role)?;
|
let ciphertext_hi = self.ciphertext_hi(role)?;
|
||||||
|
|
||||||
let amount_lo = ciphertext_lo.decrypt_u32_online(sk, &DECODE_U32_PRECOMPUTATION_FOR_G);
|
let amount_lo = ciphertext_lo.decrypt_u32(sk);
|
||||||
let amount_hi = ciphertext_hi.decrypt_u32_online(sk, &DECODE_U32_PRECOMPUTATION_FOR_G);
|
let amount_hi = ciphertext_hi.decrypt_u32(sk);
|
||||||
|
|
||||||
if let (Some(amount_lo), Some(amount_hi)) = (amount_lo, amount_hi) {
|
if let (Some(amount_lo), Some(amount_hi)) = (amount_lo, amount_hi) {
|
||||||
Ok((amount_lo as u64) + (TWO_32 * amount_hi as u64))
|
Ok(amount_lo + TWO_32 * amount_hi)
|
||||||
} else {
|
} else {
|
||||||
Err(ProofError::Verification)
|
Err(ProofError::Verification)
|
||||||
}
|
}
|
||||||
|
@ -632,17 +634,16 @@ impl FeeParameters {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "bpf"))]
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
fn calculate_fee(transfer_amount: u64, fee_rate_basis_points: u16) -> (u64, u64) {
|
fn calculate_fee(transfer_amount: u64, fee_rate_basis_points: u16) -> Option<(u64, u64)> {
|
||||||
let fee_scaled = (transfer_amount as u128) * (fee_rate_basis_points as u128);
|
let numerator = (transfer_amount as u128).checked_mul(fee_rate_basis_points as u128)?;
|
||||||
|
let mut fee = numerator.checked_div(ONE_IN_BASIS_POINTS)?;
|
||||||
let fee = (fee_scaled / MAX_FEE_BASIS_POINTS as u128) as u64;
|
let remainder = numerator.checked_rem(ONE_IN_BASIS_POINTS)?;
|
||||||
let rem = (fee_scaled % MAX_FEE_BASIS_POINTS as u128) as u64;
|
if remainder > 0 {
|
||||||
|
fee = fee.checked_add(1)?;
|
||||||
if rem == 0 {
|
|
||||||
(fee, rem)
|
|
||||||
} else {
|
|
||||||
(fee + 1, rem)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fee = u64::try_from(fee).ok()?;
|
||||||
|
Some((fee as u64, remainder as u64))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "bpf"))]
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
|
|
|
@ -400,7 +400,6 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn test_basic_correctness() {
|
fn test_basic_correctness() {
|
||||||
let n = 32;
|
let n = 32;
|
||||||
|
|
|
@ -48,7 +48,7 @@ pub struct CtxtCommEqualityProof {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[cfg(not(target_arch = "bpf"))]
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
impl CtxtCommEqualityProof {
|
impl CtxtCommEqualityProof {
|
||||||
/// Equality proof constructor.
|
/// Equality proof constructor. The proof is with respect to a ciphertext and commitment.
|
||||||
///
|
///
|
||||||
/// The function does *not* hash the public key, ciphertext, or commitment into the transcript.
|
/// The function does *not* hash the public key, ciphertext, or commitment into the transcript.
|
||||||
/// For security, the caller (the main protocol) should hash these public components prior to
|
/// For security, the caller (the main protocol) should hash these public components prior to
|
||||||
|
@ -119,7 +119,7 @@ impl CtxtCommEqualityProof {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equality proof verifier. TODO: wrt commitment
|
/// Equality proof verifier. The proof is with respect to a single ciphertext and commitment.
|
||||||
///
|
///
|
||||||
/// * `source_pubkey` - The ElGamal pubkey associated with the ciphertext to be proved
|
/// * `source_pubkey` - The ElGamal pubkey associated with the ciphertext to be proved
|
||||||
/// * `source_ciphertext` - The main ElGamal ciphertext to be proved
|
/// * `source_ciphertext` - The main ElGamal ciphertext to be proved
|
||||||
|
@ -245,7 +245,7 @@ pub struct CtxtCtxtEqualityProof {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[cfg(not(target_arch = "bpf"))]
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
impl CtxtCtxtEqualityProof {
|
impl CtxtCtxtEqualityProof {
|
||||||
/// Equality proof constructor.
|
/// Equality proof constructor. The proof is with respect to two ciphertexts.
|
||||||
///
|
///
|
||||||
/// The function does *not* hash the public key, ciphertext, or commitment into the transcript.
|
/// The function does *not* hash the public key, ciphertext, or commitment into the transcript.
|
||||||
/// For security, the caller (the main protocol) should hash these public components prior to
|
/// For security, the caller (the main protocol) should hash these public components prior to
|
||||||
|
@ -322,7 +322,7 @@ impl CtxtCtxtEqualityProof {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equality proof verifier.
|
/// Equality proof verifier. The proof is with respect to two ciphertexts.
|
||||||
///
|
///
|
||||||
/// * `source_pubkey` - The ElGamal pubkey associated with the first ciphertext to be proved
|
/// * `source_pubkey` - The ElGamal pubkey associated with the first ciphertext to be proved
|
||||||
/// * `destination_pubkey` - The ElGamal pubkey associated with the second ciphertext to be proved
|
/// * `destination_pubkey` - The ElGamal pubkey associated with the second ciphertext to be proved
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
|
use crate::{
|
||||||
|
encryption::elgamal::{ElGamalCiphertext, ElGamalSecretKey},
|
||||||
|
zk_token_elgamal::pod,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
|
impl pod::ElGamalCiphertext {
|
||||||
|
pub fn decrypt(self, secret_key: &ElGamalSecretKey) -> Option<u64> {
|
||||||
|
let deserialized_ciphertext: Option<ElGamalCiphertext> = self.try_into().ok();
|
||||||
|
if let Some(ciphertext) = deserialized_ciphertext {
|
||||||
|
ciphertext.decrypt_u32(secret_key)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use {super::*, crate::encryption::elgamal::ElGamalKeypair};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pod_decryption() {
|
||||||
|
let keypair = ElGamalKeypair::new_rand();
|
||||||
|
|
||||||
|
let pod_ciphertext = pod::ElGamalCiphertext([0u8; 64]);
|
||||||
|
assert_eq!(pod_ciphertext.decrypt(&keypair.secret).unwrap(), 0);
|
||||||
|
|
||||||
|
let amount = 55_u64;
|
||||||
|
let ciphertext = keypair.public.encrypt(amount);
|
||||||
|
let pod_ciphertext: pod::ElGamalCiphertext = ciphertext.into();
|
||||||
|
assert_eq!(pod_ciphertext.decrypt(&keypair.secret).unwrap(), 55);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
pub mod convert;
|
pub mod convert;
|
||||||
|
pub mod decryption;
|
||||||
pub mod ops;
|
pub mod ops;
|
||||||
pub mod pod;
|
pub mod pod;
|
||||||
|
|
Loading…
Reference in New Issue