spl_token_2022/extension/confidential_mint_burn/
account_info.rsuse {
super::ConfidentialMintBurn,
crate::{
error::TokenError,
extension::confidential_transfer::{
ConfidentialTransferAccount, DecryptableBalance, EncryptedBalance,
},
},
bytemuck::{Pod, Zeroable},
solana_zk_sdk::{
encryption::{
auth_encryption::{AeCiphertext, AeKey},
elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
pedersen::PedersenOpening,
pod::{
auth_encryption::PodAeCiphertext,
elgamal::{PodElGamalCiphertext, PodElGamalPubkey},
},
},
zk_elgamal_proof_program::proof_data::CiphertextCiphertextEqualityProofData,
},
spl_token_confidential_transfer_proof_generation::{
burn::{burn_split_proof_data, BurnProofData},
mint::{mint_split_proof_data, MintProofData},
},
};
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct SupplyAccountInfo {
pub current_supply: PodElGamalCiphertext,
pub decryptable_supply: PodAeCiphertext,
pub supply_elgamal_pubkey: PodElGamalPubkey,
}
impl SupplyAccountInfo {
pub fn new(extension: &ConfidentialMintBurn) -> Self {
Self {
current_supply: extension.confidential_supply,
decryptable_supply: extension.decryptable_supply,
supply_elgamal_pubkey: extension.supply_elgamal_pubkey,
}
}
pub fn decrypted_current_supply(
&self,
aes_key: &AeKey,
elgamal_keypair: &ElGamalKeypair,
) -> Result<u64, TokenError> {
let current_decyptable_supply = AeCiphertext::try_from(self.decryptable_supply)
.map_err(|_| TokenError::MalformedCiphertext)?
.decrypt(aes_key)
.ok_or(TokenError::MalformedCiphertext)?;
let decryptable_supply_ciphertext =
elgamal_keypair.pubkey().encrypt(current_decyptable_supply);
#[allow(clippy::arithmetic_side_effects)]
let supply_delta_ciphertext = decryptable_supply_ciphertext
- ElGamalCiphertext::try_from(self.current_supply)
.map_err(|_| TokenError::MalformedCiphertext)?;
let decryptable_to_current_diff = elgamal_keypair
.secret()
.decrypt_u32(&supply_delta_ciphertext)
.ok_or(TokenError::MalformedCiphertext)?;
current_decyptable_supply
.checked_sub(decryptable_to_current_diff)
.ok_or(TokenError::Overflow)
}
pub fn generate_rotate_supply_elgamal_pubkey_proof(
&self,
current_supply_elgamal_keypair: &ElGamalKeypair,
new_supply_elgamal_keypair: &ElGamalKeypair,
aes_key: &AeKey,
) -> Result<CiphertextCiphertextEqualityProofData, TokenError> {
let current_supply =
self.decrypted_current_supply(aes_key, current_supply_elgamal_keypair)?;
let new_supply_opening = PedersenOpening::new_rand();
let new_supply_ciphertext = new_supply_elgamal_keypair
.pubkey()
.encrypt_with(current_supply, &new_supply_opening);
CiphertextCiphertextEqualityProofData::new(
current_supply_elgamal_keypair,
new_supply_elgamal_keypair.pubkey(),
&self
.current_supply
.try_into()
.map_err(|_| TokenError::MalformedCiphertext)?,
&new_supply_ciphertext,
&new_supply_opening,
current_supply,
)
.map_err(|_| TokenError::ProofGeneration)
}
pub fn generate_split_mint_proof_data(
&self,
mint_amount: u64,
current_supply: u64,
supply_elgamal_keypair: &ElGamalKeypair,
destination_elgamal_pubkey: &ElGamalPubkey,
auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
) -> Result<MintProofData, TokenError> {
let current_supply_ciphertext = self
.current_supply
.try_into()
.map_err(|_| TokenError::MalformedCiphertext)?;
mint_split_proof_data(
¤t_supply_ciphertext,
mint_amount,
current_supply,
supply_elgamal_keypair,
destination_elgamal_pubkey,
auditor_elgamal_pubkey,
)
.map_err(|e| -> TokenError { e.into() })
}
pub fn new_decryptable_supply(
&self,
mint_amount: u64,
elgamal_keypair: &ElGamalKeypair,
aes_key: &AeKey,
) -> Result<AeCiphertext, TokenError> {
let current_decrypted_supply = self.decrypted_current_supply(aes_key, elgamal_keypair)?;
let new_decrypted_available_balance = current_decrypted_supply
.checked_add(mint_amount)
.ok_or(TokenError::Overflow)?;
Ok(aes_key.encrypt(new_decrypted_available_balance))
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct BurnAccountInfo {
pub available_balance: EncryptedBalance,
pub decryptable_available_balance: DecryptableBalance,
}
impl BurnAccountInfo {
pub fn new(account: &ConfidentialTransferAccount) -> Self {
Self {
available_balance: account.available_balance,
decryptable_available_balance: account.decryptable_available_balance,
}
}
pub fn generate_split_burn_proof_data(
&self,
burn_amount: u64,
source_elgamal_keypair: &ElGamalKeypair,
aes_key: &AeKey,
supply_elgamal_pubkey: &ElGamalPubkey,
auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
) -> Result<BurnProofData, TokenError> {
let current_available_balance_ciphertext = self
.available_balance
.try_into()
.map_err(|_| TokenError::MalformedCiphertext)?;
let current_decryptable_available_balance = self
.decryptable_available_balance
.try_into()
.map_err(|_| TokenError::MalformedCiphertext)?;
burn_split_proof_data(
¤t_available_balance_ciphertext,
¤t_decryptable_available_balance,
burn_amount,
source_elgamal_keypair,
aes_key,
auditor_elgamal_pubkey,
supply_elgamal_pubkey,
)
.map_err(|e| -> TokenError { e.into() })
}
}