2021-09-29 21:45:35 -07:00
|
|
|
use {
|
2021-09-30 10:25:36 -07:00
|
|
|
crate::zk_token_elgamal::pod,
|
2021-09-29 21:45:35 -07:00
|
|
|
bytemuck::{Pod, Zeroable},
|
|
|
|
};
|
2022-05-18 18:17:29 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2021-09-29 21:45:35 -07:00
|
|
|
use {
|
|
|
|
crate::{
|
2021-12-16 06:26:39 -08:00
|
|
|
encryption::elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
|
2021-09-29 21:45:35 -07:00
|
|
|
errors::ProofError,
|
|
|
|
instruction::Verifiable,
|
2021-12-16 06:15:29 -08:00
|
|
|
sigma_proofs::zero_balance_proof::ZeroBalanceProof,
|
2021-09-29 21:45:35 -07:00
|
|
|
transcript::TranscriptProtocol,
|
|
|
|
},
|
|
|
|
merlin::Transcript,
|
|
|
|
std::convert::TryInto,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// This struct includes the cryptographic proof *and* the account data information needed to verify
|
|
|
|
/// the proof
|
|
|
|
///
|
|
|
|
/// - The pre-instruction should call CloseAccountData::verify_proof(&self)
|
|
|
|
/// - The actual program should check that `balance` is consistent with what is
|
|
|
|
/// currently stored in the confidential token account
|
|
|
|
///
|
|
|
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
|
|
|
#[repr(C)]
|
|
|
|
pub struct CloseAccountData {
|
2021-12-06 08:23:31 -08:00
|
|
|
/// The source account ElGamal pubkey
|
2022-02-01 11:11:28 -08:00
|
|
|
pub pubkey: pod::ElGamalPubkey, // 32 bytes
|
2021-12-06 08:23:31 -08:00
|
|
|
|
2021-09-29 21:45:35 -07:00
|
|
|
/// The source account available balance in encrypted form
|
2022-02-01 11:11:28 -08:00
|
|
|
pub ciphertext: pod::ElGamalCiphertext, // 64 bytes
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
/// Proof that the source account available balance is zero
|
2022-10-14 04:15:20 -07:00
|
|
|
pub proof: CloseAccountProof, // 96 bytes
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
2022-05-18 18:17:29 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2021-09-29 21:45:35 -07:00
|
|
|
impl CloseAccountData {
|
2022-02-01 11:11:28 -08:00
|
|
|
pub fn new(
|
|
|
|
keypair: &ElGamalKeypair,
|
|
|
|
ciphertext: &ElGamalCiphertext,
|
|
|
|
) -> Result<Self, ProofError> {
|
2022-08-22 18:01:03 -07:00
|
|
|
let pod_pubkey = pod::ElGamalPubkey(keypair.public.to_bytes());
|
2022-02-01 11:11:28 -08:00
|
|
|
let pod_ciphertext = pod::ElGamalCiphertext(ciphertext.to_bytes());
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2022-02-01 11:11:28 -08:00
|
|
|
let mut transcript = CloseAccountProof::transcript_new(&pod_pubkey, &pod_ciphertext);
|
|
|
|
|
|
|
|
let proof = CloseAccountProof::new(keypair, ciphertext, &mut transcript);
|
|
|
|
|
|
|
|
Ok(CloseAccountData {
|
|
|
|
pubkey: pod_pubkey,
|
|
|
|
ciphertext: pod_ciphertext,
|
2021-09-29 21:45:35 -07:00
|
|
|
proof,
|
2022-02-01 11:11:28 -08:00
|
|
|
})
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-18 18:17:29 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2021-09-29 21:45:35 -07:00
|
|
|
impl Verifiable for CloseAccountData {
|
|
|
|
fn verify(&self) -> Result<(), ProofError> {
|
2022-02-01 11:11:28 -08:00
|
|
|
let mut transcript = CloseAccountProof::transcript_new(&self.pubkey, &self.ciphertext);
|
|
|
|
|
|
|
|
let pubkey = self.pubkey.try_into()?;
|
|
|
|
let ciphertext = self.ciphertext.try_into()?;
|
|
|
|
self.proof.verify(&pubkey, &ciphertext, &mut transcript)
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This struct represents the cryptographic proof component that certifies that the encrypted
|
|
|
|
/// balance is zero
|
|
|
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
|
|
|
#[repr(C)]
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
pub struct CloseAccountProof {
|
2021-12-16 06:15:29 -08:00
|
|
|
pub proof: pod::ZeroBalanceProof,
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(non_snake_case)]
|
2022-05-18 18:17:29 -07:00
|
|
|
#[cfg(not(target_os = "solana"))]
|
2021-09-29 21:45:35 -07:00
|
|
|
impl CloseAccountProof {
|
2022-02-01 11:11:28 -08:00
|
|
|
fn transcript_new(
|
|
|
|
pubkey: &pod::ElGamalPubkey,
|
|
|
|
ciphertext: &pod::ElGamalCiphertext,
|
|
|
|
) -> Transcript {
|
|
|
|
let mut transcript = Transcript::new(b"CloseAccountProof");
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2022-02-01 11:11:28 -08:00
|
|
|
transcript.append_pubkey(b"pubkey", pubkey);
|
|
|
|
transcript.append_ciphertext(b"ciphertext", ciphertext);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2022-02-01 11:11:28 -08:00
|
|
|
transcript
|
|
|
|
}
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2022-02-01 11:11:28 -08:00
|
|
|
pub fn new(
|
|
|
|
keypair: &ElGamalKeypair,
|
|
|
|
ciphertext: &ElGamalCiphertext,
|
|
|
|
transcript: &mut Transcript,
|
|
|
|
) -> Self {
|
|
|
|
let proof = ZeroBalanceProof::new(keypair, ciphertext, transcript);
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2022-02-17 09:45:07 -08:00
|
|
|
Self {
|
2021-12-16 06:26:39 -08:00
|
|
|
proof: proof.into(),
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-06 08:23:31 -08:00
|
|
|
pub fn verify(
|
|
|
|
&self,
|
2022-02-01 11:11:28 -08:00
|
|
|
pubkey: &ElGamalPubkey,
|
|
|
|
ciphertext: &ElGamalCiphertext,
|
|
|
|
transcript: &mut Transcript,
|
2021-12-06 08:23:31 -08:00
|
|
|
) -> Result<(), ProofError> {
|
2021-12-16 06:26:39 -08:00
|
|
|
let proof: ZeroBalanceProof = self.proof.try_into()?;
|
2022-02-01 11:11:28 -08:00
|
|
|
proof.verify(pubkey, ciphertext, transcript)?;
|
|
|
|
|
2022-01-04 05:39:03 -08:00
|
|
|
Ok(())
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2022-02-01 11:11:28 -08:00
|
|
|
use super::*;
|
2021-09-29 21:45:35 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_close_account_correctness() {
|
2022-02-01 11:11:28 -08:00
|
|
|
let keypair = ElGamalKeypair::new_rand();
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-12-06 08:23:31 -08:00
|
|
|
// general case: encryption of 0
|
2022-02-01 11:11:28 -08:00
|
|
|
let ciphertext = keypair.public.encrypt(0_u64);
|
|
|
|
let close_account_data = CloseAccountData::new(&keypair, &ciphertext).unwrap();
|
|
|
|
assert!(close_account_data.verify().is_ok());
|
2021-09-29 21:45:35 -07:00
|
|
|
|
2021-12-06 08:23:31 -08:00
|
|
|
// general case: encryption of > 0
|
2022-02-01 11:11:28 -08:00
|
|
|
let ciphertext = keypair.public.encrypt(1_u64);
|
|
|
|
let close_account_data = CloseAccountData::new(&keypair, &ciphertext).unwrap();
|
|
|
|
assert!(close_account_data.verify().is_err());
|
2021-09-29 21:45:35 -07:00
|
|
|
}
|
|
|
|
}
|