diff --git a/token/client/src/token.rs b/token/client/src/token.rs index 0e67c867..cda30deb 100644 --- a/token/client/src/token.rs +++ b/token/client/src/token.rs @@ -20,11 +20,12 @@ use { }, spl_token_2022::{ extension::{ - confidential_transfer, confidential_transfer::EncryptionPubkey, cpi_guard, - default_account_state, interest_bearing_mint, memo_transfer, transfer_fee, - BaseStateWithExtensions, ExtensionType, StateWithExtensionsOwned, + confidential_transfer, cpi_guard, default_account_state, interest_bearing_mint, + memo_transfer, transfer_fee, BaseStateWithExtensions, ExtensionType, + StateWithExtensionsOwned, }, instruction, + pod::EncryptionPubkey, solana_zk_token_sdk::{ encryption::{auth_encryption::*, elgamal::*}, errors::ProofError, @@ -106,8 +107,8 @@ pub enum ExtensionInitializationParams { ConfidentialTransferMint { authority: Option, auto_approve_new_accounts: bool, - auditor_encryption_pubkey: EncryptionPubkey, - withdraw_withheld_authority_encryption_pubkey: EncryptionPubkey, + auditor_encryption_pubkey: Option, + withdraw_withheld_authority_encryption_pubkey: Option, }, DefaultAccountState { state: AccountState, @@ -158,10 +159,10 @@ impl ExtensionInitializationParams { } => confidential_transfer::instruction::initialize_mint( token_program_id, mint, - authority.as_ref(), + authority, auto_approve_new_accounts, - &auditor_encryption_pubkey, - &withdraw_withheld_authority_encryption_pubkey, + auditor_encryption_pubkey, + withdraw_withheld_authority_encryption_pubkey, ), Self::DefaultAccountState { state } => { default_account_state::instruction::initialize_default_account_state( @@ -1492,7 +1493,7 @@ where authority: &S, new_authority: Option<&S>, auto_approve_new_account: bool, - auditor_encryption_pubkey: &EncryptionPubkey, + auditor_encryption_pubkey: Option, ) -> TokenResult { let mut signers = vec![authority]; let new_authority_pubkey = if let Some(new_authority) = new_authority { @@ -1789,16 +1790,20 @@ where /// Fetch the ElGamal pubkey key of the auditor associated with a confidential token mint pub async fn confidential_transfer_get_auditor_encryption_pubkey( &self, - ) -> TokenResult { + ) -> TokenResult> { let mint_state = self.get_mint_info().await.unwrap(); let ct_mint = mint_state.get_extension::()?; - let auditor_pubkey = ct_mint - .auditor_encryption_pubkey - .try_into() - .map_err(TokenError::Proof)?; + let auditor_encryption_pubkey: Option = + ct_mint.auditor_encryption_pubkey.into(); - Ok(auditor_pubkey) + if let Some(encryption_pubkey) = auditor_encryption_pubkey { + let encryption_pubkey: ElGamalPubkey = + encryption_pubkey.try_into().map_err(TokenError::Proof)?; + Ok(Some(encryption_pubkey)) + } else { + Ok(None) + } } /// Fetch the ElGamal pubkey key of the withdraw withheld authority associated with a @@ -1807,16 +1812,20 @@ where S: Signer, >( &self, - ) -> TokenResult { + ) -> TokenResult> { let mint_state = self.get_mint_info().await.unwrap(); let ct_mint = mint_state.get_extension::()?; - let auditor_pubkey = ct_mint - .withdraw_withheld_authority_encryption_pubkey - .try_into() - .map_err(TokenError::Proof)?; + let withdraw_withheld_authority_encryption_pubkey: Option = + ct_mint.withdraw_withheld_authority_encryption_pubkey.into(); - Ok(auditor_pubkey) + if let Some(encryption_pubkey) = withdraw_withheld_authority_encryption_pubkey { + let encryption_pubkey: ElGamalPubkey = + encryption_pubkey.try_into().map_err(TokenError::Proof)?; + Ok(Some(encryption_pubkey)) + } else { + Ok(None) + } } /// Deposit SPL Tokens into the pending balance of a confidential token account @@ -1932,7 +1941,7 @@ where source_available_balance: u64, source_available_balance_ciphertext: &ElGamalCiphertext, destination_elgamal_pubkey: &ElGamalPubkey, - auditor_elgamal_pubkey: &ElGamalPubkey, + auditor_elgamal_pubkey: Option, ) -> TokenResult { let source_elgamal_keypair = ElGamalKeypair::new(source_token_authority, source_token_account) @@ -1966,7 +1975,7 @@ where source_available_balance: u64, source_available_balance_ciphertext: &ElGamalCiphertext, destination_elgamal_pubkey: &ElGamalPubkey, - auditor_elgamal_pubkey: &ElGamalPubkey, + auditor_elgamal_pubkey: Option, source_elgamal_keypair: &ElGamalKeypair, source_authenticated_encryption_key: &AeKey, ) -> TokenResult { @@ -1974,6 +1983,8 @@ where return Err(TokenError::MaximumDepositTransferAmountExceeded); } + let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or_default(); + let proof_data = confidential_transfer::instruction::TransferData::new( amount, ( @@ -1981,7 +1992,7 @@ where source_available_balance_ciphertext, ), source_elgamal_keypair, - (destination_elgamal_pubkey, auditor_elgamal_pubkey), + (destination_elgamal_pubkey, &auditor_elgamal_pubkey), ) .map_err(TokenError::Proof)?; @@ -2019,7 +2030,7 @@ where source_available_balance: u64, source_available_balance_ciphertext: &ElGamalCiphertext, destination_elgamal_pubkey: &ElGamalPubkey, - auditor_elgamal_pubkey: &ElGamalPubkey, + auditor_elgamal_pubkey: Option, withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey, epoch_info: &EpochInfo, ) -> TokenResult { @@ -2057,7 +2068,7 @@ where source_available_balance: u64, source_available_balance_ciphertext: &ElGamalCiphertext, destination_elgamal_pubkey: &ElGamalPubkey, - auditor_elgamal_pubkey: &ElGamalPubkey, + auditor_elgamal_pubkey: Option, withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey, source_elgamal_keypair: &ElGamalKeypair, source_authenticated_encryption_key: &AeKey, @@ -2067,7 +2078,8 @@ where return Err(TokenError::MaximumDepositTransferAmountExceeded); } - // TODO: take transfer fee params as input + let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or_default(); + let mint_state = self.get_mint_info().await.unwrap(); let transfer_fee_config = mint_state .get_extension::() @@ -2081,7 +2093,7 @@ where source_available_balance_ciphertext, ), source_elgamal_keypair, - (destination_elgamal_pubkey, auditor_elgamal_pubkey), + (destination_elgamal_pubkey, &auditor_elgamal_pubkey), FeeParameters { fee_rate_basis_points: u16::from(fee_parameters.transfer_fee_basis_points), maximum_fee: u64::from(fee_parameters.maximum_fee), diff --git a/token/program-2022-test/tests/confidential_transfer.rs b/token/program-2022-test/tests/confidential_transfer.rs index 8700c212..4c24a48d 100644 --- a/token/program-2022-test/tests/confidential_transfer.rs +++ b/token/program-2022-test/tests/confidential_transfer.rs @@ -17,6 +17,7 @@ use { }, BaseStateWithExtensions, ExtensionType, }, + pod::EncryptionPubkey, solana_zk_token_sdk::{ encryption::{auth_encryption::*, elgamal::*}, zk_token_elgamal::pod::Zeroable, @@ -53,9 +54,7 @@ fn test_epoch_info() -> EpochInfo { struct ConfidentialTransferMintWithKeypairs { ct_mint: ConfidentialTransferMint, ct_mint_authority: Keypair, - #[allow(dead_code)] ct_mint_transfer_auditor_encryption_keypair: ElGamalKeypair, - #[allow(dead_code)] ct_mint_withdraw_withheld_authority_encryption_keypair: ElGamalKeypair, } @@ -63,15 +62,28 @@ impl ConfidentialTransferMintWithKeypairs { fn new() -> Self { let ct_mint_authority = Keypair::new(); let ct_mint_transfer_auditor_encryption_keypair = ElGamalKeypair::new_rand(); + let ct_mint_transfer_auditor_encryption_pubkey: EncryptionPubkey = + ct_mint_transfer_auditor_encryption_keypair + .public + .try_into() + .unwrap(); let ct_mint_withdraw_withheld_authority_encryption_keypair = ElGamalKeypair::new_rand(); + let ct_mint_withdraw_withheld_authority_encryption_pubkey: EncryptionPubkey = + ct_mint_withdraw_withheld_authority_encryption_keypair + .public + .try_into() + .unwrap(); let ct_mint = ConfidentialTransferMint { - authority: ct_mint_authority.pubkey(), + authority: Some(ct_mint_authority.pubkey()).try_into().unwrap(), auto_approve_new_accounts: true.into(), - auditor_encryption_pubkey: ct_mint_transfer_auditor_encryption_keypair.public.into(), - withdraw_withheld_authority_encryption_pubkey: - ct_mint_withdraw_withheld_authority_encryption_keypair - .public - .into(), + auditor_encryption_pubkey: Some(ct_mint_transfer_auditor_encryption_pubkey) + .try_into() + .unwrap(), + withdraw_withheld_authority_encryption_pubkey: Some( + ct_mint_withdraw_withheld_authority_encryption_pubkey, + ) + .try_into() + .unwrap(), withheld_amount: EncryptedWithheldAmount::zeroed(), }; Self { @@ -286,11 +298,12 @@ async fn ct_initialize_and_update_mint() { context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(ct_mint.authority), + authority: ct_mint.authority.into(), auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey, + auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey.into(), withdraw_withheld_authority_encryption_pubkey: ct_mint - .withdraw_withheld_authority_encryption_pubkey, + .withdraw_withheld_authority_encryption_pubkey + .into(), }, ]) .await @@ -305,7 +318,7 @@ async fn ct_initialize_and_update_mint() { // Change the authority let new_ct_mint_authority = Keypair::new(); let new_ct_mint = ConfidentialTransferMint { - authority: new_ct_mint_authority.pubkey(), + authority: Some(new_ct_mint_authority.pubkey()).try_into().unwrap(), ..ConfidentialTransferMint::default() }; @@ -314,10 +327,9 @@ async fn ct_initialize_and_update_mint() { &wrong_keypair, Some(&new_ct_mint_authority), new_ct_mint.auto_approve_new_accounts.into(), - &new_ct_mint + new_ct_mint .withdraw_withheld_authority_encryption_pubkey - .try_into() - .unwrap(), + .into(), ) .await .unwrap_err(); @@ -332,10 +344,9 @@ async fn ct_initialize_and_update_mint() { &ct_mint_authority, Some(&new_ct_mint_authority), new_ct_mint.auto_approve_new_accounts.into(), - &new_ct_mint + new_ct_mint .withdraw_withheld_authority_encryption_pubkey - .try_into() - .unwrap(), + .into(), ) .await .unwrap(); @@ -364,10 +375,9 @@ async fn ct_initialize_and_update_mint() { &new_ct_mint_authority, None, new_ct_mint.auto_approve_new_accounts.into(), - &new_ct_mint + new_ct_mint .withdraw_withheld_authority_encryption_pubkey - .try_into() - .unwrap(), + .into(), ) .await .unwrap(); @@ -402,11 +412,12 @@ async fn ct_configure_token_account() { context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(ct_mint.authority), + authority: ct_mint.authority.into(), auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey, + auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey.into(), withdraw_withheld_authority_encryption_pubkey: ct_mint - .withdraw_withheld_authority_encryption_pubkey, + .withdraw_withheld_authority_encryption_pubkey + .into(), }, ]) .await @@ -479,11 +490,12 @@ async fn ct_enable_disable_confidential_credits() { context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(ct_mint.authority), + authority: ct_mint.authority.into(), auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey, + auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey.into(), withdraw_withheld_authority_encryption_pubkey: ct_mint - .withdraw_withheld_authority_encryption_pubkey, + .withdraw_withheld_authority_encryption_pubkey + .into(), }, ]) .await @@ -527,11 +539,12 @@ async fn ct_enable_disable_non_confidential_credits() { context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(ct_mint.authority), + authority: ct_mint.authority.into(), auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey, + auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey.into(), withdraw_withheld_authority_encryption_pubkey: ct_mint - .withdraw_withheld_authority_encryption_pubkey, + .withdraw_withheld_authority_encryption_pubkey + .into(), }, ]) .await @@ -624,11 +637,12 @@ async fn ct_new_account_is_empty() { context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(ct_mint.authority), + authority: ct_mint.authority.into(), auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey, + auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey.into(), withdraw_withheld_authority_encryption_pubkey: ct_mint - .withdraw_withheld_authority_encryption_pubkey, + .withdraw_withheld_authority_encryption_pubkey + .into(), }, ]) .await @@ -652,11 +666,12 @@ async fn ct_deposit() { context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(ct_mint.authority), + authority: ct_mint.authority.into(), auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey, + auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey.into(), withdraw_withheld_authority_encryption_pubkey: ct_mint - .withdraw_withheld_authority_encryption_pubkey, + .withdraw_withheld_authority_encryption_pubkey + .into(), }, ]) .await @@ -782,11 +797,12 @@ async fn ct_withdraw() { context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(ct_mint.authority), + authority: ct_mint.authority.into(), auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey, + auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey.into(), withdraw_withheld_authority_encryption_pubkey: ct_mint - .withdraw_withheld_authority_encryption_pubkey, + .withdraw_withheld_authority_encryption_pubkey + .into(), }, ]) .await @@ -885,17 +901,21 @@ async fn ct_withdraw() { #[cfg(feature = "zk-ops")] #[tokio::test] async fn ct_transfer() { - let ConfidentialTransferMintWithKeypairs { ct_mint, .. } = - ConfidentialTransferMintWithKeypairs::new(); + let ConfidentialTransferMintWithKeypairs { + ct_mint, + ct_mint_transfer_auditor_encryption_keypair, + .. + } = ConfidentialTransferMintWithKeypairs::new(); let mut context = TestContext::new().await; context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(ct_mint.authority), + authority: ct_mint.authority.into(), auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey, + auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey.into(), withdraw_withheld_authority_encryption_pubkey: ct_mint - .withdraw_withheld_authority_encryption_pubkey, + .withdraw_withheld_authority_encryption_pubkey + .into(), }, ]) .await @@ -932,7 +952,7 @@ async fn ct_transfer() { 42, &extension.available_balance.try_into().unwrap(), &alice_meta.elgamal_keypair.public, - &ct_mint.auditor_encryption_pubkey.try_into().unwrap(), + Some(ct_mint_transfer_auditor_encryption_keypair.public), ) .await .unwrap(); @@ -967,7 +987,7 @@ async fn ct_transfer() { 42, &extension.available_balance.try_into().unwrap(), &alice_meta.elgamal_keypair.public, - &ct_mint.auditor_encryption_pubkey.try_into().unwrap(), + Some(ct_mint_transfer_auditor_encryption_keypair.public), ) .await .unwrap(); @@ -1001,7 +1021,7 @@ async fn ct_transfer() { 0, &extension.available_balance.try_into().unwrap(), &alice_meta.elgamal_keypair.public, - &ct_mint.auditor_encryption_pubkey.try_into().unwrap(), + Some(ct_mint_transfer_auditor_encryption_keypair.public), ) .await .unwrap_err(); @@ -1052,7 +1072,7 @@ async fn ct_transfer() { 42, &extension.available_balance.try_into().unwrap(), &bob_meta.elgamal_keypair.public, - &ct_mint.auditor_encryption_pubkey.try_into().unwrap(), + Some(ct_mint_transfer_auditor_encryption_keypair.public), ) .await .unwrap(); @@ -1122,8 +1142,12 @@ async fn ct_transfer() { #[cfg(feature = "zk-ops")] #[tokio::test] async fn ct_transfer_with_fee() { - let ConfidentialTransferMintWithKeypairs { ct_mint, .. } = - ConfidentialTransferMintWithKeypairs::new(); + let ConfidentialTransferMintWithKeypairs { + ct_mint, + ct_mint_transfer_auditor_encryption_keypair, + ct_mint_withdraw_withheld_authority_encryption_keypair, + .. + } = ConfidentialTransferMintWithKeypairs::new(); let mut context = TestContext::new().await; context @@ -1135,11 +1159,12 @@ async fn ct_transfer_with_fee() { maximum_fee: TEST_MAXIMUM_FEE, }, ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(ct_mint.authority), + authority: ct_mint.authority.into(), auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey, + auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey.into(), withdraw_withheld_authority_encryption_pubkey: ct_mint - .withdraw_withheld_authority_encryption_pubkey, + .withdraw_withheld_authority_encryption_pubkey + .into(), }, ]) .await @@ -1179,7 +1204,7 @@ async fn ct_transfer_with_fee() { 100, &extension.available_balance.try_into().unwrap(), &alice_meta.elgamal_keypair.public, - &ct_mint.auditor_encryption_pubkey.try_into().unwrap(), + Some(ct_mint_transfer_auditor_encryption_keypair.public), ) .await .unwrap(); @@ -1214,7 +1239,7 @@ async fn ct_transfer_with_fee() { 100, &extension.available_balance.try_into().unwrap(), &alice_meta.elgamal_keypair.public, - &ct_mint.auditor_encryption_pubkey.try_into().unwrap(), + Some(ct_mint_transfer_auditor_encryption_keypair.public), ) .await .unwrap(); @@ -1265,11 +1290,8 @@ async fn ct_transfer_with_fee() { 100, &extension.available_balance.try_into().unwrap(), &bob_meta.elgamal_keypair.public, - &ct_mint.auditor_encryption_pubkey.try_into().unwrap(), - &ct_mint - .withdraw_withheld_authority_encryption_pubkey - .try_into() - .unwrap(), + Some(ct_mint_transfer_auditor_encryption_keypair.public), + &ct_mint_withdraw_withheld_authority_encryption_keypair.public, &epoch_info, ) .await @@ -1343,6 +1365,7 @@ async fn ct_transfer_with_fee() { async fn ct_withdraw_withheld_tokens_from_mint() { let ConfidentialTransferMintWithKeypairs { ct_mint, + ct_mint_transfer_auditor_encryption_keypair, ct_mint_withdraw_withheld_authority_encryption_keypair, .. } = ConfidentialTransferMintWithKeypairs::new(); @@ -1359,11 +1382,12 @@ async fn ct_withdraw_withheld_tokens_from_mint() { maximum_fee: TEST_MAXIMUM_FEE, }, ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(ct_mint.authority), + authority: ct_mint.authority.into(), auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey, + auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey.into(), withdraw_withheld_authority_encryption_pubkey: ct_mint - .withdraw_withheld_authority_encryption_pubkey, + .withdraw_withheld_authority_encryption_pubkey + .into(), }, ]) .await @@ -1434,11 +1458,8 @@ async fn ct_withdraw_withheld_tokens_from_mint() { 100, &extension.available_balance.try_into().unwrap(), &bob_meta.elgamal_keypair.public, - &ct_mint.auditor_encryption_pubkey.try_into().unwrap(), - &ct_mint - .withdraw_withheld_authority_encryption_pubkey - .try_into() - .unwrap(), + Some(ct_mint_transfer_auditor_encryption_keypair.public), + &ct_mint_withdraw_withheld_authority_encryption_keypair.public, &epoch_info, ) .await @@ -1521,11 +1542,12 @@ async fn ct_withdraw_withheld_tokens_from_accounts() { maximum_fee: TEST_MAXIMUM_FEE, }, ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(ct_mint.authority), + authority: ct_mint.authority.into(), auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey, + auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey.into(), withdraw_withheld_authority_encryption_pubkey: ct_mint - .withdraw_withheld_authority_encryption_pubkey, + .withdraw_withheld_authority_encryption_pubkey + .into(), }, ]) .await @@ -1565,7 +1587,7 @@ async fn ct_withdraw_withheld_tokens_from_accounts() { 100, &extension.available_balance.try_into().unwrap(), &bob_meta.elgamal_keypair.public, - &ct_mint_transfer_auditor_encryption_keypair.public, + Some(ct_mint_transfer_auditor_encryption_keypair.public), &ct_mint_withdraw_withheld_authority_encryption_keypair.public, &epoch_info, ) @@ -1628,17 +1650,21 @@ async fn ct_withdraw_withheld_tokens_from_accounts() { #[cfg(feature = "zk-ops")] #[tokio::test] async fn ct_transfer_memo() { - let ConfidentialTransferMintWithKeypairs { ct_mint, .. } = - ConfidentialTransferMintWithKeypairs::new(); + let ConfidentialTransferMintWithKeypairs { + ct_mint, + ct_mint_transfer_auditor_encryption_keypair, + .. + } = ConfidentialTransferMintWithKeypairs::new(); let mut context = TestContext::new().await; context .init_token_with_mint(vec![ ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(ct_mint.authority), + authority: ct_mint.authority.into(), auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey, + auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey.into(), withdraw_withheld_authority_encryption_pubkey: ct_mint - .withdraw_withheld_authority_encryption_pubkey, + .withdraw_withheld_authority_encryption_pubkey + .into(), }, ]) .await @@ -1676,7 +1702,7 @@ async fn ct_transfer_memo() { 42, &extension.available_balance.try_into().unwrap(), &bob_meta.elgamal_keypair.public, - &ct_mint.auditor_encryption_pubkey.try_into().unwrap(), + Some(ct_mint_transfer_auditor_encryption_keypair.public), ) .await .unwrap_err(); @@ -1702,7 +1728,7 @@ async fn ct_transfer_memo() { 42, &extension.available_balance.try_into().unwrap(), &bob_meta.elgamal_keypair.public, - &ct_mint.auditor_encryption_pubkey.try_into().unwrap(), + Some(ct_mint_transfer_auditor_encryption_keypair.public), ) .await .unwrap(); @@ -1735,8 +1761,12 @@ async fn ct_transfer_memo() { #[cfg(feature = "zk-ops")] #[tokio::test] async fn ct_transfer_with_fee_memo() { - let ConfidentialTransferMintWithKeypairs { ct_mint, .. } = - ConfidentialTransferMintWithKeypairs::new(); + let ConfidentialTransferMintWithKeypairs { + ct_mint, + ct_mint_transfer_auditor_encryption_keypair, + ct_mint_withdraw_withheld_authority_encryption_keypair, + .. + } = ConfidentialTransferMintWithKeypairs::new(); let mut context = TestContext::new().await; context @@ -1748,11 +1778,12 @@ async fn ct_transfer_with_fee_memo() { maximum_fee: TEST_MAXIMUM_FEE, }, ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(ct_mint.authority), + authority: ct_mint.authority.into(), auto_approve_new_accounts: ct_mint.auto_approve_new_accounts.try_into().unwrap(), - auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey, + auditor_encryption_pubkey: ct_mint.auditor_encryption_pubkey.into(), withdraw_withheld_authority_encryption_pubkey: ct_mint - .withdraw_withheld_authority_encryption_pubkey, + .withdraw_withheld_authority_encryption_pubkey + .into(), }, ]) .await @@ -1792,11 +1823,8 @@ async fn ct_transfer_with_fee_memo() { 100, &extension.available_balance.try_into().unwrap(), &bob_meta.elgamal_keypair.public, - &ct_mint.auditor_encryption_pubkey.try_into().unwrap(), - &ct_mint - .withdraw_withheld_authority_encryption_pubkey - .try_into() - .unwrap(), + Some(ct_mint_transfer_auditor_encryption_keypair.public), + &ct_mint_withdraw_withheld_authority_encryption_keypair.public, &epoch_info, ) .await @@ -1822,11 +1850,8 @@ async fn ct_transfer_with_fee_memo() { 100, &extension.available_balance.try_into().unwrap(), &bob_meta.elgamal_keypair.public, - &ct_mint.auditor_encryption_pubkey.try_into().unwrap(), - &ct_mint - .withdraw_withheld_authority_encryption_pubkey - .try_into() - .unwrap(), + Some(ct_mint_transfer_auditor_encryption_keypair.public), + &ct_mint_withdraw_withheld_authority_encryption_keypair.public, &epoch_info, ) .await diff --git a/token/program-2022/src/extension/confidential_transfer/instruction.rs b/token/program-2022/src/extension/confidential_transfer/instruction.rs index 1d9b0782..75f5ac74 100644 --- a/token/program-2022/src/extension/confidential_transfer/instruction.rs +++ b/token/program-2022/src/extension/confidential_transfer/instruction.rs @@ -431,14 +431,14 @@ pub enum ConfidentialTransferInstruction { pub struct InitializeMintData { /// Authority to modify the `ConfidentialTransferMint` configuration and to approve new /// accounts. - pub authority: Pubkey, + pub authority: OptionalNonZeroPubkey, /// Determines if newly configured accounts must be approved by the `authority` before they may /// be used by the user. pub auto_approve_new_accounts: PodBool, /// New authority to decode any transfer amount in a confidential transfer. - pub auditor_encryption_pubkey: EncryptionPubkey, + pub auditor_encryption_pubkey: OptionalNonZeroEncryptionPubkey, /// Authority to withdraw withheld fees that are associated with accounts. - pub withdraw_withheld_authority_encryption_pubkey: EncryptionPubkey, + pub withdraw_withheld_authority_encryption_pubkey: OptionalNonZeroEncryptionPubkey, } /// Data expected by `ConfidentialTransferInstruction::UpdateMint` @@ -449,7 +449,7 @@ pub struct UpdateMintData { /// be used by the user. pub auto_approve_new_accounts: PodBool, /// New authority to decode any transfer amount in a confidential transfer. - pub auditor_encryption_pubkey: EncryptionPubkey, + pub auditor_encryption_pubkey: OptionalNonZeroEncryptionPubkey, } /// Data expected by `ConfidentialTransferInstruction::ConfigureAccount` @@ -547,32 +547,25 @@ pub struct WithdrawWithheldTokensFromAccountsData { pub fn initialize_mint( token_program_id: &Pubkey, mint: &Pubkey, - authority: Option<&Pubkey>, + authority: Option, auto_approve_new_accounts: bool, - auditor_encryption_pubkey: &EncryptionPubkey, - withdraw_withheld_authority_encryption_pubkey: &EncryptionPubkey, + auditor_encryption_pubkey: Option, + withdraw_withheld_authority_encryption_pubkey: Option, ) -> Result { check_program_account(token_program_id)?; let accounts = vec![AccountMeta::new(*mint, false)]; - // TODO: use `OptionalNonZeroPubkey` - let authority = if let Some(authority) = authority { - *authority - } else { - Pubkey::default() - }; - Ok(encode_instruction( token_program_id, accounts, TokenInstruction::ConfidentialTransferExtension, ConfidentialTransferInstruction::InitializeMint, &InitializeMintData { - authority, + authority: authority.try_into()?, auto_approve_new_accounts: auto_approve_new_accounts.into(), - auditor_encryption_pubkey: *auditor_encryption_pubkey, + auditor_encryption_pubkey: auditor_encryption_pubkey.try_into()?, withdraw_withheld_authority_encryption_pubkey: - *withdraw_withheld_authority_encryption_pubkey, + withdraw_withheld_authority_encryption_pubkey.try_into()?, }, )) } @@ -585,7 +578,7 @@ pub fn update_mint( authority: &Pubkey, new_authority: Option<&Pubkey>, auto_approve_new_accounts: bool, - auditor_encryption_pubkey: &EncryptionPubkey, + auditor_encryption_pubkey: Option, ) -> Result { check_program_account(token_program_id)?; @@ -606,7 +599,7 @@ pub fn update_mint( ConfidentialTransferInstruction::UpdateMint, &UpdateMintData { auto_approve_new_accounts: auto_approve_new_accounts.into(), - auditor_encryption_pubkey: *auditor_encryption_pubkey, + auditor_encryption_pubkey: auditor_encryption_pubkey.try_into()?, }, )) } diff --git a/token/program-2022/src/extension/confidential_transfer/mod.rs b/token/program-2022/src/extension/confidential_transfer/mod.rs index 20861483..78347461 100644 --- a/token/program-2022/src/extension/confidential_transfer/mod.rs +++ b/token/program-2022/src/extension/confidential_transfer/mod.rs @@ -5,7 +5,7 @@ use { pod::*, }, bytemuck::{Pod, Zeroable}, - solana_program::{entrypoint::ProgramResult, pubkey::Pubkey}, + solana_program::entrypoint::ProgramResult, solana_zk_token_sdk::zk_token_elgamal::pod, }; @@ -25,8 +25,6 @@ pub mod instruction; /// Confidential Transfer Extension processor pub mod processor; -/// ElGamal public key used for encryption -pub type EncryptionPubkey = pod::ElGamalPubkey; /// ElGamal ciphertext containing an account balance pub type EncryptedBalance = pod::ElGamalCiphertext; /// Authenticated encryption containing an account balance @@ -43,11 +41,8 @@ pub struct ConfidentialTransferMint { /// Authority to modify the `ConfidentialTransferMint` configuration and to approve new /// accounts (if `auto_approve_new_accounts` is true) /// - /// Note that setting an authority of `Pubkey::default()` is the idiomatic way to disable - /// future changes to the configuration. - /// /// The legacy Token Multisig account is not supported as the authority - pub authority: Pubkey, + pub authority: OptionalNonZeroPubkey, /// Indicate if newly configured accounts must be approved by the `authority` before they may be /// used by the user. @@ -58,25 +53,21 @@ pub struct ConfidentialTransferMint { pub auto_approve_new_accounts: PodBool, /// Authority to decode any transfer amount in a confidential transafer. - /// - /// * If non-zero, transfers must include ElGamal cyphertext with this public key permitting the - /// auditor to decode the transfer amount. - /// * If all zero, auditing is currently disabled. - pub auditor_encryption_pubkey: EncryptionPubkey, + pub auditor_encryption_pubkey: OptionalNonZeroEncryptionPubkey, - /// Authority to withraw withheld fees that are associated with accounts. It must be set to an - /// all zero pubkey if the mint is not extended for fees. + /// Authority to withraw withheld fees that are associated with accounts. It must be set to + /// `None` if the mint is not extended for fees. /// /// Note that the withdraw withheld authority has the ability to decode any withheld fee /// amount that are associated with accounts. When combined with the fee parameters, the /// withheld fee amounts can reveal information about transfer amounts. /// - /// * If non-zero, transfers must include ElGamal cyphertext of the transfer fee with this + /// * If not `None`, transfers must include ElGamal cyphertext of the transfer fee with this /// public key. If this is the case, but the base mint is not extended for fees, then any /// transfer will fail. - /// * If all zero, transfer fee is disabled. If this is the case, but the base mint is extended + /// * If `None`, transfer fee is disabled. If this is the case, but the base mint is extended /// for fees, then any transfer will fail. - pub withdraw_withheld_authority_encryption_pubkey: EncryptionPubkey, + pub withdraw_withheld_authority_encryption_pubkey: OptionalNonZeroEncryptionPubkey, /// Withheld transfer fee confidential tokens that have been moved to the mint for withdrawal. /// This will always be zero if fees are never enabled. diff --git a/token/program-2022/src/extension/confidential_transfer/processor.rs b/token/program-2022/src/extension/confidential_transfer/processor.rs index 9d293931..db9520e9 100644 --- a/token/program-2022/src/extension/confidential_transfer/processor.rs +++ b/token/program-2022/src/extension/confidential_transfer/processor.rs @@ -55,10 +55,10 @@ fn decode_proof_instruction( /// Processes an [InitializeMint] instruction. fn process_initialize_mint( accounts: &[AccountInfo], - authority: &Pubkey, + authority: &OptionalNonZeroPubkey, auto_approve_new_account: PodBool, - auditor_encryption_pubkey: &EncryptionPubkey, - withdraw_withheld_authority_encryption_pubkey: &EncryptionPubkey, + auditor_encryption_pubkey: &OptionalNonZeroEncryptionPubkey, + withdraw_withheld_authority_encryption_pubkey: &OptionalNonZeroEncryptionPubkey, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let mint_info = next_account_info(account_info_iter)?; @@ -82,7 +82,7 @@ fn process_initialize_mint( fn process_update_mint( accounts: &[AccountInfo], auto_approve_new_account: PodBool, - auditor_encryption_pubkey: &EncryptionPubkey, + auditor_encryption_pubkey: &OptionalNonZeroEncryptionPubkey, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let mint_info = next_account_info(account_info_iter)?; @@ -93,18 +93,26 @@ fn process_update_mint( let mint_data = &mut mint_info.data.borrow_mut(); let mut mint = StateWithExtensionsMut::::unpack(mint_data)?; let confidential_transfer_mint = mint.get_extension_mut::()?; + let maybe_confidential_transfer_mint_authority: Option = + confidential_transfer_mint.authority.into(); + let confidential_transfer_mint_authority = + maybe_confidential_transfer_mint_authority.ok_or(TokenError::NoAuthorityExists)?; - if authority_info.is_signer - && confidential_transfer_mint.authority == *authority_info.key - && (new_authority_info.is_signer || *new_authority_info.key == Pubkey::default()) + if !authority_info.is_signer + || confidential_transfer_mint_authority != *authority_info.key + || (!new_authority_info.is_signer && *new_authority_info.key != Pubkey::default()) { - confidential_transfer_mint.authority = *new_authority_info.key; - confidential_transfer_mint.auto_approve_new_accounts = auto_approve_new_account; - confidential_transfer_mint.auditor_encryption_pubkey = *auditor_encryption_pubkey; - Ok(()) - } else { - Err(ProgramError::MissingRequiredSignature) + return Err(ProgramError::MissingRequiredSignature); } + + if *new_authority_info.key == Pubkey::default() { + confidential_transfer_mint.authority = None.try_into()?; + } else { + confidential_transfer_mint.authority = Some(*new_authority_info.key).try_into()?; + } + confidential_transfer_mint.auto_approve_new_accounts = auto_approve_new_account; + confidential_transfer_mint.auditor_encryption_pubkey = *auditor_encryption_pubkey; + Ok(()) } /// Processes a [ConfigureAccount] instruction. @@ -191,8 +199,12 @@ fn process_approve_account(accounts: &[AccountInfo]) -> ProgramResult { let mint_data = &mint_info.data.borrow_mut(); let mint = StateWithExtensions::::unpack(mint_data)?; let confidential_transfer_mint = mint.get_extension::()?; + let maybe_confidential_transfer_mint_authority: Option = + confidential_transfer_mint.authority.into(); + let confidential_transfer_mint_authority = + maybe_confidential_transfer_mint_authority.ok_or(TokenError::NoAuthorityExists)?; - if authority_info.is_signer && *authority_info.key == confidential_transfer_mint.authority { + if authority_info.is_signer && *authority_info.key == confidential_transfer_mint_authority { let mut confidential_transfer_state = token_account.get_extension_mut::()?; confidential_transfer_state.approved = true.into(); @@ -502,8 +514,9 @@ fn process_transfer( )?; // Check that the auditor encryption public key associated wth the confidential mint is // consistent with what was actually used to generate the zkp. - if proof_data.transfer_pubkeys.auditor_pubkey - != confidential_transfer_mint.auditor_encryption_pubkey + if !confidential_transfer_mint + .auditor_encryption_pubkey + .equals(&proof_data.transfer_pubkeys.auditor_pubkey) { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } @@ -562,15 +575,19 @@ fn process_transfer( )?; // Check that the encryption public keys associated with the confidential extension mint // are consistent with the keys that were used to generate the zkp. - if proof_data.transfer_with_fee_pubkeys.auditor_pubkey - != confidential_transfer_mint.auditor_encryption_pubkey + if !confidential_transfer_mint + .auditor_encryption_pubkey + .equals(&proof_data.transfer_with_fee_pubkeys.auditor_pubkey) { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } - if proof_data - .transfer_with_fee_pubkeys - .withdraw_withheld_authority_pubkey - != confidential_transfer_mint.withdraw_withheld_authority_encryption_pubkey + if !confidential_transfer_mint + .withdraw_withheld_authority_encryption_pubkey + .equals( + &proof_data + .transfer_with_fee_pubkeys + .withdraw_withheld_authority_pubkey, + ) { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } @@ -970,8 +987,9 @@ fn process_withdraw_withheld_tokens_from_mint( )?; // Checks that the withdraw authority encryption public key associated with the mint is // consistent with what was actually used to generate the zkp. - if proof_data.withdraw_withheld_authority_pubkey - != confidential_transfer_mint.withdraw_withheld_authority_encryption_pubkey + if !confidential_transfer_mint + .withdraw_withheld_authority_encryption_pubkey + .equals(&proof_data.withdraw_withheld_authority_pubkey) { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } @@ -1097,8 +1115,9 @@ fn process_withdraw_withheld_tokens_from_accounts( // Checks that the withdraw authority encryption public key associated with the mint is // consistent with what was actually used to generate the zkp. let confidential_transfer_mint = mint.get_extension_mut::()?; - if proof_data.withdraw_withheld_authority_pubkey - != confidential_transfer_mint.withdraw_withheld_authority_encryption_pubkey + if !confidential_transfer_mint + .withdraw_withheld_authority_encryption_pubkey + .equals(&proof_data.withdraw_withheld_authority_pubkey) { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } diff --git a/token/program-2022/src/pod.rs b/token/program-2022/src/pod.rs index d04a77c9..2934d066 100644 --- a/token/program-2022/src/pod.rs +++ b/token/program-2022/src/pod.rs @@ -2,6 +2,7 @@ use { bytemuck::{Pod, Zeroable}, solana_program::{program_error::ProgramError, program_option::COption, pubkey::Pubkey}, + solana_zk_token_sdk::zk_token_elgamal::pod, std::convert::TryFrom, }; @@ -59,6 +60,44 @@ impl From for COption { } } +/// ElGamal public key used for encryption +pub type EncryptionPubkey = pod::ElGamalPubkey; +/// An EncryptionPubkey that encodes `None` as all `0`, meant to be usable as a Pod type. +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +#[repr(transparent)] +pub struct OptionalNonZeroEncryptionPubkey(EncryptionPubkey); +impl OptionalNonZeroEncryptionPubkey { + /// Checks equality between an OptionalNonZeroEncryptionPubkey and an EncryptionPubkey when + /// interpreted as bytes. + pub fn equals(&self, other: &EncryptionPubkey) -> bool { + &self.0 == other + } +} +impl TryFrom> for OptionalNonZeroEncryptionPubkey { + type Error = ProgramError; + fn try_from(p: Option) -> Result { + match p { + None => Ok(Self(EncryptionPubkey::default())), + Some(encryption_pubkey) => { + if encryption_pubkey == EncryptionPubkey::default() { + Err(ProgramError::InvalidArgument) + } else { + Ok(Self(encryption_pubkey)) + } + } + } + } +} +impl From for Option { + fn from(p: OptionalNonZeroEncryptionPubkey) -> Self { + if p.0 == EncryptionPubkey::default() { + None + } else { + Some(p.0) + } + } +} + /// The standard `bool` is not a `Pod`, define a replacement that is #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] #[repr(transparent)]