spl_token_confidential_transfer_proof_generation/
burn.rs1#[cfg(target_arch = "wasm32")]
38use solana_zk_sdk::encryption::grouped_elgamal::GroupedElGamalCiphertext3Handles;
39use {
40 crate::{
41 encryption::BurnAmountCiphertext, errors::TokenProofGenerationError,
42 try_combine_lo_hi_ciphertexts, try_split_u64, CiphertextValidityProofWithAuditorCiphertext,
43 },
44 solana_zk_sdk::{
45 encryption::{
46 auth_encryption::{AeCiphertext, AeKey},
47 elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
48 pedersen::Pedersen,
49 },
50 zk_elgamal_proof_program::proof_data::{
51 BatchedGroupedCiphertext3HandlesValidityProofData, BatchedRangeProofU128Data,
52 CiphertextCommitmentEqualityProofData, ZkProofData,
53 },
54 },
55};
56
57const REMAINING_BALANCE_BIT_LENGTH: usize = 64;
58const BURN_AMOUNT_LO_BIT_LENGTH: usize = 16;
59const BURN_AMOUNT_HI_BIT_LENGTH: usize = 32;
60const RANGE_PROOF_PADDING_BIT_LENGTH: usize = 16;
62
63pub struct BurnProofData {
65 pub equality_proof_data: CiphertextCommitmentEqualityProofData,
66 pub ciphertext_validity_proof_data_with_ciphertext:
67 CiphertextValidityProofWithAuditorCiphertext,
68 pub range_proof_data: BatchedRangeProofU128Data,
69}
70
71pub fn burn_split_proof_data(
72 current_available_balance_ciphertext: &ElGamalCiphertext,
73 current_decryptable_available_balance: &AeCiphertext,
74 burn_amount: u64,
75 source_elgamal_keypair: &ElGamalKeypair,
76 source_aes_key: &AeKey,
77 supply_elgamal_pubkey: &ElGamalPubkey,
78 auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
79) -> Result<BurnProofData, TokenProofGenerationError> {
80 let default_auditor_pubkey = ElGamalPubkey::default();
81 let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or(&default_auditor_pubkey);
82
83 let (burn_amount_lo, burn_amount_hi) = try_split_u64(burn_amount, BURN_AMOUNT_LO_BIT_LENGTH)
85 .ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;
86
87 let (burn_amount_ciphertext_lo, burn_amount_opening_lo) = BurnAmountCiphertext::new(
89 burn_amount_lo,
90 source_elgamal_keypair.pubkey(),
91 supply_elgamal_pubkey,
92 auditor_elgamal_pubkey,
93 );
94 #[cfg(not(target_arch = "wasm32"))]
95 let grouped_ciphertext_lo = burn_amount_ciphertext_lo.0;
96 #[cfg(target_arch = "wasm32")]
97 let grouped_ciphertext_lo = GroupedElGamalCiphertext3Handles::encrypt_with_u64(
98 source_elgamal_keypair.pubkey(),
99 supply_elgamal_pubkey,
100 auditor_elgamal_pubkey,
101 burn_amount_lo,
102 &burn_amount_opening_lo,
103 );
104
105 let (burn_amount_ciphertext_hi, burn_amount_opening_hi) = BurnAmountCiphertext::new(
106 burn_amount_hi,
107 source_elgamal_keypair.pubkey(),
108 supply_elgamal_pubkey,
109 auditor_elgamal_pubkey,
110 );
111 #[cfg(not(target_arch = "wasm32"))]
112 let grouped_ciphertext_hi = burn_amount_ciphertext_hi.0;
113 #[cfg(target_arch = "wasm32")]
114 let grouped_ciphertext_hi = GroupedElGamalCiphertext3Handles::encrypt_with_u64(
115 source_elgamal_keypair.pubkey(),
116 supply_elgamal_pubkey,
117 auditor_elgamal_pubkey,
118 burn_amount_hi,
119 &burn_amount_opening_hi,
120 );
121 let current_decrypted_available_balance = current_decryptable_available_balance
123 .decrypt(source_aes_key)
124 .ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;
125
126 let burn_amount_ciphertext_source_lo = burn_amount_ciphertext_lo
128 .0
129 .to_elgamal_ciphertext(0)
130 .unwrap();
131 let burn_amount_ciphertext_source_hi = burn_amount_ciphertext_hi
132 .0
133 .to_elgamal_ciphertext(0)
134 .unwrap();
135
136 #[allow(clippy::arithmetic_side_effects)]
137 let new_available_balance_ciphertext = current_available_balance_ciphertext
138 - try_combine_lo_hi_ciphertexts(
139 &burn_amount_ciphertext_source_lo,
140 &burn_amount_ciphertext_source_hi,
141 BURN_AMOUNT_LO_BIT_LENGTH,
142 )
143 .ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;
144
145 let remaining_balance = current_decrypted_available_balance
147 .checked_sub(burn_amount)
148 .ok_or(TokenProofGenerationError::NotEnoughFunds)?;
149
150 let (new_available_balance_commitment, new_available_balance_opening) =
151 Pedersen::new(remaining_balance);
152
153 let equality_proof_data = CiphertextCommitmentEqualityProofData::new(
155 source_elgamal_keypair,
156 &new_available_balance_ciphertext,
157 &new_available_balance_commitment,
158 &new_available_balance_opening,
159 remaining_balance,
160 )
161 .map_err(TokenProofGenerationError::from)?;
162
163 let ciphertext_validity_proof_data = BatchedGroupedCiphertext3HandlesValidityProofData::new(
165 source_elgamal_keypair.pubkey(),
166 supply_elgamal_pubkey,
167 auditor_elgamal_pubkey,
168 &grouped_ciphertext_lo,
169 &grouped_ciphertext_hi,
170 burn_amount_lo,
171 burn_amount_hi,
172 &burn_amount_opening_lo,
173 &burn_amount_opening_hi,
174 )
175 .map_err(TokenProofGenerationError::from)?;
176
177 let burn_amount_auditor_ciphertext_lo = ciphertext_validity_proof_data
178 .context_data()
179 .grouped_ciphertext_lo
180 .try_extract_ciphertext(2)
181 .map_err(|_| TokenProofGenerationError::CiphertextExtraction)?;
182
183 let burn_amount_auditor_ciphertext_hi = ciphertext_validity_proof_data
184 .context_data()
185 .grouped_ciphertext_hi
186 .try_extract_ciphertext(2)
187 .map_err(|_| TokenProofGenerationError::CiphertextExtraction)?;
188
189 let ciphertext_validity_proof_data_with_ciphertext =
190 CiphertextValidityProofWithAuditorCiphertext {
191 proof_data: ciphertext_validity_proof_data,
192 ciphertext_lo: burn_amount_auditor_ciphertext_lo,
193 ciphertext_hi: burn_amount_auditor_ciphertext_hi,
194 };
195
196 let (padding_commitment, padding_opening) = Pedersen::new(0_u64);
202 let range_proof_data = BatchedRangeProofU128Data::new(
203 vec![
204 &new_available_balance_commitment,
205 burn_amount_ciphertext_lo.get_commitment(),
206 burn_amount_ciphertext_hi.get_commitment(),
207 &padding_commitment,
208 ],
209 vec![remaining_balance, burn_amount_lo, burn_amount_hi, 0],
210 vec![
211 REMAINING_BALANCE_BIT_LENGTH,
212 BURN_AMOUNT_LO_BIT_LENGTH,
213 BURN_AMOUNT_HI_BIT_LENGTH,
214 RANGE_PROOF_PADDING_BIT_LENGTH,
215 ],
216 vec![
217 &new_available_balance_opening,
218 &burn_amount_opening_lo,
219 &burn_amount_opening_hi,
220 &padding_opening,
221 ],
222 )
223 .map_err(TokenProofGenerationError::from)?;
224
225 Ok(BurnProofData {
226 equality_proof_data,
227 ciphertext_validity_proof_data_with_ciphertext,
228 range_proof_data,
229 })
230}