safe_zk_token_sdk/instruction/
close_account.rs1use {
2 crate::zk_token_elgamal::pod,
3 bytemuck::{Pod, Zeroable},
4};
5#[cfg(not(target_os = "solana"))]
6use {
7 crate::{
8 encryption::elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
9 errors::ProofError,
10 instruction::Verifiable,
11 sigma_proofs::zero_balance_proof::ZeroBalanceProof,
12 transcript::TranscriptProtocol,
13 },
14 merlin::Transcript,
15 std::convert::TryInto,
16};
17
18#[derive(Clone, Copy, Pod, Zeroable)]
26#[repr(C)]
27pub struct CloseAccountData {
28 pub pubkey: pod::ElGamalPubkey, pub ciphertext: pod::ElGamalCiphertext, pub proof: CloseAccountProof, }
37
38#[cfg(not(target_os = "solana"))]
39impl CloseAccountData {
40 pub fn new(
41 keypair: &ElGamalKeypair,
42 ciphertext: &ElGamalCiphertext,
43 ) -> Result<Self, ProofError> {
44 let pod_pubkey = pod::ElGamalPubkey((&keypair.public).to_bytes());
45 let pod_ciphertext = pod::ElGamalCiphertext(ciphertext.to_bytes());
46
47 let mut transcript = CloseAccountProof::transcript_new(&pod_pubkey, &pod_ciphertext);
48
49 let proof = CloseAccountProof::new(keypair, ciphertext, &mut transcript);
50
51 Ok(CloseAccountData {
52 pubkey: pod_pubkey,
53 ciphertext: pod_ciphertext,
54 proof,
55 })
56 }
57}
58
59#[cfg(not(target_os = "solana"))]
60impl Verifiable for CloseAccountData {
61 fn verify(&self) -> Result<(), ProofError> {
62 let mut transcript = CloseAccountProof::transcript_new(&self.pubkey, &self.ciphertext);
63
64 let pubkey = self.pubkey.try_into()?;
65 let ciphertext = self.ciphertext.try_into()?;
66 self.proof.verify(&pubkey, &ciphertext, &mut transcript)
67 }
68}
69
70#[derive(Clone, Copy, Pod, Zeroable)]
73#[repr(C)]
74#[allow(non_snake_case)]
75pub struct CloseAccountProof {
76 pub proof: pod::ZeroBalanceProof,
77}
78
79#[allow(non_snake_case)]
80#[cfg(not(target_os = "solana"))]
81impl CloseAccountProof {
82 fn transcript_new(
83 pubkey: &pod::ElGamalPubkey,
84 ciphertext: &pod::ElGamalCiphertext,
85 ) -> Transcript {
86 let mut transcript = Transcript::new(b"CloseAccountProof");
87
88 transcript.append_pubkey(b"pubkey", pubkey);
89 transcript.append_ciphertext(b"ciphertext", ciphertext);
90
91 transcript
92 }
93
94 pub fn new(
95 keypair: &ElGamalKeypair,
96 ciphertext: &ElGamalCiphertext,
97 transcript: &mut Transcript,
98 ) -> Self {
99 let proof = ZeroBalanceProof::new(keypair, ciphertext, transcript);
100
101 Self {
102 proof: proof.into(),
103 }
104 }
105
106 pub fn verify(
107 &self,
108 pubkey: &ElGamalPubkey,
109 ciphertext: &ElGamalCiphertext,
110 transcript: &mut Transcript,
111 ) -> Result<(), ProofError> {
112 let proof: ZeroBalanceProof = self.proof.try_into()?;
113 proof.verify(pubkey, ciphertext, transcript)?;
114
115 Ok(())
116 }
117}
118
119#[cfg(test)]
120mod test {
121 use super::*;
122
123 #[test]
124 fn test_close_account_correctness() {
125 let keypair = ElGamalKeypair::new_rand();
126
127 let ciphertext = keypair.public.encrypt(0_u64);
129 let close_account_data = CloseAccountData::new(&keypair, &ciphertext).unwrap();
130 assert!(close_account_data.verify().is_ok());
131
132 let ciphertext = keypair.public.encrypt(1_u64);
134 let close_account_data = CloseAccountData::new(&keypair, &ciphertext).unwrap();
135 assert!(close_account_data.verify().is_err());
136 }
137}