safe_zk_token_sdk/instruction/
withdraw_withheld.rs1use {
2 crate::zk_token_elgamal::pod,
3 bytemuck::{Pod, Zeroable},
4};
5#[cfg(not(target_os = "solana"))]
6use {
7 crate::{
8 encryption::{
9 elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
10 pedersen::PedersenOpening,
11 },
12 errors::ProofError,
13 instruction::Verifiable,
14 sigma_proofs::equality_proof::CtxtCtxtEqualityProof,
15 transcript::TranscriptProtocol,
16 },
17 merlin::Transcript,
18 std::convert::TryInto,
19};
20
21#[derive(Clone, Copy, Pod, Zeroable)]
29#[repr(C)]
30pub struct WithdrawWithheldTokensData {
31 pub withdraw_withheld_authority_pubkey: pod::ElGamalPubkey,
32
33 pub destination_pubkey: pod::ElGamalPubkey,
34
35 pub withdraw_withheld_authority_ciphertext: pod::ElGamalCiphertext,
36
37 pub destination_ciphertext: pod::ElGamalCiphertext,
38
39 pub proof: WithdrawWithheldTokensProof,
40}
41
42#[cfg(not(target_os = "solana"))]
43impl WithdrawWithheldTokensData {
44 pub fn new(
45 withdraw_withheld_authority_keypair: &ElGamalKeypair,
46 destination_pubkey: &ElGamalPubkey,
47 withdraw_withheld_authority_ciphertext: &ElGamalCiphertext,
48 amount: u64,
49 ) -> Result<Self, ProofError> {
50 let destination_opening = PedersenOpening::new_rand();
52 let destination_ciphertext = destination_pubkey.encrypt_with(amount, &destination_opening);
53
54 let pod_withdraw_withheld_authority_pubkey =
55 pod::ElGamalPubkey(withdraw_withheld_authority_keypair.public.to_bytes());
56 let pod_destination_pubkey = pod::ElGamalPubkey(destination_pubkey.to_bytes());
57 let pod_withdraw_withheld_authority_ciphertext =
58 pod::ElGamalCiphertext(withdraw_withheld_authority_ciphertext.to_bytes());
59 let pod_destination_ciphertext = pod::ElGamalCiphertext(destination_ciphertext.to_bytes());
60
61 let mut transcript = WithdrawWithheldTokensProof::transcript_new(
62 &pod_withdraw_withheld_authority_pubkey,
63 &pod_destination_pubkey,
64 &pod_withdraw_withheld_authority_ciphertext,
65 &pod_destination_ciphertext,
66 );
67
68 let proof = WithdrawWithheldTokensProof::new(
69 withdraw_withheld_authority_keypair,
70 destination_pubkey,
71 withdraw_withheld_authority_ciphertext,
72 amount,
73 &destination_opening,
74 &mut transcript,
75 );
76
77 Ok(Self {
78 withdraw_withheld_authority_pubkey: pod_withdraw_withheld_authority_pubkey,
79 destination_pubkey: pod_destination_pubkey,
80 withdraw_withheld_authority_ciphertext: pod_withdraw_withheld_authority_ciphertext,
81 destination_ciphertext: pod_destination_ciphertext,
82 proof,
83 })
84 }
85}
86
87#[cfg(not(target_os = "solana"))]
88impl Verifiable for WithdrawWithheldTokensData {
89 fn verify(&self) -> Result<(), ProofError> {
90 let mut transcript = WithdrawWithheldTokensProof::transcript_new(
91 &self.withdraw_withheld_authority_pubkey,
92 &self.destination_pubkey,
93 &self.withdraw_withheld_authority_ciphertext,
94 &self.destination_ciphertext,
95 );
96
97 let withdraw_withheld_authority_pubkey =
98 self.withdraw_withheld_authority_pubkey.try_into()?;
99 let destination_pubkey = self.destination_pubkey.try_into()?;
100 let withdraw_withheld_authority_ciphertext =
101 self.withdraw_withheld_authority_ciphertext.try_into()?;
102 let destination_ciphertext = self.destination_ciphertext.try_into()?;
103
104 self.proof.verify(
105 &withdraw_withheld_authority_pubkey,
106 &destination_pubkey,
107 &withdraw_withheld_authority_ciphertext,
108 &destination_ciphertext,
109 &mut transcript,
110 )
111 }
112}
113
114#[derive(Clone, Copy, Pod, Zeroable)]
117#[repr(C)]
118#[allow(non_snake_case)]
119pub struct WithdrawWithheldTokensProof {
120 pub proof: pod::CtxtCtxtEqualityProof,
121}
122
123#[allow(non_snake_case)]
124#[cfg(not(target_os = "solana"))]
125impl WithdrawWithheldTokensProof {
126 fn transcript_new(
127 withdraw_withheld_authority_pubkey: &pod::ElGamalPubkey,
128 destination_pubkey: &pod::ElGamalPubkey,
129 withdraw_withheld_authority_ciphertext: &pod::ElGamalCiphertext,
130 destination_ciphertext: &pod::ElGamalCiphertext,
131 ) -> Transcript {
132 let mut transcript = Transcript::new(b"WithdrawWithheldTokensProof");
133
134 transcript.append_pubkey(
135 b"withdraw-withheld-authority-pubkey",
136 withdraw_withheld_authority_pubkey,
137 );
138 transcript.append_pubkey(b"dest-pubkey", destination_pubkey);
139
140 transcript.append_ciphertext(
141 b"ciphertext-withdraw-withheld-authority",
142 withdraw_withheld_authority_ciphertext,
143 );
144 transcript.append_ciphertext(b"ciphertext-dest", destination_ciphertext);
145
146 transcript
147 }
148
149 pub fn new(
150 withdraw_withheld_authority_keypair: &ElGamalKeypair,
151 destination_pubkey: &ElGamalPubkey,
152 withdraw_withheld_authority_ciphertext: &ElGamalCiphertext,
153 amount: u64,
154 destination_opening: &PedersenOpening,
155 transcript: &mut Transcript,
156 ) -> Self {
157 let equality_proof = CtxtCtxtEqualityProof::new(
158 withdraw_withheld_authority_keypair,
159 destination_pubkey,
160 withdraw_withheld_authority_ciphertext,
161 amount,
162 destination_opening,
163 transcript,
164 );
165
166 Self {
167 proof: equality_proof.into(),
168 }
169 }
170
171 pub fn verify(
172 &self,
173 source_pubkey: &ElGamalPubkey,
174 destination_pubkey: &ElGamalPubkey,
175 source_ciphertext: &ElGamalCiphertext,
176 destination_ciphertext: &ElGamalCiphertext,
177 transcript: &mut Transcript,
178 ) -> Result<(), ProofError> {
179 let proof: CtxtCtxtEqualityProof = self.proof.try_into()?;
180 proof.verify(
181 source_pubkey,
182 destination_pubkey,
183 source_ciphertext,
184 destination_ciphertext,
185 transcript,
186 )?;
187
188 Ok(())
189 }
190}
191
192#[cfg(test)]
193mod test {
194 use super::*;
195
196 #[test]
197 fn test_withdraw_withheld() {
198 let withdraw_withheld_authority_keypair = ElGamalKeypair::new_rand();
199 let dest_keypair = ElGamalKeypair::new_rand();
200
201 let amount: u64 = 0;
202 let withdraw_withheld_authority_ciphertext =
203 withdraw_withheld_authority_keypair.public.encrypt(amount);
204
205 let withdraw_withheld_tokens_data = WithdrawWithheldTokensData::new(
206 &withdraw_withheld_authority_keypair,
207 &dest_keypair.public,
208 &withdraw_withheld_authority_ciphertext,
209 amount,
210 )
211 .unwrap();
212
213 assert!(withdraw_withheld_tokens_data.verify().is_ok());
214
215 let amount: u64 = 55;
216 let withdraw_withheld_authority_ciphertext =
217 withdraw_withheld_authority_keypair.public.encrypt(amount);
218
219 let withdraw_withheld_tokens_data = WithdrawWithheldTokensData::new(
220 &withdraw_withheld_authority_keypair,
221 &dest_keypair.public,
222 &withdraw_withheld_authority_ciphertext,
223 amount,
224 )
225 .unwrap();
226
227 assert!(withdraw_withheld_tokens_data.verify().is_ok());
228
229 let amount = u64::max_value();
230 let withdraw_withheld_authority_ciphertext =
231 withdraw_withheld_authority_keypair.public.encrypt(amount);
232
233 let withdraw_withheld_tokens_data = WithdrawWithheldTokensData::new(
234 &withdraw_withheld_authority_keypair,
235 &dest_keypair.public,
236 &withdraw_withheld_authority_ciphertext,
237 amount,
238 )
239 .unwrap();
240
241 assert!(withdraw_withheld_tokens_data.verify().is_ok());
242 }
243}