2022-02-17 09:45:07 -08:00
|
|
|
use {
|
|
|
|
crate::zk_token_elgamal::pod,
|
|
|
|
bytemuck::{Pod, Zeroable},
|
|
|
|
};
|
2022-05-18 18:17:29 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-02-17 09:45:07 -08:00
|
|
|
use {
|
|
|
|
crate::{
|
|
|
|
encryption::{
|
|
|
|
elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
|
|
|
|
pedersen::PedersenOpening,
|
|
|
|
},
|
|
|
|
errors::ProofError,
|
|
|
|
instruction::Verifiable,
|
|
|
|
sigma_proofs::equality_proof::CtxtCtxtEqualityProof,
|
|
|
|
transcript::TranscriptProtocol,
|
|
|
|
},
|
|
|
|
merlin::Transcript,
|
|
|
|
std::convert::TryInto,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// This struct includes the cryptographic proof *and* the account data information needed to verify
|
|
|
|
/// the proof
|
|
|
|
///
|
2022-09-15 01:11:14 -07:00
|
|
|
/// - The pre-instruction should call WithdrawWithheldTokensData::verify_proof(&self)
|
|
|
|
/// - The actual program should check that the ciphertext in this struct is consistent with what is
|
|
|
|
/// currently stored in the confidential token account
|
2022-02-17 09:45:07 -08:00
|
|
|
///
|
|
|
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
|
|
|
#[repr(C)]
|
|
|
|
pub struct WithdrawWithheldTokensData {
|
2022-03-03 12:07:27 -08:00
|
|
|
pub withdraw_withheld_authority_pubkey: pod::ElGamalPubkey,
|
2022-02-17 09:45:07 -08:00
|
|
|
|
2022-03-03 12:07:27 -08:00
|
|
|
pub destination_pubkey: pod::ElGamalPubkey,
|
2022-02-17 09:45:07 -08:00
|
|
|
|
2022-03-03 12:07:27 -08:00
|
|
|
pub withdraw_withheld_authority_ciphertext: pod::ElGamalCiphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
|
2022-03-03 12:07:27 -08:00
|
|
|
pub destination_ciphertext: pod::ElGamalCiphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
|
|
|
|
pub proof: WithdrawWithheldTokensProof,
|
|
|
|
}
|
|
|
|
|
2022-10-14 04:15:20 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-02-17 09:45:07 -08:00
|
|
|
impl WithdrawWithheldTokensData {
|
|
|
|
pub fn new(
|
2022-03-03 12:07:27 -08:00
|
|
|
withdraw_withheld_authority_keypair: &ElGamalKeypair,
|
|
|
|
destination_pubkey: &ElGamalPubkey,
|
|
|
|
withdraw_withheld_authority_ciphertext: &ElGamalCiphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
amount: u64,
|
|
|
|
) -> Result<Self, ProofError> {
|
2022-04-11 09:53:31 -07:00
|
|
|
// encrypt withdraw amount under destination public key
|
2022-03-03 12:07:27 -08:00
|
|
|
let destination_opening = PedersenOpening::new_rand();
|
|
|
|
let destination_ciphertext = destination_pubkey.encrypt_with(amount, &destination_opening);
|
2022-02-17 09:45:07 -08:00
|
|
|
|
2022-03-03 12:07:27 -08:00
|
|
|
let pod_withdraw_withheld_authority_pubkey =
|
|
|
|
pod::ElGamalPubkey(withdraw_withheld_authority_keypair.public.to_bytes());
|
|
|
|
let pod_destination_pubkey = pod::ElGamalPubkey(destination_pubkey.to_bytes());
|
|
|
|
let pod_withdraw_withheld_authority_ciphertext =
|
|
|
|
pod::ElGamalCiphertext(withdraw_withheld_authority_ciphertext.to_bytes());
|
|
|
|
let pod_destination_ciphertext = pod::ElGamalCiphertext(destination_ciphertext.to_bytes());
|
2022-02-17 09:45:07 -08:00
|
|
|
|
|
|
|
let mut transcript = WithdrawWithheldTokensProof::transcript_new(
|
2022-03-03 12:07:27 -08:00
|
|
|
&pod_withdraw_withheld_authority_pubkey,
|
|
|
|
&pod_destination_pubkey,
|
|
|
|
&pod_withdraw_withheld_authority_ciphertext,
|
|
|
|
&pod_destination_ciphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
let proof = WithdrawWithheldTokensProof::new(
|
2022-03-03 12:07:27 -08:00
|
|
|
withdraw_withheld_authority_keypair,
|
|
|
|
destination_pubkey,
|
|
|
|
withdraw_withheld_authority_ciphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
amount,
|
2022-03-03 12:07:27 -08:00
|
|
|
&destination_opening,
|
2022-02-17 09:45:07 -08:00
|
|
|
&mut transcript,
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(Self {
|
2022-03-03 12:07:27 -08:00
|
|
|
withdraw_withheld_authority_pubkey: pod_withdraw_withheld_authority_pubkey,
|
|
|
|
destination_pubkey: pod_destination_pubkey,
|
|
|
|
withdraw_withheld_authority_ciphertext: pod_withdraw_withheld_authority_ciphertext,
|
|
|
|
destination_ciphertext: pod_destination_ciphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
proof,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-18 18:17:29 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-02-17 09:45:07 -08:00
|
|
|
impl Verifiable for WithdrawWithheldTokensData {
|
|
|
|
fn verify(&self) -> Result<(), ProofError> {
|
|
|
|
let mut transcript = WithdrawWithheldTokensProof::transcript_new(
|
2022-03-03 12:07:27 -08:00
|
|
|
&self.withdraw_withheld_authority_pubkey,
|
|
|
|
&self.destination_pubkey,
|
|
|
|
&self.withdraw_withheld_authority_ciphertext,
|
|
|
|
&self.destination_ciphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
);
|
|
|
|
|
2022-03-03 12:07:27 -08:00
|
|
|
let withdraw_withheld_authority_pubkey =
|
|
|
|
self.withdraw_withheld_authority_pubkey.try_into()?;
|
|
|
|
let destination_pubkey = self.destination_pubkey.try_into()?;
|
|
|
|
let withdraw_withheld_authority_ciphertext =
|
|
|
|
self.withdraw_withheld_authority_ciphertext.try_into()?;
|
|
|
|
let destination_ciphertext = self.destination_ciphertext.try_into()?;
|
2022-02-17 09:45:07 -08:00
|
|
|
|
|
|
|
self.proof.verify(
|
2022-03-03 12:07:27 -08:00
|
|
|
&withdraw_withheld_authority_pubkey,
|
|
|
|
&destination_pubkey,
|
|
|
|
&withdraw_withheld_authority_ciphertext,
|
|
|
|
&destination_ciphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
&mut transcript,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This struct represents the cryptographic proof component that certifies the account's solvency
|
|
|
|
/// for withdrawal
|
|
|
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
|
|
|
#[repr(C)]
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
pub struct WithdrawWithheldTokensProof {
|
|
|
|
pub proof: pod::CtxtCtxtEqualityProof,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(non_snake_case)]
|
2022-05-18 18:17:29 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2022-02-17 09:45:07 -08:00
|
|
|
impl WithdrawWithheldTokensProof {
|
|
|
|
fn transcript_new(
|
2022-03-03 12:07:27 -08:00
|
|
|
withdraw_withheld_authority_pubkey: &pod::ElGamalPubkey,
|
|
|
|
destination_pubkey: &pod::ElGamalPubkey,
|
|
|
|
withdraw_withheld_authority_ciphertext: &pod::ElGamalCiphertext,
|
|
|
|
destination_ciphertext: &pod::ElGamalCiphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
) -> Transcript {
|
|
|
|
let mut transcript = Transcript::new(b"WithdrawWithheldTokensProof");
|
|
|
|
|
|
|
|
transcript.append_pubkey(
|
|
|
|
b"withdraw-withheld-authority-pubkey",
|
2022-03-03 12:07:27 -08:00
|
|
|
withdraw_withheld_authority_pubkey,
|
2022-02-17 09:45:07 -08:00
|
|
|
);
|
2022-03-03 12:07:27 -08:00
|
|
|
transcript.append_pubkey(b"dest-pubkey", destination_pubkey);
|
2022-02-17 09:45:07 -08:00
|
|
|
|
|
|
|
transcript.append_ciphertext(
|
|
|
|
b"ciphertext-withdraw-withheld-authority",
|
2022-03-03 12:07:27 -08:00
|
|
|
withdraw_withheld_authority_ciphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
);
|
2022-03-03 12:07:27 -08:00
|
|
|
transcript.append_ciphertext(b"ciphertext-dest", destination_ciphertext);
|
2022-02-17 09:45:07 -08:00
|
|
|
|
|
|
|
transcript
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new(
|
2022-03-03 12:07:27 -08:00
|
|
|
withdraw_withheld_authority_keypair: &ElGamalKeypair,
|
|
|
|
destination_pubkey: &ElGamalPubkey,
|
|
|
|
withdraw_withheld_authority_ciphertext: &ElGamalCiphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
amount: u64,
|
2022-03-03 12:07:27 -08:00
|
|
|
destination_opening: &PedersenOpening,
|
2022-02-17 09:45:07 -08:00
|
|
|
transcript: &mut Transcript,
|
|
|
|
) -> Self {
|
|
|
|
let equality_proof = CtxtCtxtEqualityProof::new(
|
2022-03-03 12:07:27 -08:00
|
|
|
withdraw_withheld_authority_keypair,
|
|
|
|
destination_pubkey,
|
|
|
|
withdraw_withheld_authority_ciphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
amount,
|
2022-03-03 12:07:27 -08:00
|
|
|
destination_opening,
|
2022-02-17 09:45:07 -08:00
|
|
|
transcript,
|
|
|
|
);
|
|
|
|
|
|
|
|
Self {
|
|
|
|
proof: equality_proof.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn verify(
|
|
|
|
&self,
|
2022-03-03 12:07:27 -08:00
|
|
|
source_pubkey: &ElGamalPubkey,
|
|
|
|
destination_pubkey: &ElGamalPubkey,
|
|
|
|
source_ciphertext: &ElGamalCiphertext,
|
|
|
|
destination_ciphertext: &ElGamalCiphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
transcript: &mut Transcript,
|
|
|
|
) -> Result<(), ProofError> {
|
|
|
|
let proof: CtxtCtxtEqualityProof = self.proof.try_into()?;
|
|
|
|
proof.verify(
|
2022-03-03 12:07:27 -08:00
|
|
|
source_pubkey,
|
|
|
|
destination_pubkey,
|
|
|
|
source_ciphertext,
|
|
|
|
destination_ciphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
transcript,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
2022-04-11 09:53:31 -07:00
|
|
|
fn test_withdraw_withheld() {
|
2022-03-03 12:07:27 -08:00
|
|
|
let withdraw_withheld_authority_keypair = ElGamalKeypair::new_rand();
|
|
|
|
let dest_keypair = ElGamalKeypair::new_rand();
|
2022-02-17 09:45:07 -08:00
|
|
|
|
2022-04-11 09:53:31 -07:00
|
|
|
let amount: u64 = 0;
|
|
|
|
let withdraw_withheld_authority_ciphertext =
|
|
|
|
withdraw_withheld_authority_keypair.public.encrypt(amount);
|
|
|
|
|
|
|
|
let withdraw_withheld_tokens_data = WithdrawWithheldTokensData::new(
|
|
|
|
&withdraw_withheld_authority_keypair,
|
|
|
|
&dest_keypair.public,
|
|
|
|
&withdraw_withheld_authority_ciphertext,
|
|
|
|
amount,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert!(withdraw_withheld_tokens_data.verify().is_ok());
|
|
|
|
|
2022-02-17 09:45:07 -08:00
|
|
|
let amount: u64 = 55;
|
2022-03-03 12:07:27 -08:00
|
|
|
let withdraw_withheld_authority_ciphertext =
|
|
|
|
withdraw_withheld_authority_keypair.public.encrypt(amount);
|
2022-02-17 09:45:07 -08:00
|
|
|
|
|
|
|
let withdraw_withheld_tokens_data = WithdrawWithheldTokensData::new(
|
2022-03-03 12:07:27 -08:00
|
|
|
&withdraw_withheld_authority_keypair,
|
|
|
|
&dest_keypair.public,
|
|
|
|
&withdraw_withheld_authority_ciphertext,
|
2022-02-17 09:45:07 -08:00
|
|
|
amount,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert!(withdraw_withheld_tokens_data.verify().is_ok());
|
2022-04-11 09:53:31 -07:00
|
|
|
|
|
|
|
let amount = u64::max_value();
|
|
|
|
let withdraw_withheld_authority_ciphertext =
|
|
|
|
withdraw_withheld_authority_keypair.public.encrypt(amount);
|
|
|
|
|
|
|
|
let withdraw_withheld_tokens_data = WithdrawWithheldTokensData::new(
|
|
|
|
&withdraw_withheld_authority_keypair,
|
|
|
|
&dest_keypair.public,
|
|
|
|
&withdraw_withheld_authority_ciphertext,
|
|
|
|
amount,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert!(withdraw_withheld_tokens_data.verify().is_ok());
|
2022-02-17 09:45:07 -08:00
|
|
|
}
|
|
|
|
}
|