[confidential-extension] Use `OptionalNonZeroPubkey` and `OptionalNonZeroEncryptionPubkey` for confidential extension (#3943)

* use OptionalNonZeroPubkey for the confidential mint authority pubkey

* add OptionalNonZeroEncryptionPubkey

* update tests

* Update token/program-2022/src/pod.rs

Co-authored-by: Jon Cinque <jon.cinque@gmail.com>

* remove unnecessary COption convertion for OptionalNonZeroEncryptionPubkey

Co-authored-by: Jon Cinque <jon.cinque@gmail.com>
This commit is contained in:
samkim-crypto 2022-12-23 11:06:18 +09:00 committed by GitHub
parent 19b8fae56c
commit b9aba3fb8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 263 additions and 184 deletions

View File

@ -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<Pubkey>,
auto_approve_new_accounts: bool,
auditor_encryption_pubkey: EncryptionPubkey,
withdraw_withheld_authority_encryption_pubkey: EncryptionPubkey,
auditor_encryption_pubkey: Option<EncryptionPubkey>,
withdraw_withheld_authority_encryption_pubkey: Option<EncryptionPubkey>,
},
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<EncryptionPubkey>,
) -> TokenResult<T::Output> {
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<S: Signer>(
&self,
) -> TokenResult<ElGamalPubkey> {
) -> TokenResult<Option<ElGamalPubkey>> {
let mint_state = self.get_mint_info().await.unwrap();
let ct_mint =
mint_state.get_extension::<confidential_transfer::ConfidentialTransferMint>()?;
let auditor_pubkey = ct_mint
.auditor_encryption_pubkey
.try_into()
.map_err(TokenError::Proof)?;
let auditor_encryption_pubkey: Option<EncryptionPubkey> =
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<ElGamalPubkey> {
) -> TokenResult<Option<ElGamalPubkey>> {
let mint_state = self.get_mint_info().await.unwrap();
let ct_mint =
mint_state.get_extension::<confidential_transfer::ConfidentialTransferMint>()?;
let auditor_pubkey = ct_mint
.withdraw_withheld_authority_encryption_pubkey
.try_into()
.map_err(TokenError::Proof)?;
let withdraw_withheld_authority_encryption_pubkey: Option<EncryptionPubkey> =
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<ElGamalPubkey>,
) -> TokenResult<T::Output> {
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<ElGamalPubkey>,
source_elgamal_keypair: &ElGamalKeypair,
source_authenticated_encryption_key: &AeKey,
) -> TokenResult<T::Output> {
@ -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<ElGamalPubkey>,
withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey,
epoch_info: &EpochInfo,
) -> TokenResult<T::Output> {
@ -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<ElGamalPubkey>,
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::<transfer_fee::TransferFeeConfig>()
@ -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),

View File

@ -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 = ConfidentialTransferMint {
authority: ct_mint_authority.pubkey(),
auto_approve_new_accounts: true.into(),
auditor_encryption_pubkey: ct_mint_transfer_auditor_encryption_keypair.public.into(),
withdraw_withheld_authority_encryption_pubkey:
let ct_mint_withdraw_withheld_authority_encryption_pubkey: EncryptionPubkey =
ct_mint_withdraw_withheld_authority_encryption_keypair
.public
.into(),
.try_into()
.unwrap();
let ct_mint = ConfidentialTransferMint {
authority: Some(ct_mint_authority.pubkey()).try_into().unwrap(),
auto_approve_new_accounts: true.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

View File

@ -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<Pubkey>,
auto_approve_new_accounts: bool,
auditor_encryption_pubkey: &EncryptionPubkey,
withdraw_withheld_authority_encryption_pubkey: &EncryptionPubkey,
auditor_encryption_pubkey: Option<EncryptionPubkey>,
withdraw_withheld_authority_encryption_pubkey: Option<EncryptionPubkey>,
) -> Result<Instruction, ProgramError> {
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<EncryptionPubkey>,
) -> Result<Instruction, ProgramError> {
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()?,
},
))
}

View File

@ -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.

View File

@ -55,10 +55,10 @@ fn decode_proof_instruction<T: Pod>(
/// 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::<Mint>::unpack(mint_data)?;
let confidential_transfer_mint = mint.get_extension_mut::<ConfidentialTransferMint>()?;
let maybe_confidential_transfer_mint_authority: Option<Pubkey> =
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;
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(())
} else {
Err(ProgramError::MissingRequiredSignature)
}
}
/// 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::<Mint>::unpack(mint_data)?;
let confidential_transfer_mint = mint.get_extension::<ConfidentialTransferMint>()?;
let maybe_confidential_transfer_mint_authority: Option<Pubkey> =
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::<ConfidentialTransferAccount>()?;
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
if !confidential_transfer_mint
.withdraw_withheld_authority_encryption_pubkey
.equals(
&proof_data
.transfer_with_fee_pubkeys
.withdraw_withheld_authority_pubkey
!= confidential_transfer_mint.withdraw_withheld_authority_encryption_pubkey
.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::<ConfidentialTransferMint>()?;
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());
}

View File

@ -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<OptionalNonZeroPubkey> for COption<Pubkey> {
}
}
/// 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<Option<EncryptionPubkey>> for OptionalNonZeroEncryptionPubkey {
type Error = ProgramError;
fn try_from(p: Option<EncryptionPubkey>) -> Result<Self, Self::Error> {
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<OptionalNonZeroEncryptionPubkey> for Option<EncryptionPubkey> {
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)]