spl_token_2022/extension/confidential_mint_burn/
account_info.rs1use {
2 super::ConfidentialMintBurn,
3 crate::{
4 error::TokenError,
5 extension::confidential_transfer::{
6 ConfidentialTransferAccount, DecryptableBalance, EncryptedBalance,
7 },
8 },
9 bytemuck::{Pod, Zeroable},
10 solana_zk_sdk::{
11 encryption::{
12 auth_encryption::{AeCiphertext, AeKey},
13 elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
14 pedersen::PedersenOpening,
15 pod::{
16 auth_encryption::PodAeCiphertext,
17 elgamal::{PodElGamalCiphertext, PodElGamalPubkey},
18 },
19 },
20 zk_elgamal_proof_program::proof_data::CiphertextCiphertextEqualityProofData,
21 },
22 spl_token_confidential_transfer_proof_generation::{
23 burn::{burn_split_proof_data, BurnProofData},
24 mint::{mint_split_proof_data, MintProofData},
25 },
26};
27
28#[repr(C)]
31#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
32pub struct SupplyAccountInfo {
33 pub current_supply: PodElGamalCiphertext,
35 pub decryptable_supply: PodAeCiphertext,
37 pub supply_elgamal_pubkey: PodElGamalPubkey,
39}
40
41impl SupplyAccountInfo {
42 pub fn new(extension: &ConfidentialMintBurn) -> Self {
45 Self {
46 current_supply: extension.confidential_supply,
47 decryptable_supply: extension.decryptable_supply,
48 supply_elgamal_pubkey: extension.supply_elgamal_pubkey,
49 }
50 }
51
52 pub fn decrypted_current_supply(
56 &self,
57 aes_key: &AeKey,
58 elgamal_keypair: &ElGamalKeypair,
59 ) -> Result<u64, TokenError> {
60 let current_decyptable_supply = AeCiphertext::try_from(self.decryptable_supply)
62 .map_err(|_| TokenError::MalformedCiphertext)?
63 .decrypt(aes_key)
64 .ok_or(TokenError::MalformedCiphertext)?;
65
66 let decryptable_supply_ciphertext =
69 elgamal_keypair.pubkey().encrypt(current_decyptable_supply);
70 #[allow(clippy::arithmetic_side_effects)]
71 let supply_delta_ciphertext = decryptable_supply_ciphertext
72 - ElGamalCiphertext::try_from(self.current_supply)
73 .map_err(|_| TokenError::MalformedCiphertext)?;
74 let decryptable_to_current_diff = elgamal_keypair
75 .secret()
76 .decrypt_u32(&supply_delta_ciphertext)
77 .ok_or(TokenError::MalformedCiphertext)?;
78
79 current_decyptable_supply
81 .checked_sub(decryptable_to_current_diff)
82 .ok_or(TokenError::Overflow)
83 }
84
85 pub fn generate_rotate_supply_elgamal_pubkey_proof(
88 &self,
89 current_supply_elgamal_keypair: &ElGamalKeypair,
90 new_supply_elgamal_pubkey: &ElGamalPubkey,
91 aes_key: &AeKey,
92 ) -> Result<CiphertextCiphertextEqualityProofData, TokenError> {
93 let current_supply =
94 self.decrypted_current_supply(aes_key, current_supply_elgamal_keypair)?;
95
96 let new_supply_opening = PedersenOpening::new_rand();
97 let new_supply_ciphertext =
98 new_supply_elgamal_pubkey.encrypt_with(current_supply, &new_supply_opening);
99
100 CiphertextCiphertextEqualityProofData::new(
101 current_supply_elgamal_keypair,
102 new_supply_elgamal_pubkey,
103 &self
104 .current_supply
105 .try_into()
106 .map_err(|_| TokenError::MalformedCiphertext)?,
107 &new_supply_ciphertext,
108 &new_supply_opening,
109 current_supply,
110 )
111 .map_err(|_| TokenError::ProofGeneration)
112 }
113
114 pub fn generate_split_mint_proof_data(
117 &self,
118 mint_amount: u64,
119 supply_elgamal_keypair: &ElGamalKeypair,
120 aes_key: &AeKey,
121 destination_elgamal_pubkey: &ElGamalPubkey,
122 auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
123 ) -> Result<MintProofData, TokenError> {
124 let current_supply_ciphertext = self
125 .current_supply
126 .try_into()
127 .map_err(|_| TokenError::MalformedCiphertext)?;
128
129 let current_supply = self.decrypted_current_supply(aes_key, supply_elgamal_keypair)?;
130
131 mint_split_proof_data(
132 ¤t_supply_ciphertext,
133 mint_amount,
134 current_supply,
135 supply_elgamal_keypair,
136 destination_elgamal_pubkey,
137 auditor_elgamal_pubkey,
138 )
139 .map_err(|e| -> TokenError { e.into() })
140 }
141
142 pub fn new_decryptable_supply(
144 &self,
145 mint_amount: u64,
146 elgamal_keypair: &ElGamalKeypair,
147 aes_key: &AeKey,
148 ) -> Result<AeCiphertext, TokenError> {
149 let current_decrypted_supply = self.decrypted_current_supply(aes_key, elgamal_keypair)?;
150 let new_decrypted_available_balance = current_decrypted_supply
151 .checked_add(mint_amount)
152 .ok_or(TokenError::Overflow)?;
153
154 Ok(aes_key.encrypt(new_decrypted_available_balance))
155 }
156}
157
158#[repr(C)]
161#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
162pub struct BurnAccountInfo {
163 pub available_balance: EncryptedBalance,
165 pub decryptable_available_balance: DecryptableBalance,
167}
168
169impl BurnAccountInfo {
170 pub fn new(account: &ConfidentialTransferAccount) -> Self {
173 Self {
174 available_balance: account.available_balance,
175 decryptable_available_balance: account.decryptable_available_balance,
176 }
177 }
178
179 pub fn generate_split_burn_proof_data(
182 &self,
183 burn_amount: u64,
184 source_elgamal_keypair: &ElGamalKeypair,
185 aes_key: &AeKey,
186 supply_elgamal_pubkey: &ElGamalPubkey,
187 auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
188 ) -> Result<BurnProofData, TokenError> {
189 let current_available_balance_ciphertext = self
190 .available_balance
191 .try_into()
192 .map_err(|_| TokenError::MalformedCiphertext)?;
193 let current_decryptable_available_balance = self
194 .decryptable_available_balance
195 .try_into()
196 .map_err(|_| TokenError::MalformedCiphertext)?;
197
198 burn_split_proof_data(
199 ¤t_available_balance_ciphertext,
200 ¤t_decryptable_available_balance,
201 burn_amount,
202 source_elgamal_keypair,
203 aes_key,
204 supply_elgamal_pubkey,
205 auditor_elgamal_pubkey,
206 )
207 .map_err(|e| -> TokenError { e.into() })
208 }
209
210 pub fn new_decryptable_balance(
212 &self,
213 burn_amount: u64,
214 aes_key: &AeKey,
215 ) -> Result<AeCiphertext, TokenError> {
216 let current_available_balance = AeCiphertext::try_from(self.decryptable_available_balance)
217 .map_err(|_| TokenError::MalformedCiphertext)?
218 .decrypt(aes_key)
219 .ok_or(TokenError::MalformedCiphertext)?;
220 let new_decryptable_balance = current_available_balance
221 .checked_sub(burn_amount)
222 .ok_or(TokenError::Overflow)?;
223
224 Ok(aes_key.encrypt(new_decryptable_balance))
225 }
226}