Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[program-2022, token-client] Add confidential mint and burn support #7565

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion token/confidential-transfer/proof-generation/src/burn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@ pub fn burn_split_proof_data(
burn_amount: u64,
source_elgamal_keypair: &ElGamalKeypair,
source_aes_key: &AeKey,
auditor_elgamal_pubkey: &ElGamalPubkey,
auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
supply_elgamal_pubkey: &ElGamalPubkey,
) -> Result<BurnProofData, TokenProofGenerationError> {
let default_auditor_pubkey = ElGamalPubkey::default();
let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or(&default_auditor_pubkey);

// split the burn amount into low and high bits
let (burn_amount_lo, burn_amount_hi) = try_split_u64(burn_amount, BURN_AMOUNT_LO_BIT_LENGTH)
.ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;
Expand Down
9 changes: 4 additions & 5 deletions token/confidential-transfer/proof-generation/src/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use {
},
solana_zk_sdk::{
encryption::{
auth_encryption::{AeCiphertext, AeKey},
elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
pedersen::Pedersen,
},
Expand All @@ -28,18 +27,19 @@ pub struct MintProofData {
pub ciphertext_validity_proof_data_with_ciphertext:
CiphertextValidityProofWithAuditorCiphertext,
pub range_proof_data: BatchedRangeProofU128Data,
pub new_decryptable_supply: AeCiphertext,
}

pub fn mint_split_proof_data(
current_supply_ciphertext: &ElGamalCiphertext,
mint_amount: u64,
current_supply: u64,
supply_elgamal_keypair: &ElGamalKeypair,
supply_aes_key: &AeKey,
destination_elgamal_pubkey: &ElGamalPubkey,
auditor_elgamal_pubkey: &ElGamalPubkey,
auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
) -> Result<MintProofData, TokenProofGenerationError> {
let default_auditor_pubkey = ElGamalPubkey::default();
let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or(&default_auditor_pubkey);

// split the mint amount into low and high bits
let (mint_amount_lo, mint_amount_hi) = try_split_u64(mint_amount, MINT_AMOUNT_LO_BIT_LENGTH)
.ok_or(TokenProofGenerationError::IllegalAmountBitLength)?;
Expand Down Expand Up @@ -158,6 +158,5 @@ pub fn mint_split_proof_data(
equality_proof_data,
ciphertext_validity_proof_data_with_ciphertext,
range_proof_data,
new_decryptable_supply: supply_aes_key.encrypt(new_supply),
})
}
7 changes: 2 additions & 5 deletions token/confidential-transfer/proof-tests/tests/proof_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,23 +222,20 @@ fn test_mint_validity(mint_amount: u64, supply: u64) {
let auditor_pubkey = auditor_keypair.pubkey();

let supply_keypair = ElGamalKeypair::new_rand();
let supply_aes_key = AeKey::new_rand();

let supply_ciphertext = supply_keypair.pubkey().encrypt(supply);

let MintProofData {
equality_proof_data,
ciphertext_validity_proof_data_with_ciphertext,
range_proof_data,
new_decryptable_supply: _,
} = mint_split_proof_data(
&supply_ciphertext,
mint_amount,
supply,
&supply_keypair,
&supply_aes_key,
destination_pubkey,
auditor_pubkey,
Some(auditor_pubkey),
)
.unwrap();

Expand Down Expand Up @@ -291,7 +288,7 @@ fn test_burn_validity(spendable_balance: u64, burn_amount: u64) {
burn_amount,
&source_keypair,
&aes_key,
auditor_pubkey,
Some(auditor_pubkey),
supply_pubkey,
)
.unwrap();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
use {
super::ConfidentialMintBurn,
crate::error::TokenError,
crate::{
error::TokenError,
extension::confidential_transfer::{
ConfidentialTransferAccount, DecryptableBalance, EncryptedBalance,
},
},
bytemuck::{Pod, Zeroable},
solana_zk_sdk::{
encryption::{
auth_encryption::{AeCiphertext, AeKey},
elgamal::{ElGamalCiphertext, ElGamalKeypair},
elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey},
pedersen::PedersenOpening,
pod::{
auth_encryption::PodAeCiphertext,
Expand All @@ -14,6 +19,10 @@ use {
},
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},
},
};

/// Confidential Mint Burn extension information needed to construct a
Expand Down Expand Up @@ -43,7 +52,7 @@ impl SupplyAccountInfo {
/// Computes the current supply from the decryptable supply and the
/// difference between the decryptable supply and the ElGamal encrypted
/// supply ciphertext
pub fn decrypt_current_supply(
pub fn decrypted_current_supply(
&self,
aes_key: &AeKey,
elgamal_keypair: &ElGamalKeypair,
Expand Down Expand Up @@ -82,7 +91,7 @@ impl SupplyAccountInfo {
new_supply_elgamal_keypair: &ElGamalKeypair,
) -> Result<CiphertextCiphertextEqualityProofData, TokenError> {
let current_supply =
self.decrypt_current_supply(aes_key, current_supply_elgamal_keypair)?;
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
Expand All @@ -102,4 +111,98 @@ impl SupplyAccountInfo {
)
.map_err(|_| TokenError::ProofGeneration)
}

/// Create a mint proof data that is split into equality, ciphertext
/// validity, and range proof.
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(
&current_supply_ciphertext,
mint_amount,
current_supply,
supply_elgamal_keypair,
destination_elgamal_pubkey,
auditor_elgamal_pubkey,
)
.map_err(|e| -> TokenError { e.into() })
}

/// Compute the new decryptable supply.
pub fn new_decryptable_supply(
&self,
mint_amount: u64,
aes_key: &AeKey,
elgamal_keypair: &ElGamalKeypair, // TODO: check consistency of the order of params
) -> 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))
}
}

/// Confidential Mint Burn extension information needed to construct a
/// `Burn` instruction.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct BurnAccountInfo {
/// The available balance (encrypted by `encryption_pubkey`)
pub available_balance: EncryptedBalance,
/// The decryptable available balance
pub decryptable_available_balance: DecryptableBalance,
}

impl BurnAccountInfo {
/// Create the `ApplyPendingBalance` instruction account information from
/// `ConfidentialTransferAccount`.
pub fn new(account: &ConfidentialTransferAccount) -> Self {
Self {
available_balance: account.available_balance,
decryptable_available_balance: account.decryptable_available_balance,
}
}

/// Create a burn proof data that is split into equality, ciphertext
/// validity, and range proof.
pub fn generate_split_burn_proof_data(
&self,
burn_amount: u64,
source_elgamal_keypair: &ElGamalKeypair,
aes_key: &AeKey,
auditor_elgamal_pubkey: Option<&ElGamalPubkey>,
supply_elgamal_pubkey: &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(
&current_available_balance_ciphertext,
&current_decryptable_available_balance,
burn_amount,
source_elgamal_keypair,
aes_key,
auditor_elgamal_pubkey,
supply_elgamal_pubkey,
)
.map_err(|e| -> TokenError { e.into() })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use {
#[cfg(not(target_os = "solana"))]
use {
solana_zk_sdk::{
encryption::{auth_encryption::AeCiphertext, elgamal::ElGamalPubkey},
encryption::elgamal::ElGamalPubkey,
zk_elgamal_proof_program::{
instruction::ProofInstruction,
proof_data::{
Expand Down Expand Up @@ -291,8 +291,8 @@ pub struct BurnInstructionData {
pub fn initialize_mint(
token_program_id: &Pubkey,
mint: &Pubkey,
supply_elgamal_pubkey: PodElGamalPubkey,
decryptable_supply: PodAeCiphertext,
supply_elgamal_pubkey: &PodElGamalPubkey,
decryptable_supply: &DecryptableBalance,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let accounts = vec![AccountMeta::new(*mint, false)];
Expand All @@ -303,8 +303,8 @@ pub fn initialize_mint(
TokenInstruction::ConfidentialMintBurnExtension,
ConfidentialMintBurnInstruction::InitializeMint,
&InitializeMintData {
supply_elgamal_pubkey,
decryptable_supply,
supply_elgamal_pubkey: *supply_elgamal_pubkey,
decryptable_supply: *decryptable_supply,
},
))
}
Expand All @@ -317,7 +317,7 @@ pub fn rotate_supply_elgamal_pubkey(
mint: &Pubkey,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
new_supply_elgamal_pubkey: ElGamalPubkey,
new_supply_elgamal_pubkey: &PodElGamalPubkey,
ciphertext_equality_proof: ProofLocation<CiphertextCiphertextEqualityProofData>,
) -> Result<Vec<Instruction>, ProgramError> {
check_program_account(token_program_id)?;
Expand Down Expand Up @@ -349,7 +349,7 @@ pub fn rotate_supply_elgamal_pubkey(
TokenInstruction::ConfidentialMintBurnExtension,
ConfidentialMintBurnInstruction::RotateSupplyElGamalPubkey,
&RotateSupplyElGamalPubkeyData {
new_supply_elgamal_pubkey: PodElGamalPubkey::from(new_supply_elgamal_pubkey),
new_supply_elgamal_pubkey: *new_supply_elgamal_pubkey,
proof_instruction_offset,
},
)];
Expand All @@ -366,7 +366,7 @@ pub fn update_decryptable_supply(
mint: &Pubkey,
authority: &Pubkey,
multisig_signers: &[&Pubkey],
new_decryptable_supply: AeCiphertext,
new_decryptable_supply: &DecryptableBalance,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let mut accounts = vec![
Expand All @@ -382,7 +382,7 @@ pub fn update_decryptable_supply(
TokenInstruction::ConfidentialMintBurnExtension,
ConfidentialMintBurnInstruction::UpdateDecryptableSupply,
&UpdateDecryptableSupplyData {
new_decryptable_supply: new_decryptable_supply.into(),
new_decryptable_supply: *new_decryptable_supply,
},
))
}
Expand Down Expand Up @@ -417,7 +417,7 @@ pub fn confidential_mint_with_split_proofs(
BatchedGroupedCiphertext3HandlesValidityProofData,
>,
range_proof_location: ProofLocation<BatchedRangeProofU128Data>,
new_decryptable_supply: AeCiphertext,
new_decryptable_supply: &DecryptableBalance,
) -> Result<Vec<Instruction>, ProgramError> {
check_program_account(token_program_id)?;
let mut accounts = vec![AccountMeta::new(*token_account, false)];
Expand Down Expand Up @@ -473,7 +473,7 @@ pub fn confidential_mint_with_split_proofs(
TokenInstruction::ConfidentialMintBurnExtension,
ConfidentialMintBurnInstruction::Mint,
&MintInstructionData {
new_decryptable_supply: new_decryptable_supply.into(),
new_decryptable_supply: *new_decryptable_supply,
mint_amount_auditor_ciphertext_lo: *mint_amount_auditor_ciphertext_lo,
mint_amount_auditor_ciphertext_hi: *mint_amount_auditor_ciphertext_hi,
equality_proof_instruction_offset,
Expand All @@ -495,7 +495,7 @@ pub fn confidential_burn_with_split_proofs(
token_account: &Pubkey,
mint: &Pubkey,
supply_elgamal_pubkey: Option<ElGamalPubkey>,
new_decryptable_available_balance: DecryptableBalance,
new_decryptable_available_balance: &DecryptableBalance,
burn_amount_auditor_ciphertext_lo: &PodElGamalCiphertext,
burn_amount_auditor_ciphertext_hi: &PodElGamalCiphertext,
authority: &Pubkey,
Expand Down Expand Up @@ -559,7 +559,7 @@ pub fn confidential_burn_with_split_proofs(
TokenInstruction::ConfidentialMintBurnExtension,
ConfidentialMintBurnInstruction::Burn,
&BurnInstructionData {
new_decryptable_available_balance,
new_decryptable_available_balance: *new_decryptable_available_balance,
burn_amount_auditor_ciphertext_lo: *burn_amount_auditor_ciphertext_lo,
burn_amount_auditor_ciphertext_hi: *burn_amount_auditor_ciphertext_hi,
equality_proof_instruction_offset,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ impl ApplyPendingBalanceAccountInfo {
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct WithdrawAccountInfo {
/// The available balance (encrypted by `encrypiton_pubkey`)
/// The available balance (encrypted by `encryption_pubkey`)
pub available_balance: EncryptedBalance,
/// The decryptable available balance
pub decryptable_available_balance: DecryptableBalance,
Expand Down Expand Up @@ -214,7 +214,7 @@ impl WithdrawAccountInfo {
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct TransferAccountInfo {
/// The available balance (encrypted by `encrypiton_pubkey`)
/// The available balance (encrypted by `encryption_pubkey`)
pub available_balance: EncryptedBalance,
/// The decryptable available balance
pub decryptable_available_balance: DecryptableBalance,
Expand Down
Loading
Loading